cURL: follow redirects
[LibreOffice.git] / compilerplugins / clang / pluginhandler.cxx
blob89ba8f435da0133a715f9e7286868bb3d5a96dd2
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * Based on LLVM/Clang.
7 * This file is distributed under the University of Illinois Open Source
8 * License. See LICENSE.TXT for details.
12 #include "compat.hxx"
13 #include "pluginhandler.hxx"
15 #include <clang/Frontend/CompilerInstance.h>
16 #include <clang/Frontend/FrontendPluginRegistry.h>
17 #include <clang/Lex/PPCallbacks.h>
18 #include <stdio.h>
19 #include <sys/stat.h>
20 #include <unistd.h>
23 This source file manages all plugin actions. It is not necessary to modify this
24 file when adding new actions.
27 namespace
30 bool isPrefix( const string& prefix, const string& full)
32 return full.compare(0, prefix.size(), prefix) == 0;
37 namespace loplugin
40 struct PluginData
42 Plugin* (*create)( const Plugin::InstantiationData& );
43 Plugin* object;
44 const char* optionName;
45 bool isPPCallback;
46 bool byDefault;
49 const int MAX_PLUGINS = 100;
50 static PluginData plugins[ MAX_PLUGINS ];
51 static int pluginCount = 0;
52 static bool bPluginObjectsCreated = false;
54 PluginHandler::PluginHandler( CompilerInstance& compiler, const vector< string >& args )
55 : compiler( compiler )
56 , rewriter( compiler.getSourceManager(), compiler.getLangOpts())
57 , scope( "mainfile" )
58 , warningsAsErrors( false )
60 set< string > rewriters;
61 for( vector< string >::const_iterator it = args.begin();
62 it != args.end();
63 ++it )
65 if( it->size() >= 2 && (*it)[ 0 ] == '-' && (*it)[ 1 ] == '-' )
66 handleOption( it->substr( 2 ));
67 else
68 rewriters.insert( *it );
70 createPlugins( rewriters );
71 bPluginObjectsCreated = true;
74 PluginHandler::~PluginHandler()
76 for( int i = 0;
77 i < pluginCount;
78 ++i )
79 if( plugins[ i ].object != NULL )
81 // PPCallbacks is owned by preprocessor object, don't delete those
82 if( !plugins[ i ].isPPCallback )
83 delete plugins[ i ].object;
87 void PluginHandler::handleOption( const string& option )
89 if( option.substr( 0, 6 ) == "scope=" )
91 scope = option.substr( 6 );
92 if( scope == "mainfile" || scope == "all" )
93 ; // ok
94 else
96 struct stat st;
97 if( stat(( SRCDIR "/" + scope ).c_str(), &st ) != 0 || !S_ISDIR( st.st_mode ))
98 report( DiagnosticsEngine::Fatal, "unknown scope %0 (no such module directory)" ) << scope;
101 else if( option.substr( 0, 14 ) == "warnings-only=" )
103 warningsOnly = option.substr(14);
105 else if( option == "warnings-as-errors" )
106 warningsAsErrors = true;
107 else
108 report( DiagnosticsEngine::Fatal, "unknown option %0" ) << option;
111 void PluginHandler::createPlugins( set< string > rewriters )
113 for( int i = 0;
114 i < pluginCount;
115 ++i )
117 if( rewriters.erase( plugins[i].optionName ) != 0 )
118 plugins[ i ].object = plugins[ i ].create( Plugin::InstantiationData { plugins[ i ].optionName, *this, compiler, &rewriter } );
119 else if( plugins[ i ].byDefault )
120 plugins[ i ].object = plugins[ i ].create( Plugin::InstantiationData { plugins[ i ].optionName, *this, compiler, NULL } );
122 for( auto r: rewriters )
123 report( DiagnosticsEngine::Fatal, "unknown plugin tool %0" ) << r;
126 void PluginHandler::registerPlugin( Plugin* (*create)( const Plugin::InstantiationData& ), const char* optionName, bool isPPCallback, bool byDefault )
128 assert( !bPluginObjectsCreated );
129 assert( pluginCount < MAX_PLUGINS );
130 plugins[ pluginCount ].create = create;
131 plugins[ pluginCount ].object = NULL;
132 plugins[ pluginCount ].optionName = optionName;
133 plugins[ pluginCount ].isPPCallback = isPPCallback;
134 plugins[ pluginCount ].byDefault = byDefault;
135 ++pluginCount;
138 DiagnosticBuilder PluginHandler::report( DiagnosticsEngine::Level level, const char* plugin, StringRef message, CompilerInstance& compiler,
139 SourceLocation loc )
141 DiagnosticsEngine& diag = compiler.getDiagnostics();
142 // Do some mappings (e.g. for -Werror) that clang does not do for custom messages for some reason.
143 if( level == DiagnosticsEngine::Warning && ((diag.getWarningsAsErrors() && (plugin == nullptr || plugin != warningsOnly)) || warningsAsErrors))
144 level = DiagnosticsEngine::Error;
145 if( level == DiagnosticsEngine::Error && diag.getErrorsAsFatal())
146 level = DiagnosticsEngine::Fatal;
147 string fullMessage = ( message + " [loplugin" ).str();
148 if( plugin )
150 fullMessage += ":";
151 fullMessage += plugin;
153 fullMessage += "]";
154 if( loc.isValid())
155 return diag.Report( loc, compat::getCustomDiagID(diag, level, fullMessage) );
156 else
157 return diag.Report( compat::getCustomDiagID(diag, level, fullMessage) );
160 DiagnosticBuilder PluginHandler::report( DiagnosticsEngine::Level level, StringRef message, SourceLocation loc )
162 return report( level, nullptr, message, compiler, loc );
165 bool PluginHandler::addRemoval( SourceLocation loc )
167 return removals.insert( loc ).second;
170 void PluginHandler::HandleTranslationUnit( ASTContext& context )
172 if( context.getDiagnostics().hasErrorOccurred())
173 return;
174 StringRef const mainFileName = context.getSourceManager().getFileEntryForID(context.getSourceManager().getMainFileID())->getName();
175 if (mainFileName.endswith(".ii"))
177 report(DiagnosticsEngine::Fatal,
178 "input file has suffix .ii: \"%0\"\nhighly suspicious, probably ccache generated, this will break warning suppressions; export CCACHE_CPP2=1 to prevent this") << mainFileName;
179 return;
182 for( int i = 0;
183 i < pluginCount;
184 ++i )
186 if( plugins[ i ].object != NULL )
187 plugins[ i ].object->run();
189 for( Rewriter::buffer_iterator it = rewriter.buffer_begin();
190 it != rewriter.buffer_end();
191 ++it )
193 const FileEntry* e = context.getSourceManager().getFileEntryForID( it->first );
194 if( e == NULL )
195 continue; // Failed modification because of a macro expansion?
196 /* Check where the file actually is, and warn about cases where modification
197 most probably doesn't matter (generated files in workdir).
198 The order here is important, as INSTDIR and WORKDIR are often in SRCDIR/BUILDDIR,
199 and BUILDDIR is sometimes in SRCDIR. */
200 string modifyFile;
201 const char* pathWarning = NULL;
202 bool bSkip = false;
203 StringRef const name = e->getName();
204 if( name.startswith(WORKDIR "/") )
205 pathWarning = "modified source in workdir/ : %0";
206 else if( strcmp( SRCDIR, BUILDDIR ) != 0 && name.startswith(BUILDDIR "/") )
207 pathWarning = "modified source in build dir : %0";
208 else if( name.startswith(SRCDIR "/") )
209 ; // ok
210 else
212 pathWarning = "modified source in unknown location, not modifying : %0";
213 bSkip = true;
215 if( modifyFile.empty())
216 modifyFile = name;
217 // Check whether the modified file is in the wanted scope
218 if( scope == "mainfile" )
220 if( it->first != context.getSourceManager().getMainFileID())
221 continue;
223 else if( scope == "all" )
224 ; // ok
225 else // scope is module
227 if( !( isPrefix( SRCDIR "/" + scope + "/", modifyFile ) || isPrefix( SRCDIR "/include/" + scope + "/", modifyFile ) ) )
228 continue;
230 // Warn only now, so that files not in scope do not cause warnings.
231 if( pathWarning != NULL )
232 report( DiagnosticsEngine::Warning, pathWarning ) << name;
233 if( bSkip )
234 continue;
235 char* filename = new char[ modifyFile.length() + 100 ];
236 sprintf( filename, "%s.new.%d", modifyFile.c_str(), getpid());
237 string error;
238 bool bOk = false;
239 std::unique_ptr<raw_fd_ostream> ostream(
240 compat::create_raw_fd_ostream(filename, error) );
241 if( error.empty())
243 it->second.write( *ostream );
244 ostream->close();
245 if( !ostream->has_error() && rename( filename, modifyFile.c_str()) == 0 )
246 bOk = true;
248 ostream->clear_error();
249 unlink( filename );
250 if( !bOk )
251 report( DiagnosticsEngine::Error, "cannot write modified source to %0 (%1)" ) << modifyFile << error;
252 delete[] filename;
256 #if CLANG_VERSION >= 30600
257 std::unique_ptr<ASTConsumer> LibreOfficeAction::CreateASTConsumer( CompilerInstance& Compiler, StringRef )
259 return llvm::make_unique<PluginHandler>( Compiler, _args );
261 #else
262 ASTConsumer* LibreOfficeAction::CreateASTConsumer( CompilerInstance& Compiler, StringRef )
264 return new PluginHandler( Compiler, _args );
266 #endif
268 bool LibreOfficeAction::ParseArgs( const CompilerInstance&, const vector< string >& args )
270 _args = args;
271 return true;
274 static FrontendPluginRegistry::Add< loplugin::LibreOfficeAction > X( "loplugin", "LibreOffice compile check plugin" );
276 } // namespace
278 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */