Szelethus created this revision.
Szelethus added reviewers: NoQ, baloghadamsoftware, xazax.hun, rnkovacs,
dcoughlin, martong, balazske.
Szelethus added a project: clang.
Herald added subscribers: cfe-commits, ASDenysPetrov, steakhal, Charusso,
gamesh411, dkrupp, donat.nagy, mikhail.ramalho, a.sidorin, szepet, whisperity.
Szelethus added a parent revision: D77846: [analyzer][CallAndMessage][NFC]
Split up checkPreCall.
As listed in the summary D77846 <https://reviews.llvm.org/D77846>, we have 5
different categories of bugs we're checking for in CallAndMessage. I think the
documentation placed in the code explains my thought process behind my
decisions quite well.
A non-obvious change I had here is removing the entry for
CallAndMessageUnInitRefArg. In fact, I removed the `CheckerNameRef` typed field
back in D77845 <https://reviews.llvm.org/D77845> (it was dead code), so that
checker didn't really exist in any meaningful way anyways.
Repository:
rG LLVM Github Monorepo
https://reviews.llvm.org/D77866
Files:
clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
clang/include/clang/StaticAnalyzer/Core/BugReporter/BugType.h
clang/include/clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h
clang/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp
clang/test/Analysis/PR40625.cpp
clang/test/Analysis/analyzer-config.c
clang/test/Analysis/analyzer-enabled-checkers.c
clang/test/Analysis/call-and-message.c
clang/test/Analysis/call-and-message.cpp
clang/test/Analysis/call-and-message.m
clang/test/Analysis/call-and-message.mm
clang/test/Analysis/exercise-ps.c
clang/test/Analysis/reference.mm
clang/test/Analysis/uninit-const.c
clang/test/Analysis/uninit-const.cpp
clang/test/Analysis/uninit-msg-expr.m
Index: clang/test/Analysis/uninit-msg-expr.m
===================================================================
--- clang/test/Analysis/uninit-msg-expr.m
+++ /dev/null
@@ -1,56 +0,0 @@
-// RUN: %clang_analyze_cc1 -analyzer-checker=core -analyzer-store=region -verify %s
-
-//===----------------------------------------------------------------------===//
-// The following code is reduced using delta-debugging from
-// Foundation.h (Mac OS X).
-//
-// It includes the basic definitions for the test cases below.
-// Not directly including Foundation.h directly makes this test case
-// both svelte and portable to non-Mac platforms.
-//===----------------------------------------------------------------------===//
-
-typedef signed char BOOL;
-typedef unsigned int NSUInteger;
-typedef struct _NSZone NSZone;
-@class NSInvocation, NSMethodSignature, NSCoder, NSString, NSEnumerator;
-@protocol NSObject - (BOOL)isEqual:(id)object; @end
-@protocol NSCopying - (id)copyWithZone:(NSZone *)zone; @end
-@protocol NSMutableCopying - (id)mutableCopyWithZone:(NSZone *)zone; @end
-@protocol NSCoding - (void)encodeWithCoder:(NSCoder *)aCoder; @end
-@interface NSObject <NSObject> {} @end
-@class NSString, NSData;
-@class NSString, NSData, NSMutableData, NSMutableDictionary, NSMutableArray;
-typedef struct {} NSFastEnumerationState;
-@protocol NSFastEnumeration
-- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id *)stackbuf count:(NSUInteger)len;
-@end
-@class NSData, NSIndexSet, NSString, NSURL;
-@interface NSArray : NSObject <NSCopying, NSMutableCopying, NSCoding, NSFastEnumeration>
-- (NSUInteger)count;
-@end
-@interface NSArray (NSArrayCreation)
-+ (id)array;
-- (NSUInteger)length;
-- (void)addObject:(id)object;
-@end
-extern NSString * const NSUndoManagerCheckpointNotification;
-
-//===----------------------------------------------------------------------===//
-// Test cases.
-//===----------------------------------------------------------------------===//
-
-unsigned f1() {
- NSString *aString;
- return [aString length]; // expected-warning {{Receiver in message expression is an uninitialized value}}
-}
-
-unsigned f2() {
- NSString *aString = 0;
- return [aString length]; // no-warning
-}
-
-void f3() {
- NSMutableArray *aArray = [NSArray array];
- NSString *aString;
- [aArray addObject:aString]; // expected-warning {{1st argument in message expression is an uninitialized value}}
-}
Index: clang/test/Analysis/uninit-const.cpp
===================================================================
--- clang/test/Analysis/uninit-const.cpp
+++ clang/test/Analysis/uninit-const.cpp
@@ -1,5 +1,14 @@
-// RUN: %clang_analyze_cc1 -analyzer-checker=cplusplus.NewDelete,core,alpha.core.CallAndMessageUnInitRefArg -analyzer-output=text -verify %s
-// RUN: %clang_analyze_cc1 -analyzer-checker=cplusplus.NewDelete,core,alpha.core.CallAndMessageUnInitRefArg -analyzer-output=text -DTEST_INLINABLE_ALLOCATORS -verify %s
+// RUN: %clang_analyze_cc1 -analyzer-output=text -verify %s \
+// RUN: -analyzer-checker=core \
+// RUN: -analyzer-checker=cplusplus.NewDelete \
+// RUN: -analyzer-config core.CallAndMessage:ArgPointeeInitializedness=true
+
+// RUN: %clang_analyze_cc1 -analyzer-output=text -verify %s \
+// RUN: -DTEST_INLINABLE_ALLOCATORS \
+// RUN: -analyzer-checker=core \
+// RUN: -analyzer-checker=cplusplus.NewDelete \
+// RUN: -analyzer-config core.CallAndMessage:ArgPointeeInitializedness=true
+
// Passing uninitialized const data to unknown function
#include "Inputs/system-header-simulator-cxx.h"
Index: clang/test/Analysis/uninit-const.c
===================================================================
--- clang/test/Analysis/uninit-const.c
+++ clang/test/Analysis/uninit-const.c
@@ -1,4 +1,8 @@
-// RUN: %clang_analyze_cc1 -analyzer-checker=unix.Malloc,core,alpha.core.CallAndMessageUnInitRefArg,debug.ExprInspection -analyzer-output=text -verify %s
+// RUN: %clang_analyze_cc1 -analyzer-output=text -verify %s \
+// RUN: -analyzer-checker=core \
+// RUN: -analyzer-checker=unix.Malloc \
+// RUN: -analyzer-checker=debug.ExprInspection \
+// RUN: -analyzer-config core.CallAndMessage:ArgPointeeInitializedness=true
void clang_analyzer_warnIfReached();
Index: clang/test/Analysis/reference.mm
===================================================================
--- clang/test/Analysis/reference.mm
+++ /dev/null
@@ -1,17 +0,0 @@
-// RUN: %clang_analyze_cc1 -analyzer-checker=core -verify -Wno-null-dereference %s
-
-@interface Foo
-- (int &)ref;
-@end
-
-Foo *getFoo() { return 0; }
-
-void testNullPointerSuppression() {
- getFoo().ref = 1;
-}
-
-void testPositiveNullReference() {
- Foo *x = 0;
- x.ref = 1; // expected-warning {{The receiver of message 'ref' is nil, which results in forming a null reference}}
-}
-
Index: clang/test/Analysis/exercise-ps.c
===================================================================
--- clang/test/Analysis/exercise-ps.c
+++ clang/test/Analysis/exercise-ps.c
@@ -1,9 +1,10 @@
-// RUN: %clang_analyze_cc1 -analyzer-checker=core,alpha.core -analyzer-store=region -verify %s
+// RUN: %clang_analyze_cc1 %s -verify \
+// RUN: -analyzer-checker=core \
+// RUN: -analyzer-config core.CallAndMessage:ArgPointeeInitializedness=true
//
// Just exercise the analyzer on code that has at one point caused issues
// (i.e., no assertions or crashes).
-
static void f1(const char *x, char *y) {
while (*x != 0) {
*y++ = *x++;
Index: clang/test/Analysis/call-and-message.mm
===================================================================
--- /dev/null
+++ clang/test/Analysis/call-and-message.mm
@@ -0,0 +1,32 @@
+// RUN: %clang_analyze_cc1 %s -verify \
+// RUN: -analyzer-checker=core \
+// RUN: -analyzer-config core.CallAndMessage:FunctionPointer=false \
+// RUN: -analyzer-config core.CallAndMessage:ParameterCount=false \
+// RUN: -analyzer-config core.CallAndMessage:CXXThisMethodCall=false \
+// RUN: -analyzer-config core.CallAndMessage:CXXDeallocationArg=false \
+// RUN: -analyzer-config core.CallAndMessage:ArgInitializedness=false \
+// RUN: -analyzer-config core.CallAndMessage:ArgPointeeInitializedness=false \
+// RUN: -analyzer-config core.CallAndMessage:NilReceiver=true \
+// RUN: -analyzer-config core.CallAndMessage:UndefReceiver=false \
+// RUN: -analyzer-output=plist -o %t.plist
+// RUN: cat %t.plist | FileCheck %s
+
+@interface Foo
+- (int &)ref;
+@end
+
+Foo *getFoo() { return 0; }
+
+void testNullPointerSuppression() {
+ getFoo().ref = 1;
+}
+
+void testPositiveNullReference() {
+ Foo *x = 0;
+ x.ref = 1; // expected-warning {{The receiver of message 'ref' is nil, which results in forming a null reference [core.CallAndMessage]}}
+}
+
+// TODO: If this hash ever changes, turn core.CallAndMessage:NilReceiver from a
+// checker option into a checker, as described in the CallAndMessage comments!
+// CHECK: <key>issue_hash_content_of_line_in_context</key>
+// CHECK-SAME: <string>abe2e0574dd901094c511bae2f93f926</string>
Index: clang/test/Analysis/call-and-message.m
===================================================================
--- /dev/null
+++ clang/test/Analysis/call-and-message.m
@@ -0,0 +1,134 @@
+// RUN: %clang_analyze_cc1 %s -verify \
+// RUN: -Wno-objc-root-class \
+// RUN: -analyzer-checker=core \
+// RUN: -analyzer-config core.CallAndMessage:FunctionPointer=false \
+// RUN: -analyzer-config core.CallAndMessage:ParameterCount=false \
+// RUN: -analyzer-config core.CallAndMessage:CXXThisMethodCall=false \
+// RUN: -analyzer-config core.CallAndMessage:CXXDeallocationArg=false \
+// RUN: -analyzer-config core.CallAndMessage:ArgInitializedness=false \
+// RUN: -analyzer-config core.CallAndMessage:ArgPointeeInitializedness=false \
+// RUN: -analyzer-config core.CallAndMessage:NilReceiver=false \
+// RUN: -analyzer-config core.CallAndMessage:UndefReceiver=true \
+// RUN: -analyzer-output=plist -o %t.plist
+// RUN: cat %t.plist | FileCheck %s
+
+//===----------------------------------------------------------------------===//
+// The following code is reduced using delta-debugging from
+// Foundation.h (Mac OS X).
+//
+// It includes the basic definitions for the test cases below.
+// Not directly including Foundation.h directly makes this test case
+// both svelte and portable to non-Mac platforms.
+//===----------------------------------------------------------------------===//
+
+typedef signed char BOOL;
+typedef unsigned int NSUInteger;
+typedef struct _NSZone NSZone;
+@class NSInvocation, NSMethodSignature, NSCoder, NSString, NSEnumerator;
+@protocol NSObject
+- (BOOL)isEqual:(id)object;
+@end
+@protocol NSCopying
+- (id)copyWithZone:(NSZone *)zone;
+@end
+@protocol NSMutableCopying
+- (id)mutableCopyWithZone:(NSZone *)zone;
+@end
+@protocol NSCoding
+- (void)encodeWithCoder:(NSCoder *)aCoder;
+@end
+@interface NSObject <NSObject> {
+}
+@end
+@class NSString, NSData;
+@class NSString, NSData, NSMutableData, NSMutableDictionary, NSMutableArray;
+typedef struct {
+} NSFastEnumerationState;
+@protocol NSFastEnumeration
+- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id *)stackbuf count:(NSUInteger)len;
+@end
+@class NSData, NSIndexSet, NSString, NSURL;
+@interface NSArray : NSObject <NSCopying, NSMutableCopying, NSCoding, NSFastEnumeration>
+- (NSUInteger)count;
+@end
+@interface NSArray (NSArrayCreation)
++ (id)array;
+- (NSUInteger)length;
+- (void)addObject:(id)object;
+@end
+extern NSString *const NSUndoManagerCheckpointNotification;
+
+//===----------------------------------------------------------------------===//
+// Test cases.
+//===----------------------------------------------------------------------===//
+
+unsigned f1() {
+ NSString *aString;
+ return [aString length]; // expected-warning {{Receiver in message expression is an uninitialized value [core.CallAndMessage]}}
+}
+
+// TODO: If this hash ever changes, turn core.CallAndMessage:UndefReceiver from
+// a checker option into a checker, as described in the CallAndMessage comments!
+// CHECK: <key>issue_hash_content_of_line_in_context</key>
+// CHECK-SAME: <string>29873175e1cc0a98f7040057279925a0</string>
+
+@interface RDar9241180
+@property(readwrite, assign) id x;
+- (id)testAnalyzer1:(int)y;
+@end
+
+@implementation RDar9241180
+@synthesize x;
+- (id)testAnalyzer1:(int)y {
+ RDar9241180 *o;
+ if (y && o.x) // expected-warning {{Property access on an uninitialized object pointer [core.CallAndMessage]}}
+ return o;
+
+ // TODO: If this hash ever changes, turn core.CallAndMessage:UndefReceiver from
+ // a checker option into a checker, as described in the CallAndMessage comments!
+ // CHECK: <key>issue_hash_content_of_line_in_context</key>
+ // CHECK-SAME: <string>00ddd30796a283de33e662da8449c796</string>
+
+ return o; // expected-warning {{Undefined or garbage value returned to caller [core.uninitialized.UndefReturn]}}
+}
+@end
+
+// CHECK: <key>issue_hash_content_of_line_in_context</key>
+// CHECK-SAME: <string>8d468e24df7d887f4182bf49f5dd8b71</string>
+
+typedef signed char BOOL;
+typedef unsigned int NSUInteger;
+
+@interface Subscriptable : NSObject
+- (void)setObject:(id)obj atIndexedSubscript:(NSUInteger)index;
+- (id)objectAtIndexedSubscript:(NSUInteger)index;
+
+- (void)setObject:(id)obj forKeyedSubscript:(id)key;
+- (id)objectForKeyedSubscript:(id)key;
+@end
+
+@interface Test : Subscriptable
+@end
+
+@implementation Test
+
+// <rdar://problem/9241180> for subscripting
+- (id)testUninitializedObject:(BOOL)keyed {
+ Test *o;
+ if (keyed) {
+ if (o[self]) // expected-warning {{Subscript access on an uninitialized object pointer [core.CallAndMessage]}}
+ return o; // no-warning (sink)
+ } else {
+ if (o[0]) // expected-warning {{Subscript access on an uninitialized object pointer [core.CallAndMessage]}}
+ return o; // no-warning (sink)
+ }
+ return self;
+}
+@end
+
+// TODO: If this hash ever changes, turn core.CallAndMessage:UndefReceiver from
+// a checker option into a checker, as described in the CallAndMessage comments!
+// CHECK: <key>issue_hash_content_of_line_in_context</key>
+// CHECK-SAME: <string>8d943563d78377fc5dfcd4fdde904e5e</string>
+// CHECK: <key>issue_hash_content_of_line_in_context</key>
+// CHECK-SAME: <string>9a2a9698763d62bed38d91fe5fb4aefd</string>
Index: clang/test/Analysis/call-and-message.cpp
===================================================================
--- /dev/null
+++ clang/test/Analysis/call-and-message.cpp
@@ -0,0 +1,172 @@
+// RUN: %clang_analyze_cc1 %s -verify=fn-pointer \
+// RUN: -analyzer-checker=core \
+// RUN: -analyzer-config core.CallAndMessage:FunctionPointer=true \
+// RUN: -analyzer-config core.CallAndMessage:ParameterCount=false \
+// RUN: -analyzer-config core.CallAndMessage:CXXThisMethodCall=false \
+// RUN: -analyzer-config core.CallAndMessage:CXXDeallocationArg=false \
+// RUN: -analyzer-config core.CallAndMessage:ArgInitializedness=false \
+// RUN: -analyzer-config core.CallAndMessage:ArgPointeeInitializedness=false \
+// RUN: -analyzer-config core.CallAndMessage:NilReceiver=false \
+// RUN: -analyzer-config core.CallAndMessage:UndefReceiver=false
+
+// RUN: %clang_analyze_cc1 %s -verify=param-count \
+// RUN: -analyzer-checker=core \
+// RUN: -analyzer-config core.CallAndMessage:FunctionPointer=false \
+// RUN: -analyzer-config core.CallAndMessage:ParameterCount=true \
+// RUN: -analyzer-config core.CallAndMessage:CXXThisMethodCall=false \
+// RUN: -analyzer-config core.CallAndMessage:CXXDeallocationArg=false \
+// RUN: -analyzer-config core.CallAndMessage:ArgInitializedness=false \
+// RUN: -analyzer-config core.CallAndMessage:ArgPointeeInitializedness=false \
+// RUN: -analyzer-config core.CallAndMessage:NilReceiver=false \
+// RUN: -analyzer-config core.CallAndMessage:UndefReceiver=false
+
+// RUN: %clang_analyze_cc1 %s -verify=method \
+// RUN: -analyzer-checker=core \
+// RUN: -analyzer-config core.CallAndMessage:FunctionPointer=false \
+// RUN: -analyzer-config core.CallAndMessage:ParameterCount=false \
+// RUN: -analyzer-config core.CallAndMessage:CXXThisMethodCall=true \
+// RUN: -analyzer-config core.CallAndMessage:CXXDeallocationArg=false \
+// RUN: -analyzer-config core.CallAndMessage:ArgInitializedness=false \
+// RUN: -analyzer-config core.CallAndMessage:ArgPointeeInitializedness=false \
+// RUN: -analyzer-config core.CallAndMessage:NilReceiver=false \
+// RUN: -analyzer-config core.CallAndMessage:UndefReceiver=false
+
+// RUN: %clang_analyze_cc1 %s -verify=delete \
+// RUN: -analyzer-checker=core \
+// RUN: -analyzer-config core.CallAndMessage:FunctionPointer=false \
+// RUN: -analyzer-config core.CallAndMessage:ParameterCount=false \
+// RUN: -analyzer-config core.CallAndMessage:CXXThisMethodCall=false \
+// RUN: -analyzer-config core.CallAndMessage:CXXDeallocationArg=true \
+// RUN: -analyzer-config core.CallAndMessage:ArgInitializedness=false \
+// RUN: -analyzer-config core.CallAndMessage:ArgPointeeInitializedness=false \
+// RUN: -analyzer-config core.CallAndMessage:NilReceiver=false \
+// RUN: -analyzer-config core.CallAndMessage:UndefReceiver=false
+
+// RUN: %clang_analyze_cc1 %s -verify=arg-init \
+// RUN: -analyzer-checker=core \
+// RUN: -analyzer-config core.CallAndMessage:FunctionPointer=false \
+// RUN: -analyzer-config core.CallAndMessage:ParameterCount=false \
+// RUN: -analyzer-config core.CallAndMessage:CXXThisMethodCall=false \
+// RUN: -analyzer-config core.CallAndMessage:CXXDeallocationArg=false \
+// RUN: -analyzer-config core.CallAndMessage:ArgInitializedness=true \
+// RUN: -analyzer-config core.CallAndMessage:ArgPointeeInitializedness=false \
+// RUN: -analyzer-config core.CallAndMessage:NilReceiver=false \
+// RUN: -analyzer-config core.CallAndMessage:UndefReceiver=false
+
+// Testing for ArgPointeeInitializedness is in call-and-message.c.
+
+// RUN: %clang_analyze_cc1 %s \
+// RUN: -verify=fn-pointer,param-count,method,delete,arg-init \
+// RUN: -analyzer-checker=core \
+// RUN: -analyzer-output=plist -o %t.plist
+// RUN: cat %t.plist | FileCheck %s
+
+namespace function_pointer {
+using Fn = void (*)();
+
+void uninit() {
+ Fn f;
+ f(); // fn-pointer-warning{{Called function pointer is an uninitialized pointer value [core.CallAndMessage]}}
+}
+
+void null() {
+ Fn f = nullptr;
+ f(); // fn-pointer-warning{{Called function pointer is null (null dereference) [core.CallAndMessage]}}
+}
+
+// TODO: If this hash ever changes, turn
+// core.CallAndMessage:FunctionPointer from a checker option into a
+// checker, as described in the CallAndMessage comments!
+// CHECK: <key>issue_hash_content_of_line_in_context</key>
+// CHECK-SAME: <string>eb2083c01775eef452afa75728dd4d8f</string>
+// CHECK: <key>issue_hash_content_of_line_in_context</key>
+// CHECK-SAME: <string>407c50d9bedd8db28bf34f9411308100</string>
+
+} // namespace function_pointer
+
+namespace wrong_param_count {
+using FnOneParam = void (*)(int);
+using FnTwoParam = void (*)(int, int);
+
+void f(int, int) {}
+
+void wrong_cast() {
+ FnTwoParam f1 = f;
+ FnOneParam f2 = reinterpret_cast<FnOneParam>(f1);
+ f2(5); // param-count-warning{{Function taking 2 arguments is called with fewer (1) [core.CallAndMessage]}}
+}
+
+// TODO: If this hash ever changes, turn
+// core.CallAndMessage:ParameterCount from a checker option into a
+// checker, as described in the CallAndMessage comments!
+// CHECK: <key>issue_hash_content_of_line_in_context</key>
+// CHECK-SAME: <string>9ff0e9b728422017945c9d5a673de223</string>
+} // namespace wrong_param_count
+
+namespace method_call {
+struct A {
+ void m();
+};
+
+void uninit() {
+ A *a;
+ a->m(); // method-warning{{Called C++ object pointer is uninitialized [core.CallAndMessage]}}
+}
+
+// TODO: If this hash ever changes, turn
+// core.CallAndMessage:CXXThisMethodCall from a checker option into a
+// checker, as described in the CallAndMessage comments!
+// CHECK: <key>issue_hash_content_of_line_in_context</key>
+// CHECK-SAME: <string>7bc35c70465837948a3f5018f27b21cd</string>
+
+void null() {
+ A *a = nullptr;
+ a->m(); // method-warning{{Called C++ object pointer is null [core.CallAndMessage]}}
+}
+
+// TODO: If this hash ever changes, turn
+// core.CallAndMessage:CXXThisMethodCall from a checker option into a
+// checker, as described in the CallAndMessage comments!
+// CHECK: <key>issue_hash_content_of_line_in_context</key>
+// CHECK-SAME: <string>8ec260c9ef11d7c51fa872212df1163f</string>
+} // namespace method_call
+
+namespace operator_delete {
+void f() {
+ int *i;
+ delete i; // delete-warning{{Argument to 'delete' is uninitialized [core.CallAndMessage]}}
+}
+
+// TODO: If this hash ever changes, turn
+// core.CallAndMessage:CXXDeallocationArg from a checker option into a
+// checker, as described in the CallAndMessage comments!
+// CHECK: <key>issue_hash_content_of_line_in_context</key>
+// CHECK-SAME: <string>a8ff99ebaa8746457d3e14af8ef7e75c</string>
+} // namespace operator_delete
+
+namespace uninit_arg {
+template <class T>
+void consume(T);
+
+void fundamental_uninit() {
+ int i;
+ consume(i); // arg-init-warning{{1st function call argument is an uninitialized value [core.CallAndMessage]}}
+}
+
+struct A {
+ int i;
+};
+
+void record_uninit() {
+ A a;
+ consume(a); // arg-init-warning{{Passed-by-value struct argument contains uninitialized data (e.g., field: 'i') [core.CallAndMessage]}}
+}
+
+// TODO: If this hash ever changes, turn
+// core.CallAndMessage:ArgInitializedness from a checker option into a
+// checker, as described in the CallAndMessage comments!
+// CHECK: <key>issue_hash_content_of_line_in_context</key>
+// CHECK-SAME: <string>a46bb5c1ee44d4611ffeb13f7f499605</string>
+// CHECK: <key>issue_hash_content_of_line_in_context</key>
+// CHECK-SAME: <string>e0e0d30ea5a7b2e3a71e1931fa0768a5</string>
+} // namespace uninit_arg
Index: clang/test/Analysis/call-and-message.c
===================================================================
--- /dev/null
+++ clang/test/Analysis/call-and-message.c
@@ -0,0 +1,24 @@
+// RUN: %clang_analyze_cc1 %s -verify \
+// RUN: -analyzer-checker=core \
+// RUN: -analyzer-config core.CallAndMessage:ArgPointeeInitializedness=true \
+// RUN: -analyzer-output=plist -o %t.plist
+// RUN: cat %t.plist | FileCheck %s
+
+// RUN: %clang_analyze_cc1 %s -verify=no-pointee \
+// RUN: -analyzer-checker=core \
+// RUN: -analyzer-config core.CallAndMessage:ArgPointeeInitializedness=false
+
+// no-pointee-no-diagnostics
+
+void doStuff_pointerToConstInt(const int *u){};
+void pointee_uninit() {
+ int i;
+ int *p = &i;
+ doStuff_pointerToConstInt(p); // expected-warning{{1st function call argument is a pointer to uninitialized value [core.CallAndMessage]}}
+}
+
+// TODO: If this hash ever changes, turn
+// core.CallAndMessage:ArgPointeeInitializedness from a checker option into a
+// checker, as described in the CallAndMessage comments!
+// CHECK: <key>issue_hash_content_of_line_in_context</key>
+// CHECK-SAME: <string>97a74322d64dca40aa57303842c745a1</string>
Index: clang/test/Analysis/analyzer-enabled-checkers.c
===================================================================
--- clang/test/Analysis/analyzer-enabled-checkers.c
+++ clang/test/Analysis/analyzer-enabled-checkers.c
@@ -7,11 +7,12 @@
// CHECK: OVERVIEW: Clang Static Analyzer Enabled Checkers List
// CHECK-EMPTY:
// CHECK-NEXT: apiModeling.StdCLibraryFunctions
-// CHECK-NEXT: core.CallAndMessage
+// CHECK-NEXT: core.CallAndMessageModeling
// CHECK-NEXT: apiModeling.StdCLibraryFunctionArgs
// CHECK-NEXT: apiModeling.TrustNonnull
// CHECK-NEXT: apiModeling.llvm.CastValue
// CHECK-NEXT: apiModeling.llvm.ReturnValue
+// CHECK-NEXT: core.CallAndMessage
// CHECK-NEXT: core.DivideZero
// CHECK-NEXT: core.DynamicTypePropagation
// CHECK-NEXT: core.NonNullParamChecker
Index: clang/test/Analysis/analyzer-config.c
===================================================================
--- clang/test/Analysis/analyzer-config.c
+++ clang/test/Analysis/analyzer-config.c
@@ -1,7 +1,7 @@
// RUN: %clang_analyze_cc1 -analyzer-checker=debug.ConfigDumper > %t 2>&1
// RUN: FileCheck --input-file=%t %s --match-full-lines
-// CHECK: [config]
+// CHECK: [config]
// CHECK-NEXT: add-pop-up-notes = true
// CHECK-NEXT: aggressive-binary-operation-simplification = false
// CHECK-NEXT: alpha.clone.CloneChecker:IgnoredFilesPattern = ""
@@ -28,6 +28,14 @@
// CHECK-NEXT: cfg-rich-constructors = true
// CHECK-NEXT: cfg-scopes = false
// CHECK-NEXT: cfg-temporary-dtors = true
+// CHECK-NEXT: core.CallAndMessage:ArgInitializedness = true
+// CHECK-NEXT: core.CallAndMessage:ArgPointeeInitializedness = false
+// CHECK-NEXT: core.CallAndMessage:CXXDeallocationArg = true
+// CHECK-NEXT: core.CallAndMessage:CXXThisMethodCall = true
+// CHECK-NEXT: core.CallAndMessage:FunctionPointer = true
+// CHECK-NEXT: core.CallAndMessage:NilReceiver = true
+// CHECK-NEXT: core.CallAndMessage:ParameterCount = true
+// CHECK-NEXT: core.CallAndMessage:UndefReceiver = true
// CHECK-NEXT: cplusplus.Move:WarnOn = KnownsAndLocals
// CHECK-NEXT: crosscheck-with-z3 = false
// CHECK-NEXT: ctu-dir = ""
@@ -106,4 +114,4 @@
// CHECK-NEXT: unroll-loops = false
// CHECK-NEXT: widen-loops = false
// CHECK-NEXT: [stats]
-// CHECK-NEXT: num-entries = 103
+// CHECK-NEXT: num-entries = 111
Index: clang/test/Analysis/PR40625.cpp
===================================================================
--- clang/test/Analysis/PR40625.cpp
+++ clang/test/Analysis/PR40625.cpp
@@ -1,4 +1,6 @@
-// RUN: %clang_analyze_cc1 -std=c++11 -analyzer-checker=core,alpha.core.CallAndMessageUnInitRefArg %s -verify
+// RUN: %clang_analyze_cc1 %s -verify \
+// RUN: -analyzer-checker=core \
+// RUN: -analyzer-config core.CallAndMessage:ArgPointeeInitializedness=true
void f(const int *end);
Index: clang/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp
===================================================================
--- clang/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp
+++ clang/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp
@@ -49,9 +49,36 @@
mutable std::unique_ptr<BugType> BT_call_few_args;
public:
- enum CheckKind { CK_CallAndMessageUnInitRefArg, CK_NumCheckKinds };
+ // These correspond with the checker options. Looking at other checkers such
+ // as MallocChecker and CStringChecker, this is similar as to how they pull
+ // off having a modeling class, but emitting diagnostics under a smaller
+ // checker's name that can be safely disabled without disturbing the
+ // underlaying modeling engine.
+ // The reason behind having *checker options* rather then actual *checkers*
+ // here is that CallAndMessage is among the oldest checkers out there, and can
+ // be responsible for the majority of the reports on any given project. This
+ // is obviously not ideal, but changing checker name has the consequence of
+ // changing the issue hashes associated with the reports, and databases
+ // relying on this (CodeChecker, for instance) would suffer greatly.
+ // If we ever end up making changes to the issue hash generation algorithm, or
+ // the warning messages here, we should totally jump on the opportunity to
+ // convert these to actual checkers.
+ enum CheckKind {
+ CK_FunctionPointer,
+ CK_ParameterCount,
+ CK_CXXThisMethodCall,
+ CK_CXXDeallocationArg,
+ CK_ArgInitializedness,
+ CK_ArgPointeeInitializedness,
+ CK_NilReceiver,
+ CK_UndefReceiver,
+ CK_NumCheckKinds
+ };
DefaultBool ChecksEnabled[CK_NumCheckKinds];
+ // The original core.CallAndMessage checker name. This should rather be an
+ // array, as seen in MallocChecker and CStringChecker.
+ CheckerNameRef OriginalName;
void checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const;
@@ -98,7 +125,7 @@
void LazyInit_BT(const char *desc, std::unique_ptr<BugType> &BT) const {
if (!BT)
- BT.reset(new BuiltinBug(this, desc));
+ BT.reset(new BuiltinBug(OriginalName, desc));
}
bool uninitRefOrPointer(CheckerContext &C, const SVal &V,
SourceRange ArgRange, const Expr *ArgEx,
@@ -163,7 +190,10 @@
CheckerContext &C, const SVal &V, SourceRange ArgRange, const Expr *ArgEx,
std::unique_ptr<BugType> &BT, const ParmVarDecl *ParamDecl, const char *BD,
int ArgumentNumber) const {
- if (!ChecksEnabled[CK_CallAndMessageUnInitRefArg])
+
+ // The pointee being uninitialized is a sign of code smell, not a bug, no need
+ // to sink here.
+ if (!ChecksEnabled[CK_ArgPointeeInitializedness])
return false;
// No parameter declaration available, i.e. variadic function argument.
@@ -265,6 +295,10 @@
return true;
if (V.isUndef()) {
+ if (!ChecksEnabled[CK_ArgInitializedness]) {
+ C.addSink();
+ return true;
+ }
if (ExplodedNode *N = C.generateErrorNode()) {
LazyInit_BT(BD, BT);
// Generate a report for this bug.
@@ -291,6 +325,10 @@
D->getStore());
if (F.Find(D->getRegion())) {
+ if (!ChecksEnabled[CK_ArgInitializedness]) {
+ C.addSink();
+ return true;
+ }
if (ExplodedNode *N = C.generateErrorNode()) {
LazyInit_BT(BD, BT);
SmallString<512> Str;
@@ -338,9 +376,14 @@
SVal L = State->getSVal(Callee, LCtx);
if (L.isUndef()) {
+ if (!ChecksEnabled[CK_FunctionPointer]) {
+ C.addSink(State);
+ return nullptr;
+ }
if (!BT_call_undef)
BT_call_undef.reset(new BuiltinBug(
- this, "Called function pointer is an uninitialized pointer value"));
+ OriginalName,
+ "Called function pointer is an uninitialized pointer value"));
emitBadCall(BT_call_undef.get(), C, Callee);
return nullptr;
}
@@ -349,9 +392,13 @@
std::tie(StNonNull, StNull) = State->assume(L.castAs<DefinedOrUnknownSVal>());
if (StNull && !StNonNull) {
+ if (!ChecksEnabled[CK_FunctionPointer]) {
+ C.addSink(StNull);
+ return nullptr;
+ }
if (!BT_call_null)
BT_call_null.reset(new BuiltinBug(
- this, "Called function pointer is null (null dereference)"));
+ OriginalName, "Called function pointer is null (null dereference)"));
emitBadCall(BT_call_null.get(), C, Callee);
return nullptr;
}
@@ -368,6 +415,11 @@
if (Call.getNumArgs() >= Params)
return State;
+ if (!ChecksEnabled[CK_ParameterCount]) {
+ C.addSink(State);
+ return nullptr;
+ }
+
ExplodedNode *N = C.generateErrorNode();
if (!N)
return nullptr;
@@ -395,9 +447,13 @@
SVal V = CC->getCXXThisVal();
if (V.isUndef()) {
+ if (!ChecksEnabled[CK_CXXThisMethodCall]) {
+ C.addSink(State);
+ return nullptr;
+ }
if (!BT_cxx_call_undef)
- BT_cxx_call_undef.reset(
- new BuiltinBug(this, "Called C++ object pointer is uninitialized"));
+ BT_cxx_call_undef.reset(new BuiltinBug(
+ OriginalName, "Called C++ object pointer is uninitialized"));
emitBadCall(BT_cxx_call_undef.get(), C, CC->getCXXThisExpr());
return nullptr;
}
@@ -406,9 +462,13 @@
std::tie(StNonNull, StNull) = State->assume(V.castAs<DefinedOrUnknownSVal>());
if (StNull && !StNonNull) {
+ if (!ChecksEnabled[CK_CXXThisMethodCall]) {
+ C.addSink(StNull);
+ return nullptr;
+ }
if (!BT_cxx_call_null)
BT_cxx_call_null.reset(
- new BuiltinBug(this, "Called C++ object pointer is null"));
+ new BuiltinBug(OriginalName, "Called C++ object pointer is null"));
emitBadCall(BT_cxx_call_null.get(), C, CC->getCXXThisExpr());
return nullptr;
}
@@ -426,13 +486,18 @@
if (!Arg.isUndef())
return State;
+ if (!ChecksEnabled[CK_CXXDeallocationArg]) {
+ C.addSink(State);
+ return nullptr;
+ }
+
StringRef Desc;
ExplodedNode *N = C.generateErrorNode();
if (!N)
return nullptr;
if (!BT_cxx_delete_undef)
BT_cxx_delete_undef.reset(
- new BuiltinBug(this, "Uninitialized argument value"));
+ new BuiltinBug(OriginalName, "Uninitialized argument value"));
if (DE->isArrayFormAsWritten())
Desc = "Argument to 'delete[]' is uninitialized";
else
@@ -515,12 +580,16 @@
CheckerContext &C) const {
SVal recVal = msg.getReceiverSVal();
if (recVal.isUndef()) {
+ if (!ChecksEnabled[CK_UndefReceiver]) {
+ C.addSink();
+ return;
+ }
if (ExplodedNode *N = C.generateErrorNode()) {
BugType *BT = nullptr;
switch (msg.getMessageKind()) {
case OCM_Message:
if (!BT_msg_undef)
- BT_msg_undef.reset(new BuiltinBug(this,
+ BT_msg_undef.reset(new BuiltinBug(OriginalName,
"Receiver in message expression "
"is an uninitialized value"));
BT = BT_msg_undef.get();
@@ -528,13 +597,15 @@
case OCM_PropertyAccess:
if (!BT_objc_prop_undef)
BT_objc_prop_undef.reset(new BuiltinBug(
- this, "Property access on an uninitialized object pointer"));
+ OriginalName,
+ "Property access on an uninitialized object pointer"));
BT = BT_objc_prop_undef.get();
break;
case OCM_Subscript:
if (!BT_objc_subscript_undef)
BT_objc_subscript_undef.reset(new BuiltinBug(
- this, "Subscript access on an uninitialized object pointer"));
+ OriginalName,
+ "Subscript access on an uninitialized object pointer"));
BT = BT_objc_subscript_undef.get();
break;
}
@@ -561,10 +632,14 @@
void CallAndMessageChecker::emitNilReceiverBug(CheckerContext &C,
const ObjCMethodCall &msg,
ExplodedNode *N) const {
+ if (!ChecksEnabled[CK_NilReceiver]) {
+ C.addSink();
+ return;
+ }
if (!BT_msg_ret)
- BT_msg_ret.reset(
- new BuiltinBug(this, "Receiver in message expression is 'nil'"));
+ BT_msg_ret.reset(new BuiltinBug(OriginalName,
+ "Receiver in message expression is 'nil'"));
const ObjCMessageExpr *ME = msg.getOriginExpr();
@@ -659,21 +734,34 @@
C.addTransition(state);
}
-void ento::registerCallAndMessageChecker(CheckerManager &mgr) {
+void ento::registerCallAndMessageModeling(CheckerManager &mgr) {
mgr.registerChecker<CallAndMessageChecker>();
}
-bool ento::shouldRegisterCallAndMessageChecker(const CheckerManager &mgr) {
+bool ento::shouldRegisterCallAndMessageModeling(const CheckerManager &mgr) {
return true;
}
-#define REGISTER_CHECKER(name) \
- void ento::register##name(CheckerManager &mgr) { \
- CallAndMessageChecker *checker = mgr.getChecker<CallAndMessageChecker>(); \
- checker->ChecksEnabled[CallAndMessageChecker::CK_##name] = true; \
- \
- } \
- \
- bool ento::shouldRegister##name(const CheckerManager &mgr) { return true; }
+void ento::registerCallAndMessageChecker(CheckerManager &mgr) {
+ CallAndMessageChecker *checker = mgr.getChecker<CallAndMessageChecker>();
+
+ checker->OriginalName = mgr.getCurrentCheckerName();
+
+#define QUERY_CHECKER_OPTION(OPTION) \
+ checker->ChecksEnabled[CallAndMessageChecker::CK_##OPTION] = \
+ mgr.getAnalyzerOptions().getCheckerBooleanOption( \
+ mgr.getCurrentCheckerName(), #OPTION);
+
+ QUERY_CHECKER_OPTION(FunctionPointer)
+ QUERY_CHECKER_OPTION(ParameterCount)
+ QUERY_CHECKER_OPTION(CXXThisMethodCall)
+ QUERY_CHECKER_OPTION(CXXDeallocationArg)
+ QUERY_CHECKER_OPTION(ArgInitializedness)
+ QUERY_CHECKER_OPTION(ArgPointeeInitializedness)
+ QUERY_CHECKER_OPTION(NilReceiver)
+ QUERY_CHECKER_OPTION(UndefReceiver)
+}
-REGISTER_CHECKER(CallAndMessageUnInitRefArg)
+bool ento::shouldRegisterCallAndMessageChecker(const CheckerManager &mgr) {
+ return true;
+}
Index: clang/include/clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h
===================================================================
--- clang/include/clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h
+++ clang/include/clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h
@@ -175,8 +175,7 @@
/// @param Pred The transition will be generated from the specified Pred node
/// to the newly generated node.
/// @param Tag The tag to uniquely identify the creation site.
- ExplodedNode *addTransition(ProgramStateRef State,
- ExplodedNode *Pred,
+ ExplodedNode *addTransition(ProgramStateRef State, ExplodedNode *Pred,
const ProgramPointTag *Tag = nullptr) {
return addTransitionImpl(State, false, Pred, Tag);
}
@@ -189,6 +188,14 @@
return addTransitionImpl(State ? State : getState(), true, Pred, Tag);
}
+ /// Add a sink node to the current path of execution, halting analysis.
+ void addSink(ProgramStateRef State = nullptr,
+ const ProgramPointTag *Tag = nullptr) {
+ // Say this 3 times fast.
+ State = State ? State : getState();
+ addTransition(State, generateSink(State, getPredecessor()));
+ }
+
/// Generate a transition to a node that will be used to report
/// an error. This node will be a sink. That is, it will stop exploration of
/// the given path.
Index: clang/include/clang/StaticAnalyzer/Core/BugReporter/BugType.h
===================================================================
--- clang/include/clang/StaticAnalyzer/Core/BugReporter/BugType.h
+++ clang/include/clang/StaticAnalyzer/Core/BugReporter/BugType.h
@@ -78,13 +78,16 @@
const char *description)
: BugType(checker, name, categories::LogicError), desc(description) {}
+ BuiltinBug(class CheckerNameRef checker, const char *name)
+ : BugType(checker, name, categories::LogicError), desc(name) {}
+
BuiltinBug(const CheckerBase *checker, const char *name)
: BugType(checker, name, categories::LogicError), desc(name) {}
StringRef getDescription() const { return desc; }
};
-} // end ento namespace
+} // namespace ento
} // end clang namespace
#endif
Index: clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
===================================================================
--- clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
+++ clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
@@ -120,14 +120,70 @@
let ParentPackage = Core in {
-def DereferenceChecker : Checker<"NullDereference">,
- HelpText<"Check for dereferences of null pointers">,
+def CallAndMessageModeling : Checker<"CallAndMessageModeling">,
+ HelpText<"Responsible for essential modeling and assumptions after a "
+ "function/method call. For instance, if we can't reason about the "
+ "nullability of the implicit this parameter after a method call, "
+ "this checker conservatively assumes it to be non-null">,
Documentation<HasDocumentation>;
def CallAndMessageChecker : Checker<"CallAndMessage">,
HelpText<"Check for logical errors for function calls and Objective-C "
"message expressions (e.g., uninitialized arguments, null function "
"pointers)">,
+ CheckerOptions<[
+ CmdLineOption<Boolean,
+ "FunctionPointer",
+ "Check whether a called function pointer is null or "
+ "undefined",
+ "true",
+ Released>,
+ CmdLineOption<Boolean,
+ "ParameterCount",
+ "Check whether a function was called with the appropriate "
+ "number of arguments",
+ "true",
+ Released>,
+ CmdLineOption<Boolean,
+ "CXXThisMethodCall",
+ "Check whether the implicit this parameter is null or "
+ "undefined upon a method call",
+ "true",
+ Released>,
+ CmdLineOption<Boolean,
+ "CXXDeallocationArg",
+ "Check whether the argument of operator delete is undefined",
+ "true",
+ Released>,
+ CmdLineOption<Boolean,
+ "ArgInitializedness",
+ "Check whether any of the pass-by-value parameters is "
+ "undefined",
+ "true",
+ Released>,
+ CmdLineOption<Boolean,
+ "ArgPointeeInitializedness",
+ "Check whether the pointee of a pass-by-reference or "
+ "pass-by-pointer is undefined",
+ "false",
+ InAlpha>,
+ CmdLineOption<Boolean,
+ "NilReceiver",
+ "Check whether the reciever in the message expression is nil",
+ "true",
+ Released>,
+ CmdLineOption<Boolean,
+ "UndefReceiver",
+ "Check whether the reciever in the message expression is "
+ "undefined",
+ "true",
+ Released>,
+ ]>,
+ Documentation<HasDocumentation>,
+ Dependencies<[CallAndMessageModeling]>;
+
+def DereferenceChecker : Checker<"NullDereference">,
+ HelpText<"Check for dereferences of null pointers">,
Documentation<HasDocumentation>;
def NonNullParamChecker : Checker<"NonNullParamChecker">,
@@ -209,13 +265,6 @@
HelpText<"Warn about unintended use of sizeof() on pointer expressions">,
Documentation<HasAlphaDocumentation>;
-def CallAndMessageUnInitRefArg : Checker<"CallAndMessageUnInitRefArg">,
- HelpText<"Check for logical errors for function calls and Objective-C "
- "message expressions (e.g., uninitialized arguments, null function "
- "pointers, and pointer to undefined variables)">,
- Dependencies<[CallAndMessageChecker]>,
- Documentation<HasAlphaDocumentation>;
-
def TestAfterDivZeroChecker : Checker<"TestAfterDivZero">,
HelpText<"Check for division by variable that is later compared against 0. "
"Either the comparison is useless or there is division by zero.">,
@@ -299,7 +348,7 @@
HelpText<"Check constraints of arguments of C standard library functions, "
"such as whether the parameter of isalpha is in the range [0, 255] "
"or is EOF.">,
- Dependencies<[StdCLibraryFunctionsChecker, CallAndMessageChecker]>,
+ Dependencies<[StdCLibraryFunctionsChecker, CallAndMessageModeling]>,
Documentation<NotDocumented>;
def TrustNonnullChecker : Checker<"TrustNonnull">,
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits