
Extend the matchers gathering API for types to record template parameters. The TypeLoc type hierarchy has some types which are templates used in CRTP such as PointerLikeTypeLoc. Record the inherited template and template arguments of types inheriting those CRTP types in the ClassInheritance map. Because the name inherited from is now computed, the value type in that map changes from StringRef to std::string. This also causes the toJSON override signature used to serialize that map to change. Remove the logic for skipping over empty ClassData instances. Several classes such as TypeOfExprTypeLoc inherit a CRTP class which provides interesting locations though the derived class does not. Record it as a class to make the locations it inherits available. Record the typeSourceInfo accessors too as they provide access to TypeLocs in many classes. The existing unit tests use UnorderedElementsAre to compare the introspection result with the expected result. Our current implementation of google mock (in gmock-generated-matchers.h) is limited to support for comparing a container of 10 elements. As we are now returning more than 10 results for one of the introspection tests, change it to instead compare against an ordered vector of pairs. Because a macro is used to generate API strings and API calls, disable clang-format in blocks of expected results. Otherwise clang-format would insert whitespaces which would then be compared against the introspected strings and fail the test. Introduce a recursion guard in the generated code. The TypeLoc class has IgnoreParens() API which by default returns itself, so it would otherwise recurse infinitely. Differential Revision: https://reviews.llvm.org/D100516
423 lines
14 KiB
Python
Executable File
423 lines
14 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
# -*- coding: utf-8 -*-
|
|
|
|
import os
|
|
import sys
|
|
import json
|
|
|
|
import argparse
|
|
|
|
class Generator(object):
|
|
|
|
implementationContent = ''
|
|
|
|
def __init__(self, templateClasses):
|
|
self.templateClasses = templateClasses
|
|
|
|
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;
|
|
|
|
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 == "TypeLoc":
|
|
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:
|
|
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'
|
|
|
|
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 == "TypeLoc":
|
|
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 == "TypeLoc":
|
|
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:
|
|
self.implementationContent += \
|
|
"""
|
|
if (const auto *N = Node.get<{0}>())
|
|
""".format(CladeName)
|
|
ArgPrefix = ""
|
|
if CladeName == "TypeLoc":
|
|
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('--empty-implementation',
|
|
help='Generate empty implementation',
|
|
action="store", type=int)
|
|
|
|
options = parser.parse_args()
|
|
|
|
use_empty_implementation = options.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:
|
|
with open(os.path.join(os.getcwd(),
|
|
options.output_file), 'w') as f:
|
|
f.write("""
|
|
namespace clang {
|
|
namespace tooling {
|
|
|
|
bool NodeIntrospection::hasIntrospectionSupport() { return false; }
|
|
|
|
NodeLocationAccessors NodeIntrospection::GetLocations(clang::Stmt const *) {
|
|
return {};
|
|
}
|
|
NodeLocationAccessors NodeIntrospection::GetLocations(clang::Decl const *) {
|
|
return {};
|
|
}
|
|
NodeLocationAccessors NodeIntrospection::GetLocations(
|
|
clang::CXXCtorInitializer const *) {
|
|
return {};
|
|
}
|
|
NodeLocationAccessors NodeIntrospection::GetLocations(
|
|
clang::NestedNameSpecifierLoc const*) {
|
|
return {};
|
|
}
|
|
NodeLocationAccessors NodeIntrospection::GetLocations(
|
|
clang::TemplateArgumentLoc const*) {
|
|
return {};
|
|
}
|
|
NodeLocationAccessors NodeIntrospection::GetLocations(
|
|
clang::CXXBaseSpecifier const*) {
|
|
return {};
|
|
}
|
|
NodeLocationAccessors NodeIntrospection::GetLocations(
|
|
clang::TypeLoc const&) {
|
|
return {};
|
|
}
|
|
NodeLocationAccessors
|
|
NodeIntrospection::GetLocations(clang::DynTypedNode const &) {
|
|
return {};
|
|
}
|
|
} // namespace tooling
|
|
} // namespace clang
|
|
""")
|
|
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 [
|
|
'NestedNameSpecifierLoc',
|
|
'TemplateArgumentLoc',
|
|
'TypeLoc'])
|
|
|
|
for (CladeName, ClassNameData) in jsonData['classesInClade'].items():
|
|
g.GenerateBaseGetLocationsFunction(
|
|
ClassNameData,
|
|
jsonData['classEntries'],
|
|
CladeName,
|
|
jsonData["classInheritance"],
|
|
CladeName not in [
|
|
'NestedNameSpecifierLoc',
|
|
'TemplateArgumentLoc',
|
|
'TypeLoc'])
|
|
|
|
g.GenerateDynNodeVisitor(jsonData['classesInClade'].keys())
|
|
|
|
g.GenerateEpilogue()
|
|
|
|
g.GenerateFiles(options.output_file)
|
|
|
|
if __name__ == '__main__':
|
|
main()
|