1 | //===--- AnalysisConsumer.cpp - ASTConsumer for running Analyses ----------===// |
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 | // "Meta" ASTConsumer for running different source analyses. |
10 | // |
11 | //===----------------------------------------------------------------------===// |
12 | |
13 | #include "clang/StaticAnalyzer/Frontend/AnalysisConsumer.h" |
14 | #include "ModelInjector.h" |
15 | #include "clang/AST/Decl.h" |
16 | #include "clang/AST/DeclCXX.h" |
17 | #include "clang/AST/DeclObjC.h" |
18 | #include "clang/AST/RecursiveASTVisitor.h" |
19 | #include "clang/Analysis/Analyses/LiveVariables.h" |
20 | #include "clang/Analysis/CFG.h" |
21 | #include "clang/Analysis/CallGraph.h" |
22 | #include "clang/Analysis/CodeInjector.h" |
23 | #include "clang/Analysis/MacroExpansionContext.h" |
24 | #include "clang/Analysis/PathDiagnostic.h" |
25 | #include "clang/Basic/SourceManager.h" |
26 | #include "clang/CrossTU/CrossTranslationUnit.h" |
27 | #include "clang/Frontend/CompilerInstance.h" |
28 | #include "clang/Lex/Preprocessor.h" |
29 | #include "clang/Rewrite/Core/Rewriter.h" |
30 | #include "clang/StaticAnalyzer/Core/AnalyzerOptions.h" |
31 | #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" |
32 | #include "clang/StaticAnalyzer/Core/CheckerManager.h" |
33 | #include "clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h" |
34 | #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" |
35 | #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" |
36 | #include "llvm/ADT/PostOrderIterator.h" |
37 | #include "llvm/ADT/ScopeExit.h" |
38 | #include "llvm/ADT/Statistic.h" |
39 | #include "llvm/Support/FileSystem.h" |
40 | #include "llvm/Support/Path.h" |
41 | #include "llvm/Support/Program.h" |
42 | #include "llvm/Support/Timer.h" |
43 | #include "llvm/Support/raw_ostream.h" |
44 | #include <memory> |
45 | #include <queue> |
46 | #include <utility> |
47 | |
48 | using namespace clang; |
49 | using namespace ento; |
50 | |
51 | #define DEBUG_TYPE "AnalysisConsumer" |
52 | |
53 | STATISTIC(NumFunctionTopLevel, "The # of functions at top level." ); |
54 | STATISTIC(NumFunctionsAnalyzed, |
55 | "The # of functions and blocks analyzed (as top level " |
56 | "with inlining turned on)." ); |
57 | STATISTIC(NumBlocksInAnalyzedFunctions, |
58 | "The # of basic blocks in the analyzed functions." ); |
59 | STATISTIC(NumVisitedBlocksInAnalyzedFunctions, |
60 | "The # of visited basic blocks in the analyzed functions." ); |
61 | STATISTIC(PercentReachableBlocks, "The % of reachable basic blocks." ); |
62 | STATISTIC(MaxCFGSize, "The maximum number of basic blocks in a function." ); |
63 | |
64 | //===----------------------------------------------------------------------===// |
65 | // AnalysisConsumer declaration. |
66 | //===----------------------------------------------------------------------===// |
67 | |
68 | namespace { |
69 | |
70 | class AnalysisConsumer : public AnalysisASTConsumer, |
71 | public RecursiveASTVisitor<AnalysisConsumer> { |
72 | enum { |
73 | AM_None = 0, |
74 | AM_Syntax = 0x1, |
75 | AM_Path = 0x2 |
76 | }; |
77 | typedef unsigned AnalysisMode; |
78 | |
79 | /// Mode of the analyzes while recursively visiting Decls. |
80 | AnalysisMode RecVisitorMode; |
81 | /// Bug Reporter to use while recursively visiting Decls. |
82 | BugReporter *RecVisitorBR; |
83 | |
84 | std::vector<std::function<void(CheckerRegistry &)>> CheckerRegistrationFns; |
85 | |
86 | public: |
87 | ASTContext *Ctx; |
88 | Preprocessor &PP; |
89 | const std::string OutDir; |
90 | AnalyzerOptions &Opts; |
91 | ArrayRef<std::string> Plugins; |
92 | CodeInjector *Injector; |
93 | cross_tu::CrossTranslationUnitContext CTU; |
94 | |
95 | /// Stores the declarations from the local translation unit. |
96 | /// Note, we pre-compute the local declarations at parse time as an |
97 | /// optimization to make sure we do not deserialize everything from disk. |
98 | /// The local declaration to all declarations ratio might be very small when |
99 | /// working with a PCH file. |
100 | SetOfDecls LocalTUDecls; |
101 | |
102 | MacroExpansionContext MacroExpansions; |
103 | |
104 | // Set of PathDiagnosticConsumers. Owned by AnalysisManager. |
105 | PathDiagnosticConsumers PathConsumers; |
106 | |
107 | StoreManagerCreator CreateStoreMgr; |
108 | ConstraintManagerCreator CreateConstraintMgr; |
109 | |
110 | std::unique_ptr<CheckerManager> checkerMgr; |
111 | std::unique_ptr<AnalysisManager> Mgr; |
112 | |
113 | /// Time the analyzes time of each translation unit. |
114 | std::unique_ptr<llvm::TimerGroup> AnalyzerTimers; |
115 | std::unique_ptr<llvm::Timer> SyntaxCheckTimer; |
116 | std::unique_ptr<llvm::Timer> ExprEngineTimer; |
117 | std::unique_ptr<llvm::Timer> BugReporterTimer; |
118 | |
119 | /// The information about analyzed functions shared throughout the |
120 | /// translation unit. |
121 | FunctionSummariesTy FunctionSummaries; |
122 | |
123 | AnalysisConsumer(CompilerInstance &CI, const std::string &outdir, |
124 | AnalyzerOptions &opts, ArrayRef<std::string> plugins, |
125 | CodeInjector *injector) |
126 | : RecVisitorMode(0), RecVisitorBR(nullptr), Ctx(nullptr), |
127 | PP(CI.getPreprocessor()), OutDir(outdir), Opts(opts), |
128 | Plugins(plugins), Injector(injector), CTU(CI), |
129 | MacroExpansions(CI.getLangOpts()) { |
130 | DigestAnalyzerOptions(); |
131 | if (Opts.AnalyzerDisplayProgress || Opts.PrintStats || |
132 | Opts.ShouldSerializeStats) { |
133 | AnalyzerTimers = std::make_unique<llvm::TimerGroup>( |
134 | args: "analyzer" , args: "Analyzer timers" ); |
135 | SyntaxCheckTimer = std::make_unique<llvm::Timer>( |
136 | args: "syntaxchecks" , args: "Syntax-based analysis time" , args&: *AnalyzerTimers); |
137 | ExprEngineTimer = std::make_unique<llvm::Timer>( |
138 | args: "exprengine" , args: "Path exploration time" , args&: *AnalyzerTimers); |
139 | BugReporterTimer = std::make_unique<llvm::Timer>( |
140 | args: "bugreporter" , args: "Path-sensitive report post-processing time" , |
141 | args&: *AnalyzerTimers); |
142 | } |
143 | |
144 | if (Opts.PrintStats || Opts.ShouldSerializeStats) { |
145 | llvm::EnableStatistics(/* DoPrintOnExit= */ false); |
146 | } |
147 | |
148 | if (Opts.ShouldDisplayMacroExpansions) |
149 | MacroExpansions.registerForPreprocessor(PP); |
150 | } |
151 | |
152 | ~AnalysisConsumer() override { |
153 | if (Opts.PrintStats) { |
154 | llvm::PrintStatistics(); |
155 | } |
156 | } |
157 | |
158 | void DigestAnalyzerOptions() { |
159 | switch (Opts.AnalysisDiagOpt) { |
160 | case PD_NONE: |
161 | break; |
162 | #define ANALYSIS_DIAGNOSTICS(NAME, CMDFLAG, DESC, CREATEFN) \ |
163 | case PD_##NAME: \ |
164 | CREATEFN(Opts.getDiagOpts(), PathConsumers, OutDir, PP, CTU, \ |
165 | MacroExpansions); \ |
166 | break; |
167 | #include "clang/StaticAnalyzer/Core/Analyses.def" |
168 | default: |
169 | llvm_unreachable("Unknown analyzer output type!" ); |
170 | } |
171 | |
172 | // Create the analyzer component creators. |
173 | CreateStoreMgr = &CreateRegionStoreManager; |
174 | |
175 | switch (Opts.AnalysisConstraintsOpt) { |
176 | default: |
177 | llvm_unreachable("Unknown constraint manager." ); |
178 | #define ANALYSIS_CONSTRAINTS(NAME, CMDFLAG, DESC, CREATEFN) \ |
179 | case NAME##Model: CreateConstraintMgr = CREATEFN; break; |
180 | #include "clang/StaticAnalyzer/Core/Analyses.def" |
181 | } |
182 | } |
183 | |
184 | void DisplayTime(llvm::TimeRecord &Time) { |
185 | if (!Opts.AnalyzerDisplayProgress) { |
186 | return; |
187 | } |
188 | llvm::errs() << " : " << llvm::format(Fmt: "%1.1f" , Vals: Time.getWallTime() * 1000) |
189 | << " ms\n" ; |
190 | } |
191 | |
192 | void DisplayFunction(const Decl *D, AnalysisMode Mode, |
193 | ExprEngine::InliningModes IMode) { |
194 | if (!Opts.AnalyzerDisplayProgress) |
195 | return; |
196 | |
197 | SourceManager &SM = Mgr->getASTContext().getSourceManager(); |
198 | PresumedLoc Loc = SM.getPresumedLoc(Loc: D->getLocation()); |
199 | if (Loc.isValid()) { |
200 | llvm::errs() << "ANALYZE" ; |
201 | |
202 | if (Mode == AM_Syntax) |
203 | llvm::errs() << " (Syntax)" ; |
204 | else if (Mode == AM_Path) { |
205 | llvm::errs() << " (Path, " ; |
206 | switch (IMode) { |
207 | case ExprEngine::Inline_Minimal: |
208 | llvm::errs() << " Inline_Minimal" ; |
209 | break; |
210 | case ExprEngine::Inline_Regular: |
211 | llvm::errs() << " Inline_Regular" ; |
212 | break; |
213 | } |
214 | llvm::errs() << ")" ; |
215 | } else |
216 | assert(Mode == (AM_Syntax | AM_Path) && "Unexpected mode!" ); |
217 | |
218 | llvm::errs() << ": " << Loc.getFilename() << ' ' |
219 | << AnalysisDeclContext::getFunctionName(D); |
220 | } |
221 | } |
222 | |
223 | void Initialize(ASTContext &Context) override { |
224 | Ctx = &Context; |
225 | checkerMgr = std::make_unique<CheckerManager>(args&: *Ctx, args&: Opts, args&: PP, args&: Plugins, |
226 | args&: CheckerRegistrationFns); |
227 | |
228 | Mgr = std::make_unique<AnalysisManager>(args&: *Ctx, args&: PP, args&: PathConsumers, |
229 | args&: CreateStoreMgr, args&: CreateConstraintMgr, |
230 | args: checkerMgr.get(), args&: Opts, args&: Injector); |
231 | } |
232 | |
233 | /// Store the top level decls in the set to be processed later on. |
234 | /// (Doing this pre-processing avoids deserialization of data from PCH.) |
235 | bool HandleTopLevelDecl(DeclGroupRef D) override; |
236 | void HandleTopLevelDeclInObjCContainer(DeclGroupRef D) override; |
237 | |
238 | void HandleTranslationUnit(ASTContext &C) override; |
239 | |
240 | /// Determine which inlining mode should be used when this function is |
241 | /// analyzed. This allows to redefine the default inlining policies when |
242 | /// analyzing a given function. |
243 | ExprEngine::InliningModes |
244 | getInliningModeForFunction(const Decl *D, const SetOfConstDecls &Visited); |
245 | |
246 | /// Build the call graph for all the top level decls of this TU and |
247 | /// use it to define the order in which the functions should be visited. |
248 | void HandleDeclsCallGraph(const unsigned LocalTUDeclsSize); |
249 | |
250 | /// Run analyzes(syntax or path sensitive) on the given function. |
251 | /// \param Mode - determines if we are requesting syntax only or path |
252 | /// sensitive only analysis. |
253 | /// \param VisitedCallees - The output parameter, which is populated with the |
254 | /// set of functions which should be considered analyzed after analyzing the |
255 | /// given root function. |
256 | void HandleCode(Decl *D, AnalysisMode Mode, |
257 | ExprEngine::InliningModes IMode = ExprEngine::Inline_Minimal, |
258 | SetOfConstDecls *VisitedCallees = nullptr); |
259 | |
260 | void RunPathSensitiveChecks(Decl *D, |
261 | ExprEngine::InliningModes IMode, |
262 | SetOfConstDecls *VisitedCallees); |
263 | |
264 | /// Visitors for the RecursiveASTVisitor. |
265 | bool shouldWalkTypesOfTypeLocs() const { return false; } |
266 | |
267 | /// Handle callbacks for arbitrary Decls. |
268 | bool VisitDecl(Decl *D) { |
269 | AnalysisMode Mode = getModeForDecl(D, Mode: RecVisitorMode); |
270 | if (Mode & AM_Syntax) { |
271 | if (SyntaxCheckTimer) |
272 | SyntaxCheckTimer->startTimer(); |
273 | checkerMgr->runCheckersOnASTDecl(D, mgr&: *Mgr, BR&: *RecVisitorBR); |
274 | if (SyntaxCheckTimer) |
275 | SyntaxCheckTimer->stopTimer(); |
276 | } |
277 | return true; |
278 | } |
279 | |
280 | bool VisitVarDecl(VarDecl *VD) { |
281 | if (!Opts.IsNaiveCTUEnabled) |
282 | return true; |
283 | |
284 | if (VD->hasExternalStorage() || VD->isStaticDataMember()) { |
285 | if (!cross_tu::shouldImport(VD, ACtx: *Ctx)) |
286 | return true; |
287 | } else { |
288 | // Cannot be initialized in another TU. |
289 | return true; |
290 | } |
291 | |
292 | if (VD->getAnyInitializer()) |
293 | return true; |
294 | |
295 | llvm::Expected<const VarDecl *> CTUDeclOrError = |
296 | CTU.getCrossTUDefinition(VD, CrossTUDir: Opts.CTUDir, IndexName: Opts.CTUIndexName, |
297 | DisplayCTUProgress: Opts.DisplayCTUProgress); |
298 | |
299 | if (!CTUDeclOrError) { |
300 | handleAllErrors(E: CTUDeclOrError.takeError(), |
301 | Handlers: [&](const cross_tu::IndexError &IE) { |
302 | CTU.emitCrossTUDiagnostics(IE); |
303 | }); |
304 | } |
305 | |
306 | return true; |
307 | } |
308 | |
309 | bool VisitFunctionDecl(FunctionDecl *FD) { |
310 | IdentifierInfo *II = FD->getIdentifier(); |
311 | if (II && II->getName().starts_with(Prefix: "__inline" )) |
312 | return true; |
313 | |
314 | // We skip function template definitions, as their semantics is |
315 | // only determined when they are instantiated. |
316 | if (FD->isThisDeclarationADefinition() && |
317 | !FD->isDependentContext()) { |
318 | assert(RecVisitorMode == AM_Syntax || Mgr->shouldInlineCall() == false); |
319 | HandleCode(D: FD, Mode: RecVisitorMode); |
320 | } |
321 | return true; |
322 | } |
323 | |
324 | bool VisitObjCMethodDecl(ObjCMethodDecl *MD) { |
325 | if (MD->isThisDeclarationADefinition()) { |
326 | assert(RecVisitorMode == AM_Syntax || Mgr->shouldInlineCall() == false); |
327 | HandleCode(D: MD, Mode: RecVisitorMode); |
328 | } |
329 | return true; |
330 | } |
331 | |
332 | bool VisitBlockDecl(BlockDecl *BD) { |
333 | if (BD->hasBody()) { |
334 | assert(RecVisitorMode == AM_Syntax || Mgr->shouldInlineCall() == false); |
335 | // Since we skip function template definitions, we should skip blocks |
336 | // declared in those functions as well. |
337 | if (!BD->isDependentContext()) { |
338 | HandleCode(D: BD, Mode: RecVisitorMode); |
339 | } |
340 | } |
341 | return true; |
342 | } |
343 | |
344 | void AddDiagnosticConsumer(PathDiagnosticConsumer *Consumer) override { |
345 | PathConsumers.push_back(x: Consumer); |
346 | } |
347 | |
348 | void AddCheckerRegistrationFn(std::function<void(CheckerRegistry&)> Fn) override { |
349 | CheckerRegistrationFns.push_back(x: std::move(Fn)); |
350 | } |
351 | |
352 | private: |
353 | void storeTopLevelDecls(DeclGroupRef DG); |
354 | |
355 | /// Check if we should skip (not analyze) the given function. |
356 | AnalysisMode getModeForDecl(Decl *D, AnalysisMode Mode); |
357 | void runAnalysisOnTranslationUnit(ASTContext &C); |
358 | |
359 | /// Print \p S to stderr if \c Opts.AnalyzerDisplayProgress is set. |
360 | void reportAnalyzerProgress(StringRef S); |
361 | }; // namespace |
362 | } // end anonymous namespace |
363 | |
364 | |
365 | //===----------------------------------------------------------------------===// |
366 | // AnalysisConsumer implementation. |
367 | //===----------------------------------------------------------------------===// |
368 | bool AnalysisConsumer::HandleTopLevelDecl(DeclGroupRef DG) { |
369 | storeTopLevelDecls(DG); |
370 | return true; |
371 | } |
372 | |
373 | void AnalysisConsumer::HandleTopLevelDeclInObjCContainer(DeclGroupRef DG) { |
374 | storeTopLevelDecls(DG); |
375 | } |
376 | |
377 | void AnalysisConsumer::storeTopLevelDecls(DeclGroupRef DG) { |
378 | for (auto &I : DG) { |
379 | |
380 | // Skip ObjCMethodDecl, wait for the objc container to avoid |
381 | // analyzing twice. |
382 | if (isa<ObjCMethodDecl>(Val: I)) |
383 | continue; |
384 | |
385 | LocalTUDecls.push_back(x: I); |
386 | } |
387 | } |
388 | |
389 | static bool shouldSkipFunction(const Decl *D, |
390 | const SetOfConstDecls &Visited, |
391 | const SetOfConstDecls &VisitedAsTopLevel) { |
392 | if (VisitedAsTopLevel.count(V: D)) |
393 | return true; |
394 | |
395 | // Skip analysis of inheriting constructors as top-level functions. These |
396 | // constructors don't even have a body written down in the code, so even if |
397 | // we find a bug, we won't be able to display it. |
398 | if (const auto *CD = dyn_cast<CXXConstructorDecl>(Val: D)) |
399 | if (CD->isInheritingConstructor()) |
400 | return true; |
401 | |
402 | // We want to re-analyse the functions as top level in the following cases: |
403 | // - The 'init' methods should be reanalyzed because |
404 | // ObjCNonNilReturnValueChecker assumes that '[super init]' never returns |
405 | // 'nil' and unless we analyze the 'init' functions as top level, we will |
406 | // not catch errors within defensive code. |
407 | // - We want to reanalyze all ObjC methods as top level to report Retain |
408 | // Count naming convention errors more aggressively. |
409 | if (isa<ObjCMethodDecl>(Val: D)) |
410 | return false; |
411 | // We also want to reanalyze all C++ copy and move assignment operators to |
412 | // separately check the two cases where 'this' aliases with the parameter and |
413 | // where it may not. (cplusplus.SelfAssignmentChecker) |
414 | if (const auto *MD = dyn_cast<CXXMethodDecl>(Val: D)) { |
415 | if (MD->isCopyAssignmentOperator() || MD->isMoveAssignmentOperator()) |
416 | return false; |
417 | } |
418 | |
419 | // Otherwise, if we visited the function before, do not reanalyze it. |
420 | return Visited.count(V: D); |
421 | } |
422 | |
423 | ExprEngine::InliningModes |
424 | AnalysisConsumer::getInliningModeForFunction(const Decl *D, |
425 | const SetOfConstDecls &Visited) { |
426 | // We want to reanalyze all ObjC methods as top level to report Retain |
427 | // Count naming convention errors more aggressively. But we should tune down |
428 | // inlining when reanalyzing an already inlined function. |
429 | if (Visited.count(V: D) && isa<ObjCMethodDecl>(Val: D)) { |
430 | const ObjCMethodDecl *ObjCM = cast<ObjCMethodDecl>(Val: D); |
431 | if (ObjCM->getMethodFamily() != OMF_init) |
432 | return ExprEngine::Inline_Minimal; |
433 | } |
434 | |
435 | return ExprEngine::Inline_Regular; |
436 | } |
437 | |
438 | void AnalysisConsumer::HandleDeclsCallGraph(const unsigned LocalTUDeclsSize) { |
439 | // Build the Call Graph by adding all the top level declarations to the graph. |
440 | // Note: CallGraph can trigger deserialization of more items from a pch |
441 | // (though HandleInterestingDecl); triggering additions to LocalTUDecls. |
442 | // We rely on random access to add the initially processed Decls to CG. |
443 | CallGraph CG; |
444 | for (unsigned i = 0 ; i < LocalTUDeclsSize ; ++i) { |
445 | CG.addToCallGraph(D: LocalTUDecls[i]); |
446 | } |
447 | |
448 | // Walk over all of the call graph nodes in topological order, so that we |
449 | // analyze parents before the children. Skip the functions inlined into |
450 | // the previously processed functions. Use external Visited set to identify |
451 | // inlined functions. The topological order allows the "do not reanalyze |
452 | // previously inlined function" performance heuristic to be triggered more |
453 | // often. |
454 | SetOfConstDecls Visited; |
455 | SetOfConstDecls VisitedAsTopLevel; |
456 | llvm::ReversePostOrderTraversal<clang::CallGraph*> RPOT(&CG); |
457 | for (auto &N : RPOT) { |
458 | NumFunctionTopLevel++; |
459 | |
460 | Decl *D = N->getDecl(); |
461 | |
462 | // Skip the abstract root node. |
463 | if (!D) |
464 | continue; |
465 | |
466 | // Skip the functions which have been processed already or previously |
467 | // inlined. |
468 | if (shouldSkipFunction(D, Visited, VisitedAsTopLevel)) |
469 | continue; |
470 | |
471 | // The CallGraph might have declarations as callees. However, during CTU |
472 | // the declaration might form a declaration chain with the newly imported |
473 | // definition from another TU. In this case we don't want to analyze the |
474 | // function definition as toplevel. |
475 | if (const auto *FD = dyn_cast<FunctionDecl>(Val: D)) { |
476 | // Calling 'hasBody' replaces 'FD' in place with the FunctionDecl |
477 | // that has the body. |
478 | FD->hasBody(Definition&: FD); |
479 | if (CTU.isImportedAsNew(ToDecl: FD)) |
480 | continue; |
481 | } |
482 | |
483 | // Analyze the function. |
484 | SetOfConstDecls VisitedCallees; |
485 | |
486 | HandleCode(D, Mode: AM_Path, IMode: getInliningModeForFunction(D, Visited), |
487 | VisitedCallees: (Mgr->options.InliningMode == All ? nullptr : &VisitedCallees)); |
488 | |
489 | // Add the visited callees to the global visited set. |
490 | for (const Decl *Callee : VisitedCallees) |
491 | // Decls from CallGraph are already canonical. But Decls coming from |
492 | // CallExprs may be not. We should canonicalize them manually. |
493 | Visited.insert(V: isa<ObjCMethodDecl>(Val: Callee) ? Callee |
494 | : Callee->getCanonicalDecl()); |
495 | VisitedAsTopLevel.insert(V: D); |
496 | } |
497 | } |
498 | |
499 | static bool fileContainsString(StringRef Substring, ASTContext &C) { |
500 | const SourceManager &SM = C.getSourceManager(); |
501 | FileID FID = SM.getMainFileID(); |
502 | StringRef Buffer = SM.getBufferOrFake(FID).getBuffer(); |
503 | return Buffer.contains(Other: Substring); |
504 | } |
505 | |
506 | static void reportAnalyzerFunctionMisuse(const AnalyzerOptions &Opts, |
507 | const ASTContext &Ctx) { |
508 | llvm::errs() << "Every top-level function was skipped.\n" ; |
509 | |
510 | if (!Opts.AnalyzerDisplayProgress) |
511 | llvm::errs() << "Pass the -analyzer-display-progress for tracking which " |
512 | "functions are analyzed.\n" ; |
513 | |
514 | bool HasBrackets = |
515 | Opts.AnalyzeSpecificFunction.find(s: "(" ) != std::string::npos; |
516 | |
517 | if (Ctx.getLangOpts().CPlusPlus && !HasBrackets) { |
518 | llvm::errs() |
519 | << "For analyzing C++ code you need to pass the function parameter " |
520 | "list: -analyze-function=\"foobar(int, _Bool)\"\n" ; |
521 | } else if (!Ctx.getLangOpts().CPlusPlus && HasBrackets) { |
522 | llvm::errs() << "For analyzing C code you shouldn't pass the function " |
523 | "parameter list, only the name of the function: " |
524 | "-analyze-function=foobar\n" ; |
525 | } |
526 | } |
527 | |
528 | void AnalysisConsumer::runAnalysisOnTranslationUnit(ASTContext &C) { |
529 | BugReporter BR(*Mgr); |
530 | const TranslationUnitDecl *TU = C.getTranslationUnitDecl(); |
531 | BR.setAnalysisEntryPoint(TU); |
532 | if (SyntaxCheckTimer) |
533 | SyntaxCheckTimer->startTimer(); |
534 | checkerMgr->runCheckersOnASTDecl(D: TU, mgr&: *Mgr, BR); |
535 | if (SyntaxCheckTimer) |
536 | SyntaxCheckTimer->stopTimer(); |
537 | |
538 | // Run the AST-only checks using the order in which functions are defined. |
539 | // If inlining is not turned on, use the simplest function order for path |
540 | // sensitive analyzes as well. |
541 | RecVisitorMode = AM_Syntax; |
542 | if (!Mgr->shouldInlineCall()) |
543 | RecVisitorMode |= AM_Path; |
544 | RecVisitorBR = &BR; |
545 | |
546 | // Process all the top level declarations. |
547 | // |
548 | // Note: TraverseDecl may modify LocalTUDecls, but only by appending more |
549 | // entries. Thus we don't use an iterator, but rely on LocalTUDecls |
550 | // random access. By doing so, we automatically compensate for iterators |
551 | // possibly being invalidated, although this is a bit slower. |
552 | const unsigned LocalTUDeclsSize = LocalTUDecls.size(); |
553 | for (unsigned i = 0 ; i < LocalTUDeclsSize ; ++i) { |
554 | TraverseDecl(D: LocalTUDecls[i]); |
555 | } |
556 | |
557 | if (Mgr->shouldInlineCall()) |
558 | HandleDeclsCallGraph(LocalTUDeclsSize); |
559 | |
560 | // After all decls handled, run checkers on the entire TranslationUnit. |
561 | checkerMgr->runCheckersOnEndOfTranslationUnit(TU, mgr&: *Mgr, BR); |
562 | |
563 | BR.FlushReports(); |
564 | RecVisitorBR = nullptr; |
565 | |
566 | // If the user wanted to analyze a specific function and the number of basic |
567 | // blocks analyzed is zero, than the user might not specified the function |
568 | // name correctly. |
569 | // FIXME: The user might have analyzed the requested function in Syntax mode, |
570 | // but we are unaware of that. |
571 | if (!Opts.AnalyzeSpecificFunction.empty() && NumFunctionsAnalyzed == 0) |
572 | reportAnalyzerFunctionMisuse(Opts, Ctx: *Ctx); |
573 | } |
574 | |
575 | void AnalysisConsumer::reportAnalyzerProgress(StringRef S) { |
576 | if (Opts.AnalyzerDisplayProgress) |
577 | llvm::errs() << S; |
578 | } |
579 | |
580 | void AnalysisConsumer::HandleTranslationUnit(ASTContext &C) { |
581 | // Don't run the actions if an error has occurred with parsing the file. |
582 | DiagnosticsEngine &Diags = PP.getDiagnostics(); |
583 | if (Diags.hasErrorOccurred() || Diags.hasFatalErrorOccurred()) |
584 | return; |
585 | |
586 | // Explicitly destroy the PathDiagnosticConsumer. This will flush its output. |
587 | // FIXME: This should be replaced with something that doesn't rely on |
588 | // side-effects in PathDiagnosticConsumer's destructor. This is required when |
589 | // used with option -disable-free. |
590 | const auto DiagFlusherScopeExit = |
591 | llvm::make_scope_exit(F: [this] { Mgr.reset(); }); |
592 | |
593 | if (Opts.ShouldIgnoreBisonGeneratedFiles && |
594 | fileContainsString(Substring: "/* A Bison parser, made by" , C)) { |
595 | reportAnalyzerProgress(S: "Skipping bison-generated file\n" ); |
596 | return; |
597 | } |
598 | |
599 | if (Opts.ShouldIgnoreFlexGeneratedFiles && |
600 | fileContainsString(Substring: "/* A lexical scanner generated by flex" , C)) { |
601 | reportAnalyzerProgress(S: "Skipping flex-generated file\n" ); |
602 | return; |
603 | } |
604 | |
605 | // Don't analyze if the user explicitly asked for no checks to be performed |
606 | // on this file. |
607 | if (Opts.DisableAllCheckers) { |
608 | reportAnalyzerProgress(S: "All checks are disabled using a supplied option\n" ); |
609 | return; |
610 | } |
611 | |
612 | // Otherwise, just run the analysis. |
613 | runAnalysisOnTranslationUnit(C); |
614 | |
615 | // Count how many basic blocks we have not covered. |
616 | NumBlocksInAnalyzedFunctions = FunctionSummaries.getTotalNumBasicBlocks(); |
617 | NumVisitedBlocksInAnalyzedFunctions = |
618 | FunctionSummaries.getTotalNumVisitedBasicBlocks(); |
619 | if (NumBlocksInAnalyzedFunctions > 0) |
620 | PercentReachableBlocks = |
621 | (FunctionSummaries.getTotalNumVisitedBasicBlocks() * 100) / |
622 | NumBlocksInAnalyzedFunctions; |
623 | } |
624 | |
625 | AnalysisConsumer::AnalysisMode |
626 | AnalysisConsumer::getModeForDecl(Decl *D, AnalysisMode Mode) { |
627 | if (!Opts.AnalyzeSpecificFunction.empty() && |
628 | AnalysisDeclContext::getFunctionName(D) != Opts.AnalyzeSpecificFunction) |
629 | return AM_None; |
630 | |
631 | // Unless -analyze-all is specified, treat decls differently depending on |
632 | // where they came from: |
633 | // - Main source file: run both path-sensitive and non-path-sensitive checks. |
634 | // - Header files: run non-path-sensitive checks only. |
635 | // - System headers: don't run any checks. |
636 | if (Opts.AnalyzeAll) |
637 | return Mode; |
638 | |
639 | const SourceManager &SM = Ctx->getSourceManager(); |
640 | |
641 | const SourceLocation Loc = [&SM](Decl *D) -> SourceLocation { |
642 | const Stmt *Body = D->getBody(); |
643 | SourceLocation SL = Body ? Body->getBeginLoc() : D->getLocation(); |
644 | return SM.getExpansionLoc(Loc: SL); |
645 | }(D); |
646 | |
647 | // Ignore system headers. |
648 | if (Loc.isInvalid() || SM.isInSystemHeader(Loc)) |
649 | return AM_None; |
650 | |
651 | // Disable path sensitive analysis in user-headers. |
652 | if (!Mgr->isInCodeFile(SL: Loc)) |
653 | return Mode & ~AM_Path; |
654 | |
655 | return Mode; |
656 | } |
657 | |
658 | void AnalysisConsumer::HandleCode(Decl *D, AnalysisMode Mode, |
659 | ExprEngine::InliningModes IMode, |
660 | SetOfConstDecls *VisitedCallees) { |
661 | if (!D->hasBody()) |
662 | return; |
663 | Mode = getModeForDecl(D, Mode); |
664 | if (Mode == AM_None) |
665 | return; |
666 | |
667 | // Clear the AnalysisManager of old AnalysisDeclContexts. |
668 | Mgr->ClearContexts(); |
669 | // Ignore autosynthesized code. |
670 | if (Mgr->getAnalysisDeclContext(D)->isBodyAutosynthesized()) |
671 | return; |
672 | |
673 | CFG *DeclCFG = Mgr->getCFG(D); |
674 | if (DeclCFG) |
675 | MaxCFGSize.updateMax(V: DeclCFG->size()); |
676 | |
677 | DisplayFunction(D, Mode, IMode); |
678 | BugReporter BR(*Mgr); |
679 | BR.setAnalysisEntryPoint(D); |
680 | |
681 | if (Mode & AM_Syntax) { |
682 | llvm::TimeRecord CheckerStartTime; |
683 | if (SyntaxCheckTimer) { |
684 | CheckerStartTime = SyntaxCheckTimer->getTotalTime(); |
685 | SyntaxCheckTimer->startTimer(); |
686 | } |
687 | checkerMgr->runCheckersOnASTBody(D, mgr&: *Mgr, BR); |
688 | if (SyntaxCheckTimer) { |
689 | SyntaxCheckTimer->stopTimer(); |
690 | llvm::TimeRecord CheckerEndTime = SyntaxCheckTimer->getTotalTime(); |
691 | CheckerEndTime -= CheckerStartTime; |
692 | DisplayTime(Time&: CheckerEndTime); |
693 | } |
694 | } |
695 | |
696 | BR.FlushReports(); |
697 | |
698 | if ((Mode & AM_Path) && checkerMgr->hasPathSensitiveCheckers()) { |
699 | RunPathSensitiveChecks(D, IMode, VisitedCallees); |
700 | if (IMode != ExprEngine::Inline_Minimal) |
701 | NumFunctionsAnalyzed++; |
702 | } |
703 | } |
704 | |
705 | //===----------------------------------------------------------------------===// |
706 | // Path-sensitive checking. |
707 | //===----------------------------------------------------------------------===// |
708 | |
709 | void AnalysisConsumer::RunPathSensitiveChecks(Decl *D, |
710 | ExprEngine::InliningModes IMode, |
711 | SetOfConstDecls *VisitedCallees) { |
712 | // Construct the analysis engine. First check if the CFG is valid. |
713 | // FIXME: Inter-procedural analysis will need to handle invalid CFGs. |
714 | if (!Mgr->getCFG(D)) |
715 | return; |
716 | |
717 | // See if the LiveVariables analysis scales. |
718 | if (!Mgr->getAnalysisDeclContext(D)->getAnalysis<RelaxedLiveVariables>()) |
719 | return; |
720 | |
721 | ExprEngine Eng(CTU, *Mgr, VisitedCallees, &FunctionSummaries, IMode); |
722 | |
723 | // Execute the worklist algorithm. |
724 | llvm::TimeRecord ExprEngineStartTime; |
725 | if (ExprEngineTimer) { |
726 | ExprEngineStartTime = ExprEngineTimer->getTotalTime(); |
727 | ExprEngineTimer->startTimer(); |
728 | } |
729 | Eng.ExecuteWorkList(L: Mgr->getAnalysisDeclContextManager().getStackFrame(D), |
730 | Steps: Mgr->options.MaxNodesPerTopLevelFunction); |
731 | if (ExprEngineTimer) { |
732 | ExprEngineTimer->stopTimer(); |
733 | llvm::TimeRecord ExprEngineEndTime = ExprEngineTimer->getTotalTime(); |
734 | ExprEngineEndTime -= ExprEngineStartTime; |
735 | DisplayTime(Time&: ExprEngineEndTime); |
736 | } |
737 | |
738 | if (!Mgr->options.DumpExplodedGraphTo.empty()) |
739 | Eng.DumpGraph(trim: Mgr->options.TrimGraph, Filename: Mgr->options.DumpExplodedGraphTo); |
740 | |
741 | // Visualize the exploded graph. |
742 | if (Mgr->options.visualizeExplodedGraphWithGraphViz) |
743 | Eng.ViewGraph(trim: Mgr->options.TrimGraph); |
744 | |
745 | // Display warnings. |
746 | if (BugReporterTimer) |
747 | BugReporterTimer->startTimer(); |
748 | Eng.getBugReporter().FlushReports(); |
749 | if (BugReporterTimer) |
750 | BugReporterTimer->stopTimer(); |
751 | } |
752 | |
753 | //===----------------------------------------------------------------------===// |
754 | // AnalysisConsumer creation. |
755 | //===----------------------------------------------------------------------===// |
756 | |
757 | std::unique_ptr<AnalysisASTConsumer> |
758 | ento::CreateAnalysisConsumer(CompilerInstance &CI) { |
759 | // Disable the effects of '-Werror' when using the AnalysisConsumer. |
760 | CI.getPreprocessor().getDiagnostics().setWarningsAsErrors(false); |
761 | |
762 | AnalyzerOptions &analyzerOpts = CI.getAnalyzerOpts(); |
763 | bool hasModelPath = analyzerOpts.Config.count(Key: "model-path" ) > 0; |
764 | |
765 | return std::make_unique<AnalysisConsumer>( |
766 | args&: CI, args&: CI.getFrontendOpts().OutputFile, args&: analyzerOpts, |
767 | args&: CI.getFrontendOpts().Plugins, |
768 | args: hasModelPath ? new ModelInjector(CI) : nullptr); |
769 | } |
770 | |