Make -Wformat walk the typedef chain when looking for size_t, etc.

Clang's -Wformat fix-its currently suggest using "%zu" for values of
type size_t (in C99 or C++11 mode). However, for a type such as
std::vector<T>::size_type, it does not notice that type is actually
typedeffed to size_t, and instead suggests a format for the underlying
type, such as "%lu" or "%u".

This commit makes the format string fix mechanism walk the typedef chain
so that it notices if the type is size_t, even if that isn't "at the
top".

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@160886 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/include/clang/Analysis/Analyses/FormatString.h b/include/clang/Analysis/Analyses/FormatString.h
index f99b97a..1fb05d7 100644
--- a/include/clang/Analysis/Analyses/FormatString.h
+++ b/include/clang/Analysis/Analyses/FormatString.h
@@ -355,6 +355,10 @@
   bool hasStandardConversionSpecifier(const LangOptions &LangOpt) const;
 
   bool hasStandardLengthConversionCombination() const;
+
+  /// For a TypedefType QT, if it is a named integer type such as size_t,
+  /// assign the appropriate value to LM and return true.
+  static bool namedTypeToLengthModifier(QualType QT, LengthModifier &LM);
 };
 
 } // end analyze_format_string namespace
diff --git a/lib/Analysis/FormatString.cpp b/lib/Analysis/FormatString.cpp
index caceeac..a68b9bb 100644
--- a/lib/Analysis/FormatString.cpp
+++ b/lib/Analysis/FormatString.cpp
@@ -681,3 +681,37 @@
   }
   return true;
 }
+
+bool FormatSpecifier::namedTypeToLengthModifier(QualType QT,
+                                                LengthModifier &LM) {
+  assert(isa<TypedefType>(QT) && "Expected a TypedefType");
+  const TypedefNameDecl *Typedef = cast<TypedefType>(QT)->getDecl();
+
+  for (;;) {
+    const IdentifierInfo *Identifier = Typedef->getIdentifier();
+    if (Identifier->getName() == "size_t") {
+      LM.setKind(LengthModifier::AsSizeT);
+      return true;
+    } else if (Identifier->getName() == "ssize_t") {
+      // Not C99, but common in Unix.
+      LM.setKind(LengthModifier::AsSizeT);
+      return true;
+    } else if (Identifier->getName() == "intmax_t") {
+      LM.setKind(LengthModifier::AsIntMax);
+      return true;
+    } else if (Identifier->getName() == "uintmax_t") {
+      LM.setKind(LengthModifier::AsIntMax);
+      return true;
+    } else if (Identifier->getName() == "ptrdiff_t") {
+      LM.setKind(LengthModifier::AsPtrDiff);
+      return true;
+    }
+
+    QualType T = Typedef->getUnderlyingType();
+    if (!isa<TypedefType>(T))
+      break;
+
+    Typedef = cast<TypedefType>(T)->getDecl();
+  }
+  return false;
+}
diff --git a/lib/Analysis/PrintfFormatString.cpp b/lib/Analysis/PrintfFormatString.cpp
index aa6d742..2a9644a 100644
--- a/lib/Analysis/PrintfFormatString.cpp
+++ b/lib/Analysis/PrintfFormatString.cpp
@@ -447,21 +447,8 @@
   }
 
   // Handle size_t, ptrdiff_t, etc. that have dedicated length modifiers in C99.
-  if (isa<TypedefType>(QT) && (LangOpt.C99 || LangOpt.CPlusPlus0x)) {
-    const IdentifierInfo *Identifier = QT.getBaseTypeIdentifier();
-    if (Identifier->getName() == "size_t") {
-      LM.setKind(LengthModifier::AsSizeT);
-    } else if (Identifier->getName() == "ssize_t") {
-      // Not C99, but common in Unix.
-      LM.setKind(LengthModifier::AsSizeT);
-    } else if (Identifier->getName() == "intmax_t") {
-      LM.setKind(LengthModifier::AsIntMax);
-    } else if (Identifier->getName() == "uintmax_t") {
-      LM.setKind(LengthModifier::AsIntMax);
-    } else if (Identifier->getName() == "ptrdiff_t") {
-      LM.setKind(LengthModifier::AsPtrDiff);
-    }
-  }
+  if (isa<TypedefType>(QT) && (LangOpt.C99 || LangOpt.CPlusPlus0x))
+    namedTypeToLengthModifier(QT, LM);
 
   // If fixing the length modifier was enough, we are done.
   const analyze_printf::ArgTypeResult &ATR = getArgType(Ctx, IsObjCLiteral);
diff --git a/lib/Analysis/ScanfFormatString.cpp b/lib/Analysis/ScanfFormatString.cpp
index 066d5d6..5c7e6ef 100644
--- a/lib/Analysis/ScanfFormatString.cpp
+++ b/lib/Analysis/ScanfFormatString.cpp
@@ -382,21 +382,8 @@
   }
 
   // Handle size_t, ptrdiff_t, etc. that have dedicated length modifiers in C99.
