/* -*- 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. It changes the SvStream operator<< calls into calls to more explicitly named methods, which reduces the casting needed, and makes it less likely that we will accidentally write data to a file using the wrong data-type-size. TODO we don't currently cope with macro expansion e.g. if the constant on the RHS is a #define TODO we don't currently cope with code like "(*this) << 1;" TODO we don't currently cope with code like "aStream << x << endl;" the "endl" parts ends up dangling. TODO we don't currently cope with custom overloads of operator<< in some of the use-sites. */ #include "plugin.hxx" #include #include namespace loplugin { class SvStreamOutputOperators : public loplugin::FilteringRewritePlugin< SvStreamOutputOperators > { public: explicit SvStreamOutputOperators( InstantiationData const & data ); virtual void run() override; bool VisitCallExpr( const CallExpr* call ); private: SourceLocation after(const SourceLocation& loc); }; SvStreamOutputOperators::SvStreamOutputOperators( InstantiationData const & data ) : FilteringRewritePlugin( data ) { } void SvStreamOutputOperators::run() { TraverseDecl( compiler.getASTContext().getTranslationUnitDecl()); } bool SvStreamOutputOperators::VisitCallExpr( const CallExpr* callExpr ) { if( ignoreLocation( callExpr )) return true; if( callExpr->getNumArgs() < 2 ) return true; const FunctionDecl* func = dyn_cast_or_null< FunctionDecl >( callExpr->getCalleeDecl() ); if ( func == NULL ) return true; if( func->getNumParams() != 1 ) return true; string qualifiedName = func->getQualifiedNameAsString(); bool bOutputOperator; if( qualifiedName == "SvStream::operator<<" ) bOutputOperator = true; else if( qualifiedName == "SvStream::operator>>" ) bOutputOperator = false; else return true; string arg0 = func->getParamDecl( 0 )->getType().getAsString(); string newIOMethod; if (bOutputOperator) { if( arg0 == "sal_uInt16" ) newIOMethod = "WriteUInt16"; else if( arg0 == "sal_uInt32" ) newIOMethod = "WriteUInt32"; else if( arg0 == "sal_uInt64" ) newIOMethod = "WriteUInt64"; else if( arg0 == "sal_Int16" ) newIOMethod = "WriteInt16"; else if( arg0 == "sal_Int32" ) newIOMethod = "WriteInt32"; else if( arg0 == "sal_Int64" ) newIOMethod = "WriteInt64"; else if( arg0 == "sal_uInt8" ) newIOMethod = "WriteUInt8"; else if( arg0 == "sal_Unicode" ) newIOMethod = "WriteUnicode"; else if( arg0 == "rtl::OString" ) newIOMethod = "WriteOString"; else if( arg0 == "bool" ) newIOMethod = "WriteBool"; else if( arg0 == "signed char" ) newIOMethod = "WriteSChar"; else if( arg0 == "char" ) newIOMethod = "WriteChar"; else if( arg0 == "unsigned char" ) newIOMethod = "WriteUChar"; else if( arg0 == "float" ) newIOMethod = "WriteFloat"; else if( arg0 == "double" ) newIOMethod = "WriteDouble"; else if( arg0 == "const double &" ) newIOMethod = "WriteDouble"; else if( arg0 == "const char *" ) newIOMethod = "WriteCharPtr"; else if( arg0 == "char *" ) newIOMethod = "WriteCharPtr"; else if( arg0 == "const unsigned char *" ) newIOMethod = "WriteUCharPtr"; else if( arg0 == "unsigned char *" ) newIOMethod = "WriteUCharPtr"; else if( arg0 == "class SvStream &" ) newIOMethod = "WriteStream"; else { report( DiagnosticsEngine::Warning, "found call to operator<< that I cannot convert with type: " + arg0, callExpr->getLocStart()); return true; } } else { if( arg0 == "sal_uInt16 &" ) newIOMethod = "ReadUInt16"; else if( arg0 == "sal_uInt32 &" ) newIOMethod = "ReadUInt32"; else if( arg0 == "sal_uInt64 &" ) newIOMethod = "ReadUInt64"; else if( arg0 == "sal_Int16 &" ) newIOMethod = "ReadInt16"; else if( arg0 == "sal_Int32 &" ) newIOMethod = "ReadInt32"; else if( arg0 == "sal_Int64 &" ) newIOMethod = "ReadInt64"; else if( arg0 == "sal_uInt8 &" ) newIOMethod = "ReadUInt8"; else if( arg0 == "signed char &" ) newIOMethod = "ReadSChar"; else if( arg0 == "char &" ) newIOMethod = "ReadChar"; else if( arg0 == "unsigned char &" ) newIOMethod = "ReadUChar"; else if( arg0 == "float &" ) newIOMethod = "ReadFloat"; else if( arg0 == "double &" ) newIOMethod = "ReadDouble"; else if( arg0 == "class SvStream &" ) newIOMethod = "ReadStream"; else { report( DiagnosticsEngine::Warning, "found call to operator>> that I cannot convert with type: " + arg0, callExpr->getLocStart()); return true; } } // CallExpr overrides the children() method from Stmt, but not the const variant of it, so we need to cast const away. StmtRange range = const_cast(callExpr)->children(); const Stmt* child1 = *range; // ImplicitCastExpr ++range; const Stmt* child2 = *range; // ImplicitCastExpr if( dyn_cast_or_null< UnaryOperator >( child2 ) != NULL ) { // remove the "*" before the stream variable if( !replaceText( callExpr->getLocStart(), 1, "" ) ) return true; if( !replaceText( child1->getLocStart().getLocWithOffset(-1), 4, "->" ) ) return true; } else { if( !replaceText( child1->getLocStart().getLocWithOffset(-1), 4, "." ) ) return true; } if( !insertTextBefore( callExpr->getArg( 1 )->getLocStart(), newIOMethod + "( " ) ) return true; if( !insertTextAfter( after( callExpr->getLocEnd() ), " )" ) ) return true; //TODO for some reason this is currently removing too much text // if there was a cast e.g. "r << (sal_Int32) 1", then remove the cast // const CStyleCastExpr* cast = dyn_cast_or_null< CStyleCastExpr >( callExpr->getArg(1) ); // if (cast != NULL) // { // replaceText( SourceRange( cast->getLParenLoc(), cast->getRParenLoc() ), "" ); // } // if there was already parentheses around the expression, remove them const ParenExpr* paren = dyn_cast_or_null< ParenExpr >( callExpr->getArg(1) ); if (paren != NULL) { if( !replaceText( paren->getLocStart(), 1, "" ) ) return true; if( !replaceText( paren->getLocEnd(), 1, "" ) ) return true; } // report( DiagnosticsEngine::Note, "found", callExpr->getLocStart()); return true; } SourceLocation SvStreamOutputOperators::after( const SourceLocation& loc ) { return Lexer::getLocForEndOfToken( loc, 0, compiler.getASTContext().getSourceManager(), compiler.getASTContext().getLangOpts() ); } static Plugin::Registration< SvStreamOutputOperators > X( "svstreamoutputoperators" ); } // namespace /* vim:set shiftwidth=4 softtabstop=4 expandtab: */