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).
202 lines
7.2 KiB
C++
202 lines
7.2 KiB
C++
//===--- ParseAST.cpp - Provide the clang::ParseAST method ----------------===//
|
|
//
|
|
// 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 implements the clang::ParseAST method.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "clang/Parse/ParseAST.h"
|
|
#include "clang/AST/ASTConsumer.h"
|
|
#include "clang/AST/ASTContext.h"
|
|
#include "clang/AST/ExternalASTSource.h"
|
|
#include "clang/AST/Stmt.h"
|
|
#include "clang/Parse/Parser.h"
|
|
#include "clang/Sema/CodeCompleteConsumer.h"
|
|
#include "clang/Sema/EnterExpressionEvaluationContext.h"
|
|
#include "clang/Sema/Sema.h"
|
|
#include "clang/Sema/SemaConsumer.h"
|
|
#include "clang/Sema/TemplateInstCallback.h"
|
|
#include "llvm/Support/CrashRecoveryContext.h"
|
|
#include "llvm/Support/TimeProfiler.h"
|
|
#include <cstdio>
|
|
#include <memory>
|
|
|
|
using namespace clang;
|
|
|
|
namespace {
|
|
|
|
/// Resets LLVM's pretty stack state so that stack traces are printed correctly
|
|
/// when there are nested CrashRecoveryContexts and the inner one recovers from
|
|
/// a crash.
|
|
class ResetStackCleanup
|
|
: public llvm::CrashRecoveryContextCleanupBase<ResetStackCleanup,
|
|
const void> {
|
|
public:
|
|
ResetStackCleanup(llvm::CrashRecoveryContext *Context, const void *Top)
|
|
: llvm::CrashRecoveryContextCleanupBase<ResetStackCleanup, const void>(
|
|
Context, Top) {}
|
|
void recoverResources() override {
|
|
llvm::RestorePrettyStackState(resource);
|
|
}
|
|
};
|
|
|
|
/// If a crash happens while the parser is active, an entry is printed for it.
|
|
class PrettyStackTraceParserEntry : public llvm::PrettyStackTraceEntry {
|
|
const Parser &P;
|
|
public:
|
|
PrettyStackTraceParserEntry(const Parser &p) : P(p) {}
|
|
void print(raw_ostream &OS) const override;
|
|
};
|
|
|
|
/// If a crash happens while the parser is active, print out a line indicating
|
|
/// what the current token is.
|
|
void PrettyStackTraceParserEntry::print(raw_ostream &OS) const {
|
|
const Token &Tok = P.getCurToken();
|
|
if (Tok.is(tok::eof)) {
|
|
OS << "<eof> parser at end of file\n";
|
|
return;
|
|
}
|
|
|
|
if (Tok.getLocation().isInvalid()) {
|
|
OS << "<unknown> parser at unknown location\n";
|
|
return;
|
|
}
|
|
|
|
const Preprocessor &PP = P.getPreprocessor();
|
|
Tok.getLocation().print(OS, PP.getSourceManager());
|
|
if (Tok.isAnnotation()) {
|
|
OS << ": at annotation token\n";
|
|
} else {
|
|
// Do the equivalent of PP.getSpelling(Tok) except for the parts that would
|
|
// allocate memory.
|
|
bool Invalid = false;
|
|
const SourceManager &SM = P.getPreprocessor().getSourceManager();
|
|
unsigned Length = Tok.getLength();
|
|
const char *Spelling = SM.getCharacterData(Tok.getLocation(), &Invalid);
|
|
if (Invalid) {
|
|
OS << ": unknown current parser token\n";
|
|
return;
|
|
}
|
|
OS << ": current parser token '" << StringRef(Spelling, Length) << "'\n";
|
|
}
|
|
}
|
|
|
|
} // namespace
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// Public interface to the file
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
/// ParseAST - Parse the entire file specified, notifying the ASTConsumer as
|
|
/// the file is parsed. This inserts the parsed decls into the translation unit
|
|
/// held by Ctx.
|
|
///
|
|
void clang::ParseAST(Preprocessor &PP, ASTConsumer *Consumer,
|
|
ASTContext &Ctx, bool PrintStats,
|
|
TranslationUnitKind TUKind,
|
|
CodeCompleteConsumer *CompletionConsumer,
|
|
bool SkipFunctionBodies) {
|
|
|
|
std::unique_ptr<Sema> S(
|
|
new Sema(PP, Ctx, *Consumer, TUKind, CompletionConsumer));
|
|
|
|
// Recover resources if we crash before exiting this method.
|
|
llvm::CrashRecoveryContextCleanupRegistrar<Sema> CleanupSema(S.get());
|
|
|
|
ParseAST(*S, PrintStats, SkipFunctionBodies);
|
|
}
|
|
|
|
void clang::ParseAST(Sema &S, bool PrintStats, bool SkipFunctionBodies) {
|
|
// Collect global stats on Decls/Stmts (until we have a module streamer).
|
|
if (PrintStats) {
|
|
Decl::EnableStatistics();
|
|
Stmt::EnableStatistics();
|
|
}
|
|
|
|
// Also turn on collection of stats inside of the Sema object.
|
|
bool OldCollectStats = PrintStats;
|
|
std::swap(OldCollectStats, S.CollectStats);
|
|
|
|
// Initialize the template instantiation observer chain.
|
|
// FIXME: See note on "finalize" below.
|
|
initialize(S.TemplateInstCallbacks, S);
|
|
|
|
ASTConsumer *Consumer = &S.getASTConsumer();
|
|
|
|
std::unique_ptr<Parser> ParseOP(
|
|
new Parser(S.getPreprocessor(), S, SkipFunctionBodies));
|
|
Parser &P = *ParseOP;
|
|
|
|
llvm::CrashRecoveryContextCleanupRegistrar<const void, ResetStackCleanup>
|
|
CleanupPrettyStack(llvm::SavePrettyStackState());
|
|
PrettyStackTraceParserEntry CrashInfo(P);
|
|
|
|
// Recover resources if we crash before exiting this method.
|
|
llvm::CrashRecoveryContextCleanupRegistrar<Parser>
|
|
CleanupParser(ParseOP.get());
|
|
|
|
S.getPreprocessor().EnterMainSourceFile();
|
|
ExternalASTSource *External = S.getASTContext().getExternalSource();
|
|
if (External)
|
|
External->StartTranslationUnit(Consumer);
|
|
|
|
// If a PCH through header is specified that does not have an include in
|
|
// the source, or a PCH is being created with #pragma hdrstop with nothing
|
|
// after the pragma, there won't be any tokens or a Lexer.
|
|
bool HaveLexer = S.getPreprocessor().getCurrentLexer();
|
|
|
|
if (HaveLexer) {
|
|
llvm::TimeTraceScope TimeScope("Frontend", [&]() {
|
|
llvm::TimeTraceMetadata M;
|
|
if (llvm::isTimeTraceVerbose()) {
|
|
const SourceManager &SM = S.getSourceManager();
|
|
if (const auto *FE = SM.getFileEntryForID(SM.getMainFileID()))
|
|
M.File = FE->tryGetRealPathName();
|
|
}
|
|
return M;
|
|
});
|
|
P.Initialize();
|
|
Parser::DeclGroupPtrTy ADecl;
|
|
Sema::ModuleImportState ImportState;
|
|
EnterExpressionEvaluationContext PotentiallyEvaluated(
|
|
S, Sema::ExpressionEvaluationContext::PotentiallyEvaluated);
|
|
|
|
for (bool AtEOF = P.ParseFirstTopLevelDecl(ADecl, ImportState); !AtEOF;
|
|
AtEOF = P.ParseTopLevelDecl(ADecl, ImportState)) {
|
|
// If we got a null return and something *was* parsed, ignore it. This
|
|
// is due to a top-level semicolon, an action override, or a parse error
|
|
// skipping something.
|
|
if (ADecl && !Consumer->HandleTopLevelDecl(ADecl.get()))
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Process any TopLevelDecls generated by #pragma weak.
|
|
for (Decl *D : S.WeakTopLevelDecls())
|
|
Consumer->HandleTopLevelDecl(DeclGroupRef(D));
|
|
|
|
Consumer->HandleTranslationUnit(S.getASTContext());
|
|
|
|
// Finalize the template instantiation observer chain.
|
|
// FIXME: This (and init.) should be done in the Sema class, but because
|
|
// Sema does not have a reliable "Finalize" function (it has a
|
|
// destructor, but it is not guaranteed to be called ("-disable-free")).
|
|
// So, do the initialization above and do the finalization here:
|
|
finalize(S.TemplateInstCallbacks, S);
|
|
|
|
std::swap(OldCollectStats, S.CollectStats);
|
|
if (PrintStats) {
|
|
llvm::errs() << "\nSTATISTICS:\n";
|
|
if (HaveLexer) P.getActions().PrintStats();
|
|
S.getASTContext().PrintStats();
|
|
Decl::PrintStats();
|
|
Stmt::PrintStats();
|
|
Consumer->PrintStats();
|
|
}
|
|
}
|