-  if (isa<TypedefType>(PT) && (LangOpt.C99 || LangOpt.CPlusPlus0x)) {
-    const IdentifierInfo *Identifier = QT.getBaseTypeIdentifier();
-    if (Identifier->getName() == "size_t") {
-      LM.setKind(LengthModifier::AsSizeT);
-    } else if (Identifier->getName() == "ssize_t") {
-      // Not C99, but common in Unix.
-      LM.setKind(LengthModifier::AsSizeT);
-    } else if (Identifier->getName() == "intmax_t") {
-      LM.setKind(LengthModifier::AsIntMax);
-    } else if (Identifier->getName() == "uintmax_t") {
-      LM.setKind(LengthModifier::AsIntMax);
-    } else if (Identifier->getName() == "ptrdiff_t") {
-      LM.setKind(LengthModifier::AsPtrDiff);
-    }
-  }
+  if (isa<TypedefType>(PT) && (LangOpt.C99 || LangOpt.CPlusPlus0x))
+    namedTypeToLengthModifier(PT, LM);
 
   // If fixing the length modifier was enough, we are done.
   const analyze_scanf::ScanfArgTypeResult &ATR = getArgType(Ctx);
diff --git a/test/Sema/format-strings-fixit.c b/test/Sema/format-strings-fixit.c
index 800691e..15ac713 100644
--- a/test/Sema/format-strings-fixit.c
+++ b/test/Sema/format-strings-fixit.c
@@ -64,6 +64,18 @@
   printf("%f", (uintmax_t) 42);
   printf("%f", (ptrdiff_t) 42);
 
+  // Look beyond the first typedef.
+  typedef size_t my_size_type;
+  typedef intmax_t my_intmax_type;
+  typedef uintmax_t my_uintmax_type;
+  typedef ptrdiff_t my_ptrdiff_type;
+  typedef int my_int_type;
+  printf("%f", (my_size_type) 42);
+  printf("%f", (my_intmax_type) 42);
+  printf("%f", (my_uintmax_type) 42);
+  printf("%f", (my_ptrdiff_type) 42);
+  printf("%f", (my_int_type) 42);
+
   // string
   printf("%ld", "foo");
 
@@ -122,6 +134,18 @@
   scanf("%f", &uIntmaxVar);
   scanf("%f", &ptrdiffVar);
 
+  // Look beyond the first typedef for named integer types.
+  typedef size_t my_size_type;
+  typedef intmax_t my_intmax_type;
+  typedef uintmax_t my_uintmax_type;
+  typedef ptrdiff_t my_ptrdiff_type;
+  typedef int my_int_type;
+  scanf("%f", (my_size_type*)&sizeVar);
+  scanf("%f", (my_intmax_type*)&intmaxVar);
+  scanf("%f", (my_uintmax_type*)&uIntmaxVar);
+  scanf("%f", (my_ptrdiff_type*)&ptrdiffVar);
+  scanf("%f", (my_int_type*)&intVar);
+
   // Preserve the original formatting.
   scanf("%o", &longVar);
   scanf("%u", &longVar);
@@ -162,6 +186,11 @@
 // CHECK: printf("%jd", (intmax_t) 42);
 // CHECK: printf("%ju", (uintmax_t) 42);
 // CHECK: printf("%td", (ptrdiff_t) 42);
+// CHECK: printf("%zu", (my_size_type) 42);
+// CHECK: printf("%jd", (my_intmax_type) 42);
+// CHECK: printf("%ju", (my_uintmax_type) 42);
+// CHECK: printf("%td", (my_ptrdiff_type) 42);
+// CHECK: printf("%d", (my_int_type) 42);
 // CHECK: printf("%s", "foo");
 // CHECK: printf("%lo", (long) 42);
 // CHECK: printf("%lu", (long) 42);
@@ -193,6 +222,11 @@
 // CHECK: scanf("%jd", &intmaxVar);
 // CHECK: scanf("%ju", &uIntmaxVar);
 // CHECK: scanf("%td", &ptrdiffVar);
+// CHECK: scanf("%zu", (my_size_type*)&sizeVar);
+// CHECK: scanf("%jd", (my_intmax_type*)&intmaxVar);
+// CHECK: scanf("%ju", (my_uintmax_type*)&uIntmaxVar);
+// CHECK: scanf("%td", (my_ptrdiff_type*)&ptrdiffVar);
+// CHECK: scanf("%d", (my_int_type*)&intVar);
 // CHECK: scanf("%lo", &longVar);
 // CHECK: scanf("%lu", &longVar);
 // CHECK: scanf("%lx", &longVar);