update credits
[LibreOffice.git] / compilerplugins / clang / pluginhandler.cxx
blobcd554aac97e7ee46d27437b6befcb01e9bc76d7c
1 /*
2 * This file is part of the LibreOffice project.
4 * Based on LLVM/Clang.
6 * This file is distributed under the University of Illinois Open Source
7 * License. See LICENSE.TXT for details.
9 */
11 #include "pluginhandler.hxx"
13 #include <clang/Frontend/CompilerInstance.h>
14 #include <clang/Frontend/FrontendPluginRegistry.h>
15 #include <clang/Lex/PPCallbacks.h>
16 #include <stdio.h>
17 #include <sys/stat.h>
18 #include <unistd.h>
21 This source file manages all plugin actions. It is not necessary to modify this
22 file when adding new actions.
24 namespace loplugin
27 struct PluginData
29 Plugin* (*create)( CompilerInstance&, Rewriter& );
30 Plugin* object;
31 const char* optionName;
32 bool isRewriter;
35 const int MAX_PLUGINS = 100;
36 static PluginData plugins[ MAX_PLUGINS ];
37 static int pluginCount = 0;
38 static bool pluginObjectsCreated = false;
40 PluginHandler::PluginHandler( CompilerInstance& compiler, const vector< string >& args )
41 : compiler( compiler )
42 , rewriter( compiler.getSourceManager(), compiler.getLangOpts())
43 , scope( "mainfile" )
45 bool wasPlugin = false;
46 for( vector< string >::const_iterator it = args.begin();
47 it != args.end();
48 ++it )
50 if( it->size() >= 2 && (*it)[ 0 ] == '-' && (*it)[ 1 ] == '-' )
51 handleOption( it->substr( 2 ));
52 else
54 createPlugin( *it );
55 wasPlugin = true;
58 if( !wasPlugin )
59 createPlugin( "" ); // = all non-rewriters
60 pluginObjectsCreated = true;
63 PluginHandler::~PluginHandler()
65 for( int i = 0;
66 i < pluginCount;
67 ++i )
68 if( plugins[ i ].object != NULL )
70 // PPCallbacks is owned by preprocessor object, don't delete those
71 if( dynamic_cast< PPCallbacks* >( plugins[ i ].object ) == NULL )
72 delete plugins[ i ].object;
76 void PluginHandler::handleOption( const string& option )
78 if( option.substr( 0, 6 ) == "scope=" )
80 scope = option.substr( 6 );
81 if( scope == "mainfile" || scope == "all" )
82 ; // ok
83 else
85 struct stat st;
86 if( stat(( SRCDIR "/" + scope ).c_str(), &st ) != 0 || !S_ISDIR( st.st_mode ))
87 report( DiagnosticsEngine::Fatal, "unknown scope %0 (no such module directory)" ) << scope;
90 else
91 report( DiagnosticsEngine::Fatal, "unknown option %0" ) << option;
94 void PluginHandler::createPlugin( const string& name )
96 for( int i = 0;
97 i < pluginCount;
98 ++i )
100 if( name.empty()) // no plugin given -> create non-writer plugins
102 if( !plugins[ i ].isRewriter )
103 plugins[ i ].object = plugins[ i ].create( compiler, rewriter );
105 else if( plugins[ i ].optionName == name )
107 plugins[ i ].object = plugins[ i ].create( compiler, rewriter );
108 return;
111 if( !name.empty())
112 report( DiagnosticsEngine::Fatal, "unknown plugin tool %0" ) << name;
115 void PluginHandler::registerPlugin( Plugin* (*create)( CompilerInstance&, Rewriter& ), const char* optionName, bool isRewriter )
117 assert( !pluginObjectsCreated );
118 assert( pluginCount < MAX_PLUGINS );
119 plugins[ pluginCount ].create = create;
120 plugins[ pluginCount ].object = NULL;
121 plugins[ pluginCount ].optionName = optionName;
122 plugins[ pluginCount ].isRewriter = isRewriter;
123 ++pluginCount;
126 DiagnosticBuilder PluginHandler::report( DiagnosticsEngine::Level level, StringRef message, SourceLocation loc )
128 return Plugin::report( level, message, compiler, loc );
131 void PluginHandler::HandleTranslationUnit( ASTContext& context )
133 if( context.getDiagnostics().hasErrorOccurred())
134 return;
135 for( int i = 0;
136 i < pluginCount;
137 ++i )
139 if( plugins[ i ].object != NULL )
140 plugins[ i ].object->run();
142 for( Rewriter::buffer_iterator it = rewriter.buffer_begin();
143 it != rewriter.buffer_end();
144 ++it )
146 const FileEntry* e = context.getSourceManager().getFileEntryForID( it->first );
147 /* Check where the file actually is, and warn about cases where modification
148 most probably doesn't matter (generated files in workdir).
149 The order here is important, as OUTDIR and WORKDIR are often in SRCDIR/BUILDDIR,
150 and BUILDDIR is sometimes in SRCDIR. */
151 string modifyFile;
152 const char* pathWarning = NULL;
153 bool skip = false;
154 if( strncmp( e->getName(), OUTDIR "/", strlen( OUTDIR "/" )) == 0 )
156 /* Try to find a matching file for a file in solver/ (include files
157 are usually included from there rather than from the source dir) if possible. */
158 if( strncmp( e->getName(), OUTDIR "/inc/", strlen( OUTDIR ) + strlen( "/inc/" )) == 0 )
160 string filename( e->getName());
161 int modulePos = strlen( OUTDIR ) + strlen( "/inc/" );
162 size_t moduleEnd = filename.find( '/', modulePos );
163 if( moduleEnd != string::npos )
165 modifyFile = SRCDIR "/" + filename.substr( modulePos, moduleEnd - modulePos )
166 + "/inc/" + filename.substr( modulePos );
169 if( modifyFile.empty())
170 pathWarning = "modified source in solver/ : %0";
172 else if( strncmp( e->getName(), WORKDIR "/", strlen( WORKDIR "/" )) == 0 )
173 pathWarning = "modified source in workdir/ : %0";
174 else if( strcmp( SRCDIR, BUILDDIR ) != 0 && strncmp( e->getName(), BUILDDIR "/", strlen( BUILDDIR "/" )) == 0 )
175 pathWarning = "modified source in build dir : %0";
176 else if( strncmp( e->getName(), SRCDIR "/", strlen( SRCDIR "/" )) == 0 )
177 ; // ok
178 else
180 pathWarning = "modified source in unknown location, not modifying : %0";
181 skip = true;
183 if( modifyFile.empty())
184 modifyFile = e->getName();
185 // Check whether the modified file is in the wanted scope (done after path checking above), so
186 // that files mapped from OUTDIR to SRCDIR are included.
187 if( scope == "mainfile" )
189 if( it->first != context.getSourceManager().getMainFileID())
190 continue;
192 else if( scope == "all" )
193 ; // ok
194 else // scope is module
196 if( strncmp( modifyFile.c_str(), ( SRCDIR "/" + scope + "/" ).c_str(), ( SRCDIR "/" + scope + "/" ).size()) != 0 )
197 continue;
199 // Warn only now, so that files not in scope do not cause warnings.
200 if( pathWarning != NULL )
201 report( DiagnosticsEngine::Warning, pathWarning ) << e->getName();
202 if( skip )
203 continue;
204 char* filename = new char[ modifyFile.length() + 100 ];
205 sprintf( filename, "%s.new.%d", modifyFile.c_str(), getpid());
206 string error;
207 bool ok = false;
208 raw_fd_ostream ostream( filename, error );
209 if( error.empty())
211 it->second.write( ostream );
212 ostream.close();
213 if( !ostream.has_error() && rename( filename, modifyFile.c_str()) == 0 )
214 ok = true;
216 ostream.clear_error();
217 unlink( filename );
218 if( !ok )
219 report( DiagnosticsEngine::Error, "cannot write modified source to %0 (%1)" ) << modifyFile << error;
220 delete[] filename;
224 ASTConsumer* LibreOfficeAction::CreateASTConsumer( CompilerInstance& Compiler, StringRef )
226 return new PluginHandler( Compiler, _args );
229 bool LibreOfficeAction::ParseArgs( const CompilerInstance&, const vector< string >& args )
231 _args = args;
232 return true;
236 static FrontendPluginRegistry::Add< loplugin::LibreOfficeAction > X( "loplugin", "LibreOffice compile check plugin" );
238 } // namespace