/* -*- 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 #include #include #include #include #include #include #include #include // Info about a Visit* function in a plugin. struct VisitFunctionInfo { std::string name; std::string argument; }; // Info about a Traverse* function in a plugin. struct TraverseFunctionInfo { std::string name; std::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 { std::string className; // e.g. "BadStatics" std::string variableName; // e.g. "badStatics" std::string lowercaseName; bool shouldVisitTemplateInstantiations; bool shouldVisitImplicitCode; std::set< VisitFunctionInfo, VisitFunctionInfoLess > visitFunctions; std::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 std::vector< PluginInfo > plugins[ Plugin_End ]; void generateVisitor( PluginType type ); void generate() { std::ostream& output = std::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 \n" "\n" "#include \n" "#include \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\"" << std::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; std::ostream& output = std::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"; std::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"; } std::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 std::string getValue( const std::string& line, const char* tag ) { size_t taglen = strlen( tag ); if( line.size() < taglen + 2 ) return std::string(); if( line.compare( 0, taglen, tag ) != 0 ) return std::string(); if( line[ taglen ] != ':' ) return std::string(); return line.substr( taglen + 1 ); } static bool readFile( const std::string& fileName ) { std::ifstream file( fileName ); if( !file ) { std::cerr << "Cannot open file " << fileName << std::endl; return false; } PluginInfo pluginInfo; std::string line; do { getline( file, line ); } while( !line.empty() && line[ 0 ] == '#' ); std::string version = getValue( line, "InfoVersion" ); if( version != "1" ) { std::cerr << "Incorrect version '" << version << "' in " << fileName << std::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(;;) { std::string line; getline( file, line ); if( file.eof() || !file ) { std::cerr << "Unexpected end of file" << std::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" ) { std::cerr << "Missing VisitFunctionEnd" << std::endl; return false; } pluginInfo.visitFunctions.insert( std::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" ) { std::cerr << "Missing TraverseFunctionEnd" << std::endl; return false; } pluginInfo.traverseFunctions.insert( std::move( traverseInfo )); } else { std::string value; value = getValue( line, "ShouldVisitTemplateInstantiations" ); if( value == "1" ) pluginInfo.shouldVisitTemplateInstantiations = true; else { value = getValue( line, "ShouldVisitImplicitCode" ); if( value == "1" ) pluginInfo.shouldVisitImplicitCode = true; else { std::cerr << "Unknown line " << line << std::endl; return false; } } } } assert( endOk ); (void)endOk; if( pluginInfo.shouldVisitTemplateInstantiations && pluginInfo.shouldVisitImplicitCode ) plugins[ PluginVisitTemplatesImplicit ].push_back( std::move( pluginInfo )); else if( pluginInfo.shouldVisitTemplateInstantiations ) plugins[ PluginVisitTemplates ].push_back( std::move( pluginInfo )); else if( pluginInfo.shouldVisitImplicitCode ) plugins[ PluginVisitImplicit ].push_back( std::move( pluginInfo )); else plugins[ PluginBasic ].push_back( std::move( pluginInfo )); return true; } int main(int argc, char** argv) { for( int i = 1 ; i < argc; ++i ) { if( !readFile( argv[ i ] )) { std::cerr << "Error reading " << argv[ i ] << std::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; }