2012-10-05 18:17:13 +02: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.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2012-10-15 15:36:25 +02:00
|
|
|
#include "plugin.hxx"
|
2012-10-05 18:17:13 +02:00
|
|
|
|
2012-10-15 15:36:25 +02:00
|
|
|
#include <clang/Basic/FileManager.h>
|
2012-10-05 18:17:13 +02:00
|
|
|
|
2013-02-02 16:35:47 +01:00
|
|
|
#include "pluginhandler.hxx"
|
2012-10-09 14:50:19 +02:00
|
|
|
|
2013-02-02 18:34:12 +01:00
|
|
|
/*
|
|
|
|
Base classes for plugin actions.
|
|
|
|
*/
|
2012-10-05 18:17:13 +02:00
|
|
|
namespace loplugin
|
|
|
|
{
|
|
|
|
|
2013-03-21 16:42:10 +01:00
|
|
|
Plugin::Plugin( CompilerInstance& compiler )
|
|
|
|
: compiler( compiler )
|
2012-10-09 16:27:25 +02:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
DiagnosticBuilder Plugin::report( DiagnosticsEngine::Level level, StringRef message, SourceLocation loc )
|
2013-02-02 19:31:24 +01:00
|
|
|
{
|
2013-03-21 16:42:10 +01:00
|
|
|
return report( level, message, compiler, loc );
|
2013-02-02 19:31:24 +01:00
|
|
|
}
|
|
|
|
|
2013-03-21 16:42:10 +01:00
|
|
|
DiagnosticBuilder Plugin::report( DiagnosticsEngine::Level level, StringRef message, CompilerInstance& compiler,
|
2013-02-02 19:31:24 +01:00
|
|
|
SourceLocation loc )
|
2012-10-09 16:27:25 +02:00
|
|
|
{
|
2013-03-21 16:42:10 +01:00
|
|
|
DiagnosticsEngine& diag = compiler.getDiagnostics();
|
2012-10-09 16:28:13 +02:00
|
|
|
#if 0
|
|
|
|
// Do some mappings (e.g. for -Werror) that clang does not do for custom messages for some reason.
|
2012-10-09 16:27:25 +02:00
|
|
|
if( level == DiagnosticsEngine::Warning && diag.getWarningsAsErrors())
|
|
|
|
level = DiagnosticsEngine::Error;
|
|
|
|
if( level == DiagnosticsEngine::Error && diag.getErrorsAsFatal())
|
|
|
|
level = DiagnosticsEngine::Fatal;
|
2012-10-09 16:28:13 +02:00
|
|
|
#endif
|
2013-02-02 19:38:56 +01:00
|
|
|
string fullMessage = ( message + " [loplugin]" ).str();
|
2012-10-13 17:38:58 +02:00
|
|
|
if( loc.isValid())
|
2013-02-02 19:38:56 +01:00
|
|
|
return diag.Report( loc, diag.getCustomDiagID( level, fullMessage ));
|
2012-10-13 17:38:58 +02:00
|
|
|
else
|
2013-02-02 19:38:56 +01:00
|
|
|
return diag.Report( diag.getCustomDiagID( level, fullMessage ));
|
2012-10-09 16:27:25 +02:00
|
|
|
}
|
|
|
|
|
2012-10-09 16:39:49 +02:00
|
|
|
bool Plugin::ignoreLocation( SourceLocation loc )
|
|
|
|
{
|
2013-03-21 16:42:10 +01:00
|
|
|
SourceLocation expansionLoc = compiler.getSourceManager().getExpansionLoc( loc );
|
|
|
|
if( compiler.getSourceManager().isInSystemHeader( expansionLoc ))
|
2013-01-03 20:15:21 +01:00
|
|
|
return true;
|
2013-03-21 16:42:10 +01:00
|
|
|
const char* bufferName = compiler.getSourceManager().getPresumedLoc( expansionLoc ).getFilename();
|
2013-01-07 12:24:58 +01:00
|
|
|
if( bufferName == NULL )
|
2013-01-03 20:15:21 +01:00
|
|
|
return true;
|
2013-05-03 16:40:51 +02:00
|
|
|
if( strncmp( bufferName, OUTDIR, strlen( OUTDIR )) != 0
|
|
|
|
&& strncmp( bufferName, WORKDIR, strlen( WORKDIR )) != 0
|
|
|
|
&& strncmp( bufferName, BUILDDIR, strlen( BUILDDIR )) != 0
|
|
|
|
&& strncmp( bufferName, SRCDIR, strlen( SRCDIR )) != 0 )
|
|
|
|
return true; // not in LO sources
|
|
|
|
// Sometimes a VisitXXX function may be called more than once for one source location.
|
|
|
|
// This can happen e.g. with a default argument for a function, if it involves constructing
|
|
|
|
// a temporary, then this temporary will be constructed whenever the function is called
|
|
|
|
// and the default argument is needed. As this would mean processing the same piece of code
|
|
|
|
// more than once, and thus possibly modifying the source code more than once, just
|
|
|
|
// ignore an already seen location.
|
|
|
|
if( alreadySeen.find( loc ) != alreadySeen.end())
|
|
|
|
return true;
|
|
|
|
alreadySeen.insert( loc );
|
|
|
|
return false;
|
2012-10-09 16:39:49 +02:00
|
|
|
}
|
|
|
|
|
2013-03-21 16:42:10 +01:00
|
|
|
void Plugin::registerPlugin( Plugin* (*create)( CompilerInstance&, Rewriter& ), const char* optionName, bool isRewriter )
|
2013-02-02 17:45:18 +01:00
|
|
|
{
|
|
|
|
PluginHandler::registerPlugin( create, optionName, isRewriter );
|
|
|
|
}
|
|
|
|
|
|
|
|
/////
|
2012-10-15 17:34:13 +02:00
|
|
|
|
2013-03-21 16:42:10 +01:00
|
|
|
RewritePlugin::RewritePlugin( CompilerInstance& compiler, Rewriter& rewriter )
|
|
|
|
: Plugin( compiler )
|
2012-10-15 17:34:13 +02:00
|
|
|
, rewriter( rewriter )
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
bool RewritePlugin::insertText( SourceLocation Loc, StringRef Str, bool InsertAfter, bool indentNewLines )
|
|
|
|
{
|
|
|
|
if( rewriter.InsertText( Loc, Str, InsertAfter, indentNewLines ))
|
|
|
|
return reportEditFailure( Loc );
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool RewritePlugin::insertTextAfter( SourceLocation Loc, StringRef Str )
|
|
|
|
{
|
|
|
|
if( rewriter.InsertTextAfter( Loc, Str ))
|
|
|
|
return reportEditFailure( Loc );
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool RewritePlugin::insertTextAfterToken( SourceLocation Loc, StringRef Str )
|
|
|
|
{
|
|
|
|
if( rewriter.InsertTextAfterToken( Loc, Str ))
|
|
|
|
return reportEditFailure( Loc );
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool RewritePlugin::insertTextBefore( SourceLocation Loc, StringRef Str )
|
|
|
|
{
|
|
|
|
if( rewriter.InsertTextBefore( Loc, Str ))
|
|
|
|
return reportEditFailure( Loc );
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2013-05-03 14:08:48 +02:00
|
|
|
// These two removeText() overloads should not be merged into one, as the SourceRange
|
|
|
|
// one uses a token range (which counts token length for some reason), so exact length
|
|
|
|
// given to this overload would not match afterwards.
|
2012-10-15 17:34:13 +02:00
|
|
|
bool RewritePlugin::removeText( SourceLocation Start, unsigned Length, RewriteOptions opts )
|
|
|
|
{
|
2013-05-03 14:08:48 +02:00
|
|
|
if( opts.RemoveWholeStatement )
|
|
|
|
{
|
|
|
|
SourceRange range( Start, Start.getLocWithOffset( Length - 1 ));
|
|
|
|
if( !adjustForWholeStatement( &range ))
|
|
|
|
return reportEditFailure( Start );
|
|
|
|
Start = range.getBegin();
|
|
|
|
Length = range.getEnd().getRawEncoding() - range.getBegin().getRawEncoding();
|
|
|
|
}
|
|
|
|
if( rewriter.RemoveText( Start, Length, opts ))
|
|
|
|
return reportEditFailure( Start );
|
|
|
|
return true;
|
2012-10-15 17:34:13 +02:00
|
|
|
}
|
|
|
|
|
2012-12-20 23:08:52 +01:00
|
|
|
bool RewritePlugin::removeText( SourceRange range, RewriteOptions opts )
|
2012-10-15 17:34:13 +02:00
|
|
|
{
|
2012-12-20 23:08:52 +01:00
|
|
|
if( opts.RemoveWholeStatement )
|
|
|
|
{
|
|
|
|
if( !adjustForWholeStatement( &range ))
|
|
|
|
return reportEditFailure( range.getBegin());
|
|
|
|
}
|
2012-10-15 17:34:13 +02:00
|
|
|
if( rewriter.RemoveText( range, opts ))
|
|
|
|
return reportEditFailure( range.getBegin());
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2012-12-20 23:08:52 +01:00
|
|
|
bool RewritePlugin::adjustForWholeStatement( SourceRange* range )
|
2012-10-15 17:34:13 +02:00
|
|
|
{
|
2012-12-20 23:08:52 +01:00
|
|
|
SourceManager& SM = rewriter.getSourceMgr();
|
|
|
|
SourceLocation fileStartLoc = SM.getLocForStartOfFile( SM.getFileID( range->getBegin()));
|
|
|
|
if( fileStartLoc.isInvalid())
|
|
|
|
return false;
|
|
|
|
bool invalid = false;
|
|
|
|
const char* fileBuf = SM.getCharacterData( fileStartLoc, &invalid );
|
|
|
|
if( invalid )
|
|
|
|
return false;
|
|
|
|
const char* startBuf = SM.getCharacterData( range->getBegin(), &invalid );
|
|
|
|
if( invalid )
|
|
|
|
return false;
|
|
|
|
const char* endBuf = SM.getCharacterData( range->getEnd(), &invalid );
|
|
|
|
if( invalid )
|
|
|
|
return false;
|
|
|
|
const char* startSpacePos = startBuf;
|
|
|
|
// do not skip \n here, RemoveLineIfEmpty can take care of that
|
|
|
|
--startSpacePos;
|
|
|
|
while( startSpacePos >= fileBuf && ( *startSpacePos == ' ' || *startSpacePos == '\t' ))
|
|
|
|
--startSpacePos;
|
|
|
|
const char* semiPos = strchr( endBuf, ';' );
|
|
|
|
if( semiPos == NULL )
|
|
|
|
return false;
|
|
|
|
*range = SourceRange( range->getBegin().getLocWithOffset( startSpacePos - startBuf + 1 ),
|
|
|
|
range->getEnd().getLocWithOffset( semiPos - endBuf + 1 ));
|
2012-10-15 17:34:13 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool RewritePlugin::replaceText( SourceLocation Start, unsigned OrigLength, StringRef NewStr )
|
|
|
|
{
|
|
|
|
if( rewriter.ReplaceText( Start, OrigLength, NewStr ))
|
|
|
|
return reportEditFailure( Start );
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool RewritePlugin::replaceText( SourceRange range, StringRef NewStr )
|
|
|
|
{
|
|
|
|
if( rewriter.ReplaceText( range, NewStr ))
|
|
|
|
return reportEditFailure( range.getBegin());
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool RewritePlugin::replaceText( SourceRange range, SourceRange replacementRange )
|
|
|
|
{
|
|
|
|
if( rewriter.ReplaceText( range, replacementRange ))
|
|
|
|
return reportEditFailure( range.getBegin());
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool RewritePlugin::reportEditFailure( SourceLocation loc )
|
|
|
|
{
|
2013-02-02 19:38:56 +01:00
|
|
|
report( DiagnosticsEngine::Warning, "cannot perform source modification (macro expansion involved?)", loc );
|
2012-10-15 17:34:13 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2012-10-05 18:17:13 +02:00
|
|
|
} // namespace
|