/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | //===-- NullabilityChecker.cpp - Nullability checker ----------------------===// |
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 checker tries to find nullability violations. There are several kinds of |
10 | | // possible violations: |
11 | | // * Null pointer is passed to a pointer which has a _Nonnull type. |
12 | | // * Null pointer is returned from a function which has a _Nonnull return type. |
13 | | // * Nullable pointer is passed to a pointer which has a _Nonnull type. |
14 | | // * Nullable pointer is returned from a function which has a _Nonnull return |
15 | | // type. |
16 | | // * Nullable pointer is dereferenced. |
17 | | // |
18 | | // This checker propagates the nullability information of the pointers and looks |
19 | | // for the patterns that are described above. Explicit casts are trusted and are |
20 | | // considered a way to suppress false positives for this checker. The other way |
21 | | // to suppress warnings would be to add asserts or guarding if statements to the |
22 | | // code. In addition to the nullability propagation this checker also uses some |
23 | | // heuristics to suppress potential false positives. |
24 | | // |
25 | | //===----------------------------------------------------------------------===// |
26 | | |
27 | | #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" |
28 | | |
29 | | #include "clang/Analysis/AnyCall.h" |
30 | | #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" |
31 | | #include "clang/StaticAnalyzer/Core/Checker.h" |
32 | | #include "clang/StaticAnalyzer/Core/CheckerManager.h" |
33 | | #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" |
34 | | #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" |
35 | | #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h" |
36 | | |
37 | | #include "llvm/ADT/STLExtras.h" |
38 | | #include "llvm/ADT/StringExtras.h" |
39 | | #include "llvm/Support/Path.h" |
40 | | |
41 | | using namespace clang; |
42 | | using namespace ento; |
43 | | |
44 | | namespace { |
45 | | |
46 | | /// Returns the most nullable nullability. This is used for message expressions |
47 | | /// like [receiver method], where the nullability of this expression is either |
48 | | /// the nullability of the receiver or the nullability of the return type of the |
49 | | /// method, depending on which is more nullable. Contradicted is considered to |
50 | | /// be the most nullable, to avoid false positive results. |
51 | 214 | Nullability getMostNullable(Nullability Lhs, Nullability Rhs) { |
52 | 214 | return static_cast<Nullability>( |
53 | 214 | std::min(static_cast<char>(Lhs), static_cast<char>(Rhs))); |
54 | 214 | } |
55 | | |
56 | 78 | const char *getNullabilityString(Nullability Nullab) { |
57 | 78 | switch (Nullab) { |
58 | 0 | case Nullability::Contradicted: |
59 | 0 | return "contradicted"; |
60 | 78 | case Nullability::Nullable: |
61 | 78 | return "nullable"; |
62 | 0 | case Nullability::Unspecified: |
63 | 0 | return "unspecified"; |
64 | 0 | case Nullability::Nonnull: |
65 | 0 | return "nonnull"; |
66 | 78 | } |
67 | 0 | llvm_unreachable("Unexpected enumeration."); |
68 | 0 | return ""; |
69 | 78 | } |
70 | | |
71 | | // These enums are used as an index to ErrorMessages array. |
72 | | enum class ErrorKind : int { |
73 | | NilAssignedToNonnull, |
74 | | NilPassedToNonnull, |
75 | | NilReturnedToNonnull, |
76 | | NullableAssignedToNonnull, |
77 | | NullableReturnedToNonnull, |
78 | | NullableDereferenced, |
79 | | NullablePassedToNonnull |
80 | | }; |
81 | | |
82 | | class NullabilityChecker |
83 | | : public Checker<check::Bind, check::PreCall, check::PreStmt<ReturnStmt>, |
84 | | check::PostCall, check::PostStmt<ExplicitCastExpr>, |
85 | | check::PostObjCMessage, check::DeadSymbols, eval::Assume, |
86 | | check::Location, check::Event<ImplicitNullDerefEvent>, |
87 | | check::BeginFunction> { |
88 | | |
89 | | public: |
90 | | // If true, the checker will not diagnose nullabilility issues for calls |
91 | | // to system headers. This option is motivated by the observation that large |
92 | | // projects may have many nullability warnings. These projects may |
93 | | // find warnings about nullability annotations that they have explicitly |
94 | | // added themselves higher priority to fix than warnings on calls to system |
95 | | // libraries. |
96 | | bool NoDiagnoseCallsToSystemHeaders = false; |
97 | | |
98 | | void checkBind(SVal L, SVal V, const Stmt *S, CheckerContext &C) const; |
99 | | void checkPostStmt(const ExplicitCastExpr *CE, CheckerContext &C) const; |
100 | | void checkPreStmt(const ReturnStmt *S, CheckerContext &C) const; |
101 | | void checkPostObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const; |
102 | | void checkPostCall(const CallEvent &Call, CheckerContext &C) const; |
103 | | void checkPreCall(const CallEvent &Call, CheckerContext &C) const; |
104 | | void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const; |
105 | | void checkEvent(ImplicitNullDerefEvent Event) const; |
106 | | void checkLocation(SVal Location, bool IsLoad, const Stmt *S, |
107 | | CheckerContext &C) const; |
108 | | void checkBeginFunction(CheckerContext &Ctx) const; |
109 | | ProgramStateRef evalAssume(ProgramStateRef State, SVal Cond, |
110 | | bool Assumption) const; |
111 | | |
112 | | void printState(raw_ostream &Out, ProgramStateRef State, const char *NL, |
113 | | const char *Sep) const override; |
114 | | |
115 | | enum CheckKind { |
116 | | CK_NullPassedToNonnull, |
117 | | CK_NullReturnedFromNonnull, |
118 | | CK_NullableDereferenced, |
119 | | CK_NullablePassedToNonnull, |
120 | | CK_NullableReturnedFromNonnull, |
121 | | CK_NumCheckKinds |
122 | | }; |
123 | | |
124 | | bool ChecksEnabled[CK_NumCheckKinds] = {false}; |
125 | | CheckerNameRef CheckNames[CK_NumCheckKinds]; |
126 | | mutable std::unique_ptr<BugType> BTs[CK_NumCheckKinds]; |
127 | | |
128 | 206 | const std::unique_ptr<BugType> &getBugType(CheckKind Kind) const { |
129 | 206 | if (!BTs[Kind]) |
130 | 36 | BTs[Kind].reset(new BugType(CheckNames[Kind], "Nullability", |
131 | 36 | categories::MemoryError)); |
132 | 206 | return BTs[Kind]; |
133 | 206 | } |
134 | | |
135 | | // When set to false no nullability information will be tracked in |
136 | | // NullabilityMap. It is possible to catch errors like passing a null pointer |
137 | | // to a callee that expects nonnull argument without the information that is |
138 | | // stored in the NullabilityMap. This is an optimization. |
139 | | bool NeedTracking = false; |
140 | | |
141 | | private: |
142 | | class NullabilityBugVisitor : public BugReporterVisitor { |
143 | | public: |
144 | 110 | NullabilityBugVisitor(const MemRegion *M) : Region(M) {} |
145 | | |
146 | 110 | void Profile(llvm::FoldingSetNodeID &ID) const override { |
147 | 110 | static int X = 0; |
148 | 110 | ID.AddPointer(&X); |
149 | 110 | ID.AddPointer(Region); |
150 | 110 | } |
151 | | |
152 | | PathDiagnosticPieceRef VisitNode(const ExplodedNode *N, |
153 | | BugReporterContext &BRC, |
154 | | PathSensitiveBugReport &BR) override; |
155 | | |
156 | | private: |
157 | | // The tracked region. |
158 | | const MemRegion *Region; |
159 | | }; |
160 | | |
161 | | /// When any of the nonnull arguments of the analyzed function is null, do not |
162 | | /// report anything and turn off the check. |
163 | | /// |
164 | | /// When \p SuppressPath is set to true, no more bugs will be reported on this |
165 | | /// path by this checker. |
166 | | void reportBugIfInvariantHolds(StringRef Msg, ErrorKind Error, CheckKind CK, |
167 | | ExplodedNode *N, const MemRegion *Region, |
168 | | CheckerContext &C, |
169 | | const Stmt *ValueExpr = nullptr, |
170 | | bool SuppressPath = false) const; |
171 | | |
172 | | void reportBug(StringRef Msg, ErrorKind Error, CheckKind CK, ExplodedNode *N, |
173 | | const MemRegion *Region, BugReporter &BR, |
174 | 206 | const Stmt *ValueExpr = nullptr) const { |
175 | 206 | const std::unique_ptr<BugType> &BT = getBugType(CK); |
176 | 206 | auto R = std::make_unique<PathSensitiveBugReport>(*BT, Msg, N); |
177 | 206 | if (Region) { |
178 | 110 | R->markInteresting(Region); |
179 | 110 | R->addVisitor<NullabilityBugVisitor>(Region); |
180 | 110 | } |
181 | 206 | if (ValueExpr) { |
182 | 167 | R->addRange(ValueExpr->getSourceRange()); |
183 | 167 | if (Error == ErrorKind::NilAssignedToNonnull || |
184 | 167 | Error == ErrorKind::NilPassedToNonnull151 || |
185 | 167 | Error == ErrorKind::NilReturnedToNonnull95 ) |
186 | 96 | if (const auto *Ex = dyn_cast<Expr>(ValueExpr)) |
187 | 96 | bugreporter::trackExpressionValue(N, Ex, *R); |
188 | 167 | } |
189 | 206 | BR.emitReport(std::move(R)); |
190 | 206 | } |
191 | | |
192 | | /// If an SVal wraps a region that should be tracked, it will return a pointer |
193 | | /// to the wrapped region. Otherwise it will return a nullptr. |
194 | | const SymbolicRegion *getTrackRegion(SVal Val, |
195 | | bool CheckSuperRegion = false) const; |
196 | | |
197 | | /// Returns true if the call is diagnosable in the current analyzer |
198 | | /// configuration. |
199 | 133 | bool isDiagnosableCall(const CallEvent &Call) const { |
200 | 133 | if (NoDiagnoseCallsToSystemHeaders && Call.isInSystemHeader()59 ) |
201 | 6 | return false; |
202 | | |
203 | 127 | return true; |
204 | 133 | } |
205 | | }; |
206 | | |
207 | | class NullabilityState { |
208 | | public: |
209 | | NullabilityState(Nullability Nullab, const Stmt *Source = nullptr) |
210 | 155 | : Nullab(Nullab), Source(Source) {} |
211 | | |
212 | 180 | const Stmt *getNullabilitySource() const { return Source; } |
213 | | |
214 | 5.35k | Nullability getValue() const { return Nullab; } |
215 | | |
216 | 203 | void Profile(llvm::FoldingSetNodeID &ID) const { |
217 | 203 | ID.AddInteger(static_cast<char>(Nullab)); |
218 | 203 | ID.AddPointer(Source); |
219 | 203 | } |
220 | | |
221 | 0 | void print(raw_ostream &Out) const { |
222 | 0 | Out << getNullabilityString(Nullab) << "\n"; |
223 | 0 | } |
224 | | |
225 | | private: |
226 | | Nullability Nullab; |
227 | | // Source is the expression which determined the nullability. For example in a |
228 | | // message like [nullable nonnull_returning] has nullable nullability, because |
229 | | // the receiver is nullable. Here the receiver will be the source of the |
230 | | // nullability. This is useful information when the diagnostics are generated. |
231 | | const Stmt *Source; |
232 | | }; |
233 | | |
234 | 35 | bool operator==(NullabilityState Lhs, NullabilityState Rhs) { |
235 | 35 | return Lhs.getValue() == Rhs.getValue() && |
236 | 35 | Lhs.getNullabilitySource() == Rhs.getNullabilitySource(); |
237 | 35 | } |
238 | | |
239 | | // For the purpose of tracking historical property accesses, the key for lookup |
240 | | // is an object pointer (could be an instance or a class) paired with the unique |
241 | | // identifier for the property being invoked on that object. |
242 | | using ObjectPropPair = std::pair<const MemRegion *, const IdentifierInfo *>; |
243 | | |
244 | | // Metadata associated with the return value from a recorded property access. |
245 | | struct ConstrainedPropertyVal { |
246 | | // This will reference the conjured return SVal for some call |
247 | | // of the form [object property] |
248 | | DefinedOrUnknownSVal Value; |
249 | | |
250 | | // If the SVal has been determined to be nonnull, that is recorded here |
251 | | bool isConstrainedNonnull; |
252 | | |
253 | | ConstrainedPropertyVal(DefinedOrUnknownSVal SV) |
254 | 44 | : Value(SV), isConstrainedNonnull(false) {} |
255 | | |
256 | 76 | void Profile(llvm::FoldingSetNodeID &ID) const { |
257 | 76 | Value.Profile(ID); |
258 | 76 | ID.AddInteger(isConstrainedNonnull ? 132 : 044 ); |
259 | 76 | } |
260 | | }; |
261 | | |
262 | | bool operator==(const ConstrainedPropertyVal &Lhs, |
263 | 4 | const ConstrainedPropertyVal &Rhs) { |
264 | 4 | return Lhs.Value == Rhs.Value && |
265 | 4 | Lhs.isConstrainedNonnull == Rhs.isConstrainedNonnull; |
266 | 4 | } |
267 | | |
268 | | } // end anonymous namespace |
269 | | |
270 | | REGISTER_MAP_WITH_PROGRAMSTATE(NullabilityMap, const MemRegion *, |
271 | | NullabilityState) |
272 | | REGISTER_MAP_WITH_PROGRAMSTATE(PropertyAccessesMap, ObjectPropPair, |
273 | | ConstrainedPropertyVal) |
274 | | |
275 | | // We say "the nullability type invariant is violated" when a location with a |
276 | | // non-null type contains NULL or a function with a non-null return type returns |
277 | | // NULL. Violations of the nullability type invariant can be detected either |
278 | | // directly (for example, when NULL is passed as an argument to a nonnull |
279 | | // parameter) or indirectly (for example, when, inside a function, the |
280 | | // programmer defensively checks whether a nonnull parameter contains NULL and |
281 | | // finds that it does). |
282 | | // |
283 | | // As a matter of policy, the nullability checker typically warns on direct |
284 | | // violations of the nullability invariant (although it uses various |
285 | | // heuristics to suppress warnings in some cases) but will not warn if the |
286 | | // invariant has already been violated along the path (either directly or |
287 | | // indirectly). As a practical matter, this prevents the analyzer from |
288 | | // (1) warning on defensive code paths where a nullability precondition is |
289 | | // determined to have been violated, (2) warning additional times after an |
290 | | // initial direct violation has been discovered, and (3) warning after a direct |
291 | | // violation that has been implicitly or explicitly suppressed (for |
292 | | // example, with a cast of NULL to _Nonnull). In essence, once an invariant |
293 | | // violation is detected on a path, this checker will be essentially turned off |
294 | | // for the rest of the analysis |
295 | | // |
296 | | // The analyzer takes this approach (rather than generating a sink node) to |
297 | | // ensure coverage of defensive paths, which may be important for backwards |
298 | | // compatibility in codebases that were developed without nullability in mind. |
299 | | REGISTER_TRAIT_WITH_PROGRAMSTATE(InvariantViolated, bool) |
300 | | |
301 | | enum class NullConstraint { IsNull, IsNotNull, Unknown }; |
302 | | |
303 | | static NullConstraint getNullConstraint(DefinedOrUnknownSVal Val, |
304 | 1.85k | ProgramStateRef State) { |
305 | 1.85k | ConditionTruthVal Nullness = State->isNull(Val); |
306 | 1.85k | if (Nullness.isConstrainedFalse()) |
307 | 713 | return NullConstraint::IsNotNull; |
308 | 1.14k | if (Nullness.isConstrainedTrue()) |
309 | 331 | return NullConstraint::IsNull; |
310 | 811 | return NullConstraint::Unknown; |
311 | 1.14k | } |
312 | | |
313 | 2.69k | static bool isValidPointerType(QualType T) { |
314 | 2.69k | return T->isAnyPointerType() || T->isBlockPointerType()741 ; |
315 | 2.69k | } |
316 | | |
317 | | const SymbolicRegion * |
318 | 1.55k | NullabilityChecker::getTrackRegion(SVal Val, bool CheckSuperRegion) const { |
319 | 1.55k | if (!NeedTracking) |
320 | 83 | return nullptr; |
321 | | |
322 | 1.47k | auto RegionSVal = Val.getAs<loc::MemRegionVal>(); |
323 | 1.47k | if (!RegionSVal) |
324 | 191 | return nullptr; |
325 | | |
326 | 1.28k | const MemRegion *Region = RegionSVal->getRegion(); |
327 | | |
328 | 1.28k | if (CheckSuperRegion) { |
329 | 49 | if (const SubRegion *FieldReg = Region->getAs<FieldRegion>()) { |
330 | 10 | if (const auto *ER = dyn_cast<ElementRegion>(FieldReg->getSuperRegion())) |
331 | 10 | FieldReg = ER; |
332 | 10 | return dyn_cast<SymbolicRegion>(FieldReg->getSuperRegion()); |
333 | 10 | } |
334 | 39 | if (auto ElementReg = Region->getAs<ElementRegion>()) |
335 | 0 | return dyn_cast<SymbolicRegion>(ElementReg->getSuperRegion()); |
336 | 39 | } |
337 | | |
338 | 1.27k | return dyn_cast<SymbolicRegion>(Region); |
339 | 1.28k | } |
340 | | |
341 | | PathDiagnosticPieceRef NullabilityChecker::NullabilityBugVisitor::VisitNode( |
342 | | const ExplodedNode *N, BugReporterContext &BRC, |
343 | 4.95k | PathSensitiveBugReport &BR) { |
344 | 4.95k | ProgramStateRef State = N->getState(); |
345 | 4.95k | ProgramStateRef StatePrev = N->getFirstPred()->getState(); |
346 | | |
347 | 4.95k | const NullabilityState *TrackedNullab = State->get<NullabilityMap>(Region); |
348 | 4.95k | const NullabilityState *TrackedNullabPrev = |
349 | 4.95k | StatePrev->get<NullabilityMap>(Region); |
350 | 4.95k | if (!TrackedNullab) |
351 | 2.38k | return nullptr; |
352 | | |
353 | 2.56k | if (TrackedNullabPrev && |
354 | 2.56k | TrackedNullabPrev->getValue() == TrackedNullab->getValue()2.45k ) |
355 | 2.45k | return nullptr; |
356 | | |
357 | | // Retrieve the associated statement. |
358 | 110 | const Stmt *S = TrackedNullab->getNullabilitySource(); |
359 | 110 | if (!S || S->getBeginLoc().isInvalid()34 ) { |
360 | 78 | S = N->getStmtForDiagnostics(); |
361 | 78 | } |
362 | | |
363 | 110 | if (!S) |
364 | 32 | return nullptr; |
365 | | |
366 | 78 | std::string InfoText = |
367 | 78 | (llvm::Twine("Nullability '") + |
368 | 78 | getNullabilityString(TrackedNullab->getValue()) + "' is inferred") |
369 | 78 | .str(); |
370 | | |
371 | | // Generate the extra diagnostic. |
372 | 78 | PathDiagnosticLocation Pos(S, BRC.getSourceManager(), |
373 | 78 | N->getLocationContext()); |
374 | 78 | return std::make_shared<PathDiagnosticEventPiece>(Pos, InfoText, true); |
375 | 110 | } |
376 | | |
377 | | /// Returns true when the value stored at the given location has been |
378 | | /// constrained to null after being passed through an object of nonnnull type. |
379 | | static bool checkValueAtLValForInvariantViolation(ProgramStateRef State, |
380 | 2.33k | SVal LV, QualType T) { |
381 | 2.33k | if (getNullabilityAnnotation(T) != Nullability::Nonnull) |
382 | 1.83k | return false; |
383 | | |
384 | 499 | auto RegionVal = LV.getAs<loc::MemRegionVal>(); |
385 | 499 | if (!RegionVal) |
386 | 0 | return false; |
387 | | |
388 | | // If the value was constrained to null *after* it was passed through that |
389 | | // location, it could not have been a concrete pointer *when* it was passed. |
390 | | // In that case we would have handled the situation when the value was |
391 | | // bound to that location, by emitting (or not emitting) a report. |
392 | | // Therefore we are only interested in symbolic regions that can be either |
393 | | // null or non-null depending on the value of their respective symbol. |
394 | 499 | auto StoredVal = State->getSVal(*RegionVal).getAs<loc::MemRegionVal>(); |
395 | 499 | if (!StoredVal || !isa<SymbolicRegion>(StoredVal->getRegion())) |
396 | 0 | return false; |
397 | | |
398 | 499 | if (getNullConstraint(*StoredVal, State) == NullConstraint::IsNull) |
399 | 0 | return true; |
400 | | |
401 | 499 | return false; |
402 | 499 | } |
403 | | |
404 | | static bool |
405 | | checkParamsForPreconditionViolation(ArrayRef<ParmVarDecl *> Params, |
406 | | ProgramStateRef State, |
407 | 3.58k | const LocationContext *LocCtxt) { |
408 | 3.58k | for (const auto *ParamDecl : Params) { |
409 | 2.09k | if (ParamDecl->isParameterPack()) |
410 | 0 | break; |
411 | | |
412 | 2.09k | SVal LV = State->getLValue(ParamDecl, LocCtxt); |
413 | 2.09k | if (checkValueAtLValForInvariantViolation(State, LV, |
414 | 2.09k | ParamDecl->getType())) { |
415 | 0 | return true; |
416 | 0 | } |
417 | 2.09k | } |
418 | 3.58k | return false; |
419 | 3.58k | } |
420 | | |
421 | | static bool |
422 | | checkSelfIvarsForInvariantViolation(ProgramStateRef State, |
423 | 3.58k | const LocationContext *LocCtxt) { |
424 | 3.58k | auto *MD = dyn_cast<ObjCMethodDecl>(LocCtxt->getDecl()); |
425 | 3.58k | if (!MD || !MD->isInstanceMethod()754 ) |
426 | 2.84k | return false; |
427 | | |
428 | 739 | const ImplicitParamDecl *SelfDecl = LocCtxt->getSelfDecl(); |
429 | 739 | if (!SelfDecl) |
430 | 0 | return false; |
431 | | |
432 | 739 | SVal SelfVal = State->getSVal(State->getRegion(SelfDecl, LocCtxt)); |
433 | | |
434 | 739 | const ObjCObjectPointerType *SelfType = |
435 | 739 | dyn_cast<ObjCObjectPointerType>(SelfDecl->getType()); |
436 | 739 | if (!SelfType) |
437 | 0 | return false; |
438 | | |
439 | 739 | const ObjCInterfaceDecl *ID = SelfType->getInterfaceDecl(); |
440 | 739 | if (!ID) |
441 | 0 | return false; |
442 | | |
443 | 739 | for (const auto *IvarDecl : ID->ivars()) { |
444 | 244 | SVal LV = State->getLValue(IvarDecl, SelfVal); |
445 | 244 | if (checkValueAtLValForInvariantViolation(State, LV, IvarDecl->getType())) { |
446 | 0 | return true; |
447 | 0 | } |
448 | 244 | } |
449 | 739 | return false; |
450 | 739 | } |
451 | | |
452 | | static bool checkInvariantViolation(ProgramStateRef State, ExplodedNode *N, |
453 | 4.01k | CheckerContext &C) { |
454 | 4.01k | if (State->get<InvariantViolated>()) |
455 | 432 | return true; |
456 | | |
457 | 3.58k | const LocationContext *LocCtxt = C.getLocationContext(); |
458 | 3.58k | const Decl *D = LocCtxt->getDecl(); |
459 | 3.58k | if (!D) |
460 | 0 | return false; |
461 | | |
462 | 3.58k | ArrayRef<ParmVarDecl*> Params; |
463 | 3.58k | if (const auto *BD = dyn_cast<BlockDecl>(D)) |
464 | 18 | Params = BD->parameters(); |
465 | 3.56k | else if (const auto *FD = dyn_cast<FunctionDecl>(D)) |
466 | 2.81k | Params = FD->parameters(); |
467 | 754 | else if (const auto *MD = dyn_cast<ObjCMethodDecl>(D)) |
468 | 754 | Params = MD->parameters(); |
469 | 0 | else |
470 | 0 | return false; |
471 | | |
472 | 3.58k | if (checkParamsForPreconditionViolation(Params, State, LocCtxt) || |
473 | 3.58k | checkSelfIvarsForInvariantViolation(State, LocCtxt)) { |
474 | 0 | if (!N->isSink()) |
475 | 0 | C.addTransition(State->set<InvariantViolated>(true), N); |
476 | 0 | return true; |
477 | 0 | } |
478 | 3.58k | return false; |
479 | 3.58k | } |
480 | | |
481 | | void NullabilityChecker::reportBugIfInvariantHolds( |
482 | | StringRef Msg, ErrorKind Error, CheckKind CK, ExplodedNode *N, |
483 | | const MemRegion *Region, CheckerContext &C, const Stmt *ValueExpr, |
484 | 179 | bool SuppressPath) const { |
485 | 179 | ProgramStateRef OriginalState = N->getState(); |
486 | | |
487 | 179 | if (checkInvariantViolation(OriginalState, N, C)) |
488 | 0 | return; |
489 | 179 | if (SuppressPath) { |
490 | 71 | OriginalState = OriginalState->set<InvariantViolated>(true); |
491 | 71 | N = C.addTransition(OriginalState, N); |
492 | 71 | } |
493 | | |
494 | 179 | reportBug(Msg, Error, CK, N, Region, C.getBugReporter(), ValueExpr); |
495 | 179 | } |
496 | | |
497 | | /// Cleaning up the program state. |
498 | | void NullabilityChecker::checkDeadSymbols(SymbolReaper &SR, |
499 | 3.83k | CheckerContext &C) const { |
500 | 3.83k | ProgramStateRef State = C.getState(); |
501 | 3.83k | NullabilityMapTy Nullabilities = State->get<NullabilityMap>(); |
502 | 3.83k | for (const MemRegion *Reg : llvm::make_first_range(Nullabilities)) { |
503 | 1.13k | const auto *Region = Reg->getAs<SymbolicRegion>(); |
504 | 1.13k | assert(Region && "Non-symbolic region is tracked."); |
505 | 1.13k | if (SR.isDead(Region->getSymbol())) { |
506 | 384 | State = State->remove<NullabilityMap>(Reg); |
507 | 384 | } |
508 | 1.13k | } |
509 | | |
510 | | // When an object goes out of scope, we can free the history associated |
511 | | // with any property accesses on that object |
512 | 3.83k | PropertyAccessesMapTy PropertyAccesses = State->get<PropertyAccessesMap>(); |
513 | 3.83k | for (ObjectPropPair PropKey : llvm::make_first_range(PropertyAccesses)) { |
514 | 124 | const MemRegion *ReceiverRegion = PropKey.first; |
515 | 124 | if (!SR.isLiveRegion(ReceiverRegion)) { |
516 | 40 | State = State->remove<PropertyAccessesMap>(PropKey); |
517 | 40 | } |
518 | 124 | } |
519 | | |
520 | | // When one of the nonnull arguments are constrained to be null, nullability |
521 | | // preconditions are violated. It is not enough to check this only when we |
522 | | // actually report an error, because at that time interesting symbols might be |
523 | | // reaped. |
524 | 3.83k | if (checkInvariantViolation(State, C.getPredecessor(), C)) |
525 | 432 | return; |
526 | 3.40k | C.addTransition(State); |
527 | 3.40k | } |
528 | | |
529 | | /// This callback triggers when a pointer is dereferenced and the analyzer does |
530 | | /// not know anything about the value of that pointer. When that pointer is |
531 | | /// nullable, this code emits a warning. |
532 | 49 | void NullabilityChecker::checkEvent(ImplicitNullDerefEvent Event) const { |
533 | 49 | if (Event.SinkNode->getState()->get<InvariantViolated>()) |
534 | 0 | return; |
535 | | |
536 | 49 | const MemRegion *Region = |
537 | 49 | getTrackRegion(Event.Location, /*CheckSuperRegion=*/true); |
538 | 49 | if (!Region) |
539 | 2 | return; |
540 | | |
541 | 47 | ProgramStateRef State = Event.SinkNode->getState(); |
542 | 47 | const NullabilityState *TrackedNullability = |
543 | 47 | State->get<NullabilityMap>(Region); |
544 | | |
545 | 47 | if (!TrackedNullability) |
546 | 20 | return; |
547 | | |
548 | 27 | if (ChecksEnabled[CK_NullableDereferenced] && |
549 | 27 | TrackedNullability->getValue() == Nullability::Nullable) { |
550 | 27 | BugReporter &BR = *Event.BR; |
551 | | // Do not suppress errors on defensive code paths, because dereferencing |
552 | | // a nullable pointer is always an error. |
553 | 27 | if (Event.IsDirectDereference) |
554 | 20 | reportBug("Nullable pointer is dereferenced", |
555 | 20 | ErrorKind::NullableDereferenced, CK_NullableDereferenced, |
556 | 20 | Event.SinkNode, Region, BR); |
557 | 7 | else { |
558 | 7 | reportBug("Nullable pointer is passed to a callee that requires a " |
559 | 7 | "non-null", |
560 | 7 | ErrorKind::NullablePassedToNonnull, CK_NullableDereferenced, |
561 | 7 | Event.SinkNode, Region, BR); |
562 | 7 | } |
563 | 27 | } |
564 | 27 | } |
565 | | |
566 | 486 | void NullabilityChecker::checkBeginFunction(CheckerContext &C) const { |
567 | 486 | if (!C.inTopFrame()) |
568 | 144 | return; |
569 | | |
570 | 342 | const LocationContext *LCtx = C.getLocationContext(); |
571 | 342 | auto AbstractCall = AnyCall::forDecl(LCtx->getDecl()); |
572 | 342 | if (!AbstractCall || AbstractCall->parameters().empty()340 ) |
573 | 188 | return; |
574 | | |
575 | 154 | ProgramStateRef State = C.getState(); |
576 | 181 | for (const ParmVarDecl *Param : AbstractCall->parameters()) { |
577 | 181 | if (!isValidPointerType(Param->getType())) |
578 | 18 | continue; |
579 | | |
580 | 163 | Nullability RequiredNullability = |
581 | 163 | getNullabilityAnnotation(Param->getType()); |
582 | 163 | if (RequiredNullability != Nullability::Nullable) |
583 | 139 | continue; |
584 | | |
585 | 24 | const VarRegion *ParamRegion = State->getRegion(Param, LCtx); |
586 | 24 | const MemRegion *ParamPointeeRegion = |
587 | 24 | State->getSVal(ParamRegion).getAsRegion(); |
588 | 24 | if (!ParamPointeeRegion) |
589 | 0 | continue; |
590 | | |
591 | 24 | State = State->set<NullabilityMap>(ParamPointeeRegion, |
592 | 24 | NullabilityState(RequiredNullability)); |
593 | 24 | } |
594 | 154 | C.addTransition(State); |
595 | 154 | } |
596 | | |
597 | | // Whenever we see a load from a typed memory region that's been annotated as |
598 | | // 'nonnull', we want to trust the user on that and assume that it is is indeed |
599 | | // non-null. |
600 | | // |
601 | | // We do so even if the value is known to have been assigned to null. |
602 | | // The user should be warned on assigning the null value to a non-null pointer |
603 | | // as opposed to warning on the later dereference of this pointer. |
604 | | // |
605 | | // \code |
606 | | // int * _Nonnull var = 0; // we want to warn the user here... |
607 | | // // . . . |
608 | | // *var = 42; // ...and not here |
609 | | // \endcode |
610 | | void NullabilityChecker::checkLocation(SVal Location, bool IsLoad, |
611 | | const Stmt *S, |
612 | 1.74k | CheckerContext &Context) const { |
613 | | // We should care only about loads. |
614 | | // The main idea is to add a constraint whenever we're loading a value from |
615 | | // an annotated pointer type. |
616 | 1.74k | if (!IsLoad) |
617 | 219 | return; |
618 | | |
619 | | // Annotations that we want to consider make sense only for types. |
620 | 1.52k | const auto *Region = |
621 | 1.52k | dyn_cast_or_null<TypedValueRegion>(Location.getAsRegion()); |
622 | 1.52k | if (!Region) |
623 | 5 | return; |
624 | | |
625 | 1.51k | ProgramStateRef State = Context.getState(); |
626 | | |
627 | 1.51k | auto StoredVal = State->getSVal(Region).getAs<loc::MemRegionVal>(); |
628 | 1.51k | if (!StoredVal) |
629 | 191 | return; |
630 | | |
631 | 1.32k | Nullability NullabilityOfTheLoadedValue = |
632 | 1.32k | getNullabilityAnnotation(Region->getValueType()); |
633 | | |
634 | 1.32k | if (NullabilityOfTheLoadedValue == Nullability::Nonnull) { |
635 | | // It doesn't matter what we think about this particular pointer, it should |
636 | | // be considered non-null as annotated by the developer. |
637 | 186 | if (ProgramStateRef NewState = State->assume(*StoredVal, true)) { |
638 | 186 | Context.addTransition(NewState); |
639 | 186 | } |
640 | 186 | } |
641 | 1.32k | } |
642 | | |
643 | | /// Find the outermost subexpression of E that is not an implicit cast. |
644 | | /// This looks through the implicit casts to _Nonnull that ARC adds to |
645 | | /// return expressions of ObjC types when the return type of the function or |
646 | | /// method is non-null but the express is not. |
647 | 1.16k | static const Expr *lookThroughImplicitCasts(const Expr *E) { |
648 | 1.16k | return E->IgnoreImpCasts(); |
649 | 1.16k | } |
650 | | |
651 | | /// This method check when nullable pointer or null value is returned from a |
652 | | /// function that has nonnull return type. |
653 | | void NullabilityChecker::checkPreStmt(const ReturnStmt *S, |
654 | 317 | CheckerContext &C) const { |
655 | 317 | auto RetExpr = S->getRetValue(); |
656 | 317 | if (!RetExpr) |
657 | 20 | return; |
658 | | |
659 | 297 | if (!isValidPointerType(RetExpr->getType())) |
660 | 18 | return; |
661 | | |
662 | 279 | ProgramStateRef State = C.getState(); |
663 | 279 | if (State->get<InvariantViolated>()) |
664 | 20 | return; |
665 | | |
666 | 259 | auto RetSVal = C.getSVal(S).getAs<DefinedOrUnknownSVal>(); |
667 | 259 | if (!RetSVal) |
668 | 0 | return; |
669 | | |
670 | 259 | bool InSuppressedMethodFamily = false; |
671 | | |
672 | 259 | QualType RequiredRetType; |
673 | 259 | AnalysisDeclContext *DeclCtxt = |
674 | 259 | C.getLocationContext()->getAnalysisDeclContext(); |
675 | 259 | const Decl *D = DeclCtxt->getDecl(); |
676 | 259 | if (auto *MD = dyn_cast<ObjCMethodDecl>(D)) { |
677 | | // HACK: This is a big hammer to avoid warning when there are defensive |
678 | | // nil checks in -init and -copy methods. We should add more sophisticated |
679 | | // logic here to suppress on common defensive idioms but still |
680 | | // warn when there is a likely problem. |
681 | 129 | ObjCMethodFamily Family = MD->getMethodFamily(); |
682 | 129 | if (OMF_init == Family || OMF_copy == Family75 || OMF_mutableCopy == Family67 ) |
683 | 70 | InSuppressedMethodFamily = true; |
684 | | |
685 | 129 | RequiredRetType = MD->getReturnType(); |
686 | 130 | } else if (auto *FD = dyn_cast<FunctionDecl>(D)) { |
687 | 126 | RequiredRetType = FD->getReturnType(); |
688 | 126 | } else { |
689 | 4 | return; |
690 | 4 | } |
691 | | |
692 | 255 | NullConstraint Nullness = getNullConstraint(*RetSVal, State); |
693 | | |
694 | 255 | Nullability RequiredNullability = getNullabilityAnnotation(RequiredRetType); |
695 | | |
696 | | // If the returned value is null but the type of the expression |
697 | | // generating it is nonnull then we will suppress the diagnostic. |
698 | | // This enables explicit suppression when returning a nil literal in a |
699 | | // function with a _Nonnull return type: |
700 | | // return (NSString * _Nonnull)0; |
701 | 255 | Nullability RetExprTypeLevelNullability = |
702 | 255 | getNullabilityAnnotation(lookThroughImplicitCasts(RetExpr)->getType()); |
703 | | |
704 | 255 | bool NullReturnedFromNonNull = (RequiredNullability == Nullability::Nonnull && |
705 | 255 | Nullness == NullConstraint::IsNull185 ); |
706 | 255 | if (ChecksEnabled[CK_NullReturnedFromNonnull] && NullReturnedFromNonNull && |
707 | 255 | RetExprTypeLevelNullability != Nullability::Nonnull72 && |
708 | 255 | !InSuppressedMethodFamily56 && C.getLocationContext()->inTopFrame()28 ) { |
709 | 24 | static CheckerProgramPointTag Tag(this, "NullReturnedFromNonnull"); |
710 | 24 | ExplodedNode *N = C.generateErrorNode(State, &Tag); |
711 | 24 | if (!N) |
712 | 0 | return; |
713 | | |
714 | 24 | SmallString<256> SBuf; |
715 | 24 | llvm::raw_svector_ostream OS(SBuf); |
716 | 24 | OS << (RetExpr->getType()->isObjCObjectPointerType() ? "nil"8 : "Null"16 ); |
717 | 24 | OS << " returned from a " << C.getDeclDescription(D) << |
718 | 24 | " that is expected to return a non-null value"; |
719 | 24 | reportBugIfInvariantHolds(OS.str(), ErrorKind::NilReturnedToNonnull, |
720 | 24 | CK_NullReturnedFromNonnull, N, nullptr, C, |
721 | 24 | RetExpr); |
722 | 24 | return; |
723 | 24 | } |
724 | | |
725 | | // If null was returned from a non-null function, mark the nullability |
726 | | // invariant as violated even if the diagnostic was suppressed. |
727 | 231 | if (NullReturnedFromNonNull) { |
728 | 48 | State = State->set<InvariantViolated>(true); |
729 | 48 | C.addTransition(State); |
730 | 48 | return; |
731 | 48 | } |
732 | | |
733 | 183 | const MemRegion *Region = getTrackRegion(*RetSVal); |
734 | 183 | if (!Region) |
735 | 27 | return; |
736 | | |
737 | 156 | const NullabilityState *TrackedNullability = |
738 | 156 | State->get<NullabilityMap>(Region); |
739 | 156 | if (TrackedNullability) { |
740 | 36 | Nullability TrackedNullabValue = TrackedNullability->getValue(); |
741 | 36 | if (ChecksEnabled[CK_NullableReturnedFromNonnull] && |
742 | 36 | Nullness != NullConstraint::IsNotNull && |
743 | 36 | TrackedNullabValue == Nullability::Nullable && |
744 | 36 | RequiredNullability == Nullability::Nonnull24 ) { |
745 | 8 | static CheckerProgramPointTag Tag(this, "NullableReturnedFromNonnull"); |
746 | 8 | ExplodedNode *N = C.addTransition(State, C.getPredecessor(), &Tag); |
747 | | |
748 | 8 | SmallString<256> SBuf; |
749 | 8 | llvm::raw_svector_ostream OS(SBuf); |
750 | 8 | OS << "Nullable pointer is returned from a " << C.getDeclDescription(D) << |
751 | 8 | " that is expected to return a non-null value"; |
752 | | |
753 | 8 | reportBugIfInvariantHolds(OS.str(), ErrorKind::NullableReturnedToNonnull, |
754 | 8 | CK_NullableReturnedFromNonnull, N, Region, C); |
755 | 8 | } |
756 | 36 | return; |
757 | 36 | } |
758 | 120 | if (RequiredNullability == Nullability::Nullable) { |
759 | 2 | State = State->set<NullabilityMap>(Region, |
760 | 2 | NullabilityState(RequiredNullability, |
761 | 2 | S)); |
762 | 2 | C.addTransition(State); |
763 | 2 | } |
764 | 120 | } |
765 | | |
766 | | /// This callback warns when a nullable pointer or a null value is passed to a |
767 | | /// function that expects its argument to be nonnull. |
768 | | void NullabilityChecker::checkPreCall(const CallEvent &Call, |
769 | 974 | CheckerContext &C) const { |
770 | 974 | if (!Call.getDecl()) |
771 | 0 | return; |
772 | | |
773 | 974 | ProgramStateRef State = C.getState(); |
774 | 974 | if (State->get<InvariantViolated>()) |
775 | 85 | return; |
776 | | |
777 | 889 | ProgramStateRef OrigState = State; |
778 | | |
779 | 889 | unsigned Idx = 0; |
780 | 889 | for (const ParmVarDecl *Param : Call.parameters()) { |
781 | 509 | if (Param->isParameterPack()) |
782 | 0 | break; |
783 | | |
784 | 509 | if (Idx >= Call.getNumArgs()) |
785 | 0 | break; |
786 | | |
787 | 509 | const Expr *ArgExpr = Call.getArgExpr(Idx); |
788 | 509 | auto ArgSVal = Call.getArgSVal(Idx++).getAs<DefinedOrUnknownSVal>(); |
789 | 509 | if (!ArgSVal) |
790 | 0 | continue; |
791 | | |
792 | 509 | if (!isValidPointerType(Param->getType()) && |
793 | 509 | !Param->getType()->isReferenceType()43 ) |
794 | 33 | continue; |
795 | | |
796 | 476 | NullConstraint Nullness = getNullConstraint(*ArgSVal, State); |
797 | | |
798 | 476 | Nullability RequiredNullability = |
799 | 476 | getNullabilityAnnotation(Param->getType()); |
800 | 476 | Nullability ArgExprTypeLevelNullability = |
801 | 476 | getNullabilityAnnotation(lookThroughImplicitCasts(ArgExpr)->getType()); |
802 | | |
803 | 476 | unsigned ParamIdx = Param->getFunctionScopeIndex() + 1; |
804 | | |
805 | 476 | if (ChecksEnabled[CK_NullPassedToNonnull] && |
806 | 476 | Nullness == NullConstraint::IsNull && |
807 | 476 | ArgExprTypeLevelNullability != Nullability::Nonnull90 && |
808 | 476 | RequiredNullability == Nullability::Nonnull78 && |
809 | 476 | isDiagnosableCall(Call)58 ) { |
810 | 56 | ExplodedNode *N = C.generateErrorNode(State); |
811 | 56 | if (!N) |
812 | 0 | return; |
813 | | |
814 | 56 | SmallString<256> SBuf; |
815 | 56 | llvm::raw_svector_ostream OS(SBuf); |
816 | 56 | OS << (Param->getType()->isObjCObjectPointerType() ? "nil"20 : "Null"36 ); |
817 | 56 | OS << " passed to a callee that requires a non-null " << ParamIdx |
818 | 56 | << llvm::getOrdinalSuffix(ParamIdx) << " parameter"; |
819 | 56 | reportBugIfInvariantHolds(OS.str(), ErrorKind::NilPassedToNonnull, |
820 | 56 | CK_NullPassedToNonnull, N, nullptr, C, ArgExpr, |
821 | 56 | /*SuppressPath=*/false); |
822 | 56 | return; |
823 | 56 | } |
824 | | |
825 | 420 | const MemRegion *Region = getTrackRegion(*ArgSVal); |
826 | 420 | if (!Region) |
827 | 86 | continue; |
828 | | |
829 | 334 | const NullabilityState *TrackedNullability = |
830 | 334 | State->get<NullabilityMap>(Region); |
831 | | |
832 | 334 | if (TrackedNullability) { |
833 | 153 | if (Nullness == NullConstraint::IsNotNull || |
834 | 153 | TrackedNullability->getValue() != Nullability::Nullable123 ) |
835 | 38 | continue; |
836 | | |
837 | 115 | if (ChecksEnabled[CK_NullablePassedToNonnull] && |
838 | 115 | RequiredNullability == Nullability::Nonnull && |
839 | 115 | isDiagnosableCall(Call)75 ) { |
840 | 71 | ExplodedNode *N = C.addTransition(State); |
841 | 71 | SmallString<256> SBuf; |
842 | 71 | llvm::raw_svector_ostream OS(SBuf); |
843 | 71 | OS << "Nullable pointer is passed to a callee that requires a non-null " |
844 | 71 | << ParamIdx << llvm::getOrdinalSuffix(ParamIdx) << " parameter"; |
845 | 71 | reportBugIfInvariantHolds(OS.str(), ErrorKind::NullablePassedToNonnull, |
846 | 71 | CK_NullablePassedToNonnull, N, Region, C, |
847 | 71 | ArgExpr, /*SuppressPath=*/true); |
848 | 71 | return; |
849 | 71 | } |
850 | 44 | if (ChecksEnabled[CK_NullableDereferenced] && |
851 | 44 | Param->getType()->isReferenceType()) { |
852 | 0 | ExplodedNode *N = C.addTransition(State); |
853 | 0 | reportBugIfInvariantHolds("Nullable pointer is dereferenced", |
854 | 0 | ErrorKind::NullableDereferenced, |
855 | 0 | CK_NullableDereferenced, N, Region, C, |
856 | 0 | ArgExpr, /*SuppressPath=*/true); |
857 | 0 | return; |
858 | 0 | } |
859 | 44 | continue; |
860 | 44 | } |
861 | 334 | } |
862 | 762 | if (State != OrigState) |
863 | 0 | C.addTransition(State); |
864 | 762 | } |
865 | | |
866 | | /// Suppress the nullability warnings for some functions. |
867 | | void NullabilityChecker::checkPostCall(const CallEvent &Call, |
868 | 899 | CheckerContext &C) const { |
869 | 899 | auto Decl = Call.getDecl(); |
870 | 899 | if (!Decl) |
871 | 0 | return; |
872 | | // ObjC Messages handles in a different callback. |
873 | 899 | if (Call.getKind() == CE_ObjCMessage) |
874 | 347 | return; |
875 | 552 | const FunctionType *FuncType = Decl->getFunctionType(); |
876 | 552 | if (!FuncType) |
877 | 6 | return; |
878 | 546 | QualType ReturnType = FuncType->getReturnType(); |
879 | 546 | if (!isValidPointerType(ReturnType)) |
880 | 401 | return; |
881 | 145 | ProgramStateRef State = C.getState(); |
882 | 145 | if (State->get<InvariantViolated>()) |
883 | 6 | return; |
884 | | |
885 | 139 | const MemRegion *Region = getTrackRegion(Call.getReturnValue()); |
886 | 139 | if (!Region) |
887 | 8 | return; |
888 | | |
889 | | // CG headers are misannotated. Do not warn for symbols that are the results |
890 | | // of CG calls. |
891 | 131 | const SourceManager &SM = C.getSourceManager(); |
892 | 131 | StringRef FilePath = SM.getFilename(SM.getSpellingLoc(Decl->getBeginLoc())); |
893 | 131 | if (llvm::sys::path::filename(FilePath).startswith("CG")) { |
894 | 0 | State = State->set<NullabilityMap>(Region, Nullability::Contradicted); |
895 | 0 | C.addTransition(State); |
896 | 0 | return; |
897 | 0 | } |
898 | | |
899 | 131 | const NullabilityState *TrackedNullability = |
900 | 131 | State->get<NullabilityMap>(Region); |
901 | | |
902 | 131 | if (!TrackedNullability && |
903 | 131 | getNullabilityAnnotation(ReturnType) == Nullability::Nullable115 ) { |
904 | 60 | State = State->set<NullabilityMap>(Region, Nullability::Nullable); |
905 | 60 | C.addTransition(State); |
906 | 60 | } |
907 | 131 | } |
908 | | |
909 | | static Nullability getReceiverNullability(const ObjCMethodCall &M, |
910 | 214 | ProgramStateRef State) { |
911 | 214 | if (M.isReceiverSelfOrSuper()) { |
912 | | // For super and super class receivers we assume that the receiver is |
913 | | // nonnull. |
914 | 44 | return Nullability::Nonnull; |
915 | 44 | } |
916 | | // Otherwise look up nullability in the state. |
917 | 170 | SVal Receiver = M.getReceiverSVal(); |
918 | 170 | if (auto DefOrUnknown = Receiver.getAs<DefinedOrUnknownSVal>()) { |
919 | | // If the receiver is constrained to be nonnull, assume that it is nonnull |
920 | | // regardless of its type. |
921 | 170 | NullConstraint Nullness = getNullConstraint(*DefOrUnknown, State); |
922 | 170 | if (Nullness == NullConstraint::IsNotNull) |
923 | 145 | return Nullability::Nonnull; |
924 | 170 | } |
925 | 25 | auto ValueRegionSVal = Receiver.getAs<loc::MemRegionVal>(); |
926 | 25 | if (ValueRegionSVal) { |
927 | 0 | const MemRegion *SelfRegion = ValueRegionSVal->getRegion(); |
928 | 0 | assert(SelfRegion); |
929 | | |
930 | 0 | const NullabilityState *TrackedSelfNullability = |
931 | 0 | State->get<NullabilityMap>(SelfRegion); |
932 | 0 | if (TrackedSelfNullability) |
933 | 0 | return TrackedSelfNullability->getValue(); |
934 | 0 | } |
935 | 25 | return Nullability::Unspecified; |
936 | 25 | } |
937 | | |
938 | | // The return value of a property access is typically a temporary value which |
939 | | // will not be tracked in a persistent manner by the analyzer. We use |
940 | | // evalAssume() in order to immediately record constraints on those temporaries |
941 | | // at the time they are imposed (e.g. by a nil-check conditional). |
942 | | ProgramStateRef NullabilityChecker::evalAssume(ProgramStateRef State, SVal Cond, |
943 | 4.23k | bool Assumption) const { |
944 | 4.23k | PropertyAccessesMapTy PropertyAccesses = State->get<PropertyAccessesMap>(); |
945 | 4.23k | for (auto [PropKey, PropVal] : PropertyAccesses) { |
946 | 168 | if (!PropVal.isConstrainedNonnull) { |
947 | 60 | ConditionTruthVal IsNonNull = State->isNonNull(PropVal.Value); |
948 | 60 | if (IsNonNull.isConstrainedTrue()) { |
949 | 24 | ConstrainedPropertyVal Replacement = PropVal; |
950 | 24 | Replacement.isConstrainedNonnull = true; |
951 | 24 | State = State->set<PropertyAccessesMap>(PropKey, Replacement); |
952 | 36 | } else if (IsNonNull.isConstrainedFalse()) { |
953 | | // Space optimization: no point in tracking constrained-null cases |
954 | 24 | State = State->remove<PropertyAccessesMap>(PropKey); |
955 | 24 | } |
956 | 60 | } |
957 | 168 | } |
958 | | |
959 | 4.23k | return State; |
960 | 4.23k | } |
961 | | |
962 | | /// Calculate the nullability of the result of a message expr based on the |
963 | | /// nullability of the receiver, the nullability of the return value, and the |
964 | | /// constraints. |
965 | | void NullabilityChecker::checkPostObjCMessage(const ObjCMethodCall &M, |
966 | 347 | CheckerContext &C) const { |
967 | 347 | auto Decl = M.getDecl(); |
968 | 347 | if (!Decl) |
969 | 0 | return; |
970 | 347 | QualType RetType = Decl->getReturnType(); |
971 | 347 | if (!isValidPointerType(RetType)) |
972 | 111 | return; |
973 | | |
974 | 236 | ProgramStateRef State = C.getState(); |
975 | 236 | if (State->get<InvariantViolated>()) |
976 | 12 | return; |
977 | | |
978 | 224 | const MemRegion *ReturnRegion = getTrackRegion(M.getReturnValue()); |
979 | 224 | if (!ReturnRegion) |
980 | 9 | return; |
981 | | |
982 | 215 | auto Interface = Decl->getClassInterface(); |
983 | 215 | auto Name = Interface ? Interface->getName()160 : ""55 ; |
984 | | // In order to reduce the noise in the diagnostics generated by this checker, |
985 | | // some framework and programming style based heuristics are used. These |
986 | | // heuristics are for Cocoa APIs which have NS prefix. |
987 | 215 | if (Name.startswith("NS")) { |
988 | | // Developers rely on dynamic invariants such as an item should be available |
989 | | // in a collection, or a collection is not empty often. Those invariants can |
990 | | // not be inferred by any static analysis tool. To not to bother the users |
991 | | // with too many false positives, every item retrieval function should be |
992 | | // ignored for collections. The instance methods of dictionaries in Cocoa |
993 | | // are either item retrieval related or not interesting nullability wise. |
994 | | // Using this fact, to keep the code easier to read just ignore the return |
995 | | // value of every instance method of dictionaries. |
996 | 22 | if (M.isInstanceMessage() && Name.contains("Dictionary")9 ) { |
997 | 1 | State = |
998 | 1 | State->set<NullabilityMap>(ReturnRegion, Nullability::Contradicted); |
999 | 1 | C.addTransition(State); |
1000 | 1 | return; |
1001 | 1 | } |
1002 | | // For similar reasons ignore some methods of Cocoa arrays. |
1003 | 21 | StringRef FirstSelectorSlot = M.getSelector().getNameForSlot(0); |
1004 | 21 | if (Name.contains("Array") && |
1005 | 21 | (0 FirstSelectorSlot == "firstObject"0 || |
1006 | 0 | FirstSelectorSlot == "lastObject")) { |
1007 | 0 | State = |
1008 | 0 | State->set<NullabilityMap>(ReturnRegion, Nullability::Contradicted); |
1009 | 0 | C.addTransition(State); |
1010 | 0 | return; |
1011 | 0 | } |
1012 | | |
1013 | | // Encoding related methods of string should not fail when lossless |
1014 | | // encodings are used. Using lossless encodings is so frequent that ignoring |
1015 | | // this class of methods reduced the emitted diagnostics by about 30% on |
1016 | | // some projects (and all of that was false positives). |
1017 | 21 | if (Name.contains("String")) { |
1018 | 0 | for (auto *Param : M.parameters()) { |
1019 | 0 | if (Param->getName() == "encoding") { |
1020 | 0 | State = State->set<NullabilityMap>(ReturnRegion, |
1021 | 0 | Nullability::Contradicted); |
1022 | 0 | C.addTransition(State); |
1023 | 0 | return; |
1024 | 0 | } |
1025 | 0 | } |
1026 | 0 | } |
1027 | 21 | } |
1028 | | |
1029 | 214 | const ObjCMessageExpr *Message = M.getOriginExpr(); |
1030 | 214 | Nullability SelfNullability = getReceiverNullability(M, State); |
1031 | | |
1032 | 214 | const NullabilityState *NullabilityOfReturn = |
1033 | 214 | State->get<NullabilityMap>(ReturnRegion); |
1034 | | |
1035 | 214 | if (NullabilityOfReturn) { |
1036 | | // When we have a nullability tracked for the return value, the nullability |
1037 | | // of the expression will be the most nullable of the receiver and the |
1038 | | // return value. |
1039 | 2 | Nullability RetValTracked = NullabilityOfReturn->getValue(); |
1040 | 2 | Nullability ComputedNullab = |
1041 | 2 | getMostNullable(RetValTracked, SelfNullability); |
1042 | 2 | if (ComputedNullab != RetValTracked && |
1043 | 2 | ComputedNullab != Nullability::Unspecified0 ) { |
1044 | 0 | const Stmt *NullabilitySource = |
1045 | 0 | ComputedNullab == RetValTracked |
1046 | 0 | ? NullabilityOfReturn->getNullabilitySource() |
1047 | 0 | : Message->getInstanceReceiver(); |
1048 | 0 | State = State->set<NullabilityMap>( |
1049 | 0 | ReturnRegion, NullabilityState(ComputedNullab, NullabilitySource)); |
1050 | 0 | C.addTransition(State); |
1051 | 0 | } |
1052 | 2 | return; |
1053 | 2 | } |
1054 | | |
1055 | | // No tracked information. Use static type information for return value. |
1056 | 212 | Nullability RetNullability = getNullabilityAnnotation(RetType); |
1057 | | |
1058 | | // Properties might be computed, which means the property value could |
1059 | | // theoretically change between calls even in commonly-observed cases like |
1060 | | // this: |
1061 | | // |
1062 | | // if (foo.prop) { // ok, it's nonnull here... |
1063 | | // [bar doStuffWithNonnullVal:foo.prop]; // ...but what about |
1064 | | // here? |
1065 | | // } |
1066 | | // |
1067 | | // If the property is nullable-annotated, a naive analysis would lead to many |
1068 | | // false positives despite the presence of probably-correct nil-checks. To |
1069 | | // reduce the false positive rate, we maintain a history of the most recently |
1070 | | // observed property value. For each property access, if the prior value has |
1071 | | // been constrained to be not nil then we will conservatively assume that the |
1072 | | // next access can be inferred as nonnull. |
1073 | 212 | if (RetNullability != Nullability::Nonnull && |
1074 | 212 | M.getMessageKind() == OCM_PropertyAccess134 && !C.wasInlined74 ) { |
1075 | 64 | bool LookupResolved = false; |
1076 | 64 | if (const MemRegion *ReceiverRegion = getTrackRegion(M.getReceiverSVal())) { |
1077 | 64 | if (IdentifierInfo *Ident = M.getSelector().getIdentifierInfoForSlot(0)) { |
1078 | 64 | LookupResolved = true; |
1079 | 64 | ObjectPropPair Key = std::make_pair(ReceiverRegion, Ident); |
1080 | 64 | const ConstrainedPropertyVal *PrevPropVal = |
1081 | 64 | State->get<PropertyAccessesMap>(Key); |
1082 | 64 | if (PrevPropVal && PrevPropVal->isConstrainedNonnull20 ) { |
1083 | 20 | RetNullability = Nullability::Nonnull; |
1084 | 44 | } else { |
1085 | | // If a previous property access was constrained as nonnull, we hold |
1086 | | // on to that constraint (effectively inferring that all subsequent |
1087 | | // accesses on that code path can be inferred as nonnull). If the |
1088 | | // previous property access was *not* constrained as nonnull, then |
1089 | | // let's throw it away in favor of keeping the SVal associated with |
1090 | | // this more recent access. |
1091 | 44 | if (auto ReturnSVal = |
1092 | 44 | M.getReturnValue().getAs<DefinedOrUnknownSVal>()) { |
1093 | 44 | State = State->set<PropertyAccessesMap>( |
1094 | 44 | Key, ConstrainedPropertyVal(*ReturnSVal)); |
1095 | 44 | } |
1096 | 44 | } |
1097 | 64 | } |
1098 | 64 | } |
1099 | | |
1100 | 64 | if (!LookupResolved) { |
1101 | | // Fallback: err on the side of suppressing the false positive. |
1102 | 0 | RetNullability = Nullability::Nonnull; |
1103 | 0 | } |
1104 | 64 | } |
1105 | | |
1106 | 212 | Nullability ComputedNullab = getMostNullable(RetNullability, SelfNullability); |
1107 | 212 | if (ComputedNullab == Nullability::Nullable) { |
1108 | 56 | const Stmt *NullabilitySource = ComputedNullab == RetNullability |
1109 | 56 | ? Message |
1110 | 56 | : Message->getInstanceReceiver()0 ; |
1111 | 56 | State = State->set<NullabilityMap>( |
1112 | 56 | ReturnRegion, NullabilityState(ComputedNullab, NullabilitySource)); |
1113 | 56 | C.addTransition(State); |
1114 | 56 | } |
1115 | 212 | } |
1116 | | |
1117 | | /// Explicit casts are trusted. If there is a disagreement in the nullability |
1118 | | /// annotations in the destination and the source or '0' is casted to nonnull |
1119 | | /// track the value as having contraditory nullability. This will allow users to |
1120 | | /// suppress warnings. |
1121 | | void NullabilityChecker::checkPostStmt(const ExplicitCastExpr *CE, |
1122 | 129 | CheckerContext &C) const { |
1123 | 129 | QualType OriginType = CE->getSubExpr()->getType(); |
1124 | 129 | QualType DestType = CE->getType(); |
1125 | 129 | if (!isValidPointerType(OriginType)) |
1126 | 17 | return; |
1127 | 112 | if (!isValidPointerType(DestType)) |
1128 | 22 | return; |
1129 | | |
1130 | 90 | ProgramStateRef State = C.getState(); |
1131 | 90 | if (State->get<InvariantViolated>()) |
1132 | 16 | return; |
1133 | | |
1134 | 74 | Nullability DestNullability = getNullabilityAnnotation(DestType); |
1135 | | |
1136 | | // No explicit nullability in the destination type, so this cast does not |
1137 | | // change the nullability. |
1138 | 74 | if (DestNullability == Nullability::Unspecified) |
1139 | 2 | return; |
1140 | | |
1141 | 72 | auto RegionSVal = C.getSVal(CE).getAs<DefinedOrUnknownSVal>(); |
1142 | 72 | const MemRegion *Region = getTrackRegion(*RegionSVal); |
1143 | 72 | if (!Region) |
1144 | 56 | return; |
1145 | | |
1146 | | // When 0 is converted to nonnull mark it as contradicted. |
1147 | 16 | if (DestNullability == Nullability::Nonnull) { |
1148 | 16 | NullConstraint Nullness = getNullConstraint(*RegionSVal, State); |
1149 | 16 | if (Nullness == NullConstraint::IsNull) { |
1150 | 0 | State = State->set<NullabilityMap>(Region, Nullability::Contradicted); |
1151 | 0 | C.addTransition(State); |
1152 | 0 | return; |
1153 | 0 | } |
1154 | 16 | } |
1155 | | |
1156 | 16 | const NullabilityState *TrackedNullability = |
1157 | 16 | State->get<NullabilityMap>(Region); |
1158 | | |
1159 | 16 | if (!TrackedNullability) { |
1160 | 0 | if (DestNullability != Nullability::Nullable) |
1161 | 0 | return; |
1162 | 0 | State = State->set<NullabilityMap>(Region, |
1163 | 0 | NullabilityState(DestNullability, CE)); |
1164 | 0 | C.addTransition(State); |
1165 | 0 | return; |
1166 | 0 | } |
1167 | | |
1168 | 16 | if (TrackedNullability->getValue() != DestNullability && |
1169 | 16 | TrackedNullability->getValue() != Nullability::Contradicted) { |
1170 | 12 | State = State->set<NullabilityMap>(Region, Nullability::Contradicted); |
1171 | 12 | C.addTransition(State); |
1172 | 12 | } |
1173 | 16 | } |
1174 | | |
1175 | | /// For a given statement performing a bind, attempt to syntactically |
1176 | | /// match the expression resulting in the bound value. |
1177 | 439 | static const Expr * matchValueExprForBind(const Stmt *S) { |
1178 | | // For `x = e` the value expression is the right-hand side. |
1179 | 439 | if (auto *BinOp = dyn_cast<BinaryOperator>(S)) { |
1180 | 143 | if (BinOp->getOpcode() == BO_Assign) |
1181 | 143 | return BinOp->getRHS(); |
1182 | 143 | } |
1183 | | |
1184 | | // For `int x = e` the value expression is the initializer. |
1185 | 296 | if (auto *DS = dyn_cast<DeclStmt>(S)) { |
1186 | 295 | if (DS->isSingleDecl()) { |
1187 | 295 | auto *VD = dyn_cast<VarDecl>(DS->getSingleDecl()); |
1188 | 295 | if (!VD) |
1189 | 0 | return nullptr; |
1190 | | |
1191 | 295 | if (const Expr *Init = VD->getInit()) |
1192 | 295 | return Init; |
1193 | 295 | } |
1194 | 295 | } |
1195 | | |
1196 | 1 | return nullptr; |
1197 | 296 | } |
1198 | | |
1199 | | /// Returns true if \param S is a DeclStmt for a local variable that |
1200 | | /// ObjC automated reference counting initialized with zero. |
1201 | 16 | static bool isARCNilInitializedLocal(CheckerContext &C, const Stmt *S) { |
1202 | | // We suppress diagnostics for ARC zero-initialized _Nonnull locals. This |
1203 | | // prevents false positives when a _Nonnull local variable cannot be |
1204 | | // initialized with an initialization expression: |
1205 | | // NSString * _Nonnull s; // no-warning |
1206 | | // @autoreleasepool { |
1207 | | // s = ... |
1208 | | // } |
1209 | | // |
1210 | | // FIXME: We should treat implicitly zero-initialized _Nonnull locals as |
1211 | | // uninitialized in Sema's UninitializedValues analysis to warn when a use of |
1212 | | // the zero-initialized definition will unexpectedly yield nil. |
1213 | | |
1214 | | // Locals are only zero-initialized when automated reference counting |
1215 | | // is turned on. |
1216 | 16 | if (!C.getASTContext().getLangOpts().ObjCAutoRefCount) |
1217 | 7 | return false; |
1218 | | |
1219 | 9 | auto *DS = dyn_cast<DeclStmt>(S); |
1220 | 9 | if (!DS || !DS->isSingleDecl()4 ) |
1221 | 5 | return false; |
1222 | | |
1223 | 4 | auto *VD = dyn_cast<VarDecl>(DS->getSingleDecl()); |
1224 | 4 | if (!VD) |
1225 | 0 | return false; |
1226 | | |
1227 | | // Sema only zero-initializes locals with ObjCLifetimes. |
1228 | 4 | if(!VD->getType().getQualifiers().hasObjCLifetime()) |
1229 | 2 | return false; |
1230 | | |
1231 | 2 | const Expr *Init = VD->getInit(); |
1232 | 2 | assert(Init && "ObjC local under ARC without initializer"); |
1233 | | |
1234 | | // Return false if the local is explicitly initialized (e.g., with '= nil'). |
1235 | 2 | if (!isa<ImplicitValueInitExpr>(Init)) |
1236 | 2 | return false; |
1237 | | |
1238 | 0 | return true; |
1239 | 2 | } |
1240 | | |
1241 | | /// Propagate the nullability information through binds and warn when nullable |
1242 | | /// pointer or null symbol is assigned to a pointer with a nonnull type. |
1243 | | void NullabilityChecker::checkBind(SVal L, SVal V, const Stmt *S, |
1244 | 599 | CheckerContext &C) const { |
1245 | 599 | const TypedValueRegion *TVR = |
1246 | 599 | dyn_cast_or_null<TypedValueRegion>(L.getAsRegion()); |
1247 | 599 | if (!TVR) |
1248 | 24 | return; |
1249 | | |
1250 | 575 | QualType LocType = TVR->getValueType(); |
1251 | 575 | if (!isValidPointerType(LocType)) |
1252 | 77 | return; |
1253 | | |
1254 | 498 | ProgramStateRef State = C.getState(); |
1255 | 498 | if (State->get<InvariantViolated>()) |
1256 | 59 | return; |
1257 | | |
1258 | 439 | auto ValDefOrUnknown = V.getAs<DefinedOrUnknownSVal>(); |
1259 | 439 | if (!ValDefOrUnknown) |
1260 | 0 | return; |
1261 | | |
1262 | 439 | NullConstraint RhsNullness = getNullConstraint(*ValDefOrUnknown, State); |
1263 | | |
1264 | 439 | Nullability ValNullability = Nullability::Unspecified; |
1265 | 439 | if (SymbolRef Sym = ValDefOrUnknown->getAsSymbol()) |
1266 | 261 | ValNullability = getNullabilityAnnotation(Sym->getType()); |
1267 | | |
1268 | 439 | Nullability LocNullability = getNullabilityAnnotation(LocType); |
1269 | | |
1270 | | // If the type of the RHS expression is nonnull, don't warn. This |
1271 | | // enables explicit suppression with a cast to nonnull. |
1272 | 439 | Nullability ValueExprTypeLevelNullability = Nullability::Unspecified; |
1273 | 439 | const Expr *ValueExpr = matchValueExprForBind(S); |
1274 | 439 | if (ValueExpr) { |
1275 | 438 | ValueExprTypeLevelNullability = |
1276 | 438 | getNullabilityAnnotation(lookThroughImplicitCasts(ValueExpr)->getType()); |
1277 | 438 | } |
1278 | | |
1279 | 439 | bool NullAssignedToNonNull = (LocNullability == Nullability::Nonnull && |
1280 | 439 | RhsNullness == NullConstraint::IsNull112 ); |
1281 | 439 | if (ChecksEnabled[CK_NullPassedToNonnull] && NullAssignedToNonNull && |
1282 | 439 | ValNullability != Nullability::Nonnull34 && |
1283 | 439 | ValueExprTypeLevelNullability != Nullability::Nonnull34 && |
1284 | 439 | !isARCNilInitializedLocal(C, S)16 ) { |
1285 | 16 | static CheckerProgramPointTag Tag(this, "NullPassedToNonnull"); |
1286 | 16 | ExplodedNode *N = C.generateErrorNode(State, &Tag); |
1287 | 16 | if (!N) |
1288 | 0 | return; |
1289 | | |
1290 | | |
1291 | 16 | const Stmt *ValueStmt = S; |
1292 | 16 | if (ValueExpr) |
1293 | 16 | ValueStmt = ValueExpr; |
1294 | | |
1295 | 16 | SmallString<256> SBuf; |
1296 | 16 | llvm::raw_svector_ostream OS(SBuf); |
1297 | 16 | OS << (LocType->isObjCObjectPointerType() ? "nil"3 : "Null"13 ); |
1298 | 16 | OS << " assigned to a pointer which is expected to have non-null value"; |
1299 | 16 | reportBugIfInvariantHolds(OS.str(), ErrorKind::NilAssignedToNonnull, |
1300 | 16 | CK_NullPassedToNonnull, N, nullptr, C, ValueStmt); |
1301 | 16 | return; |
1302 | 16 | } |
1303 | | |
1304 | | // If null was returned from a non-null function, mark the nullability |
1305 | | // invariant as violated even if the diagnostic was suppressed. |
1306 | 423 | if (NullAssignedToNonNull) { |
1307 | 18 | State = State->set<InvariantViolated>(true); |
1308 | 18 | C.addTransition(State); |
1309 | 18 | return; |
1310 | 18 | } |
1311 | | |
1312 | | // Intentionally missing case: '0' is bound to a reference. It is handled by |
1313 | | // the DereferenceChecker. |
1314 | | |
1315 | 405 | const MemRegion *ValueRegion = getTrackRegion(*ValDefOrUnknown); |
1316 | 405 | if (!ValueRegion) |
1317 | 148 | return; |
1318 | | |
1319 | 257 | const NullabilityState *TrackedNullability = |
1320 | 257 | State->get<NullabilityMap>(ValueRegion); |
1321 | | |
1322 | 257 | if (TrackedNullability) { |
1323 | 79 | if (RhsNullness == NullConstraint::IsNotNull || |
1324 | 79 | TrackedNullability->getValue() != Nullability::Nullable) |
1325 | 5 | return; |
1326 | 74 | if (ChecksEnabled[CK_NullablePassedToNonnull] && |
1327 | 74 | LocNullability == Nullability::Nonnull) { |
1328 | 4 | static CheckerProgramPointTag Tag(this, "NullablePassedToNonnull"); |
1329 | 4 | ExplodedNode *N = C.addTransition(State, C.getPredecessor(), &Tag); |
1330 | 4 | reportBugIfInvariantHolds("Nullable pointer is assigned to a pointer " |
1331 | 4 | "which is expected to have non-null value", |
1332 | 4 | ErrorKind::NullableAssignedToNonnull, |
1333 | 4 | CK_NullablePassedToNonnull, N, ValueRegion, C); |
1334 | 4 | } |
1335 | 74 | return; |
1336 | 79 | } |
1337 | | |
1338 | 178 | const auto *BinOp = dyn_cast<BinaryOperator>(S); |
1339 | | |
1340 | 178 | if (ValNullability == Nullability::Nullable) { |
1341 | | // Trust the static information of the value more than the static |
1342 | | // information on the location. |
1343 | 0 | const Stmt *NullabilitySource = BinOp ? BinOp->getRHS() : S; |
1344 | 0 | State = State->set<NullabilityMap>( |
1345 | 0 | ValueRegion, NullabilityState(ValNullability, NullabilitySource)); |
1346 | 0 | C.addTransition(State); |
1347 | 0 | return; |
1348 | 0 | } |
1349 | | |
1350 | 178 | if (LocNullability == Nullability::Nullable) { |
1351 | 0 | const Stmt *NullabilitySource = BinOp ? BinOp->getLHS() : S; |
1352 | 0 | State = State->set<NullabilityMap>( |
1353 | 0 | ValueRegion, NullabilityState(LocNullability, NullabilitySource)); |
1354 | 0 | C.addTransition(State); |
1355 | 0 | } |
1356 | 178 | } |
1357 | | |
1358 | | void NullabilityChecker::printState(raw_ostream &Out, ProgramStateRef State, |
1359 | 0 | const char *NL, const char *Sep) const { |
1360 | |
|
1361 | 0 | NullabilityMapTy B = State->get<NullabilityMap>(); |
1362 | |
|
1363 | 0 | if (State->get<InvariantViolated>()) |
1364 | 0 | Out << Sep << NL |
1365 | 0 | << "Nullability invariant was violated, warnings suppressed." << NL; |
1366 | |
|
1367 | 0 | if (B.isEmpty()) |
1368 | 0 | return; |
1369 | | |
1370 | 0 | if (!State->get<InvariantViolated>()) |
1371 | 0 | Out << Sep << NL; |
1372 | |
|
1373 | 0 | for (auto [Region, State] : B) { |
1374 | 0 | Out << Region << " : "; |
1375 | 0 | State.print(Out); |
1376 | 0 | Out << NL; |
1377 | 0 | } |
1378 | 0 | } |
1379 | | |
1380 | 52 | void ento::registerNullabilityBase(CheckerManager &mgr) { |
1381 | 52 | mgr.registerChecker<NullabilityChecker>(); |
1382 | 52 | } |
1383 | | |
1384 | 346 | bool ento::shouldRegisterNullabilityBase(const CheckerManager &mgr) { |
1385 | 346 | return true; |
1386 | 346 | } |
1387 | | |
1388 | | #define REGISTER_CHECKER(name, trackingRequired) \ |
1389 | 157 | void ento::register##name##Checker(CheckerManager &mgr) { \ |
1390 | 157 | NullabilityChecker *checker = mgr.getChecker<NullabilityChecker>(); \ |
1391 | 157 | checker->ChecksEnabled[NullabilityChecker::CK_##name] = true; \ |
1392 | 157 | checker->CheckNames[NullabilityChecker::CK_##name] = \ |
1393 | 157 | mgr.getCurrentCheckerName(); \ |
1394 | 157 | checker->NeedTracking = checker->NeedTracking || trackingRequired121 ; \ |
1395 | 157 | checker->NoDiagnoseCallsToSystemHeaders = \ |
1396 | 157 | checker->NoDiagnoseCallsToSystemHeaders || \ |
1397 | 157 | mgr.getAnalyzerOptions().getCheckerBooleanOption( \ |
1398 | 148 | checker, "NoDiagnoseCallsToSystemHeaders", true); \ |
1399 | 157 | } \ clang::ento::registerNullPassedToNonnullChecker(clang::ento::CheckerManager&) Line | Count | Source | 1389 | 51 | void ento::register##name##Checker(CheckerManager &mgr) { \ | 1390 | 51 | NullabilityChecker *checker = mgr.getChecker<NullabilityChecker>(); \ | 1391 | 51 | checker->ChecksEnabled[NullabilityChecker::CK_##name] = true; \ | 1392 | 51 | checker->CheckNames[NullabilityChecker::CK_##name] = \ | 1393 | 51 | mgr.getCurrentCheckerName(); \ | 1394 | 51 | checker->NeedTracking = checker->NeedTracking || trackingRequired; \ | 1395 | 51 | checker->NoDiagnoseCallsToSystemHeaders = \ | 1396 | 51 | checker->NoDiagnoseCallsToSystemHeaders || \ | 1397 | 51 | mgr.getAnalyzerOptions().getCheckerBooleanOption( \ | 1398 | 51 | checker, "NoDiagnoseCallsToSystemHeaders", true); \ | 1399 | 51 | } \ |
clang::ento::registerNullReturnedFromNonnullChecker(clang::ento::CheckerManager&) Line | Count | Source | 1389 | 52 | void ento::register##name##Checker(CheckerManager &mgr) { \ | 1390 | 52 | NullabilityChecker *checker = mgr.getChecker<NullabilityChecker>(); \ | 1391 | 52 | checker->ChecksEnabled[NullabilityChecker::CK_##name] = true; \ | 1392 | 52 | checker->CheckNames[NullabilityChecker::CK_##name] = \ | 1393 | 52 | mgr.getCurrentCheckerName(); \ | 1394 | 52 | checker->NeedTracking = checker->NeedTracking || trackingRequired; \ | 1395 | 52 | checker->NoDiagnoseCallsToSystemHeaders = \ | 1396 | 52 | checker->NoDiagnoseCallsToSystemHeaders || \ | 1397 | 52 | mgr.getAnalyzerOptions().getCheckerBooleanOption( \ | 1398 | 49 | checker, "NoDiagnoseCallsToSystemHeaders", true); \ | 1399 | 52 | } \ |
clang::ento::registerNullableDereferencedChecker(clang::ento::CheckerManager&) Line | Count | Source | 1389 | 18 | void ento::register##name##Checker(CheckerManager &mgr) { \ | 1390 | 18 | NullabilityChecker *checker = mgr.getChecker<NullabilityChecker>(); \ | 1391 | 18 | checker->ChecksEnabled[NullabilityChecker::CK_##name] = true; \ | 1392 | 18 | checker->CheckNames[NullabilityChecker::CK_##name] = \ | 1393 | 18 | mgr.getCurrentCheckerName(); \ | 1394 | 18 | checker->NeedTracking = checker->NeedTracking || trackingRequired; \ | 1395 | 18 | checker->NoDiagnoseCallsToSystemHeaders = \ | 1396 | 18 | checker->NoDiagnoseCallsToSystemHeaders || \ | 1397 | 18 | mgr.getAnalyzerOptions().getCheckerBooleanOption( \ | 1398 | 16 | checker, "NoDiagnoseCallsToSystemHeaders", true); \ | 1399 | 18 | } \ |
clang::ento::registerNullablePassedToNonnullChecker(clang::ento::CheckerManager&) Line | Count | Source | 1389 | 18 | void ento::register##name##Checker(CheckerManager &mgr) { \ | 1390 | 18 | NullabilityChecker *checker = mgr.getChecker<NullabilityChecker>(); \ | 1391 | 18 | checker->ChecksEnabled[NullabilityChecker::CK_##name] = true; \ | 1392 | 18 | checker->CheckNames[NullabilityChecker::CK_##name] = \ | 1393 | 18 | mgr.getCurrentCheckerName(); \ | 1394 | 18 | checker->NeedTracking = checker->NeedTracking || trackingRequired0 ; \ | 1395 | 18 | checker->NoDiagnoseCallsToSystemHeaders = \ | 1396 | 18 | checker->NoDiagnoseCallsToSystemHeaders || \ | 1397 | 18 | mgr.getAnalyzerOptions().getCheckerBooleanOption( \ | 1398 | 16 | checker, "NoDiagnoseCallsToSystemHeaders", true); \ | 1399 | 18 | } \ |
clang::ento::registerNullableReturnedFromNonnullChecker(clang::ento::CheckerManager&) Line | Count | Source | 1389 | 18 | void ento::register##name##Checker(CheckerManager &mgr) { \ | 1390 | 18 | NullabilityChecker *checker = mgr.getChecker<NullabilityChecker>(); \ | 1391 | 18 | checker->ChecksEnabled[NullabilityChecker::CK_##name] = true; \ | 1392 | 18 | checker->CheckNames[NullabilityChecker::CK_##name] = \ | 1393 | 18 | mgr.getCurrentCheckerName(); \ | 1394 | 18 | checker->NeedTracking = checker->NeedTracking || trackingRequired0 ; \ | 1395 | 18 | checker->NoDiagnoseCallsToSystemHeaders = \ | 1396 | 18 | checker->NoDiagnoseCallsToSystemHeaders || \ | 1397 | 18 | mgr.getAnalyzerOptions().getCheckerBooleanOption( \ | 1398 | 16 | checker, "NoDiagnoseCallsToSystemHeaders", true); \ | 1399 | 18 | } \ |
|
1400 | | \ |
1401 | 322 | bool ento::shouldRegister##name##Checker(const CheckerManager &mgr) { \ |
1402 | 322 | return true; \ |
1403 | 322 | } clang::ento::shouldRegisterNullPassedToNonnullChecker(clang::ento::CheckerManager const&) Line | Count | Source | 1401 | 106 | bool ento::shouldRegister##name##Checker(const CheckerManager &mgr) { \ | 1402 | 106 | return true; \ | 1403 | 106 | } |
clang::ento::shouldRegisterNullReturnedFromNonnullChecker(clang::ento::CheckerManager const&) Line | Count | Source | 1401 | 108 | bool ento::shouldRegister##name##Checker(const CheckerManager &mgr) { \ | 1402 | 108 | return true; \ | 1403 | 108 | } |
clang::ento::shouldRegisterNullableDereferencedChecker(clang::ento::CheckerManager const&) Line | Count | Source | 1401 | 36 | bool ento::shouldRegister##name##Checker(const CheckerManager &mgr) { \ | 1402 | 36 | return true; \ | 1403 | 36 | } |
clang::ento::shouldRegisterNullablePassedToNonnullChecker(clang::ento::CheckerManager const&) Line | Count | Source | 1401 | 36 | bool ento::shouldRegister##name##Checker(const CheckerManager &mgr) { \ | 1402 | 36 | return true; \ | 1403 | 36 | } |
clang::ento::shouldRegisterNullableReturnedFromNonnullChecker(clang::ento::CheckerManager const&) Line | Count | Source | 1401 | 36 | bool ento::shouldRegister##name##Checker(const CheckerManager &mgr) { \ | 1402 | 36 | return true; \ | 1403 | 36 | } |
|
1404 | | |
1405 | | // The checks are likely to be turned on by default and it is possible to do |
1406 | | // them without tracking any nullability related information. As an optimization |
1407 | | // no nullability information will be tracked when only these two checks are |
1408 | | // enables. |
1409 | | REGISTER_CHECKER(NullPassedToNonnull, false) |
1410 | | REGISTER_CHECKER(NullReturnedFromNonnull, false) |
1411 | | |
1412 | | REGISTER_CHECKER(NullableDereferenced, true) |
1413 | | REGISTER_CHECKER(NullablePassedToNonnull, true) |
1414 | | REGISTER_CHECKER(NullableReturnedFromNonnull, true) |