diff options
Diffstat (limited to 'clang/lib')
| -rw-r--r-- | clang/lib/Analysis/PrintfFormatString.cpp | 62 | ||||
| -rw-r--r-- | clang/lib/Sema/SemaChecking.cpp | 47 |
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()"). |

