diff --git a/clang/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp b/clang/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp --- a/clang/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp @@ -97,7 +97,8 @@ namespace { class NilArgChecker : public Checker, - check::PostStmt > { + check::PostStmt, + EventDispatcher> { mutable std::unique_ptr BT; mutable llvm::SmallDenseMap StringSelectors; @@ -141,11 +142,31 @@ CheckerContext &C) const { ProgramStateRef State = C.getState(); if (State->isNull(C.getSVal(E)).isConstrainedTrue()) { - if (ExplodedNode *N = C.generateErrorNode()) { generateBugReport(N, Msg, E->getSourceRange(), E, C); + return; } } + + auto ArgSVal = C.getSVal(E).getAs(); + if (!ArgSVal) + return; + + ProgramStateRef StNonNull, StNull; + std::tie(StNonNull, StNull) = State->assume(*ArgSVal); + + if (StNull) { + if (ExplodedNode *N = C.generateSink(StNull, C.getPredecessor())) { + ImplicitNullDerefEvent event = {*ArgSVal, false, N, &C.getBugReporter(), + /*IsDirectDereference=*/false}; + dispatchEvent(event); + } + } + + if (StNonNull) + State = StNonNull; + + C.addTransition(State); } void NilArgChecker::warnIfNilArg(CheckerContext &C, diff --git a/clang/test/Analysis/NSContainers.m b/clang/test/Analysis/NSContainers.m --- a/clang/test/Analysis/NSContainers.m +++ b/clang/test/Analysis/NSContainers.m @@ -1,4 +1,4 @@ -// RUN: %clang_analyze_cc1 -Wno-objc-literal-conversion -analyzer-checker=core,osx.cocoa.NonNilReturnValue,osx.cocoa.NilArg,osx.cocoa.Loops,debug.ExprInspection -verify -Wno-objc-root-class %s +// RUN: %clang_analyze_cc1 -Wno-objc-literal-conversion -analyzer-checker=core,osx.cocoa.NonNilReturnValue,osx.cocoa.NilArg,osx.cocoa.Loops,debug.ExprInspection,nullability -verify -Wno-objc-root-class %s void clang_analyzer_eval(int); @@ -323,3 +323,16 @@ // that 'obj' can be nil in this context. dict[obj] = getStringFromString(obj); // no-warning } + +Foo * _Nonnull getNonnullFoo(); +Foo * _Nullable getNullableFoo(); + +void testCreateDictionaryLiteralWithNullableArg() { + (void)@{@"abc" : getNonnullFoo()}; // no warning + (void)@{@"abc" : getNullableFoo()}; // expected-warning {{Nullable pointer is passed to a callee that requires a non-null}} +} + +void testCreateArrayLiteralWithNullableArg() { + (void)@[getNonnullFoo()]; // no warning + (void)@[getNullableFoo()]; // expected-warning {{Nullable pointer is passed to a callee that requires a non-null}} +}