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在这种情况下可以处理的最小值.
除非你严格限制允许人们编写的代码,否则在不解析所有头文件的情况下,基本上不可能很好地解析C++(因此在关键字/正则表达式之外的语法突出显示).预处理器特别擅长为您解决问题.
关于模糊解析(在视觉工作室的背景下)的困难有一些想法,这可能是有趣的:http://blogs.msdn.com/b/vcblog/archive/2011/03/03/10136696.aspx
我知道问题相当陈旧,但请看一下:
LibFuzzy是一个基于Clang的Lexer启发式解析C++的库.模糊解析器是容错的,在不了解构建系统和不完整的源文件的情况下工作.由于解析器必然会进行猜测,因此生成的语法树可能部分错误.
它是一个来自clang-highlight的子项目,一个(实验?)工具似乎不再开发.
我只对模糊解析部分感兴趣,并在我的github页面上分叉,在那里我修复了几个小问题并使工具自治(它可以在clang的源代码树之外编译).不要尝试用C++ 14(G ++ 6的默认模式)编译它,因为会有冲突make_unique.
根据这个页面,clang-format有自己的模糊解析器(并且是积极开发的),但解析器(是?)更加紧密地耦合到工具.
我认为另一种解决方案比模糊解析更适合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}\nRun Code Online (Sandbox Code Playgroud)\n\n这个想法和DynamicIDHandler类来自cling项目,其中未知符号是可变的(因此有注释和代码)。
\n