Skip to content

Commit

Permalink
Make conversion specifier warning refer to typedef if possible.
Browse files Browse the repository at this point in the history
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
  • Loading branch information
zmodem committed Dec 2, 2011
1 parent 07165b9 commit 5fdc1b9
Show file tree
Hide file tree
Showing 5 changed files with 74 additions and 20 deletions.
4 changes: 3 additions & 1 deletion include/clang/Analysis/Analyses/FormatString.h
Expand Up @@ -23,6 +23,8 @@

namespace clang {

class Sema;

//===----------------------------------------------------------------------===//
/// Common components of both fprintf and fscanf format strings.
namespace analyze_format_string {
Expand Down Expand Up @@ -448,7 +450,7 @@ class PrintfSpecifier : public analyze_format_string::FormatSpecifier {
/// 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;
Expand Down
25 changes: 12 additions & 13 deletions lib/Analysis/FormatString.cpp
Expand Up @@ -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>())
Expand All @@ -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;
}
Expand Down Expand Up @@ -485,5 +486,3 @@ bool FormatSpecifier::hasValidLengthModifier() const {
}
return false;
}


33 changes: 28 additions & 5 deletions lib/Analysis/PrintfFormatString.cpp
Expand Up @@ -13,6 +13,7 @@
//===----------------------------------------------------------------------===//

#include "clang/Analysis/Analyses/FormatString.h"
#include "clang/Sema/Sema.h"
#include "FormatStringParsing.h"

using clang::analyze_format_string::ArgTypeResult;
Expand Down Expand Up @@ -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();
Expand All @@ -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())
Expand All @@ -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?
Expand Down
2 changes: 1 addition & 1 deletion lib/Sema/SemaChecking.cpp
Expand Up @@ -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
Expand Down
30 changes: 30 additions & 0 deletions 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'}}
}

0 comments on commit 5fdc1b9

Please sign in to comment.