cb424d7448
verify-patch-sanity.py validates every active recipe .patch has internally- consistent hunk line counts — catching the 'malformed patch at line N' failure at commit/CI/preflight time instead of hours into a cook. This cycle hit that class three times (qtwaylandscanner, sddm, xwayland), each only discovered when cookbook tried to apply the patch. Running it across the repo found 29 latent malformed patches (validated against GNU patch: e.g. relibc/P3-sysv-ipc reproduces 'malformed patch at line 22'). They were harmless only because they sit in vendored recipes (baked, not re- applied) — but would fail on any version-bump re-derivation. --fix recounts the hunk headers (body untouched) and repaired all 29. Wired into build-preflight.sh (Phase 1.0D) and redbear-ci.yml, with a unit test (test-patch-sanity.sh). Skips archived/legacy trees and unvalidatable formats (empty placeholders, bare-@@ git hunks).
140 lines
5.0 KiB
C++
140 lines
5.0 KiB
C++
//===-- ASTTableGen.cpp - Helper functions for working with AST records ---===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This file defines some helper functions for working with tblegen reocrds
|
|
// for the Clang AST: that is, the contents of files such as DeclNodes.td,
|
|
// StmtNodes.td, and TypeNodes.td.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "ASTTableGen.h"
|
|
#include "llvm/TableGen/Error.h"
|
|
#include "llvm/TableGen/Record.h"
|
|
|
|
using namespace llvm;
|
|
using namespace clang;
|
|
using namespace clang::tblgen;
|
|
|
|
StringRef clang::tblgen::HasProperties::getName() const {
|
|
if (auto node = getAs<ASTNode>()) {
|
|
return node.getName();
|
|
} else if (auto typeCase = getAs<TypeCase>()) {
|
|
return typeCase.getCaseName();
|
|
} else {
|
|
PrintFatalError(getLoc(), "unexpected node declaring properties");
|
|
}
|
|
}
|
|
|
|
static StringRef removeExpectedNodeNameSuffix(const Record *node,
|
|
StringRef suffix) {
|
|
StringRef nodeName = node->getName();
|
|
if (!nodeName.ends_with(suffix)) {
|
|
PrintFatalError(node->getLoc(),
|
|
Twine("name of node doesn't end in ") + suffix);
|
|
}
|
|
return nodeName.drop_back(suffix.size());
|
|
}
|
|
|
|
// Decl node names don't end in Decl for historical reasons, and it would
|
|
// be somewhat annoying to fix now. Conveniently, this means the ID matches
|
|
// is exactly the node name, and the class name is simply that plus Decl.
|
|
std::string clang::tblgen::DeclNode::getClassName() const {
|
|
return (Twine(getName()) + "Decl").str();
|
|
}
|
|
StringRef clang::tblgen::DeclNode::getId() const {
|
|
return getName();
|
|
}
|
|
|
|
// Type nodes are all named ending in Type, just like the corresponding
|
|
// C++ class, and the ID just strips this suffix.
|
|
StringRef clang::tblgen::TypeNode::getClassName() const {
|
|
return getName();
|
|
}
|
|
StringRef clang::tblgen::TypeNode::getId() const {
|
|
return removeExpectedNodeNameSuffix(getRecord(), "Type");
|
|
}
|
|
|
|
// Stmt nodes are named the same as the C++ class, which has no regular
|
|
// naming convention (all the non-expression statements end in Stmt,
|
|
// and *many* expressions end in Expr, but there are also several
|
|
// core expression classes like IntegerLiteral and BinaryOperator with
|
|
// no standard suffix). The ID adds "Class" for historical reasons.
|
|
StringRef clang::tblgen::StmtNode::getClassName() const {
|
|
return getName();
|
|
}
|
|
std::string clang::tblgen::StmtNode::getId() const {
|
|
return (Twine(getName()) + "Class").str();
|
|
}
|
|
|
|
/// Emit a string spelling out the C++ value type.
|
|
void PropertyType::emitCXXValueTypeName(bool forRead, raw_ostream &out) const {
|
|
if (!isGenericSpecialization()) {
|
|
if (!forRead && isConstWhenWriting())
|
|
out << "const ";
|
|
out << getCXXTypeName();
|
|
} else if (auto elementType = getArrayElementType()) {
|
|
out << "llvm::ArrayRef<";
|
|
elementType.emitCXXValueTypeName(forRead, out);
|
|
out << ">";
|
|
} else if (auto valueType = getOptionalElementType()) {
|
|
out << "std::optional<";
|
|
valueType.emitCXXValueTypeName(forRead, out);
|
|
out << ">";
|
|
} else {
|
|
//PrintFatalError(getLoc(), "unexpected generic property type");
|
|
abort();
|
|
}
|
|
}
|
|
|
|
// A map from a node to each of its child nodes.
|
|
using ChildMap = std::multimap<ASTNode, ASTNode>;
|
|
|
|
static void visitASTNodeRecursive(ASTNode node, ASTNode base,
|
|
const ChildMap &map,
|
|
ASTNodeHierarchyVisitor<ASTNode> visit) {
|
|
visit(node, base);
|
|
|
|
auto i = map.lower_bound(node), e = map.upper_bound(node);
|
|
for (; i != e; ++i) {
|
|
visitASTNodeRecursive(i->second, node, map, visit);
|
|
}
|
|
}
|
|
|
|
static void visitHierarchy(const RecordKeeper &records, StringRef nodeClassName,
|
|
ASTNodeHierarchyVisitor<ASTNode> visit) {
|
|
// Check for the node class, just as a basic correctness check.
|
|
if (!records.getClass(nodeClassName)) {
|
|
PrintFatalError(Twine("cannot find definition for node class ")
|
|
+ nodeClassName);
|
|
}
|
|
|
|
// Derive the child map for all nodes in the hierarchy.
|
|
ChildMap hierarchy;
|
|
ASTNode root;
|
|
for (ASTNode node : records.getAllDerivedDefinitions(nodeClassName)) {
|
|
if (auto base = node.getBase())
|
|
hierarchy.insert(std::make_pair(base, node));
|
|
else if (root)
|
|
PrintFatalError(node.getLoc(),
|
|
"multiple root nodes in " + nodeClassName + " hierarchy");
|
|
else
|
|
root = node;
|
|
}
|
|
if (!root)
|
|
PrintFatalError(Twine("no root node in ") + nodeClassName + " hierarchy");
|
|
|
|
// Now visit the map recursively, starting at the root node.
|
|
visitASTNodeRecursive(root, ASTNode(), hierarchy, visit);
|
|
}
|
|
|
|
void clang::tblgen::visitASTNodeHierarchyImpl(
|
|
const RecordKeeper &records, StringRef nodeClassName,
|
|
ASTNodeHierarchyVisitor<ASTNode> visit) {
|
|
visitHierarchy(records, nodeClassName, visit);
|
|
}
|