--- /dev/null
+//===- NodeIntrospection.h ------------------------------------*- 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
+//
+//===----------------------------------------------------------------------===//
+//
+// This file contains the implementation of the NodeIntrospection.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLING_NODEINTROSPECTION_H
+#define LLVM_CLANG_TOOLING_NODEINTROSPECTION_H
+
+#include "clang/AST/ASTTypeTraits.h"
+#include "clang/AST/DeclarationName.h"
+
+#include <memory>
+#include <set>
+
+namespace clang {
+
+class Stmt;
+
+namespace tooling {
+
+class LocationCall {
+public:
+ enum LocationCallFlags { NoFlags, ReturnsPointer, IsCast };
+ LocationCall(std::shared_ptr<LocationCall> on, std::string name,
+ LocationCallFlags flags = NoFlags)
+ : m_on(on), m_name(name), m_flags(flags) {}
+ LocationCall(std::shared_ptr<LocationCall> on, std::string name,
+ std::vector<std::string> const &args,
+ LocationCallFlags flags = NoFlags)
+ : m_on(on), m_name(name), m_flags(flags) {}
+
+ LocationCall *on() const { return m_on.get(); }
+ StringRef name() const { return m_name; }
+ std::vector<std::string> const &args() const { return m_args; }
+ bool returnsPointer() const { return m_flags & ReturnsPointer; }
+ bool isCast() const { return m_flags & IsCast; }
+
+private:
+ std::shared_ptr<LocationCall> m_on;
+ std::string m_name;
+ std::vector<std::string> m_args;
+ LocationCallFlags m_flags;
+};
+
+class LocationCallFormatterCpp {
+public:
+ static std::string format(LocationCall *Call);
+};
+
+namespace internal {
+struct RangeLessThan {
+ bool operator()(
+ std::pair<SourceRange, std::shared_ptr<LocationCall>> const &LHS,
+ std::pair<SourceRange, std::shared_ptr<LocationCall>> const &RHS) const;
+};
+} // namespace internal
+
+template <typename T, typename U, typename Comp = std::less<std::pair<T, U>>>
+using UniqueMultiMap = std::set<std::pair<T, U>, Comp>;
+
+using SourceLocationMap =
+ UniqueMultiMap<SourceLocation, std::shared_ptr<LocationCall>>;
+using SourceRangeMap =
+ UniqueMultiMap<SourceRange, std::shared_ptr<LocationCall>,
+ internal::RangeLessThan>;
+
+struct NodeLocationAccessors {
+ SourceLocationMap LocationAccessors;
+ SourceRangeMap RangeAccessors;
+};
+
+namespace NodeIntrospection {
+NodeLocationAccessors GetLocations(clang::Stmt const *Object);
+NodeLocationAccessors GetLocations(clang::DynTypedNode const &Node);
+} // namespace NodeIntrospection
+} // namespace tooling
+} // namespace clang
+#endif
add_subdirectory(Inclusions)
add_subdirectory(Refactoring)
add_subdirectory(ASTDiff)
+add_subdirectory(DumpTool)
add_subdirectory(Syntax)
add_subdirectory(DependencyScanning)
add_subdirectory(Transformer)
+find_package(Python3 COMPONENTS Interpreter)
+
+# The generation of ASTNodeAPI.json takes a long time in a
+# Debug build due to parsing AST.h. Disable the processing
+# but setting CLANG_TOOLING_BUILD_AST_INTROSPECTION as an
+# internal hidden setting to override.
+# When the processing is disabled, a trivial/empty JSON
+# file is generated by clang-ast-dump and generate_cxx_src_locs.py
+# generates the same API, but with a trivial implementation.
+
+option(CLANG_TOOLING_BUILD_AST_INTROSPECTION "Enable AST introspection" TRUE)
+set(skip_expensive_processing $<OR:$<CONFIG:Debug>,$<NOT:$<BOOL:${CLANG_TOOLING_BUILD_AST_INTROSPECTION}>>>)
+
+add_custom_command(
+ COMMENT Generate ASTNodeAPI.json
+ OUTPUT ${CMAKE_BINARY_DIR}/ASTNodeAPI.json
+ DEPENDS clang-ast-dump clang-headers
+ COMMAND
+ $<TARGET_FILE:clang-ast-dump>
+ # Skip this in debug mode because parsing AST.h is too slow
+ --skip-processing=${skip_expensive_processing}
+ --astheader=${CMAKE_SOURCE_DIR}/../clang/include/clang/AST/AST.h
+ -I ${CMAKE_BINARY_DIR}/lib/clang/${LLVM_VERSION_MAJOR}.${LLVM_VERSION_MINOR}.${LLVM_VERSION_PATCH}/include
+ -I ${CMAKE_SOURCE_DIR}/../clang/include
+ -I ${CMAKE_BINARY_DIR}/tools/clang/include/
+ -I ${CMAKE_BINARY_DIR}/include
+ -I ${CMAKE_SOURCE_DIR}/include
+ --json-output-path ${CMAKE_BINARY_DIR}/ASTNodeAPI.json
+)
+
+add_custom_target(run-ast-api-dump-tool
+ DEPENDS ${CMAKE_BINARY_DIR}/ASTNodeAPI.json
+)
+
+# Replace the last lib component of the current binary directory with include
+string(FIND ${CMAKE_CURRENT_BINARY_DIR} "/lib/" PATH_LIB_START REVERSE)
+if(PATH_LIB_START EQUAL -1)
+ message(FATAL_ERROR "Couldn't find lib component in binary directory")
+endif()
+math(EXPR PATH_LIB_END "${PATH_LIB_START}+5")
+string(SUBSTRING ${CMAKE_CURRENT_BINARY_DIR} 0 ${PATH_LIB_START} PATH_HEAD)
+string(SUBSTRING ${CMAKE_CURRENT_BINARY_DIR} ${PATH_LIB_END} -1 PATH_TAIL)
+string(CONCAT BINARY_INCLUDE_DIR ${PATH_HEAD} "/include/clang/" ${PATH_TAIL})
+
+add_custom_command(
+ COMMENT Generate NodeIntrospection.inc
+ OUTPUT ${BINARY_INCLUDE_DIR}/NodeIntrospection.inc
+ DEPENDS ${CMAKE_BINARY_DIR}/ASTNodeAPI.json ${CMAKE_CURRENT_SOURCE_DIR}/DumpTool/generate_cxx_src_locs.py
+ COMMAND
+ ${CMAKE_COMMAND} -E make_directory
+ ${CMAKE_CURRENT_BINARY_DIR}/generated/
+ COMMAND
+ ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/DumpTool/generate_cxx_src_locs.py
+ --json-input-path ${CMAKE_BINARY_DIR}/ASTNodeAPI.json
+ --output-file generated/NodeIntrospection.inc
+ --empty-implementation ${skip_expensive_processing}
+ COMMAND
+ ${CMAKE_COMMAND} -E copy_if_different
+ ${CMAKE_CURRENT_BINARY_DIR}/generated/NodeIntrospection.inc
+ ${BINARY_INCLUDE_DIR}/NodeIntrospection.inc
+)
+
+add_custom_target(run-ast-api-generate-tool
+ DEPENDS
+ ${BINARY_INCLUDE_DIR}/NodeIntrospection.inc
+)
+
add_clang_library(clangTooling
AllTUsExecution.cpp
ArgumentsAdjusters.cpp
Refactoring.cpp
RefactoringCallbacks.cpp
StandaloneExecution.cpp
+ NodeIntrospection.cpp
+ ${BINARY_INCLUDE_DIR}/NodeIntrospection.inc
Tooling.cpp
DEPENDS
--- /dev/null
+//===- APIData.h ---------------------------------------------*- C++ -*----===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_LIB_TOOLING_DUMPTOOL_APIDATA_H
+#define LLVM_CLANG_LIB_TOOLING_DUMPTOOL_APIDATA_H
+
+#include <string>
+#include <vector>
+
+namespace clang {
+namespace tooling {
+
+struct ClassData {
+
+ bool isEmpty() const {
+ return ASTClassLocations.empty() && ASTClassRanges.empty();
+ }
+
+ std::vector<std::string> ASTClassLocations;
+ std::vector<std::string> ASTClassRanges;
+ // TODO: Extend this with locations available via typelocs etc.
+};
+
+} // namespace tooling
+} // namespace clang
+
+#endif
--- /dev/null
+//===- ASTSrcLocProcessor.cpp --------------------------------*- C++ -*----===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ASTSrcLocProcessor.h"
+
+#include "clang/Frontend/CompilerInstance.h"
+#include "llvm/Support/JSON.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(
+ // TODO: Extend this with other clades
+ namedDecl(hasName("clang::Stmt")).bind("nodeClade")),
+ optionally(isDerivedFrom(cxxRecordDecl().bind("derivedFrom"))))
+ .bind("className"),
+ 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<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(ClassData const &Obj) {
+ llvm::json::Object JsonObj;
+
+ if (!Obj.ASTClassLocations.empty())
+ JsonObj["sourceLocations"] = Obj.ASTClassLocations;
+ if (!Obj.ASTClassRanges.empty())
+ JsonObj["sourceRanges"] = Obj.ASTClassRanges;
+ return JsonObj;
+}
+
+llvm::json::Object toJSON(llvm::StringMap<ClassData> const &Obj) {
+ using llvm::json::toJSON;
+
+ llvm::json::Object JsonObj;
+ for (const auto &Item : Obj) {
+ if (!Item.second.isEmpty())
+ JsonObj[Item.first()] = ::toJSON(Item.second);
+ }
+ return JsonObj;
+}
+
+void WriteJSON(std::string JsonPath,
+ llvm::StringMap<StringRef> const &ClassInheritance,
+ llvm::StringMap<std::vector<StringRef>> const &ClassesInClade,
+ llvm::StringMap<ClassData> const &ClassEntries) {
+ llvm::json::Object JsonObj;
+
+ using llvm::json::toJSON;
+
+ JsonObj["classInheritance"] = ::toJSON(ClassInheritance);
+ JsonObj["classesInClade"] = ::toJSON(ClassesInClade);
+ JsonObj["classEntries"] = ::toJSON(ClassEntries);
+
+ std::error_code EC;
+ llvm::raw_fd_ostream JsonOut(JsonPath, EC, llvm::sys::fs::F_Text);
+ if (EC)
+ return;
+
+ llvm::json::Value JsonVal(std::move(JsonObj));
+ JsonOut << formatv("{0:2}", JsonVal);
+}
+
+void ASTSrcLocProcessor::generate() {
+ WriteJSON(JsonPath, ClassInheritance, ClassesInClade, ClassEntries);
+}
+
+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(equalsNode(ASTClass)),
+ 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")) {
+ // Only record the getBeginLoc etc on Stmt etc, because it will call
+ // more-derived implementations pseudo-virtually.
+ if ((ASTClass->getName() != "Stmt" && ASTClass->getName() != "Decl") &&
+ (Node->getName() == "getBeginLoc" || Node->getName() == "getEndLoc" ||
+ Node->getName() == "getSourceRange")) {
+ continue;
+ }
+ // Only record the getExprLoc on Expr, because it will call
+ // more-derived implementations pseudo-virtually.
+ if (ASTClass->getName() != "Expr" && Node->getName() == "getExprLoc") {
+ continue;
+ }
+ Methods.push_back(Node->getName().str());
+ }
+ }
+ return Methods;
+}
+
+void ASTSrcLocProcessor::run(const MatchFinder::MatchResult &Result) {
+
+ if (const auto *ASTClass =
+ Result.Nodes.getNodeAs<clang::CXXRecordDecl>("className")) {
+
+ StringRef ClassName = ASTClass->getName();
+
+ ClassData CD;
+
+ const auto *NodeClade =
+ Result.Nodes.getNodeAs<clang::CXXRecordDecl>("nodeClade");
+ StringRef CladeName = NodeClade->getName();
+
+ if (const auto *DerivedFrom =
+ Result.Nodes.getNodeAs<clang::CXXRecordDecl>("derivedFrom"))
+ ClassInheritance[ClassName] = DerivedFrom->getName();
+
+ CD.ASTClassLocations =
+ CaptureMethods("class clang::SourceLocation", ASTClass, Result);
+ CD.ASTClassRanges =
+ CaptureMethods("class clang::SourceRange", ASTClass, Result);
+
+ if (!CD.isEmpty()) {
+ ClassEntries[ClassName] = CD;
+ ClassesInClade[CladeName].push_back(ClassName);
+ }
+ }
+}
--- /dev/null
+//===- ASTSrcLocProcessor.h ---------------------------------*- C++ -*-----===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLING_DUMPTOOL_ASTSRCLOCPROCESSOR_H
+#define LLVM_CLANG_TOOLING_DUMPTOOL_ASTSRCLOCPROCESSOR_H
+
+#include "APIData.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "llvm/ADT/StringRef.h"
+#include <memory>
+#include <string>
+#include <vector>
+
+namespace clang {
+
+class CompilerInstance;
+
+namespace tooling {
+
+class ASTSrcLocProcessor : public ast_matchers::MatchFinder::MatchCallback {
+public:
+ explicit ASTSrcLocProcessor(StringRef JsonPath);
+
+ std::unique_ptr<ASTConsumer> createASTConsumer(CompilerInstance &Compiler,
+ StringRef File);
+
+ void generate();
+
+private:
+ void run(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+ llvm::StringMap<StringRef> ClassInheritance;
+ llvm::StringMap<std::vector<StringRef>> ClassesInClade;
+ llvm::StringMap<ClassData> ClassEntries;
+
+ std::string JsonPath;
+ std::unique_ptr<clang::ast_matchers::MatchFinder> Finder;
+};
+
+} // namespace tooling
+} // namespace clang
+
+#endif
--- /dev/null
+
+add_clang_executable(clang-ast-dump
+ ASTSrcLocProcessor.cpp
+ ClangSrcLocDump.cpp
+)
+
+target_link_libraries(clang-ast-dump
+ PRIVATE
+ clangAST
+ clangASTMatchers
+ clangBasic
+ clangDriver
+ clangFormat
+ clangFrontend
+ clangLex
+ clangRewrite
+ clangSerialization
+ clangToolingCore
+)
--- /dev/null
+//===- ClangSrcLocDump.cpp ------------------------------------*- C++ -*---===//\r
+//\r
+// The LLVM Compiler Infrastructure\r
+//\r
+// This file is distributed under the University of Illinois Open Source\r
+// License. See LICENSE.TXT for details.\r
+//\r
+//===----------------------------------------------------------------------===//\r
+\r
+#include "clang/Basic/Diagnostic.h"\r
+#include "clang/Driver/Compilation.h"\r
+#include "clang/Driver/Driver.h"\r
+#include "clang/Driver/Job.h"\r
+#include "clang/Driver/Options.h"\r
+#include "clang/Driver/Tool.h"\r
+#include "clang/Frontend/CompilerInstance.h"\r
+#include "clang/Frontend/TextDiagnosticPrinter.h"\r
+#include "clang/Tooling/Tooling.h"\r
+#include "llvm/Option/ArgList.h"\r
+#include "llvm/Support/CommandLine.h"\r
+#include "llvm/Support/Host.h"\r
+#include "llvm/Support/JSON.h"\r
+\r
+#include "ASTSrcLocProcessor.h"\r
+\r
+using namespace clang::tooling;\r
+using namespace clang;\r
+using namespace llvm;\r
+\r
+static cl::list<std::string> IncludeDirectories(\r
+ "I", cl::desc("Include directories to use while compiling"),\r
+ cl::value_desc("directory"), cl::Required, cl::OneOrMore, cl::Prefix);\r
+\r
+static cl::opt<std::string>\r
+ AstHeaderFile("astheader", cl::desc("AST header to parse API from"),\r
+ cl::Required, cl::value_desc("AST header file"));\r
+\r
+static cl::opt<bool>\r
+ SkipProcessing("skip-processing",\r
+ cl::desc("Avoid processing the AST header file"),\r
+ cl::Required, cl::value_desc("bool"));\r
+\r
+static cl::opt<std::string> JsonOutputPath("json-output-path",\r
+ cl::desc("json output path"),\r
+ cl::Required,\r
+ cl::value_desc("path"));\r
+\r
+class ASTSrcLocGenerationAction : public clang::ASTFrontendAction {\r
+public:\r
+ ASTSrcLocGenerationAction() : Processor(JsonOutputPath) {}\r
+\r
+ ~ASTSrcLocGenerationAction() { Processor.generate(); }\r
+\r
+ std::unique_ptr<clang::ASTConsumer>\r
+ CreateASTConsumer(clang::CompilerInstance &Compiler,\r
+ llvm::StringRef File) override {\r
+ return Processor.createASTConsumer(Compiler, File);\r
+ }\r
+\r
+private:\r
+ ASTSrcLocProcessor Processor;\r
+};\r
+\r
+int main(int argc, const char **argv) {\r
+\r
+ cl::ParseCommandLineOptions(argc, argv);\r
+\r
+ if (SkipProcessing) {\r
+ std::error_code EC;\r
+ llvm::raw_fd_ostream JsonOut(JsonOutputPath, EC, llvm::sys::fs::F_Text);\r
+ if (EC)\r
+ return 1;\r
+ JsonOut << formatv("{0:2}", llvm::json::Value(llvm::json::Object()));\r
+ return 0;\r
+ }\r
+\r
+ std::vector<std::string> Args;\r
+ Args.push_back("-xc++-header");\r
+\r
+ llvm::transform(IncludeDirectories, std::back_inserter(Args),\r
+ [](const std::string &IncDir) { return "-I" + IncDir; });\r
+\r
+ Args.push_back(AstHeaderFile);\r
+\r
+ std::vector<const char *> Argv(Args.size(), nullptr);\r
+ llvm::transform(Args, Argv.begin(),\r
+ [](const std::string &Arg) { return Arg.c_str(); });\r
+\r
+ IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();\r
+ unsigned MissingArgIndex, MissingArgCount;\r
+ auto Opts = driver::getDriverOptTable();\r
+ auto ParsedArgs = Opts.ParseArgs(llvm::makeArrayRef(Argv).slice(1),\r
+ MissingArgIndex, MissingArgCount);\r
+ ParseDiagnosticArgs(*DiagOpts, ParsedArgs);\r
+ TextDiagnosticPrinter DiagnosticPrinter(llvm::errs(), &*DiagOpts);\r
+ DiagnosticsEngine Diagnostics(\r
+ IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs()), &*DiagOpts,\r
+ &DiagnosticPrinter, false);\r
+\r
+ FileManager Files(FileSystemOptions(), vfs::getRealFileSystem());\r
+\r
+ auto Driver = std::make_unique<driver::Driver>(\r
+ "clang++", llvm::sys::getDefaultTargetTriple(), Diagnostics,\r
+ "ast-api-dump-tool", &Files.getVirtualFileSystem());\r
+\r
+ auto Comp = Driver->BuildCompilation(llvm::makeArrayRef(Argv));\r
+ if (!Comp)\r
+ return 1;\r
+\r
+ const auto &Jobs = Comp->getJobs();\r
+ if (Jobs.size() != 1 || !isa<driver::Command>(*Jobs.begin())) {\r
+ SmallString<256> error_msg;\r
+ llvm::raw_svector_ostream error_stream(error_msg);\r
+ Jobs.Print(error_stream, "; ", true);\r
+ return 1;\r
+ }\r
+\r
+ const auto &Cmd = cast<driver::Command>(*Jobs.begin());\r
+ const llvm::opt::ArgStringList &CC1Args = Cmd.getArguments();\r
+\r
+ auto Invocation = std::make_unique<CompilerInvocation>();\r
+ CompilerInvocation::CreateFromArgs(*Invocation, CC1Args, Diagnostics);\r
+\r
+ CompilerInstance Compiler(std::make_shared<clang::PCHContainerOperations>());\r
+ Compiler.setInvocation(std::move(Invocation));\r
+\r
+ Compiler.createDiagnostics(&DiagnosticPrinter, false);\r
+ if (!Compiler.hasDiagnostics())\r
+ return 1;\r
+\r
+ Compiler.createSourceManager(Files);\r
+\r
+ ASTSrcLocGenerationAction ScopedToolAction;\r
+ Compiler.ExecuteAction(ScopedToolAction);\r
+\r
+ Files.clearStatCache();\r
+\r
+ return 0;\r
+}\r
--- /dev/null
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+import os
+import sys
+import json
+
+from optparse import OptionParser
+
+parser = OptionParser()
+parser.add_option('--json-input-path',
+ help='Read API description from FILE', metavar='FILE')
+parser.add_option('--output-file', help='Generate output in FILEPATH',
+ metavar='FILEPATH')
+parser.add_option('--empty-implementation', help='Generate empty implementation',
+ action="store", type="int", metavar='FILEPATH')
+
+(options, args) = parser.parse_args()
+
+if options.empty_implementation:
+ with open(os.path.join(os.getcwd(),
+ options.output_file), 'w') as f:
+ f.write("""
+namespace clang {
+namespace tooling {
+
+NodeLocationAccessors NodeIntrospection::GetLocations(clang::Stmt const *) {
+ return {};
+}
+NodeLocationAccessors
+NodeIntrospection::GetLocations(clang::DynTypedNode const &) {
+ return {};
+}
+} // namespace tooling
+} // namespace clang
+""")
+ sys.exit(0)
+
+with open(options.json_input_path) as f:
+ jsonData = json.load(f)
+
+
+class Generator(object):
+
+ implementationContent = ''
+
+ def GeneratePrologue(self):
+
+ self.implementationContent += \
+ """
+/*===- Generated file -------------------------------------------*- C++ -*-===*\
+|* *|
+|* Introspection of available AST node SourceLocations *|
+|* *|
+|* Automatically generated file, do not edit! *|
+|* *|
+\*===----------------------------------------------------------------------===*/
+
+namespace clang {
+namespace tooling {
+
+using LocationAndString = SourceLocationMap::value_type;
+using RangeAndString = SourceRangeMap::value_type;
+"""
+
+ def GenerateBaseGetLocationsDeclaration(self, CladeName):
+ self.implementationContent += \
+ """
+void GetLocationsImpl(std::shared_ptr<LocationCall> const& Prefix, clang::{0} const *Object, SourceLocationMap &Locs,
+ SourceRangeMap &Rngs);
+""".format(CladeName)
+
+ def GenerateSrcLocMethod(self, ClassName, ClassData):
+
+ self.implementationContent += \
+ """
+static void GetLocations{0}(std::shared_ptr<LocationCall> const& Prefix,
+ clang::{0} const &Object,
+ SourceLocationMap &Locs, SourceRangeMap &Rngs)
+{{
+""".format(ClassName)
+
+ if 'sourceLocations' in ClassData:
+ for locName in ClassData['sourceLocations']:
+ self.implementationContent += \
+ """
+ Locs.insert(LocationAndString(Object.{0}(), std::make_shared<LocationCall>(Prefix, "{0}")));
+""".format(locName)
+
+ self.implementationContent += '\n'
+
+ if 'sourceRanges' in ClassData:
+ for rngName in ClassData['sourceRanges']:
+ self.implementationContent += \
+ """
+ Rngs.insert(RangeAndString(Object.{0}(), std::make_shared<LocationCall>(Prefix, "{0}")));
+""".format(rngName)
+
+ self.implementationContent += '\n'
+
+ self.implementationContent += '}\n'
+
+ def GenerateFiles(self, OutputFile):
+ with open(os.path.join(os.getcwd(),
+ OutputFile), 'w') as f:
+ f.write(self.implementationContent)
+
+ def GenerateTrivialBaseGetLocationsFunction(self, CladeName):
+ MethodReturnType = 'NodeLocationAccessors'
+
+ Signature = \
+ 'GetLocations(clang::{0} const *Object)'.format(CladeName)
+
+ self.implementationContent += \
+ '{0} NodeIntrospection::{1} {{ return {{}}; }}'.format(MethodReturnType,
+ Signature)
+
+ def GenerateBaseGetLocationsFunction(self, ASTClassNames, CladeName):
+
+ MethodReturnType = 'NodeLocationAccessors'
+
+ Signature = \
+ 'GetLocations(clang::{0} const *Object)'.format(CladeName)
+ ImplSignature = \
+ """
+GetLocationsImpl(std::shared_ptr<LocationCall> const& Prefix,
+ clang::{0} const *Object, SourceLocationMap &Locs,
+ SourceRangeMap &Rngs)
+""".format(CladeName)
+
+ self.implementationContent += \
+ 'void {0} {{ GetLocations{1}(Prefix, *Object, Locs, Rngs);'.format(ImplSignature,
+ CladeName)
+
+ for ASTClassName in ASTClassNames:
+ if ASTClassName != CladeName:
+ self.implementationContent += \
+ """
+if (auto Derived = llvm::dyn_cast<clang::{0}>(Object)) {{
+ GetLocations{0}(Prefix, *Derived, Locs, Rngs);
+}}
+""".format(ASTClassName)
+
+ self.implementationContent += '}'
+
+ self.implementationContent += \
+ """
+{0} NodeIntrospection::{1} {{
+ NodeLocationAccessors Result;
+ std::shared_ptr<LocationCall> Prefix;
+
+ GetLocationsImpl(Prefix, Object, Result.LocationAccessors,
+ Result.RangeAccessors);
+""".format(MethodReturnType,
+ Signature)
+
+ self.implementationContent += 'return Result; }'
+
+ def GenerateDynNodeVisitor(self, CladeNames):
+ MethodReturnType = 'NodeLocationAccessors'
+
+ Signature = \
+ 'GetLocations(clang::DynTypedNode const &Node)'
+
+ self.implementationContent += MethodReturnType \
+ + ' NodeIntrospection::' + Signature + '{'
+
+ for CladeName in CladeNames:
+ self.implementationContent += \
+ """
+ if (const auto *N = Node.get<{0}>())
+ return GetLocations(const_cast<{0} *>(N));""".format(CladeName)
+
+ self.implementationContent += 'return {}; }'
+
+ def GenerateEpilogue(self):
+
+ self.implementationContent += '''
+ }
+}
+'''
+
+
+g = Generator()
+
+g.GeneratePrologue()
+
+if 'classesInClade' in jsonData:
+ for (CladeName, ClassNameData) in jsonData['classesInClade'].items():
+ g.GenerateBaseGetLocationsDeclaration(CladeName)
+
+ for (ClassName, ClassAccessors) in jsonData['classEntries'].items():
+ if ClassAccessors:
+ g.GenerateSrcLocMethod(ClassName, ClassAccessors)
+
+ for (CladeName, ClassNameData) in jsonData['classesInClade'].items():
+ g.GenerateBaseGetLocationsFunction(ClassNameData, CladeName)
+
+ g.GenerateDynNodeVisitor(jsonData['classesInClade'].keys())
+else:
+ for CladeName in ['Stmt']:
+ g.GenerateTrivialBaseGetLocationsFunction(CladeName)
+
+ g.GenerateDynNodeVisitor([])
+
+g.GenerateEpilogue()
+
+g.GenerateFiles(options.output_file)
--- /dev/null
+//===- NodeIntrospection.h -----------------------------------*- 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
+//
+//===----------------------------------------------------------------------===//
+//
+// This file contains the implementation of the NodeIntrospection.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Tooling/NodeIntrospection.h"
+
+#include "clang/AST/AST.h"
+
+namespace clang {
+
+namespace tooling {
+
+std::string LocationCallFormatterCpp::format(LocationCall *Call) {
+ SmallVector<LocationCall *> vec;
+ while (Call) {
+ vec.push_back(Call);
+ Call = Call->on();
+ }
+ std::string result;
+ for (auto *VecCall : llvm::reverse(llvm::makeArrayRef(vec).drop_front())) {
+ result +=
+ (VecCall->name() + "()" + (VecCall->returnsPointer() ? "->" : "."))
+ .str();
+ }
+ result += (vec.back()->name() + "()").str();
+ return result;
+}
+
+namespace internal {
+bool RangeLessThan::operator()(
+ std::pair<SourceRange, std::shared_ptr<LocationCall>> const &LHS,
+ std::pair<SourceRange, std::shared_ptr<LocationCall>> const &RHS) const {
+ if (!LHS.first.isValid() || !RHS.first.isValid())
+ return false;
+
+ if (LHS.first.getBegin() < RHS.first.getBegin())
+ return true;
+ else if (LHS.first.getBegin() != RHS.first.getBegin())
+ return false;
+
+ if (LHS.first.getEnd() < RHS.first.getEnd())
+ return true;
+ else if (LHS.first.getEnd() != RHS.first.getEnd())
+ return false;
+
+ return LHS.second->name() < RHS.second->name();
+}
+} // namespace internal
+
+} // namespace tooling
+} // namespace clang
+
+#include "clang/Tooling/NodeIntrospection.inc"
add_subdirectory(AST)
add_subdirectory(CrossTU)
add_subdirectory(Tooling)
+add_subdirectory(Introspection)
add_subdirectory(Format)
add_subdirectory(Frontend)
add_subdirectory(Rewrite)
--- /dev/null
+set(LLVM_LINK_COMPONENTS
+ ${LLVM_TARGETS_TO_BUILD}
+ FrontendOpenMP
+ Support
+ )
+
+add_clang_unittest(IntrospectionTests
+ IntrospectionTest.cpp
+ )
+
+clang_target_link_libraries(IntrospectionTests
+ PRIVATE
+ clangAST
+ clangASTMatchers
+ clangTooling
+ clangBasic
+ clangSerialization
+ clangFrontend
+ )
+target_compile_definitions(IntrospectionTests PRIVATE
+ SKIP_INTROSPECTION_GENERATION=$<OR:$<CONFIG:Debug>,$<NOT:$<BOOL:${CLANG_TOOLING_BUILD_AST_INTROSPECTION}>>>
+)
+target_link_libraries(IntrospectionTests
+ PRIVATE
+ LLVMTestingSupport
+)
--- /dev/null
+//===- unittest/Introspection/IntrospectionTest.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
+//
+//===----------------------------------------------------------------------===//
+//
+// Tests for AST location API introspection.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Tooling/NodeIntrospection.h"
+#include "clang/Tooling/Tooling.h"
+#include "gmock/gmock-matchers.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+using namespace clang;
+using namespace clang::ast_matchers;
+using namespace clang::tooling;
+
+using ::testing::UnorderedElementsAre;
+using ::testing::Pair;
+
+#if SKIP_INTROSPECTION_GENERATION
+
+TEST(Introspection, NonFatalAPI) {
+ auto AST = buildASTFromCode("void foo() {} void bar() { foo(); }", "foo.cpp",
+ std::make_shared<PCHContainerOperations>());
+ auto &Ctx = AST->getASTContext();
+ auto &TU = *Ctx.getTranslationUnitDecl();
+
+ auto BoundNodes = ast_matchers::match(
+ decl(hasDescendant(
+ callExpr(callee(functionDecl(hasName("foo")))).bind("fooCall"))),
+ TU, Ctx);
+
+ EXPECT_EQ(BoundNodes.size(), 1u);
+
+ auto *FooCall = BoundNodes[0].getNodeAs<CallExpr>("fooCall");
+
+ auto result = NodeIntrospection::GetLocations(FooCall);
+
+ EXPECT_EQ(result.LocationAccessors.size(), 0);
+ EXPECT_EQ(result.RangeAccessors.size(), 0);
+}
+
+#else
+
+TEST(Introspection, SourceLocations) {
+ auto AST = buildASTFromCode("void foo() {} void bar() { foo(); }", "foo.cpp",
+ std::make_shared<PCHContainerOperations>());
+ auto &Ctx = AST->getASTContext();
+ auto &TU = *Ctx.getTranslationUnitDecl();
+
+ auto BoundNodes = ast_matchers::match(
+ decl(hasDescendant(
+ callExpr(callee(functionDecl(hasName("foo")))).bind("fooCall"))),
+ TU, Ctx);
+
+ EXPECT_EQ(BoundNodes.size(), 1u);
+
+ auto *FooCall = BoundNodes[0].getNodeAs<CallExpr>("fooCall");
+
+ auto result = NodeIntrospection::GetLocations(FooCall);
+
+ std::map<std::string, SourceLocation> ExpectedLocations;
+ llvm::transform(result.LocationAccessors,
+ std::inserter(ExpectedLocations, ExpectedLocations.end()),
+ [](const auto &Accessor) {
+ return std::make_pair(
+ LocationCallFormatterCpp::format(Accessor.second.get()),
+ Accessor.first);
+ });
+
+ EXPECT_THAT(ExpectedLocations,
+ UnorderedElementsAre(
+ Pair("getBeginLoc()", FooCall->getBeginLoc()),
+ Pair("getEndLoc()", FooCall->getEndLoc()),
+ Pair("getExprLoc()", FooCall->getExprLoc()),
+ Pair("getRParenLoc()", FooCall->getRParenLoc())));
+
+ std::map<std::string, SourceRange> ExpectedRanges;
+ llvm::transform(result.RangeAccessors,
+ std::inserter(ExpectedRanges, ExpectedRanges.end()),
+ [](const auto &Accessor) {
+ return std::make_pair(
+ LocationCallFormatterCpp::format(Accessor.second.get()),
+ Accessor.first);
+ });
+
+ EXPECT_THAT(ExpectedRanges,
+ UnorderedElementsAre(
+ Pair("getSourceRange()", FooCall->getSourceRange())));
+}
+#endif