aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/clang/Basic/DiagnosticSemaKinds.td17
-rw-r--r--include/clang/Sema/Sema.h9
-rw-r--r--lib/Sema/SemaChecking.cpp70
-rw-r--r--lib/Sema/SemaExpr.cpp27
-rw-r--r--test/SemaCXX/printf-block.cpp2
-rw-r--r--test/SemaCXX/printf-cstr.cpp2
-rw-r--r--test/SemaObjC/format-strings-objc.m8
-rw-r--r--test/SemaObjC/method-bad-param.m2
8 files changed, 83 insertions, 54 deletions
diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td
index 2da36b8269..18b63decee 100644
--- a/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/include/clang/Basic/DiagnosticSemaKinds.td
@@ -4891,16 +4891,19 @@ def err_ref_bad_target : Error<
"reference to %select{__device__|__global__|__host__|__host__ __device__}0 "
"function %1 in %select{__device__|__global__|__host__|__host__ __device__}2 function">;
-
-def err_cannot_pass_objc_interface_to_vararg : Error<
- "cannot pass object with interface type %0 by-value through variadic "
- "%select{function|block|method}1">;
-
def warn_non_pod_vararg_with_format_string : Warning<
"cannot pass %select{non-POD|non-trivial}0 object of type %1 to variadic "
- "function; expected type from format string was %2">,
- InGroup<DiagGroup<"non-pod-varargs">>, DefaultError;
+ "%select{function|block|method|constructor}2; expected type from format "
+ "string was %3">, InGroup<DiagGroup<"non-pod-varargs">>, DefaultError;
+// The arguments to this diagnostic should match the warning above.
+def err_cannot_pass_objc_interface_to_vararg_format : Error<
+ "cannot pass object with interface type %1 by value to variadic "
+ "%select{function|block|method|constructor}2; expected type from format "
+ "string was %3">;
+def err_cannot_pass_objc_interface_to_vararg : Error<
+ "cannot pass object with interface type %0 by value through variadic "
+ "%select{function|block|method|constructor}1">;
def warn_cannot_pass_non_pod_arg_to_vararg : Warning<
"cannot pass object of %select{non-POD|non-trivial}0 type %1 through variadic"
" %select{function|block|method|constructor}2; call will abort at runtime">,
diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h
index 4cd9a72c7c..6fa762da70 100644
--- a/include/clang/Sema/Sema.h
+++ b/include/clang/Sema/Sema.h
@@ -6455,7 +6455,7 @@ public:
bool AllowExplicit = false);
// DefaultVariadicArgumentPromotion - Like DefaultArgumentPromotion, but
- // will return ExprError() if the resulting type is not a POD type.
+ // will create a runtime trap if the resulting type is not a POD type.
ExprResult DefaultVariadicArgumentPromotion(Expr *E, VariadicCallType CT,
FunctionDecl *FDecl);
@@ -7117,20 +7117,23 @@ private:
unsigned format_idx,
unsigned firstDataArg,
FormatStringType Type,
+ VariadicCallType CallType,
bool inFunctionCall = true);
void CheckFormatString(const StringLiteral *FExpr, const Expr *OrigFormatExpr,
Expr **Args, unsigned NumArgs, bool HasVAListArg,
unsigned format_idx, unsigned firstDataArg,
- FormatStringType Type, bool inFunctionCall);
+ FormatStringType Type, bool inFunctionCall,
+ VariadicCallType CallType);
- bool CheckFormatArguments(const FormatAttr *Format, CallExpr *TheCall);
bool CheckFormatArguments(const FormatAttr *Format, Expr **Args,
unsigned NumArgs, bool IsCXXMember,
+ VariadicCallType CallType,
SourceLocation Loc, SourceRange Range);
bool CheckFormatArguments(Expr **Args, unsigned NumArgs,
bool HasVAListArg, unsigned format_idx,
unsigned firstDataArg, FormatStringType Type,
+ VariadicCallType CallType,
SourceLocation Loc, SourceRange range);
void CheckNonNullArguments(const NonNullAttr *NonNull,
diff --git a/lib/Sema/SemaChecking.cpp b/lib/Sema/SemaChecking.cpp
index 1f57b26c9e..519a5ca3fd 100644
--- a/lib/Sema/SemaChecking.cpp
+++ b/lib/Sema/SemaChecking.cpp
@@ -499,7 +499,8 @@ void Sema::checkCall(NamedDecl *FDecl, Expr **Args,
for (specific_attr_iterator<FormatAttr>
I = FDecl->specific_attr_begin<FormatAttr>(),
E = FDecl->specific_attr_end<FormatAttr>(); I != E ; ++I)
- if (CheckFormatArguments(*I, Args, NumArgs, IsMemberFunction, Loc, Range))
+ if (CheckFormatArguments(*I, Args, NumArgs, IsMemberFunction, CallType,
+ Loc, Range))
HandledFormatString = true;
// Refuse POD arguments that weren't caught by the format string
@@ -1610,7 +1611,8 @@ Sema::StringLiteralCheckType
Sema::checkFormatStringExpr(const Expr *E, Expr **Args,
unsigned NumArgs, bool HasVAListArg,
unsigned format_idx, unsigned firstDataArg,
- FormatStringType Type, bool inFunctionCall) {
+ FormatStringType Type, VariadicCallType CallType,
+ bool inFunctionCall) {
tryAgain:
if (E->isTypeDependent() || E->isValueDependent())
return SLCT_NotALiteral;
@@ -1634,13 +1636,13 @@ Sema::checkFormatStringExpr(const Expr *E, Expr **Args,
StringLiteralCheckType Left =
checkFormatStringExpr(C->getTrueExpr(), Args, NumArgs,
HasVAListArg, format_idx, firstDataArg,
- Type, inFunctionCall);
+ Type, CallType, inFunctionCall);
if (Left == SLCT_NotALiteral)
return SLCT_NotALiteral;
StringLiteralCheckType Right =
checkFormatStringExpr(C->getFalseExpr(), Args, NumArgs,
HasVAListArg, format_idx, firstDataArg,
- Type, inFunctionCall);
+ Type, CallType, inFunctionCall);
return Left < Right ? Left : Right;
}
@@ -1691,7 +1693,7 @@ Sema::checkFormatStringExpr(const Expr *E, Expr **Args,
}
return checkFormatStringExpr(Init, Args, NumArgs,
HasVAListArg, format_idx,
- firstDataArg, Type,
+ firstDataArg, Type, CallType,
/*inFunctionCall*/false);
}
}
@@ -1749,7 +1751,7 @@ Sema::checkFormatStringExpr(const Expr *E, Expr **Args,
return checkFormatStringExpr(Arg, Args, NumArgs,
HasVAListArg, format_idx, firstDataArg,
- Type, inFunctionCall);
+ Type, CallType, inFunctionCall);
} else if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(ND)) {
unsigned BuiltinID = FD->getBuiltinID();
if (BuiltinID == Builtin::BI__builtin___CFStringMakeConstantString ||
@@ -1757,7 +1759,8 @@ Sema::checkFormatStringExpr(const Expr *E, Expr **Args,
const Expr *Arg = CE->getArg(0);
return checkFormatStringExpr(Arg, Args, NumArgs,
HasVAListArg, format_idx,
- firstDataArg, Type, inFunctionCall);
+ firstDataArg, Type, CallType,
+ inFunctionCall);
}
}
}
@@ -1775,7 +1778,7 @@ Sema::checkFormatStringExpr(const Expr *E, Expr **Args,
if (StrE) {
CheckFormatString(StrE, E, Args, NumArgs, HasVAListArg, format_idx,
- firstDataArg, Type, inFunctionCall);
+ firstDataArg, Type, inFunctionCall, CallType);
return SLCT_CheckedLiteral;
}
@@ -1812,31 +1815,25 @@ Sema::FormatStringType Sema::GetFormatStringType(const FormatAttr *Format) {
.Default(FST_Unknown);
}
-/// CheckPrintfScanfArguments - Check calls to printf and scanf (and similar
+/// CheckFormatArguments - Check calls to printf and scanf (and similar
/// functions) for correct use of format strings.
/// Returns true if a format string has been fully checked.
-bool Sema::CheckFormatArguments(const FormatAttr *Format, CallExpr *TheCall) {
- bool IsCXXMember = isa<CXXMemberCallExpr>(TheCall);
- return CheckFormatArguments(Format, TheCall->getArgs(),
- TheCall->getNumArgs(),
- IsCXXMember, TheCall->getRParenLoc(),
- TheCall->getCallee()->getSourceRange());
-}
-
bool Sema::CheckFormatArguments(const FormatAttr *Format, Expr **Args,
unsigned NumArgs, bool IsCXXMember,
+ VariadicCallType CallType,
SourceLocation Loc, SourceRange Range) {
FormatStringInfo FSI;
if (getFormatStringInfo(Format, IsCXXMember, &FSI))
return CheckFormatArguments(Args, NumArgs, FSI.HasVAListArg, FSI.FormatIdx,
FSI.FirstDataArg, GetFormatStringType(Format),
- Loc, Range);
+ CallType, Loc, Range);
return false;
}
bool Sema::CheckFormatArguments(Expr **Args, unsigned NumArgs,
bool HasVAListArg, unsigned format_idx,
unsigned firstDataArg, FormatStringType Type,
+ VariadicCallType CallType,
SourceLocation Loc, SourceRange Range) {
// CHECK: printf/scanf-like function is called with no format string.
if (format_idx >= NumArgs) {
@@ -1860,7 +1857,7 @@ bool Sema::CheckFormatArguments(Expr **Args, unsigned NumArgs,
// the same format string checking logic for both ObjC and C strings.
StringLiteralCheckType CT =
checkFormatStringExpr(OrigFormatExpr, Args, NumArgs, HasVAListArg,
- format_idx, firstDataArg, Type);
+ format_idx, firstDataArg, Type, CallType);
if (CT != SLCT_NotALiteral)
// Literal format string found, check done!
return CT == SLCT_CheckedLiteral;
@@ -1908,18 +1905,20 @@ protected:
bool usesPositionalArgs;
bool atFirstArg;
bool inFunctionCall;
+ Sema::VariadicCallType CallType;
public:
CheckFormatHandler(Sema &s, const StringLiteral *fexpr,
const Expr *origFormatExpr, unsigned firstDataArg,
unsigned numDataArgs, const char *beg, bool hasVAListArg,
Expr **args, unsigned numArgs,
- unsigned formatIdx, bool inFunctionCall)
+ unsigned formatIdx, bool inFunctionCall,
+ Sema::VariadicCallType callType)
: S(s), FExpr(fexpr), OrigFormatExpr(origFormatExpr),
FirstDataArg(firstDataArg), NumDataArgs(numDataArgs),
Beg(beg), HasVAListArg(hasVAListArg),
Args(args), NumArgs(numArgs), FormatIdx(formatIdx),
usesPositionalArgs(false), atFirstArg(true),
- inFunctionCall(inFunctionCall) {
+ inFunctionCall(inFunctionCall), CallType(callType) {
CoveredArgs.resize(numDataArgs);
CoveredArgs.reset();
}
@@ -2238,11 +2237,13 @@ public:
unsigned numDataArgs, bool isObjC,
const char *beg, bool hasVAListArg,
Expr **Args, unsigned NumArgs,
- unsigned formatIdx, bool inFunctionCall)
+ unsigned formatIdx, bool inFunctionCall,
+ Sema::VariadicCallType CallType)
: CheckFormatHandler(s, fexpr, origFormatExpr, firstDataArg,
numDataArgs, beg, hasVAListArg, Args, NumArgs,
- formatIdx, inFunctionCall), ObjCContext(isObjC) {}
-
+ formatIdx, inFunctionCall, CallType), ObjCContext(isObjC)
+ {}
+
bool HandleInvalidPrintfConversionSpecifier(
const analyze_printf::PrintfSpecifier &FS,
@@ -2646,10 +2647,17 @@ CheckPrintfHandler::checkFormatExpr(const analyze_printf::PrintfSpecifier &FS,
// was deferred until now, we emit a warning for non-POD
// arguments here.
if (S.isValidVarArgType(E->getType()) == Sema::VAK_Invalid) {
+ unsigned DiagKind;
+ if (E->getType()->isObjCObjectType())
+ DiagKind = diag::err_cannot_pass_objc_interface_to_vararg_format;
+ else
+ DiagKind = diag::warn_non_pod_vararg_with_format_string;
+
EmitFormatDiagnostic(
- S.PDiag(diag::warn_non_pod_vararg_with_format_string)
+ S.PDiag(DiagKind)
<< S.getLangOpts().CPlusPlus0x
<< E->getType()
+ << CallType
<< ATR.getRepresentativeTypeName(S.Context)
<< CSR
<< E->getSourceRange(),
@@ -2678,10 +2686,12 @@ public:
const Expr *origFormatExpr, unsigned firstDataArg,
unsigned numDataArgs, const char *beg, bool hasVAListArg,
Expr **Args, unsigned NumArgs,
- unsigned formatIdx, bool inFunctionCall)
+ unsigned formatIdx, bool inFunctionCall,
+ Sema::VariadicCallType CallType)
: CheckFormatHandler(s, fexpr, origFormatExpr, firstDataArg,
numDataArgs, beg, hasVAListArg,
- Args, NumArgs, formatIdx, inFunctionCall) {}
+ Args, NumArgs, formatIdx, inFunctionCall, CallType)
+ {}
bool HandleScanfSpecifier(const analyze_scanf::ScanfSpecifier &FS,
const char *startSpecifier,
@@ -2842,7 +2852,7 @@ void Sema::CheckFormatString(const StringLiteral *FExpr,
Expr **Args, unsigned NumArgs,
bool HasVAListArg, unsigned format_idx,
unsigned firstDataArg, FormatStringType Type,
- bool inFunctionCall) {
+ bool inFunctionCall, VariadicCallType CallType) {
// CHECK: is the format string a wide literal?
if (!FExpr->isAscii() && !FExpr->isUTF8()) {
@@ -2872,7 +2882,7 @@ void Sema::CheckFormatString(const StringLiteral *FExpr,
CheckPrintfHandler H(*this, FExpr, OrigFormatExpr, firstDataArg,
numDataArgs, (Type == FST_NSString),
Str, HasVAListArg, Args, NumArgs, format_idx,
- inFunctionCall);
+ inFunctionCall, CallType);
if (!analyze_format_string::ParsePrintfString(H, Str, Str + StrLen,
getLangOpts()))
@@ -2880,7 +2890,7 @@ void Sema::CheckFormatString(const StringLiteral *FExpr,
} else if (Type == FST_Scanf) {
CheckScanfHandler H(*this, FExpr, OrigFormatExpr, firstDataArg, numDataArgs,
Str, HasVAListArg, Args, NumArgs, format_idx,
- inFunctionCall);
+ inFunctionCall, CallType);
if (!analyze_format_string::ParseScanfString(H, Str, Str + StrLen,
getLangOpts()))
diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp
index 177165c79e..f2e50ba77b 100644
--- a/lib/Sema/SemaExpr.cpp
+++ b/lib/Sema/SemaExpr.cpp
@@ -602,14 +602,20 @@ ExprResult Sema::DefaultArgumentPromotion(Expr *E) {
/// Incomplete types are considered POD, since this check can be performed
/// when we're in an unevaluated context.
Sema::VarArgKind Sema::isValidVarArgType(const QualType &Ty) {
- if (Ty->isIncompleteType() || Ty.isCXX98PODType(Context))
+ if (Ty->isIncompleteType()) {
+ if (Ty->isObjCObjectType())
+ return VAK_Invalid;
return VAK_Valid;
+ }
+
+ if (Ty.isCXX98PODType(Context))
+ return VAK_Valid;
+
// C++0x [expr.call]p7:
// Passing a potentially-evaluated argument of class type (Clause 9)
// having a non-trivial copy constructor, a non-trivial move constructor,
// or a non-trivial destructor, with no corresponding parameter,
// is conditionally-supported with implementation-defined semantics.
-
if (getLangOpts().CPlusPlus0x && !Ty->isDependentType())
if (CXXRecordDecl *Record = Ty->getAsCXXRecordDecl())
if (Record->hasTrivialCopyConstructor() &&
@@ -635,18 +641,23 @@ bool Sema::variadicArgumentPODCheck(const Expr *E, VariadicCallType CT) {
PDiag(diag::warn_cxx98_compat_pass_non_pod_arg_to_vararg)
<< E->getType() << CT);
break;
- case VAK_Invalid:
+ case VAK_Invalid: {
+ if (Ty->isObjCObjectType())
+ return DiagRuntimeBehavior(E->getLocStart(), 0,
+ PDiag(diag::err_cannot_pass_objc_interface_to_vararg)
+ << Ty << CT);
+
return DiagRuntimeBehavior(E->getLocStart(), 0,
PDiag(diag::warn_cannot_pass_non_pod_arg_to_vararg)
<< getLangOpts().CPlusPlus0x << Ty << CT);
}
+ }
// c++ rules are enforced elsewhere.
return false;
}
/// DefaultVariadicArgumentPromotion - Like DefaultArgumentPromotion, but
-/// will warn if the resulting type is not a POD type, and rejects ObjC
-/// interfaces passed by value.
+/// will create a trap if the resulting type is not a POD type.
ExprResult Sema::DefaultVariadicArgumentPromotion(Expr *E, VariadicCallType CT,
FunctionDecl *FDecl) {
if (const BuiltinType *PlaceholderTy = E->getType()->getAsPlaceholderType()) {
@@ -670,12 +681,6 @@ ExprResult Sema::DefaultVariadicArgumentPromotion(Expr *E, VariadicCallType CT,
return ExprError();
E = ExprRes.take();
- if (E->getType()->isObjCObjectType() &&
- DiagRuntimeBehavior(E->getLocStart(), 0,
- PDiag(diag::err_cannot_pass_objc_interface_to_vararg)
- << E->getType() << CT))
- return ExprError();
-
// Diagnostics regarding non-POD argument types are
// emitted along with format string checking in Sema::CheckFunctionCall().
if (isValidVarArgType(E->getType()) == VAK_Invalid) {
diff --git a/test/SemaCXX/printf-block.cpp b/test/SemaCXX/printf-block.cpp
index a7d266bac5..4a580037e7 100644
--- a/test/SemaCXX/printf-block.cpp
+++ b/test/SemaCXX/printf-block.cpp
@@ -14,5 +14,5 @@ void test_block() {
HasNoCStr hncs(str);
int n = 4;
block(n, "%s %d", str, n); // no-warning
- block(n, "%s %s", hncs, n); // expected-warning{{cannot pass non-POD object of type 'HasNoCStr' to variadic function; expected type from format string was 'char *'}} expected-warning{{format specifies type 'char *' but the argument has type 'int'}}
+ block(n, "%s %s", hncs, n); // expected-warning{{cannot pass non-POD object of type 'HasNoCStr' to variadic block; expected type from format string was 'char *'}} expected-warning{{format specifies type 'char *' but the argument has type 'int'}}
}
diff --git a/test/SemaCXX/printf-cstr.cpp b/test/SemaCXX/printf-cstr.cpp
index efb2f704e1..a7eeb06b9c 100644
--- a/test/SemaCXX/printf-cstr.cpp
+++ b/test/SemaCXX/printf-cstr.cpp
@@ -49,5 +49,5 @@ void constructor_test() {
const char str[] = "test";
HasCStr hcs(str);
Printf p("%s %d %s", str, 10, 10); // expected-warning {{format specifies type 'char *' but the argument has type 'int'}}
- Printf q("%s %d", hcs, 10); // expected-warning {{cannot pass non-POD object of type 'HasCStr' to variadic function; expected type from format string was 'char *'}} expected-note{{did you mean to call the c_str() method?}}
+ Printf q("%s %d", hcs, 10); // expected-warning {{cannot pass non-POD object of type 'HasCStr' to variadic constructor; expected type from format string was 'char *'}} expected-note{{did you mean to call the c_str() method?}}
}
diff --git a/test/SemaObjC/format-strings-objc.m b/test/SemaObjC/format-strings-objc.m
index a53a304bd1..840694ac79 100644
--- a/test/SemaObjC/format-strings-objc.m
+++ b/test/SemaObjC/format-strings-objc.m
@@ -227,3 +227,11 @@ void testInvalidFormatArgument(NSDictionary *dict) {
[Foo fooWithFormat:@"%@ %@", dict[CFSTR("abc")]]; // expected-error{{indexing expression is invalid because subscript type 'CFStringRef' (aka 'const struct __CFString *') is not an integral or Objective-C pointer type}} expected-warning{{more '%' conversions than data arguments}}
}
+
+// <rdar://problem/11825593>
+void testByValueObjectInFormat(Foo *obj) {
+ printf("%d %d %d", 1L, *obj, 1L); // expected-error {{cannot pass object with interface type 'Foo' by value to variadic function; expected type from format string was 'int'}} expected-warning 2 {{format specifies type 'int' but the argument has type 'long'}}
+
+ [Bar log2:@"%d", *obj]; // expected-error {{cannot pass object with interface type 'Foo' by value to variadic method; expected type from format string was 'int'}}
+}
+
diff --git a/test/SemaObjC/method-bad-param.m b/test/SemaObjC/method-bad-param.m
index 9505cb44a3..d44b53614a 100644
--- a/test/SemaObjC/method-bad-param.m
+++ b/test/SemaObjC/method-bad-param.m
@@ -26,7 +26,7 @@ foo somefunc2() {} // expected-error {{interface type 'foo' cannot be returned b
// rdar://6780761
void f0(foo *a0) {
extern void g0(int x, ...);
- g0(1, *(foo*)a0); // expected-error {{cannot pass object with interface type 'foo' by-value through variadic function}}
+ g0(1, *(foo*)a0); // expected-error {{cannot pass object with interface type 'foo' by value through variadic function}}
}
// rdar://8421082