[clang] 1c8f999 - [analyzer][CallAndMessage] Add checker options for each bug type
Kirstóf Umann via cfe-commits
cfe-commits at lists.llvm.org
Thu May 21 06:33:20 PDT 2020
Author: Kirstóf Umann
Date: 2020-05-21T15:31:37+02:00
New Revision: 1c8f999e0b59731a4214f76528f83e4196e1fcc3
URL: https://github.com/llvm/llvm-project/commit/1c8f999e0b59731a4214f76528f83e4196e1fcc3
DIFF: https://github.com/llvm/llvm-project/commit/1c8f999e0b59731a4214f76528f83e4196e1fcc3.diff
LOG: [analyzer][CallAndMessage] Add checker options for each bug type
iAs listed in the summary 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 (it was dead code), so that checker didn't really exist in any
meaningful way anyways.
Differential Revision: https://reviews.llvm.org/D77866
Added:
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
Modified:
clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
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/exercise-ps.c
clang/test/Analysis/uninit-const.c
clang/test/Analysis/uninit-const.cpp
Removed:
clang/test/Analysis/reference.mm
clang/test/Analysis/uninit-msg-expr.m
################################################################################
diff --git a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
index d06c64ad118b..93c4d964d772 100644
--- a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
+++ b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
@@ -122,14 +122,70 @@ def FuchsiaAlpha : Package<"fuchsia">, ParentPackage<Alpha>;
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">,
@@ -211,13 +267,6 @@ def SizeofPointerChecker : Checker<"SizeofPtr">,
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.">,
@@ -295,7 +344,7 @@ let ParentPackage = APIModeling in {
def StdCLibraryFunctionsChecker : Checker<"StdCLibraryFunctions">,
HelpText<"Improve modeling of the C standard library functions">,
- Dependencies<[NonNullParamChecker, CallAndMessageChecker]>,
+ Dependencies<[NonNullParamChecker, CallAndMessageModeling]>,
CheckerOptions<[
CmdLineOption<Boolean,
"DisplayLoadedSummaries",
diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h
index 2b5d37b6cc41..8fd7f52585b8 100644
--- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h
+++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h
@@ -175,8 +175,7 @@ class CheckerContext {
/// @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 @@ class CheckerContext {
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) {
+ if (!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.
diff --git a/clang/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp
index 58eb329d1bf9..3e46e2372516 100644
--- a/clang/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp
@@ -47,9 +47,36 @@ class CallAndMessageChecker
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;
@@ -96,7 +123,7 @@ class CallAndMessageChecker
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,
@@ -161,7 +188,10 @@ bool CallAndMessageChecker::uninitRefOrPointer(
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.
@@ -263,6 +293,10 @@ bool CallAndMessageChecker::PreVisitProcessArg(CheckerContext &C,
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 @@ bool CallAndMessageChecker::PreVisitProcessArg(CheckerContext &C,
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 @@ ProgramStateRef CallAndMessageChecker::checkFunctionPointerCall(
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 @@ ProgramStateRef CallAndMessageChecker::checkFunctionPointerCall(
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;
}
@@ -366,6 +413,11 @@ ProgramStateRef CallAndMessageChecker::checkParameterCount(
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 @@ ProgramStateRef CallAndMessageChecker::checkCXXMethodCall(
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 @@ ProgramStateRef CallAndMessageChecker::checkCXXMethodCall(
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;
}
@@ -424,13 +484,18 @@ CallAndMessageChecker::checkCXXDeallocation(const CXXDeallocatorCall *DC,
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 @@ void CallAndMessageChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
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 @@ void CallAndMessageChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
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::checkObjCMessageNil(const ObjCMethodCall &msg,
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 @@ void CallAndMessageChecker::HandleNilReceiver(CheckerContext &C,
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;
+}
diff --git a/clang/test/Analysis/PR40625.cpp b/clang/test/Analysis/PR40625.cpp
index ac23a71c1c6a..5ebe2122945e 100644
--- 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
index 85cd6738b6dc..778467387382 100644
--- 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
index ad195b086d87..028754f0fbed 100644
--- 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
index 000000000000..da62a6f5071a
--- /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: <key>issue_hash_content_of_line_in_context</key>
+// CHECK-SAME: <string>97a74322d64dca40aa57303842c745a1</string>
diff --git a/clang/test/Analysis/call-and-message.cpp b/clang/test/Analysis/call-and-message.cpp
new file mode 100644
index 000000000000..25ae23833478
--- /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: <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
diff --git a/clang/test/Analysis/call-and-message.m b/clang/test/Analysis/call-and-message.m
new file mode 100644
index 000000000000..cef501400b72
--- /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;
+ at class NSInvocation, NSMethodSignature, NSCoder, NSString, NSEnumerator;
+ at protocol NSObject
+- (BOOL)isEqual:(id)object;
+ at end
+ at protocol NSCopying
+- (id)copyWithZone:(NSZone *)zone;
+ at end
+ at protocol NSMutableCopying
+- (id)mutableCopyWithZone:(NSZone *)zone;
+ at end
+ at protocol NSCoding
+- (void)encodeWithCoder:(NSCoder *)aCoder;
+ at end
+ at interface NSObject <NSObject> {
+}
+ at end
+ at class NSString, NSData;
+ at class NSString, NSData, NSMutableData, NSMutableDictionary, NSMutableArray;
+typedef struct {
+} NSFastEnumerationState;
+ at protocol NSFastEnumeration
+- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id *)stackbuf count:(NSUInteger)len;
+ at end
+ at class NSData, NSIndexSet, NSString, NSURL;
+ at interface NSArray : NSObject <NSCopying, NSMutableCopying, NSCoding, NSFastEnumeration>
+- (NSUInteger)count;
+ at end
+ at interface NSArray (NSArrayCreation)
++ (id)array;
+- (NSUInteger)length;
+- (void)addObject:(id)object;
+ at 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>
+
+ at interface RDar9241180
+ at property(readwrite, assign) id x;
+- (id)testAnalyzer1:(int)y;
+ at end
+
+ at implementation RDar9241180
+ at 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]}}
+}
+ at end
+
+// CHECK: <key>issue_hash_content_of_line_in_context</key>
+// CHECK-SAME: <string>8d468e24df7d887f4182bf49f5dd8b71</string>
+
+typedef signed char BOOL;
+typedef unsigned int NSUInteger;
+
+ at 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;
+ at end
+
+ at interface Test : Subscriptable
+ at end
+
+ at 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;
+}
+ at 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>
diff --git a/clang/test/Analysis/call-and-message.mm b/clang/test/Analysis/call-and-message.mm
new file mode 100644
index 000000000000..4a1d88f2f098
--- /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
+
+ at interface Foo
+- (int &)ref;
+ at 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>
diff --git a/clang/test/Analysis/exercise-ps.c b/clang/test/Analysis/exercise-ps.c
index c459260c2a1d..9bcda12b0b26 100644
--- 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
index 1d73ccd6ead3..000000000000
--- a/clang/test/Analysis/reference.mm
+++ /dev/null
@@ -1,17 +0,0 @@
-// RUN: %clang_analyze_cc1 -analyzer-checker=core -verify -Wno-null-dereference %s
-
- at interface Foo
-- (int &)ref;
- at 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
index ec29c236069c..051c6dd510cb 100644
--- 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
index 3b7089cbcacd..1920a42fb8d0 100644
--- 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
index 8454137967f6..000000000000
--- 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;
- at class NSInvocation, NSMethodSignature, NSCoder, NSString, NSEnumerator;
- at protocol NSObject - (BOOL)isEqual:(id)object; @end
- at protocol NSCopying - (id)copyWithZone:(NSZone *)zone; @end
- at protocol NSMutableCopying - (id)mutableCopyWithZone:(NSZone *)zone; @end
- at protocol NSCoding - (void)encodeWithCoder:(NSCoder *)aCoder; @end
- at interface NSObject <NSObject> {} @end
- at class NSString, NSData;
- at class NSString, NSData, NSMutableData, NSMutableDictionary, NSMutableArray;
-typedef struct {} NSFastEnumerationState;
- at protocol NSFastEnumeration
-- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id *)stackbuf count:(NSUInteger)len;
- at end
- at class NSData, NSIndexSet, NSString, NSURL;
- at interface NSArray : NSObject <NSCopying, NSMutableCopying, NSCoding, NSFastEnumeration>
-- (NSUInteger)count;
- at end
- at interface NSArray (NSArrayCreation)
-+ (id)array;
-- (NSUInteger)length;
-- (void)addObject:(id)object;
- at 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}}
-}
More information about the cfe-commits
mailing list