/* -*- 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>
// 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.shouldVisitImplicitCod
e )
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;
}