1 //===--- ParseAST.cpp - Provide the clang::ParseAST method ----------------===//
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 implements the clang::ParseAST method.
10 //
11 //===----------------------------------------------------------------------===//
12
13 #include "clang/Parse/ParseAST.h"
14 #include "clang/AST/ASTConsumer.h"
15 #include "clang/AST/ASTContext.h"
16 #include "clang/AST/ExternalASTSource.h"
17 #include "clang/AST/Stmt.h"
18 #include "clang/Parse/ParseDiagnostic.h"
19 #include "clang/Parse/Parser.h"
20 #include "clang/Sema/CodeCompleteConsumer.h"
21 #include "clang/Sema/EnterExpressionEvaluationContext.h"
22 #include "clang/Sema/Sema.h"
23 #include "clang/Sema/SemaConsumer.h"
24 #include "clang/Sema/TemplateInstCallback.h"
25 #include "llvm/Support/CrashRecoveryContext.h"
26 #include "llvm/Support/TimeProfiler.h"
27 #include <cstdio>
28 #include <memory>
29
30 using namespace clang;
31
32 namespace {
33
34 /// Resets LLVM's pretty stack state so that stack traces are printed correctly
35 /// when there are nested CrashRecoveryContexts and the inner one recovers from
36 /// a crash.
37 class ResetStackCleanup
38 : public llvm::CrashRecoveryContextCleanupBase<ResetStackCleanup,
39 const void> {
40 public:
ResetStackCleanup(llvm::CrashRecoveryContext * Context,const void * Top)41 ResetStackCleanup(llvm::CrashRecoveryContext *Context, const void *Top)
42 : llvm::CrashRecoveryContextCleanupBase<ResetStackCleanup, const void>(
43 Context, Top) {}
recoverResources()44 void recoverResources() override {
45 llvm::RestorePrettyStackState(resource);
46 }
47 };
48
49 /// If a crash happens while the parser is active, an entry is printed for it.
50 class PrettyStackTraceParserEntry : public llvm::PrettyStackTraceEntry {
51 const Parser &P;
52 public:
PrettyStackTraceParserEntry(const Parser & p)53 PrettyStackTraceParserEntry(const Parser &p) : P(p) {}
54 void print(raw_ostream &OS) const override;
55 };
56
57 /// If a crash happens while the parser is active, print out a line indicating
58 /// what the current token is.
print(raw_ostream & OS) const59 void PrettyStackTraceParserEntry::print(raw_ostream &OS) const {
60 const Token &Tok = P.getCurToken();
61 if (Tok.is(tok::eof)) {
62 OS << "<eof> parser at end of file\n";
63 return;
64 }
65
66 if (Tok.getLocation().isInvalid()) {
67 OS << "<unknown> parser at unknown location\n";
68 return;
69 }
70
71 const Preprocessor &PP = P.getPreprocessor();
72 Tok.getLocation().print(OS, PP.getSourceManager());
73 if (Tok.isAnnotation()) {
74 OS << ": at annotation token\n";
75 } else {
76 // Do the equivalent of PP.getSpelling(Tok) except for the parts that would
77 // allocate memory.
78 bool Invalid = false;
79 const SourceManager &SM = P.getPreprocessor().getSourceManager();
80 unsigned Length = Tok.getLength();
81 const char *Spelling = SM.getCharacterData(Tok.getLocation(), &Invalid);
82 if (Invalid) {
83 OS << ": unknown current parser token\n";
84 return;
85 }
86 OS << ": current parser token '" << StringRef(Spelling, Length) << "'\n";
87 }
88 }
89
90 } // namespace
91
92 //===----------------------------------------------------------------------===//
93 // Public interface to the file
94 //===----------------------------------------------------------------------===//
95
96 /// ParseAST - Parse the entire file specified, notifying the ASTConsumer as
97 /// the file is parsed. This inserts the parsed decls into the translation unit
98 /// held by Ctx.
99 ///
ParseAST(Preprocessor & PP,ASTConsumer * Consumer,ASTContext & Ctx,bool PrintStats,TranslationUnitKind TUKind,CodeCompleteConsumer * CompletionConsumer,bool SkipFunctionBodies)100 void clang::ParseAST(Preprocessor &PP, ASTConsumer *Consumer,
101 ASTContext &Ctx, bool PrintStats,
102 TranslationUnitKind TUKind,
103 CodeCompleteConsumer *CompletionConsumer,
104 bool SkipFunctionBodies) {
105
106 std::unique_ptr<Sema> S(
107 new Sema(PP, Ctx, *Consumer, TUKind, CompletionConsumer));
108
109 // Recover resources if we crash before exiting this method.
110 llvm::CrashRecoveryContextCleanupRegistrar<Sema> CleanupSema(S.get());
111
112 ParseAST(*S.get(), PrintStats, SkipFunctionBodies);
113 }
114
ParseAST(Sema & S,bool PrintStats,bool SkipFunctionBodies)115 void clang::ParseAST(Sema &S, bool PrintStats, bool SkipFunctionBodies) {
116 // Collect global stats on Decls/Stmts (until we have a module streamer).
117 if (PrintStats) {
118 Decl::EnableStatistics();
119 Stmt::EnableStatistics();
120 }
121
122 // Also turn on collection of stats inside of the Sema object.
123 bool OldCollectStats = PrintStats;
124 std::swap(OldCollectStats, S.CollectStats);
125
126 // Initialize the template instantiation observer chain.
127 // FIXME: See note on "finalize" below.
128 initialize(S.TemplateInstCallbacks, S);
129
130 ASTConsumer *Consumer = &S.getASTConsumer();
131
132 std::unique_ptr<Parser> ParseOP(
133 new Parser(S.getPreprocessor(), S, SkipFunctionBodies));
134 Parser &P = *ParseOP.get();
135
136 llvm::CrashRecoveryContextCleanupRegistrar<const void, ResetStackCleanup>
137 CleanupPrettyStack(llvm::SavePrettyStackState());
138 PrettyStackTraceParserEntry CrashInfo(P);
139
140 // Recover resources if we crash before exiting this method.
141 llvm::CrashRecoveryContextCleanupRegistrar<Parser>
142 CleanupParser(ParseOP.get());
143
144 S.getPreprocessor().EnterMainSourceFile();
145 ExternalASTSource *External = S.getASTContext().getExternalSource();
146 if (External)
147 External->StartTranslationUnit(Consumer);
148
149 // If a PCH through header is specified that does not have an include in
150 // the source, or a PCH is being created with #pragma hdrstop with nothing
151 // after the pragma, there won't be any tokens or a Lexer.
152 bool HaveLexer = S.getPreprocessor().getCurrentLexer();
153
154 if (HaveLexer) {
155 llvm::TimeTraceScope TimeScope("Frontend", [&]() {
156 llvm::TimeTraceMetadata M;
157 if (llvm::isTimeTraceVerbose()) {
158 const SourceManager &SM = S.getSourceManager();
159 if (const auto *FE = SM.getFileEntryForID(SM.getMainFileID()))
160 M.File = FE->tryGetRealPathName();
161 }
162 return M;
163 });
164 P.Initialize();
165 Parser::DeclGroupPtrTy ADecl;
166 Sema::ModuleImportState ImportState;
167 EnterExpressionEvaluationContext PotentiallyEvaluated(
168 S, Sema::ExpressionEvaluationContext::PotentiallyEvaluated);
169
170 for (bool AtEOF = P.ParseFirstTopLevelDecl(ADecl, ImportState); !AtEOF;
171 AtEOF = P.ParseTopLevelDecl(ADecl, ImportState)) {
172 // If we got a null return and something *was* parsed, ignore it. This
173 // is due to a top-level semicolon, an action override, or a parse error
174 // skipping something.
175 if (ADecl && !Consumer->HandleTopLevelDecl(ADecl.get()))
176 return;
177 }
178 }
179
180 // Process any TopLevelDecls generated by #pragma weak.
181 for (Decl *D : S.WeakTopLevelDecls())
182 Consumer->HandleTopLevelDecl(DeclGroupRef(D));
183
184 Consumer->HandleTranslationUnit(S.getASTContext());
185
186 // Finalize the template instantiation observer chain.
187 // FIXME: This (and init.) should be done in the Sema class, but because
188 // Sema does not have a reliable "Finalize" function (it has a
189 // destructor, but it is not guaranteed to be called ("-disable-free")).
190 // So, do the initialization above and do the finalization here:
191 finalize(S.TemplateInstCallbacks, S);
192
193 std::swap(OldCollectStats, S.CollectStats);
194 if (PrintStats) {
195 llvm::errs() << "\nSTATISTICS:\n";
196 if (HaveLexer) P.getActions().PrintStats();
197 S.getASTContext().PrintStats();
198 Decl::PrintStats();
199 Stmt::PrintStats();
200 Consumer->PrintStats();
201 }
202 }
203