summaryrefslogtreecommitdiffstats
path: root/compilerplugins/clang/sharedvisitor
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 16:51:28 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 16:51:28 +0000
commit940b4d1848e8c70ab7642901a68594e8016caffc (patch)
treeeb72f344ee6c3d9b80a7ecc079ea79e9fba8676d /compilerplugins/clang/sharedvisitor
parentInitial commit. (diff)
downloadlibreoffice-940b4d1848e8c70ab7642901a68594e8016caffc.tar.xz
libreoffice-940b4d1848e8c70ab7642901a68594e8016caffc.zip
Adding upstream version 1:7.0.4.upstream/1%7.0.4upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'compilerplugins/clang/sharedvisitor')
-rw-r--r--compilerplugins/clang/sharedvisitor/README45
-rw-r--r--compilerplugins/clang/sharedvisitor/analyzer.cxx305
-rw-r--r--compilerplugins/clang/sharedvisitor/dummyplugin.hxx82
-rw-r--r--compilerplugins/clang/sharedvisitor/generator.cxx482
-rw-r--r--compilerplugins/clang/sharedvisitor/precompiled_clang.hxx12
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"