[clang] fa6b7dd - [StaticAnalyzer] Fix false negative on NilArgChecker when creating literal object
Balazs Benics via cfe-commits
cfe-commits at lists.llvm.org
Thu Jun 8 07:48:59 PDT 2023
Author: tripleCC
Date: 2023-06-08T16:48:24+02:00
New Revision: fa6b7dd520fc175a246c943a7c9802e4808118b1
URL: https://github.com/llvm/llvm-project/commit/fa6b7dd520fc175a246c943a7c9802e4808118b1
DIFF: https://github.com/llvm/llvm-project/commit/fa6b7dd520fc175a246c943a7c9802e4808118b1.diff
LOG: [StaticAnalyzer] Fix false negative on NilArgChecker when creating literal object
Fix false negative on NilArgChecker when creating literal object, such
as @[nullableObject].
Patch By tripleCC!
Differential Revision: https://reviews.llvm.org/D152269
Added:
Modified:
clang/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp
clang/test/Analysis/NSContainers.m
Removed:
################################################################################
diff --git a/clang/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp b/clang/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp
index 44166aaf5b85b..f88b17d437eaa 100644
--- a/clang/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp
@@ -95,56 +95,64 @@ static FoundationClass findKnownClass(const ObjCInterfaceDecl *ID,
//===----------------------------------------------------------------------===//
namespace {
- class NilArgChecker : public Checker<check::PreObjCMessage,
- check::PostStmt<ObjCDictionaryLiteral>,
- check::PostStmt<ObjCArrayLiteral> > {
- mutable std::unique_ptr<APIMisuse> BT;
-
- mutable llvm::SmallDenseMap<Selector, unsigned, 16> StringSelectors;
- mutable Selector ArrayWithObjectSel;
- mutable Selector AddObjectSel;
- mutable Selector InsertObjectAtIndexSel;
- mutable Selector ReplaceObjectAtIndexWithObjectSel;
- mutable Selector SetObjectAtIndexedSubscriptSel;
- mutable Selector ArrayByAddingObjectSel;
- mutable Selector DictionaryWithObjectForKeySel;
- mutable Selector SetObjectForKeySel;
- mutable Selector SetObjectForKeyedSubscriptSel;
- mutable Selector RemoveObjectForKeySel;
-
- void warnIfNilExpr(const Expr *E,
- const char *Msg,
- CheckerContext &C) const;
-
- void warnIfNilArg(CheckerContext &C,
- const ObjCMethodCall &msg, unsigned Arg,
- FoundationClass Class,
- bool CanBeSubscript = false) const;
-
- void generateBugReport(ExplodedNode *N,
- StringRef Msg,
- SourceRange Range,
- const Expr *Expr,
- CheckerContext &C) const;
-
- public:
- void checkPreObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const;
- void checkPostStmt(const ObjCDictionaryLiteral *DL,
- CheckerContext &C) const;
- void checkPostStmt(const ObjCArrayLiteral *AL,
- CheckerContext &C) const;
- };
+class NilArgChecker : public Checker<check::PreObjCMessage,
+ check::PostStmt<ObjCDictionaryLiteral>,
+ check::PostStmt<ObjCArrayLiteral>,
+ EventDispatcher<ImplicitNullDerefEvent>> {
+ mutable std::unique_ptr<APIMisuse> BT;
+
+ mutable llvm::SmallDenseMap<Selector, unsigned, 16> StringSelectors;
+ mutable Selector ArrayWithObjectSel;
+ mutable Selector AddObjectSel;
+ mutable Selector InsertObjectAtIndexSel;
+ mutable Selector ReplaceObjectAtIndexWithObjectSel;
+ mutable Selector SetObjectAtIndexedSubscriptSel;
+ mutable Selector ArrayByAddingObjectSel;
+ mutable Selector DictionaryWithObjectForKeySel;
+ mutable Selector SetObjectForKeySel;
+ mutable Selector SetObjectForKeyedSubscriptSel;
+ mutable Selector RemoveObjectForKeySel;
+
+ void warnIfNilExpr(const Expr *E, const char *Msg, CheckerContext &C) const;
+
+ void warnIfNilArg(CheckerContext &C, const ObjCMethodCall &msg, unsigned Arg,
+ FoundationClass Class, bool CanBeSubscript = false) const;
+
+ void generateBugReport(ExplodedNode *N, StringRef Msg, SourceRange Range,
+ const Expr *Expr, CheckerContext &C) const;
+
+public:
+ void checkPreObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const;
+ void checkPostStmt(const ObjCDictionaryLiteral *DL, CheckerContext &C) const;
+ void checkPostStmt(const ObjCArrayLiteral *AL, CheckerContext &C) const;
+};
} // end anonymous namespace
void NilArgChecker::warnIfNilExpr(const Expr *E,
const char *Msg,
CheckerContext &C) const {
- ProgramStateRef State = C.getState();
- if (State->isNull(C.getSVal(E)).isConstrainedTrue()) {
+ auto Location = C.getSVal(E).getAs<Loc>();
+ if (!Location)
+ return;
+
+ auto [NonNull, Null] = C.getState()->assume(*Location);
+ // If it's known to be null.
+ if (!NonNull && Null) {
if (ExplodedNode *N = C.generateErrorNode()) {
generateBugReport(N, Msg, E->getSourceRange(), E, C);
+ return;
+ }
+ }
+
+ // If it might be null, assume that it cannot after this operation.
+ if (Null) {
+ // One needs to make sure the pointer is non-null to be used here.
+ if (ExplodedNode *N = C.generateSink(Null, C.getPredecessor())) {
+ dispatchEvent({*Location, /*IsLoad=*/false, N, &C.getBugReporter(),
+ /*IsDirectDereference=*/false});
}
+ C.addTransition(NonNull);
}
}
diff --git a/clang/test/Analysis/NSContainers.m b/clang/test/Analysis/NSContainers.m
index f41189a5e1dcf..ceeb9ffbf2c89 100644
--- a/clang/test/Analysis/NSContainers.m
+++ b/clang/test/Analysis/NSContainers.m
@@ -1,4 +1,7 @@
-// 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 -Wno-objc-root-class -fobjc-arc \
+// RUN: -analyzer-checker=core,osx.cocoa,nullability \
+// RUN: -analyzer-config eagerly-assume=false \
+// RUN: -analyzer-checker=debug.ExprInspection -verify %s
void clang_analyzer_eval(int);
@@ -31,13 +34,13 @@ - (id)mutableCopy;
@end
typedef struct {
- unsigned long state;
- id *itemsPtr;
- unsigned long *mutationsPtr;
- unsigned long extra[5];
+ unsigned long state;
+ id __unsafe_unretained _Nullable * _Nullable itemsPtr;
+ unsigned long * _Nullable mutationsPtr;
+ unsigned long extra[5];
} NSFastEnumerationState;
@protocol NSFastEnumeration
-- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id [])buffer count:(NSUInteger)len;
+- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id __unsafe_unretained _Nullable [_Nonnull])buffer count:(NSUInteger)len;
@end
@interface NSArray : NSObject <NSCopying, NSMutableCopying, NSSecureCoding, NSFastEnumeration>
@@ -323,3 +326,43 @@ void testInlinedDefensiveCheck(NSMutableDictionary *dict, id obj) {
// that 'obj' can be nil in this context.
dict[obj] = getStringFromString(obj); // no-warning
}
+
+Foo * getMightBeNullFoo();
+Foo * _Nonnull getNonnullFoo();
+Foo * _Nullable getNullableFoo();
+
+void testCreateDictionaryLiteralWithNullableArg() {
+ Foo *p1 = getMightBeNullFoo();
+ Foo *p2 = getNonnullFoo();
+ Foo *p3 = getNullableFoo();
+
+ clang_analyzer_eval(p1 == nil); // expected-warning {{UNKNOWN}}
+ clang_analyzer_eval(p2 == nil); // expected-warning {{UNKNOWN}}
+ clang_analyzer_eval(p3 == nil); // expected-warning {{UNKNOWN}}
+
+ (void)@{@"abc" : p1}; // no-warning
+ (void)@{@"abc" : p2}; // no-warning
+ (void)@{@"abc" : p3}; // expected-warning {{Nullable pointer is passed to a callee that requires a non-null}}
+
+ clang_analyzer_eval(p1 == nil); // expected-warning {{FALSE}}
+ clang_analyzer_eval(p2 == nil); // expected-warning {{FALSE}}
+ clang_analyzer_eval(p3 == nil); // expected-warning {{FALSE}}
+}
+
+void testCreateArrayLiteralWithNullableArg() {
+ Foo *p1 = getMightBeNullFoo();
+ Foo *p2 = getNonnullFoo();
+ Foo *p3 = getNullableFoo();
+
+ clang_analyzer_eval(p1 == nil); // expected-warning {{UNKNOWN}}
+ clang_analyzer_eval(p2 == nil); // expected-warning {{UNKNOWN}}
+ clang_analyzer_eval(p3 == nil); // expected-warning {{UNKNOWN}}
+
+ (void)@[p1]; // no-warning
+ (void)@[p2]; // no-warning
+ (void)@[p3]; // expected-warning {{Nullable pointer is passed to a callee that requires a non-null}}
+
+ clang_analyzer_eval(p1 == nil); // expected-warning {{FALSE}}
+ clang_analyzer_eval(p2 == nil); // expected-warning {{FALSE}}
+ clang_analyzer_eval(p3 == nil); // expected-warning {{FALSE}}
+}
More information about the cfe-commits
mailing list