aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/clang/Analysis/Analyses/FormatString.h11
-rw-r--r--lib/Analysis/PrintfFormatString.cpp12
-rw-r--r--lib/Analysis/ScanfFormatString.cpp18
-rw-r--r--lib/Sema/SemaChecking.cpp6
-rw-r--r--test/Sema/format-strings-fixit.c70
5 files changed, 87 insertions, 30 deletions
diff --git a/include/clang/Analysis/Analyses/FormatString.h b/include/clang/Analysis/Analyses/FormatString.h
index 49221bbc9b..f3fe32474c 100644
--- a/include/clang/Analysis/Analyses/FormatString.h
+++ b/include/clang/Analysis/Analyses/FormatString.h
@@ -467,10 +467,11 @@ public:
const OptionalFlag &hasSpacePrefix() const { return HasSpacePrefix; }
bool usesPositionalArg() const { return UsesPositionalArg; }
- /// Changes the specifier and length according to a QualType, retaining any
- /// flags or options. Returns true on success, or false when a conversion
- /// was not successful.
- bool fixType(QualType QT, const LangOptions &LangOpt);
+ /// Changes the specifier and length according to a QualType, retaining any
+ /// flags or options. Returns true on success, or false when a conversion
+ /// was not successful.
+ bool fixType(QualType QT, const LangOptions &LangOpt, ASTContext &Ctx,
+ bool IsObjCLiteral);
void toString(raw_ostream &os) const;
@@ -567,7 +568,7 @@ public:
ScanfArgTypeResult getArgType(ASTContext &Ctx) const;
- bool fixType(QualType QT, const LangOptions &LangOpt);
+ bool fixType(QualType QT, const LangOptions &LangOpt, ASTContext &Ctx);
void toString(raw_ostream &os) const;
diff --git a/lib/Analysis/PrintfFormatString.cpp b/lib/Analysis/PrintfFormatString.cpp
index 6da37fc44a..ff0174e3c3 100644
--- a/lib/Analysis/PrintfFormatString.cpp
+++ b/lib/Analysis/PrintfFormatString.cpp
@@ -336,7 +336,8 @@ ArgTypeResult PrintfSpecifier::getArgType(ASTContext &Ctx,
return ArgTypeResult();
}
-bool PrintfSpecifier::fixType(QualType QT, const LangOptions &LangOpt) {
+bool PrintfSpecifier::fixType(QualType QT, const LangOptions &LangOpt,
+ ASTContext &Ctx, bool IsObjCLiteral) {
// Handle strings first (char *, wchar_t *)
if (QT->isPointerType() && (QT->getPointeeType()->isAnyCharacterType())) {
CS.setKind(ConversionSpecifier::sArg);
@@ -432,6 +433,11 @@ bool PrintfSpecifier::fixType(QualType QT, const LangOptions &LangOpt) {
}
}
+ // If fixing the length modifier was enough, we are done.
+ const analyze_printf::ArgTypeResult &ATR = getArgType(Ctx, IsObjCLiteral);
+ if (hasValidLengthModifier() && ATR.isValid() && ATR.matchesType(Ctx, QT))
+ return true;
+
// Set conversion specifier and disable any flags which do not apply to it.
// Let typedefs to char fall through to int, as %c is silly for uint8_t.
if (isa<TypedefType>(QT) && QT->isAnyCharacterType()) {
@@ -451,9 +457,7 @@ bool PrintfSpecifier::fixType(QualType QT, const LangOptions &LangOpt) {
HasAlternativeForm = 0;
}
else if (QT->isUnsignedIntegerType()) {
- // Preserve the original formatting, e.g. 'X', 'o'.
- if (!cast<PrintfConversionSpecifier>(CS).isUIntArg())
- CS.setKind(ConversionSpecifier::uArg);
+ CS.setKind(ConversionSpecifier::uArg);
HasAlternativeForm = 0;
HasPlusPrefix = 0;
} else {
diff --git a/lib/Analysis/ScanfFormatString.cpp b/lib/Analysis/ScanfFormatString.cpp
index c1cdef8632..5990a56c35 100644
--- a/lib/Analysis/ScanfFormatString.cpp
+++ b/lib/Analysis/ScanfFormatString.cpp
@@ -307,8 +307,8 @@ ScanfArgTypeResult ScanfSpecifier::getArgType(ASTContext &Ctx) const {
return ScanfArgTypeResult();
}
-bool ScanfSpecifier::fixType(QualType QT, const LangOptions &LangOpt)
-{
+bool ScanfSpecifier::fixType(QualType QT, const LangOptions &LangOpt,
+ ASTContext &Ctx) {
if (!QT->isPointerType())
return false;
@@ -390,17 +390,19 @@ bool ScanfSpecifier::fixType(QualType QT, const LangOptions &LangOpt)
}
}
+ // If fixing the length modifier was enough, we are done.
+ const analyze_scanf::ScanfArgTypeResult &ATR = getArgType(Ctx);
+ if (hasValidLengthModifier() && ATR.isValid() && ATR.matchesType(Ctx, QT))
+ return true;
+
// Figure out the conversion specifier.
if (PT->isRealFloatingType())
CS.setKind(ConversionSpecifier::fArg);
else if (PT->isSignedIntegerType())
CS.setKind(ConversionSpecifier::dArg);
- else if (PT->isUnsignedIntegerType()) {
- // Preserve the original formatting, e.g. 'X', 'o'.
- if (!CS.isUIntArg()) {
- CS.setKind(ConversionSpecifier::uArg);
- }
- } else
+ else if (PT->isUnsignedIntegerType())
+ CS.setKind(ConversionSpecifier::uArg);
+ else
llvm_unreachable("Unexpected type");
return true;
diff --git a/lib/Sema/SemaChecking.cpp b/lib/Sema/SemaChecking.cpp
index 3393cf73f1..1d75ef6e6f 100644
--- a/lib/Sema/SemaChecking.cpp
+++ b/lib/Sema/SemaChecking.cpp
@@ -2181,7 +2181,8 @@ CheckPrintfHandler::HandlePrintfSpecifier(const analyze_printf::PrintfSpecifier
// We may be able to offer a FixItHint if it is a supported type.
PrintfSpecifier fixedFS = FS;
- bool success = fixedFS.fixType(Ex->getType(), S.getLangOptions());
+ bool success = fixedFS.fixType(Ex->getType(), S.getLangOptions(),
+ S.Context, IsObjCLiteral);
if (success) {
// Get the fix string from the fixed format specifier
@@ -2340,7 +2341,8 @@ bool CheckScanfHandler::HandleScanfSpecifier(
const analyze_scanf::ScanfArgTypeResult &ATR = FS.getArgType(S.Context);
if (ATR.isValid() && !ATR.matchesType(S.Context, Ex->getType())) {
ScanfSpecifier fixedFS = FS;
- bool success = fixedFS.fixType(Ex->getType(), S.getLangOptions());
+ bool success = fixedFS.fixType(Ex->getType(), S.getLangOptions(),
+ S.Context);
if (success) {
// Get the fix string from the fixed format specifier.
diff --git a/test/Sema/format-strings-fixit.c b/test/Sema/format-strings-fixit.c
index 4fb6d75d76..80b1be0578 100644
--- a/test/Sema/format-strings-fixit.c
+++ b/test/Sema/format-strings-fixit.c
@@ -48,7 +48,7 @@ void test() {
printf("%hhs", "foo");
printf("%1$zp", (void *)0);
- // Perserve the original formatting for unsigned integers.
+ // Preserve the original formatting for unsigned integers.
unsigned long val = 42;
printf("%X", val);
@@ -60,6 +60,21 @@ void test() {
// string
printf("%ld", "foo");
+
+ // Preserve the original choice of conversion specifier.
+ printf("%o", (long) 42);
+ printf("%u", (long) 42);
+ printf("%x", (long) 42);
+ printf("%X", (long) 42);
+ printf("%i", (unsigned long) 42);
+ printf("%d", (unsigned long) 42);
+ printf("%F", (long double) 42);
+ printf("%e", (long double) 42);
+ printf("%E", (long double) 42);
+ printf("%g", (long double) 42);
+ printf("%G", (long double) 42);
+ printf("%a", (long double) 42);
+ printf("%A", (long double) 42);
}
int scanf(char const *, ...);
@@ -101,20 +116,30 @@ void test2() {
scanf("%f", &uIntmaxVar);
scanf("%f", &ptrdiffVar);
- // Perserve the original formatting for unsigned integers.
- scanf("%o", &uLongVar);
- scanf("%x", &uLongVar);
- scanf("%X", &uLongVar);
+ // Preserve the original formatting.
+ scanf("%o", &longVar);
+ scanf("%u", &longVar);
+ scanf("%x", &longVar);
+ scanf("%X", &longVar);
+ scanf("%i", &uLongVar);
+ scanf("%d", &uLongVar);
+ scanf("%F", &longDoubleVar);
+ scanf("%e", &longDoubleVar);
+ scanf("%E", &longDoubleVar);
+ scanf("%g", &longDoubleVar);
+ scanf("%G", &longDoubleVar);
+ scanf("%a", &longDoubleVar);
+ scanf("%A", &longDoubleVar);
}
-// Validate the fixes...
+// Validate the fixes.
// CHECK: printf("%d", (int) 123);
// CHECK: printf("abc%s", "testing testing 123");
-// CHECK: printf("%ld", (long) -12);
+// CHECK: printf("%lu", (long) -12);
// CHECK: printf("%d", 123);
// CHECK: printf("%s\n", "x");
// CHECK: printf("%f\n", 1.23);
-// CHECK: printf("%.2llu", (unsigned long long) 123456);
+// CHECK: printf("%+.2lld", (unsigned long long) 123456);
// CHECK: printf("%1Lf", (long double) 1.23);
// CHECK: printf("%0u", (unsigned) 31337);
// CHECK: printf("%p", (void *) 0);
@@ -132,6 +157,19 @@ void test2() {
// CHECK: printf("%ju", (uintmax_t) 42);
// CHECK: printf("%td", (ptrdiff_t) 42);
// CHECK: printf("%s", "foo");
+// CHECK: printf("%lo", (long) 42);
+// CHECK: printf("%lu", (long) 42);
+// CHECK: printf("%lx", (long) 42);
+// CHECK: printf("%lX", (long) 42);
+// CHECK: printf("%li", (unsigned long) 42);
+// CHECK: printf("%ld", (unsigned long) 42);
+// CHECK: printf("%LF", (long double) 42);
+// CHECK: printf("%Le", (long double) 42);
+// CHECK: printf("%LE", (long double) 42);
+// CHECK: printf("%Lg", (long double) 42);
+// CHECK: printf("%LG", (long double) 42);
+// CHECK: printf("%La", (long double) 42);
+// CHECK: printf("%LA", (long double) 42);
// CHECK: scanf("%s", str);
// CHECK: scanf("%hd", &shortVar);
@@ -149,6 +187,16 @@ void test2() {
// CHECK: scanf("%jd", &intmaxVar);
// CHECK: scanf("%ju", &uIntmaxVar);
// CHECK: scanf("%td", &ptrdiffVar);
-// CHECK: scanf("%lo", &uLongVar);
-// CHECK: scanf("%lx", &uLongVar);
-// CHECK: scanf("%lX", &uLongVar);
+// CHECK: scanf("%lo", &longVar);
+// CHECK: scanf("%lu", &longVar);
+// CHECK: scanf("%lx", &longVar);
+// CHECK: scanf("%lX", &longVar);
+// CHECK: scanf("%li", &uLongVar);
+// CHECK: scanf("%ld", &uLongVar);
+// CHECK: scanf("%LF", &longDoubleVar);
+// CHECK: scanf("%Le", &longDoubleVar);
+// CHECK: scanf("%LE", &longDoubleVar);
+// CHECK: scanf("%Lg", &longDoubleVar);
+// CHECK: scanf("%LG", &longDoubleVar);
+// CHECK: scanf("%La", &longDoubleVar);
+// CHECK: scanf("%LA", &longDoubleVar);