summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--compilerplugins/clang/plugin.cxx64
-rw-r--r--compilerplugins/clang/plugin.hxx63
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