diff options
| author | JF Bastien <jfbastien@apple.com> | 2018-06-22 21:54:40 +0000 | 
|---|---|---|
| committer | JF Bastien <jfbastien@apple.com> | 2018-06-22 21:54:40 +0000 | 
| commit | ec7d7f312e5cfd963e5796d1401843bab8df83ef (patch) | |
| tree | a2f5c053841ccde136342ef6e90ef4ee4ae7d503 | |
| parent | eb7488e799880f63b22eaa3cd5e5209abba33129 (diff) | |
| download | bcm5719-llvm-ec7d7f312e5cfd963e5796d1401843bab8df83ef.tar.gz bcm5719-llvm-ec7d7f312e5cfd963e5796d1401843bab8df83ef.zip  | |
[Sema] -Wformat-pedantic only for NSInteger/NSUInteger %zu/%zi on Darwin
Summary:
Pick D42933 back up, and make NSInteger/NSUInteger with %zu/%zi specifiers on Darwin warn only in pedantic mode. The default -Wformat recently started warning for the following code because of the added support for analysis for the '%zi' specifier.
     NSInteger i = NSIntegerMax;
     NSLog(@"max NSInteger = %zi", i);
The problem is that on armv7 %zi is 'long', and NSInteger is typedefed to 'int' in Foundation. We should avoid this warning as it's inconvenient to our users: it's target specific (happens only on armv7 and not arm64), and breaks their existing code. We should also silence the warning for the '%zu' specifier to ensure consistency. This is acceptable because Darwin guarantees that, despite the unfortunate choice of typedef, sizeof(size_t) == sizeof(NS[U]Integer), the warning is therefore noisy for pedantic reasons. Once this is in I'll update public documentation.
Related discussion on cfe-dev:
http://lists.llvm.org/pipermail/cfe-dev/2018-May/058050.html
<rdar://36874921&40501559>
Reviewers: ahatanak, vsapsai, alexshap, aaron.ballman, javed.absar, jfb, rjmccall
Subscribers: kristof.beyls, aheejin, cfe-commits
Differential Revision: https://reviews.llvm.org/D47290
llvm-svn: 335393
| -rw-r--r-- | clang/include/clang/Analysis/Analyses/FormatString.h | 22 | ||||
| -rw-r--r-- | clang/include/clang/Basic/DiagnosticSemaKinds.td | 4 | ||||
| -rw-r--r-- | clang/lib/Analysis/PrintfFormatString.cpp | 4 | ||||
| -rw-r--r-- | clang/lib/Sema/SemaChecking.cpp | 66 | ||||
| -rw-r--r-- | clang/test/FixIt/fixit-format-ios-nopedantic.m | 21 | ||||
| -rw-r--r-- | clang/test/FixIt/fixit-format-ios.m | 2 | ||||
| -rw-r--r-- | clang/test/SemaObjC/format-size-spec-nsinteger.m | 30 | 
7 files changed, 109 insertions, 40 deletions
diff --git a/clang/include/clang/Analysis/Analyses/FormatString.h b/clang/include/clang/Analysis/Analyses/FormatString.h index a23aaae7662..6d76718048b 100644 --- a/clang/include/clang/Analysis/Analyses/FormatString.h +++ b/clang/include/clang/Analysis/Analyses/FormatString.h @@ -256,18 +256,19 @@ public:  private:    const Kind K;    QualType T; -  const char *Name; -  bool Ptr; +  const char *Name = nullptr; +  bool Ptr = false, IsSizeT = false; +  public: -  ArgType(Kind k = UnknownTy, const char *n = nullptr) -      : K(k), Name(n), Ptr(false) {} -  ArgType(QualType t, const char *n = nullptr) -      : K(SpecificTy), T(t), Name(n), Ptr(false) {} -  ArgType(CanQualType t) : K(SpecificTy), T(t), Name(nullptr), Ptr(false) {} +  ArgType(Kind K = UnknownTy, const char *N = nullptr) : K(K), Name(N) {} +  ArgType(QualType T, const char *N = nullptr) : K(SpecificTy), T(T), Name(N) {} +  ArgType(CanQualType T) : K(SpecificTy), T(T) {}    static ArgType Invalid() { return ArgType(InvalidTy); }    bool isValid() const { return K != InvalidTy; } +  bool isSizeT() const { return IsSizeT; } +    /// Create an ArgType which corresponds to the type pointer to A.    static ArgType PtrTo(const ArgType& A) {      assert(A.K >= InvalidTy && "ArgType cannot be pointer to invalid/unknown"); @@ -276,6 +277,13 @@ public:      return Res;    } +  /// Create an ArgType which corresponds to the size_t/ssize_t type. +  static ArgType makeSizeT(const ArgType &A) { +    ArgType Res = A; +    Res.IsSizeT = true; +    return Res; +  } +    MatchKind matchesType(ASTContext &C, QualType argTy) const;    QualType getRepresentativeType(ASTContext &C) const; diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index b7ede8518ee..f08cd00c62e 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -7719,6 +7719,10 @@ def warn_format_argument_needs_cast : Warning<    "%select{values of type|enum values with underlying type}2 '%0' should not "    "be used as format arguments; add an explicit cast to %1 instead">,    InGroup<Format>; +def warn_format_argument_needs_cast_pedantic : Warning< +  "%select{values of type|enum values with underlying type}2 '%0' should not " +  "be used as format arguments; add an explicit cast to %1 instead">, +  InGroup<FormatPedantic>, DefaultIgnore;  def warn_printf_positional_arg_exceeds_data_args : Warning <    "data argument position '%0' exceeds the number of data arguments (%1)">,    InGroup<Format>; diff --git a/clang/lib/Analysis/PrintfFormatString.cpp b/clang/lib/Analysis/PrintfFormatString.cpp index 3b64508b5a6..93d64f03ead 100644 --- a/clang/lib/Analysis/PrintfFormatString.cpp +++ b/clang/lib/Analysis/PrintfFormatString.cpp @@ -466,7 +466,7 @@ ArgType PrintfSpecifier::getArgType(ASTContext &Ctx,        case LengthModifier::AsIntMax:          return ArgType(Ctx.getIntMaxType(), "intmax_t");        case LengthModifier::AsSizeT: -        return ArgType(Ctx.getSignedSizeType(), "ssize_t"); +        return ArgType::makeSizeT(ArgType(Ctx.getSignedSizeType(), "ssize_t"));        case LengthModifier::AsInt3264:          return Ctx.getTargetInfo().getTriple().isArch64Bit()                     ? ArgType(Ctx.LongLongTy, "__int64") @@ -499,7 +499,7 @@ ArgType PrintfSpecifier::getArgType(ASTContext &Ctx,        case LengthModifier::AsIntMax:          return ArgType(Ctx.getUIntMaxType(), "uintmax_t");        case LengthModifier::AsSizeT: -        return ArgType(Ctx.getSizeType(), "size_t"); +        return ArgType::makeSizeT(ArgType(Ctx.getSizeType(), "size_t"));        case LengthModifier::AsInt3264:          return Ctx.getTargetInfo().getTriple().isArch64Bit()                     ? ArgType(Ctx.UnsignedLongLongTy, "unsigned __int64") diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp index 53be0f6d9ff..12a599b4f13 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -6805,11 +6805,11 @@ CheckPrintfHandler::checkFormatExpr(const analyze_printf::PrintfSpecifier &FS,      ExprTy = TET->getUnderlyingExpr()->getType();    } -  analyze_printf::ArgType::MatchKind match = AT.matchesType(S.Context, ExprTy); - -  if (match == analyze_printf::ArgType::Match) { +  const analyze_printf::ArgType::MatchKind Match = +      AT.matchesType(S.Context, ExprTy); +  bool Pedantic = Match == analyze_printf::ArgType::NoMatchPedantic; +  if (Match == analyze_printf::ArgType::Match)      return true; -  }    // Look through argument promotions for our error message's reported type.    // This includes the integral and floating promotions, but excludes array @@ -6885,6 +6885,11 @@ CheckPrintfHandler::checkFormatExpr(const analyze_printf::PrintfSpecifier &FS,      QualType CastTy;      std::tie(CastTy, CastTyName) = shouldNotPrintDirectly(S.Context, IntendedTy, E);      if (!CastTy.isNull()) { +      // %zi/%zu are OK to use for NSInteger/NSUInteger of type int +      // (long in ASTContext). Only complain to pedants. +      if ((CastTyName == "NSInteger" || CastTyName == "NSUInteger") && +          AT.isSizeT() && AT.matchesType(S.Context, CastTy)) +        Pedantic = true;        IntendedTy = CastTy;        ShouldNotPrintDirectly = true;      } @@ -6892,10 +6897,10 @@ CheckPrintfHandler::checkFormatExpr(const analyze_printf::PrintfSpecifier &FS,    // We may be able to offer a FixItHint if it is a supported type.    PrintfSpecifier fixedFS = FS; -  bool success = +  bool Success =        fixedFS.fixType(IntendedTy, S.getLangOpts(), S.Context, isObjCContext()); -  if (success) { +  if (Success) {      // Get the fix string from the fixed format specifier      SmallString<16> buf;      llvm::raw_svector_ostream os(buf); @@ -6904,13 +6909,13 @@ CheckPrintfHandler::checkFormatExpr(const analyze_printf::PrintfSpecifier &FS,      CharSourceRange SpecRange = getSpecifierRange(StartSpecifier, SpecifierLen);      if (IntendedTy == ExprTy && !ShouldNotPrintDirectly) { -      unsigned diag = diag::warn_format_conversion_argument_type_mismatch; -      if (match == analyze_format_string::ArgType::NoMatchPedantic) { -        diag = diag::warn_format_conversion_argument_type_mismatch_pedantic; -      } +      unsigned Diag = +          Pedantic +              ? diag::warn_format_conversion_argument_type_mismatch_pedantic +              : diag::warn_format_conversion_argument_type_mismatch;        // In this case, the specifier is wrong and should be changed to match        // the argument. -      EmitFormatDiagnostic(S.PDiag(diag) +      EmitFormatDiagnostic(S.PDiag(Diag)                                 << AT.getRepresentativeTypeName(S.Context)                                 << IntendedTy << IsEnum << E->getSourceRange(),                             E->getLocStart(), @@ -6963,9 +6968,11 @@ CheckPrintfHandler::checkFormatExpr(const analyze_printf::PrintfSpecifier &FS,            Name = TypedefTy->getDecl()->getName();          else            Name = CastTyName; -        EmitFormatDiagnostic(S.PDiag(diag::warn_format_argument_needs_cast) -                               << Name << IntendedTy << IsEnum -                               << E->getSourceRange(), +        unsigned Diag = Pedantic +                            ? diag::warn_format_argument_needs_cast_pedantic +                            : diag::warn_format_argument_needs_cast; +        EmitFormatDiagnostic(S.PDiag(Diag) << Name << IntendedTy << IsEnum +                                           << E->getSourceRange(),                               E->getLocStart(), /*IsStringLocation=*/false,                               SpecRange, Hints);        } else { @@ -6989,13 +6996,13 @@ CheckPrintfHandler::checkFormatExpr(const analyze_printf::PrintfSpecifier &FS,      switch (S.isValidVarArgType(ExprTy)) {      case Sema::VAK_Valid:      case Sema::VAK_ValidInCXX11: { -      unsigned diag = diag::warn_format_conversion_argument_type_mismatch; -      if (match == analyze_printf::ArgType::NoMatchPedantic) { -        diag = diag::warn_format_conversion_argument_type_mismatch_pedantic; -      } +      unsigned Diag = +          Pedantic +              ? diag::warn_format_conversion_argument_type_mismatch_pedantic +              : diag::warn_format_conversion_argument_type_mismatch;        EmitFormatDiagnostic( -          S.PDiag(diag) << AT.getRepresentativeTypeName(S.Context) << ExprTy +          S.PDiag(Diag) << AT.getRepresentativeTypeName(S.Context) << ExprTy                          << IsEnum << CSR << E->getSourceRange(),            E->getLocStart(), /*IsStringLocation*/ false, CSR);        break; @@ -7178,29 +7185,28 @@ bool CheckScanfHandler::HandleScanfSpecifier(      return true;    } -  analyze_format_string::ArgType::MatchKind match = +  analyze_format_string::ArgType::MatchKind Match =        AT.matchesType(S.Context, Ex->getType()); -  if (match == analyze_format_string::ArgType::Match) { +  bool Pedantic = Match == analyze_format_string::ArgType::NoMatchPedantic; +  if (Match == analyze_format_string::ArgType::Match)      return true; -  }    ScanfSpecifier fixedFS = FS; -  bool success = fixedFS.fixType(Ex->getType(), Ex->IgnoreImpCasts()->getType(), +  bool Success = fixedFS.fixType(Ex->getType(), Ex->IgnoreImpCasts()->getType(),                                   S.getLangOpts(), S.Context); -  unsigned diag = diag::warn_format_conversion_argument_type_mismatch; -  if (match == analyze_format_string::ArgType::NoMatchPedantic) { -    diag = diag::warn_format_conversion_argument_type_mismatch_pedantic; -  } +  unsigned Diag = +      Pedantic ? diag::warn_format_conversion_argument_type_mismatch_pedantic +               : diag::warn_format_conversion_argument_type_mismatch; -  if (success) { +  if (Success) {      // Get the fix string from the fixed format specifier.      SmallString<128> buf;      llvm::raw_svector_ostream os(buf);      fixedFS.toString(os);      EmitFormatDiagnostic( -        S.PDiag(diag) << AT.getRepresentativeTypeName(S.Context) +        S.PDiag(Diag) << AT.getRepresentativeTypeName(S.Context)                        << Ex->getType() << false << Ex->getSourceRange(),          Ex->getLocStart(),          /*IsStringLocation*/ false, @@ -7208,7 +7214,7 @@ bool CheckScanfHandler::HandleScanfSpecifier(          FixItHint::CreateReplacement(              getSpecifierRange(startSpecifier, specifierLen), os.str()));    } else { -    EmitFormatDiagnostic(S.PDiag(diag) +    EmitFormatDiagnostic(S.PDiag(Diag)                               << AT.getRepresentativeTypeName(S.Context)                               << Ex->getType() << false << Ex->getSourceRange(),                           Ex->getLocStart(), diff --git a/clang/test/FixIt/fixit-format-ios-nopedantic.m b/clang/test/FixIt/fixit-format-ios-nopedantic.m new file mode 100644 index 00000000000..0b332376c06 --- /dev/null +++ b/clang/test/FixIt/fixit-format-ios-nopedantic.m @@ -0,0 +1,21 @@ +// RUN: cp %s %t +// RUN: %clang_cc1 -triple thumbv7-apple-ios8.0.0 -fsyntax-only -Wformat -Werror -fixit %t + +int printf(const char *restrict, ...); +typedef unsigned int NSUInteger; +typedef int NSInteger; +NSUInteger getNSUInteger(); +NSInteger getNSInteger(); + +void test() { +  // For thumbv7-apple-ios8.0.0 the underlying type of ssize_t is long +  // and the underlying type of size_t is unsigned long. + +  printf("test 1: %zu", getNSUInteger()); + +  printf("test 2: %zu %zu", getNSUInteger(), getNSUInteger()); + +  printf("test 3: %zd", getNSInteger()); + +  printf("test 4: %zd %zd", getNSInteger(), getNSInteger()); +} diff --git a/clang/test/FixIt/fixit-format-ios.m b/clang/test/FixIt/fixit-format-ios.m index ef664307786..9c3670d0c84 100644 --- a/clang/test/FixIt/fixit-format-ios.m +++ b/clang/test/FixIt/fixit-format-ios.m @@ -1,5 +1,5 @@  // RUN: cp %s %t -// RUN: %clang_cc1 -triple thumbv7-apple-ios8.0.0 -fsyntax-only -Wformat -fixit %t +// RUN: %clang_cc1 -triple thumbv7-apple-ios8.0.0 -fsyntax-only -Wformat-pedantic -fixit %t  // RUN: grep -v CHECK %t | FileCheck %s  int printf(const char * restrict, ...); diff --git a/clang/test/SemaObjC/format-size-spec-nsinteger.m b/clang/test/SemaObjC/format-size-spec-nsinteger.m new file mode 100644 index 00000000000..9eb954ec512 --- /dev/null +++ b/clang/test/SemaObjC/format-size-spec-nsinteger.m @@ -0,0 +1,30 @@ +// RUN: %clang_cc1 -triple thumbv7-apple-ios -Wno-objc-root-class -fsyntax-only -verify -Wformat %s +// RUN: %clang_cc1 -triple thumbv7-apple-ios -Wno-objc-root-class -fsyntax-only -verify -Wformat-pedantic -DPEDANTIC %s + +#if !defined(PEDANTIC) +// expected-no-diagnostics +#endif + +#if __LP64__ +typedef unsigned long NSUInteger; +typedef long NSInteger; +#else +typedef unsigned int NSUInteger; +typedef int NSInteger; +#endif + +@class NSString; + +extern void NSLog(NSString *format, ...); + +void testSizeSpecifier() { +  NSInteger i = 0; +  NSUInteger j = 0; +  NSLog(@"max NSInteger = %zi", i); +  NSLog(@"max NSUinteger = %zu", j); + +#if defined(PEDANTIC) +  // expected-warning@-4 {{values of type 'NSInteger' should not be used as format arguments; add an explicit cast to 'long' instead}} +  // expected-warning@-4 {{values of type 'NSUInteger' should not be used as format arguments; add an explicit cast to 'unsigned long' instead}} +#endif +}  | 

