/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * 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. * */ #include "plugin.hxx" #include #include #include "pluginhandler.hxx" /* Base classes for plugin actions. */ namespace loplugin { Plugin::Plugin( CompilerInstance& compiler ) : compiler( compiler ) { } DiagnosticBuilder Plugin::report( DiagnosticsEngine::Level level, StringRef message, SourceLocation loc ) { return report( level, message, compiler, loc ); } DiagnosticBuilder Plugin::report( DiagnosticsEngine::Level level, 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. if( level == DiagnosticsEngine::Warning && diag.getWarningsAsErrors()) level = DiagnosticsEngine::Error; if( level == DiagnosticsEngine::Error && diag.getErrorsAsFatal()) level = DiagnosticsEngine::Fatal; string fullMessage = ( message + " [loplugin]" ).str(); if( loc.isValid()) return diag.Report( loc, diag.getCustomDiagID( level, fullMessage )); else return diag.Report( diag.getCustomDiagID( level, fullMessage )); } bool Plugin::ignoreLocation( SourceLocation loc ) { SourceLocation expansionLoc = compiler.getSourceManager().getExpansionLoc( loc ); if( compiler.getSourceManager().isInSystemHeader( expansionLoc )) return true; const char* bufferName = compiler.getSourceManager().getPresumedLoc( expansionLoc ).getFilename(); if( bufferName == NULL ) return true; if( strncmp( bufferName, WORKDIR, strlen( WORKDIR )) == 0 || strncmp( bufferName, BUILDDIR, strlen( BUILDDIR )) == 0 || strncmp( bufferName, SRCDIR, strlen( SRCDIR )) == 0 ) return false; // ok return true; } void Plugin::registerPlugin( Plugin* (*create)( CompilerInstance&, Rewriter& ), const char* optionName, bool isRewriter, bool isPPCallback ) { PluginHandler::registerPlugin( create, optionName, isRewriter, isPPCallback ); } unordered_map< const Stmt*, const Stmt* > Plugin::parents; const Stmt* Plugin::parentStmt( const Stmt* stmt ) { if( parents.empty()) buildParents( compiler ); assert( parents.count( stmt ) == 1 ); return parents[ stmt ]; } Stmt* Plugin::parentStmt( Stmt* stmt ) { if( parents.empty()) buildParents( compiler ); assert( parents.count( stmt ) == 1 ); return const_cast< Stmt* >( parents[ stmt ] ); } namespace { class ParentBuilder : public RecursiveASTVisitor< ParentBuilder > { public: bool VisitFunctionDecl( const FunctionDecl* function ); void walk( const Stmt* stmt ); unordered_map< const Stmt*, const Stmt* >* parents; }; bool ParentBuilder::VisitFunctionDecl( const FunctionDecl* function ) { // if( ignoreLocation( declaration )) // return true; ??? if( function->doesThisDeclarationHaveABody()) { const Stmt* body = function->getBody(); (*parents)[ body ] = NULL; // no parent walk( body ); } if( const CXXConstructorDecl* ctor = dyn_cast< CXXConstructorDecl >( function )) { for( CXXConstructorDecl::init_const_iterator it = ctor->init_begin(); it != ctor->init_end(); ++it ) { const Expr* init_expression = (*it)->getInit(); (*parents)[ init_expression ] = NULL; walk( init_expression ); } } return true; } void ParentBuilder::walk( const Stmt* stmt ) { for( ConstStmtIterator it = stmt->child_begin(); it != stmt->child_end(); ++it ) { if( *it != NULL ) { (*parents)[ *it ] = stmt; walk( *it ); } } } } // namespace void Plugin::buildParents( CompilerInstance& compiler ) { assert( parents.empty()); ParentBuilder builder; builder.parents = &parents; builder.TraverseDecl( compiler.getASTContext().getTranslationUnitDecl()); } SourceLocation Plugin::locationAfterToken( SourceLocation location ) { return Lexer::getLocForEndOfToken( location, 0, compiler.getSourceManager(), compiler.getLangOpts()); } ///// RewritePlugin::RewritePlugin( CompilerInstance& compiler, Rewriter& rewriter ) : Plugin( compiler ) , 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; } bool RewritePlugin::removeText( SourceLocation Start, unsigned Length, RewriteOptions opts ) { CharSourceRange range( SourceRange( Start, Start.getLocWithOffset( Length )), false ); return removeText( range, opts ); } bool RewritePlugin::removeText( SourceRange range, RewriteOptions opts ) { return removeText( CharSourceRange( range, true ), opts ); } bool RewritePlugin::removeText( CharSourceRange range, RewriteOptions opts ) { if( rewriter.getRangeSize( range, opts ) == -1 ) return reportEditFailure( range.getBegin()); if( removals.find( range.getBegin()) != removals.end()) report( DiagnosticsEngine::Warning, "double code removal, possible plugin error", range.getBegin()); removals.insert( range.getBegin()); if( opts.flags & RemoveWholeStatement || opts.flags & RemoveAllWhitespace ) { if( !adjustRangeForOptions( &range, opts )) return reportEditFailure( range.getBegin()); } if( rewriter.RemoveText( range, opts )) return reportEditFailure( range.getBegin()); return true; } bool RewritePlugin::adjustRangeForOptions( CharSourceRange* range, RewriteOptions opts ) { 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; SourceLocation locationEnd = range->getEnd(); if( range->isTokenRange()) locationEnd = locationAfterToken( locationEnd ); const char* endBuf = SM.getCharacterData( locationEnd, &invalid ); if( invalid ) return false; const char* startPos = startBuf; --startPos; while( startPos >= fileBuf && ( *startPos == ' ' || *startPos == '\t' )) --startPos; if( startPos >= fileBuf && *startPos == '\n' ) startPos = startBuf - 1; // do not remove indentation whitespace (RemoveLineIfEmpty can do that) const char* endPos = endBuf; while( *endPos == ' ' || *endPos == '\t' ) ++endPos; if( opts.flags & RemoveWholeStatement ) { if( *endPos == ';' ) ++endPos; else return false; } *range = CharSourceRange( SourceRange( range->getBegin().getLocWithOffset( startPos - startBuf + 1 ), locationEnd.getLocWithOffset( endPos - endBuf )), false ); return true; } bool RewritePlugin::replaceText( SourceLocation Start, unsigned OrigLength, StringRef NewStr ) { if( OrigLength != 0 && removals.find( Start ) != removals.end()) report( DiagnosticsEngine::Warning, "double code replacement, possible plugin error", Start ); removals.insert( Start ); if( rewriter.ReplaceText( Start, OrigLength, NewStr )) return reportEditFailure( Start ); return true; } bool RewritePlugin::replaceText( SourceRange range, StringRef NewStr ) { if( rewriter.getRangeSize( range ) == -1 ) return reportEditFailure( range.getBegin()); if( removals.find( range.getBegin()) != removals.end()) report( DiagnosticsEngine::Warning, "double code replacement, possible plugin error", range.getBegin()); removals.insert( range.getBegin()); if( rewriter.ReplaceText( range, NewStr )) return reportEditFailure( range.getBegin()); return true; } bool RewritePlugin::replaceText( SourceRange range, SourceRange replacementRange ) { if( rewriter.getRangeSize( range ) == -1 ) return reportEditFailure( range.getBegin()); if( removals.find( range.getBegin()) != removals.end()) report( DiagnosticsEngine::Warning, "double code replacement, possible plugin error", range.getBegin()); removals.insert( range.getBegin()); if( rewriter.ReplaceText( range, replacementRange )) return reportEditFailure( range.getBegin()); return true; } bool RewritePlugin::reportEditFailure( SourceLocation loc ) { report( DiagnosticsEngine::Warning, "cannot perform source modification (macro expansion involved?)", loc ); return false; } } // namespace /* vim:set shiftwidth=4 softtabstop=4 expandtab: */