bump product version to 5.0.4.1
[LibreOffice.git] / compilerplugins / clang / pluginhandler.cxx
blob0a5af667e9db916effc9d3ac1df2ef3ddbbedd37
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 pluginObjectsCreated = false;
54 PluginHandler::PluginHandler( CompilerInstance& compiler, const vector< string >& args )
55 : compiler( compiler )
56 , rewriter( compiler.getSourceManager(), compiler.getLangOpts())
57 , scope( "mainfile" )
59 set< string > rewriters;
60 for( vector< string >::const_iterator it = args.begin();
61 it != args.end();
62 ++it )
64 if( it->size() >= 2 && (*it)[ 0 ] == '-' && (*it)[ 1 ] == '-' )
65 handleOption( it->substr( 2 ));
66 else
67 rewriters.insert( *it );
69 createPlugins( rewriters );
70 pluginObjectsCreated = true;
73 PluginHandler::~PluginHandler()
75 for( int i = 0;
76 i < pluginCount;
77 ++i )
78 if( plugins[ i ].object != NULL )
80 // PPCallbacks is owned by preprocessor object, don't delete those
81 if( !plugins[ i ].isPPCallback )
82 delete plugins[ i ].object;
86 void PluginHandler::handleOption( const string& option )
88 if( option.substr( 0, 6 ) == "scope=" )
90 scope = option.substr( 6 );
91 if( scope == "mainfile" || scope == "all" )
92 ; // ok
93 else
95 struct stat st;
96 if( stat(( SRCDIR "/" + scope ).c_str(), &st ) != 0 || !S_ISDIR( st.st_mode ))
97 report( DiagnosticsEngine::Fatal, "unknown scope %0 (no such module directory)" ) << scope;
100 else if( option.substr( 0, 14 ) == "warnings-only=" )
102 warningsOnly = option.substr(14);
104 else
105 report( DiagnosticsEngine::Fatal, "unknown option %0" ) << option;
108 void PluginHandler::createPlugins( set< string > rewriters )
110 for( int i = 0;
111 i < pluginCount;
112 ++i )
114 if( rewriters.erase( plugins[i].optionName ) != 0 )
115 plugins[ i ].object = plugins[ i ].create( Plugin::InstantiationData { plugins[ i ].optionName, *this, compiler, &rewriter } );
116 else if( plugins[ i ].byDefault )
117 plugins[ i ].object = plugins[ i ].create( Plugin::InstantiationData { plugins[ i ].optionName, *this, compiler, NULL } );
119 for( auto r: rewriters )
120 report( DiagnosticsEngine::Fatal, "unknown plugin tool %0" ) << r;
123 void PluginHandler::registerPlugin( Plugin* (*create)( const Plugin::InstantiationData& ), const char* optionName, bool isPPCallback, bool byDefault )
125 assert( !pluginObjectsCreated );
126 assert( pluginCount < MAX_PLUGINS );
127 plugins[ pluginCount ].create = create;
128 plugins[ pluginCount ].object = NULL;
129 plugins[ pluginCount ].optionName = optionName;
130 plugins[ pluginCount ].isPPCallback = isPPCallback;
131 plugins[ pluginCount ].byDefault = byDefault;
132 ++pluginCount;
135 DiagnosticBuilder PluginHandler::report( DiagnosticsEngine::Level level, const char* plugin, StringRef message, CompilerInstance& compiler,
136 SourceLocation loc )
138 DiagnosticsEngine& diag = compiler.getDiagnostics();
139 // Do some mappings (e.g. for -Werror) that clang does not do for custom messages for some reason.
140 if( level == DiagnosticsEngine::Warning && diag.getWarningsAsErrors() && (plugin == nullptr || plugin != warningsOnly))
141 level = DiagnosticsEngine::Error;
142 if( level == DiagnosticsEngine::Error && diag.getErrorsAsFatal())
143 level = DiagnosticsEngine::Fatal;
144 string fullMessage = ( message + " [loplugin" ).str();
145 if( plugin )
147 fullMessage += ":";
148 fullMessage += plugin;
150 fullMessage += "]";
151 if( loc.isValid())
152 return diag.Report( loc, compat::getCustomDiagID(diag, level, fullMessage) );
153 else
154 return diag.Report( compat::getCustomDiagID(diag, level, fullMessage) );
157 DiagnosticBuilder PluginHandler::report( DiagnosticsEngine::Level level, StringRef message, SourceLocation loc )
159 return report( level, nullptr, message, compiler, loc );
162 bool PluginHandler::addRemoval( SourceLocation loc )
164 return removals.insert( loc ).second;
167 void PluginHandler::HandleTranslationUnit( ASTContext& context )
169 if( context.getDiagnostics().hasErrorOccurred())
170 return;
171 for( int i = 0;
172 i < pluginCount;
173 ++i )
175 if( plugins[ i ].object != NULL )
176 plugins[ i ].object->run();
178 for( Rewriter::buffer_iterator it = rewriter.buffer_begin();
179 it != rewriter.buffer_end();
180 ++it )
182 const FileEntry* e = context.getSourceManager().getFileEntryForID( it->first );
183 if( e == NULL )
184 continue; // Failed modification because of a macro expansion?
185 /* Check where the file actually is, and warn about cases where modification
186 most probably doesn't matter (generated files in workdir).
187 The order here is important, as INSTDIR and WORKDIR are often in SRCDIR/BUILDDIR,
188 and BUILDDIR is sometimes in SRCDIR. */
189 string modifyFile;
190 const char* pathWarning = NULL;
191 bool skip = false;
192 if( strncmp( e->getName(), WORKDIR "/", strlen( WORKDIR "/" )) == 0 )
193 pathWarning = "modified source in workdir/ : %0";
194 else if( strcmp( SRCDIR, BUILDDIR ) != 0 && strncmp( e->getName(), BUILDDIR "/", strlen( BUILDDIR "/" )) == 0 )
195 pathWarning = "modified source in build dir : %0";
196 else if( strncmp( e->getName(), SRCDIR "/", strlen( SRCDIR "/" )) == 0 )
197 ; // ok
198 else
200 pathWarning = "modified source in unknown location, not modifying : %0";
201 skip = true;
203 if( modifyFile.empty())
204 modifyFile = e->getName();
205 // Check whether the modified file is in the wanted scope
206 if( scope == "mainfile" )
208 if( it->first != context.getSourceManager().getMainFileID())
209 continue;
211 else if( scope == "all" )
212 ; // ok
213 else // scope is module
215 if( !( isPrefix( SRCDIR "/" + scope + "/", modifyFile ) || isPrefix( SRCDIR "/include/" + scope + "/", modifyFile ) ) )
216 continue;
218 // Warn only now, so that files not in scope do not cause warnings.
219 if( pathWarning != NULL )
220 report( DiagnosticsEngine::Warning, pathWarning ) << e->getName();
221 if( skip )
222 continue;
223 char* filename = new char[ modifyFile.length() + 100 ];
224 sprintf( filename, "%s.new.%d", modifyFile.c_str(), getpid());
225 string error;
226 bool ok = false;
227 std::unique_ptr<raw_fd_ostream> ostream(
228 compat::create_raw_fd_ostream(filename, error) );
229 if( error.empty())
231 it->second.write( *ostream );
232 ostream->close();
233 if( !ostream->has_error() && rename( filename, modifyFile.c_str()) == 0 )
234 ok = true;
236 ostream->clear_error();
237 unlink( filename );
238 if( !ok )
239 report( DiagnosticsEngine::Error, "cannot write modified source to %0 (%1)" ) << modifyFile << error;
240 delete[] filename;
244 #if (__clang_major__ == 3 && __clang_minor__ >= 6) || __clang_major__ > 3
245 std::unique_ptr<ASTConsumer> LibreOfficeAction::CreateASTConsumer( CompilerInstance& Compiler, StringRef )
247 return make_unique<PluginHandler>( Compiler, _args );
249 #else
250 ASTConsumer* LibreOfficeAction::CreateASTConsumer( CompilerInstance& Compiler, StringRef )
252 return new PluginHandler( Compiler, _args );
254 #endif
256 bool LibreOfficeAction::ParseArgs( const CompilerInstance&, const vector< string >& args )
258 _args = args;
259 return true;
262 static FrontendPluginRegistry::Add< loplugin::LibreOfficeAction > X( "loplugin", "LibreOffice compile check plugin" );
264 } // namespace
266 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */