summaryrefslogtreecommitdiffstats
path: root/clang/lib
diff options
context:
space:
mode:
Diffstat (limited to 'clang/lib')
-rw-r--r--clang/lib/Analysis/PrintfFormatString.cpp62
-rw-r--r--clang/lib/Sema/SemaChecking.cpp47
2 files changed, 108 insertions, 1 deletions
diff --git a/clang/lib/Analysis/PrintfFormatString.cpp b/clang/lib/Analysis/PrintfFormatString.cpp
index b8d3ec18016..f0976bce972 100644
--- a/clang/lib/Analysis/PrintfFormatString.cpp
+++ b/clang/lib/Analysis/PrintfFormatString.cpp
@@ -49,6 +49,24 @@ static bool ParsePrecision(FormatStringHandler &H, PrintfSpecifier &FS,
return false;
}
+static bool ParseObjCFlags(FormatStringHandler &H, PrintfSpecifier &FS,
+ const char *FlagBeg, const char *E, bool Warn) {
+ StringRef Flag(FlagBeg, E - FlagBeg);
+ // Currently there is only one flag.
+ if (Flag == "tt") {
+ FS.setHasObjCTechnicalTerm(FlagBeg);
+ return false;
+ }
+ // Handle either the case of no flag or an invalid flag.
+ if (Warn) {
+ if (Flag == "")
+ H.HandleEmptyObjCModifierFlag(FlagBeg, E - FlagBeg);
+ else
+ H.HandleInvalidObjCModifierFlag(FlagBeg, E - FlagBeg);
+ }
+ return true;
+}
+
static PrintfSpecifierResult ParsePrintfSpecifier(FormatStringHandler &H,
const char *&Beg,
const char *E,
@@ -168,6 +186,38 @@ static PrintfSpecifierResult ParsePrintfSpecifier(FormatStringHandler &H,
return true;
}
+ // Look for the Objective-C modifier flags, if any.
+ // We parse these here, even if they don't apply to
+ // the conversion specifier, and then emit an error
+ // later if the conversion specifier isn't '@'. This
+ // enables better recovery, and we don't know if
+ // these flags are applicable until later.
+ const char *ObjCModifierFlagsStart = nullptr,
+ *ObjCModifierFlagsEnd = nullptr;
+ if (*I == '[') {
+ ObjCModifierFlagsStart = I;
+ ++I;
+ auto flagStart = I;
+ for (;; ++I) {
+ ObjCModifierFlagsEnd = I;
+ if (I == E) {
+ if (Warn)
+ H.HandleIncompleteSpecifier(Start, E - Start);
+ return true;
+ }
+ // Did we find the closing ']'?
+ if (*I == ']') {
+ if (ParseObjCFlags(H, FS, flagStart, I, Warn))
+ return true;
+ ++I;
+ break;
+ }
+ // There are no separators defined yet for multiple
+ // Objective-C modifier flags. When those are
+ // defined, this is the place to check.
+ }
+ }
+
if (*I == '\0') {
// Detect spurious null characters, which are likely errors.
H.HandleNullChar(I);
@@ -240,6 +290,18 @@ static PrintfSpecifierResult ParsePrintfSpecifier(FormatStringHandler &H,
if (Target.getTriple().isOSMSVCRT())
k = ConversionSpecifier::ZArg;
}
+
+ // Check to see if we used the Objective-C modifier flags with
+ // a conversion specifier other than '@'.
+ if (k != ConversionSpecifier::ObjCObjArg &&
+ k != ConversionSpecifier::InvalidSpecifier &&
+ ObjCModifierFlagsStart) {
+ H.HandleObjCFlagsWithNonObjCConversion(ObjCModifierFlagsStart,
+ ObjCModifierFlagsEnd + 1,
+ conversionPosition);
+ return true;
+ }
+
PrintfConversionSpecifier CS(conversionPosition, k);
FS.setConversionSpecifier(CS);
if (CS.consumesDataArgument() && !FS.usesPositionalArg())
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index 870da782b97..5737e8338ff 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -3572,8 +3572,18 @@ public:
const char *startSpecifier, unsigned specifierLen);
bool checkForCStrMembers(const analyze_printf::ArgType &AT,
const Expr *E);
+
+ void HandleEmptyObjCModifierFlag(const char *startFlag,
+ unsigned flagLen) override;
-};
+ void HandleInvalidObjCModifierFlag(const char *startFlag,
+ unsigned flagLen) override;
+
+ void HandleObjCFlagsWithNonObjCConversion(const char *flagsStart,
+ const char *flagsEnd,
+ const char *conversionPosition)
+ override;
+};
}
bool CheckPrintfHandler::HandleInvalidPrintfConversionSpecifier(
@@ -3693,6 +3703,41 @@ void CheckPrintfHandler::HandleIgnoredFlag(
getSpecifierRange(ignoredFlag.getPosition(), 1)));
}
+// void EmitFormatDiagnostic(PartialDiagnostic PDiag, SourceLocation StringLoc,
+// bool IsStringLocation, Range StringRange,
+// ArrayRef<FixItHint> Fixit = None);
+
+void CheckPrintfHandler::HandleEmptyObjCModifierFlag(const char *startFlag,
+ unsigned flagLen) {
+ // Warn about an empty flag.
+ EmitFormatDiagnostic(S.PDiag(diag::warn_printf_empty_objc_flag),
+ getLocationOfByte(startFlag),
+ /*IsStringLocation*/true,
+ getSpecifierRange(startFlag, flagLen));
+}
+
+void CheckPrintfHandler::HandleInvalidObjCModifierFlag(const char *startFlag,
+ unsigned flagLen) {
+ // Warn about an invalid flag.
+ auto Range = getSpecifierRange(startFlag, flagLen);
+ StringRef flag(startFlag, flagLen);
+ EmitFormatDiagnostic(S.PDiag(diag::warn_printf_invalid_objc_flag) << flag,
+ getLocationOfByte(startFlag),
+ /*IsStringLocation*/true,
+ Range, FixItHint::CreateRemoval(Range));
+}
+
+void CheckPrintfHandler::HandleObjCFlagsWithNonObjCConversion(
+ const char *flagsStart, const char *flagsEnd, const char *conversionPosition) {
+ // Warn about using '[...]' without a '@' conversion.
+ auto Range = getSpecifierRange(flagsStart, flagsEnd - flagsStart + 1);
+ auto diag = diag::warn_printf_ObjCflags_without_ObjCConversion;
+ EmitFormatDiagnostic(S.PDiag(diag) << StringRef(conversionPosition, 1),
+ getLocationOfByte(conversionPosition),
+ /*IsStringLocation*/true,
+ Range, FixItHint::CreateRemoval(Range));
+}
+
// Determines if the specified is a C++ class or struct containing
// a member with the specified name and kind (e.g. a CXXMethodDecl named
// "c_str()").
OpenPOWER on IntegriCloud