2019-03-07 14:31:17 +01:00
|
|
|
/* -*- 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/.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
This tool generates 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).
|
|
|
|
|
|
|
|
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 .
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
#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 <iostream>
|
|
|
|
#include <fstream>
|
|
|
|
#include <set>
|
|
|
|
|
|
|
|
#include "../check.hxx"
|
|
|
|
#include "../check.cxx"
|
|
|
|
|
|
|
|
using namespace std;
|
|
|
|
|
|
|
|
using namespace clang;
|
|
|
|
|
|
|
|
using namespace loplugin;
|
|
|
|
|
|
|
|
// 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"
|
|
|
|
"\n";
|
|
|
|
|
|
|
|
output << "#undef LO_CLANG_SHARED_PLUGINS // to get sources of individual plugins\n";
|
|
|
|
for( const auto& pluginGroup : plugins )
|
|
|
|
for( const PluginInfo& plugin : pluginGroup )
|
|
|
|
output << "#include \"../" << plugin.lowercaseName << ".cxx\"" << endl;
|
|
|
|
|
|
|
|
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 << " {}\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 << " " << plugin.variableName << " = nullptr;\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 << " " << plugin.variableName << " = save" << plugin.className << ";\n";
|
|
|
|
}
|
|
|
|
output << " return ret;\n";
|
|
|
|
output << " }\n";
|
|
|
|
}
|
|
|
|
|
|
|
|
output <<
|
|
|
|
"private:\n";
|
|
|
|
|
|
|
|
output <<
|
|
|
|
" bool anyPluginActive() const\n"
|
|
|
|
" {\n";
|
|
|
|
first = true;
|
|
|
|
for( const PluginInfo& plugin : plugins[ type ] )
|
|
|
|
{
|
|
|
|
if( first )
|
|
|
|
output << " return " << plugin.variableName << " != nullptr";
|
|
|
|
else
|
|
|
|
output << "\n || " << plugin.variableName << " != nullptr";
|
|
|
|
first = false;
|
|
|
|
}
|
|
|
|
output << ";\n";
|
|
|
|
output << " }\n";
|
|
|
|
|
|
|
|
for( const PluginInfo& plugin : plugins[ type ] )
|
|
|
|
output << " " << plugin.className << "* " << plugin.variableName << ";\n";
|
|
|
|
|
|
|
|
output <<
|
|
|
|
"};\n"
|
|
|
|
"\n"
|
|
|
|
"loplugin::Plugin::Registration< SharedRecursiveASTVisitor" << pluginTypeNames[ type ]
|
|
|
|
<< " > registration" << pluginTypeNames[ type ] << "(\"sharedvisitor" << pluginTypeNames[ type ] << "\");\n"
|
|
|
|
"\n";
|
|
|
|
}
|
|
|
|
|
|
|
|
class CheckFileVisitor
|
|
|
|
: public RecursiveASTVisitor< CheckFileVisitor >
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
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( PluginInfo& pluginInfo, StringRef name )
|
|
|
|
{
|
|
|
|
TraverseFunctionInfo info;
|
|
|
|
info.name = name;
|
|
|
|
auto foundInfo = pluginInfo.traverseFunctions.find( info );
|
|
|
|
if( foundInfo != pluginInfo.traverseFunctions.end())
|
|
|
|
{
|
|
|
|
info = move( *foundInfo );
|
|
|
|
pluginInfo.traverseFunctions.erase( foundInfo );
|
|
|
|
}
|
|
|
|
return info;
|
|
|
|
}
|
|
|
|
|
2019-03-12 12:35:53 +01:00
|
|
|
static bool foundSomething;
|
|
|
|
|
2019-03-07 14:31:17 +01:00
|
|
|
bool CheckFileVisitor::VisitCXXRecordDecl( CXXRecordDecl* decl )
|
|
|
|
{
|
|
|
|
if( !isDerivedFrom( decl, inheritsPluginClassCheck ))
|
|
|
|
return true;
|
|
|
|
|
|
|
|
if( decl->getName() == "FilteringPlugin" || decl->getName() == "FilteringRewritePlugin" )
|
|
|
|
return true;
|
|
|
|
|
|
|
|
PluginInfo pluginInfo;
|
|
|
|
pluginInfo.className = decl->getName().str();
|
|
|
|
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;
|
|
|
|
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 )
|
|
|
|
{
|
|
|
|
VisitFunctionInfo visitInfo;
|
|
|
|
visitInfo.name = method->getName().str();
|
|
|
|
visitInfo.argument = method->getParamDecl( 0 )->getTypeSourceInfo()->getType().getAsString();
|
|
|
|
pluginInfo.visitFunctions.insert( move( visitInfo ));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
cerr << "Unhandled Visit* function: " << pluginInfo.className << "::" << method->getName().str() << endl;
|
|
|
|
abort();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if( method->getName().startswith( "Traverse" ))
|
|
|
|
{
|
|
|
|
if( method->getNumParams() == 1 )
|
|
|
|
{
|
|
|
|
TraverseFunctionInfo traverseInfo = findOrCreateTraverseFunctionInfo( pluginInfo, method->getName());
|
|
|
|
traverseInfo.argument = method->getParamDecl( 0 )->getTypeSourceInfo()->getType().getAsString();
|
|
|
|
pluginInfo.traverseFunctions.insert( move( traverseInfo ));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
cerr << "Unhandled Traverse* function: " << pluginInfo.className << "::" << method->getName().str() << endl;
|
|
|
|
abort();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if( method->getName().startswith( "PreTraverse" ))
|
|
|
|
{
|
|
|
|
TraverseFunctionInfo traverseInfo = findOrCreateTraverseFunctionInfo( pluginInfo, method->getName(). substr( 3 ));
|
|
|
|
traverseInfo.hasPre = true;
|
|
|
|
pluginInfo.traverseFunctions.insert( move( traverseInfo ));
|
|
|
|
}
|
|
|
|
else if( method->getName().startswith( "PostTraverse" ))
|
|
|
|
{
|
|
|
|
TraverseFunctionInfo traverseInfo = findOrCreateTraverseFunctionInfo( pluginInfo, method->getName().substr( 4 ));
|
|
|
|
traverseInfo.hasPost = true;
|
|
|
|
pluginInfo.traverseFunctions.insert( move( traverseInfo ));
|
|
|
|
}
|
|
|
|
else if( method->getName() == "shouldVisitTemplateInstantiations" )
|
|
|
|
pluginInfo.shouldVisitTemplateInstantiations = true;
|
|
|
|
else if( method->getName() == "shouldVisitImplicitCode" )
|
|
|
|
pluginInfo.shouldVisitImplicitCode = true;
|
|
|
|
else if( method->getName().startswith( "WalkUp" ))
|
|
|
|
{
|
|
|
|
cerr << "WalkUp function not supported for shared visitor: " << pluginInfo.className << "::" << method->getName().str() << endl;
|
|
|
|
abort();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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 ));
|
2019-03-12 12:35:53 +01:00
|
|
|
|
|
|
|
foundSomething = true;
|
2019-03-07 14:31:17 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
class FindNamedClassConsumer
|
|
|
|
: public ASTConsumer
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
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)
|
|
|
|
{
|
|
|
|
for( int i = 1; i < argc; ++ i )
|
|
|
|
{
|
|
|
|
string contents = readSourceFile(argv[i]);
|
|
|
|
if( contents.empty())
|
|
|
|
continue;
|
|
|
|
#define STRINGIFY2(a) #a
|
|
|
|
#define STRINGIFY(a) STRINGIFY2(a)
|
|
|
|
vector< string > args =
|
|
|
|
{
|
|
|
|
"-I" STRINGIFY(BUILDDIR) "/config_host", // plugin sources use e.g. config_global.h
|
|
|
|
"-I" STRINGIFY(CLANGDIR) "/include", // clang's headers
|
|
|
|
"-I" STRINGIFY(CLANGSYSINCLUDE), // clang system headers
|
|
|
|
"-std=c++11",
|
|
|
|
"-D__STDC_CONSTANT_MACROS", // Clang headers require these.
|
|
|
|
"-D__STDC_FORMAT_MACROS",
|
|
|
|
"-D__STDC_LIMIT_MACROS",
|
|
|
|
};
|
2019-03-12 12:35:53 +01:00
|
|
|
foundSomething = false;
|
2019-03-07 14:31:17 +01:00
|
|
|
if( !clang::tooling::runToolOnCodeWithArgs( new FindNamedClassAction, contents, args, argv[ i ] ))
|
|
|
|
{
|
|
|
|
cerr << "Failed to analyze: " << argv[ i ] << endl;
|
|
|
|
return 2;
|
|
|
|
}
|
2019-03-12 12:35:53 +01:00
|
|
|
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;
|
|
|
|
}
|
2019-03-07 14:31:17 +01:00
|
|
|
}
|
|
|
|
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;
|
|
|
|
}
|