diff options
Diffstat (limited to 'compilerplugins/clang/sharedvisitor')
-rw-r--r-- | compilerplugins/clang/sharedvisitor/README | 45 | ||||
-rw-r--r-- | compilerplugins/clang/sharedvisitor/analyzer.cxx | 305 | ||||
-rw-r--r-- | compilerplugins/clang/sharedvisitor/dummyplugin.hxx | 82 | ||||
-rw-r--r-- | compilerplugins/clang/sharedvisitor/generator.cxx | 482 | ||||
-rw-r--r-- | compilerplugins/clang/sharedvisitor/precompiled_clang.hxx | 12 |
5 files changed, 926 insertions, 0 deletions
diff --git a/compilerplugins/clang/sharedvisitor/README b/compilerplugins/clang/sharedvisitor/README new file mode 100644 index 000000000..7b4a8b3c7 --- /dev/null +++ b/compilerplugins/clang/sharedvisitor/README @@ -0,0 +1,45 @@ +These tools generate another "plugin" which in fact only dispatches Visit* and Traverse* +calls to all other plugins registered with it. This means that there is just one +RecursiveASTVisitor pass for all those plugins instead of one per each, which +with the current number of plugins actually makes a performance difference. + +If you work on a plugin, comment out LO_CLANG_SHARED_PLUGINS in Makefile-clang.mk in order +to disable the feature (re-generating takes time). + +There are two tools: +- analyzer, which analyses one .cxx file (one plugins) and writes info about it to a .plugininfo + file, this allows parallelising this part, since it can take some time +- generator, which reads all the .plugininfo files and generates sharedvisitor.cxx + +Requirements for plugins: +- Can use Visit* and Traverse* functions, but not WalkUp*. +- Visit* functions can generally remain unmodified. +- run() function must be split into preRun() and postRun() if there's any additional functionality + besides calling TraverseDecl(). The shared visitor will call the preRun() and postRun() functions + as necessary while calling its own run(). The run() function of the plugin must stay + (in case of a non-shared build) but should generally look like this: + if( preRun()) + if( TraverseDecl(compiler.getASTContext().getTranslationUnitDecl())) + postRun(); +- Traverse* functions must be split into PreTraverse* and PostTraverse*, similarly to how run() + is handled, the Traverse* function should generally look like this: + bool ret = true; + if( PreTraverse*(decl)) + { + ret = RecursiveASTVisitor::Traverse*(decl); + PostTraverse*(decl, ret); + } + return ret; + + +TODO: +- Create macros for the standardized layout of run(), Traverse*, etc.? +- Possibly check plugin sources more thoroughly (e.g. that run() doesn't actually do more). +- Have one tool that extracts info from plugin .cxx files into some .txt file and another tool + that generates sharedvisitor.cxx based on those files? That would generally make the generation + faster when doing incremental changes. The .txt file could also contain some checksum of the .cxx + to avoid the analysing pass completely if just the timestamp has changed. +- Do not re-compile sharedvisitor.cxx if its contents have not actually changed. +- Is it possible to make the clang code analyze just the .cxx without also parsing all the headers? +- Instead of having to comment out LO_CLANG_SHARED_PLUGINS, implement --enable-compiler-plugins=debug . +- Try make analyzer use a precompiled header of Clang headers, for better performance. diff --git a/compilerplugins/clang/sharedvisitor/analyzer.cxx b/compilerplugins/clang/sharedvisitor/analyzer.cxx new file mode 100644 index 000000000..ea519abb0 --- /dev/null +++ b/compilerplugins/clang/sharedvisitor/analyzer.cxx @@ -0,0 +1,305 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include "clang/AST/ASTConsumer.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendAction.h" +#include "clang/Tooling/Tooling.h" +#include "llvm/ADT/StringExtras.h" + +#include <cassert> +#include <cstddef> +#include <cstring> +#include <iostream> +#include <memory> +#include <fstream> +#include <set> + +#include "config_clang.h" +#include "../check.hxx" +#include "../check.cxx" + +using namespace std; + +using namespace clang; +using namespace llvm; + +using namespace loplugin; + +// Info about a Traverse* function in a plugin. +struct TraverseFunctionInfo +{ + string name; + string argument; + bool hasPre = false; + bool hasPost = false; +}; + +struct TraverseFunctionInfoLess +{ + bool operator()( const TraverseFunctionInfo& l, const TraverseFunctionInfo& r ) const + { + return l.name < r.name; + } +}; + +static set< TraverseFunctionInfo, TraverseFunctionInfoLess > traverseFunctions; + +class CheckFileVisitor + : public RecursiveASTVisitor< CheckFileVisitor > +{ +public: + void setContext(ASTContext const& context) { context_ = &context; } + + bool VisitCXXRecordDecl(CXXRecordDecl *Declaration); + + bool TraverseNamespaceDecl(NamespaceDecl * decl) + { + // Skip non-LO namespaces the same way FilteringPlugin does. + if( !ContextCheck( decl ).Namespace( "loplugin" ).GlobalNamespace() + && !ContextCheck( decl ).AnonymousNamespace()) + { + return true; + } + return RecursiveASTVisitor<CheckFileVisitor>::TraverseNamespaceDecl(decl); + } + +private: + ASTContext const* context_ = nullptr; + + QualType unqualifyPointeeType(QualType type) + { + assert(context_ != nullptr); + if (auto const t = type->getAs<clang::PointerType>()) + { + return context_->getQualifiedType( + context_->getPointerType(t->getPointeeType().getUnqualifiedType()), + type.getQualifiers()); + } + return type; + } +}; + +static bool inheritsPluginClassCheck( const Decl* decl ) +{ + return bool( DeclCheck( decl ).Class( "FilteringPlugin" ).Namespace( "loplugin" ).GlobalNamespace()) + || bool( DeclCheck( decl ).Class( "FilteringRewritePlugin" ).Namespace( "loplugin" ).GlobalNamespace()); +} + +static TraverseFunctionInfo findOrCreateTraverseFunctionInfo( StringRef name ) +{ + TraverseFunctionInfo info; + info.name = name.str(); + auto foundInfo = traverseFunctions.find( info ); + if( foundInfo != traverseFunctions.end()) + { + info = move( *foundInfo ); + traverseFunctions.erase( foundInfo ); + } + return info; +} + +static bool foundSomething; + +bool CheckFileVisitor::VisitCXXRecordDecl( CXXRecordDecl* decl ) +{ + if( !isDerivedFrom( decl, inheritsPluginClassCheck )) + return true; + + if( decl->getName() == "FilteringPlugin" || decl->getName() == "FilteringRewritePlugin" ) + return true; + + cout << "# This file is autogenerated. Do not modify." << endl; + cout << "# Generated by compilerplugins/clang/sharedvisitor/analyzer.cxx ." << endl; + cout << "InfoVersion:1" << endl; + cout << "ClassName:" << decl->getName().str() << endl; + traverseFunctions.clear(); + for( const CXXMethodDecl* method : decl->methods()) + { + if( !method->getDeclName().isIdentifier()) + continue; + if( method->isStatic() || method->getAccess() != AS_public ) + continue; + if( method->getName().startswith( "Visit" )) + { + if( method->getNumParams() == 1 ) + { + cout << "VisitFunctionStart" << endl; + cout << "VisitFunctionName:" << method->getName().str() << endl; + cout << "VisitFunctionArgument:" + << unqualifyPointeeType( + method->getParamDecl( 0 )->getTypeSourceInfo()->getType()).getAsString() + << endl; + cout << "VisitFunctionEnd" << endl; + } + else + { + cerr << "Unhandled Visit* function: " << decl->getName().str() + << "::" << method->getName().str() << endl; + abort(); + } + } + else if( method->getName().startswith( "Traverse" )) + { + if( method->getNumParams() == 1 ) + { + TraverseFunctionInfo traverseInfo = findOrCreateTraverseFunctionInfo( method->getName()); + traverseInfo.argument = method->getParamDecl( 0 )->getTypeSourceInfo()->getType().getAsString(); + traverseFunctions.insert( move( traverseInfo )); + } + else + { + cerr << "Unhandled Traverse* function: " << decl->getName().str() + << "::" << method->getName().str() << endl; + abort(); + } + } + else if( method->getName().startswith( "PreTraverse" )) + { + TraverseFunctionInfo traverseInfo = findOrCreateTraverseFunctionInfo( method->getName().substr( 3 )); + traverseInfo.hasPre = true; + traverseFunctions.insert( move( traverseInfo )); + } + else if( method->getName().startswith( "PostTraverse" )) + { + TraverseFunctionInfo traverseInfo = findOrCreateTraverseFunctionInfo( method->getName().substr( 4 )); + traverseInfo.hasPost = true; + traverseFunctions.insert( move( traverseInfo )); + } + else if( method->getName() == "shouldVisitTemplateInstantiations" ) + cout << "ShouldVisitTemplateInstantiations:1" << endl; + else if (method->getName() == "shouldVisitImplicitCode") + cout << "ShouldVisitImplicitCode:1" << endl; + else if( method->getName().startswith( "WalkUp" )) + { + cerr << "WalkUp function not supported for shared visitor: " << decl->getName().str() + << "::" << method->getName().str() << endl; + abort(); + } + } + + for( const auto& traverseFunction : traverseFunctions ) + { + cout << "TraverseFunctionStart" << endl; + cout << "TraverseFunctionName:" << traverseFunction.name << endl; + cout << "TraverseFunctionArgument:" << traverseFunction.argument << endl; + cout << "TraverseFunctionHasPre:" << traverseFunction.hasPre << endl; + cout << "TraverseFunctionHasPost:" << traverseFunction.hasPost << endl; + cout << "TraverseFunctionEnd" << endl; + } + + cout << "InfoEnd" << endl; + foundSomething = true; + return true; +} + +class FindNamedClassConsumer + : public ASTConsumer +{ +public: + void Initialize(ASTContext& context) override + { + visitor.setContext(context); + } + virtual void HandleTranslationUnit(ASTContext& context) override + { + visitor.TraverseDecl( context.getTranslationUnitDecl()); + } +private: + CheckFileVisitor visitor; +}; + +class FindNamedClassAction + : public ASTFrontendAction + { +public: + virtual unique_ptr<ASTConsumer> CreateASTConsumer( CompilerInstance&, StringRef ) override + { + return unique_ptr<ASTConsumer>( new FindNamedClassConsumer ); + } +}; + + +string readSourceFile( const char* filename ) +{ + string contents; + ifstream stream( filename ); + if( !stream ) + { + cerr << "Failed to open: " << filename << endl; + exit( 1 ); + } + string line; + bool hasIfdef = false; + while( getline( stream, line )) + { + // TODO add checks that it's e.g. not "#ifdef" ? + if( line.find( "#ifndef LO_CLANG_SHARED_PLUGINS" ) == 0 ) + hasIfdef = true; + contents += line; + contents += '\n'; + } + if( stream.eof() && hasIfdef ) + return contents; + return ""; +} + +int main(int argc, char** argv) +{ + vector< string > args; + int i = 1; + for( ; i < argc; ++ i ) + { + constexpr std::size_t prefixlen = 5; // strlen("-arg="); + if (std::strncmp(argv[i], "-arg=", prefixlen) != 0) + { + break; + } + args.push_back(argv[i] + prefixlen); + } + SmallVector< StringRef, 20 > clangflags; + SplitString( CLANGFLAGS, clangflags ); + for (auto const & i: clangflags) { + args.push_back(i.str()); + } + args.insert( + args.end(), + { // These must match LO_CLANG_ANALYZER_PCH_CXXFLAGS in Makefile-clang.mk . + "-I" BUILDDIR "/config_host" // plugin sources use e.g. config_global.h +#if LO_CLANG_USE_ANALYZER_PCH + , + "-include-pch", // use PCH with Clang headers to speed up parsing/analysing + BUILDDIR "/compilerplugins/clang/sharedvisitor/clang.pch" +#endif + }); + for( ; i < argc; ++ i ) + { + string contents = readSourceFile(argv[i]); + if( contents.empty()) + continue; + foundSomething = false; +#if CLANG_VERSION >= 100000 + if( !tooling::runToolOnCodeWithArgs( std::unique_ptr<FindNamedClassAction>(new FindNamedClassAction), contents, args, argv[ i ] )) +#else + if( !tooling::runToolOnCodeWithArgs( new FindNamedClassAction, contents, args, argv[ i ] )) +#endif + { + cerr << "Failed to analyze: " << argv[ i ] << endl; + return 2; + } + if( !foundSomething ) + { + // there's #ifndef LO_CLANG_SHARED_PLUGINS in the source, but no class matched + cerr << "Failed to find code: " << argv[ i ] << endl; + return 2; + } + } + return 0; +} diff --git a/compilerplugins/clang/sharedvisitor/dummyplugin.hxx b/compilerplugins/clang/sharedvisitor/dummyplugin.hxx new file mode 100644 index 000000000..b24157965 --- /dev/null +++ b/compilerplugins/clang/sharedvisitor/dummyplugin.hxx @@ -0,0 +1,82 @@ +/* -*- 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. + * + */ + +#ifndef DUMMYPLUGIN_H +#define DUMMYPLUGIN_H + +#include "../plugin.hxx" + +using namespace clang; +using namespace llvm; + +namespace loplugin +{ + +// These classes are used as base classes when building with LO_CLANG_SHARED_PLUGINS. +// Since plugin classes in that case should use just one shared RecursiveASTVisitor, +// sharedvisitor/generator.cxx will make these to be the base classes used, so that +// compiling the code doesn't spend a several minutes optimizing instances +// of RecursiveASTVisitor that will never get used. + +template<typename T> +class DummyRecursiveASTVisitor +{ +public: + // These need to be reimplemented, because plugins contain calls to them, + // but they should actually never get called in the shared-visitor mode. + // This could be autogenerated too, but it's probably simpler to just extend + // manually as needed. + bool TraverseDecl( Decl* ) { return complain(); } + bool TraverseLinkageSpecDecl( LinkageSpecDecl* ) { return complain(); } + bool TraverseStmt( Stmt* ) { return complain(); } + bool TraverseIfStmt( IfStmt* ) { return complain(); } + bool TraverseWhileStmt( WhileStmt* ) { return complain(); } + bool TraverseDoStmt( DoStmt* ) { return complain(); } + bool TraverseForStmt( ForStmt* ) { return complain(); } + bool TraverseParenExpr( ParenExpr* ) { return complain(); } + bool TraverseUnaryLNot( UnaryOperator* ) { return complain(); } + bool TraverseBinLAnd( BinaryOperator* ) { return complain(); } + bool TraverseBinLOr( BinaryOperator* ) { return complain(); } + bool TraverseConditionalOperator( ConditionalOperator* ) { return complain(); } + bool TraverseCXXCatchStmt( CXXCatchStmt* ) { return complain(); } + bool TraverseCXXDestructorDecl( CXXDestructorDecl* ) { return complain(); } + bool TraverseFunctionDecl( FunctionDecl* ) { return complain(); } + bool TraverseSwitchStmt( SwitchStmt* ) { return complain(); } + bool TraverseImplicitCastExpr( ImplicitCastExpr* ) { return complain(); } + bool TraverseCStyleCastExpr( CStyleCastExpr* ) { return complain(); } + bool TraverseCXXStaticCastExpr( CXXStaticCastExpr* ) { return complain(); } + bool TraverseCXXFunctionalCastExpr( CXXFunctionalCastExpr* ) { return complain(); } + bool TraverseFriendDecl( FriendDecl* ) { return complain(); } + bool TraverseTypeLoc( TypeLoc ) { return complain(); } + bool TraverseAlignedAttr( AlignedAttr* ) { return complain(); } +private: + bool complain() { assert(false && "should not be calling this in sharedplugin mode"); abort(); return false; } +}; + +template<typename Derived> +class DummyFilteringPlugin : public DummyRecursiveASTVisitor<Derived>, public Plugin +{ +public: + explicit DummyFilteringPlugin( const InstantiationData& data ) : Plugin(data) {} +}; + +template<typename Derived> +class DummyFilteringRewritePlugin : public DummyRecursiveASTVisitor<Derived>, public RewritePlugin +{ +public: + explicit DummyFilteringRewritePlugin( const InstantiationData& data ) : RewritePlugin(data) {} +}; + +} // namespace + +#endif // DUMMYPLUGIN_H + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/compilerplugins/clang/sharedvisitor/generator.cxx b/compilerplugins/clang/sharedvisitor/generator.cxx new file mode 100644 index 000000000..62c0798a4 --- /dev/null +++ b/compilerplugins/clang/sharedvisitor/generator.cxx @@ -0,0 +1,482 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include <algorithm> +#include <cassert> +#include <cstddef> +#include <cstring> +#include <iostream> +#include <fstream> +#include <memory> +#include <set> +#include <vector> + +using namespace std; + +// Info about a Visit* function in a plugin. +struct VisitFunctionInfo +{ + string name; + string argument; +}; + + +// Info about a Traverse* function in a plugin. +struct TraverseFunctionInfo +{ + string name; + string argument; + bool hasPre = false; + bool hasPost = false; +}; + +struct VisitFunctionInfoLess +{ + bool operator()( const VisitFunctionInfo& l, const VisitFunctionInfo& r ) const + { + return l.name < r.name; + } +}; + +struct TraverseFunctionInfoLess +{ + bool operator()( const TraverseFunctionInfo& l, const TraverseFunctionInfo& r ) const + { + return l.name < r.name; + } +}; + + +// Information about each LO plugin. +struct PluginInfo +{ + string className; // e.g. "BadStatics" + string variableName; // e.g. "badStatics" + string lowercaseName; + bool shouldVisitTemplateInstantiations; + bool shouldVisitImplicitCode; + set< VisitFunctionInfo, VisitFunctionInfoLess > visitFunctions; + set< TraverseFunctionInfo, TraverseFunctionInfoLess > traverseFunctions; +}; + +// We need separate visitors for shouldVisitTemplateInstantiations and shouldVisitImplicitCode, +// so split plugins into groups by what they should visit. +// It seems that trying to handle the shouldVisit* functionality with just one visitor +// is tricky. +enum PluginType +{ + PluginBasic, + PluginVisitTemplates, + PluginVisitImplicit, + PluginVisitTemplatesImplicit, +}; + +const int Plugin_Begin = PluginBasic; +const int Plugin_End = PluginVisitTemplatesImplicit + 1; +static const char* const pluginTypeNames[ Plugin_End ] + = { "Basic", "VisitTemplates", "VisitImplicit", "VisitTemplatesImplicit" }; + +static vector< PluginInfo > plugins[ Plugin_End ]; + + +void generateVisitor( PluginType type ); + +void generate() +{ + ostream& output = cout; + output << +"// This file is autogenerated. Do not modify.\n" +"// Generated by compilerplugins/clang/sharedvisitor/generator.cxx .\n" +"\n" +"#ifdef LO_CLANG_SHARED_PLUGINS\n" +"\n" +"#include <config_clang.h>\n" +"\n" +"#include <clang/AST/ASTContext.h>\n" +"#include <clang/AST/RecursiveASTVisitor.h>\n" +"\n" +"#include \"plugin.hxx\"\n" +"#include \"sharedvisitor/dummyplugin.hxx\"\n" +"\n"; + + output << "#undef LO_CLANG_SHARED_PLUGINS // to get sources of individual plugins\n"; + output << "// make use of the dummy base classes\n"; + output << "#define RecursiveASTVisitor DummyRecursiveASTVisitor\n"; + output << "#define FilteringPlugin DummyFilteringPlugin\n"; + output << "#define FilteringRewritePlugin DummyFilteringRewritePlugin\n"; + output << "\n"; + for( const auto& pluginGroup : plugins ) + for( const PluginInfo& plugin : pluginGroup ) + output << "#include \"" << plugin.lowercaseName << ".cxx\"" << endl; + output << "\n"; + output << "#undef RecursiveASTVisitor\n"; + output << "#undef FilteringPlugin\n"; + output << "#undef FilteringRewritePlugin\n"; + + output << +"\n" +"using namespace clang;\n" +"using namespace llvm;\n" +"\n" +"namespace loplugin\n" +"{\n"; + + for( int type = Plugin_Begin; type < Plugin_End; ++type ) + generateVisitor( static_cast< PluginType >( type )); + + output << +"} // namespace loplugin\n" +"\n" +"#endif // LO_CLANG_SHARED_PLUGINS\n"; +} + +void generateVisitor( PluginType type ) +{ + if( plugins[ type ].empty()) + return; + ostream& output = cout; + output << +"\n" +"class SharedRecursiveASTVisitor" << pluginTypeNames[ type ] << "\n" +" : public FilteringPlugin< SharedRecursiveASTVisitor" << pluginTypeNames[ type ] << ">\n" +"{\n" +"public:\n" +" explicit SharedRecursiveASTVisitor" << pluginTypeNames[ type ] << "(const InstantiationData& rData)\n" +" : FilteringPlugin(rData)\n"; + for( const PluginInfo& plugin : plugins[ type ] ) + output << " , " << plugin.variableName << "( nullptr )\n"; + output << " , activeRefCount( 0 )\n"; + output << " {}\n"; + + output << +" ~SharedRecursiveASTVisitor" << pluginTypeNames[ type ] << "()\n" +" {\n" +" if( activeRefCount != 0 )\n" +" abort();\n" +" }\n"; + + output << +" virtual bool preRun() override\n" +" {\n"; + for( const PluginInfo& plugin : plugins[ type ] ) + { + output << " if( " << plugin.variableName << " && !" << plugin.variableName << "->preRun())\n"; + // This will disable the plugin for the rest of the run. + output << " " << plugin.variableName << " = nullptr;\n"; + } + output << +" return anyPluginActive();\n" +" }\n"; + + output << +" virtual void postRun() override\n" +" {\n"; + for( const PluginInfo& plugin : plugins[ type ] ) + { + output << " if( " << plugin.variableName << " )\n"; + output << " " << plugin.variableName << "->postRun();\n"; + } + output << +" }\n"; + + output << +" virtual void run() override {\n" +" if (preRun()) {\n" +" TraverseDecl(compiler.getASTContext().getTranslationUnitDecl());\n" +" postRun();\n" +" }\n" +" }\n" +" enum { isSharedPlugin = true };\n"; + + output << +" virtual bool setSharedPlugin( Plugin* plugin, const char* name ) override\n" +" {\n"; + bool first = true; + for( const PluginInfo& plugin : plugins[ type ] ) + { + output << " "; + if( !first ) + output << "else "; + first = false; + output << "if( strcmp( name, \"" << plugin.lowercaseName << "\" ) == 0 )\n"; + output << " " << plugin.variableName << " = static_cast< " << plugin.className << "* >( plugin );\n"; + } + output << +" else\n" +" return false;\n" +" return true;\n" +" }\n"; + + if( type == PluginVisitTemplates || type == PluginVisitTemplatesImplicit ) + output << "bool shouldVisitTemplateInstantiations() const { return true; }\n"; + if( type == PluginVisitImplicit || type == PluginVisitTemplatesImplicit ) + output << "bool shouldVisitImplicitCode() const { return true; }\n"; + + set< VisitFunctionInfo, VisitFunctionInfoLess > visitFunctions; + for( const PluginInfo& plugin : plugins[ type ] ) + for( const VisitFunctionInfo& visit : plugin.visitFunctions ) + visitFunctions.insert( visit ); + for( const VisitFunctionInfo& visit : visitFunctions ) + { + output << " bool " << visit.name << "(" << visit.argument << " arg)\n"; + output << +" {\n" +" if( ignoreLocation( arg ))\n" +" return true;\n"; + for( const PluginInfo& plugin : plugins[ type ] ) + { + if( plugin.visitFunctions.find( visit ) == plugin.visitFunctions.end()) + continue; + output << " if( " << plugin.variableName << " != nullptr "; + output << ")\n"; + output << " {\n"; + output << " if( !" << plugin.variableName << "->" << visit.name << "( arg ))\n"; + // This will disable the plugin for the rest of the run (as would returning false + // from Visit* normally do in the non-shared case). + output << " " << plugin.variableName << " = nullptr;\n"; + output << " }\n"; + } + output << +" return anyPluginActive();\n" +" }\n"; + } + + set< TraverseFunctionInfo, TraverseFunctionInfoLess > traverseFunctions; + for( const PluginInfo& plugin : plugins[ type ] ) + for( const TraverseFunctionInfo& traverse : plugin.traverseFunctions ) + traverseFunctions.insert( traverse ); + for( const TraverseFunctionInfo& traverse : traverseFunctions ) + { + output << " bool " << traverse.name << "(" << traverse.argument << " arg)\n"; + output << " {\n"; + for( const PluginInfo& plugin : plugins[ type ] ) + { + auto pluginTraverse = plugin.traverseFunctions.find( traverse ); + if( pluginTraverse == plugin.traverseFunctions.end()) + continue; + output << " " << plugin.className << "* save" << plugin.className << " = " << plugin.variableName << ";\n"; + if( pluginTraverse->hasPre ) + { + output << " if( " << plugin.variableName << " != nullptr "; + output << ")\n"; + output << " {\n"; + output << " if( !" << plugin.variableName << "->Pre" << traverse.name << "( arg ))\n"; + // This will disable the plugin for the time of the traverse, until restored later, + // just like directly returning from Traverse* would skip that part. + output << " {\n"; + output << " " << plugin.variableName << " = nullptr;\n"; + output << " ++activeRefCount;\n"; + output << " }\n"; + output << " }\n"; + } + } + output << " bool ret = RecursiveASTVisitor::" << traverse.name << "( arg );\n"; + for( const PluginInfo& plugin : plugins[ type ] ) + { + auto pluginTraverse = plugin.traverseFunctions.find( traverse ); + if( pluginTraverse == plugin.traverseFunctions.end()) + continue; + if( pluginTraverse->hasPost ) + { + output << " if( " << plugin.variableName << " != nullptr "; + output << ")\n"; + output << " {\n"; + output << " if( !" << plugin.variableName << "->Post" << traverse.name << "( arg, ret ))\n"; + // This will disable the plugin for the rest of the run. + output << " save" << plugin.className << " = nullptr;\n"; + output << " }\n"; + } + output << " if( " << plugin.variableName << " == nullptr && save" << plugin.className << " != nullptr )\n"; + output << " --activeRefCount;\n"; + output << " " << plugin.variableName << " = save" << plugin.className << ";\n"; + } + output << " if( false ) // silence -Wunused-function warnings\n"; + output << " {\n"; + for( const PluginInfo& plugin : plugins[ type ] ) + { + auto pluginTraverse = plugin.traverseFunctions.find( traverse ); + if( pluginTraverse == plugin.traverseFunctions.end()) + continue; + output << " " << plugin.variableName << "->" << pluginTraverse->name << "( arg );\n"; + } + output << " }\n"; + output << " return ret;\n"; + output << " }\n"; + } + + output << +"private:\n"; + + output << +" bool anyPluginActive() const\n" +" {\n" +" return activeRefCount > 0"; + for( const PluginInfo& plugin : plugins[ type ] ) + output << "\n || " << plugin.variableName << " != nullptr"; + output << ";\n"; + output << " }\n"; + + for( const PluginInfo& plugin : plugins[ type ] ) + output << " " << plugin.className << "* " << plugin.variableName << ";\n"; + output << " int activeRefCount;\n"; + + output << +"};\n" +"\n" +"loplugin::Plugin::Registration< SharedRecursiveASTVisitor" << pluginTypeNames[ type ] + << " > registration" << pluginTypeNames[ type ] << "(\"sharedvisitor" << pluginTypeNames[ type ] << "\");\n" +"\n"; +} + +static string getValue( const string& line, const char* tag ) +{ + size_t taglen = strlen( tag ); + if( line.size() < taglen + 2 ) + return string(); + if( line.compare( 0, taglen, tag ) != 0 ) + return string(); + if( line[ taglen ] != ':' ) + return string(); + return line.substr( taglen + 1 ); +} + +static bool readFile( const string& fileName ) +{ + ifstream file( fileName ); + if( !file ) + { + cerr << "Cannot open file " << fileName << endl; + return false; + } + PluginInfo pluginInfo; + string line; + do + { + getline( file, line ); + } while( !line.empty() && line[ 0 ] == '#' ); + string version = getValue( line, "InfoVersion" ); + if( version != "1" ) + { + cerr << "Incorrect version '" << version << "' in " << fileName << endl; + return false; + } + getline( file, line ); + pluginInfo.className = getValue( line, "ClassName" ); + pluginInfo.variableName = pluginInfo.className; + assert( pluginInfo.variableName.size() > 0 ); + pluginInfo.variableName[ 0 ] = tolower( pluginInfo.variableName[ 0 ] ); + pluginInfo.lowercaseName = pluginInfo.className; + for( char& c : pluginInfo.lowercaseName ) + c = tolower( c ); + pluginInfo.shouldVisitTemplateInstantiations = false; + pluginInfo.shouldVisitImplicitCode = false; + bool endOk = false; + for(;;) + { + string line; + getline( file, line ); + if( file.eof() || !file ) + { + cerr << "Unexpected end of file" << endl; + return false; + } + if( line.empty()) + continue; + if( line == "InfoEnd" ) + { + endOk = true; + break; + } + else if( line == "VisitFunctionStart" ) + { + VisitFunctionInfo visitInfo; + getline( file, line ); + visitInfo.name = getValue( line, "VisitFunctionName" ); + getline( file, line ); + visitInfo.argument = getValue( line, "VisitFunctionArgument" ); + getline( file, line ); + if( line != "VisitFunctionEnd" ) + { + cerr << "Missing VisitFunctionEnd" << endl; + return false; + } + pluginInfo.visitFunctions.insert( move( visitInfo )); + } + else if( line == "TraverseFunctionStart" ) + { + TraverseFunctionInfo traverseInfo; + getline( file, line ); + traverseInfo.name = getValue( line, "TraverseFunctionName" ); + getline( file, line ); + traverseInfo.argument = getValue( line, "TraverseFunctionArgument" ); + getline( file, line ); + traverseInfo.hasPre = getValue( line, "TraverseFunctionHasPre" ) == "1"; + getline( file, line ); + traverseInfo.hasPost = getValue( line, "TraverseFunctionHasPost" ) == "1"; + getline( file, line ); + if( line != "TraverseFunctionEnd" ) + { + cerr << "Missing TraverseFunctionEnd" << endl; + return false; + } + pluginInfo.traverseFunctions.insert( move( traverseInfo )); + } + else + { + string value; + value = getValue( line, "ShouldVisitTemplateInstantiations" ); + if( value == "1" ) + pluginInfo.shouldVisitTemplateInstantiations = true; + else + { + value = getValue( line, "ShouldVisitImplicitCode" ); + if( value == "1" ) + pluginInfo.shouldVisitImplicitCode = true; + else + { + cerr << "Unknown line " << line << endl; + return false; + } + } + } + } + + assert( endOk ); + (void)endOk; + + if( pluginInfo.shouldVisitTemplateInstantiations && pluginInfo.shouldVisitImplicitCode ) + plugins[ PluginVisitTemplatesImplicit ].push_back( move( pluginInfo )); + else if( pluginInfo.shouldVisitTemplateInstantiations ) + plugins[ PluginVisitTemplates ].push_back( move( pluginInfo )); + else if( pluginInfo.shouldVisitImplicitCode ) + plugins[ PluginVisitImplicit ].push_back( move( pluginInfo )); + else + plugins[ PluginBasic ].push_back( move( pluginInfo )); + + return true; +} + +int main(int argc, char** argv) +{ + for( int i = 1 ; i < argc; ++i ) + { + if( !readFile( argv[ i ] )) + { + cerr << "Error reading " << argv[ i ] << endl; + return 1; + } + } + for( int type = Plugin_Begin; type < Plugin_End; ++type ) + { + sort( plugins[ static_cast< PluginType >( type ) ].begin(), plugins[ static_cast< PluginType >( type ) ].end(), + []( const PluginInfo& l, const PluginInfo& r ) { return l.className < r.className; } ); + } + generate(); + return 0; +} diff --git a/compilerplugins/clang/sharedvisitor/precompiled_clang.hxx b/compilerplugins/clang/sharedvisitor/precompiled_clang.hxx new file mode 100644 index 000000000..3baf4b04f --- /dev/null +++ b/compilerplugins/clang/sharedvisitor/precompiled_clang.hxx @@ -0,0 +1,12 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +// Used by the analyzer to speed up processing plugin sources. + +#include "../plugin.hxx" |