用于模糊解析C++的Clang

Ser*_*kiy 17 c++ syntax parsing fuzzy clang

是否可以通过clang与现有的libclang API解析C++与不完整的声明?即解析.cpp文件,不包括所有标题,动态推断声明.所以,例如以下文字:

A B::Foo(){return stuff();}
Run Code Online (Sandbox Code Playgroud)

将检测未知符号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在这种情况下可以处理的最小值.

Ale*_*son 5

除非你严格限制允许人们编写的代码,否则在不解析所有头文件的情况下,基本上不可能很好地解析C++(因此在关键字/正则表达式之外的语法突出显示).预处理器特别擅长为您解决问题.

关于模糊解析(在视觉工作室的背景下)的困难有一些想法,这可能是有趣的:http://blogs.msdn.com/b/vcblog/archive/2011/03/03/10136696.aspx


Dav*_*rcz 5

我知道问题相当陈旧,但请看一下:

LibFuzzy是一个基于Clang的Lexer启发式解析C++的库.模糊解析器是容错的,在不了解构建系统和不完整的源文件的情况下工作.由于解析器必然会进行猜测,因此生成的语法树可能部分错误.

它是一个来自clang-highlight的子项目,一个(实验?)工具似乎不再开发.

我只对模糊解析部分感兴趣,并在我的github页面上分叉,在那里我修复了几个小问题并使工具自治(它可以在clang的源代码树之外编译).不要尝试用C++ 14(G ++ 6的默认模式)编译它,因为会有冲突make_unique.

根据这个页面,clang-format有自己的模糊解析器(并且是积极开发的),但解析器(是?)更加紧密地耦合到工具.


Dav*_*rcz 4

我认为另一种解决方案比模糊解析更适合OP。

\n\n

解析时,clang 通过分析器的Sema部分维护语义信息。当遇到未知符号时,Sema将回退到ExternalSemaSource以获取有关该符号的一些信息。通过这个,你可以实现你想要的。

\n\n

这是如何设置它的快速示例。它并不完全起作用(我没有在LookupUnqualified方法中做任何事情),您可能需要做进一步的调查,我认为这是一个好的开始。

