| //===- 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<MatchFinder>(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<clang::ASTConsumer> |
| ASTSrcLocProcessor::createASTConsumer(clang::CompilerInstance &Compiler, |
| StringRef File) { |
| return Finder->newASTConsumer(); |
| } |
| |
| llvm::json::Object toJSON(llvm::StringMap<std::vector<StringRef>> 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<std::string> 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<ClassData> 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<std::string> |
| 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(asString(TypeString))) |
| .bind("classMethod")), |
| *ASTClass, *Result.Context); |
| |
| std::vector<std::string> Methods; |
| for (const auto &BN : BoundNodesVec) { |
| if (const auto *Node = BN.getNodeAs<clang::NamedDecl>("classMethod")) { |
| const auto *StmtOrDeclBase = |
| BN.getNodeAs<clang::CXXRecordDecl>("stmtOrDeclBase"); |
| const auto *TypeLocBase = |
| BN.getNodeAs<clang::CXXRecordDecl>("typeLocBase"); |
| const auto *ExprBase = BN.getNodeAs<clang::CXXRecordDecl>("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<clang::CXXRecordDecl>("className"); |
| |
| StringRef CladeName; |
| if (ASTClass) { |
| if (const auto *NodeClade = |
| Result.Nodes.getNodeAs<clang::CXXRecordDecl>("nodeClade")) |
| CladeName = NodeClade->getName(); |
| } else { |
| ASTClass = Result.Nodes.getNodeAs<clang::CXXRecordDecl>("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<clang::CXXRecordDecl>("derivedFrom")) { |
| |
| if (const auto *Templ = |
| llvm::dyn_cast<clang::ClassTemplateSpecializationDecl>( |
| 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); |
| } |