Kaydet (Commit) 0809d7d7 authored tarafından Luboš Luňák's avatar Luboš Luňák

update/rework the Rewriter wrapper functions

Some improvements, like making it simple to actually remove a statement
or a token including its associated whitespace.

Change-Id: I02a5bd919f1fadae1dcd45a76f9d25df353ac518
üst 0b99537e
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include "plugin.hxx" #include "plugin.hxx"
#include <clang/Basic/FileManager.h> #include <clang/Basic/FileManager.h>
#include <clang/Lex/Lexer.h>
#include "pluginhandler.hxx" #include "pluginhandler.hxx"
...@@ -180,35 +181,25 @@ bool RewritePlugin::insertTextBefore( SourceLocation Loc, StringRef Str ) ...@@ -180,35 +181,25 @@ bool RewritePlugin::insertTextBefore( SourceLocation Loc, StringRef Str )
return true; 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 ) bool RewritePlugin::removeText( SourceLocation Start, unsigned Length, RewriteOptions opts )
{ {
if( removals.find( Start ) != removals.end()) CharSourceRange range( SourceRange( Start, Start.getLocWithOffset( Length )), false );
report( DiagnosticsEngine::Warning, "double code removal, possible plugin error", Start ); return removeText( range, opts );
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;
} }
bool RewritePlugin::removeText( SourceRange range, RewriteOptions 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()) if( removals.find( range.getBegin()) != removals.end())
report( DiagnosticsEngine::Warning, "double code removal, possible plugin error", range.getBegin()); report( DiagnosticsEngine::Warning, "double code removal, possible plugin error", range.getBegin());
removals.insert( 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()); return reportEditFailure( range.getBegin());
} }
if( rewriter.RemoveText( range, opts )) if( rewriter.RemoveText( range, opts ))
...@@ -216,7 +207,7 @@ bool RewritePlugin::removeText( SourceRange range, RewriteOptions opts ) ...@@ -216,7 +207,7 @@ bool RewritePlugin::removeText( SourceRange range, RewriteOptions opts )
return true; return true;
} }
bool RewritePlugin::adjustForWholeStatement( SourceRange* range ) bool RewritePlugin::adjustRangeForOptions( CharSourceRange* range, RewriteOptions opts )
{ {
SourceManager& SM = rewriter.getSourceMgr(); SourceManager& SM = rewriter.getSourceMgr();
SourceLocation fileStartLoc = SM.getLocForStartOfFile( SM.getFileID( range->getBegin())); SourceLocation fileStartLoc = SM.getLocForStartOfFile( SM.getFileID( range->getBegin()));
...@@ -229,19 +220,30 @@ bool RewritePlugin::adjustForWholeStatement( SourceRange* range ) ...@@ -229,19 +220,30 @@ bool RewritePlugin::adjustForWholeStatement( SourceRange* range )
const char* startBuf = SM.getCharacterData( range->getBegin(), &invalid ); const char* startBuf = SM.getCharacterData( range->getBegin(), &invalid );
if( invalid ) if( invalid )
return false; 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 ) if( invalid )
return false; return false;
const char* startSpacePos = startBuf; const char* startPos = startBuf;
// do not skip \n here, RemoveLineIfEmpty can take care of that --startPos;
--startSpacePos; while( startPos >= fileBuf && ( *startPos == ' ' || *startPos == '\t' ))
while( startSpacePos >= fileBuf && ( *startSpacePos == ' ' || *startSpacePos == '\t' )) --startPos;
--startSpacePos; if( startPos >= fileBuf && *startPos == '\n' )
const char* semiPos = strchr( endBuf, ';' ); startPos = startBuf - 1; // do not remove indentation whitespace (RemoveLineIfEmpty can do that)
if( semiPos == NULL ) const char* endPos = endBuf;
return false; while( *endPos == ' ' || *endPos == '\t' )
*range = SourceRange( range->getBegin().getLocWithOffset( startSpacePos - startBuf + 1 ), ++endPos;
range->getEnd().getLocWithOffset( semiPos - endBuf + 1 )); 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; return true;
} }
......
...@@ -81,39 +81,41 @@ class RewritePlugin ...@@ -81,39 +81,41 @@ class RewritePlugin
public: public:
explicit RewritePlugin( CompilerInstance& compiler, Rewriter& rewriter ); explicit RewritePlugin( CompilerInstance& compiler, Rewriter& rewriter );
protected: protected:
// This enum allows passing just 'RemoveLineIfEmpty' to functions below. enum RewriteOption
enum RemoveLineIfEmpty_t { RemoveLineIfEmpty }; {
// Use this to remove the declaration/statement as a whole, i.e. all whitespace before the statement // This enum allows passing just 'RemoveLineIfEmpty' to functions below.
// and the trailing semicolor (is not part of the AST element range itself). // If the resulting line would be completely empty, it'll be removed.
// The trailing semicolon must be present. RemoveLineIfEmpty = 1 << 0,
enum RemoveWholeStatement_t { RemoveWholeStatement }; // Use this to remove the declaration/statement as a whole, i.e. all whitespace before the statement
enum RemoveLineIfEmptyAndWholeStatement_t { RemoveLineIfEmptyAndWholeStatement }; // and the trailing semicolor (is not part of the AST element range itself).
// syntactic sugar to be able to write 'RemoveLineIfEmpty | RemoveWholeStatement' // The trailing semicolon must be present.
friend RemoveLineIfEmptyAndWholeStatement_t operator|( RemoveLineIfEmpty_t, RemoveWholeStatement_t ) RemoveWholeStatement = 1 << 1,
{ return RemoveLineIfEmptyAndWholeStatement; } // 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 struct RewriteOptions
: public Rewriter::RewriteOptions : public Rewriter::RewriteOptions
{ {
RewriteOptions() : RemoveWholeStatement( false ) {} // default RewriteOptions();
RewriteOptions( RemoveLineIfEmpty_t ) : RemoveWholeStatement( false ) { RemoveLineIfEmpty = true; } RewriteOptions( RewriteOption option );
RewriteOptions( RemoveWholeStatement_t ) : RemoveWholeStatement( true ) {} const int flags;
RewriteOptions( RemoveLineIfEmptyAndWholeStatement_t ) : RemoveWholeStatement( true ) { RemoveLineIfEmpty = true; }
bool RemoveWholeStatement;
}; };
// 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 // These following insert/remove/replaceText functions map to functions
// in clang::Rewriter, with these differences: // in clang::Rewriter, with these differences:
// - they (more intuitively) return false on failure rather than true // - they (more intuitively) return false on failure rather than true
// - they report a warning when the change cannot be done // - they report a warning when the change cannot be done
// - There is RemoveWholeStatement to also remove the trailing semicolon when removing (must be there) // - There are more options for easier removal of surroundings of a statement/expression.
// and al preceding whitespace.
bool insertText( SourceLocation Loc, StringRef Str, bool insertText( SourceLocation Loc, StringRef Str,
bool InsertAfter = true, bool indentNewLines = false ); bool InsertAfter = true, bool indentNewLines = false );
bool insertTextAfter( SourceLocation Loc, StringRef Str ); bool insertTextAfter( SourceLocation Loc, StringRef Str );
bool insertTextAfterToken( SourceLocation Loc, StringRef Str ); bool insertTextAfterToken( SourceLocation Loc, StringRef Str );
bool insertTextBefore( SourceLocation Loc, StringRef Str ); bool insertTextBefore( SourceLocation Loc, StringRef Str );
bool removeText( SourceLocation Start, unsigned Length, RewriteOptions opts = RewriteOptions()); 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 removeText( SourceRange range, RewriteOptions opts = RewriteOptions());
bool replaceText( SourceLocation Start, unsigned OrigLength, StringRef NewStr ); bool replaceText( SourceLocation Start, unsigned OrigLength, StringRef NewStr );
bool replaceText( SourceRange range, StringRef NewStr ); bool replaceText( SourceRange range, StringRef NewStr );
...@@ -124,7 +126,7 @@ class RewritePlugin ...@@ -124,7 +126,7 @@ class RewritePlugin
template< typename T > static Plugin* createHelper( CompilerInstance& compiler, Rewriter& rewriter ); template< typename T > static Plugin* createHelper( CompilerInstance& compiler, Rewriter& rewriter );
enum { isRewriter = true }; enum { isRewriter = true };
bool reportEditFailure( SourceLocation loc ); bool reportEditFailure( SourceLocation loc );
bool adjustForWholeStatement( SourceRange* range ); bool adjustRangeForOptions( CharSourceRange* range, RewriteOptions options );
set< SourceLocation > removals; set< SourceLocation > removals;
}; };
...@@ -190,6 +192,27 @@ Plugin::Registration< T >::Registration( const char* optionName ) ...@@ -190,6 +192,27 @@ Plugin::Registration< T >::Registration( const char* optionName )
registerPlugin( &T::template createHelper< T >, optionName, T::isRewriter, T::isPPCallback ); 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 } // namespace
#endif // COMPILEPLUGIN_H #endif // COMPILEPLUGIN_H
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment