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