1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
7 * This file is distributed under the University of Illinois Open Source
8 * License. See LICENSE.TXT for details.
12 #include "pluginhandler.hxx"
14 #include <clang/Frontend/CompilerInstance.h>
15 #include <clang/Frontend/FrontendPluginRegistry.h>
16 #include <clang/Lex/PPCallbacks.h>
22 This source file manages all plugin actions. It is not necessary to modify this
23 file when adding new actions.
29 bool isPrefix( const string
& prefix
, const string
& full
)
31 return full
.compare(0, prefix
.size(), prefix
) == 0;
41 Plugin
* (*create
)( CompilerInstance
&, Rewriter
& );
43 const char* optionName
;
48 const int MAX_PLUGINS
= 100;
49 static PluginData plugins
[ MAX_PLUGINS
];
50 static int pluginCount
= 0;
51 static bool pluginObjectsCreated
= false;
53 PluginHandler::PluginHandler( CompilerInstance
& compiler
, const vector
< string
>& args
)
54 : compiler( compiler
)
55 , rewriter( compiler
.getSourceManager(), compiler
.getLangOpts())
58 bool wasPlugin
= false;
59 for( vector
< string
>::const_iterator it
= args
.begin();
63 if( it
->size() >= 2 && (*it
)[ 0 ] == '-' && (*it
)[ 1 ] == '-' )
64 handleOption( it
->substr( 2 ));
72 createPlugin( "" ); // = all non-rewriters
73 pluginObjectsCreated
= true;
76 PluginHandler::~PluginHandler()
81 if( plugins
[ i
].object
!= NULL
)
83 // PPCallbacks is owned by preprocessor object, don't delete those
84 if( !plugins
[ i
].isPPCallback
)
85 delete plugins
[ i
].object
;
89 void PluginHandler::handleOption( const string
& option
)
91 if( option
.substr( 0, 6 ) == "scope=" )
93 scope
= option
.substr( 6 );
94 if( scope
== "mainfile" || scope
== "all" )
99 if( stat(( SRCDIR
"/" + scope
).c_str(), &st
) != 0 || !S_ISDIR( st
.st_mode
))
100 report( DiagnosticsEngine::Fatal
, "unknown scope %0 (no such module directory)" ) << scope
;
104 report( DiagnosticsEngine::Fatal
, "unknown option %0" ) << option
;
107 void PluginHandler::createPlugin( const string
& name
)
113 if( name
.empty()) // no plugin given -> create non-writer plugins
115 if( !plugins
[ i
].isRewriter
)
116 plugins
[ i
].object
= plugins
[ i
].create( compiler
, rewriter
);
118 else if( plugins
[ i
].optionName
== name
)
120 plugins
[ i
].object
= plugins
[ i
].create( compiler
, rewriter
);
125 report( DiagnosticsEngine::Fatal
, "unknown plugin tool %0" ) << name
;
128 void PluginHandler::registerPlugin( Plugin
* (*create
)( CompilerInstance
&, Rewriter
& ), const char* optionName
, bool isRewriter
, bool isPPCallback
)
130 assert( !pluginObjectsCreated
);
131 assert( pluginCount
< MAX_PLUGINS
);
132 plugins
[ pluginCount
].create
= create
;
133 plugins
[ pluginCount
].object
= NULL
;
134 plugins
[ pluginCount
].optionName
= optionName
;
135 plugins
[ pluginCount
].isRewriter
= isRewriter
;
136 plugins
[ pluginCount
].isPPCallback
= isPPCallback
;
140 DiagnosticBuilder
PluginHandler::report( DiagnosticsEngine::Level level
, StringRef message
, SourceLocation loc
)
142 return Plugin::report( level
, message
, compiler
, loc
);
145 void PluginHandler::HandleTranslationUnit( ASTContext
& context
)
147 if( context
.getDiagnostics().hasErrorOccurred())
153 if( plugins
[ i
].object
!= NULL
)
154 plugins
[ i
].object
->run();
156 for( Rewriter::buffer_iterator it
= rewriter
.buffer_begin();
157 it
!= rewriter
.buffer_end();
160 const FileEntry
* e
= context
.getSourceManager().getFileEntryForID( it
->first
);
162 continue; // Failed modification because of a macro expansion?
163 /* Check where the file actually is, and warn about cases where modification
164 most probably doesn't matter (generated files in workdir).
165 The order here is important, as INSTDIR and WORKDIR are often in SRCDIR/BUILDDIR,
166 and BUILDDIR is sometimes in SRCDIR. */
168 const char* pathWarning
= NULL
;
170 if( strncmp( e
->getName(), WORKDIR
"/", strlen( WORKDIR
"/" )) == 0 )
171 pathWarning
= "modified source in workdir/ : %0";
172 else if( strcmp( SRCDIR
, BUILDDIR
) != 0 && strncmp( e
->getName(), BUILDDIR
"/", strlen( BUILDDIR
"/" )) == 0 )
173 pathWarning
= "modified source in build dir : %0";
174 else if( strncmp( e
->getName(), SRCDIR
"/", strlen( SRCDIR
"/" )) == 0 )
178 pathWarning
= "modified source in unknown location, not modifying : %0";
181 if( modifyFile
.empty())
182 modifyFile
= e
->getName();
183 // Check whether the modified file is in the wanted scope
184 if( scope
== "mainfile" )
186 if( it
->first
!= context
.getSourceManager().getMainFileID())
189 else if( scope
== "all" )
191 else // scope is module
193 if( !( isPrefix( SRCDIR
"/" + scope
+ "/", modifyFile
) || isPrefix( SRCDIR
"/include/" + scope
+ "/", modifyFile
) ) )
196 // Warn only now, so that files not in scope do not cause warnings.
197 if( pathWarning
!= NULL
)
198 report( DiagnosticsEngine::Warning
, pathWarning
) << e
->getName();
201 char* filename
= new char[ modifyFile
.length() + 100 ];
202 sprintf( filename
, "%s.new.%d", modifyFile
.c_str(), getpid());
205 raw_fd_ostream
ostream( filename
, error
);
208 it
->second
.write( ostream
);
210 if( !ostream
.has_error() && rename( filename
, modifyFile
.c_str()) == 0 )
213 ostream
.clear_error();
216 report( DiagnosticsEngine::Error
, "cannot write modified source to %0 (%1)" ) << modifyFile
<< error
;
221 ASTConsumer
* LibreOfficeAction::CreateASTConsumer( CompilerInstance
& Compiler
, StringRef
)
223 return new PluginHandler( Compiler
, _args
);
226 bool LibreOfficeAction::ParseArgs( const CompilerInstance
&, const vector
< string
>& args
)
233 static FrontendPluginRegistry::Add
< loplugin::LibreOfficeAction
> X( "loplugin", "LibreOffice compile check plugin" );
237 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */