blob: 7671f9691c09610a3cb1c046e13315bbc4a1db52 [file] [log] [blame]
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import os
import sys
import json
import filecmp
import shutil
import argparse
class Generator(object):
implementationContent = ""
RefClades = {
"DeclarationNameInfo",
"NestedNameSpecifierLoc",
"TemplateArgumentLoc",
"TypeLoc",
}
def __init__(self, templateClasses):
self.templateClasses = templateClasses
def GeneratePrologue(self):
self.implementationContent += r"""
/*===- 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;
bool NodeIntrospection::hasIntrospectionSupport() { return true; }
struct RecursionPopper
{
RecursionPopper(std::vector<clang::TypeLoc> &TypeLocRecursionGuard)
: TLRG(TypeLocRecursionGuard)
{
}
~RecursionPopper()
{
TLRG.pop_back();
}
private:
std::vector<clang::TypeLoc> &TLRG;
};
"""
def GenerateBaseGetLocationsDeclaration(self, CladeName):
InstanceDecoration = "*"
if CladeName in self.RefClades:
InstanceDecoration = "&"
self.implementationContent += """
void GetLocationsImpl(SharedLocationCall const& Prefix,
clang::{0} const {1}Object, SourceLocationMap &Locs,
SourceRangeMap &Rngs,
std::vector<clang::TypeLoc> &TypeLocRecursionGuard);
""".format(
CladeName, InstanceDecoration
)
def GenerateSrcLocMethod(self, ClassName, ClassData, CreateLocalRecursionGuard):
NormalClassName = ClassName
RecursionGuardParam = (
""
if CreateLocalRecursionGuard
else ", std::vector<clang::TypeLoc>& TypeLocRecursionGuard"
)
if "templateParms" in ClassData:
TemplatePreamble = "template <typename "
ClassName += "<"
First = True
for TA in ClassData["templateParms"]:
if not First:
ClassName += ", "
TemplatePreamble += ", typename "
First = False
ClassName += TA
TemplatePreamble += TA
ClassName += ">"
TemplatePreamble += ">\n"
self.implementationContent += TemplatePreamble
self.implementationContent += """
static void GetLocations{0}(SharedLocationCall const& Prefix,
clang::{1} const &Object,
SourceLocationMap &Locs, SourceRangeMap &Rngs {2})
{{
""".format(
NormalClassName, ClassName, RecursionGuardParam
)
if "sourceLocations" in ClassData:
for locName in ClassData["sourceLocations"]:
self.implementationContent += """
Locs.insert(LocationAndString(Object.{0}(),
llvm::makeIntrusiveRefCnt<LocationCall>(Prefix, "{0}")));
""".format(
locName
)
self.implementationContent += "\n"
if "sourceRanges" in ClassData:
for rngName in ClassData["sourceRanges"]:
self.implementationContent += """
Rngs.insert(RangeAndString(Object.{0}(),
llvm::makeIntrusiveRefCnt<LocationCall>(Prefix, "{0}")));
""".format(
rngName
)
self.implementationContent += "\n"
if (
"typeLocs" in ClassData
or "typeSourceInfos" in ClassData
or "nestedNameLocs" in ClassData
or "declNameInfos" in ClassData
):
if CreateLocalRecursionGuard:
self.implementationContent += (
"std::vector<clang::TypeLoc> TypeLocRecursionGuard;\n"
)
self.implementationContent += "\n"
if "typeLocs" in ClassData:
for typeLoc in ClassData["typeLocs"]:
self.implementationContent += """
if (Object.{0}()) {{
GetLocationsImpl(
llvm::makeIntrusiveRefCnt<LocationCall>(Prefix, "{0}"),
Object.{0}(), Locs, Rngs, TypeLocRecursionGuard);
}}
""".format(
typeLoc
)
self.implementationContent += "\n"
if "typeSourceInfos" in ClassData:
for tsi in ClassData["typeSourceInfos"]:
self.implementationContent += """
if (Object.{0}()) {{
GetLocationsImpl(llvm::makeIntrusiveRefCnt<LocationCall>(
llvm::makeIntrusiveRefCnt<LocationCall>(Prefix, "{0}",
LocationCall::ReturnsPointer), "getTypeLoc"),
Object.{0}()->getTypeLoc(), Locs, Rngs, TypeLocRecursionGuard);
}}
""".format(
tsi
)
self.implementationContent += "\n"
if "nestedNameLocs" in ClassData:
for NN in ClassData["nestedNameLocs"]:
self.implementationContent += """
if (Object.{0}())
GetLocationsImpl(
llvm::makeIntrusiveRefCnt<LocationCall>(Prefix, "{0}"),
Object.{0}(), Locs, Rngs, TypeLocRecursionGuard);
""".format(
NN
)
if "declNameInfos" in ClassData:
for declName in ClassData["declNameInfos"]:
self.implementationContent += """
GetLocationsImpl(
llvm::makeIntrusiveRefCnt<LocationCall>(Prefix, "{0}"),
Object.{0}(), Locs, Rngs, TypeLocRecursionGuard);
""".format(
declName
)
self.implementationContent += "}\n"
def GenerateFiles(self, OutputFile):
with open(os.path.join(os.getcwd(), OutputFile), "w") as f:
f.write(self.implementationContent)
def GenerateBaseGetLocationsFunction(
self,
ASTClassNames,
ClassEntries,
CladeName,
InheritanceMap,
CreateLocalRecursionGuard,
):
MethodReturnType = "NodeLocationAccessors"
InstanceDecoration = "*"
if CladeName in self.RefClades:
InstanceDecoration = "&"
Signature = "GetLocations(clang::{0} const {1}Object)".format(
CladeName, InstanceDecoration
)
ImplSignature = """
GetLocationsImpl(SharedLocationCall const& Prefix,
clang::{0} const {1}Object, SourceLocationMap &Locs,
SourceRangeMap &Rngs,
std::vector<clang::TypeLoc> &TypeLocRecursionGuard)
""".format(
CladeName, InstanceDecoration
)
self.implementationContent += "void {0} {{ ".format(ImplSignature)
if CladeName == "TypeLoc":
self.implementationContent += "if (Object.isNull()) return;"
self.implementationContent += """
if (llvm::find(TypeLocRecursionGuard, Object) != TypeLocRecursionGuard.end())
return;
TypeLocRecursionGuard.push_back(Object);
RecursionPopper RAII(TypeLocRecursionGuard);
"""
RecursionGuardParam = ""
if not CreateLocalRecursionGuard:
RecursionGuardParam = ", TypeLocRecursionGuard"
ArgPrefix = "*"
if CladeName in self.RefClades:
ArgPrefix = ""
self.implementationContent += (
"GetLocations{0}(Prefix, {1}Object, Locs, Rngs {2});".format(
CladeName, ArgPrefix, RecursionGuardParam
)
)
if CladeName == "TypeLoc":
self.implementationContent += """
if (auto QTL = Object.getAs<clang::QualifiedTypeLoc>()) {
auto Dequalified = QTL.getNextTypeLoc();
return GetLocationsImpl(llvm::makeIntrusiveRefCnt<LocationCall>(Prefix, "getNextTypeLoc"),
Dequalified,
Locs,
Rngs,
TypeLocRecursionGuard);
}"""
for ASTClassName in ASTClassNames:
if ASTClassName in self.templateClasses:
continue
if ASTClassName == CladeName:
continue
if CladeName != "TypeLoc":
self.implementationContent += """
if (auto Derived = llvm::dyn_cast<clang::{0}>(Object)) {{
GetLocations{0}(Prefix, *Derived, Locs, Rngs {1});
}}
""".format(
ASTClassName, RecursionGuardParam
)
continue
self.GenerateBaseTypeLocVisit(
ASTClassName, ClassEntries, RecursionGuardParam, InheritanceMap
)
self.implementationContent += "}"
self.implementationContent += """
{0} NodeIntrospection::{1} {{
NodeLocationAccessors Result;
SharedLocationCall Prefix;
std::vector<clang::TypeLoc> TypeLocRecursionGuard;
GetLocationsImpl(Prefix, Object, Result.LocationAccessors,
Result.RangeAccessors, TypeLocRecursionGuard);
""".format(
MethodReturnType, Signature
)
self.implementationContent += "return Result; }"
def GenerateBaseTypeLocVisit(
self, ASTClassName, ClassEntries, RecursionGuardParam, InheritanceMap
):
CallPrefix = "Prefix"
if ASTClassName != "TypeLoc":
CallPrefix = """llvm::makeIntrusiveRefCnt<LocationCall>(Prefix,
"getAs<clang::{0}>", LocationCall::IsCast)
""".format(
ASTClassName
)
if ASTClassName in ClassEntries:
self.implementationContent += """
if (auto ConcreteTL = Object.getAs<clang::{0}>())
GetLocations{1}({2}, ConcreteTL, Locs, Rngs {3});
""".format(
ASTClassName, ASTClassName, CallPrefix, RecursionGuardParam
)
if ASTClassName in InheritanceMap:
for baseTemplate in self.templateClasses:
if baseTemplate in InheritanceMap[ASTClassName]:
self.implementationContent += """
if (auto ConcreteTL = Object.getAs<clang::{0}>())
GetLocations{1}({2}, ConcreteTL, Locs, Rngs {3});
""".format(
InheritanceMap[ASTClassName],
baseTemplate,
CallPrefix,
RecursionGuardParam,
)
def GenerateDynNodeVisitor(self, CladeNames):
MethodReturnType = "NodeLocationAccessors"
Signature = "GetLocations(clang::DynTypedNode const &Node)"
self.implementationContent += (
MethodReturnType + " NodeIntrospection::" + Signature + "{"
)
for CladeName in CladeNames:
if CladeName == "DeclarationNameInfo":
continue
self.implementationContent += """
if (const auto *N = Node.get<{0}>())
""".format(
CladeName
)
ArgPrefix = ""
if CladeName in self.RefClades:
ArgPrefix = "*"
self.implementationContent += """
return GetLocations({0}const_cast<{1} *>(N));""".format(
ArgPrefix, CladeName
)
self.implementationContent += "\nreturn {}; }"
def GenerateEpilogue(self):
self.implementationContent += """
}
}
"""
def main():
parser = argparse.ArgumentParser()
parser.add_argument(
"--json-input-path", help="Read API description from FILE", metavar="FILE"
)
parser.add_argument(
"--output-file", help="Generate output in FILEPATH", metavar="FILEPATH"
)
parser.add_argument(
"--use-empty-implementation",
help="Generate empty implementation",
action="store",
type=int,
)
parser.add_argument(
"--empty-implementation",
help="Copy empty implementation from FILEPATH",
action="store",
metavar="FILEPATH",
)
options = parser.parse_args()
use_empty_implementation = options.use_empty_implementation
if not use_empty_implementation and not os.path.exists(options.json_input_path):
use_empty_implementation = True
if not use_empty_implementation:
with open(options.json_input_path) as f:
jsonData = json.load(f)
if not "classesInClade" in jsonData or not jsonData["classesInClade"]:
use_empty_implementation = True
if use_empty_implementation:
if not os.path.exists(options.output_file) or not filecmp.cmp(
options.empty_implementation, options.output_file
):
shutil.copyfile(options.empty_implementation, options.output_file)
sys.exit(0)
templateClasses = []
for (ClassName, ClassAccessors) in jsonData["classEntries"].items():
if "templateParms" in ClassAccessors:
templateClasses.append(ClassName)
g = Generator(templateClasses)
g.GeneratePrologue()
for (CladeName, ClassNameData) in jsonData["classesInClade"].items():
g.GenerateBaseGetLocationsDeclaration(CladeName)
def getCladeName(ClassName):
for (CladeName, ClassNameData) in jsonData["classesInClade"].items():
if ClassName in ClassNameData:
return CladeName
for (ClassName, ClassAccessors) in jsonData["classEntries"].items():
cladeName = getCladeName(ClassName)
g.GenerateSrcLocMethod(
ClassName, ClassAccessors, cladeName not in Generator.RefClades
)
for (CladeName, ClassNameData) in jsonData["classesInClade"].items():
g.GenerateBaseGetLocationsFunction(
ClassNameData,
jsonData["classEntries"],
CladeName,
jsonData["classInheritance"],
CladeName not in Generator.RefClades,
)
g.GenerateDynNodeVisitor(jsonData["classesInClade"].keys())
g.GenerateEpilogue()
g.GenerateFiles(options.output_file)
if __name__ == "__main__":
main()