#!/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 += \ """ /*===- 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 &TypeLocRecursionGuard) : TLRG(TypeLocRecursionGuard) { } ~RecursionPopper() { TLRG.pop_back(); } private: std::vector &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 &TypeLocRecursionGuard); """.format(CladeName, InstanceDecoration) def GenerateSrcLocMethod(self, ClassName, ClassData, CreateLocalRecursionGuard): NormalClassName = ClassName RecursionGuardParam = ('' if CreateLocalRecursionGuard else \ ', std::vector& TypeLocRecursionGuard') if "templateParms" in ClassData: TemplatePreamble = "template (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(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 TypeLocRecursionGuard;\n' self.implementationContent += '\n' if 'typeLocs' in ClassData: for typeLoc in ClassData['typeLocs']: self.implementationContent += \ """ if (Object.{0}()) {{ GetLocationsImpl( llvm::makeIntrusiveRefCnt(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( llvm::makeIntrusiveRefCnt(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(Prefix, "{0}"), Object.{0}(), Locs, Rngs, TypeLocRecursionGuard); """.format(NN) if 'declNameInfos' in ClassData: for declName in ClassData['declNameInfos']: self.implementationContent += \ """ GetLocationsImpl( llvm::makeIntrusiveRefCnt(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 &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()) { auto Dequalified = QTL.getNextTypeLoc(); return GetLocationsImpl(llvm::makeIntrusiveRefCnt(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(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 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(Prefix, "getAs", LocationCall::IsCast) '''.format(ASTClassName) if ASTClassName in ClassEntries: self.implementationContent += \ """ if (auto ConcreteTL = Object.getAs()) 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()) 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()