]> granicus.if.org Git - clang/commitdiff
[clang] Add getUnsignedPointerDiffType method
authorAlexander Shaposhnikov <shal1t712@gmail.com>
Thu, 28 Sep 2017 23:11:31 +0000 (23:11 +0000)
committerAlexander Shaposhnikov <shal1t712@gmail.com>
Thu, 28 Sep 2017 23:11:31 +0000 (23:11 +0000)
C11 standard refers to the unsigned counterpart of the type ptrdiff_t
in the paragraph 7.21.6.1p7 where it defines the format specifier %tu.
In Clang (in PrintfFormatString.cpp, lines 508-510) there is a FIXME for this case,
in particular, Clang didn't diagnose %tu issues at all, i.e.
it didn't emit any warnings on the code printf("%tu", 3.14).
In this diff we add a method getUnsignedPointerDiffType for getting the corresponding type
similarly to how it's already done in the other analogous cases (size_t, ssize_t, ptrdiff_t etc)
and fix -Wformat diagnostics for %tu plus the emitted fix-it as well.

Test plan: make check-all

Differential revision: https://reviews.llvm.org/D38270

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

include/clang/AST/ASTContext.h
include/clang/Basic/TargetInfo.h
lib/AST/ASTContext.cpp
lib/Analysis/PrintfFormatString.cpp
lib/Analysis/ScanfFormatString.cpp
test/FixIt/format.m
test/Sema/format-strings-scanf.c

index 831d4614a3c4369d35ee544f595e037183401293..eadd69c595d50c1e103929487447a451035f77ad 100644 (file)
@@ -1489,6 +1489,11 @@ public:
   /// <stddef.h>. Pointer - pointer requires this (C99 6.5.6p9).
   QualType getPointerDiffType() const;
 
+  /// \brief Return the unique unsigned counterpart of "ptrdiff_t"
+  /// integer type. The standard (C11 7.21.6.1p7) refers to this type
+  /// in the definition of %tu format specifier.
+  QualType getUnsignedPointerDiffType() const;
+
   /// \brief Return the unique type for "pid_t" defined in
   /// <sys/types.h>. We need this to compute the correct type for vfork().
   QualType getProcessIDType() const;
index 38a7bfed87e8ff157dd434bf71f37111c4f20536..8c3a5b229ae1ba70461bc954604446d36229f3f7 100644 (file)
@@ -248,6 +248,9 @@ public:
   IntType getPtrDiffType(unsigned AddrSpace) const {
     return AddrSpace == 0 ? PtrDiffType : getPtrDiffTypeV(AddrSpace);
   }
+  IntType getUnsignedPtrDiffType(unsigned AddrSpace) const {
+    return getCorrespondingUnsignedType(getPtrDiffType(AddrSpace));
+  }
   IntType getIntPtrType() const { return IntPtrType; }
   IntType getUIntPtrType() const {
     return getCorrespondingUnsignedType(IntPtrType);
index 82e74528d19bf81fd8fd79307e54c06f4c9f238d..2a6079965314537938c1d6e35f4eb66173b130e2 100644 (file)
@@ -4571,6 +4571,13 @@ QualType ASTContext::getPointerDiffType() const {
   return getFromTargetType(Target->getPtrDiffType(0));
 }
 
+/// \brief Return the unique unsigned counterpart of "ptrdiff_t"
+/// integer type. The standard (C11 7.21.6.1p7) refers to this type
+/// in the definition of %tu format specifier.
+QualType ASTContext::getUnsignedPointerDiffType() const {
+  return getFromTargetType(Target->getUnsignedPtrDiffType(0));
+}
+
 /// \brief Return the unique type for "pid_t" defined in
 /// <sys/types.h>. We need this to compute the correct type for vfork().
 QualType ASTContext::getProcessIDType() const {
index 26636376d1e98d87cdd6b961550fd49eab229f10..dfaed26564e6f119c278b02fae3c31ac7cac2fb3 100644 (file)
@@ -505,9 +505,7 @@ ArgType PrintfSpecifier::getArgType(ASTContext &Ctx,
                    ? ArgType(Ctx.UnsignedLongLongTy, "unsigned __int64")
                    : ArgType(Ctx.UnsignedIntTy, "unsigned __int32");
       case LengthModifier::AsPtrDiff:
-        // FIXME: How to get the corresponding unsigned
-        // version of ptrdiff_t?
-        return ArgType();
+        return ArgType(Ctx.getUnsignedPointerDiffType(), "unsigned ptrdiff_t");
       case LengthModifier::AsAllocate:
       case LengthModifier::AsMAllocate:
       case LengthModifier::AsWide:
index 734dc7521c639a7277059e40e66cdfb0c2f5d671..8398a4b82d5a396eba35263c37070847d4e2da47 100644 (file)
@@ -291,8 +291,8 @@ ArgType ScanfSpecifier::getArgType(ASTContext &Ctx) const {
         case LengthModifier::AsSizeT:
           return ArgType::PtrTo(ArgType(Ctx.getSizeType(), "size_t"));
         case LengthModifier::AsPtrDiff:
-          // FIXME: Unsigned version of ptrdiff_t?
-          return ArgType();
+          return ArgType::PtrTo(
+              ArgType(Ctx.getUnsignedPointerDiffType(), "unsigned ptrdiff_t"));
         case LengthModifier::AsLongDouble:
           // GNU extension.
           return ArgType::PtrTo(Ctx.UnsignedLongLongTy);
index c3cf2b1f3c567b133be8b016ac854118946ec0b2..40655a0e808ef337fe9998e06623c18868384ad6 100644 (file)
@@ -242,6 +242,37 @@ void testSizeTypes() {
   // see the comment in PrintfSpecifier::fixType in PrintfFormatString.cpp.
 }
 
+typedef __PTRDIFF_TYPE__ ptrdiff_t;
+#define __UNSIGNED_PTRDIFF_TYPE__                                              \
+  __typeof__(_Generic((__PTRDIFF_TYPE__)0,                                     \
+                      long long int : (unsigned long long int)0,               \
+                      long int : (unsigned long int)0,                         \
+                      int : (unsigned int)0,                                   \
+                      short : (unsigned short)0,                               \
+                      signed char : (unsigned char)0))
+
+void testPtrDiffTypes() {
+  __UNSIGNED_PTRDIFF_TYPE__ p1 = 0;
+  printf("%tu", p1);  // No warning.
+
+  printf("%tu", 0.f); // expected-warning-re{{format specifies type 'unsigned ptrdiff_t' (aka '{{.+}}') but the argument has type 'float'}}
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:11-[[@LINE-1]]:14}:"%f"
+  
+  ptrdiff_t p2 = 0;
+  printf("%td", p2);  // No warning.
+
+  printf("%td", 0.f); // expected-warning-re{{format specifies type 'ptrdiff_t' (aka '{{.+}}') but the argument has type 'float'}}
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:11-[[@LINE-1]]:14}:"%f"
+
+  ptrdiff_t p3 = 0;
+  printf("%tn", &p3); // No warning.
+
+  short x;
+  printf("%tn", &x); // expected-warning-re{{format specifies type 'ptrdiff_t *' (aka '{{.+}}') but the argument has type 'short *'}}
+  // PrintfSpecifier::fixType doesn't handle %n, so a fix-it is not emitted,
+  // see the comment in PrintfSpecifier::fixType in PrintfFormatString.cpp.
+}
+
 void testEnum() {
   typedef enum {
     ImplicitA = 1,
index e700d10618d371cede5fd41e48c20a2a3e7dde9c..b7cdd7dd4a9a5ce893c4ac3d31e83cfb3b4658b6 100644 (file)
@@ -13,6 +13,16 @@ typedef __SIZE_TYPE__ size_t;
                       unsigned short : (short)0,                               \
                       unsigned char : (signed char)0))
 typedef __SSIZE_TYPE__ ssize_t;                         
+
+typedef __PTRDIFF_TYPE__ ptrdiff_t;
+#define __UNSIGNED_PTRDIFF_TYPE__                                              \
+  __typeof__(_Generic((__PTRDIFF_TYPE__)0,                                     \
+                      long long int : (unsigned long long int)0,               \
+                      long int : (unsigned long int)0,                         \
+                      int : (unsigned int)0,                                   \
+                      short : (unsigned short)0,                               \
+                      signed char : (unsigned char)0))
+
 typedef struct _FILE FILE;
 typedef __WCHAR_TYPE__ wchar_t;
 
@@ -200,6 +210,26 @@ void test_size_types() {
   scanf("%zn", &d3); // expected-warning-re{{format specifies type 'ssize_t *' (aka '{{.+}}') but the argument has type 'double *'}}
 }
 
+void test_ptrdiff_t_types() {
+  __UNSIGNED_PTRDIFF_TYPE__ p1 = 0;
+  scanf("%tu", &p1); // No warning.
+
+  double d1 = 0.;
+  scanf("%tu", &d1); // expected-warning-re{{format specifies type 'unsigned ptrdiff_t *' (aka '{{.+}}') but the argument has type 'double *'}}
+
+  ptrdiff_t p2 = 0;
+  scanf("%td", &p2); // No warning.
+  
+  double d2 = 0.;
+  scanf("%td", &d2); // expected-warning-re{{format specifies type 'ptrdiff_t *' (aka '{{.+}}') but the argument has type 'double *'}}
+
+  ptrdiff_t p3 = 0;
+  scanf("%tn", &p3); // No warning.
+  
+  double d3 = 0.;
+  scanf("%tn", &d3); // expected-warning-re{{format specifies type 'ptrdiff_t *' (aka '{{.+}}') but the argument has type 'double *'}}
+}
+
 void check_conditional_literal(char *s, int *i) {
   scanf(0 ? "%s" : "%d", i); // no warning
   scanf(1 ? "%s" : "%d", i); // expected-warning{{format specifies type 'char *'}}