2013-09-21 14:42:35 +01:00
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2013-02-02 16:35:47 +01:00
/*
* This file is part of the LibreOffice project .
*
* Based on LLVM / Clang .
*
* This file is distributed under the University of Illinois Open Source
* License . See LICENSE . TXT for details .
*
*/
2014-01-31 09:50:21 +01:00
# include "compat.hxx"
2013-02-02 16:35:47 +01:00
# include "pluginhandler.hxx"
# include <clang/Frontend/CompilerInstance.h>
# include <clang/Frontend/FrontendPluginRegistry.h>
2013-04-04 12:52:04 +02:00
# include <clang/Lex/PPCallbacks.h>
2013-02-02 16:35:47 +01:00
# include <stdio.h>
2013-02-09 18:47:55 +01:00
# include <sys/stat.h>
2013-02-02 16:35:47 +01:00
# include <unistd.h>
2013-02-02 18:34:12 +01:00
/*
This source file manages all plugin actions . It is not necessary to modify this
file when adding new actions .
*/
2013-06-05 23:32:16 +02:00
namespace
{
bool isPrefix ( const string & prefix , const string & full )
{
return full . compare ( 0 , prefix . size ( ) , prefix ) = = 0 ;
}
}
2013-02-02 16:35:47 +01:00
namespace loplugin
{
2013-02-02 17:45:18 +01:00
struct PluginData
{
2014-01-27 13:09:20 +01:00
Plugin * ( * create ) ( const Plugin : : InstantiationData & ) ;
2013-02-02 17:45:18 +01:00
Plugin * object ;
const char * optionName ;
2013-08-06 17:57:45 +02:00
bool isPPCallback ;
2014-01-27 13:09:20 +01:00
bool byDefault ;
2013-02-02 17:45:18 +01:00
} ;
const int MAX_PLUGINS = 100 ;
static PluginData plugins [ MAX_PLUGINS ] ;
static int pluginCount = 0 ;
2015-09-25 11:41:53 +01:00
static bool bPluginObjectsCreated = false ;
2013-02-02 17:45:18 +01:00
2013-03-21 16:42:10 +01:00
PluginHandler : : PluginHandler ( CompilerInstance & compiler , const vector < string > & args )
: compiler ( compiler )
, rewriter ( compiler . getSourceManager ( ) , compiler . getLangOpts ( ) )
2013-02-09 18:47:55 +01:00
, scope ( " mainfile " )
2016-04-26 17:33:47 +02:00
, warningsAsErrors ( false )
2013-02-02 16:35:47 +01:00
{
2014-02-15 15:25:03 +01:00
set < string > rewriters ;
2013-02-09 18:47:55 +01:00
for ( vector < string > : : const_iterator it = args . begin ( ) ;
it ! = args . end ( ) ;
+ + it )
2013-02-02 17:45:18 +01:00
{
2013-02-09 18:47:55 +01:00
if ( it - > size ( ) > = 2 & & ( * it ) [ 0 ] = = ' - ' & & ( * it ) [ 1 ] = = ' - ' )
handleOption ( it - > substr ( 2 ) ) ;
else
2014-02-15 15:25:03 +01:00
rewriters . insert ( * it ) ;
2013-02-02 17:45:18 +01:00
}
2014-02-15 15:25:03 +01:00
createPlugins ( rewriters ) ;
2015-09-25 11:41:53 +01:00
bPluginObjectsCreated = true ;
2013-02-02 17:45:18 +01:00
}
PluginHandler : : ~ PluginHandler ( )
{
for ( int i = 0 ;
i < pluginCount ;
+ + i )
if ( plugins [ i ] . object ! = NULL )
2013-04-04 12:52:04 +02:00
{
// PPCallbacks is owned by preprocessor object, don't delete those
2013-08-06 17:57:45 +02:00
if ( ! plugins [ i ] . isPPCallback )
2013-04-04 12:52:04 +02:00
delete plugins [ i ] . object ;
}
2013-02-02 17:45:18 +01:00
}
2013-02-09 18:47:55 +01:00
void PluginHandler : : handleOption ( const string & option )
{
if ( option . substr ( 0 , 6 ) = = " scope= " )
{
scope = option . substr ( 6 ) ;
if ( scope = = " mainfile " | | scope = = " all " )
; // ok
else
{
struct stat st ;
if ( stat ( ( SRCDIR " / " + scope ) . c_str ( ) , & st ) ! = 0 | | ! S_ISDIR ( st . st_mode ) )
report ( DiagnosticsEngine : : Fatal , " unknown scope %0 (no such module directory) " ) < < scope ;
}
}
2014-01-27 13:09:20 +01:00
else if ( option . substr ( 0 , 14 ) = = " warnings-only= " )
{
warningsOnly = option . substr ( 14 ) ;
}
2016-04-26 17:33:47 +02:00
else if ( option = = " warnings-as-errors " )
warningsAsErrors = true ;
2013-02-09 18:47:55 +01:00
else
report ( DiagnosticsEngine : : Fatal , " unknown option %0 " ) < < option ;
}
2014-02-15 15:25:03 +01:00
void PluginHandler : : createPlugins ( set < string > rewriters )
2013-02-09 18:47:55 +01:00
{
for ( int i = 0 ;
i < pluginCount ;
+ + i )
{
2014-02-15 15:25:03 +01:00
if ( rewriters . erase ( plugins [ i ] . optionName ) ! = 0 )
plugins [ i ] . object = plugins [ i ] . create ( Plugin : : InstantiationData { plugins [ i ] . optionName , * this , compiler , & rewriter } ) ;
else if ( plugins [ i ] . byDefault )
plugins [ i ] . object = plugins [ i ] . create ( Plugin : : InstantiationData { plugins [ i ] . optionName , * this , compiler , NULL } ) ;
2013-02-09 18:47:55 +01:00
}
2014-02-15 15:25:03 +01:00
for ( auto r : rewriters )
report ( DiagnosticsEngine : : Fatal , " unknown plugin tool %0 " ) < < r ;
2013-02-09 18:47:55 +01:00
}
2014-01-27 13:09:20 +01:00
void PluginHandler : : registerPlugin ( Plugin * ( * create ) ( const Plugin : : InstantiationData & ) , const char * optionName , bool isPPCallback , bool byDefault )
2013-02-02 17:45:18 +01:00
{
2015-09-25 11:41:53 +01:00
assert ( ! bPluginObjectsCreated ) ;
2013-02-02 17:45:18 +01:00
assert ( pluginCount < MAX_PLUGINS ) ;
plugins [ pluginCount ] . create = create ;
plugins [ pluginCount ] . object = NULL ;
plugins [ pluginCount ] . optionName = optionName ;
2013-08-06 17:57:45 +02:00
plugins [ pluginCount ] . isPPCallback = isPPCallback ;
2014-01-27 13:09:20 +01:00
plugins [ pluginCount ] . byDefault = byDefault ;
2013-02-02 17:45:18 +01:00
+ + pluginCount ;
2013-02-02 16:35:47 +01:00
}
2014-01-27 13:09:20 +01:00
DiagnosticBuilder PluginHandler : : report ( DiagnosticsEngine : : Level level , const char * plugin , StringRef message , CompilerInstance & compiler ,
SourceLocation loc )
{
DiagnosticsEngine & diag = compiler . getDiagnostics ( ) ;
// Do some mappings (e.g. for -Werror) that clang does not do for custom messages for some reason.
2016-04-26 17:33:47 +02:00
if ( level = = DiagnosticsEngine : : Warning & & ( ( diag . getWarningsAsErrors ( ) & & ( plugin = = nullptr | | plugin ! = warningsOnly ) ) | | warningsAsErrors ) )
2014-01-27 13:09:20 +01:00
level = DiagnosticsEngine : : Error ;
if ( level = = DiagnosticsEngine : : Error & & diag . getErrorsAsFatal ( ) )
level = DiagnosticsEngine : : Fatal ;
string fullMessage = ( message + " [loplugin " ) . str ( ) ;
if ( plugin )
{
fullMessage + = " : " ;
fullMessage + = plugin ;
}
fullMessage + = " ] " ;
if ( loc . isValid ( ) )
2014-01-31 09:50:21 +01:00
return diag . Report ( loc , compat : : getCustomDiagID ( diag , level , fullMessage ) ) ;
2014-01-27 13:09:20 +01:00
else
2014-01-31 09:50:21 +01:00
return diag . Report ( compat : : getCustomDiagID ( diag , level , fullMessage ) ) ;
2014-01-27 13:09:20 +01:00
}
2013-02-02 19:31:24 +01:00
DiagnosticBuilder PluginHandler : : report ( DiagnosticsEngine : : Level level , StringRef message , SourceLocation loc )
{
2014-01-27 13:09:20 +01:00
return report ( level , nullptr , message , compiler , loc ) ;
2013-02-02 19:31:24 +01:00
}
2014-02-20 19:47:01 +01:00
bool PluginHandler : : addRemoval ( SourceLocation loc )
{
return removals . insert ( loc ) . second ;
}
2013-02-02 16:35:47 +01:00
void PluginHandler : : HandleTranslationUnit ( ASTContext & context )
{
if ( context . getDiagnostics ( ) . hasErrorOccurred ( ) )
return ;
2015-10-30 15:15:46 +01:00
char const * const mainFileName = context . getSourceManager ( ) . getFileEntryForID ( context . getSourceManager ( ) . getMainFileID ( ) ) - > getName ( ) ;
size_t const len = strlen ( mainFileName ) ;
if ( len > 3 & & strncmp ( mainFileName + len - 3 , " .ii " , 3 ) = = 0 )
{
report ( DiagnosticsEngine : : Fatal ,
" input file has suffix .ii: \" %0 \" \n highly suspicious, probably ccache generated, this will break warning suppressions; export CCACHE_CPP2=1 to prevent this " ) < < mainFileName ;
return ;
}
2013-02-02 17:45:18 +01:00
for ( int i = 0 ;
i < pluginCount ;
+ + i )
2013-02-02 16:35:47 +01:00
{
2013-02-02 17:45:18 +01:00
if ( plugins [ i ] . object ! = NULL )
plugins [ i ] . object - > run ( ) ;
2013-02-02 16:35:47 +01:00
}
for ( Rewriter : : buffer_iterator it = rewriter . buffer_begin ( ) ;
it ! = rewriter . buffer_end ( ) ;
+ + it )
{
const FileEntry * e = context . getSourceManager ( ) . getFileEntryForID ( it - > first ) ;
2013-08-14 19:00:21 +02:00
if ( e = = NULL )
continue ; // Failed modification because of a macro expansion?
2013-02-02 16:35:47 +01:00
/* Check where the file actually is, and warn about cases where modification
most probably doesn ' t matter ( generated files in workdir ) .
2013-10-31 14:02:40 +01:00
The order here is important , as INSTDIR and WORKDIR are often in SRCDIR / BUILDDIR ,
2013-02-02 16:35:47 +01:00
and BUILDDIR is sometimes in SRCDIR . */
string modifyFile ;
2013-02-09 18:47:55 +01:00
const char * pathWarning = NULL ;
2015-09-25 11:41:53 +01:00
bool bSkip = false ;
2013-10-31 14:02:40 +01:00
if ( strncmp ( e - > getName ( ) , WORKDIR " / " , strlen ( WORKDIR " / " ) ) = = 0 )
2013-02-09 18:47:55 +01:00
pathWarning = " modified source in workdir/ : %0 " ;
2013-02-09 18:31:38 +01:00
else if ( strcmp ( SRCDIR , BUILDDIR ) ! = 0 & & strncmp ( e - > getName ( ) , BUILDDIR " / " , strlen ( BUILDDIR " / " ) ) = = 0 )
2013-02-09 18:47:55 +01:00
pathWarning = " modified source in build dir : %0 " ;
2013-02-09 18:31:38 +01:00
else if ( strncmp ( e - > getName ( ) , SRCDIR " / " , strlen ( SRCDIR " / " ) ) = = 0 )
2013-02-02 16:35:47 +01:00
; // ok
else
{
2013-02-09 18:47:55 +01:00
pathWarning = " modified source in unknown location, not modifying : %0 " ;
2015-09-25 11:41:53 +01:00
bSkip = true ;
2013-02-02 16:35:47 +01:00
}
if ( modifyFile . empty ( ) )
modifyFile = e - > getName ( ) ;
2013-10-31 14:02:40 +01:00
// Check whether the modified file is in the wanted scope
2013-02-09 18:47:55 +01:00
if ( scope = = " mainfile " )
{
if ( it - > first ! = context . getSourceManager ( ) . getMainFileID ( ) )
continue ;
}
else if ( scope = = " all " )
; // ok
else // scope is module
{
2013-06-05 23:32:16 +02:00
if ( ! ( isPrefix ( SRCDIR " / " + scope + " / " , modifyFile ) | | isPrefix ( SRCDIR " /include/ " + scope + " / " , modifyFile ) ) )
2013-02-09 18:47:55 +01:00
continue ;
}
// Warn only now, so that files not in scope do not cause warnings.
if ( pathWarning ! = NULL )
report ( DiagnosticsEngine : : Warning , pathWarning ) < < e - > getName ( ) ;
2015-09-25 11:41:53 +01:00
if ( bSkip )
2013-02-09 18:47:55 +01:00
continue ;
2013-02-02 16:35:47 +01:00
char * filename = new char [ modifyFile . length ( ) + 100 ] ;
sprintf ( filename , " %s.new.%d " , modifyFile . c_str ( ) , getpid ( ) ) ;
string error ;
2015-09-25 11:41:53 +01:00
bool bOk = false ;
2014-02-25 10:15:28 +01:00
std : : unique_ptr < raw_fd_ostream > ostream (
compat : : create_raw_fd_ostream ( filename , error ) ) ;
2013-02-02 16:35:47 +01:00
if ( error . empty ( ) )
{
2014-02-25 10:15:28 +01:00
it - > second . write ( * ostream ) ;
ostream - > close ( ) ;
if ( ! ostream - > has_error ( ) & & rename ( filename , modifyFile . c_str ( ) ) = = 0 )
2015-09-25 11:41:53 +01:00
bOk = true ;
2013-02-02 16:35:47 +01:00
}
2014-02-25 10:15:28 +01:00
ostream - > clear_error ( ) ;
2013-02-02 16:35:47 +01:00
unlink ( filename ) ;
2015-09-25 11:41:53 +01:00
if ( ! bOk )
2013-02-02 19:38:56 +01:00
report ( DiagnosticsEngine : : Error , " cannot write modified source to %0 (%1) " ) < < modifyFile < < error ;
2013-02-02 16:35:47 +01:00
delete [ ] filename ;
}
}
2016-02-26 12:50:16 +01:00
# if CLANG_VERSION >= 30600
2014-08-11 15:16:08 +02:00
std : : unique_ptr < ASTConsumer > LibreOfficeAction : : CreateASTConsumer ( CompilerInstance & Compiler , StringRef )
{
2015-09-29 15:04:06 +02:00
return llvm : : make_unique < PluginHandler > ( Compiler , _args ) ;
2014-08-11 15:16:08 +02:00
}
# else
2013-03-21 16:17:07 +01:00
ASTConsumer * LibreOfficeAction : : CreateASTConsumer ( CompilerInstance & Compiler , StringRef )
2013-02-02 16:35:47 +01:00
{
2013-03-21 16:42:10 +01:00
return new PluginHandler ( Compiler , _args ) ;
2013-02-02 16:35:47 +01:00
}
2014-08-11 15:16:08 +02:00
# endif
2013-02-02 16:35:47 +01:00
2013-03-21 16:17:07 +01:00
bool LibreOfficeAction : : ParseArgs ( const CompilerInstance & , const vector < string > & args )
2013-02-02 16:35:47 +01:00
{
_args = args ;
return true ;
}
static FrontendPluginRegistry : : Add < loplugin : : LibreOfficeAction > X ( " loplugin " , " LibreOffice compile check plugin " ) ;
} // namespace
2013-09-21 14:42:35 +01:00
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */