Clang模糊解析C ++
用它现有的libclang API用clang解析C ++的不完整声明是否完全可能? 即解析.cpp文件而不包含所有标题,即时推断声明。 所以,例如下面的文字:
A B::Foo(){return stuff();}
将检测未知的符号A,呼叫我的回调,它使用我的魔法启发式来扣除A是一个类,然后以与B和Foo相同的方式调用此回调。 最后,我希望能够推断出我看到一个B类的成员Foo返回A,而东西是一个函数..或者是这个效果。 上下文:我想看看我是否能够快速地解析所有的头文件,从而实现明智的语法突出显示和动态代码分析。
[编辑]澄清,我正在寻找非常严重的C ++解析,可能有一些启发式解除一些限制。
C ++语法充满了上下文依赖关系。 Foo()是函数调用还是Foo类的临时构造? 是Foo <Bar>的东西; 一个模板Foo <Bar>实例化和声明可变的东西,还是看起来很奇怪2调用重载操作符<和操作符>? 只能在上下文中进行判断,上下文通常来自解析头文件。
我正在寻找的是一种插入自定义约定规则的方法。 例如我知道我不会重载Win32符号,所以我可以放心地假设CreateFile总是一个函数,我甚至知道它的签名。 我也知道我所有的课程都以大写字母开头,都是名词,功能通常是动词,所以我可以合理地猜测Foo和Bar是类名。 在更复杂的场景中,我知道我不会写一个像<b> c这样的无副作用的表达式; 所以我可以假设a总是一个模板实例化。 等等。
因此,问题在于是否可以在每次遇到未知符号时使用Clang API来回调,并使用我自己的非C ++启发式方法给出答案。 如果我的启发式失败,那么显然,解析失败。 我不是在谈论解析Boost库:)我谈论的是非常简单的C ++,可能没有模板,只限于在这种情况下clang可以处理的最小值。
我认为另一种解决方案将比模糊解析更适合OP。
解析时,clang通过分析器的Sema部分保留语义信息。 遇到未知符号时,Sema将回退到ExternalSemaSource以获取有关此符号的一些信息。 通过这个,你可以实现你想要的。
这是一个如何设置它的简单示例。 它不完全是功能性的(我没有在LookupUnqualified方法中做任何事情),你可能需要做进一步的调查,我认为这是一个好的开始。
// Declares clang::SyntaxOnlyAction.
#include <clang/Frontend/FrontendActions.h>
#include <clang/Tooling/CommonOptionsParser.h>
#include <clang/Tooling/Tooling.h>
#include <llvm/Support/CommandLine.h>
#include <clang/AST/AST.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 <llvm/Support/raw_ostream.h>
#include <clang/Sema/ExternalSemaSource.h>
#include <clang/Sema/Sema.h>
#include "clang/Basic/DiagnosticOptions.h"
#include "clang/Frontend/TextDiagnosticPrinter.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Basic/TargetOptions.h"
#include "clang/Basic/TargetInfo.h"
#include "clang/Basic/FileManager.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Basic/Diagnostic.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/ASTConsumer.h"
#include "clang/Parse/Parser.h"
#include "clang/Parse/ParseAST.h"
#include <clang/Sema/Lookup.h>
#include <iostream>
using namespace clang;
using namespace clang::tooling;
using namespace llvm;
class ExampleVisitor : public RecursiveASTVisitor<ExampleVisitor> {
private:
ASTContext *astContext;
public:
explicit ExampleVisitor(CompilerInstance *CI, StringRef file)
: astContext(&(CI->getASTContext())) {}
virtual bool VisitVarDecl(VarDecl *d) {
std::cout << d->getNameAsString() << "@n";
return true;
}
};
class ExampleASTConsumer : public ASTConsumer {
private:
ExampleVisitor visitor;
public:
explicit ExampleASTConsumer(CompilerInstance *CI, StringRef file)
: visitor(CI, file) {}
virtual void HandleTranslationUnit(ASTContext &Context) {
// de cette façon, on applique le visiteur sur l'ensemble de la translation
// unit
visitor.TraverseDecl(Context.getTranslationUnitDecl());
}
};
class DynamicIDHandler : public clang::ExternalSemaSource {
public:
DynamicIDHandler(clang::Sema *Sema)
: m_Sema(Sema), m_Context(Sema->getASTContext()) {}
~DynamicIDHandler() = default;
/// brief Provides last resort lookup for failed unqualified lookups
///
/// If there is failed lookup, tell sema to create an artificial declaration
/// which is of dependent type. So the lookup result is marked as dependent
/// and the diagnostics are suppressed. After that is's an interpreter's
/// responsibility to fix all these fake declarations and lookups.
/// It is done by the DynamicExprTransformer.
///
/// @param[out] R The recovered symbol.
/// @param[in] S The scope in which the lookup failed.
virtual bool LookupUnqualified(clang::LookupResult &R, clang::Scope *S) {
DeclarationName Name = R.getLookupName();
std::cout << Name.getAsString() << "n";
// IdentifierInfo *II = Name.getAsIdentifierInfo();
// SourceLocation Loc = R.getNameLoc();
// VarDecl *Result =
// // VarDecl::Create(m_Context, R.getSema().getFunctionLevelDeclContext(),
// // Loc, Loc, II, m_Context.DependentTy,
// // /*TypeSourceInfo*/ 0, SC_None, SC_None);
// if (Result) {
// R.addDecl(Result);
// // Say that we can handle the situation. Clang should try to recover
// return true;
// } else{
// return false;
// }
return false;
}
private:
clang::Sema *m_Sema;
clang::ASTContext &m_Context;
};
// *****************************************************************************/
LangOptions getFormattingLangOpts(bool Cpp03 = false) {
LangOptions LangOpts;
LangOpts.CPlusPlus = 1;
LangOpts.CPlusPlus11 = Cpp03 ? 0 : 1;
LangOpts.CPlusPlus14 = Cpp03 ? 0 : 1;
LangOpts.LineComment = 1;
LangOpts.Bool = 1;
LangOpts.ObjC1 = 1;
LangOpts.ObjC2 = 1;
return LangOpts;
}
int main() {
using clang::CompilerInstance;
using clang::TargetOptions;
using clang::TargetInfo;
using clang::FileEntry;
using clang::Token;
using clang::ASTContext;
using clang::ASTConsumer;
using clang::Parser;
using clang::DiagnosticOptions;
using clang::TextDiagnosticPrinter;
CompilerInstance ci;
ci.getLangOpts() = getFormattingLangOpts(false);
DiagnosticOptions diagnosticOptions;
ci.createDiagnostics();
std::shared_ptr<clang::TargetOptions> pto = std::make_shared<clang::TargetOptions>();
pto->Triple = llvm::sys::getDefaultTargetTriple();
TargetInfo *pti = TargetInfo::CreateTargetInfo(ci.getDiagnostics(), pto);
ci.setTarget(pti);
ci.createFileManager();
ci.createSourceManager(ci.getFileManager());
ci.createPreprocessor(clang::TU_Complete);
ci.getPreprocessorOpts().UsePredefines = false;
ci.createASTContext();
ci.setASTConsumer(
llvm::make_unique<ExampleASTConsumer>(&ci, "../src/test.cpp"));
ci.createSema(TU_Complete, nullptr);
auto &sema = ci.getSema();
sema.Initialize();
DynamicIDHandler handler(&sema);
sema.addExternalSource(&handler);
const FileEntry *pFile = ci.getFileManager().getFile("../src/test.cpp");
ci.getSourceManager().setMainFileID(ci.getSourceManager().createFileID(
pFile, clang::SourceLocation(), clang::SrcMgr::C_User));
ci.getDiagnosticClient().BeginSourceFile(ci.getLangOpts(),
&ci.getPreprocessor());
clang::ParseAST(sema,true,false);
ci.getDiagnosticClient().EndSourceFile();
return 0;
}
这个想法和DynamicIDHandler类来自于将未知符号变为可变的项目(因此注释和代码)。
除非您严格限制人们被允许编写的代码,否则在解析所有头文件的情况下解析C ++(以及因此语法突出显示超出关键字/正则表达式)基本上是不可能的。 预处理器特别擅长为你搞砸。
有一些关于模糊解析(在visual studio的上下文中)的困难的想法,这可能是有趣的:http://blogs.msdn.com/b/vcblog/archive/2011/03/03/10136696.aspx
我知道这个问题相当古老,但看看这里:
LibFuzzy是一个基于Clang's Lexer的启发式解析C ++库。 模糊解析器是容错的,在不了解构建系统和不完整的源文件的情况下工作。 由于解析器必然进行猜测,因此生成的语法树可能部分错误。
这是clang-highlight的一个子项目,这是一个似乎不再被开发的(实验性?)工具。
我只对模糊解析部分感兴趣,并将其分叉在我的github页面上,我修复了几个小问题并使该工具具有自主性(可以在clang的源树之外编译)。 不要试图用C ++ 14(G ++ 6的默认模式)进行编译,因为make_unique
会有冲突。
根据这个页面,clang-format有它自己的模糊解析器(并且正在积极开发),但解析器(is?)更加紧密地耦合到该工具。
链接地址: http://www.djcxy.com/p/94569.html