diff --git a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td --- a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td +++ b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td @@ -122,14 +122,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; 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, + CmdLineOption, + CmdLineOption, + CmdLineOption, + CmdLineOption, + CmdLineOption, + CmdLineOption, + CmdLineOption, + ]>, + Documentation, + Dependencies<[CallAndMessageModeling]>; + +def DereferenceChecker : Checker<"NullDereference">, + HelpText<"Check for dereferences of null pointers">, Documentation; def NonNullParamChecker : Checker<"NonNullParamChecker">, @@ -211,13 +267,6 @@ HelpText<"Warn about unintended use of sizeof() on pointer expressions">, Documentation; -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; - 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.">, @@ -295,7 +344,7 @@ def StdCLibraryFunctionsChecker : Checker<"StdCLibraryFunctions">, HelpText<"Improve modeling of the C standard library functions">, - Dependencies<[NonNullParamChecker, CallAndMessageChecker]>, + Dependencies<[NonNullParamChecker, CallAndMessageModeling]>, CheckerOptions<[ CmdLineOption 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; @@ -96,7 +123,7 @@ void LazyInit_BT(const char *desc, std::unique_ptr &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, @@ -161,7 +188,10 @@ CheckerContext &C, const SVal &V, SourceRange ArgRange, const Expr *ArgEx, std::unique_ptr &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. @@ -263,6 +293,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. @@ -289,6 +323,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; @@ -336,9 +374,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; } @@ -347,9 +390,13 @@ std::tie(StNonNull, StNull) = State->assume(L.castAs()); 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; } @@ -366,6 +413,11 @@ if (Call.getNumArgs() >= Params) return State; + if (!ChecksEnabled[CK_ParameterCount]) { + C.addSink(State); + return nullptr; + } + ExplodedNode *N = C.generateErrorNode(); if (!N) return nullptr; @@ -393,9 +445,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; } @@ -404,9 +460,13 @@ std::tie(StNonNull, StNull) = State->assume(V.castAs()); 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; } @@ -424,13 +484,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 @@ -511,12 +576,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(); @@ -524,13 +593,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; } @@ -557,10 +628,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(); @@ -655,21 +730,34 @@ C.addTransition(state); } -void ento::registerCallAndMessageChecker(CheckerManager &mgr) { +void ento::registerCallAndMessageModeling(CheckerManager &mgr) { mgr.registerChecker(); } -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(); \ - checker->ChecksEnabled[CallAndMessageChecker::CK_##name] = true; \ - \ - } \ - \ - bool ento::shouldRegister##name(const CheckerManager &mgr) { return true; } +void ento::registerCallAndMessageChecker(CheckerManager &mgr) { + CallAndMessageChecker *checker = mgr.getChecker(); + + 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; +} diff --git a/clang/test/Analysis/PR40625.cpp b/clang/test/Analysis/PR40625.cpp --- a/clang/test/Analysis/PR40625.cpp +++ b/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); diff --git a/clang/test/Analysis/analyzer-config.c b/clang/test/Analysis/analyzer-config.c --- a/clang/test/Analysis/analyzer-config.c +++ b/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 = "" @@ -30,6 +30,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 = "" diff --git a/clang/test/Analysis/analyzer-enabled-checkers.c b/clang/test/Analysis/analyzer-enabled-checkers.c --- a/clang/test/Analysis/analyzer-enabled-checkers.c +++ b/clang/test/Analysis/analyzer-enabled-checkers.c @@ -7,11 +7,12 @@ // CHECK: OVERVIEW: Clang Static Analyzer Enabled Checkers List // CHECK-EMPTY: // CHECK-NEXT: core.NonNullParamChecker -// CHECK-NEXT: core.CallAndMessage +// CHECK-NEXT: core.CallAndMessageModeling // CHECK-NEXT: apiModeling.StdCLibraryFunctions // 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.NonnilStringConstants diff --git a/clang/test/Analysis/call-and-message.c b/clang/test/Analysis/call-and-message.c new file mode 100644 --- /dev/null +++ b/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: issue_hash_content_of_line_in_context +// CHECK-SAME: 97a74322d64dca40aa57303842c745a1 diff --git a/clang/test/Analysis/call-and-message.cpp b/clang/test/Analysis/call-and-message.cpp new file mode 100644 --- /dev/null +++ b/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: issue_hash_content_of_line_in_context +// CHECK-SAME: eb2083c01775eef452afa75728dd4d8f +// CHECK: issue_hash_content_of_line_in_context +// CHECK-SAME: 407c50d9bedd8db28bf34f9411308100 + +} // 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(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: issue_hash_content_of_line_in_context +// CHECK-SAME: 9ff0e9b728422017945c9d5a673de223 +} // 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: issue_hash_content_of_line_in_context +// CHECK-SAME: 7bc35c70465837948a3f5018f27b21cd + +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: issue_hash_content_of_line_in_context +// CHECK-SAME: 8ec260c9ef11d7c51fa872212df1163f +} // 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: issue_hash_content_of_line_in_context +// CHECK-SAME: a8ff99ebaa8746457d3e14af8ef7e75c +} // namespace operator_delete + +namespace uninit_arg { +template +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: issue_hash_content_of_line_in_context +// CHECK-SAME: a46bb5c1ee44d4611ffeb13f7f499605 +// CHECK: issue_hash_content_of_line_in_context +// CHECK-SAME: e0e0d30ea5a7b2e3a71e1931fa0768a5 +} // namespace uninit_arg diff --git a/clang/test/Analysis/call-and-message.m b/clang/test/Analysis/call-and-message.m new file mode 100644 --- /dev/null +++ b/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 { +} +@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 +- (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: issue_hash_content_of_line_in_context +// CHECK-SAME: 29873175e1cc0a98f7040057279925a0 + +@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: issue_hash_content_of_line_in_context + // CHECK-SAME: 00ddd30796a283de33e662da8449c796 + + return o; // expected-warning {{Undefined or garbage value returned to caller [core.uninitialized.UndefReturn]}} +} +@end + +// CHECK: issue_hash_content_of_line_in_context +// CHECK-SAME: 8d468e24df7d887f4182bf49f5dd8b71 + +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 + +// 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: issue_hash_content_of_line_in_context +// CHECK-SAME: 8d943563d78377fc5dfcd4fdde904e5e +// CHECK: issue_hash_content_of_line_in_context +// CHECK-SAME: 9a2a9698763d62bed38d91fe5fb4aefd diff --git a/clang/test/Analysis/call-and-message.mm b/clang/test/Analysis/call-and-message.mm new file mode 100644 --- /dev/null +++ b/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: issue_hash_content_of_line_in_context +// CHECK-SAME: abe2e0574dd901094c511bae2f93f926 diff --git a/clang/test/Analysis/exercise-ps.c b/clang/test/Analysis/exercise-ps.c --- a/clang/test/Analysis/exercise-ps.c +++ b/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++; diff --git a/clang/test/Analysis/reference.mm b/clang/test/Analysis/reference.mm deleted file mode 100644 --- a/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}} -} - diff --git a/clang/test/Analysis/uninit-const.c b/clang/test/Analysis/uninit-const.c --- a/clang/test/Analysis/uninit-const.c +++ b/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(); diff --git a/clang/test/Analysis/uninit-const.cpp b/clang/test/Analysis/uninit-const.cpp --- a/clang/test/Analysis/uninit-const.cpp +++ b/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" diff --git a/clang/test/Analysis/uninit-msg-expr.m b/clang/test/Analysis/uninit-msg-expr.m deleted file mode 100644 --- a/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 {} @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 -- (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}} -}