[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
This commit is contained in:
Kirstóf Umann 2020-04-05 19:53:22 +02:00
parent 361e4f14e3
commit 1c8f999e0b
15 changed files with 577 additions and 119 deletions

View File

@ -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",

View File

@ -175,8 +175,7 @@ public:
/// @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 @@ public:
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.

View File

@ -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 @@ private:
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::shouldRegisterCallAndMessageModeling(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)
}
bool ento::shouldRegisterCallAndMessageChecker(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; }
REGISTER_CHECKER(CallAndMessageUnInitRefArg)

View File

@ -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);

View File

@ -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 = ""

View File

@ -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

View File

@ -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>

View File

@ -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

View File

@ -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>

View File

@ -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>

View File

@ -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++;

View File

@ -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}}
}

View File

@ -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();

View File

@ -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"

View File

@ -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}}
}