1 | //=== ASTTableGen.cpp - Helper functions for working with AST records -----===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | // |
9 | // This file defines some helper functions for working with tblegen reocrds |
10 | // for the Clang AST: that is, the contents of files such as DeclNodes.td, |
11 | // StmtNodes.td, and TypeNodes.td. |
12 | // |
13 | //===----------------------------------------------------------------------===// |
14 | |
15 | #include "ASTTableGen.h" |
16 | #include "llvm/TableGen/Record.h" |
17 | #include "llvm/TableGen/Error.h" |
18 | #include <optional> |
19 | |
20 | using namespace llvm; |
21 | using namespace clang; |
22 | using namespace clang::tblgen; |
23 | |
24 | llvm::StringRef clang::tblgen::HasProperties::getName() const { |
25 | if (auto node = getAs<ASTNode>()) { |
26 | return node.getName(); |
27 | } else if (auto typeCase = getAs<TypeCase>()) { |
28 | return typeCase.getCaseName(); |
29 | } else { |
30 | PrintFatalError(ErrorLoc: getLoc(), Msg: "unexpected node declaring properties" ); |
31 | } |
32 | } |
33 | |
34 | static StringRef removeExpectedNodeNameSuffix(Record *node, StringRef suffix) { |
35 | StringRef nodeName = node->getName(); |
36 | if (!nodeName.ends_with(Suffix: suffix)) { |
37 | PrintFatalError(ErrorLoc: node->getLoc(), |
38 | Msg: Twine("name of node doesn't end in " ) + suffix); |
39 | } |
40 | return nodeName.drop_back(N: suffix.size()); |
41 | } |
42 | |
43 | // Decl node names don't end in Decl for historical reasons, and it would |
44 | // be somewhat annoying to fix now. Conveniently, this means the ID matches |
45 | // is exactly the node name, and the class name is simply that plus Decl. |
46 | std::string clang::tblgen::DeclNode::getClassName() const { |
47 | return (Twine(getName()) + "Decl" ).str(); |
48 | } |
49 | StringRef clang::tblgen::DeclNode::getId() const { |
50 | return getName(); |
51 | } |
52 | |
53 | // Type nodes are all named ending in Type, just like the corresponding |
54 | // C++ class, and the ID just strips this suffix. |
55 | StringRef clang::tblgen::TypeNode::getClassName() const { |
56 | return getName(); |
57 | } |
58 | StringRef clang::tblgen::TypeNode::getId() const { |
59 | return removeExpectedNodeNameSuffix(node: getRecord(), suffix: "Type" ); |
60 | } |
61 | |
62 | // Stmt nodes are named the same as the C++ class, which has no regular |
63 | // naming convention (all the non-expression statements end in Stmt, |
64 | // and *many* expressions end in Expr, but there are also several |
65 | // core expression classes like IntegerLiteral and BinaryOperator with |
66 | // no standard suffix). The ID adds "Class" for historical reasons. |
67 | StringRef clang::tblgen::StmtNode::getClassName() const { |
68 | return getName(); |
69 | } |
70 | std::string clang::tblgen::StmtNode::getId() const { |
71 | return (Twine(getName()) + "Class" ).str(); |
72 | } |
73 | |
74 | /// Emit a string spelling out the C++ value type. |
75 | void PropertyType::emitCXXValueTypeName(bool forRead, raw_ostream &out) const { |
76 | if (!isGenericSpecialization()) { |
77 | if (!forRead && isConstWhenWriting()) |
78 | out << "const " ; |
79 | out << getCXXTypeName(); |
80 | } else if (auto elementType = getArrayElementType()) { |
81 | out << "llvm::ArrayRef<" ; |
82 | elementType.emitCXXValueTypeName(forRead, out); |
83 | out << ">" ; |
84 | } else if (auto valueType = getOptionalElementType()) { |
85 | out << "std::optional<" ; |
86 | valueType.emitCXXValueTypeName(forRead, out); |
87 | out << ">" ; |
88 | } else { |
89 | //PrintFatalError(getLoc(), "unexpected generic property type"); |
90 | abort(); |
91 | } |
92 | } |
93 | |
94 | // A map from a node to each of its child nodes. |
95 | using ChildMap = std::multimap<ASTNode, ASTNode>; |
96 | |
97 | static void visitASTNodeRecursive(ASTNode node, ASTNode base, |
98 | const ChildMap &map, |
99 | ASTNodeHierarchyVisitor<ASTNode> visit) { |
100 | visit(node, base); |
101 | |
102 | auto i = map.lower_bound(x: node), e = map.upper_bound(x: node); |
103 | for (; i != e; ++i) { |
104 | visitASTNodeRecursive(node: i->second, base: node, map, visit); |
105 | } |
106 | } |
107 | |
108 | static void visitHierarchy(RecordKeeper &records, |
109 | StringRef nodeClassName, |
110 | ASTNodeHierarchyVisitor<ASTNode> visit) { |
111 | // Check for the node class, just as a basic correctness check. |
112 | if (!records.getClass(Name: nodeClassName)) { |
113 | PrintFatalError(Msg: Twine("cannot find definition for node class " ) |
114 | + nodeClassName); |
115 | } |
116 | |
117 | // Find all the nodes in the hierarchy. |
118 | auto nodes = records.getAllDerivedDefinitions(ClassName: nodeClassName); |
119 | |
120 | // Derive the child map. |
121 | ChildMap hierarchy; |
122 | ASTNode root; |
123 | for (ASTNode node : nodes) { |
124 | if (auto base = node.getBase()) |
125 | hierarchy.insert(x: std::make_pair(x&: base, y&: node)); |
126 | else if (root) |
127 | PrintFatalError(ErrorLoc: node.getLoc(), |
128 | Msg: "multiple root nodes in " + nodeClassName + " hierarchy" ); |
129 | else |
130 | root = node; |
131 | } |
132 | if (!root) |
133 | PrintFatalError(Msg: Twine("no root node in " ) + nodeClassName + " hierarchy" ); |
134 | |
135 | // Now visit the map recursively, starting at the root node. |
136 | visitASTNodeRecursive(node: root, base: ASTNode(), map: hierarchy, visit); |
137 | } |
138 | |
139 | void clang::tblgen::visitASTNodeHierarchyImpl(RecordKeeper &records, |
140 | StringRef nodeClassName, |
141 | ASTNodeHierarchyVisitor<ASTNode> visit) { |
142 | visitHierarchy(records, nodeClassName, visit); |
143 | } |
144 | |