/* -*- 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 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: */