diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -51,7 +51,30 @@ Improvements to Clang's diagnostics ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -- ... +- A new diagnostics group ``-Wimplicit-widening-conversion`` was added (as part + of ``-Wall`` group) which includes the following diagnostics: + - ``-Wimplicit-widening-of-multiplication-result``: + + .. code-block:: c++ + + long mul(int a, int b) { + return a * b; // expected-warning {{performing an implicit widening conversion to type 'long' of a multiplication performed in type 'int'}} + } + + - ``-Wimplicit-widening-of-pointer-offset``: + + .. code-block:: c++ + + char* ptr_add(char *base, int a, int b) { + return base + a * b; // expected-warning {{result of multiplication in type 'int' is used as a pointer offset after an implicit widening conversion to type 'ssize_t'}} + } + + char ptr_subscript(char *base, int a, int b) { + return base[a * b]; // expected-warning {{result of multiplication in type 'int' is used as a pointer offset after an implicit widening conversion to type 'ssize_t'}} + } + + The diagnostics can be silenced by making the widening cast explicit, + or fixed, by performing the multiplication in a wider type to begin with. Non-comprehensive list of changes in this release ------------------------------------------------- diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h --- a/clang/include/clang/AST/ASTContext.h +++ b/clang/include/clang/AST/ASTContext.h @@ -2714,6 +2714,14 @@ // a given fixed point type. QualType getCorrespondingUnsignedType(QualType T) const; + // Per C99 6.2.5p6, for every signed integer type, there is a corresponding + // unsigned integer type. This method takes an unsigned type, and returns the + // corresponding signed integer type. + // With the introduction of fixed point types in ISO N1169, this method also + // accepts fixed point types and returns the corresponding signed type for + // a given fixed point type. + QualType getCorrespondingSignedType(QualType T) const; + // Per ISO N1169, this method accepts fixed point types and returns the // corresponding saturated type for a given fixed point type. QualType getCorrespondingSaturatedType(QualType Ty) const; diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td --- a/clang/include/clang/Basic/DiagnosticGroups.td +++ b/clang/include/clang/Basic/DiagnosticGroups.td @@ -843,6 +843,14 @@ UnusedPropertyIvar]>, DiagCategory<"Unused Entity Issue">; +def ImplicitWideningOfMulResult : DiagGroup<"implicit-widening-of-multiplication-result">; +def ImplicitWideningOfPtrOffset : DiagGroup<"implicit-widening-of-pointer-offset">; + +def ImplicitWideningConversion : DiagGroup<"implicit-widening-conversion", [ + ImplicitWideningOfMulResult, + ImplicitWideningOfPtrOffset, + ]>, DiagCategory<"Potentially misplaced implicit widening conversion">; + // Format settings. def FormatInvalidSpecifier : DiagGroup<"format-invalid-specifier">; def FormatSecurity : DiagGroup<"format-security">; @@ -956,8 +964,14 @@ // Note that putting warnings in -Wall will not disable them by default. If a // warning should be active _only_ when -Wall is passed in, mark it as // DefaultIgnore in addition to putting it here. -def All : DiagGroup<"all", [Most, Parentheses, Switch, SwitchBool, - MisleadingIndentation]>; +def All : DiagGroup<"all", [ + Most, + Parentheses, + Switch, + SwitchBool, + MisleadingIndentation, + ImplicitWideningConversion, + ]>; // Warnings that should be in clang-cl /w4. def : DiagGroup<"CL4", [All, Extra]>; diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -3714,6 +3714,19 @@ "code; pointer may be assumed to always convert to true">, InGroup; +def warn_imp_widening_of_mul_result : Warning< + "performing an implicit widening conversion to type %0 " + "of a multiplication performed in type %1">, + InGroup, DefaultIgnore; +def warn_imp_widening_of_ptr_offset : Warning< + "result of multiplication in type %0 is used as a pointer offset " + "after an implicit widening conversion to type '%1'">, + InGroup, DefaultIgnore; +def note_warn_imp_widening_fix : Note< + "perform multiplication in a wider type">; +def note_warn_imp_widening_silence : Note< + "make conversion explicit to silence this warning">; + def warn_xor_used_as_pow : Warning< "result of '%0' is %1; did you mean exponentiation?">, InGroup; diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -4745,6 +4745,17 @@ /// Warn when implicitly casting 0 to nullptr. void diagnoseZeroToNullptrConversion(CastKind Kind, const Expr *E); + /// Warn when performing an implicit widening conversion of a multiplication, + /// when perhaps the whole multiplication should be performed in a wider type. + void diagnoseImplicitWideningConversionOfMultiplication(Expr *E, QualType Ty, + CastKind Kind); + + /// Warn when performing an implicit widening conversion of a multiplication, + /// when perhaps the whole multiplication should be performed in a wider type, + /// when the result is used as an offset for the pointer. + void + diagnoseImplicitWideningConversionOfMultiplicationOfPointerOffset(Expr *E); + ParsingDeclState PushParsingDeclaration(sema::DelayedDiagnosticPool &pool) { return DelayedDiagnostics.push(pool); } diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -10052,6 +10052,69 @@ } } +QualType ASTContext::getCorrespondingSignedType(QualType T) const { + assert((T->hasUnsignedIntegerRepresentation() || + T->isUnsignedFixedPointType()) && + "Unexpected type"); + + // Turn <4 x unsigned int> -> <4 x signed int> + if (const auto *VTy = T->getAs()) + return getVectorType(getCorrespondingSignedType(VTy->getElementType()), + VTy->getNumElements(), VTy->getVectorKind()); + + // For enums, we return the signed version of the base type. + if (const auto *ETy = T->getAs()) + T = ETy->getDecl()->getIntegerType(); + + switch (T->castAs()->getKind()) { + case BuiltinType::Char_U: + case BuiltinType::UChar: + return SignedCharTy; + case BuiltinType::UShort: + return ShortTy; + case BuiltinType::UInt: + return IntTy; + case BuiltinType::ULong: + return LongTy; + case BuiltinType::ULongLong: + return LongLongTy; + case BuiltinType::UInt128: + return Int128Ty; + // wchar_t is special. It is either unsigned or not, but when it's unsigned, + // there's no matching "signed wchar_t". Therefore we return the signed + // version of it's underlying type instead. + case BuiltinType::WChar_U: + return getSignedWCharType(); + + case BuiltinType::UShortAccum: + return ShortAccumTy; + case BuiltinType::UAccum: + return AccumTy; + case BuiltinType::ULongAccum: + return LongAccumTy; + case BuiltinType::SatUShortAccum: + return SatShortAccumTy; + case BuiltinType::SatUAccum: + return SatAccumTy; + case BuiltinType::SatULongAccum: + return SatLongAccumTy; + case BuiltinType::UShortFract: + return ShortFractTy; + case BuiltinType::UFract: + return FractTy; + case BuiltinType::ULongFract: + return LongFractTy; + case BuiltinType::SatUShortFract: + return SatShortFractTy; + case BuiltinType::SatUFract: + return SatFractTy; + case BuiltinType::SatULongFract: + return SatLongFractTy; + default: + llvm_unreachable("Unexpected unsigned integer or fixed point type"); + } +} + ASTMutationListener::~ASTMutationListener() = default; void ASTMutationListener::DeducedReturnType(const FunctionDecl *FD, diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp --- a/clang/lib/Sema/Sema.cpp +++ b/clang/lib/Sema/Sema.cpp @@ -547,6 +547,133 @@ << FixItHint::CreateReplacement(E->getSourceRange(), "nullptr"); } +static Expr *GetLHSOfMulBinOp(Expr *E) { + // Is this: long r = int(x) * int(y); ? + // FIXME: shall we skip brackets/casts/etc? + auto *BO = dyn_cast(E); + if (!BO || BO->getOpcode() != BO_Mul) + // FIXME: what about: long r = int(x) + (int(y) * int(z)); ? + return nullptr; + return BO->getLHS(); +} + +void Sema::diagnoseImplicitWideningConversionOfMultiplication(Expr *E, + QualType Ty, + CastKind Kind) { + // We are only interested in basic integral casts. + if (Kind != CK_IntegralCast) + return; + + QualType ExprTy = E->getType(); + + assert(!ExprTy->isDependentType() && !Ty->isDependentType() && + "Don't expect to ever get here in template context."); + + // This must be a widening cast. Else we do not care. + unsigned SrcWidth = Context.getIntWidth(ExprTy); + unsigned TgtWidth = Context.getIntWidth(Ty); + if (TgtWidth <= SrcWidth) + return; + + // Does the index expression look like it might be unintentionally computed + // in a narrower-than-wanted type? + Expr *LHS = GetLHSOfMulBinOp(E); + if (!LHS) + return; + + // Ok, looks like we should diagnose this. + Diag(E->getBeginLoc(), diag::warn_imp_widening_of_mul_result) + << Ty << E->getType(); + + Diag(E->getBeginLoc(), diag::note_warn_imp_widening_silence) + << FixItHint::CreateInsertion(E->getBeginLoc(), + "(" + Ty.getAsString() + ")(") + << FixItHint::CreateInsertion(E->getEndLoc(), ")") << E->getSourceRange(); + + QualType WideExprTy; + // Get Ty of the same signedness as ExprTy, because we only want to suggest + // to widen the computation, but not change it's signedness domain. + if (Ty->isSignedIntegerType() == ExprTy->isSignedIntegerType()) + WideExprTy = Ty; + else if (Ty->isSignedIntegerType()) { + assert(ExprTy->isUnsignedIntegerType() && + "Expected source type to be signed."); + WideExprTy = Context.getCorrespondingUnsignedType(Ty); + } else { + assert(Ty->isUnsignedIntegerType() && + "Expected target type to be unsigned."); + assert(ExprTy->isSignedIntegerType() && + "Expected source type to be unsigned."); + WideExprTy = Context.getCorrespondingSignedType(Ty); + } + + Diag(E->getBeginLoc(), diag::note_warn_imp_widening_fix) + << LHS->getSourceRange() + << FixItHint::CreateInsertion(E->getBeginLoc(), + "(" + WideExprTy.getAsString() + ")"); +} + +void Sema::diagnoseImplicitWideningConversionOfMultiplicationOfPointerOffset( + Expr *E) { + // We are looking for a pointer offset operation, + // with one hand being a pointer, and another one being an offset. + Expr *PointerExpr, *IndexExpr; + if (auto *BO = dyn_cast(E)) { + PointerExpr = BO->getLHS(); + IndexExpr = BO->getRHS(); + } else if (auto *ASE = dyn_cast(E)) { + PointerExpr = ASE->getLHS(); + IndexExpr = ASE->getRHS(); + } else + return; + + if (IndexExpr->getType()->isPointerType()) + std::swap(PointerExpr, IndexExpr); + + if (!PointerExpr->getType()->isPointerType() || + IndexExpr->getType()->isPointerType()) + return; + + QualType IndexExprType = IndexExpr->getType(); + + // If the index expression's type is not known (i.e. we are in a template), + // we can't do anything here. + if (IndexExprType->isDependentType()) + return; + + QualType SSizeTy = Context.getSignedSizeType(); + QualType USizeTy = Context.getSizeType(); + QualType SizeTy = IndexExprType->isSignedIntegerType() ? SSizeTy : USizeTy; + // FIXME: is there a way to actually get the QualType for size_t/ssize_t? + StringRef TyAsString = + IndexExprType->isSignedIntegerType() ? "ssize_t" : "size_t"; + + // So, is size_t actually wider than the result of the multiplication? + if (Context.getIntWidth(IndexExprType) >= Context.getIntWidth(SizeTy)) + return; + + // Does the index expression look like it might be unintentionally computed + // in a narrower-than-wanted type? + Expr *LHS = GetLHSOfMulBinOp(IndexExpr); + if (!LHS) + return; + + // Ok, looks like we should diagnose this. + Diag(E->getBeginLoc(), diag::warn_imp_widening_of_ptr_offset) + << IndexExprType << TyAsString; + + Diag(IndexExpr->getBeginLoc(), diag::note_warn_imp_widening_silence) + << FixItHint::CreateInsertion(IndexExpr->getBeginLoc(), + (Twine("(") + TyAsString + ")(").str()) + << FixItHint::CreateInsertion(IndexExpr->getEndLoc(), ")") + << IndexExpr->getSourceRange(); + + Diag(IndexExpr->getBeginLoc(), diag::note_warn_imp_widening_fix) + << LHS->getSourceRange() + << FixItHint::CreateInsertion(IndexExpr->getBeginLoc(), + (Twine("(") + TyAsString + ")").str()); +} + /// ImpCastExprToType - If Expr is not of type 'Type', insert an implicit cast. /// If there is already an implicit cast, merge into the existing one. /// The result is of the given category. @@ -577,6 +704,7 @@ diagnoseNullableToNonnullConversion(Ty, E->getType(), E->getBeginLoc()); diagnoseZeroToNullptrConversion(Kind, E); + diagnoseImplicitWideningConversionOfMultiplication(E, Ty, Kind); QualType ExprTy = Context.getCanonicalType(E->getType()); QualType TypeTy = Context.getCanonicalType(Ty); diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -4723,8 +4723,13 @@ ExprResult Res = CreateBuiltinArraySubscriptExpr(base, lbLoc, idx, rbLoc); - if (!Res.isInvalid() && isa(Res.get())) - CheckSubscriptAccessOfNoDeref(cast(Res.get())); + if (!Res.isInvalid()) { + diagnoseImplicitWideningConversionOfMultiplicationOfPointerOffset( + Res.get()); + + if (isa(Res.get())) + CheckSubscriptAccessOfNoDeref(cast(Res.get())); + } return Res; } @@ -14327,7 +14332,16 @@ // Emit warnings for tricky precedence issues, e.g. "bitfield & 0x4 == 0" DiagnoseBinOpPrecedence(*this, Opc, TokLoc, LHSExpr, RHSExpr); - return BuildBinOp(S, TokLoc, Opc, LHSExpr, RHSExpr); + ExprResult Res = BuildBinOp(S, TokLoc, Opc, LHSExpr, RHSExpr); + + if (!Res.isInvalid()) { + if (Opc == BO_Add || Opc == BO_Sub || Opc == BO_AddAssign || + Opc == BO_SubAssign) + diagnoseImplicitWideningConversionOfMultiplicationOfPointerOffset( + Res.get()); + } + + return Res; } void Sema::LookupBinOp(Scope *S, SourceLocation OpLoc, BinaryOperatorKind Opc, diff --git a/clang/test/Misc/warning-wall.c b/clang/test/Misc/warning-wall.c --- a/clang/test/Misc/warning-wall.c +++ b/clang/test/Misc/warning-wall.c @@ -95,6 +95,9 @@ CHECK-NEXT: -Wswitch CHECK-NEXT: -Wswitch-bool CHECK-NEXT: -Wmisleading-indentation +CHECK-NEXT: -Wimplicit-widening-conversion +CHECK-NEXT: -Wimplicit-widening-of-multiplication-result +CHECK-NEXT: -Wimplicit-widening-of-pointer-offset CHECK-NOT:-W diff --git a/clang/test/Sema/implicit-widening-of-multiplication-result.c b/clang/test/Sema/implicit-widening-of-multiplication-result.c new file mode 100644 --- /dev/null +++ b/clang/test/Sema/implicit-widening-of-multiplication-result.c @@ -0,0 +1,112 @@ +// RUN: %clang_cc1 -triple x86_64-linux-gnu -fsyntax-only -Wimplicit-widening-of-multiplication-result -verify %s +// RUN: %clang_cc1 -triple x86_64-linux-gnu -fsyntax-only -Wimplicit-widening-of-multiplication-result -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck --implicit-check-not="fix-it" %s +// RUN: %clang_cc1 -triple x86_64-linux-gnu -fsyntax-only -Wimplicit-widening-of-multiplication-result -verify -x c++ %s + +// RUN: %clang_cc1 -triple i686-linux-gnu -fsyntax-only -Wimplicit-widening-of-multiplication-result -verify=silent %s +// RUN: %clang_cc1 -triple i686-linux-gnu -fsyntax-only -Wimplicit-widening-of-multiplication-result -verify=silent -x c++ %s + +// RUN: %clang_cc1 -triple x86_64-linux-gnu -fsyntax-only -Wimplicit-widening-conversion -verify %s +// RUN: %clang_cc1 -triple x86_64-linux-gnu -fsyntax-only -Wall -verify %s +// RUN: %clang_cc1 -triple x86_64-linux-gnu -fsyntax-only -verify=silent %s + +// silent-no-diagnostics + +long t0(int a, int b) { + return a * b; // #0 + // expected-warning@#0 {{performing an implicit widening conversion to type 'long' of a multiplication performed in type 'int'}} + // expected-note@#0 {{make conversion explicit to silence this warning}} + // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:10-[[@LINE-3]]:10}:"(long)(" + // CHECK: fix-it:"{{.*}}":{[[@LINE-4]]:14-[[@LINE-4]]:14}:")" + // expected-note@#0 {{perform multiplication in a wider type}} + // CHECK: fix-it:"{{.*}}":{[[@LINE-6]]:10-[[@LINE-6]]:10}:"(long)" +} +unsigned long t1(int a, int b) { + return a * b; // #1 + // expected-warning@#1 {{performing an implicit widening conversion to type 'unsigned long' of a multiplication performed in type 'int'}} + // expected-note@#1 {{make conversion explicit to silence this warning}} + // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:10-[[@LINE-3]]:10}:"(unsigned long)(" + // CHECK: fix-it:"{{.*}}":{[[@LINE-4]]:14-[[@LINE-4]]:14}:")" + // expected-note@#1 {{perform multiplication in a wider type}} + // CHECK: fix-it:"{{.*}}":{[[@LINE-6]]:10-[[@LINE-6]]:10}:"(long)" +} + +long t2(unsigned int a, int b) { + return a * b; // #2 + // expected-warning@#2 {{performing an implicit widening conversion to type 'long' of a multiplication performed in type 'unsigned int'}} + // expected-note@#2 {{make conversion explicit to silence this warning}} + // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:10-[[@LINE-3]]:10}:"(long)(" + // CHECK: fix-it:"{{.*}}":{[[@LINE-4]]:14-[[@LINE-4]]:14}:")" + // expected-note@#2 {{perform multiplication in a wider type}} + // CHECK: fix-it:"{{.*}}":{[[@LINE-6]]:10-[[@LINE-6]]:10}:"(unsigned long)" +} +unsigned long t3(unsigned int a, int b) { + return a * b; // #3 + // expected-warning@#3 {{performing an implicit widening conversion to type 'unsigned long' of a multiplication performed in type 'unsigned int'}} + // expected-note@#3 {{make conversion explicit to silence this warning}} + // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:10-[[@LINE-3]]:10}:"(unsigned long)(" + // CHECK: fix-it:"{{.*}}":{[[@LINE-4]]:14-[[@LINE-4]]:14}:")" + // expected-note@#3 {{perform multiplication in a wider type}} + // CHECK: fix-it:"{{.*}}":{[[@LINE-6]]:10-[[@LINE-6]]:10}:"(unsigned long)" +} + +long t4(int a, unsigned int b) { + return a * b; // #4 + // expected-warning@#4 {{performing an implicit widening conversion to type 'long' of a multiplication performed in type 'unsigned int'}} + // expected-note@#4 {{make conversion explicit to silence this warning}} + // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:10-[[@LINE-3]]:10}:"(long)(" + // CHECK: fix-it:"{{.*}}":{[[@LINE-4]]:14-[[@LINE-4]]:14}:")" + // expected-note@#4 {{perform multiplication in a wider type}} + // CHECK: fix-it:"{{.*}}":{[[@LINE-6]]:10-[[@LINE-6]]:10}:"(unsigned long)" +} +unsigned long t5(int a, unsigned int b) { + return a * b; // #5 + // expected-warning@#5 {{performing an implicit widening conversion to type 'unsigned long' of a multiplication performed in type 'unsigned int'}} + // expected-note@#5 {{make conversion explicit to silence this warning}} + // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:10-[[@LINE-3]]:10}:"(unsigned long)(" + // CHECK: fix-it:"{{.*}}":{[[@LINE-4]]:14-[[@LINE-4]]:14}:")" + // expected-note@#5 {{perform multiplication in a wider type}} + // CHECK: fix-it:"{{.*}}":{[[@LINE-6]]:10-[[@LINE-6]]:10}:"(unsigned long)" +} + +long t6(unsigned int a, unsigned int b) { + return a * b; // #6 + // expected-warning@#6 {{performing an implicit widening conversion to type 'long' of a multiplication performed in type 'unsigned int'}} + // expected-note@#6 {{make conversion explicit to silence this warning}} + // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:10-[[@LINE-3]]:10}:"(long)(" + // CHECK: fix-it:"{{.*}}":{[[@LINE-4]]:14-[[@LINE-4]]:14}:")" + // expected-note@#6 {{perform multiplication in a wider type}} + // CHECK: fix-it:"{{.*}}":{[[@LINE-6]]:10-[[@LINE-6]]:10}:"(unsigned long)" +} +unsigned long t7(unsigned int a, unsigned int b) { + return a * b; // #7 + // expected-warning@#7 {{performing an implicit widening conversion to type 'unsigned long' of a multiplication performed in type 'unsigned int'}} + // expected-note@#7 {{make conversion explicit to silence this warning}} + // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:10-[[@LINE-3]]:10}:"(unsigned long)(" + // CHECK: fix-it:"{{.*}}":{[[@LINE-4]]:14-[[@LINE-4]]:14}:")" + // expected-note@#7 {{perform multiplication in a wider type}} + // CHECK: fix-it:"{{.*}}":{[[@LINE-6]]:10-[[@LINE-6]]:10}:"(unsigned long)" +} + +long n8(int a, int b) { + return (a * b); +} +long n9(int a, int b) { + return (long)(a * b); +} +long n10(int a, int b) { + return (unsigned long)(a * b); +} + +long n11(long a, int b) { + return a * b; +} +long n12(int a, long b) { + return a * b; +} + +long n13(int a, int b, int c) { + return a + b * c; +} +long n14(int a, int b, int c) { + return a * b + c; +} diff --git a/clang/test/Sema/implicit-widening-of-pointer-offset-in-array-subscript-expression.c b/clang/test/Sema/implicit-widening-of-pointer-offset-in-array-subscript-expression.c new file mode 100644 --- /dev/null +++ b/clang/test/Sema/implicit-widening-of-pointer-offset-in-array-subscript-expression.c @@ -0,0 +1,79 @@ +// RUN: %clang_cc1 -triple x86_64-linux-gnu -fsyntax-only -Wimplicit-widening-of-pointer-offset -verify %s +// RUN: %clang_cc1 -triple x86_64-linux-gnu -fsyntax-only -Wimplicit-widening-of-pointer-offset -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck --implicit-check-not="fix-it" %s +// RUN: %clang_cc1 -triple x86_64-linux-gnu -fsyntax-only -Wimplicit-widening-of-pointer-offset -verify -x c++ %s + +// RUN: %clang_cc1 -triple i686-linux-gnu -fsyntax-only -Wimplicit-widening-of-pointer-offset -verify=silent %s +// RUN: %clang_cc1 -triple i686-linux-gnu -fsyntax-only -Wimplicit-widening-of-pointer-offset -verify=silent -x c++ %s + +// RUN: %clang_cc1 -triple x86_64-linux-gnu -fsyntax-only -Wimplicit-widening-conversion -verify %s +// RUN: %clang_cc1 -triple x86_64-linux-gnu -fsyntax-only -Wall -verify %s +// RUN: %clang_cc1 -triple x86_64-linux-gnu -fsyntax-only -verify=silent %s + +// silent-no-diagnostics + +char *t0(char *base, int a, int b) { + return &base[a * b]; // #0 + // expected-warning@#0 {{result of multiplication in type 'int' is used as a pointer offset after an implicit widening conversion to type 'ssize_t'}} + // expected-note@#0 {{make conversion explicit to silence this warning}} + // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:16-[[@LINE-3]]:16}:"(ssize_t)(" + // CHECK: fix-it:"{{.*}}":{[[@LINE-4]]:20-[[@LINE-4]]:20}:")" + // expected-note@#0 {{perform multiplication in a wider type}} + // CHECK: fix-it:"{{.*}}":{[[@LINE-6]]:16-[[@LINE-6]]:16}:"(ssize_t)" +} +void t1(char *base, int a, int b) { + // FIXME: test `[a * b]base` pattern? +} + +char *t2(char *base, unsigned int a, int b) { + return &base[a * b]; // #2 + // expected-warning@#2 {{result of multiplication in type 'unsigned int' is used as a pointer offset after an implicit widening conversion to type 'size_t'}} + // expected-note@#2 {{make conversion explicit to silence this warning}} + // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:16-[[@LINE-3]]:16}:"(size_t)(" + // CHECK: fix-it:"{{.*}}":{[[@LINE-4]]:20-[[@LINE-4]]:20}:")" + // expected-note@#2 {{perform multiplication in a wider type}} + // CHECK: fix-it:"{{.*}}":{[[@LINE-6]]:16-[[@LINE-6]]:16}:"(size_t)" +} + +char *t3(char *base, int a, unsigned int b) { + return &base[a * b]; // #3 + // expected-warning@#3 {{result of multiplication in type 'unsigned int' is used as a pointer offset after an implicit widening conversion to type 'size_t'}} + // expected-note@#3 {{make conversion explicit to silence this warning}} + // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:16-[[@LINE-3]]:16}:"(size_t)(" + // CHECK: fix-it:"{{.*}}":{[[@LINE-4]]:20-[[@LINE-4]]:20}:")" + // expected-note@#3 {{perform multiplication in a wider type}} + // CHECK: fix-it:"{{.*}}":{[[@LINE-6]]:16-[[@LINE-6]]:16}:"(size_t)" +} + +char *t4(char *base, unsigned int a, unsigned int b) { + return &base[a * b]; // #4 + // expected-warning@#4 {{result of multiplication in type 'unsigned int' is used as a pointer offset after an implicit widening conversion to type 'size_t'}} + // expected-note@#4 {{make conversion explicit to silence this warning}} + // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:16-[[@LINE-3]]:16}:"(size_t)(" + // CHECK: fix-it:"{{.*}}":{[[@LINE-4]]:20-[[@LINE-4]]:20}:")" + // expected-note@#4 {{perform multiplication in a wider type}} + // CHECK: fix-it:"{{.*}}":{[[@LINE-6]]:16-[[@LINE-6]]:16}:"(size_t)" +} + +char *n5(char *base, int a, int b, int c) { + return &base[a * b + c]; +} +char *n6(char *base, int a, int b, int c) { + return &base[a + b * c]; +} + +char *n11(char *base, int a, int b) { + return &base[(a * b)]; +} +char *n12(char *base, int a, int b, int c) { + return &base[(a * b + c)]; +} +char *n13(char *base, int a, int b, int c) { + return &base[(a * b) + c]; +} + +char *n14(char *base, int a, int b) { + return &base[(long)(a * b)]; +} +char *n15(char *base, int a, int b) { + return &base[(unsigned long)(a * b)]; +} diff --git a/clang/test/Sema/implicit-widening-of-pointer-offset.c b/clang/test/Sema/implicit-widening-of-pointer-offset.c new file mode 100644 --- /dev/null +++ b/clang/test/Sema/implicit-widening-of-pointer-offset.c @@ -0,0 +1,107 @@ +// RUN: %clang_cc1 -triple x86_64-linux-gnu -fsyntax-only -Wimplicit-widening-of-pointer-offset -verify %s +// RUN: %clang_cc1 -triple x86_64-linux-gnu -fsyntax-only -Wimplicit-widening-of-pointer-offset -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck --implicit-check-not="fix-it" %s +// RUN: %clang_cc1 -triple x86_64-linux-gnu -fsyntax-only -Wimplicit-widening-of-pointer-offset -verify -x c++ %s + +// RUN: %clang_cc1 -triple i686-linux-gnu -fsyntax-only -Wimplicit-widening-of-pointer-offset -verify=silent %s +// RUN: %clang_cc1 -triple i686-linux-gnu -fsyntax-only -Wimplicit-widening-of-pointer-offset -verify=silent -x c++ %s + +// RUN: %clang_cc1 -triple x86_64-linux-gnu -fsyntax-only -Wimplicit-widening-conversion -verify %s +// RUN: %clang_cc1 -triple x86_64-linux-gnu -fsyntax-only -Wall -verify %s +// RUN: %clang_cc1 -triple x86_64-linux-gnu -fsyntax-only -verify=silent %s + +// silent-no-diagnostics + +char *t0(char *base, int a, int b) { + return base + a * b; // #0 + // expected-warning@#0 {{result of multiplication in type 'int' is used as a pointer offset after an implicit widening conversion to type 'ssize_t'}} + // expected-note@#0 {{make conversion explicit to silence this warning}} + // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:17-[[@LINE-3]]:17}:"(ssize_t)(" + // CHECK: fix-it:"{{.*}}":{[[@LINE-4]]:21-[[@LINE-4]]:21}:")" + // expected-note@#0 {{perform multiplication in a wider type}} + // CHECK: fix-it:"{{.*}}":{[[@LINE-6]]:17-[[@LINE-6]]:17}:"(ssize_t)" +} +char *t1(char *base, int a, int b) { + return a * b + base; // #1 + // expected-warning@#1 {{result of multiplication in type 'int' is used as a pointer offset after an implicit widening conversion to type 'ssize_t'}} + // expected-note@#1 {{make conversion explicit to silence this warning}} + // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:10-[[@LINE-3]]:10}:"(ssize_t)(" + // CHECK: fix-it:"{{.*}}":{[[@LINE-4]]:14-[[@LINE-4]]:14}:")" + // expected-note@#1 {{perform multiplication in a wider type}} + // CHECK: fix-it:"{{.*}}":{[[@LINE-6]]:10-[[@LINE-6]]:10}:"(ssize_t)" +} + +char *t2(char *base, unsigned int a, int b) { + return base + a * b; // #2 + // expected-warning@#2 {{result of multiplication in type 'unsigned int' is used as a pointer offset after an implicit widening conversion to type 'size_t'}} + // expected-note@#2 {{make conversion explicit to silence this warning}} + // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:17-[[@LINE-3]]:17}:"(size_t)(" + // CHECK: fix-it:"{{.*}}":{[[@LINE-4]]:21-[[@LINE-4]]:21}:")" + // expected-note@#2 {{perform multiplication in a wider type}} + // CHECK: fix-it:"{{.*}}":{[[@LINE-6]]:17-[[@LINE-6]]:17}:"(size_t)" +} + +char *t3(char *base, int a, unsigned int b) { + return base + a * b; // #3 + // expected-warning@#3 {{result of multiplication in type 'unsigned int' is used as a pointer offset after an implicit widening conversion to type 'size_t'}} + // expected-note@#3 {{make conversion explicit to silence this warning}} + // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:17-[[@LINE-3]]:17}:"(size_t)(" + // CHECK: fix-it:"{{.*}}":{[[@LINE-4]]:21-[[@LINE-4]]:21}:")" + // expected-note@#3 {{perform multiplication in a wider type}} + // CHECK: fix-it:"{{.*}}":{[[@LINE-6]]:17-[[@LINE-6]]:17}:"(size_t)" +} + +char *t4(char *base, unsigned int a, unsigned int b) { + return base + a * b; // #4 + // expected-warning@#4 {{result of multiplication in type 'unsigned int' is used as a pointer offset after an implicit widening conversion to type 'size_t'}} + // expected-note@#4 {{make conversion explicit to silence this warning}} + // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:17-[[@LINE-3]]:17}:"(size_t)(" + // CHECK: fix-it:"{{.*}}":{[[@LINE-4]]:21-[[@LINE-4]]:21}:")" + // expected-note@#4 {{perform multiplication in a wider type}} + // CHECK: fix-it:"{{.*}}":{[[@LINE-6]]:17-[[@LINE-6]]:17}:"(size_t)" +} + +char *t5(char *base, int a, int b, int c) { + return base + a * b + c; // #5 + // expected-warning@#5 {{result of multiplication in type 'int' is used as a pointer offset after an implicit widening conversion to type 'ssize_t'}} + // expected-note@#5 {{make conversion explicit to silence this warning}} + // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:17-[[@LINE-3]]:17}:"(ssize_t)(" + // CHECK: fix-it:"{{.*}}":{[[@LINE-4]]:21-[[@LINE-4]]:21}:")" + // expected-note@#5 {{perform multiplication in a wider type}} + // CHECK: fix-it:"{{.*}}":{[[@LINE-6]]:17-[[@LINE-6]]:17}:"(ssize_t)" +} +char *t6(char *base, int a, int b, int c) { + return base + a + b * c; // #6 + // expected-warning@#6 {{result of multiplication in type 'int' is used as a pointer offset after an implicit widening conversion to type 'ssize_t'}} + // expected-note@#6 {{make conversion explicit to silence this warning}} + // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:21-[[@LINE-3]]:21}:"(ssize_t)(" + // CHECK: fix-it:"{{.*}}":{[[@LINE-4]]:25-[[@LINE-4]]:25}:")" + // expected-note@#6 {{perform multiplication in a wider type}} + // CHECK: fix-it:"{{.*}}":{[[@LINE-6]]:21-[[@LINE-6]]:21}:"(ssize_t)" +} + +char *n11(char *base, int a, int b) { + return base + (a * b); +} +char *n12(char *base, int a, int b, int c) { + return base + (a * b + c); +} +char *n13(char *base, int a, int b, int c) { + return base + (a * b) + c; +} + +char *n14(char *base, int a, int b) { + return base + (long)(a * b); +} +char *n15(char *base, int a, int b) { + return base + (unsigned long)(a * b); +} + +#ifdef __cplusplus +template +char *template_test(char *base, T a, T b) { + return base + a * b; +} +char *template_test_instantiation(char *base, int a, int b) { + return template_test(base, a, b); +} +#endif