
Generate a json file containing descriptions of AST classes and their public accessors which return SourceLocation or SourceRange. Use the JSON file to generate a C++ API and implementation for accessing the source locations and method names for accessing them for a given AST node. This new API can be used to implement 'srcloc' output in clang-query: http://ce.steveire.com/z/m_kTIo The JSON file can also be used to generate bindings for other languages, such as Python and Javascript: https://steveire.wordpress.com/2019/04/30/the-future-of-ast-matching In this first version of this feature, only the accessors for Stmt classes are generated, not Decls, TypeLocs etc. Those can be added after this change is reviewed, as this change is mostly about infrastructure of these code generators. Also in this version, the platforms/cmake configurations are excluded as much as possible so that support can be added iteratively. Currently a break on any platform causes a revert of the entire feature. This way, the `OR WIN32` can be removed in a future commit and if it breaks the buildbots, only that commit gets reverted, making the entire process easier to manage. Differential Revision: https://reviews.llvm.org/D93164
209 lines
6.2 KiB
Python
Executable File
209 lines
6.2 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
# -*- coding: utf-8 -*-
|
|
|
|
import os
|
|
import sys
|
|
import json
|
|
|
|
import argparse
|
|
|
|
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 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 += '\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 os.path.exists(options.json_input_path):
|
|
use_empty_implementation = True
|
|
|
|
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 {
|
|
|
|
NodeLocationAccessors NodeIntrospection::GetLocations(clang::Stmt const *) {
|
|
return {};
|
|
}
|
|
NodeLocationAccessors
|
|
NodeIntrospection::GetLocations(clang::DynTypedNode const &) {
|
|
return {};
|
|
}
|
|
} // namespace tooling
|
|
} // namespace clang
|
|
""")
|
|
sys.exit(0)
|
|
|
|
g = Generator()
|
|
|
|
g.GeneratePrologue()
|
|
|
|
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())
|
|
|
|
g.GenerateEpilogue()
|
|
|
|
g.GenerateFiles(options.output_file)
|
|
|
|
if __name__ == '__main__':
|
|
main()
|