1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144
|
/* -*- 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.
*
*/
/*
This is a rewriter.
Remove uses of the macro RTL_CONSTASCII_USTRINGPARAM. One run is for one
specific use (see below), modify source to remove other uses.
*/
#include "plugin.hxx"
#include <clang/Lex/Preprocessor.h>
namespace loplugin
{
class RtlConstAsciiMacro
: public loplugin::FilteringRewritePlugin< RtlConstAsciiMacro >
, public PPCallbacks
{
public:
explicit RtlConstAsciiMacro( const InstantiationData& data );
virtual void run() override;
bool VisitCXXConstructExpr( CXXConstructExpr* expr );
bool VisitCXXTemporaryObjectExpr( CXXTemporaryObjectExpr* expr );
bool VisitStringLiteral( const StringLiteral* literal );
virtual void MacroExpands( const Token& macro, const MacroDirective* directive,
SourceRange range, const MacroArgs* args ) override;
enum { isPPCallback = true };
private:
map< SourceLocation, SourceLocation > expansions; // start location -> end location
bool searchingForString;
bool suitableString;
};
RtlConstAsciiMacro::RtlConstAsciiMacro( const InstantiationData& data )
: FilteringRewritePlugin( data )
, searchingForString( false )
{
compiler.getPreprocessor().addPPCallbacks( this );
}
void RtlConstAsciiMacro::run()
{
TraverseDecl( compiler.getASTContext().getTranslationUnitDecl());
}
void RtlConstAsciiMacro::MacroExpands( const Token& macro, const MacroDirective*,
SourceRange range, const MacroArgs* )
{
if( macro.getIdentifierInfo()->getName() != "RTL_CONSTASCII_USTRINGPARAM" )
return;
expansions[ range.getBegin() ] = range.getEnd();
}
/* Remove use with the following ctor:
OUString( const sal_Char * value, sal_Int32 length,
rtl_TextEncoding encoding,
sal_uInt32 convertFlags = OSTRING_TO_OUSTRING_CVTFLAGS )
This means searching for CXXConstructExpr.
For removal when used with functions it should check e.g. for CallExpr.
*/
bool RtlConstAsciiMacro::VisitCXXConstructExpr( CXXConstructExpr* expr )
{
if( ignoreLocation( expr ))
return true;
if( expr->getNumArgs() != 4 )
return true;
// The last argument should be the default one when the macro is used.
if( dyn_cast< CXXDefaultArgExpr >( expr->getArg( 3 )) == NULL )
return true;
if( expr->getConstructor()->getQualifiedNameAsString() != "rtl::OUString::OUString" )
return true;
const SourceManager& src = compiler.getSourceManager();
SourceLocation start = src.getExpansionLoc( expr->getArg( 0 )->getLocStart());
// Macro fills in the first 3 arguments, so they must all come from the same expansion.
if( start != src.getExpansionLoc( expr->getArg( 2 )->getLocEnd()))
return true;
if( expansions.find( start ) == expansions.end())
return true;
SourceLocation end = expansions[ start ];
// Remove the location, since sometimes the same code may be processed more than once
// (e.g. non-trivial default arguments).
expansions.erase( start );
// Check if the string argument to the macro is suitable.
searchingForString = true;
suitableString = false;
TraverseStmt( expr->getArg( 0 ));
searchingForString = false;
if( !suitableString )
return true;
// Search for '(' (don't just remove a given length to handle possible whitespace).
const char* text = compiler.getSourceManager().getCharacterData( start );
const char* pos = text;
while( *pos != '(' )
++pos;
++pos;
if( text[ -1 ] == ' ' && *pos == ' ' )
++pos; // do not leave two spaces
removeText( start, pos - text, RemoveLineIfEmpty );
const char* textend = compiler.getSourceManager().getCharacterData( end );
if( textend[ -1 ] == ' ' && textend[ 1 ] == ' ' )
removeText( end, 2, RemoveLineIfEmpty ); // Remove ') '.
else
removeText( end, 1, RemoveLineIfEmpty ); // Remove ')'.
return true;
}
bool RtlConstAsciiMacro::VisitCXXTemporaryObjectExpr( CXXTemporaryObjectExpr* expr )
{
return VisitCXXConstructExpr( expr );
}
bool RtlConstAsciiMacro::VisitStringLiteral( const StringLiteral* literal )
{
if( !searchingForString )
return true;
if( suitableString ) // two string literals?
{
report( DiagnosticsEngine::Warning, "cannot analyze RTL_CONSTASCII_USTRINGPARAM (plugin needs fixing)" )
<< literal->getSourceRange();
return true;
}
if( !literal->isAscii()) // ignore
return true;
if( !literal->containsNonAsciiOrNull())
suitableString = true;
return true;
}
static Plugin::Registration< RtlConstAsciiMacro > X( "rtlconstasciimacro" );
} // namespace
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|