diff options
Diffstat (limited to 'compilerplugins/clang/store/svstreamoutputoperators.cxx')
-rw-r--r-- | compilerplugins/clang/store/svstreamoutputoperators.cxx | 223 |
1 files changed, 223 insertions, 0 deletions
diff --git a/compilerplugins/clang/store/svstreamoutputoperators.cxx b/compilerplugins/clang/store/svstreamoutputoperators.cxx new file mode 100644 index 000000000..6e0ff6168 --- /dev/null +++ b/compilerplugins/clang/store/svstreamoutputoperators.cxx @@ -0,0 +1,223 @@ +/* -*- 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 <clang/Lex/Lexer.h> +#include <iostream> + +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*>(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: */ |