From 70f7213d2cf81b2dba91ec939e44f07c975e684e Mon Sep 17 00:00:00 2001 From: Hans Wennborg Date: Fri, 2 Dec 2011 19:22:15 +0000 Subject: [PATCH] Make conversion specifier warning refer to typedef if possible. 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')" llvm-svn: 145697 --- .../clang/Analysis/Analyses/FormatString.h | 4 ++- clang/lib/Analysis/FormatString.cpp | 25 +++++++------- clang/lib/Analysis/PrintfFormatString.cpp | 33 ++++++++++++++++--- clang/lib/Sema/SemaChecking.cpp | 2 +- clang/test/Sema/format-strings-int-typedefs.c | 30 +++++++++++++++++ 5 files changed, 74 insertions(+), 20 deletions(-) create mode 100644 clang/test/Sema/format-strings-int-typedefs.c diff --git a/clang/include/clang/Analysis/Analyses/FormatString.h b/clang/include/clang/Analysis/Analyses/FormatString.h index b2d45e36cb13..4385fc3af015 100644 --- a/clang/include/clang/Analysis/Analyses/FormatString.h +++ b/clang/include/clang/Analysis/Analyses/FormatString.h @@ -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; diff --git a/clang/lib/Analysis/FormatString.cpp b/clang/lib/Analysis/FormatString.cpp index 6498ded4e374..0171bb7aec34 100644 --- a/clang/lib/Analysis/FormatString.cpp +++ b/clang/lib/Analysis/FormatString.cpp @@ -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()) @@ -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; } - - diff --git a/clang/lib/Analysis/PrintfFormatString.cpp b/clang/lib/Analysis/PrintfFormatString.cpp index 70dbfd30ceef..b37b23f3b184 100644 --- a/clang/lib/Analysis/PrintfFormatString.cpp +++ b/clang/lib/Analysis/PrintfFormatString.cpp @@ -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(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? diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp index 0d640a8c123c..c0d6702b5894 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -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/clang/test/Sema/format-strings-int-typedefs.c b/clang/test/Sema/format-strings-int-typedefs.c new file mode 100644 index 000000000000..931449ccc39c --- /dev/null +++ b/clang/test/Sema/format-strings-int-typedefs.c @@ -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'}} +}