| 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/Error.h" |
| 17 | #include "llvm/TableGen/Record.h" |
| 18 | |
| 19 | using namespace llvm; |
| 20 | using namespace clang; |
| 21 | using namespace clang::tblgen; |
| 22 | |
| 23 | StringRef clang::tblgen::HasProperties::getName() const { |
| 24 | if (auto node = getAs<ASTNode>()) { |
| 25 | return node.getName(); |
| 26 | } else if (auto typeCase = getAs<TypeCase>()) { |
| 27 | return typeCase.getCaseName(); |
| 28 | } else { |
| 29 | PrintFatalError(ErrorLoc: getLoc(), Msg: "unexpected node declaring properties" ); |
| 30 | } |
| 31 | } |
| 32 | |
| 33 | static StringRef removeExpectedNodeNameSuffix(const Record *node, |
| 34 | 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(const RecordKeeper &records, StringRef nodeClassName, |
| 109 | ASTNodeHierarchyVisitor<ASTNode> visit) { |
| 110 | // Check for the node class, just as a basic correctness check. |
| 111 | if (!records.getClass(Name: nodeClassName)) { |
| 112 | PrintFatalError(Msg: Twine("cannot find definition for node class " ) |
| 113 | + nodeClassName); |
| 114 | } |
| 115 | |
| 116 | // Derive the child map for all nodes in the hierarchy. |
| 117 | ChildMap hierarchy; |
| 118 | ASTNode root; |
| 119 | for (ASTNode node : records.getAllDerivedDefinitions(ClassName: nodeClassName)) { |
| 120 | if (auto base = node.getBase()) |
| 121 | hierarchy.insert(x: std::make_pair(x&: base, y&: node)); |
| 122 | else if (root) |
| 123 | PrintFatalError(ErrorLoc: node.getLoc(), |
| 124 | Msg: "multiple root nodes in " + nodeClassName + " hierarchy" ); |
| 125 | else |
| 126 | root = node; |
| 127 | } |
| 128 | if (!root) |
| 129 | PrintFatalError(Msg: Twine("no root node in " ) + nodeClassName + " hierarchy" ); |
| 130 | |
| 131 | // Now visit the map recursively, starting at the root node. |
| 132 | visitASTNodeRecursive(node: root, base: ASTNode(), map: hierarchy, visit); |
| 133 | } |
| 134 | |
| 135 | void clang::tblgen::visitASTNodeHierarchyImpl( |
| 136 | const RecordKeeper &records, StringRef nodeClassName, |
| 137 | ASTNodeHierarchyVisitor<ASTNode> visit) { |
| 138 | visitHierarchy(records, nodeClassName, visit); |
| 139 | } |
| 140 | |