diff options
Diffstat (limited to 'compilerplugins')
-rw-r--r-- | compilerplugins/clang/plugin.cxx | 64 | ||||
-rw-r--r-- | compilerplugins/clang/plugin.hxx | 63 |
2 files changed, 76 insertions, 51 deletions
diff --git a/compilerplugins/clang/plugin.cxx b/compilerplugins/clang/plugin.cxx index d647cb239aea..50ec0a9ced1b 100644 --- a/compilerplugins/clang/plugin.cxx +++ b/compilerplugins/clang/plugin.cxx @@ -11,6 +11,7 @@ #include "plugin.hxx" #include <clang/Basic/FileManager.h> +#include <clang/Lex/Lexer.h> #include "pluginhandler.hxx" @@ -180,35 +181,25 @@ bool RewritePlugin::insertTextBefore( SourceLocation Loc, StringRef Str ) return true; } -// 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. bool RewritePlugin::removeText( SourceLocation Start, unsigned Length, RewriteOptions opts ) { - if( removals.find( Start ) != removals.end()) - report( DiagnosticsEngine::Warning, "double code removal, possible plugin error", Start ); - removals.insert( Start ); - 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; + 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( removals.find( range.getBegin()) != removals.end()) report( DiagnosticsEngine::Warning, "double code removal, possible plugin error", range.getBegin()); removals.insert( range.getBegin()); - if( opts.RemoveWholeStatement ) + if( opts.flags & RemoveWholeStatement || opts.flags & RemoveAllWhitespace ) { - if( !adjustForWholeStatement( &range )) + if( !adjustRangeForOptions( &range, opts )) return reportEditFailure( range.getBegin()); } if( rewriter.RemoveText( range, opts )) @@ -216,7 +207,7 @@ bool RewritePlugin::removeText( SourceRange range, RewriteOptions opts ) return true; } -bool RewritePlugin::adjustForWholeStatement( SourceRange* range ) +bool RewritePlugin::adjustRangeForOptions( CharSourceRange* range, RewriteOptions opts ) { SourceManager& SM = rewriter.getSourceMgr(); SourceLocation fileStartLoc = SM.getLocForStartOfFile( SM.getFileID( range->getBegin())); @@ -229,19 +220,30 @@ bool RewritePlugin::adjustForWholeStatement( SourceRange* range ) const char* startBuf = SM.getCharacterData( range->getBegin(), &invalid ); if( invalid ) return false; - const char* endBuf = SM.getCharacterData( range->getEnd(), &invalid ); + SourceLocation locationEnd = range->getEnd(); + if( range->isTokenRange()) + locationEnd = Lexer::getLocForEndOfToken( locationEnd, 0, compiler.getSourceManager(), compiler.getLangOpts()); + const char* endBuf = SM.getCharacterData( locationEnd, &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 )); + 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; } diff --git a/compilerplugins/clang/plugin.hxx b/compilerplugins/clang/plugin.hxx index 0b6cb1f693ff..4fd766d8d2a0 100644 --- a/compilerplugins/clang/plugin.hxx +++ b/compilerplugins/clang/plugin.hxx @@ -81,39 +81,41 @@ class RewritePlugin public: explicit RewritePlugin( CompilerInstance& compiler, Rewriter& rewriter ); protected: - // This enum allows passing just 'RemoveLineIfEmpty' to functions below. - enum RemoveLineIfEmpty_t { RemoveLineIfEmpty }; - // Use this to remove the declaration/statement as a whole, i.e. all whitespace before the statement - // and the trailing semicolor (is not part of the AST element range itself). - // The trailing semicolon must be present. - enum RemoveWholeStatement_t { RemoveWholeStatement }; - enum RemoveLineIfEmptyAndWholeStatement_t { RemoveLineIfEmptyAndWholeStatement }; - // syntactic sugar to be able to write 'RemoveLineIfEmpty | RemoveWholeStatement' - friend RemoveLineIfEmptyAndWholeStatement_t operator|( RemoveLineIfEmpty_t, RemoveWholeStatement_t ) - { return RemoveLineIfEmptyAndWholeStatement; } + enum RewriteOption + { + // This enum allows passing just 'RemoveLineIfEmpty' to functions below. + // If the resulting line would be completely empty, it'll be removed. + RemoveLineIfEmpty = 1 << 0, + // Use this to remove the declaration/statement as a whole, i.e. all whitespace before the statement + // and the trailing semicolor (is not part of the AST element range itself). + // The trailing semicolon must be present. + RemoveWholeStatement = 1 << 1, + // Removes also all whitespace preceding and following the expression (completely, so that + // the preceding and following tokens would be right next to each other, follow with insertText( " " ) + // if this is not wanted). Despite the name, indentation whitespace is not removed. + RemoveAllWhitespace = 1 << 2 + }; struct RewriteOptions : public Rewriter::RewriteOptions { - RewriteOptions() : RemoveWholeStatement( false ) {} // default - RewriteOptions( RemoveLineIfEmpty_t ) : RemoveWholeStatement( false ) { RemoveLineIfEmpty = true; } - RewriteOptions( RemoveWholeStatement_t ) : RemoveWholeStatement( true ) {} - RewriteOptions( RemoveLineIfEmptyAndWholeStatement_t ) : RemoveWholeStatement( true ) { RemoveLineIfEmpty = true; } - bool RemoveWholeStatement; + RewriteOptions(); + RewriteOptions( RewriteOption option ); + const int flags; }; + // syntactic sugar to be able to write 'RemoveLineIfEmpty | RemoveWholeStatement' + friend RewriteOption operator|( RewriteOption option1, RewriteOption option2 ); // These following insert/remove/replaceText functions map to functions // in clang::Rewriter, with these differences: // - they (more intuitively) return false on failure rather than true // - they report a warning when the change cannot be done - // - There is RemoveWholeStatement to also remove the trailing semicolon when removing (must be there) - // and al preceding whitespace. + // - There are more options for easier removal of surroundings of a statement/expression. bool insertText( SourceLocation Loc, StringRef Str, bool InsertAfter = true, bool indentNewLines = false ); bool insertTextAfter( SourceLocation Loc, StringRef Str ); bool insertTextAfterToken( SourceLocation Loc, StringRef Str ); bool insertTextBefore( SourceLocation Loc, StringRef Str ); bool removeText( SourceLocation Start, unsigned Length, RewriteOptions opts = RewriteOptions()); - // CharSourceRange not supported, unless really needed, as it needs handling for RemoveWholeStatement. - //bool removeText( CharSourceRange range, RewriteOptions opts = RewriteOptions()); + bool removeText( CharSourceRange range, RewriteOptions opts = RewriteOptions()); bool removeText( SourceRange range, RewriteOptions opts = RewriteOptions()); bool replaceText( SourceLocation Start, unsigned OrigLength, StringRef NewStr ); bool replaceText( SourceRange range, StringRef NewStr ); @@ -124,7 +126,7 @@ class RewritePlugin template< typename T > static Plugin* createHelper( CompilerInstance& compiler, Rewriter& rewriter ); enum { isRewriter = true }; bool reportEditFailure( SourceLocation loc ); - bool adjustForWholeStatement( SourceRange* range ); + bool adjustRangeForOptions( CharSourceRange* range, RewriteOptions options ); set< SourceLocation > removals; }; @@ -190,6 +192,27 @@ Plugin::Registration< T >::Registration( const char* optionName ) registerPlugin( &T::template createHelper< T >, optionName, T::isRewriter, T::isPPCallback ); } +inline +RewritePlugin::RewriteOptions::RewriteOptions() + : flags( 0 ) + { + } + +inline +RewritePlugin::RewriteOptions::RewriteOptions( RewriteOption option ) + : flags( option ) + { + // Note that 'flags' stores also RemoveLineIfEmpty, it must be kept in sync with the base class. + if( flags & RewritePlugin::RemoveLineIfEmpty ) + this->RemoveLineIfEmpty = true; + } + +inline +RewritePlugin::RewriteOption operator|( RewritePlugin::RewriteOption option1, RewritePlugin::RewriteOption option2 ) + { + return static_cast< RewritePlugin::RewriteOption >( int( option1 ) | int( option2 )); + } + } // namespace #endif // COMPILEPLUGIN_H |