| #!/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() |