1//===- BugReporterVisitors.cpp - Helpers for reporting bugs ---------------===//
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 defines a set of BugReporter "visitors" which can be used to
10// enhance the diagnostics reported for a bug.
11//
12//===----------------------------------------------------------------------===//
13
14#include "clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h"
15#include "clang/AST/ASTContext.h"
16#include "clang/AST/Decl.h"
17#include "clang/AST/DeclBase.h"
18#include "clang/AST/DeclCXX.h"
19#include "clang/AST/Expr.h"
20#include "clang/AST/ExprCXX.h"
21#include "clang/AST/ExprObjC.h"
22#include "clang/AST/Stmt.h"
23#include "clang/AST/Type.h"
24#include "clang/ASTMatchers/ASTMatchFinder.h"
25#include "clang/Analysis/Analyses/Dominators.h"
26#include "clang/Analysis/AnalysisDeclContext.h"
27#include "clang/Analysis/CFG.h"
28#include "clang/Analysis/CFGStmtMap.h"
29#include "clang/Analysis/PathDiagnostic.h"
30#include "clang/Analysis/ProgramPoint.h"
31#include "clang/Basic/IdentifierTable.h"
32#include "clang/Basic/LLVM.h"
33#include "clang/Basic/SourceLocation.h"
34#include "clang/Basic/SourceManager.h"
35#include "clang/Lex/Lexer.h"
36#include "clang/StaticAnalyzer/Core/AnalyzerOptions.h"
37#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
38#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
39#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
40#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h"
41#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
42#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h"
43#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
44#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h"
45#include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h"
46#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
47#include "llvm/ADT/ArrayRef.h"
48#include "llvm/ADT/SmallPtrSet.h"
49#include "llvm/ADT/SmallString.h"
50#include "llvm/ADT/StringExtras.h"
51#include "llvm/ADT/StringRef.h"
52#include "llvm/Support/Casting.h"
53#include "llvm/Support/ErrorHandling.h"
54#include "llvm/Support/raw_ostream.h"
55#include <cassert>
56#include <memory>
57#include <optional>
58#include <stack>
59#include <string>
60#include <utility>
61
62using namespace clang;
63using namespace ento;
64using namespace bugreporter;
65
66//===----------------------------------------------------------------------===//
67// Utility functions.
68//===----------------------------------------------------------------------===//
69
70static const Expr *peelOffPointerArithmetic(const BinaryOperator *B) {
71 if (B->isAdditiveOp() && B->getType()->isPointerType()) {
72 if (B->getLHS()->getType()->isPointerType()) {
73 return B->getLHS();
74 } else if (B->getRHS()->getType()->isPointerType()) {
75 return B->getRHS();
76 }
77 }
78 return nullptr;
79}
80
81/// \return A subexpression of @c Ex which represents the
82/// expression-of-interest.
83static const Expr *peelOffOuterExpr(const Expr *Ex, const ExplodedNode *N);
84
85/// Given that expression S represents a pointer that would be dereferenced,
86/// try to find a sub-expression from which the pointer came from.
87/// This is used for tracking down origins of a null or undefined value:
88/// "this is null because that is null because that is null" etc.
89/// We wipe away field and element offsets because they merely add offsets.
90/// We also wipe away all casts except lvalue-to-rvalue casts, because the
91/// latter represent an actual pointer dereference; however, we remove
92/// the final lvalue-to-rvalue cast before returning from this function
93/// because it demonstrates more clearly from where the pointer rvalue was
94/// loaded. Examples:
95/// x->y.z ==> x (lvalue)
96/// foo()->y.z ==> foo() (rvalue)
97const Expr *bugreporter::getDerefExpr(const Stmt *S) {
98 const auto *E = dyn_cast<Expr>(Val: S);
99 if (!E)
100 return nullptr;
101
102 while (true) {
103 if (const auto *CE = dyn_cast<CastExpr>(Val: E)) {
104 if (CE->getCastKind() == CK_LValueToRValue) {
105 // This cast represents the load we're looking for.
106 break;
107 }
108 E = CE->getSubExpr();
109 } else if (const auto *B = dyn_cast<BinaryOperator>(Val: E)) {
110 // Pointer arithmetic: '*(x + 2)' -> 'x') etc.
111 if (const Expr *Inner = peelOffPointerArithmetic(B)) {
112 E = Inner;
113 } else if (B->isAssignmentOp()) {
114 // Follow LHS of assignments: '*p = 404' -> 'p'.
115 E = B->getLHS();
116 } else {
117 // Probably more arithmetic can be pattern-matched here,
118 // but for now give up.
119 break;
120 }
121 } else if (const auto *U = dyn_cast<UnaryOperator>(Val: E)) {
122 if (U->getOpcode() == UO_Deref || U->getOpcode() == UO_AddrOf ||
123 (U->isIncrementDecrementOp() && U->getType()->isPointerType())) {
124 // Operators '*' and '&' don't actually mean anything.
125 // We look at casts instead.
126 E = U->getSubExpr();
127 } else {
128 // Probably more arithmetic can be pattern-matched here,
129 // but for now give up.
130 break;
131 }
132 }
133 // Pattern match for a few useful cases: a[0], p->f, *p etc.
134 else if (const auto *ME = dyn_cast<MemberExpr>(Val: E)) {
135 // This handles the case when the dereferencing of a member reference
136 // happens. This is needed, because the AST for dereferencing a
137 // member reference looks like the following:
138 // |-MemberExpr
139 // `-DeclRefExpr
140 // Without this special case the notes would refer to the whole object
141 // (struct, class or union variable) instead of just the relevant member.
142
143 if (ME->getMemberDecl()->getType()->isReferenceType())
144 break;
145 E = ME->getBase();
146 } else if (const auto *IvarRef = dyn_cast<ObjCIvarRefExpr>(Val: E)) {
147 E = IvarRef->getBase();
148 } else if (const auto *AE = dyn_cast<ArraySubscriptExpr>(Val: E)) {
149 E = AE->getBase();
150 } else if (const auto *PE = dyn_cast<ParenExpr>(Val: E)) {
151 E = PE->getSubExpr();
152 } else if (const auto *FE = dyn_cast<FullExpr>(Val: E)) {
153 E = FE->getSubExpr();
154 } else {
155 // Other arbitrary stuff.
156 break;
157 }
158 }
159
160 // Special case: remove the final lvalue-to-rvalue cast, but do not recurse
161 // deeper into the sub-expression. This way we return the lvalue from which
162 // our pointer rvalue was loaded.
163 if (const auto *CE = dyn_cast<ImplicitCastExpr>(Val: E))
164 if (CE->getCastKind() == CK_LValueToRValue)
165 E = CE->getSubExpr();
166
167 return E;
168}
169
170static const VarDecl *getVarDeclForExpression(const Expr *E) {
171 if (const auto *DR = dyn_cast<DeclRefExpr>(Val: E))
172 return dyn_cast<VarDecl>(Val: DR->getDecl());
173 return nullptr;
174}
175
176static const MemRegion *
177getLocationRegionIfReference(const Expr *E, const ExplodedNode *N,
178 bool LookingForReference = true) {
179 if (const auto *ME = dyn_cast<MemberExpr>(Val: E)) {
180 // This handles null references from FieldRegions, for example:
181 // struct Wrapper { int &ref; };
182 // Wrapper w = { *(int *)0 };
183 // w.ref = 1;
184 const Expr *Base = ME->getBase();
185 const VarDecl *VD = getVarDeclForExpression(E: Base);
186 if (!VD)
187 return nullptr;
188
189 const auto *FD = dyn_cast<FieldDecl>(Val: ME->getMemberDecl());
190 if (!FD)
191 return nullptr;
192
193 if (FD->getType()->isReferenceType()) {
194 SVal StructSVal = N->getState()->getLValue(VD, SF: N->getStackFrame());
195 return N->getState()->getLValue(decl: FD, Base: StructSVal).getAsRegion();
196 }
197 return nullptr;
198 }
199
200 const VarDecl *VD = getVarDeclForExpression(E);
201 if (!VD)
202 return nullptr;
203 if (LookingForReference && !VD->getType()->isReferenceType())
204 return nullptr;
205 return N->getState()->getLValue(VD, SF: N->getStackFrame()).getAsRegion();
206}
207
208/// Comparing internal representations of symbolic values (via
209/// SVal::operator==()) is a valid way to check if the value was updated,
210/// unless it's a LazyCompoundVal that may have a different internal
211/// representation every time it is loaded from the state. In this function we
212/// do an approximate comparison for lazy compound values, checking that they
213/// are the immediate snapshots of the tracked region's bindings within the
214/// node's respective states but not really checking that these snapshots
215/// actually contain the same set of bindings.
216static bool hasVisibleUpdate(const ExplodedNode *LeftNode, SVal LeftVal,
217 const ExplodedNode *RightNode, SVal RightVal) {
218 if (LeftVal == RightVal)
219 return true;
220
221 const auto LLCV = LeftVal.getAs<nonloc::LazyCompoundVal>();
222 if (!LLCV)
223 return false;
224
225 const auto RLCV = RightVal.getAs<nonloc::LazyCompoundVal>();
226 if (!RLCV)
227 return false;
228
229 return LLCV->getRegion() == RLCV->getRegion() &&
230 LLCV->getStore() == LeftNode->getState()->getStore() &&
231 RLCV->getStore() == RightNode->getState()->getStore();
232}
233
234static std::optional<SVal> getSValForVar(const Expr *CondVarExpr,
235 const ExplodedNode *N) {
236 ProgramStateRef State = N->getState();
237 const StackFrame *SF = N->getStackFrame();
238
239 assert(CondVarExpr);
240 CondVarExpr = CondVarExpr->IgnoreImpCasts();
241
242 // The declaration of the value may rely on a pointer so take its l-value.
243 // FIXME: As seen in VisitCommonDeclRefExpr, sometimes DeclRefExpr may
244 // evaluate to a FieldRegion when it refers to a declaration of a lambda
245 // capture variable. We most likely need to duplicate that logic here.
246 if (const auto *DRE = dyn_cast<DeclRefExpr>(Val: CondVarExpr))
247 if (const auto *VD = dyn_cast<VarDecl>(Val: DRE->getDecl()))
248 return State->getSVal(LV: State->getLValue(VD, SF));
249
250 if (const auto *ME = dyn_cast<MemberExpr>(Val: CondVarExpr))
251 if (const auto *FD = dyn_cast<FieldDecl>(Val: ME->getMemberDecl()))
252 if (auto FieldL = State->getSVal(E: ME, SF).getAs<Loc>())
253 return State->getRawSVal(LV: *FieldL, T: FD->getType());
254
255 return std::nullopt;
256}
257
258static std::optional<const llvm::APSInt *>
259getConcreteIntegerValue(const Expr *CondVarExpr, const ExplodedNode *N) {
260
261 if (std::optional<SVal> V = getSValForVar(CondVarExpr, N))
262 if (auto CI = V->getAs<nonloc::ConcreteInt>())
263 return CI->getValue().get();
264 return std::nullopt;
265}
266
267static bool isVarAnInterestingCondition(const Expr *CondVarExpr,
268 const ExplodedNode *N,
269 const PathSensitiveBugReport *B) {
270 // Even if this condition is marked as interesting, it isn't *that*
271 // interesting if it didn't happen in a nested stackframe, the user could just
272 // follow the arrows.
273 if (!B->getErrorNode()->getStackFrame()->isParentOf(SF: N->getStackFrame()))
274 return false;
275
276 if (std::optional<SVal> V = getSValForVar(CondVarExpr, N))
277 if (std::optional<bugreporter::TrackingKind> K =
278 B->getInterestingnessKind(V: *V))
279 return *K == bugreporter::TrackingKind::Condition;
280
281 return false;
282}
283
284static bool isInterestingExpr(const Expr *E, const ExplodedNode *N,
285 const PathSensitiveBugReport *B) {
286 if (std::optional<SVal> V = getSValForVar(CondVarExpr: E, N))
287 return B->getInterestingnessKind(V: *V).has_value();
288 return false;
289}
290
291/// \return name of the macro inside the location \p Loc.
292static StringRef getMacroName(SourceLocation Loc,
293 BugReporterContext &BRC) {
294 return Lexer::getImmediateMacroName(
295 Loc,
296 SM: BRC.getSourceManager(),
297 LangOpts: BRC.getASTContext().getLangOpts());
298}
299
300/// \return Whether given spelling location corresponds to an expansion
301/// of a function-like macro.
302static bool isFunctionMacroExpansion(SourceLocation Loc,
303 const SourceManager &SM) {
304 if (!Loc.isMacroID())
305 return false;
306 while (SM.isMacroArgExpansion(Loc))
307 Loc = SM.getImmediateExpansionRange(Loc).getBegin();
308 FileIDAndOffset TLInfo = SM.getDecomposedLoc(Loc);
309 SrcMgr::SLocEntry SE = SM.getSLocEntry(FID: TLInfo.first);
310 const SrcMgr::ExpansionInfo &EInfo = SE.getExpansion();
311 return EInfo.isFunctionMacroExpansion();
312}
313
314/// \return Whether \c RegionOfInterest was modified at \p N,
315/// where \p ValueAfter is \c RegionOfInterest's value at the end of the
316/// stack frame.
317static bool wasRegionOfInterestModifiedAt(const SubRegion *RegionOfInterest,
318 const ExplodedNode *N,
319 SVal ValueAfter) {
320 ProgramStateRef State = N->getState();
321 ProgramStateManager &Mgr = N->getState()->getStateManager();
322
323 if (!N->getLocationAs<PostStore>() && !N->getLocationAs<PostInitializer>() &&
324 !N->getLocationAs<PostStmt>())
325 return false;
326
327 // Writing into region of interest.
328 if (auto PS = N->getLocationAs<PostStmt>())
329 if (auto *BO = PS->getStmtAs<BinaryOperator>())
330 if (BO->isAssignmentOp() && RegionOfInterest->isSubRegionOf(
331 R: N->getSVal(E: BO->getLHS()).getAsRegion()))
332 return true;
333
334 // SVal after the state is possibly different.
335 SVal ValueAtN = N->getState()->getSVal(R: RegionOfInterest);
336 if (!Mgr.getSValBuilder()
337 .areEqual(state: State, lhs: ValueAtN, rhs: ValueAfter)
338 .isConstrainedTrue() &&
339 (!ValueAtN.isUndef() || !ValueAfter.isUndef()))
340 return true;
341
342 return false;
343}
344
345//===----------------------------------------------------------------------===//
346// Implementation of BugReporterVisitor.
347//===----------------------------------------------------------------------===//
348
349PathDiagnosticPieceRef
350BugReporterVisitor::getEndPath(const ExplodedNode *, BugReporterContext &,
351 PathSensitiveBugReport &) {
352 return nullptr;
353}
354
355void BugReporterVisitor::finalizeVisitor(const ExplodedNode *,
356 BugReporterContext &,
357 PathSensitiveBugReport &) {}
358
359PathDiagnosticPieceRef
360BugReporterVisitor::getDefaultEndPath(const BugReporterContext &BRC,
361 const ExplodedNode *EndPathNode,
362 const PathSensitiveBugReport &BR) {
363 PathDiagnosticLocation L = BR.getLocation();
364 const auto &Ranges = BR.getRanges();
365
366 // Only add the statement itself as a range if we didn't specify any
367 // special ranges for this report.
368 auto P = std::make_shared<PathDiagnosticEventPiece>(
369 args&: L, args: BR.getDescription(), args: Ranges.begin() == Ranges.end());
370 for (SourceRange Range : Ranges)
371 P->addRange(R: Range);
372
373 return P;
374}
375
376//===----------------------------------------------------------------------===//
377// Implementation of NoStateChangeFuncVisitor.
378//===----------------------------------------------------------------------===//
379
380bool NoStateChangeFuncVisitor::isModifiedInFrame(const ExplodedNode *N) {
381 const StackFrame *SF = N->getStackFrame();
382 if (!FramesModifyingCalculated.count(Ptr: SF))
383 findModifyingFrames(CallExitBeginN: N);
384 return FramesModifying.count(Ptr: SF);
385}
386
387void NoStateChangeFuncVisitor::markFrameAsModifying(const StackFrame *SF) {
388 while (!SF->inTopFrame()) {
389 auto p = FramesModifying.insert(Ptr: SF);
390 if (!p.second)
391 break; // Frame and all its parents already inserted.
392
393 SF = SF->getParent();
394 }
395}
396
397static const ExplodedNode *getMatchingCallExitEnd(const ExplodedNode *N) {
398 assert(N->getLocationAs<CallEnter>());
399 // The stackframe of the callee is only found in the nodes succeeding
400 // the CallEnter node. CallEnter's stack frame refers to the caller.
401 const StackFrame *OrigSF = N->getFirstSucc()->getStackFrame();
402
403 // Similarly, the nodes preceding CallExitEnd refer to the callee's stack
404 // frame.
405 auto IsMatchingCallExitEnd = [OrigSF](const ExplodedNode *N) {
406 return N->getLocationAs<CallExitEnd>() &&
407 OrigSF == N->getFirstPred()->getStackFrame();
408 };
409 while (N && !IsMatchingCallExitEnd(N)) {
410 assert(N->succ_size() <= 1 &&
411 "This function is to be used on the trimmed ExplodedGraph!");
412 N = N->getFirstSucc();
413 }
414 return N;
415}
416
417void NoStateChangeFuncVisitor::findModifyingFrames(
418 const ExplodedNode *const CallExitBeginN) {
419
420 assert(CallExitBeginN->getLocationAs<CallExitBegin>());
421
422 const StackFrame *const OriginalSF = CallExitBeginN->getStackFrame();
423
424 const ExplodedNode *CurrCallExitBeginN = CallExitBeginN;
425 const StackFrame *CurrentSF = OriginalSF;
426
427 for (const ExplodedNode *CurrN = CallExitBeginN; CurrN;
428 CurrN = CurrN->getFirstPred()) {
429 // Found a new inlined call.
430 if (CurrN->getLocationAs<CallExitBegin>()) {
431 CurrCallExitBeginN = CurrN;
432 CurrentSF = CurrN->getStackFrame();
433 FramesModifyingCalculated.insert(Ptr: CurrentSF);
434 // We won't see a change in between two identical exploded nodes: skip.
435 continue;
436 }
437
438 if (auto CE = CurrN->getLocationAs<CallEnter>()) {
439 if (const ExplodedNode *CallExitEndN = getMatchingCallExitEnd(N: CurrN))
440 if (wasModifiedInFunction(CallEnterN: CurrN, CallExitEndN))
441 markFrameAsModifying(SF: CurrentSF);
442
443 // We exited this inlined call, lets actualize the stack frame.
444 CurrentSF = CurrN->getStackFrame();
445
446 // Stop calculating at the current function, but always regard it as
447 // modifying, so we can avoid notes like this:
448 // void f(Foo &F) {
449 // F.field = 0; // note: 0 assigned to 'F.field'
450 // // note: returning without writing to 'F.field'
451 // }
452 if (CE->getCalleeStackFrame() == OriginalSF) {
453 markFrameAsModifying(SF: CurrentSF);
454 break;
455 }
456 }
457
458 if (wasModifiedBeforeCallExit(CurrN, CallExitBeginN: CurrCallExitBeginN))
459 markFrameAsModifying(SF: CurrentSF);
460 }
461}
462
463PathDiagnosticPieceRef NoStateChangeFuncVisitor::VisitNode(
464 const ExplodedNode *N, BugReporterContext &BR, PathSensitiveBugReport &R) {
465
466 const StackFrame *SF = N->getStackFrame();
467 ProgramStateRef State = N->getState();
468 auto CallExitLoc = N->getLocationAs<CallExitBegin>();
469
470 // No diagnostic if region was modified inside the frame.
471 if (!CallExitLoc || isModifiedInFrame(N))
472 return nullptr;
473
474 CallEventRef<> Call =
475 BR.getStateManager().getCallEventManager().getCaller(CalleeSF: SF, State);
476
477 // Optimistically suppress uninitialized value bugs that result
478 // from system headers having a chance to initialize the value
479 // but failing to do so. It's too unlikely a system header's fault.
480 // It's much more likely a situation in which the function has a failure
481 // mode that the user decided not to check. If we want to hunt such
482 // omitted checks, we should provide an explicit function-specific note
483 // describing the precondition under which the function isn't supposed to
484 // initialize its out-parameter, and additionally check that such
485 // precondition can actually be fulfilled on the current path.
486 if (Call->isInSystemHeader()) {
487 // We make an exception for system header functions that have no branches.
488 // Such functions unconditionally fail to initialize the variable.
489 // If they call other functions that have more paths within them,
490 // this suppression would still apply when we visit these inner functions.
491 // One common example of a standard function that doesn't ever initialize
492 // its out parameter is operator placement new; it's up to the follow-up
493 // constructor (if any) to initialize the memory.
494 if (!N->getStackFrame()->getCFG()->isLinear()) {
495 static int i = 0;
496 R.markInvalid(Tag: &i, Data: nullptr);
497 }
498 return nullptr;
499 }
500
501 if (const auto *MC = dyn_cast<ObjCMethodCall>(Val&: Call)) {
502 // If we failed to construct a piece for self, we still want to check
503 // whether the entity of interest is in a parameter.
504 if (PathDiagnosticPieceRef Piece = maybeEmitNoteForObjCSelf(R, Call: *MC, N))
505 return Piece;
506 }
507
508 if (const auto *CCall = dyn_cast<CXXConstructorCall>(Val&: Call)) {
509 // Do not generate diagnostics for not modified parameters in
510 // constructors.
511 return maybeEmitNoteForCXXThis(R, Call: *CCall, N);
512 }
513
514 return maybeEmitNoteForParameters(R, Call: *Call, N);
515}
516
517/// \return Whether the method declaration \p Parent
518/// syntactically has a binary operation writing into the ivar \p Ivar.
519static bool potentiallyWritesIntoIvar(const Decl *Parent,
520 const ObjCIvarDecl *Ivar) {
521 using namespace ast_matchers;
522 const char *IvarBind = "Ivar";
523 if (!Parent)
524 return false;
525 Stmt *Body = Parent->getBody();
526 if (!Body)
527 return false;
528 StatementMatcher WriteIntoIvarM = binaryOperator(
529 hasOperatorName(Name: "="),
530 hasLHS(InnerMatcher: ignoringParenImpCasts(
531 InnerMatcher: objcIvarRefExpr(hasDeclaration(InnerMatcher: equalsNode(Other: Ivar))).bind(ID: IvarBind))));
532 StatementMatcher ParentM = stmt(hasDescendant(WriteIntoIvarM));
533 auto Matches = match(Matcher: ParentM, Node: *Body, Context&: Parent->getASTContext());
534 for (BoundNodes &Match : Matches) {
535 auto IvarRef = Match.getNodeAs<ObjCIvarRefExpr>(ID: IvarBind);
536 if (IvarRef->isFreeIvar())
537 return true;
538
539 const Expr *Base = IvarRef->getBase();
540 if (const auto *ICE = dyn_cast<ImplicitCastExpr>(Val: Base))
541 Base = ICE->getSubExpr();
542
543 if (const auto *DRE = dyn_cast<DeclRefExpr>(Val: Base))
544 if (const auto *ID = dyn_cast<ImplicitParamDecl>(Val: DRE->getDecl()))
545 if (ID->getParameterKind() == ImplicitParamKind::ObjCSelf)
546 return true;
547
548 return false;
549 }
550 return false;
551}
552
553/// Attempts to find the region of interest in a given CXX decl,
554/// by either following the base classes or fields.
555/// Dereferences fields up to a given recursion limit.
556/// Note that \p Vec is passed by value, leading to quadratic copying cost,
557/// but it's OK in practice since its length is limited to DEREFERENCE_LIMIT.
558/// \return A chain fields leading to the region of interest or std::nullopt.
559const std::optional<NoStoreFuncVisitor::RegionVector>
560NoStoreFuncVisitor::findRegionOfInterestInRecord(
561 const RecordDecl *RD, ProgramStateRef State, const MemRegion *R,
562 const NoStoreFuncVisitor::RegionVector &Vec /* = {} */,
563 int depth /* = 0 */) {
564
565 if (depth == DEREFERENCE_LIMIT) // Limit the recursion depth.
566 return std::nullopt;
567
568 if (const auto *RDX = dyn_cast<CXXRecordDecl>(Val: RD))
569 if (!RDX->hasDefinition())
570 return std::nullopt;
571
572 // Recursively examine the base classes.
573 // Note that following base classes does not increase the recursion depth.
574 if (const auto *RDX = dyn_cast<CXXRecordDecl>(Val: RD))
575 for (const auto &II : RDX->bases())
576 if (const RecordDecl *RRD = II.getType()->getAsRecordDecl())
577 if (std::optional<RegionVector> Out =
578 findRegionOfInterestInRecord(RD: RRD, State, R, Vec, depth))
579 return Out;
580
581 for (const FieldDecl *I : RD->fields()) {
582 QualType FT = I->getType();
583 const FieldRegion *FR = MmrMgr.getFieldRegion(FD: I, SuperRegion: cast<SubRegion>(Val: R));
584 const SVal V = State->getSVal(R: FR);
585 const MemRegion *VR = V.getAsRegion();
586
587 RegionVector VecF = Vec;
588 VecF.push_back(Elt: FR);
589
590 if (RegionOfInterest == VR)
591 return VecF;
592
593 if (const RecordDecl *RRD = FT->getAsRecordDecl())
594 if (auto Out =
595 findRegionOfInterestInRecord(RD: RRD, State, R: FR, Vec: VecF, depth: depth + 1))
596 return Out;
597
598 QualType PT = FT->getPointeeType();
599 if (PT.isNull() || PT->isVoidType() || !VR)
600 continue;
601
602 if (const RecordDecl *RRD = PT->getAsRecordDecl())
603 if (std::optional<RegionVector> Out =
604 findRegionOfInterestInRecord(RD: RRD, State, R: VR, Vec: VecF, depth: depth + 1))
605 return Out;
606 }
607
608 return std::nullopt;
609}
610
611PathDiagnosticPieceRef
612NoStoreFuncVisitor::maybeEmitNoteForObjCSelf(PathSensitiveBugReport &R,
613 const ObjCMethodCall &Call,
614 const ExplodedNode *N) {
615 if (const auto *IvarR = dyn_cast<ObjCIvarRegion>(Val: RegionOfInterest)) {
616 const MemRegion *SelfRegion = Call.getReceiverSVal().getAsRegion();
617 if (RegionOfInterest->isSubRegionOf(R: SelfRegion) &&
618 potentiallyWritesIntoIvar(Parent: Call.getRuntimeDefinition().getDecl(),
619 Ivar: IvarR->getDecl()))
620 return maybeEmitNote(R, Call, N, FieldChain: {}, MatchedRegion: SelfRegion, FirstElement: "self",
621 /*FirstIsReferenceType=*/false, IndirectionLevel: 1);
622 }
623 return nullptr;
624}
625
626PathDiagnosticPieceRef
627NoStoreFuncVisitor::maybeEmitNoteForCXXThis(PathSensitiveBugReport &R,
628 const CXXConstructorCall &Call,
629 const ExplodedNode *N) {
630 const MemRegion *ThisR = Call.getCXXThisVal().getAsRegion();
631 if (RegionOfInterest->isSubRegionOf(R: ThisR) && !Call.getDecl()->isImplicit())
632 return maybeEmitNote(R, Call, N, FieldChain: {}, MatchedRegion: ThisR, FirstElement: "this",
633 /*FirstIsReferenceType=*/false, IndirectionLevel: 1);
634
635 // Do not generate diagnostics for not modified parameters in
636 // constructors.
637 return nullptr;
638}
639
640/// \return whether \p Ty points to a const type, or is a const reference.
641static bool isPointerToConst(QualType Ty) {
642 return !Ty->getPointeeType().isNull() &&
643 Ty->getPointeeType().getCanonicalType().isConstQualified();
644}
645
646PathDiagnosticPieceRef NoStoreFuncVisitor::maybeEmitNoteForParameters(
647 PathSensitiveBugReport &R, const CallEvent &Call, const ExplodedNode *N) {
648 ArrayRef<ParmVarDecl *> Parameters = Call.parameters();
649 for (unsigned I = 0; I < Call.getNumArgs() && I < Parameters.size(); ++I) {
650 const ParmVarDecl *PVD = Parameters[I];
651 SVal V = Call.getArgSVal(Index: I);
652 bool ParamIsReferenceType = PVD->getType()->isReferenceType();
653 std::string ParamName = PVD->getNameAsString();
654
655 unsigned IndirectionLevel = 1;
656 QualType T = PVD->getType();
657 while (const MemRegion *MR = V.getAsRegion()) {
658 if (RegionOfInterest->isSubRegionOf(R: MR) && !isPointerToConst(Ty: T))
659 return maybeEmitNote(R, Call, N, FieldChain: {}, MatchedRegion: MR, FirstElement: ParamName,
660 FirstIsReferenceType: ParamIsReferenceType, IndirectionLevel);
661
662 QualType PT = T->getPointeeType();
663 if (PT.isNull() || PT->isVoidType())
664 break;
665
666 ProgramStateRef State = N->getState();
667
668 if (const RecordDecl *RD = PT->getAsRecordDecl())
669 if (std::optional<RegionVector> P =
670 findRegionOfInterestInRecord(RD, State, R: MR))
671 return maybeEmitNote(R, Call, N, FieldChain: *P, MatchedRegion: RegionOfInterest, FirstElement: ParamName,
672 FirstIsReferenceType: ParamIsReferenceType, IndirectionLevel);
673
674 V = State->getSVal(R: MR, T: PT);
675 T = PT;
676 IndirectionLevel++;
677 }
678 }
679
680 return nullptr;
681}
682
683bool NoStoreFuncVisitor::wasModifiedBeforeCallExit(
684 const ExplodedNode *CurrN, const ExplodedNode *CallExitBeginN) {
685 return ::wasRegionOfInterestModifiedAt(
686 RegionOfInterest, N: CurrN,
687 ValueAfter: CallExitBeginN->getState()->getSVal(R: RegionOfInterest));
688}
689
690static llvm::StringLiteral WillBeUsedForACondition =
691 ", which participates in a condition later";
692
693PathDiagnosticPieceRef NoStoreFuncVisitor::maybeEmitNote(
694 PathSensitiveBugReport &R, const CallEvent &Call, const ExplodedNode *N,
695 const RegionVector &FieldChain, const MemRegion *MatchedRegion,
696 StringRef FirstElement, bool FirstIsReferenceType,
697 unsigned IndirectionLevel) {
698
699 PathDiagnosticLocation L =
700 PathDiagnosticLocation::create(P: N->getLocation(), SMng: SM);
701
702 // For now this shouldn't trigger, but once it does (as we add more
703 // functions to the body farm), we'll need to decide if these reports
704 // are worth suppressing as well.
705 if (!L.hasValidLocation())
706 return nullptr;
707
708 SmallString<256> sbuf;
709 llvm::raw_svector_ostream os(sbuf);
710 os << "Returning without writing to '";
711
712 // Do not generate the note if failed to pretty-print.
713 if (!prettyPrintRegionName(FieldChain, MatchedRegion, FirstElement,
714 FirstIsReferenceType, IndirectionLevel, os))
715 return nullptr;
716
717 os << "'";
718 if (TKind == bugreporter::TrackingKind::Condition)
719 os << WillBeUsedForACondition;
720 return std::make_shared<PathDiagnosticEventPiece>(args&: L, args: os.str());
721}
722
723bool NoStoreFuncVisitor::prettyPrintRegionName(const RegionVector &FieldChain,
724 const MemRegion *MatchedRegion,
725 StringRef FirstElement,
726 bool FirstIsReferenceType,
727 unsigned IndirectionLevel,
728 llvm::raw_svector_ostream &os) {
729
730 if (FirstIsReferenceType)
731 IndirectionLevel--;
732
733 RegionVector RegionSequence;
734
735 // Add the regions in the reverse order, then reverse the resulting array.
736 assert(RegionOfInterest->isSubRegionOf(MatchedRegion));
737 const MemRegion *R = RegionOfInterest;
738 while (R != MatchedRegion) {
739 RegionSequence.push_back(Elt: R);
740 R = cast<SubRegion>(Val: R)->getSuperRegion();
741 }
742 std::reverse(first: RegionSequence.begin(), last: RegionSequence.end());
743 RegionSequence.append(in_start: FieldChain.begin(), in_end: FieldChain.end());
744
745 StringRef Sep;
746 for (const MemRegion *R : RegionSequence) {
747
748 // Just keep going up to the base region.
749 // Element regions may appear due to casts.
750 if (isa<CXXBaseObjectRegion, CXXTempObjectRegion>(Val: R))
751 continue;
752
753 if (Sep.empty())
754 Sep = prettyPrintFirstElement(FirstElement,
755 /*MoreItemsExpected=*/true,
756 IndirectionLevel, os);
757
758 os << Sep;
759
760 // Can only reasonably pretty-print DeclRegions.
761 if (!isa<DeclRegion>(Val: R))
762 return false;
763
764 const auto *DR = cast<DeclRegion>(Val: R);
765 Sep = DR->getValueType()->isAnyPointerType() ? "->" : ".";
766 DR->getDecl()->getDeclName().print(OS&: os, Policy: PP);
767 }
768
769 if (Sep.empty())
770 prettyPrintFirstElement(FirstElement,
771 /*MoreItemsExpected=*/false, IndirectionLevel, os);
772 return true;
773}
774
775StringRef NoStoreFuncVisitor::prettyPrintFirstElement(
776 StringRef FirstElement, bool MoreItemsExpected, int IndirectionLevel,
777 llvm::raw_svector_ostream &os) {
778 StringRef Out = ".";
779
780 if (IndirectionLevel > 0 && MoreItemsExpected) {
781 IndirectionLevel--;
782 Out = "->";
783 }
784
785 if (IndirectionLevel > 0 && MoreItemsExpected)
786 os << "(";
787
788 for (int i = 0; i < IndirectionLevel; i++)
789 os << "*";
790 os << FirstElement;
791
792 if (IndirectionLevel > 0 && MoreItemsExpected)
793 os << ")";
794
795 return Out;
796}
797
798//===----------------------------------------------------------------------===//
799// Implementation of MacroNullReturnSuppressionVisitor.
800//===----------------------------------------------------------------------===//
801
802namespace {
803
804/// Suppress null-pointer-dereference bugs where dereferenced null was returned
805/// the macro.
806class MacroNullReturnSuppressionVisitor final : public BugReporterVisitor {
807 const SubRegion *RegionOfInterest;
808 const SVal ValueAtDereference;
809
810 // Do not invalidate the reports where the value was modified
811 // after it got assigned to from the macro.
812 bool WasModified = false;
813
814public:
815 MacroNullReturnSuppressionVisitor(const SubRegion *R, const SVal V)
816 : RegionOfInterest(R), ValueAtDereference(V) {}
817
818 PathDiagnosticPieceRef VisitNode(const ExplodedNode *N,
819 BugReporterContext &BRC,
820 PathSensitiveBugReport &BR) override {
821 if (WasModified)
822 return nullptr;
823
824 auto BugPoint = BR.getErrorNode()->getLocation().getAs<StmtPoint>();
825 if (!BugPoint)
826 return nullptr;
827
828 const SourceManager &SMgr = BRC.getSourceManager();
829 if (auto Loc = matchAssignment(N)) {
830 if (isFunctionMacroExpansion(Loc: *Loc, SM: SMgr)) {
831 std::string MacroName = std::string(getMacroName(Loc: *Loc, BRC));
832 SourceLocation BugLoc = BugPoint->getStmt()->getBeginLoc();
833 if (!BugLoc.isMacroID() || getMacroName(Loc: BugLoc, BRC) != MacroName)
834 BR.markInvalid(Tag: getTag(), Data: MacroName.c_str());
835 }
836 }
837
838 if (wasRegionOfInterestModifiedAt(RegionOfInterest, N, ValueAfter: ValueAtDereference))
839 WasModified = true;
840
841 return nullptr;
842 }
843
844 static void addMacroVisitorIfNecessary(
845 const ExplodedNode *N, const MemRegion *R,
846 bool EnableNullFPSuppression, PathSensitiveBugReport &BR,
847 const SVal V) {
848 AnalyzerOptions &Options = N->getState()->getAnalysisManager().options;
849 if (EnableNullFPSuppression && Options.ShouldSuppressNullReturnPaths &&
850 isa<Loc>(Val: V))
851 BR.addVisitor<MacroNullReturnSuppressionVisitor>(ConstructorArgs: R->getAs<SubRegion>(),
852 ConstructorArgs: V);
853 }
854
855 void* getTag() const {
856 static int Tag = 0;
857 return static_cast<void *>(&Tag);
858 }
859
860 void Profile(llvm::FoldingSetNodeID &ID) const override {
861 ID.AddPointer(Ptr: getTag());
862 }
863
864private:
865 /// \return Source location of right hand side of an assignment
866 /// into \c RegionOfInterest, empty optional if none found.
867 std::optional<SourceLocation> matchAssignment(const ExplodedNode *N) {
868 const Stmt *S = N->getStmtForDiagnostics();
869 ProgramStateRef State = N->getState();
870 if (!S)
871 return std::nullopt;
872
873 if (const auto *DS = dyn_cast<DeclStmt>(Val: S)) {
874 if (const auto *VD = dyn_cast<VarDecl>(Val: DS->getSingleDecl()))
875 if (const Expr *RHS = VD->getInit())
876 if (RegionOfInterest->isSubRegionOf(
877 R: State->getLValue(VD, SF: N->getStackFrame()).getAsRegion()))
878 return RHS->getBeginLoc();
879 } else if (const auto *BO = dyn_cast<BinaryOperator>(Val: S)) {
880 const MemRegion *R = N->getSVal(E: BO->getLHS()).getAsRegion();
881 const Expr *RHS = BO->getRHS();
882 if (BO->isAssignmentOp() && RegionOfInterest->isSubRegionOf(R)) {
883 return RHS->getBeginLoc();
884 }
885 }
886 return std::nullopt;
887 }
888};
889
890} // end of anonymous namespace
891
892namespace {
893
894/// Emits an extra note at the return statement of an interesting stack frame.
895///
896/// The returned value is marked as an interesting value, and if it's null,
897/// adds a visitor to track where it became null.
898///
899/// This visitor is intended to be used when another visitor discovers that an
900/// interesting value comes from an inlined function call.
901class ReturnVisitor : public TrackingBugReporterVisitor {
902 const StackFrame *CalleeSF;
903 enum {
904 Initial,
905 MaybeUnsuppress,
906 Satisfied
907 } Mode = Initial;
908
909 bool EnableNullFPSuppression;
910 bool ShouldInvalidate = true;
911 AnalyzerOptions& Options;
912 bugreporter::TrackingKind TKind;
913
914public:
915 ReturnVisitor(TrackerRef ParentTracker, const StackFrame *Frame,
916 bool Suppressed, AnalyzerOptions &Options,
917 bugreporter::TrackingKind TKind)
918 : TrackingBugReporterVisitor(ParentTracker), CalleeSF(Frame),
919 EnableNullFPSuppression(Suppressed), Options(Options), TKind(TKind) {}
920
921 static void *getTag() {
922 static int Tag = 0;
923 return static_cast<void *>(&Tag);
924 }
925
926 void Profile(llvm::FoldingSetNodeID &ID) const override {
927 ID.AddPointer(Ptr: ReturnVisitor::getTag());
928 ID.AddPointer(Ptr: CalleeSF);
929 ID.AddBoolean(B: EnableNullFPSuppression);
930 }
931
932 PathDiagnosticPieceRef visitNodeInitial(const ExplodedNode *N,
933 BugReporterContext &BRC,
934 PathSensitiveBugReport &BR) {
935 // Only print a message at the interesting return statement.
936 if (N->getStackFrame() != CalleeSF)
937 return nullptr;
938
939 std::optional<StmtPoint> SP = N->getLocationAs<StmtPoint>();
940 if (!SP)
941 return nullptr;
942
943 const auto *Ret = dyn_cast<ReturnStmt>(Val: SP->getStmt());
944 if (!Ret)
945 return nullptr;
946
947 // Okay, we're at the right return statement, but do we have the return
948 // value available?
949 ProgramStateRef State = N->getState();
950 const Expr *RV = Ret->getRetValue();
951 if (!RV)
952 return nullptr;
953 SVal V = State->getSVal(E: RV, SF: CalleeSF);
954 if (V.isUnknownOrUndef())
955 return nullptr;
956
957 // Don't print any more notes after this one.
958 Mode = Satisfied;
959
960 const Expr *RetE = Ret->getRetValue();
961 assert(RetE && "Tracking a return value for a void function");
962
963 // Handle cases where a reference is returned and then immediately used.
964 std::optional<Loc> LValue;
965 if (RetE->isGLValue()) {
966 if ((LValue = V.getAs<Loc>())) {
967 SVal RValue = State->getRawSVal(LV: *LValue, T: RetE->getType());
968 if (isa<DefinedSVal>(Val: RValue))
969 V = RValue;
970 }
971 }
972
973 // Ignore aggregate rvalues.
974 if (isa<nonloc::LazyCompoundVal, nonloc::CompoundVal>(Val: V))
975 return nullptr;
976
977 RetE = RetE->IgnoreParenCasts();
978
979 // Let's track the return value.
980 getParentTracker().track(E: RetE, N, Opts: {.Kind: TKind, .EnableNullFPSuppression: EnableNullFPSuppression});
981
982 // Build an appropriate message based on the return value.
983 SmallString<64> Msg;
984 llvm::raw_svector_ostream Out(Msg);
985
986 bool WouldEventBeMeaningless = false;
987
988 if (State->isNull(V).isConstrainedTrue()) {
989 if (isa<Loc>(Val: V)) {
990
991 // If we have counter-suppression enabled, make sure we keep visiting
992 // future nodes. We want to emit a path note as well, in case
993 // the report is resurrected as valid later on.
994 if (EnableNullFPSuppression &&
995 Options.ShouldAvoidSuppressingNullArgumentPaths)
996 Mode = MaybeUnsuppress;
997
998 if (RetE->getType()->isObjCObjectPointerType()) {
999 Out << "Returning nil";
1000 } else {
1001 Out << "Returning null pointer";
1002 }
1003 } else {
1004 Out << "Returning zero";
1005 }
1006
1007 } else {
1008 if (auto CI = V.getAs<nonloc::ConcreteInt>()) {
1009 Out << "Returning the value " << CI->getValue();
1010 } else {
1011 // There is nothing interesting about returning a value, when it is
1012 // plain value without any constraints, and the function is guaranteed
1013 // to return that every time. We could use CFG::isLinear() here, but
1014 // constexpr branches are obvious to the compiler, not necesserily to
1015 // the programmer.
1016 if (N->getCFG().size() == 3)
1017 WouldEventBeMeaningless = true;
1018
1019 Out << (isa<Loc>(Val: V) ? "Returning pointer" : "Returning value");
1020 }
1021 }
1022
1023 if (LValue) {
1024 if (const MemRegion *MR = LValue->getAsRegion()) {
1025 if (MR->canPrintPretty()) {
1026 Out << " (reference to ";
1027 MR->printPretty(os&: Out);
1028 Out << ")";
1029 }
1030 }
1031 } else {
1032 // FIXME: We should have a more generalized location printing mechanism.
1033 if (const auto *DR = dyn_cast<DeclRefExpr>(Val: RetE))
1034 if (const auto *DD = dyn_cast<DeclaratorDecl>(Val: DR->getDecl()))
1035 Out << " (loaded from '" << *DD << "')";
1036 }
1037
1038 PathDiagnosticLocation L(Ret, BRC.getSourceManager(), CalleeSF);
1039 if (!L.isValid() || !L.asLocation().isValid())
1040 return nullptr;
1041
1042 if (TKind == bugreporter::TrackingKind::Condition)
1043 Out << WillBeUsedForACondition;
1044
1045 auto EventPiece = std::make_shared<PathDiagnosticEventPiece>(args&: L, args: Out.str());
1046
1047 // If we determined that the note is meaningless, make it prunable, and
1048 // don't mark the stackframe interesting.
1049 if (WouldEventBeMeaningless)
1050 EventPiece->setPrunable(isPrunable: true);
1051 else
1052 BR.markInteresting(SF: CalleeSF);
1053
1054 return EventPiece;
1055 }
1056
1057 PathDiagnosticPieceRef visitNodeMaybeUnsuppress(const ExplodedNode *N,
1058 BugReporterContext &BRC,
1059 PathSensitiveBugReport &BR) {
1060 assert(Options.ShouldAvoidSuppressingNullArgumentPaths);
1061
1062 // Are we at the entry node for this call?
1063 std::optional<CallEnter> CE = N->getLocationAs<CallEnter>();
1064 if (!CE)
1065 return nullptr;
1066
1067 if (CE->getCalleeStackFrame() != CalleeSF)
1068 return nullptr;
1069
1070 Mode = Satisfied;
1071
1072 // Don't automatically suppress a report if one of the arguments is
1073 // known to be a null pointer. Instead, start tracking /that/ null
1074 // value back to its origin.
1075 ProgramStateManager &StateMgr = BRC.getStateManager();
1076 CallEventManager &CallMgr = StateMgr.getCallEventManager();
1077
1078 ProgramStateRef State = N->getState();
1079 CallEventRef<> Call = CallMgr.getCaller(CalleeSF, State);
1080 for (unsigned I = 0, E = Call->getNumArgs(); I != E; ++I) {
1081 std::optional<Loc> ArgV = Call->getArgSVal(Index: I).getAs<Loc>();
1082 if (!ArgV)
1083 continue;
1084
1085 const Expr *ArgE = Call->getArgExpr(Index: I);
1086 if (!ArgE)
1087 continue;
1088
1089 // Is it possible for this argument to be non-null?
1090 if (!State->isNull(V: *ArgV).isConstrainedTrue())
1091 continue;
1092
1093 if (getParentTracker()
1094 .track(E: ArgE, N, Opts: {.Kind: TKind, .EnableNullFPSuppression: EnableNullFPSuppression})
1095 .FoundSomethingToTrack)
1096 ShouldInvalidate = false;
1097
1098 // If we /can't/ track the null pointer, we should err on the side of
1099 // false negatives, and continue towards marking this report invalid.
1100 // (We will still look at the other arguments, though.)
1101 }
1102
1103 return nullptr;
1104 }
1105
1106 PathDiagnosticPieceRef VisitNode(const ExplodedNode *N,
1107 BugReporterContext &BRC,
1108 PathSensitiveBugReport &BR) override {
1109 switch (Mode) {
1110 case Initial:
1111 return visitNodeInitial(N, BRC, BR);
1112 case MaybeUnsuppress:
1113 return visitNodeMaybeUnsuppress(N, BRC, BR);
1114 case Satisfied:
1115 return nullptr;
1116 }
1117
1118 llvm_unreachable("Invalid visit mode!");
1119 }
1120
1121 void finalizeVisitor(const ExplodedNode *, BugReporterContext &,
1122 PathSensitiveBugReport &BR) override {
1123 if (EnableNullFPSuppression && ShouldInvalidate)
1124 BR.markInvalid(Tag: ReturnVisitor::getTag(), Data: CalleeSF);
1125 }
1126};
1127
1128//===----------------------------------------------------------------------===//
1129// StoreSiteFinder
1130//===----------------------------------------------------------------------===//
1131
1132/// Finds last store into the given region,
1133/// which is different from a given symbolic value.
1134class StoreSiteFinder final : public TrackingBugReporterVisitor {
1135 const MemRegion *R;
1136 SVal V;
1137 bool Satisfied = false;
1138
1139 TrackingOptions Options;
1140 const StackFrame *OriginSF;
1141
1142public:
1143 /// \param V We're searching for the store where \c R received this value.
1144 /// \param R The region we're tracking.
1145 /// \param Options Tracking behavior options.
1146 /// \param OriginSF Only adds notes when the last store happened in a
1147 /// different stackframe to this one. Disregarded if the tracking kind
1148 /// is thorough.
1149 /// This is useful, because for non-tracked regions, notes about
1150 /// changes to its value in a nested stackframe could be pruned, and
1151 /// this visitor can prevent that without polluting the bugpath too
1152 /// much.
1153 StoreSiteFinder(bugreporter::TrackerRef ParentTracker, SVal V,
1154 const MemRegion *R, TrackingOptions Options,
1155 const StackFrame *OriginSF = nullptr)
1156 : TrackingBugReporterVisitor(ParentTracker), R(R), V(V), Options(Options),
1157 OriginSF(OriginSF) {
1158 assert(R);
1159 }
1160
1161 void Profile(llvm::FoldingSetNodeID &ID) const override;
1162
1163 PathDiagnosticPieceRef VisitNode(const ExplodedNode *N,
1164 BugReporterContext &BRC,
1165 PathSensitiveBugReport &BR) override;
1166};
1167} // namespace
1168
1169void StoreSiteFinder::Profile(llvm::FoldingSetNodeID &ID) const {
1170 static int tag = 0;
1171 ID.AddPointer(Ptr: &tag);
1172 ID.AddPointer(Ptr: R);
1173 ID.Add(x: V);
1174 ID.AddInteger(I: static_cast<int>(Options.Kind));
1175 ID.AddBoolean(B: Options.EnableNullFPSuppression);
1176}
1177
1178/// Returns true if \p N represents the DeclStmt declaring and initializing
1179/// \p VR.
1180static bool isInitializationOfVar(const ExplodedNode *N, const VarRegion *VR) {
1181 std::optional<PostStmt> P = N->getLocationAs<PostStmt>();
1182 if (!P)
1183 return false;
1184
1185 const DeclStmt *DS = P->getStmtAs<DeclStmt>();
1186 if (!DS)
1187 return false;
1188
1189 if (DS->getSingleDecl() != VR->getDecl())
1190 return false;
1191
1192 const auto *FrameSpace =
1193 VR->getMemorySpaceAs<StackSpaceRegion>(State: N->getState());
1194
1195 if (!FrameSpace) {
1196 // If we ever directly evaluate global DeclStmts, this assertion will be
1197 // invalid, but this still seems preferable to silently accepting an
1198 // initialization that may be for a path-sensitive variable.
1199 [[maybe_unused]] bool IsLocalStaticOrLocalExtern =
1200 VR->getDecl()->isStaticLocal() || VR->getDecl()->isLocalExternDecl();
1201 assert(IsLocalStaticOrLocalExtern &&
1202 "Declared a variable on the stack without Stack memspace?");
1203 return true;
1204 }
1205
1206 assert(VR->getDecl()->hasLocalStorage());
1207 return FrameSpace->getStackFrame() == N->getStackFrame();
1208}
1209
1210static bool isObjCPointer(const MemRegion *R) {
1211 if (R->isBoundable())
1212 if (const auto *TR = dyn_cast<TypedValueRegion>(Val: R))
1213 return TR->getValueType()->isObjCObjectPointerType();
1214
1215 return false;
1216}
1217
1218static bool isObjCPointer(const ValueDecl *D) {
1219 return D->getType()->isObjCObjectPointerType();
1220}
1221
1222namespace {
1223using DestTypeValue = std::pair<const StoreInfo &, loc::ConcreteInt>;
1224
1225llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const DestTypeValue &Val) {
1226 if (auto *TyR = Val.first.Dest->getAs<TypedRegion>()) {
1227 QualType LocTy = TyR->getLocationType();
1228 if (!LocTy.isNull()) {
1229 if (auto *PtrTy = LocTy->getAs<PointerType>()) {
1230 std::string PStr = PtrTy->getPointeeType().getAsString();
1231 if (!PStr.empty())
1232 OS << "(" << PStr << ")";
1233 }
1234 }
1235 }
1236 SmallString<16> ValStr;
1237 Val.second.getValue()->toString(Str&: ValStr, Radix: 10, Signed: true);
1238 OS << ValStr;
1239 return OS;
1240}
1241} // namespace
1242
1243/// Show diagnostics for initializing or declaring a region \p R with a bad value.
1244static void showBRDiagnostics(llvm::raw_svector_ostream &OS, StoreInfo SI) {
1245 const bool HasPrefix = SI.Dest->canPrintPretty();
1246
1247 if (HasPrefix) {
1248 SI.Dest->printPretty(os&: OS);
1249 OS << " ";
1250 }
1251
1252 const char *Action = nullptr;
1253
1254 switch (SI.StoreKind) {
1255 case StoreInfo::Initialization:
1256 Action = HasPrefix ? "initialized to " : "Initializing to ";
1257 break;
1258 case StoreInfo::BlockCapture:
1259 Action = HasPrefix ? "captured by block as " : "Captured by block as ";
1260 break;
1261 default:
1262 llvm_unreachable("Unexpected store kind");
1263 }
1264
1265 if (auto CVal = SI.Value.getAs<loc::ConcreteInt>()) {
1266 if (!*CVal->getValue())
1267 OS << Action << (isObjCPointer(R: SI.Dest) ? "nil" : "a null pointer value");
1268 else
1269 OS << Action << DestTypeValue(SI, *CVal);
1270
1271 } else if (auto CVal = SI.Value.getAs<nonloc::ConcreteInt>()) {
1272 OS << Action << CVal->getValue();
1273
1274 } else if (SI.Origin && SI.Origin->canPrintPretty()) {
1275 OS << Action << "the value of ";
1276 SI.Origin->printPretty(os&: OS);
1277
1278 } else if (SI.StoreKind == StoreInfo::Initialization) {
1279 if (const auto *VR = dyn_cast<VarRegion>(Val: SI.Dest)) {
1280 const VarDecl *VD = VR->getDecl();
1281 if (!VD->getInit() && !VD->hasGlobalStorage()) {
1282 OS << (HasPrefix ? "declared" : "Declared")
1283 << " without an initial value";
1284 return;
1285 }
1286 }
1287 OS << (HasPrefix ? "initialized" : "Initialized") << " here";
1288 }
1289}
1290
1291/// Display diagnostics for passing bad region as a parameter.
1292static void showBRParamDiagnostics(llvm::raw_svector_ostream &OS,
1293 StoreInfo SI) {
1294 const auto *VR = cast<VarRegion>(Val: SI.Dest);
1295 const auto *D = VR->getDecl();
1296
1297 OS << "Passing ";
1298
1299 if (auto CI = SI.Value.getAs<loc::ConcreteInt>()) {
1300 if (!*CI->getValue())
1301 OS << (isObjCPointer(D) ? "nil object reference" : "null pointer value");
1302 else
1303 OS << (isObjCPointer(D) ? "object reference of value " : "pointer value ")
1304 << DestTypeValue(SI, *CI);
1305
1306 } else if (SI.Value.isUndef()) {
1307 OS << "uninitialized value";
1308
1309 } else if (auto CI = SI.Value.getAs<nonloc::ConcreteInt>()) {
1310 OS << "the value " << CI->getValue();
1311
1312 } else if (SI.Origin && SI.Origin->canPrintPretty()) {
1313 SI.Origin->printPretty(os&: OS);
1314
1315 } else {
1316 OS << "value";
1317 }
1318
1319 if (const auto *Param = dyn_cast<ParmVarDecl>(Val: VR->getDecl())) {
1320 // Printed parameter indexes are 1-based, not 0-based.
1321 unsigned Idx = Param->getFunctionScopeIndex() + 1;
1322 OS << " via " << Idx << llvm::getOrdinalSuffix(Val: Idx) << " parameter";
1323 if (VR->canPrintPretty()) {
1324 OS << " ";
1325 VR->printPretty(os&: OS);
1326 }
1327 } else if (const auto *ImplParam = dyn_cast<ImplicitParamDecl>(Val: D)) {
1328 if (ImplParam->getParameterKind() == ImplicitParamKind::ObjCSelf) {
1329 OS << " via implicit parameter 'self'";
1330 }
1331 }
1332}
1333
1334/// Show default diagnostics for storing bad region.
1335static void showBRDefaultDiagnostics(llvm::raw_svector_ostream &OS,
1336 StoreInfo SI) {
1337 const bool HasSuffix = SI.Dest->canPrintPretty();
1338
1339 if (auto CV = SI.Value.getAs<loc::ConcreteInt>()) {
1340 APSIntPtr V = CV->getValue();
1341 if (!*V)
1342 OS << (isObjCPointer(R: SI.Dest)
1343 ? "nil object reference stored"
1344 : (HasSuffix ? "Null pointer value stored"
1345 : "Storing null pointer value"));
1346 else {
1347 if (isObjCPointer(R: SI.Dest)) {
1348 OS << "object reference of value " << DestTypeValue(SI, *CV)
1349 << " stored";
1350 } else {
1351 if (HasSuffix)
1352 OS << "Pointer value of " << DestTypeValue(SI, *CV) << " stored";
1353 else
1354 OS << "Storing pointer value of " << DestTypeValue(SI, *CV);
1355 }
1356 }
1357 } else if (SI.Value.isUndef()) {
1358 OS << (HasSuffix ? "Uninitialized value stored"
1359 : "Storing uninitialized value");
1360
1361 } else if (auto CV = SI.Value.getAs<nonloc::ConcreteInt>()) {
1362 if (HasSuffix)
1363 OS << "The value " << CV->getValue() << " is assigned";
1364 else
1365 OS << "Assigning " << CV->getValue();
1366
1367 } else if (SI.Origin && SI.Origin->canPrintPretty()) {
1368 if (HasSuffix) {
1369 OS << "The value of ";
1370 SI.Origin->printPretty(os&: OS);
1371 OS << " is assigned";
1372 } else {
1373 OS << "Assigning the value of ";
1374 SI.Origin->printPretty(os&: OS);
1375 }
1376
1377 } else {
1378 OS << (HasSuffix ? "Value assigned" : "Assigning value");
1379 }
1380
1381 if (HasSuffix) {
1382 OS << " to ";
1383 SI.Dest->printPretty(os&: OS);
1384 }
1385}
1386
1387static bool isTrivialCopyOrMoveCtor(const CXXConstructExpr *CE) {
1388 if (!CE)
1389 return false;
1390
1391 const auto *CtorDecl = CE->getConstructor();
1392
1393 return CtorDecl->isCopyOrMoveConstructor() && CtorDecl->isTrivial();
1394}
1395
1396static const Expr *tryExtractInitializerFromList(const InitListExpr *ILE,
1397 const MemRegion *R) {
1398
1399 const auto *TVR = dyn_cast_or_null<TypedValueRegion>(Val: R);
1400
1401 if (!TVR)
1402 return nullptr;
1403
1404 const auto ITy = ILE->getType().getCanonicalType();
1405
1406 // Push each sub-region onto the stack.
1407 std::stack<const TypedValueRegion *> TVRStack;
1408 while (isa<FieldRegion>(Val: TVR) || isa<ElementRegion>(Val: TVR)) {
1409 // We found a region that matches the type of the init list,
1410 // so we assume this is the outer-most region. This can happen
1411 // if the initializer list is inside a class. If our assumption
1412 // is wrong, we return a nullptr in the end.
1413 if (ITy == TVR->getValueType().getCanonicalType())
1414 break;
1415
1416 TVRStack.push(x: TVR);
1417 TVR = cast<TypedValueRegion>(Val: TVR->getSuperRegion());
1418 }
1419
1420 // If the type of the outer most region doesn't match the type
1421 // of the ILE, we can't match the ILE and the region.
1422 if (ITy != TVR->getValueType().getCanonicalType())
1423 return nullptr;
1424
1425 const Expr *Init = ILE;
1426 while (!TVRStack.empty()) {
1427 TVR = TVRStack.top();
1428 TVRStack.pop();
1429
1430 // We hit something that's not an init list before
1431 // running out of regions, so we most likely failed.
1432 if (!isa<InitListExpr>(Val: Init))
1433 return nullptr;
1434
1435 ILE = cast<InitListExpr>(Val: Init);
1436 auto NumInits = ILE->getNumInits();
1437
1438 if (const auto *FR = dyn_cast<FieldRegion>(Val: TVR)) {
1439 const auto *FD = FR->getDecl();
1440
1441 if (FD->getFieldIndex() >= NumInits)
1442 return nullptr;
1443
1444 Init = ILE->getInit(Init: FD->getFieldIndex());
1445 } else if (const auto *ER = dyn_cast<ElementRegion>(Val: TVR)) {
1446 const auto Ind = ER->getIndex();
1447
1448 // If index is symbolic, we can't figure out which expression
1449 // belongs to the region.
1450 if (!Ind.isConstant())
1451 return nullptr;
1452
1453 const auto IndVal = Ind.getAsInteger()->getLimitedValue();
1454 if (IndVal >= NumInits)
1455 return nullptr;
1456
1457 Init = ILE->getInit(Init: IndVal);
1458 }
1459 }
1460
1461 return Init;
1462}
1463
1464PathDiagnosticPieceRef StoreSiteFinder::VisitNode(const ExplodedNode *Succ,
1465 BugReporterContext &BRC,
1466 PathSensitiveBugReport &BR) {
1467 if (Satisfied)
1468 return nullptr;
1469
1470 const ExplodedNode *StoreSite = nullptr;
1471 const ExplodedNode *Pred = Succ->getFirstPred();
1472 const Expr *InitE = nullptr;
1473 bool IsParam = false;
1474
1475 // First see if we reached the declaration of the region.
1476 if (const auto *VR = dyn_cast<VarRegion>(Val: R)) {
1477 if (isInitializationOfVar(N: Pred, VR)) {
1478 StoreSite = Pred;
1479 InitE = VR->getDecl()->getInit();
1480 }
1481 }
1482
1483 // If this is a post initializer expression, initializing the region, we
1484 // should track the initializer expression.
1485 if (std::optional<PostInitializer> PIP =
1486 Pred->getLocationAs<PostInitializer>()) {
1487 const MemRegion *FieldReg = (const MemRegion *)PIP->getLocationValue();
1488 if (FieldReg == R) {
1489 StoreSite = Pred;
1490 InitE = PIP->getInitializer()->getInit();
1491 }
1492 }
1493
1494 // Otherwise, see if this is the store site:
1495 // (1) Succ has this binding and Pred does not, i.e. this is
1496 // where the binding first occurred.
1497 // (2) Succ has this binding and is a PostStore node for this region, i.e.
1498 // the same binding was re-assigned here.
1499 if (!StoreSite) {
1500 if (Succ->getState()->getSVal(R) != V)
1501 return nullptr;
1502
1503 if (hasVisibleUpdate(LeftNode: Pred, LeftVal: Pred->getState()->getSVal(R), RightNode: Succ, RightVal: V)) {
1504 std::optional<PostStore> PS = Succ->getLocationAs<PostStore>();
1505 if (!PS || PS->getLocationValue() != R)
1506 return nullptr;
1507 }
1508
1509 StoreSite = Succ;
1510
1511 if (std::optional<PostStmt> P = Succ->getLocationAs<PostStmt>()) {
1512 // If this is an assignment expression, we can track the value
1513 // being assigned.
1514 if (const BinaryOperator *BO = P->getStmtAs<BinaryOperator>()) {
1515 if (BO->isAssignmentOp())
1516 InitE = BO->getRHS();
1517 }
1518 // If we have a declaration like 'S s{1,2}' that needs special
1519 // handling, we handle it here.
1520 else if (const auto *DS = P->getStmtAs<DeclStmt>()) {
1521 const auto *Decl = DS->getSingleDecl();
1522 if (isa<VarDecl>(Val: Decl)) {
1523 const auto *VD = cast<VarDecl>(Val: Decl);
1524
1525 // FIXME: Here we only track the inner most region, so we lose
1526 // information, but it's still better than a crash or no information
1527 // at all.
1528 //
1529 // E.g.: The region we have is 's.s2.s3.s4.y' and we only track 'y',
1530 // and throw away the rest.
1531 if (const auto *ILE = dyn_cast<InitListExpr>(Val: VD->getInit()))
1532 InitE = tryExtractInitializerFromList(ILE, R);
1533 }
1534 } else if (const auto *CE = P->getStmtAs<CXXConstructExpr>()) {
1535
1536 const auto State = Succ->getState();
1537
1538 if (isTrivialCopyOrMoveCtor(CE) && isa<SubRegion>(Val: R)) {
1539 // Migrate the field regions from the current object to
1540 // the parent object. If we track 'a.y.e' and encounter
1541 // 'S a = b' then we need to track 'b.y.e'.
1542
1543 // Push the regions to a stack, from last to first, so
1544 // considering the example above the stack will look like
1545 // (bottom) 'e' -> 'y' (top).
1546
1547 std::stack<const SubRegion *> SRStack;
1548 const SubRegion *SR = cast<SubRegion>(Val: R);
1549 while (isa<FieldRegion>(Val: SR) || isa<ElementRegion>(Val: SR)) {
1550 SRStack.push(x: SR);
1551 SR = cast<SubRegion>(Val: SR->getSuperRegion());
1552 }
1553
1554 // Get the region for the object we copied/moved from.
1555 const auto *OriginEx = CE->getArg(Arg: 0);
1556 const auto OriginVal =
1557 State->getSVal(E: OriginEx, SF: Succ->getStackFrame());
1558
1559 // Pop the stored field regions and apply them to the origin
1560 // object in the same order we had them on the copy.
1561 // OriginField will evolve like 'b' -> 'b.y' -> 'b.y.e'.
1562 SVal OriginField = OriginVal;
1563 while (!SRStack.empty()) {
1564 const auto *TopR = SRStack.top();
1565 SRStack.pop();
1566
1567 if (const auto *FR = dyn_cast<FieldRegion>(Val: TopR)) {
1568 OriginField = State->getLValue(decl: FR->getDecl(), Base: OriginField);
1569 } else if (const auto *ER = dyn_cast<ElementRegion>(Val: TopR)) {
1570 OriginField = State->getLValue(ElementType: ER->getElementType(),
1571 Idx: ER->getIndex(), Base: OriginField);
1572 } else {
1573 // FIXME: handle other region type
1574 }
1575 }
1576
1577 // Track 'b.y.e'.
1578 getParentTracker().track(V, R: OriginField.getAsRegion(), Opts: Options);
1579 InitE = OriginEx;
1580 }
1581 }
1582 // This branch can occur in cases like `Ctor() : field{ x, y } {}'.
1583 else if (const auto *ILE = P->getStmtAs<InitListExpr>()) {
1584 // FIXME: Here we only track the top level region, so we lose
1585 // information, but it's still better than a crash or no information
1586 // at all.
1587 //
1588 // E.g.: The region we have is 's.s2.s3.s4.y' and we only track 'y', and
1589 // throw away the rest.
1590 InitE = tryExtractInitializerFromList(ILE, R);
1591 }
1592 }
1593
1594 // If this is a call entry, the variable should be a parameter.
1595 // FIXME: Handle CXXThisRegion as well. (This is not a priority because
1596 // 'this' should never be NULL, but this visitor isn't just for NULL and
1597 // UndefinedVal.)
1598 if (std::optional<CallEnter> CE = Succ->getLocationAs<CallEnter>()) {
1599 if (const auto *VR = dyn_cast<VarRegion>(Val: R)) {
1600
1601 if (const auto *Param = dyn_cast<ParmVarDecl>(Val: VR->getDecl())) {
1602 ProgramStateManager &StateMgr = BRC.getStateManager();
1603 CallEventManager &CallMgr = StateMgr.getCallEventManager();
1604
1605 CallEventRef<> Call =
1606 CallMgr.getCaller(CalleeSF: CE->getCalleeStackFrame(), State: Succ->getState());
1607 InitE = Call->getArgExpr(Index: Param->getFunctionScopeIndex());
1608 } else {
1609 // Handle Objective-C 'self'.
1610 assert(isa<ImplicitParamDecl>(VR->getDecl()));
1611 InitE =
1612 cast<ObjCMessageExpr>(Val: CE->getCalleeStackFrame()->getCallSite())
1613 ->getInstanceReceiver()
1614 ->IgnoreParenCasts();
1615 }
1616 IsParam = true;
1617 }
1618 }
1619
1620 // If this is a CXXTempObjectRegion, the Expr responsible for its creation
1621 // is wrapped inside of it.
1622 if (const auto *TmpR = dyn_cast<CXXTempObjectRegion>(Val: R))
1623 InitE = TmpR->getExpr();
1624 }
1625
1626 if (!StoreSite)
1627 return nullptr;
1628
1629 Satisfied = true;
1630
1631 // If we have an expression that provided the value, try to track where it
1632 // came from.
1633 if (InitE) {
1634 if (!IsParam)
1635 InitE = InitE->IgnoreParenCasts();
1636
1637 getParentTracker().track(E: InitE, N: StoreSite, Opts: Options);
1638 }
1639
1640 // Let's try to find the region where the value came from.
1641 const MemRegion *OldRegion = nullptr;
1642
1643 // If we have init expression, it might be simply a reference
1644 // to a variable, so we can use it.
1645 if (InitE) {
1646 // That region might still be not exactly what we are looking for.
1647 // In situations like `int &ref = val;`, we can't say that
1648 // `ref` is initialized with `val`, rather refers to `val`.
1649 //
1650 // In order, to mitigate situations like this, we check if the last
1651 // stored value in that region is the value that we track.
1652 //
1653 // TODO: support other situations better.
1654 if (const MemRegion *Candidate =
1655 getLocationRegionIfReference(E: InitE, N: Succ, LookingForReference: false)) {
1656 const StoreManager &SM = BRC.getStateManager().getStoreManager();
1657
1658 // Here we traverse the graph up to find the last node where the
1659 // candidate region is still in the store.
1660 for (const ExplodedNode *N = StoreSite; N; N = N->getFirstPred()) {
1661 if (SM.includedInBindings(store: N->getState()->getStore(), region: Candidate)) {
1662 // And if it was bound to the target value, we can use it.
1663 if (N->getState()->getSVal(R: Candidate) == V) {
1664 OldRegion = Candidate;
1665 }
1666 break;
1667 }
1668 }
1669 }
1670 }
1671
1672 // Otherwise, if the current region does indeed contain the value
1673 // we are looking for, we can look for a region where this value
1674 // was before.
1675 //
1676 // It can be useful for situations like:
1677 // new = identity(old)
1678 // where the analyzer knows that 'identity' returns the value of its
1679 // first argument.
1680 //
1681 // NOTE: If the region R is not a simple var region, it can contain
1682 // V in one of its subregions.
1683 if (!OldRegion && StoreSite->getState()->getSVal(R) == V) {
1684 // Let's go up the graph to find the node where the region is
1685 // bound to V.
1686 const ExplodedNode *NodeWithoutBinding = StoreSite->getFirstPred();
1687 for (;
1688 NodeWithoutBinding && NodeWithoutBinding->getState()->getSVal(R) == V;
1689 NodeWithoutBinding = NodeWithoutBinding->getFirstPred()) {
1690 }
1691
1692 if (NodeWithoutBinding) {
1693 // Let's try to find a unique binding for the value in that node.
1694 // We want to use this to find unique bindings because of the following
1695 // situations:
1696 // b = a;
1697 // c = identity(b);
1698 //
1699 // Telling the user that the value of 'a' is assigned to 'c', while
1700 // correct, can be confusing.
1701 StoreManager::FindUniqueBinding FB(V.getAsLocSymbol());
1702 BRC.getStateManager().iterBindings(state: NodeWithoutBinding->getState(), F&: FB);
1703 if (FB)
1704 OldRegion = FB.getRegion();
1705 }
1706 }
1707
1708 if (Options.Kind == TrackingKind::Condition && OriginSF &&
1709 !OriginSF->isParentOf(SF: StoreSite->getStackFrame()))
1710 return nullptr;
1711
1712 // Okay, we've found the binding. Emit an appropriate message.
1713 SmallString<256> sbuf;
1714 llvm::raw_svector_ostream os(sbuf);
1715
1716 StoreInfo SI = {.StoreKind: StoreInfo::Assignment, // default kind
1717 .StoreSite: StoreSite,
1718 .SourceOfTheValue: InitE,
1719 .Value: V,
1720 .Dest: R,
1721 .Origin: OldRegion};
1722
1723 if (std::optional<PostStmt> PS = StoreSite->getLocationAs<PostStmt>()) {
1724 const Stmt *S = PS->getStmt();
1725 const auto *DS = dyn_cast<DeclStmt>(Val: S);
1726 const auto *VR = dyn_cast<VarRegion>(Val: R);
1727
1728 if (DS) {
1729 SI.StoreKind = StoreInfo::Initialization;
1730 } else if (const auto *BExpr = dyn_cast<BlockExpr>(Val: S)) {
1731 SI.StoreKind = StoreInfo::BlockCapture;
1732 if (VR) {
1733 // See if we can get the BlockVarRegion.
1734 ProgramStateRef State = StoreSite->getState();
1735 SVal V = StoreSite->getSVal(E: BExpr);
1736 if (const auto *BDR =
1737 dyn_cast_or_null<BlockDataRegion>(Val: V.getAsRegion())) {
1738 if (const VarRegion *OriginalR = BDR->getOriginalRegion(VR)) {
1739 getParentTracker().track(V: State->getSVal(R: OriginalR), R: OriginalR,
1740 Opts: Options, Origin: OriginSF);
1741 }
1742 }
1743 }
1744 }
1745 } else if (SI.StoreSite->getLocation().getAs<CallEnter>() &&
1746 isa<VarRegion>(Val: SI.Dest)) {
1747 SI.StoreKind = StoreInfo::CallArgument;
1748 }
1749
1750 return getParentTracker().handle(SI, BRC, Opts: Options);
1751}
1752
1753//===----------------------------------------------------------------------===//
1754// Implementation of TrackConstraintBRVisitor.
1755//===----------------------------------------------------------------------===//
1756
1757void TrackConstraintBRVisitor::Profile(llvm::FoldingSetNodeID &ID) const {
1758 static int tag = 0;
1759 ID.AddPointer(Ptr: &tag);
1760 ID.AddString(String: Message);
1761 ID.AddBoolean(B: Assumption);
1762 ID.Add(x: Constraint);
1763}
1764
1765/// Return the tag associated with this visitor. This tag will be used
1766/// to make all PathDiagnosticPieces created by this visitor.
1767const char *TrackConstraintBRVisitor::getTag() {
1768 return "TrackConstraintBRVisitor";
1769}
1770
1771bool TrackConstraintBRVisitor::isZeroCheck() const {
1772 return !Assumption && Constraint.getAs<Loc>();
1773}
1774
1775bool TrackConstraintBRVisitor::isUnderconstrained(const ExplodedNode *N) const {
1776 if (isZeroCheck())
1777 return N->getState()->isNull(V: Constraint).isUnderconstrained();
1778 return (bool)N->getState()->assume(Cond: Constraint, Assumption: !Assumption);
1779}
1780
1781PathDiagnosticPieceRef TrackConstraintBRVisitor::VisitNode(
1782 const ExplodedNode *N, BugReporterContext &BRC, PathSensitiveBugReport &) {
1783 const ExplodedNode *PrevN = N->getFirstPred();
1784 if (IsSatisfied)
1785 return nullptr;
1786
1787 // Start tracking after we see the first state in which the value is
1788 // constrained.
1789 if (!IsTrackingTurnedOn)
1790 if (!isUnderconstrained(N))
1791 IsTrackingTurnedOn = true;
1792 if (!IsTrackingTurnedOn)
1793 return nullptr;
1794
1795 // Check if in the previous state it was feasible for this constraint
1796 // to *not* be true.
1797 if (isUnderconstrained(N: PrevN)) {
1798 IsSatisfied = true;
1799
1800 // At this point, the negation of the constraint should be infeasible. If it
1801 // is feasible, make sure that the negation of the constrainti was
1802 // infeasible in the current state. If it is feasible, we somehow missed
1803 // the transition point.
1804 assert(!isUnderconstrained(N));
1805
1806 // Construct a new PathDiagnosticPiece.
1807 ProgramPoint P = N->getLocation();
1808
1809 // If this node already have a specialized note, it's probably better
1810 // than our generic note.
1811 // FIXME: This only looks for note tags, not for other ways to add a note.
1812 if (isa_and_nonnull<NoteTag>(Val: P.getTag()))
1813 return nullptr;
1814
1815 PathDiagnosticLocation L =
1816 PathDiagnosticLocation::create(P, SMng: BRC.getSourceManager());
1817 if (!L.isValid())
1818 return nullptr;
1819
1820 auto X = std::make_shared<PathDiagnosticEventPiece>(args&: L, args: Message);
1821 X->setTag(getTag());
1822 return std::move(X);
1823 }
1824
1825 return nullptr;
1826}
1827
1828//===----------------------------------------------------------------------===//
1829// Implementation of SuppressInlineDefensiveChecksVisitor.
1830//===----------------------------------------------------------------------===//
1831
1832SuppressInlineDefensiveChecksVisitor::
1833SuppressInlineDefensiveChecksVisitor(DefinedSVal Value, const ExplodedNode *N)
1834 : V(Value) {
1835 // Check if the visitor is disabled.
1836 AnalyzerOptions &Options = N->getState()->getAnalysisManager().options;
1837 if (!Options.ShouldSuppressInlinedDefensiveChecks)
1838 IsSatisfied = true;
1839}
1840
1841void SuppressInlineDefensiveChecksVisitor::Profile(
1842 llvm::FoldingSetNodeID &ID) const {
1843 static int id = 0;
1844 ID.AddPointer(Ptr: &id);
1845 ID.Add(x: V);
1846}
1847
1848const char *SuppressInlineDefensiveChecksVisitor::getTag() {
1849 return "IDCVisitor";
1850}
1851
1852PathDiagnosticPieceRef
1853SuppressInlineDefensiveChecksVisitor::VisitNode(const ExplodedNode *Succ,
1854 BugReporterContext &BRC,
1855 PathSensitiveBugReport &BR) {
1856 const ExplodedNode *Pred = Succ->getFirstPred();
1857 if (IsSatisfied)
1858 return nullptr;
1859
1860 // Start tracking after we see the first state in which the value is null.
1861 if (!IsTrackingTurnedOn)
1862 if (Succ->getState()->isNull(V).isConstrainedTrue())
1863 IsTrackingTurnedOn = true;
1864 if (!IsTrackingTurnedOn)
1865 return nullptr;
1866
1867 // Check if in the previous state it was feasible for this value
1868 // to *not* be null.
1869 if (!Pred->getState()->isNull(V).isConstrainedTrue() &&
1870 Succ->getState()->isNull(V).isConstrainedTrue()) {
1871 IsSatisfied = true;
1872
1873 // Check if this is inlined defensive checks.
1874 const StackFrame *CurSF = Succ->getStackFrame();
1875 const StackFrame *ReportSF = BR.getErrorNode()->getStackFrame();
1876 if (CurSF != ReportSF && !CurSF->isParentOf(SF: ReportSF)) {
1877 BR.markInvalid(Tag: "Suppress IDC", Data: CurSF);
1878 return nullptr;
1879 }
1880
1881 // Treat defensive checks in function-like macros as if they were an inlined
1882 // defensive check. If the bug location is not in a macro and the
1883 // terminator for the current location is in a macro then suppress the
1884 // warning.
1885 auto BugPoint = BR.getErrorNode()->getLocation().getAs<StmtPoint>();
1886
1887 if (!BugPoint)
1888 return nullptr;
1889
1890 ProgramPoint CurPoint = Succ->getLocation();
1891 const Stmt *CurTerminatorStmt = nullptr;
1892 if (auto BE = CurPoint.getAs<BlockEdge>()) {
1893 CurTerminatorStmt = BE->getSrc()->getTerminator().getStmt();
1894 } else if (auto SP = CurPoint.getAs<StmtPoint>()) {
1895 const Stmt *CurStmt = SP->getStmt();
1896 if (!CurStmt->getBeginLoc().isMacroID())
1897 return nullptr;
1898
1899 const CFGStmtMap *Map = CurSF->getAnalysisDeclContext()->getCFGStmtMap();
1900 CurTerminatorStmt = Map->getBlock(S: CurStmt)->getTerminatorStmt();
1901 } else {
1902 return nullptr;
1903 }
1904
1905 if (!CurTerminatorStmt)
1906 return nullptr;
1907
1908 SourceLocation TerminatorLoc = CurTerminatorStmt->getBeginLoc();
1909 if (TerminatorLoc.isMacroID()) {
1910 SourceLocation BugLoc = BugPoint->getStmt()->getBeginLoc();
1911
1912 // Suppress reports unless we are in that same macro.
1913 if (!BugLoc.isMacroID() ||
1914 getMacroName(Loc: BugLoc, BRC) != getMacroName(Loc: TerminatorLoc, BRC)) {
1915 BR.markInvalid(Tag: "Suppress Macro IDC", Data: CurSF);
1916 }
1917 return nullptr;
1918 }
1919 }
1920 return nullptr;
1921}
1922
1923//===----------------------------------------------------------------------===//
1924// TrackControlDependencyCondBRVisitor.
1925//===----------------------------------------------------------------------===//
1926
1927namespace {
1928/// Tracks the expressions that are a control dependency of the node that was
1929/// supplied to the constructor.
1930/// For example:
1931///
1932/// cond = 1;
1933/// if (cond)
1934/// 10 / 0;
1935///
1936/// An error is emitted at line 3. This visitor realizes that the branch
1937/// on line 2 is a control dependency of line 3, and tracks it's condition via
1938/// trackExpressionValue().
1939class TrackControlDependencyCondBRVisitor final
1940 : public TrackingBugReporterVisitor {
1941 const ExplodedNode *Origin;
1942 ControlDependencyCalculator ControlDeps;
1943 llvm::SmallPtrSet<const CFGBlock *, 32> VisitedBlocks;
1944
1945public:
1946 TrackControlDependencyCondBRVisitor(TrackerRef ParentTracker,
1947 const ExplodedNode *O)
1948 : TrackingBugReporterVisitor(ParentTracker), Origin(O),
1949 ControlDeps(&O->getCFG()) {}
1950
1951 void Profile(llvm::FoldingSetNodeID &ID) const override {
1952 static int x = 0;
1953 ID.AddPointer(Ptr: &x);
1954 }
1955
1956 PathDiagnosticPieceRef VisitNode(const ExplodedNode *N,
1957 BugReporterContext &BRC,
1958 PathSensitiveBugReport &BR) override;
1959};
1960} // end of anonymous namespace
1961
1962static std::shared_ptr<PathDiagnosticEventPiece>
1963constructDebugPieceForTrackedCondition(const Expr *Cond,
1964 const ExplodedNode *N,
1965 BugReporterContext &BRC) {
1966
1967 if (BRC.getAnalyzerOptions().AnalysisDiagOpt == PD_NONE ||
1968 !BRC.getAnalyzerOptions().ShouldTrackConditionsDebug)
1969 return nullptr;
1970
1971 std::string ConditionText = std::string(Lexer::getSourceText(
1972 Range: CharSourceRange::getTokenRange(R: Cond->getSourceRange()),
1973 SM: BRC.getSourceManager(), LangOpts: BRC.getASTContext().getLangOpts()));
1974
1975 return std::make_shared<PathDiagnosticEventPiece>(
1976 args: PathDiagnosticLocation::createBegin(S: Cond, SM: BRC.getSourceManager(),
1977 SFAC: N->getStackFrame()),
1978 args: (Twine() + "Tracking condition '" + ConditionText + "'").str());
1979}
1980
1981static bool isAssertlikeBlock(const CFGBlock *B, ASTContext &Context) {
1982 if (B->succ_size() != 2)
1983 return false;
1984
1985 const CFGBlock *Then = B->succ_begin()->getReachableBlock();
1986 const CFGBlock *Else = (B->succ_begin() + 1)->getReachableBlock();
1987
1988 if (!Then || !Else)
1989 return false;
1990
1991 if (Then->isInevitablySinking() != Else->isInevitablySinking())
1992 return true;
1993
1994 // For the following condition the following CFG would be built:
1995 //
1996 // ------------->
1997 // / \
1998 // [B1] -> [B2] -> [B3] -> [sink]
1999 // assert(A && B || C); \ \
2000 // -----------> [go on with the execution]
2001 //
2002 // It so happens that CFGBlock::getTerminatorCondition returns 'A' for block
2003 // B1, 'A && B' for B2, and 'A && B || C' for B3. Let's check whether we
2004 // reached the end of the condition!
2005 if (const Stmt *ElseCond = Else->getTerminatorCondition())
2006 if (const auto *BinOp = dyn_cast<BinaryOperator>(Val: ElseCond))
2007 if (BinOp->isLogicalOp())
2008 return isAssertlikeBlock(B: Else, Context);
2009
2010 return false;
2011}
2012
2013PathDiagnosticPieceRef
2014TrackControlDependencyCondBRVisitor::VisitNode(const ExplodedNode *N,
2015 BugReporterContext &BRC,
2016 PathSensitiveBugReport &BR) {
2017 // We can only reason about control dependencies within the same stack frame.
2018 if (Origin->getStackFrame() != N->getStackFrame())
2019 return nullptr;
2020
2021 CFGBlock *NB = const_cast<CFGBlock *>(N->getCFGBlock());
2022
2023 // Skip if we already inspected this block.
2024 if (!VisitedBlocks.insert(Ptr: NB).second)
2025 return nullptr;
2026
2027 CFGBlock *OriginB = const_cast<CFGBlock *>(Origin->getCFGBlock());
2028
2029 // TODO: Cache CFGBlocks for each ExplodedNode.
2030 if (!OriginB || !NB)
2031 return nullptr;
2032
2033 if (isAssertlikeBlock(B: NB, Context&: BRC.getASTContext()))
2034 return nullptr;
2035
2036 if (ControlDeps.isControlDependent(A: OriginB, B: NB)) {
2037 // We don't really want to explain for range loops. Evidence suggests that
2038 // the only thing that leads to is the addition of calls to operator!=.
2039 if (llvm::isa_and_nonnull<CXXForRangeStmt>(Val: NB->getTerminatorStmt()))
2040 return nullptr;
2041
2042 if (const Expr *Condition = NB->getLastCondition()) {
2043
2044 // If we can't retrieve a sensible condition, just bail out.
2045 const Expr *InnerExpr = peelOffOuterExpr(Ex: Condition, N);
2046 if (!InnerExpr)
2047 return nullptr;
2048
2049 // If the condition was a function call, we likely won't gain much from
2050 // tracking it either. Evidence suggests that it will mostly trigger in
2051 // scenarios like this:
2052 //
2053 // void f(int *x) {
2054 // x = nullptr;
2055 // if (alwaysTrue()) // We don't need a whole lot of explanation
2056 // // here, the function name is good enough.
2057 // *x = 5;
2058 // }
2059 //
2060 // Its easy to create a counterexample where this heuristic would make us
2061 // lose valuable information, but we've never really seen one in practice.
2062 if (isa<CallExpr>(Val: InnerExpr))
2063 return nullptr;
2064
2065 // Keeping track of the already tracked conditions on a visitor level
2066 // isn't sufficient, because a new visitor is created for each tracked
2067 // expression, hence the BugReport level set.
2068 if (BR.addTrackedCondition(Cond: N)) {
2069 getParentTracker().track(E: InnerExpr, N,
2070 Opts: {.Kind: bugreporter::TrackingKind::Condition,
2071 /*EnableNullFPSuppression=*/false});
2072 return constructDebugPieceForTrackedCondition(Cond: Condition, N, BRC);
2073 }
2074 }
2075 }
2076
2077 return nullptr;
2078}
2079
2080//===----------------------------------------------------------------------===//
2081// Implementation of trackExpressionValue.
2082//===----------------------------------------------------------------------===//
2083
2084static const Expr *peelOffOuterExpr(const Expr *Ex, const ExplodedNode *N) {
2085
2086 Ex = Ex->IgnoreParenCasts();
2087 if (const auto *FE = dyn_cast<FullExpr>(Val: Ex))
2088 return peelOffOuterExpr(Ex: FE->getSubExpr(), N);
2089 if (const auto *OVE = dyn_cast<OpaqueValueExpr>(Val: Ex))
2090 return peelOffOuterExpr(Ex: OVE->getSourceExpr(), N);
2091 if (const auto *POE = dyn_cast<PseudoObjectExpr>(Val: Ex)) {
2092 const auto *PropRef = dyn_cast<ObjCPropertyRefExpr>(Val: POE->getSyntacticForm());
2093 if (PropRef && PropRef->isMessagingGetter()) {
2094 const Expr *GetterMessageSend =
2095 POE->getSemanticExpr(index: POE->getNumSemanticExprs() - 1);
2096 assert(isa<ObjCMessageExpr>(GetterMessageSend->IgnoreParenCasts()));
2097 return peelOffOuterExpr(Ex: GetterMessageSend, N);
2098 }
2099 }
2100
2101 // Peel off the ternary operator.
2102 if (const auto *CO = dyn_cast<ConditionalOperator>(Val: Ex)) {
2103 // Find a node where the branching occurred and find out which branch
2104 // we took (true/false) by looking at the ExplodedGraph.
2105 const ExplodedNode *NI = N;
2106 do {
2107 ProgramPoint ProgPoint = NI->getLocation();
2108 if (std::optional<BlockEdge> BE = ProgPoint.getAs<BlockEdge>()) {
2109 const CFGBlock *srcBlk = BE->getSrc();
2110 if (const Stmt *term = srcBlk->getTerminatorStmt()) {
2111 if (term == CO) {
2112 bool TookTrueBranch = (*(srcBlk->succ_begin()) == BE->getDst());
2113 if (TookTrueBranch)
2114 return peelOffOuterExpr(Ex: CO->getTrueExpr(), N);
2115 else
2116 return peelOffOuterExpr(Ex: CO->getFalseExpr(), N);
2117 }
2118 }
2119 }
2120 NI = NI->getFirstPred();
2121 } while (NI);
2122 }
2123
2124 if (auto *BO = dyn_cast<BinaryOperator>(Val: Ex))
2125 if (const Expr *SubEx = peelOffPointerArithmetic(B: BO))
2126 return peelOffOuterExpr(Ex: SubEx, N);
2127
2128 if (auto *UO = dyn_cast<UnaryOperator>(Val: Ex)) {
2129 if (UO->getOpcode() == UO_LNot)
2130 return peelOffOuterExpr(Ex: UO->getSubExpr(), N);
2131
2132 // FIXME: There's a hack in our Store implementation that always computes
2133 // field offsets around null pointers as if they are always equal to 0.
2134 // The idea here is to report accesses to fields as null dereferences
2135 // even though the pointer value that's being dereferenced is actually
2136 // the offset of the field rather than exactly 0.
2137 // See the FIXME in StoreManager's getLValueFieldOrIvar() method.
2138 // This code interacts heavily with this hack; otherwise the value
2139 // would not be null at all for most fields, so we'd be unable to track it.
2140 if (UO->getOpcode() == UO_AddrOf && UO->getSubExpr()->isLValue())
2141 if (const Expr *DerefEx = bugreporter::getDerefExpr(S: UO->getSubExpr()))
2142 return peelOffOuterExpr(Ex: DerefEx, N);
2143 }
2144
2145 return Ex;
2146}
2147
2148/// Find the ExplodedNode where the lvalue (the value of 'Ex')
2149/// was computed.
2150static const ExplodedNode* findNodeForExpression(const ExplodedNode *N,
2151 const Expr *Inner) {
2152 while (N) {
2153 if (N->getStmtForDiagnostics() == Inner)
2154 return N;
2155 N = N->getFirstPred();
2156 }
2157 return N;
2158}
2159
2160//===----------------------------------------------------------------------===//
2161// Tracker implementation
2162//===----------------------------------------------------------------------===//
2163
2164PathDiagnosticPieceRef StoreHandler::constructNote(StoreInfo SI,
2165 BugReporterContext &BRC,
2166 StringRef NodeText) {
2167 // Construct a new PathDiagnosticPiece.
2168 ProgramPoint P = SI.StoreSite->getLocation();
2169 PathDiagnosticLocation L;
2170 if (P.getAs<CallEnter>() && SI.SourceOfTheValue)
2171 L = PathDiagnosticLocation(SI.SourceOfTheValue, BRC.getSourceManager(),
2172 P.getStackFrame());
2173
2174 if (!L.isValid() || !L.asLocation().isValid())
2175 L = PathDiagnosticLocation::create(P, SMng: BRC.getSourceManager());
2176
2177 if (!L.isValid() || !L.asLocation().isValid())
2178 return nullptr;
2179
2180 return std::make_shared<PathDiagnosticEventPiece>(args&: L, args&: NodeText);
2181}
2182
2183namespace {
2184class DefaultStoreHandler final : public StoreHandler {
2185public:
2186 using StoreHandler::StoreHandler;
2187
2188 PathDiagnosticPieceRef handle(StoreInfo SI, BugReporterContext &BRC,
2189 TrackingOptions Opts) override {
2190 // Okay, we've found the binding. Emit an appropriate message.
2191 SmallString<256> Buffer;
2192 llvm::raw_svector_ostream OS(Buffer);
2193
2194 switch (SI.StoreKind) {
2195 case StoreInfo::Initialization:
2196 case StoreInfo::BlockCapture:
2197 showBRDiagnostics(OS, SI);
2198 break;
2199 case StoreInfo::CallArgument:
2200 showBRParamDiagnostics(OS, SI);
2201 break;
2202 case StoreInfo::Assignment:
2203 showBRDefaultDiagnostics(OS, SI);
2204 break;
2205 }
2206
2207 if (Opts.Kind == bugreporter::TrackingKind::Condition)
2208 OS << WillBeUsedForACondition;
2209
2210 return constructNote(SI, BRC, NodeText: OS.str());
2211 }
2212};
2213
2214class ControlDependencyHandler final : public ExpressionHandler {
2215public:
2216 using ExpressionHandler::ExpressionHandler;
2217
2218 Tracker::Result handle(const Expr *Inner, const ExplodedNode *InputNode,
2219 const ExplodedNode *LVNode,
2220 TrackingOptions Opts) override {
2221 PathSensitiveBugReport &Report = getParentTracker().getReport();
2222
2223 // We only track expressions if we believe that they are important. Chances
2224 // are good that control dependencies to the tracking point are also
2225 // important because of this, let's explain why we believe control reached
2226 // this point.
2227 // TODO: Shouldn't we track control dependencies of every bug location,
2228 // rather than only tracked expressions?
2229 if (LVNode->getState()
2230 ->getAnalysisManager()
2231 .getAnalyzerOptions()
2232 .ShouldTrackConditions) {
2233 Report.addVisitor<TrackControlDependencyCondBRVisitor>(
2234 ConstructorArgs: &getParentTracker(), ConstructorArgs&: InputNode);
2235 return {/*FoundSomethingToTrack=*/true};
2236 }
2237
2238 return {};
2239 }
2240};
2241
2242class NilReceiverHandler final : public ExpressionHandler {
2243public:
2244 using ExpressionHandler::ExpressionHandler;
2245
2246 Tracker::Result handle(const Expr *Inner, const ExplodedNode *InputNode,
2247 const ExplodedNode *LVNode,
2248 TrackingOptions Opts) override {
2249 // The message send could be nil due to the receiver being nil.
2250 // At this point in the path, the receiver should be live since we are at
2251 // the message send expr. If it is nil, start tracking it.
2252 if (const Expr *Receiver =
2253 NilReceiverBRVisitor::getNilReceiver(S: Inner, N: LVNode))
2254 return getParentTracker().track(E: Receiver, N: LVNode, Opts);
2255
2256 return {};
2257 }
2258};
2259
2260class ArrayIndexHandler final : public ExpressionHandler {
2261public:
2262 using ExpressionHandler::ExpressionHandler;
2263
2264 Tracker::Result handle(const Expr *Inner, const ExplodedNode *InputNode,
2265 const ExplodedNode *LVNode,
2266 TrackingOptions Opts) override {
2267 // Track the index if this is an array subscript.
2268 if (const auto *Arr = dyn_cast<ArraySubscriptExpr>(Val: Inner))
2269 return getParentTracker().track(
2270 E: Arr->getIdx(), N: LVNode,
2271 Opts: {.Kind: Opts.Kind, /*EnableNullFPSuppression*/ false});
2272
2273 return {};
2274 }
2275};
2276
2277// TODO: extract it into more handlers
2278class InterestingLValueHandler final : public ExpressionHandler {
2279public:
2280 using ExpressionHandler::ExpressionHandler;
2281
2282 Tracker::Result handle(const Expr *Inner, const ExplodedNode *InputNode,
2283 const ExplodedNode *LVNode,
2284 TrackingOptions Opts) override {
2285 ProgramStateRef LVState = LVNode->getState();
2286 const StackFrame *SF = LVNode->getStackFrame();
2287 PathSensitiveBugReport &Report = getParentTracker().getReport();
2288 Tracker::Result Result;
2289
2290 // See if the expression we're interested refers to a variable.
2291 // If so, we can track both its contents and constraints on its value.
2292 if (ExplodedGraph::isInterestingLValueExpr(Ex: Inner)) {
2293 SVal LVal = LVNode->getSVal(E: Inner);
2294
2295 const MemRegion *RR = getLocationRegionIfReference(E: Inner, N: LVNode);
2296 bool LVIsNull = LVState->isNull(V: LVal).isConstrainedTrue();
2297
2298 // If this is a C++ reference to a null pointer, we are tracking the
2299 // pointer. In addition, we should find the store at which the reference
2300 // got initialized.
2301 if (RR && !LVIsNull)
2302 Result.combineWith(Other: getParentTracker().track(V: LVal, R: RR, Opts, Origin: SF));
2303
2304 // In case of C++ references, we want to differentiate between a null
2305 // reference and reference to null pointer.
2306 // If the LVal is null, check if we are dealing with null reference.
2307 // For those, we want to track the location of the reference.
2308 const MemRegion *R =
2309 (RR && LVIsNull) ? RR : LVNode->getSVal(E: Inner).getAsRegion();
2310
2311 if (R) {
2312
2313 // Mark both the variable region and its contents as interesting.
2314 SVal V = LVState->getRawSVal(LV: loc::MemRegionVal(R));
2315 Report.addVisitor<NoStoreFuncVisitor>(ConstructorArgs: cast<SubRegion>(Val: R), ConstructorArgs&: Opts.Kind);
2316
2317 // When we got here, we do have something to track, and we will
2318 // interrupt.
2319 Result.FoundSomethingToTrack = true;
2320 Result.WasInterrupted = true;
2321
2322 MacroNullReturnSuppressionVisitor::addMacroVisitorIfNecessary(
2323 N: LVNode, R, EnableNullFPSuppression: Opts.EnableNullFPSuppression, BR&: Report, V);
2324
2325 Report.markInteresting(V, TKind: Opts.Kind);
2326 Report.addVisitor<UndefOrNullArgVisitor>(ConstructorArgs&: R);
2327
2328 // If the contents are symbolic and null, find out when they became
2329 // null.
2330 if (V.getAsLocSymbol(/*IncludeBaseRegions=*/true))
2331 if (LVState->isNull(V).isConstrainedTrue())
2332 Report.addVisitor<TrackConstraintBRVisitor>(
2333 ConstructorArgs: V.castAs<DefinedSVal>(),
2334 /*Assumption=*/ConstructorArgs: false, ConstructorArgs: "Assuming pointer value is null");
2335
2336 // Add visitor, which will suppress inline defensive checks.
2337 if (auto DV = V.getAs<DefinedSVal>())
2338 if (!DV->isZeroConstant() && Opts.EnableNullFPSuppression)
2339 // Note that LVNode may be too late (i.e., too far from the
2340 // InputNode) because the lvalue may have been computed before the
2341 // inlined call was evaluated. InputNode may as well be too early
2342 // here, because the symbol is already dead; this, however, is fine
2343 // because we can still find the node in which it collapsed to null
2344 // previously.
2345 Report.addVisitor<SuppressInlineDefensiveChecksVisitor>(ConstructorArgs&: *DV,
2346 ConstructorArgs&: InputNode);
2347 getParentTracker().track(V, R, Opts, Origin: SF);
2348 }
2349 }
2350
2351 return Result;
2352 }
2353};
2354
2355/// Adds a ReturnVisitor if the given statement represents a call that was
2356/// inlined.
2357///
2358/// This will search back through the ExplodedGraph, starting from the given
2359/// node, looking for when the given statement was processed. If it turns out
2360/// the statement is a call that was inlined, we add the visitor to the
2361/// bug report, so it can print a note later.
2362class InlinedFunctionCallHandler final : public ExpressionHandler {
2363 using ExpressionHandler::ExpressionHandler;
2364
2365 Tracker::Result handle(const Expr *E, const ExplodedNode *InputNode,
2366 const ExplodedNode *ExprNode,
2367 TrackingOptions Opts) override {
2368 if (!CallEvent::isCallStmt(S: E))
2369 return {};
2370
2371 // First, find when we processed the statement.
2372 // If we work with a 'CXXNewExpr' that is going to be purged away before
2373 // its call take place. We would catch that purge in the last condition
2374 // as a 'StmtPoint' so we have to bypass it.
2375 const bool BypassCXXNewExprEval = isa<CXXNewExpr>(Val: E);
2376
2377 // This is moving forward when we enter into another stack frame.
2378 const StackFrame *CurrentSF = ExprNode->getStackFrame();
2379
2380 do {
2381 // If that is satisfied we found our statement as an inlined call.
2382 if (std::optional<CallExitEnd> CEE =
2383 ExprNode->getLocationAs<CallExitEnd>())
2384 if (CEE->getCalleeStackFrame()->getCallSite() == E)
2385 break;
2386
2387 // Try to move forward to the end of the call-chain.
2388 ExprNode = ExprNode->getFirstPred();
2389 if (!ExprNode)
2390 break;
2391
2392 const StackFrame *PredSF = ExprNode->getStackFrame();
2393
2394 // If that is satisfied we found our statement.
2395 // FIXME: This code currently bypasses the call site for the
2396 // conservatively evaluated allocator.
2397 if (!BypassCXXNewExprEval)
2398 if (std::optional<StmtPoint> SP = ExprNode->getLocationAs<StmtPoint>())
2399 // See if we do not enter into another stack frame.
2400 if (SP->getStmt() == E && CurrentSF == PredSF)
2401 break;
2402
2403 CurrentSF = PredSF;
2404 } while (ExprNode->getStackFrame() == CurrentSF);
2405
2406 // Next, step over any post-statement checks.
2407 while (ExprNode && ExprNode->getLocation().getAs<PostStmt>())
2408 ExprNode = ExprNode->getFirstPred();
2409 if (!ExprNode)
2410 return {};
2411
2412 // Finally, see if we inlined the call.
2413 std::optional<CallExitEnd> CEE = ExprNode->getLocationAs<CallExitEnd>();
2414 if (!CEE)
2415 return {};
2416
2417 const StackFrame *CalleeSF = CEE->getCalleeStackFrame();
2418 if (CalleeSF->getCallSite() != E)
2419 return {};
2420
2421 // Check the return value.
2422 ProgramStateRef State = ExprNode->getState();
2423 SVal RetVal = ExprNode->getSVal(E);
2424
2425 // Handle cases where a reference is returned and then immediately used.
2426 if (cast<Expr>(Val: E)->isGLValue())
2427 if (std::optional<Loc> LValue = RetVal.getAs<Loc>())
2428 RetVal = State->getSVal(LV: *LValue);
2429
2430 // See if the return value is NULL. If so, suppress the report.
2431 AnalyzerOptions &Options = State->getAnalysisManager().options;
2432
2433 bool EnableNullFPSuppression = false;
2434 if (Opts.EnableNullFPSuppression && Options.ShouldSuppressNullReturnPaths)
2435 if (std::optional<Loc> RetLoc = RetVal.getAs<Loc>())
2436 EnableNullFPSuppression = State->isNull(V: *RetLoc).isConstrainedTrue();
2437
2438 PathSensitiveBugReport &Report = getParentTracker().getReport();
2439 Report.addVisitor<ReturnVisitor>(ConstructorArgs: &getParentTracker(), ConstructorArgs&: CalleeSF,
2440 ConstructorArgs&: EnableNullFPSuppression, ConstructorArgs&: Options,
2441 ConstructorArgs&: Opts.Kind);
2442 return {.FoundSomethingToTrack: true};
2443 }
2444};
2445
2446class DefaultExpressionHandler final : public ExpressionHandler {
2447public:
2448 using ExpressionHandler::ExpressionHandler;
2449
2450 Tracker::Result handle(const Expr *Inner, const ExplodedNode *InputNode,
2451 const ExplodedNode *LVNode,
2452 TrackingOptions Opts) override {
2453 ProgramStateRef LVState = LVNode->getState();
2454 const StackFrame *SF = LVNode->getStackFrame();
2455 PathSensitiveBugReport &Report = getParentTracker().getReport();
2456 Tracker::Result Result;
2457
2458 // If the expression is not an "lvalue expression", we can still
2459 // track the constraints on its contents.
2460 SVal V = LVState->getSValAsScalarOrLoc(E: Inner, SF: LVNode->getStackFrame());
2461
2462 // Is it a symbolic value?
2463 if (auto L = V.getAs<loc::MemRegionVal>()) {
2464 // FIXME: this is a hack for fixing a later crash when attempting to
2465 // dereference a void* pointer.
2466 // We should not try to dereference pointers at all when we don't care
2467 // what is written inside the pointer.
2468 bool CanDereference = true;
2469 if (const auto *SR = L->getRegionAs<SymbolicRegion>()) {
2470 if (SR->getPointeeStaticType()->isVoidType())
2471 CanDereference = false;
2472 } else if (L->getRegionAs<AllocaRegion>())
2473 CanDereference = false;
2474
2475 // At this point we are dealing with the region's LValue.
2476 // However, if the rvalue is a symbolic region, we should track it as
2477 // well. Try to use the correct type when looking up the value.
2478 SVal RVal;
2479 if (ExplodedGraph::isInterestingLValueExpr(Ex: Inner))
2480 RVal = LVState->getRawSVal(LV: *L, T: Inner->getType());
2481 else if (CanDereference)
2482 RVal = LVState->getSVal(R: L->getRegion());
2483
2484 if (CanDereference) {
2485 Report.addVisitor<UndefOrNullArgVisitor>(ConstructorArgs: L->getRegion());
2486 Result.FoundSomethingToTrack = true;
2487
2488 if (!RVal.isUnknown())
2489 Result.combineWith(
2490 Other: getParentTracker().track(V: RVal, R: L->getRegion(), Opts, Origin: SF));
2491 }
2492
2493 const MemRegion *RegionRVal = RVal.getAsRegion();
2494 if (isa_and_nonnull<SymbolicRegion>(Val: RegionRVal)) {
2495 Report.markInteresting(R: RegionRVal, TKind: Opts.Kind);
2496 Report.addVisitor<TrackConstraintBRVisitor>(
2497 ConstructorArgs: loc::MemRegionVal(RegionRVal),
2498 /*Assumption=*/ConstructorArgs: false, ConstructorArgs: "Assuming pointer value is null");
2499 Result.FoundSomethingToTrack = true;
2500 }
2501 }
2502
2503 return Result;
2504 }
2505};
2506
2507/// Attempts to add visitors to track an RValue expression back to its point of
2508/// origin.
2509class PRValueHandler final : public ExpressionHandler {
2510public:
2511 using ExpressionHandler::ExpressionHandler;
2512
2513 Tracker::Result handle(const Expr *E, const ExplodedNode *InputNode,
2514 const ExplodedNode *ExprNode,
2515 TrackingOptions Opts) override {
2516 if (!E->isPRValue())
2517 return {};
2518
2519 const ExplodedNode *RVNode = findNodeForExpression(N: ExprNode, Inner: E);
2520 if (!RVNode)
2521 return {};
2522
2523 Tracker::Result CombinedResult;
2524 Tracker &Parent = getParentTracker();
2525
2526 const auto track = [&CombinedResult, &Parent, ExprNode,
2527 Opts](const Expr *Inner) {
2528 CombinedResult.combineWith(Other: Parent.track(E: Inner, N: ExprNode, Opts));
2529 };
2530
2531 // FIXME: Initializer lists can appear in many different contexts
2532 // and most of them needs a special handling. For now let's handle
2533 // what we can. If the initializer list only has 1 element, we track
2534 // that.
2535 // This snippet even handles nesting, e.g.: int *x{{{{{y}}}}};
2536 if (const auto *ILE = dyn_cast<InitListExpr>(Val: E)) {
2537 if (ILE->getNumInits() == 1) {
2538 track(ILE->getInit(Init: 0));
2539
2540 return CombinedResult;
2541 }
2542
2543 return {};
2544 }
2545
2546 ProgramStateRef RVState = RVNode->getState();
2547 SVal V = RVState->getSValAsScalarOrLoc(E, SF: RVNode->getStackFrame());
2548 const auto *BO = dyn_cast<BinaryOperator>(Val: E);
2549
2550 if (!BO || !BO->isMultiplicativeOp() || !V.isZeroConstant())
2551 return {};
2552
2553 SVal RHSV = RVState->getSVal(E: BO->getRHS(), SF: RVNode->getStackFrame());
2554 SVal LHSV = RVState->getSVal(E: BO->getLHS(), SF: RVNode->getStackFrame());
2555
2556 // Track both LHS and RHS of a multiplication.
2557 if (BO->getOpcode() == BO_Mul) {
2558 if (LHSV.isZeroConstant())
2559 track(BO->getLHS());
2560 if (RHSV.isZeroConstant())
2561 track(BO->getRHS());
2562 } else { // Track only the LHS of a division or a modulo.
2563 if (LHSV.isZeroConstant())
2564 track(BO->getLHS());
2565 }
2566
2567 return CombinedResult;
2568 }
2569};
2570} // namespace
2571
2572Tracker::Tracker(PathSensitiveBugReport &Report) : Report(Report) {
2573 // Default expression handlers.
2574 addLowPriorityHandler<ControlDependencyHandler>();
2575 addLowPriorityHandler<NilReceiverHandler>();
2576 addLowPriorityHandler<ArrayIndexHandler>();
2577 addLowPriorityHandler<InterestingLValueHandler>();
2578 addLowPriorityHandler<InlinedFunctionCallHandler>();
2579 addLowPriorityHandler<DefaultExpressionHandler>();
2580 addLowPriorityHandler<PRValueHandler>();
2581 // Default store handlers.
2582 addHighPriorityHandler<DefaultStoreHandler>();
2583}
2584
2585Tracker::Result Tracker::track(const Expr *E, const ExplodedNode *N,
2586 TrackingOptions Opts) {
2587 if (!E || !N)
2588 return {};
2589
2590 const Expr *Inner = peelOffOuterExpr(Ex: E, N);
2591 const ExplodedNode *LVNode = findNodeForExpression(N, Inner);
2592 if (!LVNode)
2593 return {};
2594
2595 Result CombinedResult;
2596 // Iterate through the handlers in the order according to their priorities.
2597 for (ExpressionHandlerPtr &Handler : ExpressionHandlers) {
2598 CombinedResult.combineWith(Other: Handler->handle(E: Inner, Original: N, ExprNode: LVNode, Opts));
2599 if (CombinedResult.WasInterrupted) {
2600 // There is no need to confuse our users here.
2601 // We got interrupted, but our users don't need to know about it.
2602 CombinedResult.WasInterrupted = false;
2603 break;
2604 }
2605 }
2606
2607 return CombinedResult;
2608}
2609
2610Tracker::Result Tracker::track(SVal V, const MemRegion *R, TrackingOptions Opts,
2611 const StackFrame *Origin) {
2612 if (!V.isUnknown()) {
2613 Report.addVisitor<StoreSiteFinder>(ConstructorArgs: this, ConstructorArgs&: V, ConstructorArgs&: R, ConstructorArgs&: Opts, ConstructorArgs&: Origin);
2614 return {.FoundSomethingToTrack: true};
2615 }
2616 return {};
2617}
2618
2619PathDiagnosticPieceRef Tracker::handle(StoreInfo SI, BugReporterContext &BRC,
2620 TrackingOptions Opts) {
2621 // Iterate through the handlers in the order according to their priorities.
2622 for (StoreHandlerPtr &Handler : StoreHandlers) {
2623 if (PathDiagnosticPieceRef Result = Handler->handle(SI, BRC, Opts))
2624 // If the handler produced a non-null piece, return it.
2625 // There is no need in asking other handlers.
2626 return Result;
2627 }
2628 return {};
2629}
2630
2631bool bugreporter::trackExpressionValue(const ExplodedNode *InputNode,
2632 const Expr *E,
2633
2634 PathSensitiveBugReport &Report,
2635 TrackingOptions Opts) {
2636 return Tracker::create(Report)
2637 ->track(E, N: InputNode, Opts)
2638 .FoundSomethingToTrack;
2639}
2640
2641void bugreporter::trackStoredValue(SVal V, const MemRegion *R,
2642 PathSensitiveBugReport &Report,
2643 TrackingOptions Opts,
2644 const StackFrame *Origin) {
2645 Tracker::create(Report)->track(V, R, Opts, Origin);
2646}
2647
2648//===----------------------------------------------------------------------===//
2649// Implementation of NulReceiverBRVisitor.
2650//===----------------------------------------------------------------------===//
2651
2652const Expr *NilReceiverBRVisitor::getNilReceiver(const Stmt *S,
2653 const ExplodedNode *N) {
2654 const auto *ME = dyn_cast<ObjCMessageExpr>(Val: S);
2655 if (!ME)
2656 return nullptr;
2657 if (const Expr *Receiver = ME->getInstanceReceiver()) {
2658 ProgramStateRef state = N->getState();
2659 SVal V = N->getSVal(E: Receiver);
2660 if (state->isNull(V).isConstrainedTrue())
2661 return Receiver;
2662 }
2663 return nullptr;
2664}
2665
2666PathDiagnosticPieceRef
2667NilReceiverBRVisitor::VisitNode(const ExplodedNode *N, BugReporterContext &BRC,
2668 PathSensitiveBugReport &BR) {
2669 std::optional<PreStmt> P = N->getLocationAs<PreStmt>();
2670 if (!P)
2671 return nullptr;
2672
2673 const Stmt *S = P->getStmt();
2674 const Expr *Receiver = getNilReceiver(S, N);
2675 if (!Receiver)
2676 return nullptr;
2677
2678 llvm::SmallString<256> Buf;
2679 llvm::raw_svector_ostream OS(Buf);
2680
2681 if (const auto *ME = dyn_cast<ObjCMessageExpr>(Val: S)) {
2682 OS << "'";
2683 ME->getSelector().print(OS);
2684 OS << "' not called";
2685 }
2686 else {
2687 OS << "No method is called";
2688 }
2689 OS << " because the receiver is nil";
2690
2691 // The receiver was nil, and hence the method was skipped.
2692 // Register a BugReporterVisitor to issue a message telling us how
2693 // the receiver was null.
2694 bugreporter::trackExpressionValue(InputNode: N, E: Receiver, Report&: BR,
2695 Opts: {.Kind: bugreporter::TrackingKind::Thorough,
2696 /*EnableNullFPSuppression*/ false});
2697 // Issue a message saying that the method was skipped.
2698 PathDiagnosticLocation L(Receiver, BRC.getSourceManager(),
2699 N->getStackFrame());
2700 return std::make_shared<PathDiagnosticEventPiece>(args&: L, args: OS.str());
2701}
2702
2703//===----------------------------------------------------------------------===//
2704// Visitor that tries to report interesting diagnostics from conditions.
2705//===----------------------------------------------------------------------===//
2706
2707/// Return the tag associated with this visitor. This tag will be used
2708/// to make all PathDiagnosticPieces created by this visitor.
2709const char *ConditionBRVisitor::getTag() { return "ConditionBRVisitor"; }
2710
2711PathDiagnosticPieceRef
2712ConditionBRVisitor::VisitNode(const ExplodedNode *N, BugReporterContext &BRC,
2713 PathSensitiveBugReport &BR) {
2714 auto piece = VisitNodeImpl(N, BRC, BR);
2715 if (piece) {
2716 piece->setTag(getTag());
2717 if (auto *ev = dyn_cast<PathDiagnosticEventPiece>(Val: piece.get()))
2718 ev->setPrunable(isPrunable: true, /* override */ false);
2719 }
2720 return piece;
2721}
2722
2723PathDiagnosticPieceRef
2724ConditionBRVisitor::VisitNodeImpl(const ExplodedNode *N,
2725 BugReporterContext &BRC,
2726 PathSensitiveBugReport &BR) {
2727 ProgramPoint ProgPoint = N->getLocation();
2728 const std::pair<const ProgramPointTag *, const ProgramPointTag *> &Tags =
2729 ExprEngine::getEagerlyAssumeBifurcationTags();
2730
2731 // If an assumption was made on a branch, it should be caught
2732 // here by looking at the state transition.
2733 if (std::optional<BlockEdge> BE = ProgPoint.getAs<BlockEdge>()) {
2734 const CFGBlock *SrcBlock = BE->getSrc();
2735 if (const Stmt *Term = SrcBlock->getTerminatorStmt()) {
2736 // If the tag of the previous node is 'Eagerly Assume...' the current
2737 // 'BlockEdge' has the same constraint information. We do not want to
2738 // report the value as it is just an assumption on the predecessor node
2739 // which will be caught in the next VisitNode() iteration as a 'PostStmt'.
2740 const ProgramPointTag *PreviousNodeTag =
2741 N->getFirstPred()->getLocation().getTag();
2742 if (PreviousNodeTag == Tags.first || PreviousNodeTag == Tags.second)
2743 return nullptr;
2744
2745 return VisitTerminator(Term, N, SrcBlk: SrcBlock, DstBlk: BE->getDst(), R&: BR, BRC);
2746 }
2747 return nullptr;
2748 }
2749
2750 if (std::optional<PostStmt> PS = ProgPoint.getAs<PostStmt>()) {
2751 const ProgramPointTag *CurrentNodeTag = PS->getTag();
2752 if (CurrentNodeTag != Tags.first && CurrentNodeTag != Tags.second)
2753 return nullptr;
2754
2755 bool TookTrue = CurrentNodeTag == Tags.first;
2756 return VisitTrueTest(Cond: cast<Expr>(Val: PS->getStmt()), BRC, R&: BR, N, TookTrue);
2757 }
2758
2759 return nullptr;
2760}
2761
2762PathDiagnosticPieceRef ConditionBRVisitor::VisitTerminator(
2763 const Stmt *Term, const ExplodedNode *N, const CFGBlock *srcBlk,
2764 const CFGBlock *dstBlk, PathSensitiveBugReport &R,
2765 BugReporterContext &BRC) {
2766 const Expr *Cond = nullptr;
2767
2768 // In the code below, Term is a CFG terminator and Cond is a branch condition
2769 // expression upon which the decision is made on this terminator.
2770 //
2771 // For example, in "if (x == 0)", the "if (x == 0)" statement is a terminator,
2772 // and "x == 0" is the respective condition.
2773 //
2774 // Another example: in "if (x && y)", we've got two terminators and two
2775 // conditions due to short-circuit nature of operator "&&":
2776 // 1. The "if (x && y)" statement is a terminator,
2777 // and "y" is the respective condition.
2778 // 2. Also "x && ..." is another terminator,
2779 // and "x" is its condition.
2780
2781 switch (Term->getStmtClass()) {
2782 // FIXME: Stmt::SwitchStmtClass is worth handling, however it is a bit
2783 // more tricky because there are more than two branches to account for.
2784 default:
2785 return nullptr;
2786 case Stmt::IfStmtClass: {
2787 const auto *IfStatement = cast<IfStmt>(Val: Term);
2788 // Handle if consteval which doesn't have a traditional condition.
2789 if (IfStatement->isConsteval())
2790 return nullptr;
2791 Cond = IfStatement->getCond();
2792 break;
2793 }
2794 case Stmt::ConditionalOperatorClass:
2795 Cond = cast<ConditionalOperator>(Val: Term)->getCond();
2796 break;
2797 case Stmt::BinaryOperatorClass:
2798 // When we encounter a logical operator (&& or ||) as a CFG terminator,
2799 // then the condition is actually its LHS; otherwise, we'd encounter
2800 // the parent, such as if-statement, as a terminator.
2801 const auto *BO = cast<BinaryOperator>(Val: Term);
2802 assert(BO->isLogicalOp() &&
2803 "CFG terminator is not a short-circuit operator!");
2804 Cond = BO->getLHS();
2805 break;
2806 }
2807
2808 Cond = Cond->IgnoreParens();
2809
2810 // However, when we encounter a logical operator as a branch condition,
2811 // then the condition is actually its RHS, because LHS would be
2812 // the condition for the logical operator terminator.
2813 while (const auto *InnerBO = dyn_cast<BinaryOperator>(Val: Cond)) {
2814 if (!InnerBO->isLogicalOp())
2815 break;
2816 Cond = InnerBO->getRHS()->IgnoreParens();
2817 }
2818
2819 assert(Cond);
2820 assert(srcBlk->succ_size() == 2);
2821 const bool TookTrue = *(srcBlk->succ_begin()) == dstBlk;
2822 return VisitTrueTest(Cond, BRC, R, N, TookTrue);
2823}
2824
2825PathDiagnosticPieceRef
2826ConditionBRVisitor::VisitTrueTest(const Expr *Cond, BugReporterContext &BRC,
2827 PathSensitiveBugReport &R,
2828 const ExplodedNode *N, bool TookTrue) {
2829 ProgramStateRef CurrentState = N->getState();
2830 ProgramStateRef PrevState = N->getFirstPred()->getState();
2831 const StackFrame *SF = N->getStackFrame();
2832
2833 // If the constraint information is changed between the current and the
2834 // previous program state we assuming the newly seen constraint information.
2835 // If we cannot evaluate the condition (and the constraints are the same)
2836 // the analyzer has no information about the value and just assuming it.
2837 // FIXME: This logic is not entirely correct, because e.g. in code like
2838 // void f(unsigned arg) {
2839 // if (arg >= 0) {
2840 // // ...
2841 // }
2842 // }
2843 // it will say that the "arg >= 0" check is _assuming_ something new because
2844 // the constraint that "$arg >= 0" is 1 was added to the list of known
2845 // constraints. However, the unsigned value is always >= 0 so semantically
2846 // this is not a "real" assumption.
2847 bool IsAssuming =
2848 !BRC.getStateManager().haveEqualConstraints(S1: CurrentState, S2: PrevState) ||
2849 CurrentState->getSVal(E: Cond, SF).isUnknownOrUndef();
2850
2851 // These will be modified in code below, but we need to preserve the original
2852 // values in case we want to throw the generic message.
2853 const Expr *CondTmp = Cond;
2854 bool TookTrueTmp = TookTrue;
2855
2856 while (true) {
2857 CondTmp = CondTmp->IgnoreParenCasts();
2858 switch (CondTmp->getStmtClass()) {
2859 default:
2860 break;
2861 case Stmt::BinaryOperatorClass:
2862 if (auto P = VisitTrueTest(Cond, BExpr: cast<BinaryOperator>(Val: CondTmp),
2863 BRC, R, N, TookTrue: TookTrueTmp, IsAssuming))
2864 return P;
2865 break;
2866 case Stmt::DeclRefExprClass:
2867 if (auto P = VisitTrueTest(Cond, DR: cast<DeclRefExpr>(Val: CondTmp),
2868 BRC, R, N, TookTrue: TookTrueTmp, IsAssuming))
2869 return P;
2870 break;
2871 case Stmt::MemberExprClass:
2872 if (auto P = VisitTrueTest(Cond, ME: cast<MemberExpr>(Val: CondTmp),
2873 BRC, R, N, TookTrue: TookTrueTmp, IsAssuming))
2874 return P;
2875 break;
2876 case Stmt::UnaryOperatorClass: {
2877 const auto *UO = cast<UnaryOperator>(Val: CondTmp);
2878 if (UO->getOpcode() == UO_LNot) {
2879 TookTrueTmp = !TookTrueTmp;
2880 CondTmp = UO->getSubExpr();
2881 continue;
2882 }
2883 break;
2884 }
2885 }
2886 break;
2887 }
2888
2889 // Condition too complex to explain? Just say something so that the user
2890 // knew we've made some path decision at this point.
2891 // If it is too complex and we know the evaluation of the condition do not
2892 // repeat the note from 'BugReporter.cpp'
2893 if (!IsAssuming)
2894 return nullptr;
2895
2896 PathDiagnosticLocation Loc(Cond, BRC.getSourceManager(), SF);
2897 if (!Loc.isValid() || !Loc.asLocation().isValid())
2898 return nullptr;
2899
2900 return std::make_shared<PathDiagnosticEventPiece>(
2901 args&: Loc, args: TookTrue ? GenericTrueMessage : GenericFalseMessage);
2902}
2903
2904bool ConditionBRVisitor::patternMatch(const Expr *Ex, const Expr *ParentEx,
2905 const Expr *OtherEx, raw_ostream &Out,
2906 BugReporterContext &BRC,
2907 PathSensitiveBugReport &Report,
2908 const ExplodedNode *N,
2909 std::optional<bool> &Prunable,
2910 bool IsSameFieldName) {
2911 const Expr *OriginalExpr = Ex;
2912 Ex = Ex->IgnoreParenCasts();
2913 OtherEx = OtherEx->IgnoreParenCasts();
2914
2915 if (isa<GNUNullExpr, ObjCBoolLiteralExpr, CXXBoolLiteralExpr, IntegerLiteral,
2916 FloatingLiteral>(Val: Ex)) {
2917 // Use heuristics to determine if the expression is a macro
2918 // expanding to a literal and if so, use the macro's name.
2919 SourceLocation BeginLoc = OriginalExpr->getBeginLoc();
2920 SourceLocation EndLoc = OriginalExpr->getEndLoc();
2921 if (BeginLoc.isMacroID() && EndLoc.isMacroID()) {
2922 const SourceManager &SM = BRC.getSourceManager();
2923 const LangOptions &LO = BRC.getASTContext().getLangOpts();
2924 if (Lexer::isAtStartOfMacroExpansion(loc: BeginLoc, SM, LangOpts: LO) &&
2925 Lexer::isAtEndOfMacroExpansion(loc: EndLoc, SM, LangOpts: LO)) {
2926 CharSourceRange R = Lexer::getAsCharRange(Range: {BeginLoc, EndLoc}, SM, LangOpts: LO);
2927 Out << Lexer::getSourceText(Range: R, SM, LangOpts: LO);
2928 return false;
2929 }
2930 }
2931 }
2932
2933 if (const auto *DR = dyn_cast<DeclRefExpr>(Val: Ex)) {
2934 const bool quotes = isa<VarDecl>(Val: DR->getDecl());
2935 if (quotes) {
2936 Out << '\'';
2937 const ProgramState *state = N->getState().get();
2938 if (const MemRegion *R =
2939 state->getLValue(VD: cast<VarDecl>(Val: DR->getDecl()), SF: N->getStackFrame())
2940 .getAsRegion()) {
2941 if (Report.isInteresting(R))
2942 Prunable = false;
2943 else {
2944 const ProgramState *state = N->getState().get();
2945 SVal V = state->getSVal(R);
2946 if (Report.isInteresting(V))
2947 Prunable = false;
2948 }
2949 }
2950 }
2951 Out << DR->getDecl()->getDeclName().getAsString();
2952 if (quotes)
2953 Out << '\'';
2954 return quotes;
2955 }
2956
2957 if (const auto *IL = dyn_cast<IntegerLiteral>(Val: Ex)) {
2958 QualType OriginalTy = OriginalExpr->getType();
2959 if (OriginalTy->isPointerType()) {
2960 if (IL->getValue() == 0) {
2961 Out << "null";
2962 return false;
2963 }
2964 }
2965 else if (OriginalTy->isObjCObjectPointerType()) {
2966 if (IL->getValue() == 0) {
2967 Out << "nil";
2968 return false;
2969 }
2970 }
2971
2972 bool IsAnySigned = Ex->getType()->isSignedIntegerOrEnumerationType() ||
2973 OtherEx->getType()->isSignedIntegerOrEnumerationType();
2974 IL->getValue().print(OS&: Out, /*isSigned=*/IsAnySigned);
2975 return false;
2976 }
2977
2978 if (const auto *ME = dyn_cast<MemberExpr>(Val: Ex)) {
2979 if (!IsSameFieldName)
2980 Out << "field '" << ME->getMemberDecl()->getName() << '\'';
2981 else
2982 Out << '\''
2983 << Lexer::getSourceText(
2984 Range: CharSourceRange::getTokenRange(R: Ex->getSourceRange()),
2985 SM: BRC.getSourceManager(), LangOpts: BRC.getASTContext().getLangOpts(),
2986 Invalid: nullptr)
2987 << '\'';
2988 }
2989
2990 return false;
2991}
2992
2993PathDiagnosticPieceRef ConditionBRVisitor::VisitTrueTest(
2994 const Expr *Cond, const BinaryOperator *BExpr, BugReporterContext &BRC,
2995 PathSensitiveBugReport &R, const ExplodedNode *N, bool TookTrue,
2996 bool IsAssuming) {
2997 bool shouldInvert = false;
2998 std::optional<bool> shouldPrune;
2999
3000 // Check if the field name of the MemberExprs is ambiguous. Example:
3001 // " 'a.d' is equal to 'h.d' " in 'test/Analysis/null-deref-path-notes.cpp'.
3002 bool IsSameFieldName = false;
3003 const auto *LhsME = dyn_cast<MemberExpr>(Val: BExpr->getLHS()->IgnoreParenCasts());
3004 const auto *RhsME = dyn_cast<MemberExpr>(Val: BExpr->getRHS()->IgnoreParenCasts());
3005
3006 if (LhsME && RhsME)
3007 IsSameFieldName =
3008 LhsME->getMemberDecl()->getName() == RhsME->getMemberDecl()->getName();
3009
3010 SmallString<128> LhsString, RhsString;
3011 {
3012 llvm::raw_svector_ostream OutLHS(LhsString), OutRHS(RhsString);
3013 const bool isVarLHS =
3014 patternMatch(Ex: BExpr->getLHS(), ParentEx: BExpr, OtherEx: BExpr->getRHS(), Out&: OutLHS, BRC, Report&: R, N,
3015 Prunable&: shouldPrune, IsSameFieldName);
3016 const bool isVarRHS =
3017 patternMatch(Ex: BExpr->getRHS(), ParentEx: BExpr, OtherEx: BExpr->getLHS(), Out&: OutRHS, BRC, Report&: R, N,
3018 Prunable&: shouldPrune, IsSameFieldName);
3019
3020 shouldInvert = !isVarLHS && isVarRHS;
3021 }
3022
3023 BinaryOperator::Opcode Op = BExpr->getOpcode();
3024
3025 if (BinaryOperator::isAssignmentOp(Opc: Op)) {
3026 // For assignment operators, all that we care about is that the LHS
3027 // evaluates to "true" or "false".
3028 return VisitConditionVariable(LhsString, CondVarExpr: BExpr->getLHS(), BRC, R, N,
3029 TookTrue);
3030 }
3031
3032 // For non-assignment operations, we require that we can understand
3033 // both the LHS and RHS.
3034 if (LhsString.empty() || RhsString.empty() ||
3035 !BinaryOperator::isComparisonOp(Opc: Op) || Op == BO_Cmp)
3036 return nullptr;
3037
3038 // Should we invert the strings if the LHS is not a variable name?
3039 SmallString<256> buf;
3040 llvm::raw_svector_ostream Out(buf);
3041 Out << (IsAssuming ? "Assuming " : "")
3042 << (shouldInvert ? RhsString : LhsString) << " is ";
3043
3044 // Do we need to invert the opcode?
3045 if (shouldInvert)
3046 switch (Op) {
3047 default: break;
3048 case BO_LT: Op = BO_GT; break;
3049 case BO_GT: Op = BO_LT; break;
3050 case BO_LE: Op = BO_GE; break;
3051 case BO_GE: Op = BO_LE; break;
3052 }
3053
3054 if (!TookTrue)
3055 switch (Op) {
3056 case BO_EQ: Op = BO_NE; break;
3057 case BO_NE: Op = BO_EQ; break;
3058 case BO_LT: Op = BO_GE; break;
3059 case BO_GT: Op = BO_LE; break;
3060 case BO_LE: Op = BO_GT; break;
3061 case BO_GE: Op = BO_LT; break;
3062 default:
3063 return nullptr;
3064 }
3065
3066 switch (Op) {
3067 case BO_EQ:
3068 Out << "equal to ";
3069 break;
3070 case BO_NE:
3071 Out << "not equal to ";
3072 break;
3073 default:
3074 Out << BinaryOperator::getOpcodeStr(Op) << ' ';
3075 break;
3076 }
3077
3078 Out << (shouldInvert ? LhsString : RhsString);
3079 const StackFrame *SF = N->getStackFrame();
3080 const SourceManager &SM = BRC.getSourceManager();
3081
3082 if (isVarAnInterestingCondition(CondVarExpr: BExpr->getLHS(), N, B: &R) ||
3083 isVarAnInterestingCondition(CondVarExpr: BExpr->getRHS(), N, B: &R))
3084 Out << WillBeUsedForACondition;
3085
3086 // Convert 'field ...' to 'Field ...' if it is a MemberExpr.
3087 std::string Message = std::string(Out.str());
3088 Message[0] = toupper(c: Message[0]);
3089
3090 // If we know the value create a pop-up note to the value part of 'BExpr'.
3091 if (!IsAssuming) {
3092 PathDiagnosticLocation Loc;
3093 if (!shouldInvert) {
3094 if (LhsME && LhsME->getMemberLoc().isValid())
3095 Loc = PathDiagnosticLocation(LhsME->getMemberLoc(), SM);
3096 else
3097 Loc = PathDiagnosticLocation(BExpr->getLHS(), SM, SF);
3098 } else {
3099 if (RhsME && RhsME->getMemberLoc().isValid())
3100 Loc = PathDiagnosticLocation(RhsME->getMemberLoc(), SM);
3101 else
3102 Loc = PathDiagnosticLocation(BExpr->getRHS(), SM, SF);
3103 }
3104
3105 return std::make_shared<PathDiagnosticPopUpPiece>(args&: Loc, args&: Message);
3106 }
3107
3108 PathDiagnosticLocation Loc(Cond, SM, SF);
3109 auto event = std::make_shared<PathDiagnosticEventPiece>(args&: Loc, args&: Message);
3110 if (shouldPrune)
3111 event->setPrunable(isPrunable: *shouldPrune);
3112 return event;
3113}
3114
3115PathDiagnosticPieceRef ConditionBRVisitor::VisitConditionVariable(
3116 StringRef LhsString, const Expr *CondVarExpr, BugReporterContext &BRC,
3117 PathSensitiveBugReport &report, const ExplodedNode *N, bool TookTrue) {
3118 // FIXME: If there's already a constraint tracker for this variable,
3119 // we shouldn't emit anything here (c.f. the double note in
3120 // test/Analysis/inlining/path-notes.c)
3121 SmallString<256> buf;
3122 llvm::raw_svector_ostream Out(buf);
3123 Out << "Assuming " << LhsString << " is ";
3124
3125 if (!printValue(CondVarExpr, Out, N, TookTrue, /*IsAssuming=*/true))
3126 return nullptr;
3127
3128 PathDiagnosticLocation Loc(CondVarExpr, BRC.getSourceManager(),
3129 N->getStackFrame());
3130
3131 if (isVarAnInterestingCondition(CondVarExpr, N, B: &report))
3132 Out << WillBeUsedForACondition;
3133
3134 auto event = std::make_shared<PathDiagnosticEventPiece>(args&: Loc, args: Out.str());
3135
3136 if (isInterestingExpr(E: CondVarExpr, N, B: &report))
3137 event->setPrunable(isPrunable: false);
3138
3139 return event;
3140}
3141
3142PathDiagnosticPieceRef ConditionBRVisitor::VisitTrueTest(
3143 const Expr *Cond, const DeclRefExpr *DRE, BugReporterContext &BRC,
3144 PathSensitiveBugReport &report, const ExplodedNode *N, bool TookTrue,
3145 bool IsAssuming) {
3146 const auto *VD = dyn_cast<VarDecl>(Val: DRE->getDecl());
3147 if (!VD)
3148 return nullptr;
3149
3150 SmallString<256> Buf;
3151 llvm::raw_svector_ostream Out(Buf);
3152
3153 Out << (IsAssuming ? "Assuming '" : "'") << VD->getDeclName() << "' is ";
3154
3155 if (!printValue(CondVarExpr: DRE, Out, N, TookTrue, IsAssuming))
3156 return nullptr;
3157
3158 const StackFrame *SF = N->getStackFrame();
3159
3160 if (isVarAnInterestingCondition(CondVarExpr: DRE, N, B: &report))
3161 Out << WillBeUsedForACondition;
3162
3163 // If we know the value create a pop-up note to the 'DRE'.
3164 if (!IsAssuming) {
3165 PathDiagnosticLocation Loc(DRE, BRC.getSourceManager(), SF);
3166 return std::make_shared<PathDiagnosticPopUpPiece>(args&: Loc, args: Out.str());
3167 }
3168
3169 PathDiagnosticLocation Loc(Cond, BRC.getSourceManager(), SF);
3170 auto event = std::make_shared<PathDiagnosticEventPiece>(args&: Loc, args: Out.str());
3171
3172 if (isInterestingExpr(E: DRE, N, B: &report))
3173 event->setPrunable(isPrunable: false);
3174
3175 return std::move(event);
3176}
3177
3178PathDiagnosticPieceRef ConditionBRVisitor::VisitTrueTest(
3179 const Expr *Cond, const MemberExpr *ME, BugReporterContext &BRC,
3180 PathSensitiveBugReport &report, const ExplodedNode *N, bool TookTrue,
3181 bool IsAssuming) {
3182 SmallString<256> Buf;
3183 llvm::raw_svector_ostream Out(Buf);
3184
3185 Out << (IsAssuming ? "Assuming field '" : "Field '")
3186 << ME->getMemberDecl()->getName() << "' is ";
3187
3188 if (!printValue(CondVarExpr: ME, Out, N, TookTrue, IsAssuming))
3189 return nullptr;
3190
3191 PathDiagnosticLocation Loc;
3192
3193 // If we know the value create a pop-up note to the member of the MemberExpr.
3194 if (!IsAssuming && ME->getMemberLoc().isValid())
3195 Loc = PathDiagnosticLocation(ME->getMemberLoc(), BRC.getSourceManager());
3196 else
3197 Loc = PathDiagnosticLocation(Cond, BRC.getSourceManager(),
3198 N->getStackFrame());
3199
3200 if (!Loc.isValid() || !Loc.asLocation().isValid())
3201 return nullptr;
3202
3203 if (isVarAnInterestingCondition(CondVarExpr: ME, N, B: &report))
3204 Out << WillBeUsedForACondition;
3205
3206 // If we know the value create a pop-up note.
3207 if (!IsAssuming)
3208 return std::make_shared<PathDiagnosticPopUpPiece>(args&: Loc, args: Out.str());
3209
3210 auto event = std::make_shared<PathDiagnosticEventPiece>(args&: Loc, args: Out.str());
3211 if (isInterestingExpr(E: ME, N, B: &report))
3212 event->setPrunable(isPrunable: false);
3213 return event;
3214}
3215
3216bool ConditionBRVisitor::printValue(const Expr *CondVarExpr, raw_ostream &Out,
3217 const ExplodedNode *N, bool TookTrue,
3218 bool IsAssuming) {
3219 QualType Ty = CondVarExpr->getType();
3220
3221 if (Ty->isPointerType()) {
3222 Out << (TookTrue ? "non-null" : "null");
3223 return true;
3224 }
3225
3226 if (Ty->isObjCObjectPointerType()) {
3227 Out << (TookTrue ? "non-nil" : "nil");
3228 return true;
3229 }
3230
3231 if (!Ty->isIntegralOrEnumerationType())
3232 return false;
3233
3234 std::optional<const llvm::APSInt *> IntValue;
3235 if (!IsAssuming)
3236 IntValue = getConcreteIntegerValue(CondVarExpr, N);
3237
3238 if (IsAssuming || !IntValue) {
3239 if (Ty->isBooleanType())
3240 Out << (TookTrue ? "true" : "false");
3241 else
3242 Out << (TookTrue ? "not equal to 0" : "0");
3243 } else {
3244 if (Ty->isBooleanType())
3245 Out << ((*IntValue)->getBoolValue() ? "true" : "false");
3246 else
3247 Out << **IntValue;
3248 }
3249
3250 return true;
3251}
3252
3253bool ConditionBRVisitor::isPieceMessageGeneric(
3254 const PathDiagnosticPiece *Piece) {
3255 return Piece->getString() == GenericTrueMessage ||
3256 Piece->getString() == GenericFalseMessage;
3257}
3258
3259//===----------------------------------------------------------------------===//
3260// Implementation of LikelyFalsePositiveSuppressionBRVisitor.
3261//===----------------------------------------------------------------------===//
3262
3263void LikelyFalsePositiveSuppressionBRVisitor::finalizeVisitor(
3264 const ExplodedNode *N, BugReporterContext &BRC,
3265 PathSensitiveBugReport &BR) {
3266 // Here we suppress false positives coming from system headers. This list is
3267 // based on known issues.
3268 const AnalyzerOptions &Options = BRC.getAnalyzerOptions();
3269 const Decl *D = N->getStackFrame()->getDecl();
3270
3271 if (AnalysisDeclContext::isInStdNamespace(D)) {
3272 // Skip reports within the 'std' namespace. Although these can sometimes be
3273 // the user's fault, we currently don't report them very well, and
3274 // Note that this will not help for any other data structure libraries, like
3275 // TR1, Boost, or llvm/ADT.
3276 if (Options.ShouldSuppressFromCXXStandardLibrary) {
3277 BR.markInvalid(Tag: getTag(), Data: nullptr);
3278 return;
3279 } else {
3280 // If the complete 'std' suppression is not enabled, suppress reports
3281 // from the 'std' namespace that are known to produce false positives.
3282
3283 // The analyzer issues a false use-after-free when std::list::pop_front
3284 // or std::list::pop_back are called multiple times because we cannot
3285 // reason about the internal invariants of the data structure.
3286 if (const auto *MD = dyn_cast<CXXMethodDecl>(Val: D)) {
3287 const CXXRecordDecl *CD = MD->getParent();
3288 if (CD->getName() == "list") {
3289 BR.markInvalid(Tag: getTag(), Data: nullptr);
3290 return;
3291 }
3292 }
3293
3294 // The analyzer issues a false positive when the constructor of
3295 // std::__independent_bits_engine from algorithms is used.
3296 if (const auto *MD = dyn_cast<CXXConstructorDecl>(Val: D)) {
3297 const CXXRecordDecl *CD = MD->getParent();
3298 if (CD->getName() == "__independent_bits_engine") {
3299 BR.markInvalid(Tag: getTag(), Data: nullptr);
3300 return;
3301 }
3302 }
3303
3304 for (const StackFrame &SF : N->stackframes()) {
3305 const auto *MD = dyn_cast<CXXMethodDecl>(Val: SF.getDecl());
3306 if (!MD)
3307 continue;
3308
3309 const CXXRecordDecl *CD = MD->getParent();
3310 // The analyzer issues a false positive on
3311 // std::basic_string<uint8_t> v; v.push_back(1);
3312 // and
3313 // std::u16string s; s += u'a';
3314 // because we cannot reason about the internal invariants of the
3315 // data structure.
3316 if (CD->getName() == "basic_string") {
3317 BR.markInvalid(Tag: getTag(), Data: nullptr);
3318 return;
3319 }
3320
3321 // The analyzer issues a false positive on
3322 // std::shared_ptr<int> p(new int(1)); p = nullptr;
3323 // because it does not reason properly about temporary destructors.
3324 if (CD->getName() == "shared_ptr") {
3325 BR.markInvalid(Tag: getTag(), Data: nullptr);
3326 return;
3327 }
3328 }
3329 }
3330 }
3331
3332 // Skip reports within the sys/queue.h macros as we do not have the ability to
3333 // reason about data structure shapes.
3334 const SourceManager &SM = BRC.getSourceManager();
3335 FullSourceLoc Loc = BR.getLocation().asLocation();
3336 while (Loc.isMacroID()) {
3337 Loc = Loc.getSpellingLoc();
3338 if (SM.getFilename(SpellingLoc: Loc).ends_with(Suffix: "sys/queue.h")) {
3339 BR.markInvalid(Tag: getTag(), Data: nullptr);
3340 return;
3341 }
3342 }
3343}
3344
3345//===----------------------------------------------------------------------===//
3346// Implementation of UndefOrNullArgVisitor.
3347//===----------------------------------------------------------------------===//
3348
3349PathDiagnosticPieceRef
3350UndefOrNullArgVisitor::VisitNode(const ExplodedNode *N, BugReporterContext &BRC,
3351 PathSensitiveBugReport &BR) {
3352 ProgramStateRef State = N->getState();
3353 ProgramPoint ProgLoc = N->getLocation();
3354
3355 // We are only interested in visiting CallEnter nodes.
3356 std::optional<CallEnter> CEnter = ProgLoc.getAs<CallEnter>();
3357 if (!CEnter)
3358 return nullptr;
3359
3360 // Check if one of the arguments is the region the visitor is tracking.
3361 CallEventManager &CEMgr = BRC.getStateManager().getCallEventManager();
3362 CallEventRef<> Call = CEMgr.getCaller(CalleeSF: CEnter->getCalleeStackFrame(), State);
3363 unsigned Idx = 0;
3364 ArrayRef<ParmVarDecl *> parms = Call->parameters();
3365
3366 for (const auto ParamDecl : parms) {
3367 const MemRegion *ArgReg = Call->getArgSVal(Index: Idx).getAsRegion();
3368 ++Idx;
3369
3370 // Are we tracking the argument or its subregion?
3371 if ( !ArgReg || !R->isSubRegionOf(R: ArgReg->StripCasts()))
3372 continue;
3373
3374 // Check the function parameter type.
3375 assert(ParamDecl && "Formal parameter has no decl?");
3376 QualType T = ParamDecl->getType();
3377
3378 if (!(T->isAnyPointerType() || T->isReferenceType())) {
3379 // Function can only change the value passed in by address.
3380 continue;
3381 }
3382
3383 // If it is a const pointer value, the function does not intend to
3384 // change the value.
3385 if (T->getPointeeType().isConstQualified())
3386 continue;
3387
3388 // Mark the call site (StackFrame) as interesting if the value of the
3389 // argument is undefined or '0'/'NULL'.
3390 SVal BoundVal = State->getSVal(R);
3391 if (BoundVal.isUndef() || BoundVal.isZeroConstant()) {
3392 BR.markInteresting(SF: CEnter->getCalleeStackFrame());
3393 return nullptr;
3394 }
3395 }
3396 return nullptr;
3397}
3398
3399//===----------------------------------------------------------------------===//
3400// Implementation of TagVisitor.
3401//===----------------------------------------------------------------------===//
3402
3403int NoteTag::Kind = 0;
3404
3405void TagVisitor::Profile(llvm::FoldingSetNodeID &ID) const {
3406 static int Tag = 0;
3407 ID.AddPointer(Ptr: &Tag);
3408}
3409
3410PathDiagnosticPieceRef TagVisitor::VisitNode(const ExplodedNode *N,
3411 BugReporterContext &BRC,
3412 PathSensitiveBugReport &R) {
3413 ProgramPoint PP = N->getLocation();
3414 const NoteTag *T = dyn_cast_or_null<NoteTag>(Val: PP.getTag());
3415 if (!T)
3416 return nullptr;
3417
3418 if (std::optional<std::string> Msg = T->generateMessage(BRC, R)) {
3419 PathDiagnosticLocation Loc =
3420 PathDiagnosticLocation::create(P: PP, SMng: BRC.getSourceManager());
3421 auto Piece = std::make_shared<PathDiagnosticEventPiece>(args&: Loc, args&: *Msg);
3422 Piece->setPrunable(isPrunable: T->isPrunable());
3423 return Piece;
3424 }
3425
3426 return nullptr;
3427}
3428