summaryrefslogtreecommitdiffstats
path: root/compilerplugins/clang/sharedvisitor/generator.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'compilerplugins/clang/sharedvisitor/generator.cxx')
-rw-r--r--compilerplugins/clang/sharedvisitor/generator.cxx482
1 files changed, 482 insertions, 0 deletions
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;
+}