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