Coverage Report

Created: 2022-01-22 13:19

/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/Interpreter/IncrementalParser.cpp
Line
Count
Source (jump to first uncovered line)
1
//===--------- IncrementalParser.cpp - Incremental Compilation  -----------===//
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 class which performs incremental code compilation.
10
//
11
//===----------------------------------------------------------------------===//
12
13
#include "IncrementalParser.h"
14
15
#include "clang/AST/DeclContextInternals.h"
16
#include "clang/CodeGen/BackendUtil.h"
17
#include "clang/CodeGen/CodeGenAction.h"
18
#include "clang/CodeGen/ModuleBuilder.h"
19
#include "clang/Frontend/CompilerInstance.h"
20
#include "clang/Frontend/FrontendAction.h"
21
#include "clang/FrontendTool/Utils.h"
22
#include "clang/Parse/Parser.h"
23
#include "clang/Sema/Sema.h"
24
25
#include "llvm/Option/ArgList.h"
26
#include "llvm/Support/CrashRecoveryContext.h"
27
#include "llvm/Support/Error.h"
28
#include "llvm/Support/Timer.h"
29
30
#include <sstream>
31
32
namespace clang {
33
34
/// A custom action enabling the incremental processing functionality.
35
///
36
/// The usual \p FrontendAction expects one call to ExecuteAction and once it
37
/// sees a call to \p EndSourceFile it deletes some of the important objects
38
/// such as \p Preprocessor and \p Sema assuming no further input will come.
39
///
40
/// \p IncrementalAction ensures it keep its underlying action's objects alive
41
/// as long as the \p IncrementalParser needs them.
42
///
43
class IncrementalAction : public WrapperFrontendAction {
44
private:
45
  bool IsTerminating = false;
46
47
public:
48
  IncrementalAction(CompilerInstance &CI, llvm::LLVMContext &LLVMCtx,
49
                    llvm::Error &Err)
50
11
      : WrapperFrontendAction([&]() {
51
11
          llvm::ErrorAsOutParameter EAO(&Err);
52
11
          std::unique_ptr<FrontendAction> Act;
53
11
          switch (CI.getFrontendOpts().ProgramAction) {
54
0
          default:
55
0
            Err = llvm::createStringError(
56
0
                std::errc::state_not_recoverable,
57
0
                "Driver initialization failed. "
58
0
                "Incremental mode for action %d is not supported",
59
0
                CI.getFrontendOpts().ProgramAction);
60
0
            return Act;
61
1
          case frontend::ASTDump:
62
1
            LLVM_FALLTHROUGH;
63
1
          case frontend::ASTPrint:
64
1
            LLVM_FALLTHROUGH;
65
1
          case frontend::ParseSyntaxOnly:
66
1
            Act = CreateFrontendAction(CI);
67
1
            break;
68
0
          case frontend::PluginAction:
69
0
            LLVM_FALLTHROUGH;
70
0
          case frontend::EmitAssembly:
71
0
            LLVM_FALLTHROUGH;
72
2
          case frontend::EmitObj:
73
2
            LLVM_FALLTHROUGH;
74
10
          case frontend::EmitLLVMOnly:
75
10
            Act.reset(new EmitLLVMOnlyAction(&LLVMCtx));
76
10
            break;
77
11
          }
78
11
          return Act;
79
11
        }()) {}
80
61
  FrontendAction *getWrapped() const { return WrappedAction.get(); }
81
22
  TranslationUnitKind getTranslationUnitKind() override {
82
22
    return TU_Incremental;
83
22
  }
84
11
  void ExecuteAction() override {
85
11
    CompilerInstance &CI = getCompilerInstance();
86
11
    assert(CI.hasPreprocessor() && "No PP!");
87
88
    // FIXME: Move the truncation aspect of this into Sema, we delayed this till
89
    // here so the source manager would be initialized.
90
11
    if (hasCodeCompletionSupport() &&
91
11
        
!CI.getFrontendOpts().CodeCompletionAt.FileName.empty()0
)
92
0
      CI.createCodeCompletionConsumer();
93
94
    // Use a code completion consumer?
95
11
    CodeCompleteConsumer *CompletionConsumer = nullptr;
96
11
    if (CI.hasCodeCompletionConsumer())
97
0
      CompletionConsumer = &CI.getCodeCompletionConsumer();
98
99
11
    Preprocessor &PP = CI.getPreprocessor();
100
11
    PP.enableIncrementalProcessing();
101
11
    PP.EnterMainSourceFile();
102
103
11
    if (!CI.hasSema())
104
11
      CI.createSema(getTranslationUnitKind(), CompletionConsumer);
105
11
  }
106
107
  // Do not terminate after processing the input. This allows us to keep various
108
  // clang objects alive and to incrementally grow the current TU.
109
22
  void EndSourceFile() override {
110
    // The WrappedAction can be nullptr if we issued an error in the ctor.
111
22
    if (IsTerminating && 
getWrapped()11
)
112
11
      WrapperFrontendAction::EndSourceFile();
113
22
  }
114
115
11
  void FinalizeAction() {
116
11
    assert(!IsTerminating && "Already finalized!");
117
0
    IsTerminating = true;
118
11
    EndSourceFile();
119
11
  }
120
};
121
122
IncrementalParser::IncrementalParser(std::unique_ptr<CompilerInstance> Instance,
123
                                     llvm::LLVMContext &LLVMCtx,
124
                                     llvm::Error &Err)
125
11
    : CI(std::move(Instance)) {
126
11
  llvm::ErrorAsOutParameter EAO(&Err);
127
11
  Act = std::make_unique<IncrementalAction>(*CI, LLVMCtx, Err);
128
11
  if (Err)
129
0
    return;
130
11
  CI->ExecuteAction(*Act);
131
11
  Consumer = &CI->getASTConsumer();
132
11
  P.reset(
133
11
      new Parser(CI->getPreprocessor(), CI->getSema(), /*SkipBodies=*/false));
134
11
  P->Initialize();
135
11
}
136
137
11
IncrementalParser::~IncrementalParser() { Act->FinalizeAction(); }
138
139
llvm::Expected<PartialTranslationUnit &>
140
51
IncrementalParser::ParseOrWrapTopLevelDecl() {
141
  // Recover resources if we crash before exiting this method.
142
51
  Sema &S = CI->getSema();
143
51
  llvm::CrashRecoveryContextCleanupRegistrar<Sema> CleanupSema(&S);
144
51
  Sema::GlobalEagerInstantiationScope GlobalInstantiations(S, /*Enabled=*/true);
145
51
  Sema::LocalEagerInstantiationScope LocalInstantiations(S);
146
147
51
  PTUs.emplace_back(PartialTranslationUnit());
148
51
  PartialTranslationUnit &LastPTU = PTUs.back();
149
  // Add a new PTU.
150
51
  ASTContext &C = S.getASTContext();
151
51
  C.addTranslationUnitDecl();
152
51
  LastPTU.TUPart = C.getTranslationUnitDecl();
153
154
  // Skip previous eof due to last incremental input.
155
51
  if (P->getCurToken().is(tok::eof)) {
156
51
    P->ConsumeToken();
157
    // FIXME: Clang does not call ExitScope on finalizing the regular TU, we
158
    // might want to do that around HandleEndOfTranslationUnit.
159
51
    P->ExitScope();
160
51
    S.CurContext = nullptr;
161
    // Start a new PTU.
162
51
    P->EnterScope(Scope::DeclScope);
163
51
    S.ActOnTranslationUnitScope(P->getCurScope());
164
51
  }
165
166
51
  Parser::DeclGroupPtrTy ADecl;
167
88
  for (bool AtEOF = P->ParseFirstTopLevelDecl(ADecl); !AtEOF;
168
51
       
AtEOF = P->ParseTopLevelDecl(ADecl)37
) {
169
    // If we got a null return and something *was* parsed, ignore it.  This
170
    // is due to a top-level semicolon, an action override, or a parse error
171
    // skipping something.
172
37
    if (ADecl && 
!Consumer->HandleTopLevelDecl(ADecl.get())36
)
173
0
      return llvm::make_error<llvm::StringError>("Parsing failed. "
174
0
                                                 "The consumer rejected a decl",
175
0
                                                 std::error_code());
176
37
  }
177
178
51
  DiagnosticsEngine &Diags = getCI()->getDiagnostics();
179
51
  if (Diags.hasErrorOccurred()) {
180
2
    TranslationUnitDecl *MostRecentTU = C.getTranslationUnitDecl();
181
2
    TranslationUnitDecl *PreviousTU = MostRecentTU->getPreviousDecl();
182
2
    assert(PreviousTU && "Must have a TU from the ASTContext initialization!");
183
0
    TranslationUnitDecl *FirstTU = MostRecentTU->getFirstDecl();
184
2
    assert(FirstTU);
185
0
    FirstTU->RedeclLink.setLatest(PreviousTU);
186
2
    C.TUDecl = PreviousTU;
187
2
    S.TUScope->setEntity(PreviousTU);
188
189
    // Clean up the lookup table
190
2
    if (StoredDeclsMap *Map = PreviousTU->getLookupPtr()) {
191
7
      for (auto I = Map->begin(); I != Map->end(); 
++I6
) {
192
6
        StoredDeclsList &List = I->second;
193
6
        DeclContextLookupResult R = List.getLookupResult();
194
6
        for (NamedDecl *D : R)
195
6
          if (D->getTranslationUnitDecl() == MostRecentTU)
196
1
            List.remove(D);
197
6
        if (List.isNull())
198
1
          Map->erase(I);
199
6
      }
200
1
    }
201
202
    // FIXME: Do not reset the pragma handlers.
203
2
    Diags.Reset();
204
2
    return llvm::make_error<llvm::StringError>("Parsing failed.",
205
2
                                               std::error_code());
206
2
  }
207
208
  // Process any TopLevelDecls generated by #pragma weak.
209
49
  for (Decl *D : S.WeakTopLevelDecls()) {
210
0
    DeclGroupRef DGR(D);
211
0
    Consumer->HandleTopLevelDecl(DGR);
212
0
  }
213
214
49
  LocalInstantiations.perform();
215
49
  GlobalInstantiations.perform();
216
217
49
  Consumer->HandleTranslationUnit(C);
218
219
49
  return LastPTU;
220
51
}
221
222
50
static CodeGenerator *getCodeGen(FrontendAction *Act) {
223
50
  IncrementalAction *IncrAct = static_cast<IncrementalAction *>(Act);
224
50
  FrontendAction *WrappedAct = IncrAct->getWrapped();
225
50
  if (!WrappedAct->hasIRSupport())
226
17
    return nullptr;
227
33
  return static_cast<CodeGenAction *>(WrappedAct)->getCodeGenerator();
228
50
}
229
230
llvm::Expected<PartialTranslationUnit &>
231
51
IncrementalParser::Parse(llvm::StringRef input) {
232
51
  Preprocessor &PP = CI->getPreprocessor();
233
51
  assert(PP.isIncrementalProcessingEnabled() && "Not in incremental mode!?");
234
235
0
  std::ostringstream SourceName;
236
51
  SourceName << "input_line_" << InputCount++;
237
238
  // Create an uninitialized memory buffer, copy code in and append "\n"
239
51
  size_t InputSize = input.size(); // don't include trailing 0
240
  // MemBuffer size should *not* include terminating zero
241
51
  std::unique_ptr<llvm::MemoryBuffer> MB(
242
51
      llvm::WritableMemoryBuffer::getNewUninitMemBuffer(InputSize + 1,
243
51
                                                        SourceName.str()));
244
51
  char *MBStart = const_cast<char *>(MB->getBufferStart());
245
51
  memcpy(MBStart, input.data(), InputSize);
246
51
  MBStart[InputSize] = '\n';
247
248
51
  SourceManager &SM = CI->getSourceManager();
249
250
  // FIXME: Create SourceLocation, which will allow clang to order the overload
251
  // candidates for example
252
51
  SourceLocation NewLoc = SM.getLocForStartOfFile(SM.getMainFileID());
253
254
  // Create FileID for the current buffer.
255
51
  FileID FID = SM.createFileID(std::move(MB), SrcMgr::C_User, /*LoadedID=*/0,
256
51
                               /*LoadedOffset=*/0, NewLoc);
257
258
  // NewLoc only used for diags.
259
51
  if (PP.EnterSourceFile(FID, /*DirLookup=*/nullptr, NewLoc))
260
0
    return llvm::make_error<llvm::StringError>("Parsing failed. "
261
0
                                               "Cannot enter source file.",
262
0
                                               std::error_code());
263
264
51
  auto PTU = ParseOrWrapTopLevelDecl();
265
51
  if (!PTU)
266
2
    return PTU.takeError();
267
268
49
  if (PP.getLangOpts().DelayedTemplateParsing) {
269
    // Microsoft-specific:
270
    // Late parsed templates can leave unswallowed "macro"-like tokens.
271
    // They will seriously confuse the Parser when entering the next
272
    // source file. So lex until we are EOF.
273
0
    Token Tok;
274
0
    do {
275
0
      PP.Lex(Tok);
276
0
    } while (Tok.isNot(tok::eof));
277
0
  }
278
279
49
  Token AssertTok;
280
49
  PP.Lex(AssertTok);
281
49
  assert(AssertTok.is(tok::eof) &&
282
49
         "Lexer must be EOF when starting incremental parse!");
283
284
49
  if (CodeGenerator *CG = getCodeGen(Act.get())) {
285
32
    std::unique_ptr<llvm::Module> M(CG->ReleaseModule());
286
32
    CG->StartModule("incr_module_" + std::to_string(PTUs.size()),
287
32
                    M->getContext());
288
289
32
    PTU->TheModule = std::move(M);
290
32
  }
291
292
49
  return PTU;
293
51
}
294
295
1
llvm::StringRef IncrementalParser::GetMangledName(GlobalDecl GD) const {
296
1
  CodeGenerator *CG = getCodeGen(Act.get());
297
1
  assert(CG);
298
0
  return CG->GetMangledName(GD);
299
1
}
300
301
} // end namespace clang