\n\n
// Declares clang::SyntaxOnlyAction.\n#include <clang/Frontend/FrontendActions.h>\n#include <clang/Tooling/CommonOptionsParser.h>\n#include <clang/Tooling/Tooling.h>\n#include <llvm/Support/CommandLine.h>\n#include <clang/AST/AST.h>\n#include <clang/AST/ASTConsumer.h>\n#include <clang/AST/RecursiveASTVisitor.h>\n#include <clang/Frontend/ASTConsumers.h>\n#include <clang/Frontend/FrontendActions.h>\n#include <clang/Frontend/CompilerInstance.h>\n#include <clang/Tooling/CommonOptionsParser.h>\n#include <clang/Tooling/Tooling.h>\n#include <clang/Rewrite/Core/Rewriter.h>\n#include <llvm/Support/raw_ostream.h>\n#include <clang/Sema/ExternalSemaSource.h>\n#include <clang/Sema/Sema.h>\n#include "clang/Basic/DiagnosticOptions.h"\n#include "clang/Frontend/TextDiagnosticPrinter.h"\n#include "clang/Frontend/CompilerInstance.h"\n#include "clang/Basic/TargetOptions.h"\n#include "clang/Basic/TargetInfo.h"\n#include "clang/Basic/FileManager.h"\n#include "clang/Basic/SourceManager.h"\n#include "clang/Lex/Preprocessor.h"\n#include "clang/Basic/Diagnostic.h"\n#include "clang/AST/ASTContext.h"\n#include "clang/AST/ASTConsumer.h"\n#include "clang/Parse/Parser.h"\n#include "clang/Parse/ParseAST.h"\n#include <clang/Sema/Lookup.h>\n\n#include <iostream>\nusing namespace clang;\nusing namespace clang::tooling;\nusing namespace llvm;\n\nclass ExampleVisitor : public RecursiveASTVisitor<ExampleVisitor> {\nprivate:\n  ASTContext *astContext;\n\npublic:\n  explicit ExampleVisitor(CompilerInstance *CI, StringRef file)\n      : astContext(&(CI->getASTContext())) {}\n\n  virtual bool VisitVarDecl(VarDecl *d) {\n    std::cout << d->getNameAsString() << "@\\n";\n    return true;\n  }\n};\n\nclass ExampleASTConsumer : public ASTConsumer {\nprivate:\n  ExampleVisitor visitor;\n\npublic:\n  explicit ExampleASTConsumer(CompilerInstance *CI, StringRef file)\n      : visitor(CI, file) {}\n  virtual void HandleTranslationUnit(ASTContext &Context) {\n    // de cette fa\xc3\xa7on, on applique le visiteur sur l\'ensemble de la translation\n    // unit\n    visitor.TraverseDecl(Context.getTranslationUnitDecl());\n  }\n};\n\nclass DynamicIDHandler : public clang::ExternalSemaSource {\npublic:\n  DynamicIDHandler(clang::Sema *Sema)\n      : m_Sema(Sema), m_Context(Sema->getASTContext()) {}\n  ~DynamicIDHandler() = default;\n\n  /// \\brief Provides last resort lookup for failed unqualified lookups\n  ///\n  /// If there is failed lookup, tell sema to create an artificial declaration\n  /// which is of dependent type. So the lookup result is marked as dependent\n  /// and the diagnostics are suppressed. After that is\'s an interpreter\'s\n  /// responsibility to fix all these fake declarations and lookups.\n  /// It is done by the DynamicExprTransformer.\n  ///\n  /// @param[out] R The recovered symbol.\n  /// @param[in] S The scope in which the lookup failed.\n  virtual bool LookupUnqualified(clang::LookupResult &R, clang::Scope *S) {\n     DeclarationName Name = R.getLookupName();\n     std::cout << Name.getAsString() << "\\n";\n    // IdentifierInfo *II = Name.getAsIdentifierInfo();\n    // SourceLocation Loc = R.getNameLoc();\n    // VarDecl *Result =\n    //     // VarDecl::Create(m_Context, R.getSema().getFunctionLevelDeclContext(),\n    //     //                 Loc, Loc, II, m_Context.DependentTy,\n    //     //                 /*TypeSourceInfo*/ 0, SC_None, SC_None);\n    // if (Result) {\n    //   R.addDecl(Result);\n    //   // Say that we can handle the situation. Clang should try to recover\n    //   return true;\n    // } else{\n    //   return false;\n    // }\n    return false;\n  }\n\nprivate:\n  clang::Sema *m_Sema;\n  clang::ASTContext &m_Context;\n};\n\n// *****************************************************************************/\n\nLangOptions getFormattingLangOpts(bool Cpp03 = false) {\n  LangOptions LangOpts;\n  LangOpts.CPlusPlus = 1;\n  LangOpts.CPlusPlus11 = Cpp03 ? 0 : 1;\n  LangOpts.CPlusPlus14 = Cpp03 ? 0 : 1;\n  LangOpts.LineComment = 1;\n  LangOpts.Bool = 1;\n  LangOpts.ObjC1 = 1;\n  LangOpts.ObjC2 = 1;\n  return LangOpts;\n}\n\nint main() {\n  using clang::CompilerInstance;\n  using clang::TargetOptions;\n  using clang::TargetInfo;\n  using clang::FileEntry;\n  using clang::Token;\n  using clang::ASTContext;\n  using clang::ASTConsumer;\n  using clang::Parser;\n  using clang::DiagnosticOptions;\n  using clang::TextDiagnosticPrinter;\n\n  CompilerInstance ci;\n  ci.getLangOpts() = getFormattingLangOpts(false);\n  DiagnosticOptions diagnosticOptions;\n  ci.createDiagnostics();\n\n  std::shared_ptr<clang::TargetOptions> pto = std::make_shared<clang::TargetOptions>();\n  pto->Triple = llvm::sys::getDefaultTargetTriple();\n\n  TargetInfo *pti = TargetInfo::CreateTargetInfo(ci.getDiagnostics(), pto);\n\n  ci.setTarget(pti);\n  ci.createFileManager();\n  ci.createSourceManager(ci.getFileManager());\n  ci.createPreprocessor(clang::TU_Complete);\n  ci.getPreprocessorOpts().UsePredefines = false;\n  ci.createASTContext();\n\n  ci.setASTConsumer(\n      llvm::make_unique<ExampleASTConsumer>(&ci, "../src/test.cpp"));\n\n  ci.createSema(TU_Complete, nullptr);\n  auto &sema = ci.getSema();\n  sema.Initialize();\n  DynamicIDHandler handler(&sema);\n  sema.addExternalSource(&handler);\n\n  const FileEntry *pFile = ci.getFileManager().getFile("../src/test.cpp");\n  ci.getSourceManager().setMainFileID(ci.getSourceManager().createFileID(\n      pFile, clang::SourceLocation(), clang::SrcMgr::C_User));\n  ci.getDiagnosticClient().BeginSourceFile(ci.getLangOpts(),\n                                           &ci.getPreprocessor());\n  clang::ParseAST(sema,true,false);\n  ci.getDiagnosticClient().EndSourceFile();\n\n  return 0;\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

这个想法和DynamicIDHandler类来自cling项目,其中未知符号是可变的(因此有注释和代码)。

\n