]> granicus.if.org Git - clang/commitdiff
Make conversion specifier warning refer to typedef if possible.
authorHans Wennborg <hans@hanshq.net>
Fri, 2 Dec 2011 19:22:15 +0000 (19:22 +0000)
committerHans Wennborg <hans@hanshq.net>
Fri, 2 Dec 2011 19:22:15 +0000 (19:22 +0000)
For example, the warning for printf("%zu", 42.0);
changes from "conversion specifies type 'unsigned long'" to "conversion
specifies type 'size_t' (aka 'unsigned long')"

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@145697 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/Analysis/Analyses/FormatString.h
lib/Analysis/FormatString.cpp
lib/Analysis/PrintfFormatString.cpp
lib/Sema/SemaChecking.cpp
test/Sema/format-strings-int-typedefs.c [new file with mode: 0644]

index b2d45e36cb13db8d8b2a6721ecd88277c355e501..4385fc3af015b0464de90ad10c9631a74f495269 100644 (file)
@@ -23,6 +23,8 @@
 
 namespace clang {
 
+class Sema;
+
 //===----------------------------------------------------------------------===//
 /// Common components of both fprintf and fscanf format strings.
 namespace analyze_format_string {
@@ -448,7 +450,7 @@ public:
   /// will return null if the format specifier does not have
   /// a matching data argument or the matching argument matches
   /// more than one type.
-  ArgTypeResult getArgType(ASTContext &Ctx) const;
+  ArgTypeResult getArgType(Sema &S) const;
 
   const OptionalFlag &hasThousandsGrouping() const {
       return HasThousandsGrouping;
index 6498ded4e374df02b8b10b35ff9fd8eefefc15ed..0171bb7aec34a2c959c10d5324ab4e279b939bd7 100644 (file)
@@ -230,7 +230,8 @@ bool ArgTypeResult::matchesType(ASTContext &C, QualType argTy) const {
       
     case SpecificTy: {
       argTy = C.getCanonicalType(argTy).getUnqualifiedType();
-      if (T == argTy)
+      QualType U = C.getCanonicalType(T);
+      if (U == argTy)
         return true;
       // Check for "compatible types".
       if (const BuiltinType *BT = argTy->getAs<BuiltinType>())
@@ -239,26 +240,26 @@ bool ArgTypeResult::matchesType(ASTContext &C, QualType argTy) const {
             break;
           case BuiltinType::Char_S:
           case BuiltinType::SChar:
-            return T == C.UnsignedCharTy;
+            return U == C.UnsignedCharTy;
           case BuiltinType::Char_U:
           case BuiltinType::UChar:                    
-            return T == C.SignedCharTy;
+            return U == C.SignedCharTy;
           case BuiltinType::Short:
-            return T == C.UnsignedShortTy;
+            return U == C.UnsignedShortTy;
           case BuiltinType::UShort:
-            return T == C.ShortTy;
+            return U == C.ShortTy;
           case BuiltinType::Int:
-            return T == C.UnsignedIntTy;
+            return U == C.UnsignedIntTy;
           case BuiltinType::UInt:
-            return T == C.IntTy;
+            return U == C.IntTy;
           case BuiltinType::Long:
-            return T == C.UnsignedLongTy;
+            return U == C.UnsignedLongTy;
           case BuiltinType::ULong:
-            return T == C.LongTy;
+            return U == C.LongTy;
           case BuiltinType::LongLong:
-            return T == C.UnsignedLongLongTy;
+            return U == C.UnsignedLongLongTy;
           case BuiltinType::ULongLong:
-            return T == C.LongLongTy;
+            return U == C.LongLongTy;
         }
       return false;
     }
@@ -485,5 +486,3 @@ bool FormatSpecifier::hasValidLengthModifier() const {
   }
   return false;
 }
-
-
index 70dbfd30ceefd56b2a5c3dcfd1b7833c9375013d..b37b23f3b184490e640034a4615a29fb9ffac72e 100644 (file)
@@ -13,6 +13,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang/Analysis/Analyses/FormatString.h"
+#include "clang/Sema/Sema.h"
 #include "FormatStringParsing.h"
 
 using clang::analyze_format_string::ArgTypeResult;
@@ -278,8 +279,27 @@ const char *ConversionSpecifier::toString() const {
 // Methods on PrintfSpecifier.
 //===----------------------------------------------------------------------===//
 
-ArgTypeResult PrintfSpecifier::getArgType(ASTContext &Ctx) const {
+/// \brief Try to find and return a typedef type named Name whose actual type
+/// is Underlying. Return Underlying if such a typedef cannot be found.
+static QualType FindTypedef(Sema &S, const char *Name, QualType Underlying) {
+  ASTContext &Ctx = S.getASTContext();
+  IdentifierInfo &II = Ctx.Idents.get(Name);
+
+  NamedDecl *D = S.LookupSingleName(S.getCurScope(), DeclarationName(&II),
+                                    SourceLocation(), Sema::LookupOrdinaryName);
+
+  if (TypedefDecl *TD = dyn_cast_or_null<TypedefDecl>(D)) {
+    QualType TypedefType = Ctx.getTypedefType(TD, QualType());
+    if (TD->getUnderlyingType() == Underlying)
+      return TypedefType;
+  }
+
+  return Underlying;
+}
+
+ArgTypeResult PrintfSpecifier::getArgType(Sema &S) const {
   const PrintfConversionSpecifier &CS = getConversionSpecifier();
+  ASTContext &Ctx = S.getASTContext();
 
   if (!CS.consumesDataArgument())
     return ArgTypeResult::Invalid();
@@ -301,11 +321,13 @@ ArgTypeResult PrintfSpecifier::getArgType(ASTContext &Ctx) const {
       case LengthModifier::AsShort: return Ctx.ShortTy;
       case LengthModifier::AsLong: return Ctx.LongTy;
       case LengthModifier::AsLongLong: return Ctx.LongLongTy;
-      case LengthModifier::AsIntMax: return Ctx.getIntMaxType();
+      case LengthModifier::AsIntMax:
+        return FindTypedef(S, "intmax_t", Ctx.getIntMaxType());
       case LengthModifier::AsSizeT:
         // FIXME: How to get the corresponding signed version of size_t?
         return ArgTypeResult();
-      case LengthModifier::AsPtrDiff: return Ctx.getPointerDiffType();
+      case LengthModifier::AsPtrDiff:
+        return FindTypedef(S, "ptrdiff_t", Ctx.getPointerDiffType());
     }
 
   if (CS.isUIntArg())
@@ -317,9 +339,10 @@ ArgTypeResult PrintfSpecifier::getArgType(ASTContext &Ctx) const {
       case LengthModifier::AsShort: return Ctx.UnsignedShortTy;
       case LengthModifier::AsLong: return Ctx.UnsignedLongTy;
       case LengthModifier::AsLongLong: return Ctx.UnsignedLongLongTy;
-      case LengthModifier::AsIntMax: return Ctx.getUIntMaxType();
+      case LengthModifier::AsIntMax:
+        return FindTypedef(S, "uintmax_t", Ctx.getUIntMaxType());
       case LengthModifier::AsSizeT:
-        return Ctx.getSizeType();
+        return FindTypedef(S, "size_t", Ctx.getSizeType());
       case LengthModifier::AsPtrDiff:
         // FIXME: How to get the corresponding unsigned
         // version of ptrdiff_t?
index 0d640a8c123cafa9bce5bc983aa86233a78d998e..c0d6702b589449d4cf42fde0afae6e41b61b31c6 100644 (file)
@@ -2206,7 +2206,7 @@ CheckPrintfHandler::HandlePrintfSpecifier(const analyze_printf::PrintfSpecifier
   // Now type check the data expression that matches the
   // format specifier.
   const Expr *Ex = getDataArg(argIndex);
-  const analyze_printf::ArgTypeResult &ATR = FS.getArgType(S.Context);
+  const analyze_printf::ArgTypeResult &ATR = FS.getArgType(S);
   if (ATR.isValid() && !ATR.matchesType(S.Context, Ex->getType())) {
     // Check if we didn't match because of an implicit cast from a 'char'
     // or 'short' to an 'int'.  This is done because printf is a varargs
diff --git a/test/Sema/format-strings-int-typedefs.c b/test/Sema/format-strings-int-typedefs.c
new file mode 100644 (file)
index 0000000..931449c
--- /dev/null
@@ -0,0 +1,30 @@
+// RUN: %clang_cc1 -triple i386-apple-darwin9 -fsyntax-only -verify %s
+
+int printf(char const *, ...);
+
+void test(void) {
+  // size_t, et al. have not been declared yet,
+  // so the warning should refer to the builtin types.
+  printf("%jd", 42.0); // expected-warning {{conversion specifies type 'long long'}}
+  printf("%ju", 42.0); // expected-warning {{conversion specifies type 'unsigned long long'}}
+  printf("%zu", 42.0); // expected-warning {{conversion specifies type 'unsigned long'}}
+  printf("%td", 42.0); // expected-warning {{conversion specifies type 'int'}}
+
+  typedef __SIZE_TYPE__ size_t;
+  typedef __INTMAX_TYPE__ intmax_t;
+  typedef __UINTMAX_TYPE__ uintmax_t;
+  typedef __PTRDIFF_TYPE__ ptrdiff_t;
+
+  printf("%jd", 42.0); // expected-warning {{conversion specifies type 'intmax_t' (aka 'long long')}}
+  printf("%ju", 42.0); // expected-warning {{conversion specifies type 'uintmax_t' (aka 'unsigned long long')}}
+  printf("%zu", 42.0); // expected-warning {{conversion specifies type 'size_t' (aka 'unsigned long')}}
+  printf("%td", 42.0); // expected-warning {{conversion specifies type 'ptrdiff_t' (aka 'int')}}
+}
+
+void test2(void) {
+  typedef void *size_t;
+
+  // The typedef for size_t does not match the builtin type,
+  // so the warning should not refer to it.
+  printf("%zu", 42.0); // expected-warning {{conversion specifies type 'unsigned long'}}
+}