compilerplugins/Makefile-clang.mk | 1 compilerplugins/clang/lclstaticfix.cxx | 4 compilerplugins/clang/plugin.cxx | 110 +++++++++++++++++++--- compilerplugins/clang/plugin.hxx | 28 +++++ compilerplugins/clang/postfixincrementfix.cxx | 3 compilerplugins/clang/removeforwardstringdecl.cxx | 76 +++++++++++++++ compilerplugins/clang/removeforwardstringdecl.hxx | 34 ++++++ config/config_clang.h.in | 3 configure.ac | 3 9 files changed, 241 insertions(+), 21 deletions(-)
New commits: commit d5ea81b87b8a19b23007691c8b14e6787ecb0290 Author: LuboÅ¡ LuÅák <l.lu...@suse.cz> Date: Thu Jan 3 22:57:46 2013 +0100 handle case of SRCDIR == BUILDDIR Change-Id: I9daea65dc28ab13776a7c4319e5d5811515fe160 diff --git a/compilerplugins/clang/plugin.cxx b/compilerplugins/clang/plugin.cxx index 348386f..d6bc910 100644 --- a/compilerplugins/clang/plugin.cxx +++ b/compilerplugins/clang/plugin.cxx @@ -253,7 +253,7 @@ class PluginHandler else if( strncmp( e->getName(), WORKDIR, strlen( WORKDIR )) == 0 ) diag.Report( diag.getCustomDiagID( DiagnosticsEngine::Warning, "modified source in workdir/ : %0 [loplugin]" )) << e->getName(); - else if( strncmp( e->getName(), BUILDDIR, strlen( BUILDDIR )) == 0 ) + else if( strcmp( SRCDIR, BUILDDIR ) != 0 && strncmp( e->getName(), BUILDDIR, strlen( BUILDDIR )) == 0 ) diag.Report( diag.getCustomDiagID( DiagnosticsEngine::Warning, "modified source in build dir : %0 [loplugin]" )) << e->getName(); else if( strncmp( e->getName(), SRCDIR, strlen( SRCDIR )) == 0 ) commit 258aca9924d9e47737d750356d45227126dcf6a7 Author: LuboÅ¡ LuÅák <l.lu...@suse.cz> Date: Thu Jan 3 20:23:51 2013 +0100 rewriter plugin for removing forward rtl string declarations Change-Id: I12bf38985ae62756973c05aacf762ae3c405ac9b diff --git a/compilerplugins/Makefile-clang.mk b/compilerplugins/Makefile-clang.mk index 6031fe1..e652e44 100644 --- a/compilerplugins/Makefile-clang.mk +++ b/compilerplugins/Makefile-clang.mk @@ -14,6 +14,7 @@ CLANGSRC= \ bodynotinblock.cxx \ lclstaticfix.cxx \ postfixincrementfix.cxx \ + removeforwardstringdecl.cxx \ sallogareas.cxx \ unusedvariablecheck.cxx \ diff --git a/compilerplugins/clang/plugin.cxx b/compilerplugins/clang/plugin.cxx index 22bac0c..348386f 100644 --- a/compilerplugins/clang/plugin.cxx +++ b/compilerplugins/clang/plugin.cxx @@ -22,6 +22,7 @@ #include "bodynotinblock.hxx" #include "lclstaticfix.hxx" #include "postfixincrementfix.hxx" +#include "removeforwardstringdecl.hxx" #include "sallogareas.hxx" #include "unusedvariablecheck.hxx" @@ -192,6 +193,7 @@ class PluginHandler , bodyNotInBlock( context ) , lclStaticFix( context, rewriter ) , postfixIncrementFix( context, rewriter ) + , removeForwardStringDecl( context, rewriter ) , salLogAreas( context ) , unusedVariableCheck( context ) { @@ -204,6 +206,8 @@ class PluginHandler lclStaticFix.run(); else if( isArg( "postfixincrementfix" )) postfixIncrementFix.run(); + else if( isArg( "removeforwardstringdecl" )) + removeForwardStringDecl.run(); else if( args.empty()) { bodyNotInBlock.run(); @@ -292,6 +296,7 @@ class PluginHandler BodyNotInBlock bodyNotInBlock; LclStaticFix lclStaticFix; PostfixIncrementFix postfixIncrementFix; + RemoveForwardStringDecl removeForwardStringDecl; SalLogAreas salLogAreas; UnusedVariableCheck unusedVariableCheck; }; diff --git a/compilerplugins/clang/removeforwardstringdecl.cxx b/compilerplugins/clang/removeforwardstringdecl.cxx new file mode 100644 index 0000000..45891d5 --- /dev/null +++ b/compilerplugins/clang/removeforwardstringdecl.cxx @@ -0,0 +1,76 @@ +/* + * 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. + * + */ + +#include "removeforwardstringdecl.hxx" + +#include <clang/AST/ASTContext.h> +#include <clang/Basic/SourceManager.h> + +/* +This is a rewriter. + +Remove all forward declarations of rtl strings. I.e. 'namespace rtl { class OUString; }' etc. +*/ + +namespace loplugin +{ + +RemoveForwardStringDecl::RemoveForwardStringDecl( ASTContext& context, Rewriter& rewriter ) + : RewritePlugin( context, rewriter ) + { + } + +void RemoveForwardStringDecl::run() + { + TraverseDecl( context.getTranslationUnitDecl()); + } + +bool RemoveForwardStringDecl::VisitNamespaceDecl( NamespaceDecl* declaration ) + { + if( ignoreLocation( declaration )) + return true; + if( declaration->getQualifiedNameAsString() != "rtl" ) + return true; + bool canRemove = true; + for( NamespaceDecl::decl_iterator it = declaration->decls_begin(); + it != declaration->decls_end(); + ++it ) + { + if( *it != NULL ) + { + if( !tryRemoveStringForwardDecl( *it )) + canRemove = false; + } + } + if( canRemove ) // contained only forward decls that we removed + removeText( declaration->getSourceRange(), RemoveLineIfEmpty ); + return true; + } + +bool RemoveForwardStringDecl::tryRemoveStringForwardDecl( const Decl* decl ) + { + const CXXRecordDecl* classdecl = dyn_cast< CXXRecordDecl >( decl ); + if( classdecl == NULL ) + return false; + if( !classdecl->isFreeStanding() || classdecl->isCompleteDefinition()) + return false; // not a simple forward declaration + if( classdecl->getName() == "OString" || classdecl->getName() == "OUString" + || classdecl->getName() == "OStringBuffer" || classdecl->getName() == "OUStringBuffer" + || classdecl->getName() == "OStringHash" || classdecl->getName() == "OUStringHash" + || classdecl->getName() == "OStringLiteral" || classdecl->getName() == "OUStringLiteral" ) + { + removeText( SourceRange( classdecl->getOuterLocStart(), classdecl->getLocEnd()), + RemoveLineIfEmpty | RemoveWholeStatement ); + return true; + } + return false; + } + +} // namespace diff --git a/compilerplugins/clang/removeforwardstringdecl.hxx b/compilerplugins/clang/removeforwardstringdecl.hxx new file mode 100644 index 0000000..7f40e37 --- /dev/null +++ b/compilerplugins/clang/removeforwardstringdecl.hxx @@ -0,0 +1,34 @@ +/* + * 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. + * + */ + +#ifndef REMOVEFORWARDSTRINGDECL_H +#define REMOVEFORWARDSTRINGDECL_H + +#include "plugin.hxx" + +namespace loplugin +{ + +class RemoveForwardStringDecl + : public RecursiveASTVisitor< RemoveForwardStringDecl > + , public RewritePlugin + { + public: + explicit RemoveForwardStringDecl( ASTContext& context, Rewriter& rewriter ); + void run(); + bool VisitNamespaceDecl( NamespaceDecl* declaration ); + private: + bool tryRemoveStringForwardDecl( const Decl* decl ); + }; + +} // namespace + +#endif // REMOVEFORWARDSTRINGDECL_H + commit c26e655264f03bb8bc484130ab2f539a9f831f16 Author: LuboÅ¡ LuÅák <l.lu...@suse.cz> Date: Thu Dec 20 23:08:52 2012 +0100 support for removing a statement as a whole Change-Id: Icb7b017a0c76a6169f0f629bb40bf97449c75837 diff --git a/compilerplugins/clang/plugin.cxx b/compilerplugins/clang/plugin.cxx index 6535e1a..22bac0c 100644 --- a/compilerplugins/clang/plugin.cxx +++ b/compilerplugins/clang/plugin.cxx @@ -105,22 +105,47 @@ bool RewritePlugin::insertTextBefore( SourceLocation Loc, StringRef Str ) bool RewritePlugin::removeText( SourceLocation Start, unsigned Length, RewriteOptions opts ) { - if( rewriter.RemoveText( Start, Length, opts )) - return reportEditFailure( Start ); - return true; + return removeText( SourceRange( Start, Start.getLocWithOffset( Length )), opts ); } -bool RewritePlugin::removeText( CharSourceRange range, RewriteOptions opts ) +bool RewritePlugin::removeText( SourceRange range, RewriteOptions opts ) { + if( opts.RemoveWholeStatement ) + { + if( !adjustForWholeStatement( &range )) + return reportEditFailure( range.getBegin()); + } if( rewriter.RemoveText( range, opts )) return reportEditFailure( range.getBegin()); return true; } -bool RewritePlugin::removeText( SourceRange range, RewriteOptions opts ) +bool RewritePlugin::adjustForWholeStatement( SourceRange* range ) { - if( rewriter.RemoveText( range, opts )) - return reportEditFailure( range.getBegin()); + SourceManager& SM = rewriter.getSourceMgr(); + SourceLocation fileStartLoc = SM.getLocForStartOfFile( SM.getFileID( range->getBegin())); + if( fileStartLoc.isInvalid()) + return false; + bool invalid = false; + const char* fileBuf = SM.getCharacterData( fileStartLoc, &invalid ); + if( invalid ) + return false; + const char* startBuf = SM.getCharacterData( range->getBegin(), &invalid ); + if( invalid ) + return false; + const char* endBuf = SM.getCharacterData( range->getEnd(), &invalid ); + if( invalid ) + return false; + const char* startSpacePos = startBuf; + // do not skip \n here, RemoveLineIfEmpty can take care of that + --startSpacePos; + while( startSpacePos >= fileBuf && ( *startSpacePos == ' ' || *startSpacePos == '\t' )) + --startSpacePos; + const char* semiPos = strchr( endBuf, ';' ); + if( semiPos == NULL ) + return false; + *range = SourceRange( range->getBegin().getLocWithOffset( startSpacePos - startBuf + 1 ), + range->getEnd().getLocWithOffset( semiPos - endBuf + 1 )); return true; } diff --git a/compilerplugins/clang/plugin.hxx b/compilerplugins/clang/plugin.hxx index 2a587ad..b9409b2 100644 --- a/compilerplugins/clang/plugin.hxx +++ b/compilerplugins/clang/plugin.hxx @@ -44,18 +44,39 @@ class RewritePlugin public: explicit RewritePlugin( ASTContext& context, Rewriter& rewriter ); protected: - typedef Rewriter::RewriteOptions RewriteOptions; + // This enum allows passing just 'RemoveLineIfEmpty' to functions below. + enum RemoveLineIfEmpty_t { RemoveLineIfEmpty }; + // Use this to remove the declaration/statement as a whole, i.e. all whitespace before the statement + // and the trailing semicolor (is not part of the AST element range itself). + // The trailing semicolon must be present. + enum RemoveWholeStatement_t { RemoveWholeStatement }; + enum RemoveLineIfEmptyAndWholeStatement_t { RemoveLineIfEmptyAndWholeStatement }; + // syntactic sugar to be able to write 'RemoveLineIfEmpty | RemoveWholeStatement' + friend RemoveLineIfEmptyAndWholeStatement_t operator|( RemoveLineIfEmpty_t, RemoveWholeStatement_t ) + { return RemoveLineIfEmptyAndWholeStatement; } + struct RewriteOptions + : public Rewriter::RewriteOptions + { + RewriteOptions() : RemoveWholeStatement( false ) {} // default + RewriteOptions( RemoveLineIfEmpty_t ) : RemoveWholeStatement( false ) { RemoveLineIfEmpty = true; } + RewriteOptions( RemoveWholeStatement_t ) : RemoveWholeStatement( true ) {} + RewriteOptions( RemoveLineIfEmptyAndWholeStatement_t ) : RemoveWholeStatement( true ) { RemoveLineIfEmpty = true; } + bool RemoveWholeStatement; + }; // These following insert/remove/replaceText functions map to functions - // in clang::Rewriter, with two differences: + // in clang::Rewriter, with these differences: // - they (more intuitively) return false on failure rather than true // - they report a warning when the change cannot be done + // - There is RemoveWholeStatement to also remove the trailing semicolon when removing (must be there) + // and al preceding whitespace. bool insertText( SourceLocation Loc, StringRef Str, bool InsertAfter = true, bool indentNewLines = false ); bool insertTextAfter( SourceLocation Loc, StringRef Str ); bool insertTextAfterToken( SourceLocation Loc, StringRef Str ); bool insertTextBefore( SourceLocation Loc, StringRef Str ); bool removeText( SourceLocation Start, unsigned Length, RewriteOptions opts = RewriteOptions()); - bool removeText( CharSourceRange range, RewriteOptions opts = RewriteOptions()); + // CharSourceRange not supported, unless really needed, as it makes RemoveSemicolon more complicated + //bool removeText( CharSourceRange range, RewriteOptions opts = RewriteOptions()); bool removeText( SourceRange range, RewriteOptions opts = RewriteOptions()); bool replaceText( SourceLocation Start, unsigned OrigLength, StringRef NewStr ); bool replaceText( SourceRange range, StringRef NewStr ); @@ -63,6 +84,7 @@ class RewritePlugin Rewriter& rewriter; private: bool reportEditFailure( SourceLocation loc ); + bool adjustForWholeStatement( SourceRange* range ); }; inline commit 217e3f2ea1e8983328364607f244daceeafca167 Author: LuboÅ¡ LuÅák <l.lu...@suse.cz> Date: Thu Jan 3 20:15:21 2013 +0100 better handling of which files are processed by clang plugins Check that only LO's files are processed, as there's no point otherwise. Also warn about files in workdir/solver/builddir, as those are either generated or copies. Try to automatically match include files from solver to srcdir though, as that's where include files are usually included from :(. Change-Id: Ie8389e903f623a9d0e75015091acc0da78e76c3a diff --git a/compilerplugins/clang/lclstaticfix.cxx b/compilerplugins/clang/lclstaticfix.cxx index 9227af8..a5c765b 100644 --- a/compilerplugins/clang/lclstaticfix.cxx +++ b/compilerplugins/clang/lclstaticfix.cxx @@ -34,9 +34,7 @@ void LclStaticFix::run() bool LclStaticFix::VisitFunctionDecl( FunctionDecl* declaration ) { - // TODO also LO header files? or a subdir? - // Only the .cxx file can be normally edited ... ? - if( !context.getSourceManager().isFromMainFile( declaration->getLocStart())) + if( ignoreLocation( declaration )) return true; if( declaration->isCXXClassMember()) return true; diff --git a/compilerplugins/clang/plugin.cxx b/compilerplugins/clang/plugin.cxx index 62412e6..6535e1a 100644 --- a/compilerplugins/clang/plugin.cxx +++ b/compilerplugins/clang/plugin.cxx @@ -25,6 +25,8 @@ #include "sallogareas.hxx" #include "unusedvariablecheck.hxx" +#include <config_clang.h> + namespace loplugin { @@ -51,7 +53,19 @@ DiagnosticBuilder Plugin::report( DiagnosticsEngine::Level level, StringRef mess bool Plugin::ignoreLocation( SourceLocation loc ) { - return context.getSourceManager().isInSystemHeader( context.getSourceManager().getExpansionLoc( loc )); + SourceLocation expansionLoc = context.getSourceManager().getExpansionLoc( loc ); + if( context.getSourceManager().isInSystemHeader( expansionLoc )) + return true; + bool invalid; + const char* bufferName = context.getSourceManager().getBufferName( expansionLoc, &invalid ); + if( invalid ) + return true; + if( strncmp( bufferName, OUTDIR, strlen( OUTDIR )) == 0 + || strncmp( bufferName, WORKDIR, strlen( WORKDIR )) == 0 + || strncmp( bufferName, BUILDDIR, strlen( BUILDDIR )) == 0 + || strncmp( bufferName, SRCDIR, strlen( SRCDIR )) == 0 ) + return false; // ok + return true; } @@ -182,24 +196,64 @@ class PluginHandler ++it ) { const FileEntry* e = context.getSourceManager().getFileEntryForID( it->first ); - char* filename = new char[ strlen( e->getName()) + 100 ]; - sprintf( filename, "%s.new.%d", e->getName(), getpid()); + DiagnosticsEngine& diag = context.getDiagnostics(); + /* Check where the file actually is, and warn about cases where modification + most probably doesn't matter (generated files in workdir). + The order here is important, as OUTDIR and WORKDIR are often in SRCDIR/BUILDDIR, + and BUILDDIR is sometimes in SRCDIR. */ + string modifyFile; + if( strncmp( e->getName(), OUTDIR, strlen( OUTDIR )) == 0 ) + { + /* Try to find a matching file for a file in solver/ (include files + are usually included from there rather than from the source dir) if possible. */ + if( strncmp( e->getName(), OUTDIR "/inc/", strlen( OUTDIR ) + strlen( "/inc/" )) == 0 ) + { + string filename( e->getName()); + int modulePos = strlen( OUTDIR ) + strlen( "/inc/" ); + int moduleEnd = filename.find( '/', modulePos ); + if( moduleEnd != string::npos ) + { + modifyFile = SRCDIR "/" + filename.substr( modulePos, moduleEnd - modulePos ) + + "/inc/" + filename.substr( modulePos ); + } + } + if( modifyFile.empty()) + diag.Report( diag.getCustomDiagID( DiagnosticsEngine::Warning, + "modified source in solver/ : %0 [loplugin]" )) << e->getName(); + } + else if( strncmp( e->getName(), WORKDIR, strlen( WORKDIR )) == 0 ) + diag.Report( diag.getCustomDiagID( DiagnosticsEngine::Warning, + "modified source in workdir/ : %0 [loplugin]" )) << e->getName(); + else if( strncmp( e->getName(), BUILDDIR, strlen( BUILDDIR )) == 0 ) + diag.Report( diag.getCustomDiagID( DiagnosticsEngine::Warning, + "modified source in build dir : %0 [loplugin]" )) << e->getName(); + else if( strncmp( e->getName(), SRCDIR, strlen( SRCDIR )) == 0 ) + ; // ok + else + { + diag.Report( diag.getCustomDiagID( DiagnosticsEngine::Warning, + "modified source in unknown location, not modifying : %0 [loplugin]" )) << e->getName(); + continue; // ---> + } + if( modifyFile.empty()) + modifyFile = e->getName(); + char* filename = new char[ modifyFile.length() + 100 ]; + sprintf( filename, "%s.new.%d", modifyFile.c_str(), getpid()); string error; bool ok = false; raw_fd_ostream ostream( filename, error ); - DiagnosticsEngine& diag = context.getDiagnostics(); if( error.empty()) { it->second.write( ostream ); ostream.close(); - if( !ostream.has_error() && rename( filename, e->getName()) == 0 ) + if( !ostream.has_error() && rename( filename, modifyFile.c_str()) == 0 ) ok = true; } ostream.clear_error(); unlink( filename ); if( !ok ) diag.Report( diag.getCustomDiagID( DiagnosticsEngine::Error, - "cannot write modified source to %0 (%1) [loplugin]" )) << e->getName() << error; + "cannot write modified source to %0 (%1) [loplugin]" )) << modifyFile << error; delete[] filename; } } diff --git a/compilerplugins/clang/postfixincrementfix.cxx b/compilerplugins/clang/postfixincrementfix.cxx index c5c17fb..bfaf77c 100644 --- a/compilerplugins/clang/postfixincrementfix.cxx +++ b/compilerplugins/clang/postfixincrementfix.cxx @@ -28,8 +28,7 @@ void PostfixIncrementFix::run() bool PostfixIncrementFix::VisitFunctionDecl( FunctionDecl* declaration ) { - // TODO also LO header files? or a subdir? - if( !context.getSourceManager().isFromMainFile( declaration->getLocStart())) + if( ignoreLocation( declaration )) return true; if( !declaration->doesThisDeclarationHaveABody()) return true; diff --git a/config/config_clang.h.in b/config/config_clang.h.in index 60ff5bf..9d87e7a 100644 --- a/config/config_clang.h.in +++ b/config/config_clang.h.in @@ -7,6 +7,9 @@ Settings related to Clang compiler plugins. #ifndef CONFIG_CLANG_H #define CONFIG_CLANG_H +#undef BUILDDIR +#undef OUTDIR #undef SRCDIR +#undef WORKDIR #endif diff --git a/configure.ac b/configure.ac index 9f9527d67..bd5b074 100644 --- a/configure.ac +++ b/configure.ac @@ -107,6 +107,7 @@ AC_SUBST(BUILDDIR) AC_SUBST(EXEEXT_FOR_BUILD) AC_SUBST(x_Cygwin) AC_DEFINE_UNQUOTED(SRCDIR,"$SRC_ROOT") +AC_DEFINE_UNQUOTED(BUILDDIR,"$BUILDDIR") if test "z$EUID" = "z0" -a "`uname -o 2>/dev/null`" = "Cygwin"; then AC_MSG_ERROR([You must build LibreOffice as a normal user - not using an administrative account]) @@ -4092,6 +4093,8 @@ AC_SUBST(P_SEP) AC_SUBST(SOLARVER) AC_SUBST(WORKDIR) AC_SUBST(PLATFORMID) +AC_DEFINE_UNQUOTED(OUTDIR,"$OUTDIR") +AC_DEFINE_UNQUOTED(WORKDIR,"$WORKDIR") dnl =================================================================== dnl Test which package format to use
_______________________________________________ Libreoffice-commits mailing list libreoffice-comm...@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/libreoffice-commits