2 * This file is part of the LibreOffice project.
6 * This file is distributed under the University of Illinois Open Source
7 * License. See LICENSE.TXT for details.
11 #include "pluginhandler.hxx"
13 #include <clang/Frontend/CompilerInstance.h>
14 #include <clang/Frontend/FrontendPluginRegistry.h>
15 #include <clang/Lex/PPCallbacks.h>
21 This source file manages all plugin actions. It is not necessary to modify this
22 file when adding new actions.
29 Plugin
* (*create
)( CompilerInstance
&, Rewriter
& );
31 const char* optionName
;
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())
45 bool wasPlugin
= false;
46 for( vector
< string
>::const_iterator it
= args
.begin();
50 if( it
->size() >= 2 && (*it
)[ 0 ] == '-' && (*it
)[ 1 ] == '-' )
51 handleOption( it
->substr( 2 ));
59 createPlugin( "" ); // = all non-rewriters
60 pluginObjectsCreated
= true;
63 PluginHandler::~PluginHandler()
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" )
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
;
91 report( DiagnosticsEngine::Fatal
, "unknown option %0" ) << option
;
94 void PluginHandler::createPlugin( const string
& name
)
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
);
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
;
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())
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();
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. */
152 const char* pathWarning
= NULL
;
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 )
180 pathWarning
= "modified source in unknown location, not modifying : %0";
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())
192 else if( scope
== "all" )
194 else // scope is module
196 if( strncmp( modifyFile
.c_str(), ( SRCDIR
"/" + scope
+ "/" ).c_str(), ( SRCDIR
"/" + scope
+ "/" ).size()) != 0 )
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();
204 char* filename
= new char[ modifyFile
.length() + 100 ];
205 sprintf( filename
, "%s.new.%d", modifyFile
.c_str(), getpid());
208 raw_fd_ostream
ostream( filename
, error
);
211 it
->second
.write( ostream
);
213 if( !ostream
.has_error() && rename( filename
, modifyFile
.c_str()) == 0 )
216 ostream
.clear_error();
219 report( DiagnosticsEngine::Error
, "cannot write modified source to %0 (%1)" ) << modifyFile
<< error
;
224 ASTConsumer
* LibreOfficeAction::CreateASTConsumer( CompilerInstance
& Compiler
, StringRef
)
226 return new PluginHandler( Compiler
, _args
);
229 bool LibreOfficeAction::ParseArgs( const CompilerInstance
&, const vector
< string
>& args
)
236 static FrontendPluginRegistry::Add
< loplugin::LibreOfficeAction
> X( "loplugin", "LibreOffice compile check plugin" );