//===- ASTSrcLocProcessor.cpp --------------------------------*- C++ -*----===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "ASTSrcLocProcessor.h" #include "clang/Frontend/CompilerInstance.h" #include "llvm/Support/JSON.h" #include "llvm/Support/MemoryBuffer.h" using namespace clang::tooling; using namespace llvm; using namespace clang::ast_matchers; ASTSrcLocProcessor::ASTSrcLocProcessor(StringRef JsonPath) : JsonPath(JsonPath) { MatchFinder::MatchFinderOptions FinderOptions; Finder = std::make_unique(std::move(FinderOptions)); Finder->addMatcher( cxxRecordDecl( isDefinition(), isSameOrDerivedFrom( namedDecl( hasAnyName( "clang::Stmt", "clang::Decl", "clang::CXXCtorInitializer", "clang::NestedNameSpecifierLoc", "clang::TemplateArgumentLoc", "clang::CXXBaseSpecifier", "clang::DeclarationNameInfo", "clang::TypeLoc")) .bind("nodeClade")), optionally(isDerivedFrom(cxxRecordDecl().bind("derivedFrom")))) .bind("className"), this); Finder->addMatcher( cxxRecordDecl(isDefinition(), hasAnyName("clang::PointerLikeTypeLoc", "clang::TypeofLikeTypeLoc")) .bind("templateName"), this); } std::unique_ptr ASTSrcLocProcessor::createASTConsumer(clang::CompilerInstance &Compiler, StringRef File) { return Finder->newASTConsumer(); } llvm::json::Object toJSON(llvm::StringMap> const &Obj) { using llvm::json::toJSON; llvm::json::Object JsonObj; for (const auto &Item : Obj) { JsonObj[Item.first()] = Item.second; } return JsonObj; } llvm::json::Object toJSON(llvm::StringMap const &Obj) { using llvm::json::toJSON; llvm::json::Object JsonObj; for (const auto &Item : Obj) { JsonObj[Item.first()] = Item.second; } return JsonObj; } llvm::json::Object toJSON(ClassData const &Obj) { llvm::json::Object JsonObj; if (!Obj.ASTClassLocations.empty()) JsonObj["sourceLocations"] = Obj.ASTClassLocations; if (!Obj.ASTClassRanges.empty()) JsonObj["sourceRanges"] = Obj.ASTClassRanges; if (!Obj.TemplateParms.empty()) JsonObj["templateParms"] = Obj.TemplateParms; if (!Obj.TypeSourceInfos.empty()) JsonObj["typeSourceInfos"] = Obj.TypeSourceInfos; if (!Obj.TypeLocs.empty()) JsonObj["typeLocs"] = Obj.TypeLocs; if (!Obj.NestedNameLocs.empty()) JsonObj["nestedNameLocs"] = Obj.NestedNameLocs; if (!Obj.DeclNameInfos.empty()) JsonObj["declNameInfos"] = Obj.DeclNameInfos; return JsonObj; } llvm::json::Object toJSON(llvm::StringMap const &Obj) { using llvm::json::toJSON; llvm::json::Object JsonObj; for (const auto &Item : Obj) JsonObj[Item.first()] = ::toJSON(Item.second); return JsonObj; } void WriteJSON(StringRef JsonPath, llvm::json::Object &&ClassInheritance, llvm::json::Object &&ClassesInClade, llvm::json::Object &&ClassEntries) { llvm::json::Object JsonObj; using llvm::json::toJSON; JsonObj["classInheritance"] = std::move(ClassInheritance); JsonObj["classesInClade"] = std::move(ClassesInClade); JsonObj["classEntries"] = std::move(ClassEntries); llvm::json::Value JsonVal(std::move(JsonObj)); bool WriteChange = false; std::string OutString; if (auto ExistingOrErr = MemoryBuffer::getFile(JsonPath, /*IsText=*/true)) { raw_string_ostream Out(OutString); Out << formatv("{0:2}", JsonVal); if (ExistingOrErr.get()->getBuffer() == Out.str()) return; WriteChange = true; } std::error_code EC; llvm::raw_fd_ostream JsonOut(JsonPath, EC, llvm::sys::fs::OF_Text); if (EC) return; if (WriteChange) JsonOut << OutString; else JsonOut << formatv("{0:2}", JsonVal); } void ASTSrcLocProcessor::generate() { WriteJSON(JsonPath, ::toJSON(ClassInheritance), ::toJSON(ClassesInClade), ::toJSON(ClassEntries)); } void ASTSrcLocProcessor::generateEmpty() { WriteJSON(JsonPath, {}, {}, {}); } std::vector CaptureMethods(std::string TypeString, const clang::CXXRecordDecl *ASTClass, const MatchFinder::MatchResult &Result) { auto publicAccessor = [](auto... InnerMatcher) { return cxxMethodDecl(isPublic(), parameterCountIs(0), isConst(), InnerMatcher...); }; auto BoundNodesVec = match( findAll( publicAccessor( ofClass(cxxRecordDecl( equalsNode(ASTClass), optionally(isDerivedFrom( cxxRecordDecl(hasAnyName("clang::Stmt", "clang::Decl")) .bind("stmtOrDeclBase"))), optionally(isDerivedFrom( cxxRecordDecl(hasName("clang::Expr")).bind("exprBase"))), optionally( isDerivedFrom(cxxRecordDecl(hasName("clang::TypeLoc")) .bind("typeLocBase"))))), returns(hasCanonicalType(asString(TypeString)))) .bind("classMethod")), *ASTClass, *Result.Context); std::vector Methods; for (const auto &BN : BoundNodesVec) { if (const auto *Node = BN.getNodeAs("classMethod")) { const auto *StmtOrDeclBase = BN.getNodeAs("stmtOrDeclBase"); const auto *TypeLocBase = BN.getNodeAs("typeLocBase"); const auto *ExprBase = BN.getNodeAs("exprBase"); // The clang AST has several methods on base classes which are overriden // pseudo-virtually by derived classes. // We record only the pseudo-virtual methods on the base classes to // avoid duplication. if (StmtOrDeclBase && (Node->getName() == "getBeginLoc" || Node->getName() == "getEndLoc" || Node->getName() == "getSourceRange")) continue; if (ExprBase && Node->getName() == "getExprLoc") continue; if (TypeLocBase && Node->getName() == "getLocalSourceRange") continue; if ((ASTClass->getName() == "PointerLikeTypeLoc" || ASTClass->getName() == "TypeofLikeTypeLoc") && Node->getName() == "getLocalSourceRange") continue; Methods.push_back(Node->getName().str()); } } return Methods; } void ASTSrcLocProcessor::run(const MatchFinder::MatchResult &Result) { const auto *ASTClass = Result.Nodes.getNodeAs("className"); StringRef CladeName; if (ASTClass) { if (const auto *NodeClade = Result.Nodes.getNodeAs("nodeClade")) CladeName = NodeClade->getName(); } else { ASTClass = Result.Nodes.getNodeAs("templateName"); CladeName = "TypeLoc"; } StringRef ClassName = ASTClass->getName(); ClassData CD; CD.ASTClassLocations = CaptureMethods("class clang::SourceLocation", ASTClass, Result); CD.ASTClassRanges = CaptureMethods("class clang::SourceRange", ASTClass, Result); CD.TypeSourceInfos = CaptureMethods("class clang::TypeSourceInfo *", ASTClass, Result); CD.TypeLocs = CaptureMethods("class clang::TypeLoc", ASTClass, Result); CD.NestedNameLocs = CaptureMethods("class clang::NestedNameSpecifierLoc", ASTClass, Result); CD.DeclNameInfos = CaptureMethods("struct clang::DeclarationNameInfo", ASTClass, Result); auto DI = CaptureMethods("const struct clang::DeclarationNameInfo &", ASTClass, Result); CD.DeclNameInfos.insert(CD.DeclNameInfos.end(), DI.begin(), DI.end()); if (const auto *DerivedFrom = Result.Nodes.getNodeAs("derivedFrom")) { if (const auto *Templ = llvm::dyn_cast( DerivedFrom)) { const auto &TArgs = Templ->getTemplateArgs(); SmallString<256> TArgsString; llvm::raw_svector_ostream OS(TArgsString); OS << DerivedFrom->getName() << '<'; clang::PrintingPolicy PPol(Result.Context->getLangOpts()); PPol.TerseOutput = true; for (unsigned I = 0; I < TArgs.size(); ++I) { if (I > 0) OS << ", "; TArgs.get(I).getAsType().print(OS, PPol); } OS << '>'; ClassInheritance[ClassName] = TArgsString.str().str(); } else { ClassInheritance[ClassName] = DerivedFrom->getName().str(); } } if (const auto *Templ = ASTClass->getDescribedClassTemplate()) { if (auto *TParams = Templ->getTemplateParameters()) { for (const auto &TParam : *TParams) { CD.TemplateParms.push_back(TParam->getName().str()); } } } ClassEntries[ClassName] = CD; ClassesInClade[CladeName].push_back(ClassName); }