aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorJordan Rose <jordan_rose@apple.com>2012-12-05 18:44:49 +0000
committerJordan Rose <jordan_rose@apple.com>2012-12-05 18:44:49 +0000
commit2cd3440369d9241173e994485ddf2589a50a7d80 (patch)
treefeb3ef784f50d6eaa7649fc4e34667149fdac37b /lib
parent17ddc54b8b94c58286dec6f99ae1f9e12e30d297 (diff)
Format strings: offer a cast to 'unichar' for %C in Objective-C contexts.
For most cases where a conversion specifier doesn't match an argument, we usually guess that the conversion specifier is wrong. However, if the argument is an integer type and the specifier is %C, it's likely the user really did mean to print the integer as a character. (This is more common than %c because there is no way to specify a unichar literal -- you have to write an integer literal, such as '0x2603', and then cast it to unichar.) This does not change the behavior of %S, since there are fewer cases where printing a literal Unicode *string* is necessary, but this could easily be changed in the future. <rdar://problem/11982013> git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@169400 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'lib')
-rw-r--r--lib/Analysis/PrintfFormatString.cpp8
-rw-r--r--lib/Sema/SemaChecking.cpp91
2 files changed, 72 insertions, 27 deletions
diff --git a/lib/Analysis/PrintfFormatString.cpp b/lib/Analysis/PrintfFormatString.cpp
index 08207910b8..176aaedeaa 100644
--- a/lib/Analysis/PrintfFormatString.cpp
+++ b/lib/Analysis/PrintfFormatString.cpp
@@ -359,17 +359,19 @@ ArgType PrintfSpecifier::getArgType(ASTContext &Ctx,
case ConversionSpecifier::sArg:
if (LM.getKind() == LengthModifier::AsWideChar) {
if (IsObjCLiteral)
- return Ctx.getPointerType(Ctx.UnsignedShortTy.withConst());
+ return ArgType(Ctx.getPointerType(Ctx.UnsignedShortTy.withConst()),
+ "const unichar *");
return ArgType(ArgType::WCStrTy, "wchar_t *");
}
return ArgType::CStrTy;
case ConversionSpecifier::SArg:
if (IsObjCLiteral)
- return Ctx.getPointerType(Ctx.UnsignedShortTy.withConst());
+ return ArgType(Ctx.getPointerType(Ctx.UnsignedShortTy.withConst()),
+ "const unichar *");
return ArgType(ArgType::WCStrTy, "wchar_t *");
case ConversionSpecifier::CArg:
if (IsObjCLiteral)
- return Ctx.UnsignedShortTy;
+ return ArgType(Ctx.UnsignedShortTy, "unichar");
return ArgType(Ctx.WCharTy, "wchar_t");
case ConversionSpecifier::pArg:
return ArgType::CPointerTy;
diff --git a/lib/Sema/SemaChecking.cpp b/lib/Sema/SemaChecking.cpp
index d91f03416b..095d25aa02 100644
--- a/lib/Sema/SemaChecking.cpp
+++ b/lib/Sema/SemaChecking.cpp
@@ -2762,17 +2762,46 @@ CheckPrintfHandler::checkFormatExpr(const analyze_printf::PrintfSpecifier &FS,
ExprTy = S.Context.CharTy;
}
+ // %C in an Objective-C context prints a unichar, not a wchar_t.
+ // If the argument is an integer of some kind, believe the %C and suggest
+ // a cast instead of changing the conversion specifier.
QualType IntendedTy = ExprTy;
+ if (ObjCContext &&
+ FS.getConversionSpecifier().getKind() == ConversionSpecifier::CArg) {
+ if (ExprTy->isIntegralOrUnscopedEnumerationType() &&
+ !ExprTy->isCharType()) {
+ // 'unichar' is defined as a typedef of unsigned short, but we should
+ // prefer using the typedef if it is visible.
+ IntendedTy = S.Context.UnsignedShortTy;
+
+ LookupResult Result(S, &S.Context.Idents.get("unichar"), E->getLocStart(),
+ Sema::LookupOrdinaryName);
+ if (S.LookupName(Result, S.getCurScope())) {
+ NamedDecl *ND = Result.getFoundDecl();
+ if (TypedefNameDecl *TD = dyn_cast<TypedefNameDecl>(ND))
+ if (TD->getUnderlyingType() == IntendedTy)
+ IntendedTy = S.Context.getTypedefType(TD);
+ }
+ }
+ }
+
+ // Special-case some of Darwin's platform-independence types by suggesting
+ // casts to primitive types that are known to be large enough.
+ bool ShouldNotPrintDirectly = false;
if (S.Context.getTargetInfo().getTriple().isOSDarwin()) {
- // Special-case some of Darwin's platform-independence types.
if (const TypedefType *UserTy = IntendedTy->getAs<TypedefType>()) {
StringRef Name = UserTy->getDecl()->getName();
- IntendedTy = llvm::StringSwitch<QualType>(Name)
+ QualType CastTy = llvm::StringSwitch<QualType>(Name)
.Case("NSInteger", S.Context.LongTy)
.Case("NSUInteger", S.Context.UnsignedLongTy)
.Case("SInt32", S.Context.IntTy)
.Case("UInt32", S.Context.UnsignedIntTy)
- .Default(IntendedTy);
+ .Default(QualType());
+
+ if (!CastTy.isNull()) {
+ ShouldNotPrintDirectly = true;
+ IntendedTy = CastTy;
+ }
}
}
@@ -2789,7 +2818,19 @@ CheckPrintfHandler::checkFormatExpr(const analyze_printf::PrintfSpecifier &FS,
CharSourceRange SpecRange = getSpecifierRange(StartSpecifier, SpecifierLen);
- if (IntendedTy != ExprTy) {
+ if (IntendedTy == ExprTy) {
+ // In this case, the specifier is wrong and should be changed to match
+ // the argument.
+ EmitFormatDiagnostic(
+ S.PDiag(diag::warn_printf_conversion_argument_type_mismatch)
+ << AT.getRepresentativeTypeName(S.Context) << IntendedTy
+ << E->getSourceRange(),
+ E->getLocStart(),
+ /*IsStringLocation*/false,
+ SpecRange,
+ FixItHint::CreateReplacement(SpecRange, os.str()));
+
+ } else {
// The canonical type for formatting this value is different from the
// actual type of the expression. (This occurs, for example, with Darwin's
// NSInteger on 32-bit platforms, where it is typedef'd as 'int', but
@@ -2827,26 +2868,28 @@ CheckPrintfHandler::checkFormatExpr(const analyze_printf::PrintfSpecifier &FS,
Hints.push_back(FixItHint::CreateInsertion(After, ")"));
}
- // We extract the name from the typedef because we don't want to show
- // the underlying type in the diagnostic.
- const TypedefType *UserTy = cast<TypedefType>(ExprTy);
- StringRef Name = UserTy->getDecl()->getName();
-
- // Finally, emit the diagnostic.
- EmitFormatDiagnostic(S.PDiag(diag::warn_format_argument_needs_cast)
- << Name << IntendedTy
- << E->getSourceRange(),
- E->getLocStart(), /*IsStringLocation=*/false,
- SpecRange, Hints);
- } else {
- EmitFormatDiagnostic(
- S.PDiag(diag::warn_printf_conversion_argument_type_mismatch)
- << AT.getRepresentativeTypeName(S.Context) << IntendedTy
- << E->getSourceRange(),
- E->getLocStart(),
- /*IsStringLocation*/false,
- SpecRange,
- FixItHint::CreateReplacement(SpecRange, os.str()));
+ if (ShouldNotPrintDirectly) {
+ // The expression has a type that should not be printed directly.
+ // We extract the name from the typedef because we don't want to show
+ // the underlying type in the diagnostic.
+ StringRef Name = cast<TypedefType>(ExprTy)->getDecl()->getName();
+
+ EmitFormatDiagnostic(S.PDiag(diag::warn_format_argument_needs_cast)
+ << Name << IntendedTy
+ << E->getSourceRange(),
+ E->getLocStart(), /*IsStringLocation=*/false,
+ SpecRange, Hints);
+ } else {
+ // In this case, the expression could be printed using a different
+ // specifier, but we've decided that the specifier is probably correct
+ // and we should cast instead. Just use the normal warning message.
+ EmitFormatDiagnostic(
+ S.PDiag(diag::warn_printf_conversion_argument_type_mismatch)
+ << AT.getRepresentativeTypeName(S.Context) << ExprTy
+ << E->getSourceRange(),
+ E->getLocStart(), /*IsStringLocation*/false,
+ SpecRange, Hints);
+ }
}
} else {
const CharSourceRange &CSR = getSpecifierRange(StartSpecifier,