diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -30,6 +30,7 @@ #include "clang/AST/NSAPI.h" #include "clang/AST/NonTrivialTypeVisitor.h" #include "clang/AST/OperationKinds.h" +#include "clang/AST/ParentMapContext.h" #include "clang/AST/RecordLayout.h" #include "clang/AST/Stmt.h" #include "clang/AST/TemplateBase.h" @@ -54,6 +55,8 @@ #include "clang/Basic/TargetInfo.h" #include "clang/Basic/TypeTraits.h" #include "clang/Lex/Lexer.h" // TODO: Extract static functions to fix layering. +#include "clang/Lex/LiteralSupport.h" +#include "clang/Lex/Preprocessor.h" #include "clang/Sema/Initialization.h" #include "clang/Sema/Lookup.h" #include "clang/Sema/Ownership.h" @@ -86,6 +89,7 @@ #include "llvm/Support/Locale.h" #include "llvm/Support/MathExtras.h" #include "llvm/Support/SaveAndRestore.h" +#include "llvm/Support/Signals.h" #include "llvm/Support/raw_ostream.h" #include #include @@ -11049,19 +11053,132 @@ S.Diag(E->getRHS()->getBeginLoc(), diag::warn_atomic_implicit_seq_cst); } +auto &GetSeenLocs() { + static llvm::SmallSet SeenLocs; + return SeenLocs; +} + +SourceLocation GetCorrectedEndLoc(Sema &S, SourceLocation Loc) { + auto &SM = S.getSourceManager(); + if (Loc.isMacroID()) { + Loc = SM.getSpellingLoc(Loc); + assert(Loc.isFileID()); + } + return S.getLocForEndOfToken(Loc); +} + +static bool operator==(PresumedLoc loc1, PresumedLoc loc2) { + return loc1.getFileID() == loc2.getFileID() && + loc1.getColumn() == loc2.getColumn() && + loc1.getLine() == loc2.getLine(); +} + +bool endswith(std::string const &fullString, std::string const &ending) { + if (fullString.length() >= ending.length()) { + return (0 == fullString.compare (fullString.length() - ending.length(), ending.length(), ending)); + } else { + return false; + } +} + /// Diagnose an implicit cast; purely a helper for CheckImplicitConversion. static void DiagnoseImpCast(Sema &S, Expr *E, QualType SourceType, QualType T, SourceLocation CContext, unsigned diag, bool pruneControlFlow = false) { + auto &SM = S.getSourceManager(); + auto &Ctx = S.getASTContext(); + auto &PP = S.getPreprocessor(); + + SourceLocation BeginInsertLoc = E->getBeginLoc(); + SourceLocation EndInsertLoc = E->getEndLoc(); + + bool InTemplatedFunc = S.getCurFunctionDecl() && (S.getCurFunctionDecl()->getTemplatedKind() != FunctionDecl::TK_NonTemplate); + bool InMacro = BeginInsertLoc.isMacroID() || EndInsertLoc.isMacroID(); + + if (SM.getPresumedLoc(BeginInsertLoc) == SM.getPresumedLoc(EndInsertLoc) && + BeginInsertLoc.isMacroID() && EndInsertLoc.isMacroID()) { + InMacro = true; + } else { + EndInsertLoc = GetCorrectedEndLoc(S, EndInsertLoc); + } + + if (GetSeenLocs().count(BeginInsertLoc)) { + // Skip this since we already apply the fixit. + return; + } + GetSeenLocs().insert(BeginInsertLoc); + auto GetTypeName = [&](QualType T) -> std::string { + auto CanonT = T.getCanonicalType(); + if (CanonT == Ctx.getSizeType()) + return "size_t"; + + auto Spelling = T.getAsString(S.getPrintingPolicy()); + auto CanonSpelling = CanonT.getAsString(S.getPrintingPolicy()); + + if (CanonT->isSpecificBuiltinType(BuiltinType::SChar)) + return Spelling == CanonSpelling ? Spelling : "int8_t"; + if (CanonT->isSpecificBuiltinType(BuiltinType::UChar)) + return Spelling == CanonSpelling ? Spelling : "uint8_t"; + if (CanonT->isSpecificBuiltinType(BuiltinType::Short)) + return Spelling == CanonSpelling ? Spelling : "int16_t"; + if (CanonT->isSpecificBuiltinType(BuiltinType::UShort)) + return Spelling == CanonSpelling ? Spelling : "uint16_t"; + if (CanonT->isSpecificBuiltinType(BuiltinType::Int)) + return Spelling == CanonSpelling ? Spelling : "int32_t"; + if (CanonT->isSpecificBuiltinType(BuiltinType::UInt)) + return Spelling == CanonSpelling ? Spelling : "uint32_t"; + if (CanonT->isSpecificBuiltinType(BuiltinType::Long) || + CanonT->isSpecificBuiltinType(BuiltinType::LongLong)) + return Spelling == CanonSpelling ? Spelling : "int64_t"; + if (CanonT->isSpecificBuiltinType(BuiltinType::ULong) || + CanonT->isSpecificBuiltinType(BuiltinType::ULongLong)) + return Spelling == CanonSpelling ? Spelling : "uint64_t"; + + return CanonSpelling; + }; + + auto Str = GetTypeName(T); + llvm::SmallString<16> CastStr; + if (S.getLangOpts().CPlusPlus) { + CastStr.append("static_cast<"); + CastStr.append(Str); + CastStr.append(">("); + } else { + CastStr.append("("); + CastStr.append(Str); + CastStr.append(")("); + } + + if (InTemplatedFunc || InMacro) { + // Warn but do not suggest fixit since it may not entirely be possible to + // static_cast to a dependent type here. + if (pruneControlFlow) { + S.DiagRuntimeBehavior(E->getExprLoc(), E, + S.PDiag(diag) + << SourceType << T << E->getSourceRange() + << SourceRange(CContext) + ); + return; + } + S.Diag(E->getExprLoc(), diag) + << SourceType << T << E->getSourceRange() << SourceRange(CContext); + return; + } + if (pruneControlFlow) { S.DiagRuntimeBehavior(E->getExprLoc(), E, S.PDiag(diag) << SourceType << T << E->getSourceRange() - << SourceRange(CContext)); + << SourceRange(CContext) + << FixItHint::CreateInsertion(BeginInsertLoc, CastStr) + << FixItHint::CreateInsertion(EndInsertLoc, ")") + ); return; } S.Diag(E->getExprLoc(), diag) - << SourceType << T << E->getSourceRange() << SourceRange(CContext); + << SourceType << T << E->getSourceRange() << SourceRange(CContext) + << FixItHint::CreateInsertion(BeginInsertLoc, CastStr) + << FixItHint::CreateInsertion(EndInsertLoc, ")"); } /// Diagnose an implicit cast; purely a helper for CheckImplicitConversion. diff --git a/clang/test/Frontend/fuchsia-wconversion-fixes.cpp b/clang/test/Frontend/fuchsia-wconversion-fixes.cpp new file mode 100644 --- /dev/null +++ b/clang/test/Frontend/fuchsia-wconversion-fixes.cpp @@ -0,0 +1,116 @@ +// RUN: %clang_cc1 -fsyntax-only -verify -Wconversion %s +// RUN: %clang_cc1 -Wconversion %s -fixit-to-temporary -fixit-recompile -o - -E | FileCheck %s + +// FIXME: Add something that triggers the check here. +//void f(); + +// FIXME: Verify the applied fix. +// * Make the CHECK patterns specific enough and try to make verified lines +// unique to avoid incorrect matches. +// * Use {{}} for regular expressions. + +// FIXME: Add something that doesn't trigger the check here. +//void awesome_f2(); + +typedef unsigned int uint32_t; +typedef unsigned long long uint64_t; +static_assert(sizeof(uint32_t) == 4, ""); +static_assert(sizeof(uint64_t) == 8, ""); + +typedef unsigned long size_t; + +typedef signed int int32_t; +typedef signed long long int64_t; +static_assert(sizeof(int32_t) == 4, ""); +static_assert(sizeof(int64_t) == 8, ""); + +uint32_t func(uint64_t x) { + return x + 1; // expected-warning{{implicit conversion loses integer precision}} +// CHECK: uint32_t func(uint64_t x) { +// CHECK-NEXT: return static_cast(x + 1); +} + +uint32_t func2(uint64_t x) { + return static_cast(x + 1); +} + +signed char func3(unsigned char x) { + return x; // expected-warning{{implicit conversion changes signedness}} +// CHECK: signed char func3(unsigned char x) { +// CHECK-NEXT: return static_cast(x); +} + +constexpr signed int kSysError = 0xabDECADE; // expected-warning{{implicit conversion changes signedness}} +// CHECK: constexpr signed int kSysError = static_cast(0xabDECADE); + +#define MACRO(x) ((x) + 1) + +int32_t intfunc(); +#define MACRO2(x) intfunc() +#define MACRO3(x) static_cast(intfunc()) + +namespace std { +namespace __2 { +struct array { + using size_type = size_t; + int &operator[](size_type s); +}; +} +} + +int u32func(uint32_t); + +template +struct Arr { + T *begin() const; + T *end() const; +}; + +void func4(uint32_t offset, int32_t bytes_read, float f, std::__2::array &a, + int i, Arr arr) { + offset += bytes_read; // expected-warning{{implicit conversion changes signedness}} +// CHECK: offset += static_cast(bytes_read); + + struct { + int32_t st_size; + } stat_buffer; + uint64_t size = stat_buffer.st_size; // expected-warning{{implicit conversion changes signedness}} +// CHECK: uint64_t size = static_cast(stat_buffer.st_size); + + int32_t abc = 1, xyz = 2; + uint32_t x = abc - xyz; // expected-warning{{implicit conversion changes signedness}} +// CHECK: uint32_t x = static_cast(abc - xyz); + + u32func(abc - xyz); // expected-warning{{implicit conversion changes signedness}} +// CHECK: u32func(static_cast(abc - xyz)); + + MACRO(u32func(abc - xyz)); // expected-warning{{implicit conversion changes signedness}} + + x = MACRO2(intfunc()); // expected-warning{{implicit conversion changes signedness}} + + if (f) return; // expected-warning{{implicit conversion turns floating-point number into integer: 'float' to 'bool'}} +// CHECK: if (static_cast(f)) return; + + x = MACRO3(x); // expected-warning{{implicit conversion loses integer precision}} + + a[i] = 2; // expected-warning{{implicit conversion changes signedness}} +// CHECK: a[static_cast(i)] = 2; + + //for (uint32_t elem : arr) { + // u32func(elem); + //} +} + +template +struct Target { + static void invoke(Callable target, Arg arg) { + target(arg); // expected-warning{{implicit conversion loses integer precision}} + } +}; + +void voidfunc(int32_t); + +void func5(uint64_t i) { + Target target; + target.invoke(voidfunc, i); // expected-note{{in instantiation of member function}} +}