使用Clang LibTooling的Minimul源代码,这是一种非常常见的方式:
#include "pch.h"
#include "clang/Frontend/FrontendActions.h"
#include "clang/Tooling/CommonOptionsParser.h"
#include "clang/Tooling/Tooling.h"
#include "llvm/Support/CommandLine.h"
#include "clang/Driver/Options.h"
#include "clang/AST/AST.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/ASTConsumer.h"
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/Frontend/ASTConsumers.h"
#include "clang/Frontend/FrontendActions.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Tooling/CommonOptionsParser.h"
#include "clang/Tooling/Tooling.h"
#include "clang/Rewrite/Core/Rewriter.h"
#include <iostream>
using namespace std;
using namespace clang;
using namespace clang::driver;
using namespace clang::tooling;
using namespace llvm;
class ExampleVisitor : public RecursiveASTVisitor<ExampleVisitor> {
public:
explicit ExampleVisitor(CompilerInstance *CI) {}
};
class ExampleASTConsumer : public ASTConsumer {
private:
CompilerInstance *CI;
public:
explicit ExampleASTConsumer(CompilerInstance *CI) : CI(CI) {}
virtual void HandleTranslationUnit(ASTContext &Context) {
ExampleVisitor(CI).TraverseDecl(Context.getTranslationUnitDecl());
}
};
class ExampleFrontendAction : public ASTFrontendAction {
public:
virtual std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI, StringRef file) {
return std::unique_ptr<ASTConsumer>(new ExampleASTConsumer(&CI));
}
};
void run(int argc, const char **argv, llvm::cl::OptionCategory& tc) {
CommonOptionsParser op(argc, argv, tc);
ClangTool Tool(op.getCompilations(), op.getSourcePathList());
std::cout <<"getSourcePathList.size="<< op.getSourcePathList().size()<<"n";
int result = Tool.run(newFrontendActionFactory<ExampleFrontendAction>().get());
}
int main(int argc, const char **argv) {
llvm::cl::OptionCategory tc1("c1");
llvm::cl::OptionCategory tc2("c2");
llvm::cl::OptionCategory tc3("c3");
run(argc, argv,tc1);
run(argc, argv,tc2);
run(argc, argv,tc3);
std::cin.get();
return 0;
}
用于调试应用程序的参数为:
"the_only_source_file_to_scan.cpp" --
这很好。
输出是(来自main((上面的方法"run"(:
getSourcePathList.size=1
getSourcePathList.size=2
getSourcePathList.size=3
问题是main(( 使用上述相同参数调用 run(( 3 次,该参数仅包含 1 个要扫描的源文件,但每次存储在 CommonOptionsParser 中的源扫描列表的大小都会增加 1(列表中的每个项目都是来自 argv 的相同文件输入(,它似乎每次都会将要扫描的源文件附加到列表中。
上述所有内容在每次运行时都保存在新创建的临时变量中,那么 LibTooling 如何以及为什么保留上次运行的状态以及如何"重置"这些状态?
使用FixedCompilationDatabase
可以规避这个问题,它可以在一个进程中运行多个clangTool
上面的代码使用CommonOptionsParser,其代码位于
clanglibToolingCommonOptionsParser.cpp
在方法CommonOptionsParser::init中,有:
static cl::list<std::string> SourcePaths(...);
每次调用都会将其源添加到此静态变量中。因此,正是这个局部静态变量导致了先前调用的内存。在每次调用中,这个局部静态变量都会被 cl::P arseCommandLineOptions 以某种未知的方式修改,因为它根本没有传递到 cl::P arseCommandLineOptions 中。修改 SourcePath 后(即将当前调用的源添加到可能已经包含先前调用源的源的 SourcePaths(,它最终被复制到实例变量中。
顺便说一句,@AbaoZhang的线索帮助我找到位置,确实 CommonOptionsParser::init 内部使用 FixedCompilationDatabase,但不适用于上述情况。
static cl::list<std::string> SourcePaths(...);
@jw_ 是的,这段代码会产生问题。
您可以解决此问题。
for (auto iter = llvm::cl::AllSubCommands->OptionsMap.begin(); iter != llvm::cl::AllSubCommands->OptionsMap.end(); iter++)
{
iter->getValue()->setDefault();
}
for (auto iter = llvm::cl::AllSubCommands->PositionalOpts.begin(); iter != llvm::cl::AllSubCommands->PositionalOpts.end(); iter++)
{
(*iter)->setDefault();
}
for (auto iter = llvm::cl::AllSubCommands->SinkOpts.begin(); iter != llvm::cl::AllSubCommands->SinkOpts.end(); iter++)
{
(*iter)->setDefault();
}