diff options
Diffstat (limited to 'compilerplugins/clang/sharedvisitor/generator.cxx')
-rw-r--r-- | compilerplugins/clang/sharedvisitor/generator.cxx | 482 |
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; +} |