diff --git a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp --- a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp +++ b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp @@ -22,6 +22,7 @@ #include "FoldInitTypeCheck.h" #include "ForwardDeclarationNamespaceCheck.h" #include "ForwardingReferenceOverloadCheck.h" +#include "ImplicitWideningOfMultiplicationResultCheck.h" #include "InaccurateEraseCheck.h" #include "IncorrectRoundingsCheck.h" #include "InfiniteLoopCheck.h" @@ -97,6 +98,8 @@ "bugprone-forward-declaration-namespace"); CheckFactories.registerCheck( "bugprone-forwarding-reference-overload"); + CheckFactories.registerCheck( + "bugprone-implicit-widening-of-multiplication-result"); CheckFactories.registerCheck( "bugprone-inaccurate-erase"); CheckFactories.registerCheck( diff --git a/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt b/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt --- a/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt @@ -17,6 +17,7 @@ FoldInitTypeCheck.cpp ForwardDeclarationNamespaceCheck.cpp ForwardingReferenceOverloadCheck.cpp + ImplicitWideningOfMultiplicationResultCheck.cpp InaccurateEraseCheck.cpp IncorrectRoundingsCheck.cpp InfiniteLoopCheck.cpp diff --git a/clang-tools-extra/clang-tidy/bugprone/ImplicitWideningOfMultiplicationResultCheck.h b/clang-tools-extra/clang-tidy/bugprone/ImplicitWideningOfMultiplicationResultCheck.h new file mode 100644 --- /dev/null +++ b/clang-tools-extra/clang-tidy/bugprone/ImplicitWideningOfMultiplicationResultCheck.h @@ -0,0 +1,38 @@ +//===--- ImplicitWideningOfMultiplicationResultCheck.h ----------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_IMPLICITWIDENINGOFMULTIPLICATIONRESULTCHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_IMPLICITWIDENINGOFMULTIPLICATIONRESULTCHECK_H + +#include "../ClangTidyCheck.h" + +namespace clang { +namespace tidy { +namespace bugprone { + +/// Diagnoses instances of an implicit widening of multiplication result. +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/bugprone-implicit-widening-of-multiplication-result.html +class ImplicitWideningOfMultiplicationResultCheck : public ClangTidyCheck { + void handleImplicitCastExpr(const ImplicitCastExpr *ICE, ASTContext *Context); + void handlePointerOffsetting(const Expr *E, ASTContext *Context); + +public: + ImplicitWideningOfMultiplicationResultCheck(StringRef Name, + ClangTidyContext *Context) + : ClangTidyCheck(Name, Context) {} + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; +}; + +} // namespace bugprone +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_IMPLICITWIDENINGOFMULTIPLICATIONRESULTCHECK_H diff --git a/clang-tools-extra/clang-tidy/bugprone/ImplicitWideningOfMultiplicationResultCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/ImplicitWideningOfMultiplicationResultCheck.cpp new file mode 100644 --- /dev/null +++ b/clang-tools-extra/clang-tidy/bugprone/ImplicitWideningOfMultiplicationResultCheck.cpp @@ -0,0 +1,192 @@ +//===--- ImplicitWideningOfMultiplicationResultCheck.cpp - clang-tidy -----===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "ImplicitWideningOfMultiplicationResultCheck.h" +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" + +using namespace clang::ast_matchers; + +namespace clang { +namespace { +AST_MATCHER(ImplicitCastExpr, isPartOfExplicitCast) { + return Node.isPartOfExplicitCast(); +} +} // namespace +} // namespace clang + +namespace clang { +namespace tidy { +namespace bugprone { + +static const Expr *getLHSOfMulBinOp(const Expr *E) { + assert(E == E->IgnoreParens() && "Already skipped all parens!"); + // Is this: long r = int(x) * int(y); ? + // FIXME: shall we skip brackets/casts/etc? + const 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()->IgnoreParens(); +} + +void ImplicitWideningOfMultiplicationResultCheck::handleImplicitCastExpr( + const ImplicitCastExpr *ICE, ASTContext *Context) { + const Expr *E = ICE->getSubExpr()->IgnoreParens(); + QualType Ty = ICE->getType(); + QualType ETy = E->getType(); + + assert(!ETy->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(ETy); + 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? + const Expr *LHS = getLHSOfMulBinOp(E); + if (!LHS) + return; + + // Ok, looks like we should diagnose this. + diag(E->getBeginLoc(), "performing an implicit widening conversion to type " + "%0 of a multiplication performed in type %1") + << Ty << E->getType(); + + diag(E->getBeginLoc(), "make conversion explicit to silence this warning", + DiagnosticIDs::Note) + << 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() == ETy->isSignedIntegerType()) + WideExprTy = Ty; + else if (Ty->isSignedIntegerType()) { + assert(ETy->isUnsignedIntegerType() && + "Expected source type to be signed."); + WideExprTy = Context->getCorrespondingUnsignedType(Ty); + } else { + assert(Ty->isUnsignedIntegerType() && + "Expected target type to be unsigned."); + assert(ETy->isSignedIntegerType() && + "Expected source type to be unsigned."); + WideExprTy = Context->getCorrespondingSignedType(Ty); + } + + diag(E->getBeginLoc(), "perform multiplication in a wider type", + DiagnosticIDs::Note) + << LHS->getSourceRange() + << FixItHint::CreateInsertion(E->getBeginLoc(), + "(" + WideExprTy.getAsString() + ")"); +} + +void ImplicitWideningOfMultiplicationResultCheck::handlePointerOffsetting( + const Expr *E, ASTContext *Context) { + // We are looking for a pointer offset operation, + // with one hand being a pointer, and another one being an offset. + const Expr *PointerExpr, *IndexExpr; + if (const auto *BO = dyn_cast(E)) { + PointerExpr = BO->getLHS(); + IndexExpr = BO->getRHS(); + } else if (const 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; + + IndexExpr = IndexExpr->IgnoreParens(); + + 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? + // Note that SizeTy.getAsString() will be long/unsigned long/..., NOT size_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? + const Expr *LHS = getLHSOfMulBinOp(IndexExpr); + if (!LHS) + return; + + // Ok, looks like we should diagnose this. + diag(E->getBeginLoc(), + "result of multiplication in type %0 is used as a pointer offset after " + "an implicit widening conversion to type '%1'") + << IndexExprType << TyAsString; + + diag(IndexExpr->getBeginLoc(), + "make conversion explicit to silence this warning", DiagnosticIDs::Note) + << FixItHint::CreateInsertion(IndexExpr->getBeginLoc(), + (Twine("(") + TyAsString + ")(").str()) + << FixItHint::CreateInsertion(IndexExpr->getEndLoc(), ")") + << IndexExpr->getSourceRange(); + + diag(IndexExpr->getBeginLoc(), "perform multiplication in a wider type", + DiagnosticIDs::Note) + << LHS->getSourceRange() + << FixItHint::CreateInsertion(IndexExpr->getBeginLoc(), + (Twine("(") + TyAsString + ")").str()); +} + +void ImplicitWideningOfMultiplicationResultCheck::registerMatchers( + MatchFinder *Finder) { + Finder->addMatcher(implicitCastExpr(unless(anyOf(isInTemplateInstantiation(), + isPartOfExplicitCast())), + hasCastKind(CK_IntegralCast)) + .bind("x"), + this); + Finder->addMatcher( + arraySubscriptExpr(unless(isInTemplateInstantiation())).bind("x"), this); + Finder->addMatcher(binaryOperator(unless(isInTemplateInstantiation()), + hasType(isAnyPointer()), + hasAnyOperatorName("+", "-", "+=", "-=")) + .bind("x"), + this); +} + +void ImplicitWideningOfMultiplicationResultCheck::check( + const MatchFinder::MatchResult &Result) { + if (const auto *MatchedDecl = Result.Nodes.getNodeAs("x")) + handleImplicitCastExpr(MatchedDecl, Result.Context); + else if (const auto *MatchedDecl = + Result.Nodes.getNodeAs("x")) + handlePointerOffsetting(MatchedDecl, Result.Context); + else if (const auto *MatchedDecl = + Result.Nodes.getNodeAs("x")) + handlePointerOffsetting(MatchedDecl, Result.Context); +} + +} // namespace bugprone +} // namespace tidy +} // namespace clang diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -77,6 +77,11 @@ New checks ^^^^^^^^^^ +- New :doc:`bugprone-implicit-widening-of-multiplication-result + ` check. + + Diagnoses instances of an implicit widening of multiplication result. + - New :doc:`concurrency-thread-canceltype-asynchronous ` check. diff --git a/clang-tools-extra/docs/clang-tidy/checks/bugprone-implicit-widening-of-multiplication-result.rst b/clang-tools-extra/docs/clang-tidy/checks/bugprone-implicit-widening-of-multiplication-result.rst new file mode 100644 --- /dev/null +++ b/clang-tools-extra/docs/clang-tidy/checks/bugprone-implicit-widening-of-multiplication-result.rst @@ -0,0 +1,25 @@ +.. title:: clang-tidy - bugprone-implicit-widening-of-multiplication-result + +bugprone-implicit-widening-of-multiplication-result +=================================================== + +The check diagnoses instances where a result of a multiplication is implicitly +widened, and suggests (with fix-it) to either silence the code by making +widening explicit, or to perform the multiplication in a wider type, +to avoid the widening afterwards. + +Examples: + +.. code-block:: c++ + + long mul(int a, int b) { + return a * b; // warning: performing an implicit widening conversion to type 'long' of a multiplication performed in type 'int' + } + + char* ptr_add(char *base, int a, int b) { + return base + a * b; // 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]; // warning: result of multiplication in type 'int' is used as a pointer offset after an implicit widening conversion to type 'ssize_t' + } diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst --- a/clang-tools-extra/docs/clang-tidy/checks/list.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst @@ -62,6 +62,7 @@ `bugprone-fold-init-type `_, `bugprone-forward-declaration-namespace `_, `bugprone-forwarding-reference-overload `_, + `bugprone-implicit-widening-of-multiplication-result `_, "Yes" `bugprone-inaccurate-erase `_, "Yes" `bugprone-incorrect-roundings `_, `bugprone-infinite-loop `_, diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone-implicit-widening-of-multiplication-result-array-subscript-expression.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone-implicit-widening-of-multiplication-result-array-subscript-expression.cpp new file mode 100644 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone-implicit-widening-of-multiplication-result-array-subscript-expression.cpp @@ -0,0 +1,73 @@ +// RUN: %check_clang_tidy -std=c99 %s bugprone-implicit-widening-of-multiplication-result %t -- -- -x c +// RUN: %check_clang_tidy %s bugprone-implicit-widening-of-multiplication-result %t -- -- -x c++ + +char *t0(char *base, int a, int b) { + return &base[a * b]; + // CHECK-NOTES: :[[@LINE-1]]:11: warning: result of multiplication in type 'int' is used as a pointer offset after an implicit widening conversion to type 'ssize_t' + // CHECK-NOTES: :[[@LINE-2]]:16: note: make conversion explicit to silence this warning + // CHECK-NOTES: :[[@LINE-3]]:16: note: perform multiplication in a wider type +} +void *t1(char *base, int a, int b) { + return &((a * b)[base]); + // CHECK-NOTES: :[[@LINE-1]]:12: warning: result of multiplication in type 'int' is used as a pointer offset after an implicit widening conversion to type 'ssize_t' + // CHECK-NOTES: :[[@LINE-2]]:13: note: make conversion explicit to silence this warning + // CHECK-NOTES: :[[@LINE-3]]:13: note: perform multiplication in a wider type +} + +char *t2(char *base, unsigned int a, int b) { + return &base[a * b]; + // CHECK-NOTES: :[[@LINE-1]]:11: warning: result of multiplication in type 'unsigned int' is used as a pointer offset after an implicit widening conversion to type 'size_t' + // CHECK-NOTES: :[[@LINE-2]]:16: note: make conversion explicit to silence this warning + // CHECK-NOTES: :[[@LINE-3]]:16: note: perform multiplication in a wider type +} + +char *t3(char *base, int a, unsigned int b) { + return &base[a * b]; + // CHECK-NOTES: :[[@LINE-1]]:11: warning: result of multiplication in type 'unsigned int' is used as a pointer offset after an implicit widening conversion to type 'size_t' + // CHECK-NOTES: :[[@LINE-2]]:16: note: make conversion explicit to silence this warning + // CHECK-NOTES: :[[@LINE-3]]:16: note: perform multiplication in a wider type +} + +char *t4(char *base, unsigned int a, unsigned int b) { + return &base[a * b]; + // CHECK-NOTES: :[[@LINE-1]]:11: warning: result of multiplication in type 'unsigned int' is used as a pointer offset after an implicit widening conversion to type 'size_t' + // CHECK-NOTES: :[[@LINE-2]]:16: note: make conversion explicit to silence this warning + // CHECK-NOTES: :[[@LINE-3]]:16: note: perform multiplication in a wider type +} + +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 *t7(char *base, int a, int b) { + return &base[(a * b)]; + // CHECK-NOTES: :[[@LINE-1]]:11: warning: result of multiplication in type 'int' is used as a pointer offset after an implicit widening conversion to type 'ssize_t' + // CHECK-NOTES: :[[@LINE-2]]:17: note: make conversion explicit to silence this warning + // CHECK-NOTES: :[[@LINE-3]]:17: note: perform multiplication in a wider type +} +char *n8(char *base, int a, int b, int c) { + return &base[(a * b + c)]; +} +char *n9(char *base, int a, int b, int c) { + return &base[(a * b) + c]; +} + +char *n10(char *base, int a, int b) { + return &base[(long)(a * b)]; +} +char *n11(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 diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone-implicit-widening-of-multiplication-result-char.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone-implicit-widening-of-multiplication-result-char.cpp new file mode 100644 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone-implicit-widening-of-multiplication-result-char.cpp @@ -0,0 +1,99 @@ +// RUN: %check_clang_tidy -std=c99 %s bugprone-implicit-widening-of-multiplication-result %t -- -- -x c +// RUN: %check_clang_tidy %s bugprone-implicit-widening-of-multiplication-result %t -- -- -x c++ + +// RUN: %check_clang_tidy -std=c99 %s bugprone-implicit-widening-of-multiplication-result %t -- -- -x c -fsigned-char +// RUN: %check_clang_tidy %s bugprone-implicit-widening-of-multiplication-result %t -- -- -x c++ -fsigned-char + +// RUN: %check_clang_tidy -std=c99 %s bugprone-implicit-widening-of-multiplication-result %t -- -- -x c -funsigned-char +// RUN: %check_clang_tidy %s bugprone-implicit-widening-of-multiplication-result %t -- -- -x c++ -funsigned-char + +long t0(char a, char b) { + return a * b; + // CHECK-NOTES: :[[@LINE-1]]:10: warning: performing an implicit widening conversion to type 'long' of a multiplication performed in type 'int' + // CHECK-NOTES: :[[@LINE-2]]:10: note: make conversion explicit to silence this warning + // CHECK-NOTES: :[[@LINE-3]]:10: note: perform multiplication in a wider type +} +unsigned long t1(char a, char b) { + return a * b; + // CHECK-NOTES: :[[@LINE-1]]:10: warning: performing an implicit widening conversion to type 'unsigned long' of a multiplication performed in type 'int' + // CHECK-NOTES: :[[@LINE-2]]:10: note: make conversion explicit to silence this warning + // CHECK-NOTES: :[[@LINE-3]]:10: note: perform multiplication in a wider type +} + +long t2(unsigned char a, char b) { + return a * b; + // CHECK-NOTES: :[[@LINE-1]]:10: warning: performing an implicit widening conversion to type 'long' of a multiplication performed in type 'int' + // CHECK-NOTES: :[[@LINE-2]]:10: note: make conversion explicit to silence this warning + // CHECK-NOTES: :[[@LINE-3]]:10: note: perform multiplication in a wider type +} +unsigned long t3(unsigned char a, char b) { + return a * b; + // CHECK-NOTES: :[[@LINE-1]]:10: warning: performing an implicit widening conversion to type 'unsigned long' of a multiplication performed in type 'int' + // CHECK-NOTES: :[[@LINE-2]]:10: note: make conversion explicit to silence this warning + // CHECK-NOTES: :[[@LINE-3]]:10: note: perform multiplication in a wider type +} + +long t4(char a, unsigned char b) { + return a * b; + // CHECK-NOTES: :[[@LINE-1]]:10: warning: performing an implicit widening conversion to type 'long' of a multiplication performed in type 'int' + // CHECK-NOTES: :[[@LINE-2]]:10: note: make conversion explicit to silence this warning + // CHECK-NOTES: :[[@LINE-3]]:10: note: perform multiplication in a wider type +} +unsigned long t5(char a, unsigned char b) { + return a * b; + // CHECK-NOTES: :[[@LINE-1]]:10: warning: performing an implicit widening conversion to type 'unsigned long' of a multiplication performed in type 'int' + // CHECK-NOTES: :[[@LINE-2]]:10: note: make conversion explicit to silence this warning + // CHECK-NOTES: :[[@LINE-3]]:10: note: perform multiplication in a wider type +} + +long t6(unsigned char a, unsigned char b) { + return a * b; + // CHECK-NOTES: :[[@LINE-1]]:10: warning: performing an implicit widening conversion to type 'long' of a multiplication performed in type 'int' + // CHECK-NOTES: :[[@LINE-2]]:10: note: make conversion explicit to silence this warning + // CHECK-NOTES: :[[@LINE-3]]:10: note: perform multiplication in a wider type +} +unsigned long t7(unsigned char a, unsigned char b) { + return a * b; + // CHECK-NOTES: :[[@LINE-1]]:10: warning: performing an implicit widening conversion to type 'unsigned long' of a multiplication performed in type 'int' + // CHECK-NOTES: :[[@LINE-2]]:10: note: make conversion explicit to silence this warning + // CHECK-NOTES: :[[@LINE-3]]:10: note: perform multiplication in a wider type +} + +long t8(signed char a, char b) { + return a * b; + // CHECK-NOTES: :[[@LINE-1]]:10: warning: performing an implicit widening conversion to type 'long' of a multiplication performed in type 'int' + // CHECK-NOTES: :[[@LINE-2]]:10: note: make conversion explicit to silence this warning + // CHECK-NOTES: :[[@LINE-3]]:10: note: perform multiplication in a wider type +} +unsigned long t9(signed char a, char b) { + return a * b; + // CHECK-NOTES: :[[@LINE-1]]:10: warning: performing an implicit widening conversion to type 'unsigned long' of a multiplication performed in type 'int' + // CHECK-NOTES: :[[@LINE-2]]:10: note: make conversion explicit to silence this warning + // CHECK-NOTES: :[[@LINE-3]]:10: note: perform multiplication in a wider type +} + +long t10(char a, signed char b) { + return a * b; + // CHECK-NOTES: :[[@LINE-1]]:10: warning: performing an implicit widening conversion to type 'long' of a multiplication performed in type 'int' + // CHECK-NOTES: :[[@LINE-2]]:10: note: make conversion explicit to silence this warning + // CHECK-NOTES: :[[@LINE-3]]:10: note: perform multiplication in a wider type +} +unsigned long t11(char a, signed char b) { + return a * b; + // CHECK-NOTES: :[[@LINE-1]]:10: warning: performing an implicit widening conversion to type 'unsigned long' of a multiplication performed in type 'int' + // CHECK-NOTES: :[[@LINE-2]]:10: note: make conversion explicit to silence this warning + // CHECK-NOTES: :[[@LINE-3]]:10: note: perform multiplication in a wider type +} + +long t12(signed char a, signed char b) { + return a * b; + // CHECK-NOTES: :[[@LINE-1]]:10: warning: performing an implicit widening conversion to type 'long' of a multiplication performed in type 'int' + // CHECK-NOTES: :[[@LINE-2]]:10: note: make conversion explicit to silence this warning + // CHECK-NOTES: :[[@LINE-3]]:10: note: perform multiplication in a wider type +} +unsigned long t13(signed char a, signed char b) { + return a * b; + // CHECK-NOTES: :[[@LINE-1]]:10: warning: performing an implicit widening conversion to type 'unsigned long' of a multiplication performed in type 'int' + // CHECK-NOTES: :[[@LINE-2]]:10: note: make conversion explicit to silence this warning + // CHECK-NOTES: :[[@LINE-3]]:10: note: perform multiplication in a wider type +} diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone-implicit-widening-of-multiplication-result-int.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone-implicit-widening-of-multiplication-result-int.cpp new file mode 100644 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone-implicit-widening-of-multiplication-result-int.cpp @@ -0,0 +1,97 @@ +// RUN: %check_clang_tidy -std=c99 %s bugprone-implicit-widening-of-multiplication-result %t -- -- -x c +// RUN: %check_clang_tidy %s bugprone-implicit-widening-of-multiplication-result %t -- -- -x c++ + +long t0(int a, int b) { + return a * b; + // CHECK-NOTES: :[[@LINE-1]]:10: warning: performing an implicit widening conversion to type 'long' of a multiplication performed in type 'int' + // CHECK-NOTES: :[[@LINE-2]]:10: note: make conversion explicit to silence this warning + // CHECK-NOTES: :[[@LINE-3]]:10: note: perform multiplication in a wider type +} +unsigned long t1(int a, int b) { + return a * b; + // CHECK-NOTES: :[[@LINE-1]]:10: warning: performing an implicit widening conversion to type 'unsigned long' of a multiplication performed in type 'int' + // CHECK-NOTES: :[[@LINE-2]]:10: note: make conversion explicit to silence this warning + // CHECK-NOTES: :[[@LINE-3]]:10: note: perform multiplication in a wider type +} + +long t2(unsigned int a, int b) { + return a * b; + // CHECK-NOTES: :[[@LINE-1]]:10: warning: performing an implicit widening conversion to type 'long' of a multiplication performed in type 'unsigned int' + // CHECK-NOTES: :[[@LINE-2]]:10: note: make conversion explicit to silence this warning + // CHECK-NOTES: :[[@LINE-3]]:10: note: perform multiplication in a wider type +} +unsigned long t3(unsigned int a, int b) { + return a * b; + // CHECK-NOTES: :[[@LINE-1]]:10: warning: performing an implicit widening conversion to type 'unsigned long' of a multiplication performed in type 'unsigned int' + // CHECK-NOTES: :[[@LINE-2]]:10: note: make conversion explicit to silence this warning + // CHECK-NOTES: :[[@LINE-3]]:10: note: perform multiplication in a wider type +} + +long t4(int a, unsigned int b) { + return a * b; + // CHECK-NOTES: :[[@LINE-1]]:10: warning: performing an implicit widening conversion to type 'long' of a multiplication performed in type 'unsigned int' + // CHECK-NOTES: :[[@LINE-2]]:10: note: make conversion explicit to silence this warning + // CHECK-NOTES: :[[@LINE-3]]:10: note: perform multiplication in a wider type +} +unsigned long t5(int a, unsigned int b) { + return a * b; + // CHECK-NOTES: :[[@LINE-1]]:10: warning: performing an implicit widening conversion to type 'unsigned long' of a multiplication performed in type 'unsigned int' + // CHECK-NOTES: :[[@LINE-2]]:10: note: make conversion explicit to silence this warning + // CHECK-NOTES: :[[@LINE-3]]:10: note: perform multiplication in a wider type +} + +long t6(unsigned int a, unsigned int b) { + return a * b; + // CHECK-NOTES: :[[@LINE-1]]:10: warning: performing an implicit widening conversion to type 'long' of a multiplication performed in type 'unsigned int' + // CHECK-NOTES: :[[@LINE-2]]:10: note: make conversion explicit to silence this warning + // CHECK-NOTES: :[[@LINE-3]]:10: note: perform multiplication in a wider type +} +unsigned long t7(unsigned int a, unsigned int b) { + return a * b; + // CHECK-NOTES: :[[@LINE-1]]:10: warning: performing an implicit widening conversion to type 'unsigned long' of a multiplication performed in type 'unsigned int' + // CHECK-NOTES: :[[@LINE-2]]:10: note: make conversion explicit to silence this warning + // CHECK-NOTES: :[[@LINE-3]]:10: note: perform multiplication in a wider type +} + +long t8(int a, int b) { + return (a * b); + // CHECK-NOTES: :[[@LINE-1]]:11: warning: performing an implicit widening conversion to type 'long' of a multiplication performed in type 'int' + // CHECK-NOTES: :[[@LINE-2]]:11: note: make conversion explicit to silence this warning + // CHECK-NOTES: :[[@LINE-3]]:11: note: perform multiplication in a wider type +} +long t9(int a, int b) { + return (a)*b; + // CHECK-NOTES: :[[@LINE-1]]:10: warning: performing an implicit widening conversion to type 'long' of a multiplication performed in type 'int' + // CHECK-NOTES: :[[@LINE-2]]:10: note: make conversion explicit to silence this warning + // CHECK-NOTES: :[[@LINE-3]]:10: note: perform multiplication in a wider type +} +long n10(int a, int b) { + return (long)(a * b); +} +long n11(int a, int b) { + return (unsigned long)(a * b); +} + +long n12(long a, int b) { + return a * b; +} +long n13(int a, long b) { + return a * b; +} + +long n14(int a, int b, int c) { + return a + b * c; +} +long n15(int a, int b, int c) { + return a * b + c; +} + +#ifdef __cplusplus +template +T2 template_test(T1 a, T1 b) { + return a * b; +} +long template_test_instantiation(int a, int b) { + return template_test(a, b); +} +#endif diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone-implicit-widening-of-multiplication-result-pointer-offset.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone-implicit-widening-of-multiplication-result-pointer-offset.cpp new file mode 100644 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone-implicit-widening-of-multiplication-result-pointer-offset.cpp @@ -0,0 +1,82 @@ +// RUN: %check_clang_tidy -std=c99 %s bugprone-implicit-widening-of-multiplication-result %t -- -- -x c +// RUN: %check_clang_tidy %s bugprone-implicit-widening-of-multiplication-result %t -- -- -x c++ + +char *t0(char *base, int a, int b) { + return base + a * b; + // CHECK-NOTES: :[[@LINE-1]]:10: warning: result of multiplication in type 'int' is used as a pointer offset after an implicit widening conversion to type 'ssize_t' + // CHECK-NOTES: :[[@LINE-2]]:17: note: make conversion explicit to silence this warning + // CHECK-NOTES: :[[@LINE-3]]:17: note: perform multiplication in a wider type +} +char *t1(char *base, int a, int b) { + return a * b + base; + // CHECK-NOTES: :[[@LINE-1]]:10: warning: result of multiplication in type 'int' is used as a pointer offset after an implicit widening conversion to type 'ssize_t' + // CHECK-NOTES: :[[@LINE-2]]:10: note: make conversion explicit to silence this warning + // CHECK-NOTES: :[[@LINE-3]]:10: note: perform multiplication in a wider type +} + +char *t2(char *base, unsigned int a, int b) { + return base + a * b; + // CHECK-NOTES: :[[@LINE-1]]:10: warning: result of multiplication in type 'unsigned int' is used as a pointer offset after an implicit widening conversion to type 'size_t' + // CHECK-NOTES: :[[@LINE-2]]:17: note: make conversion explicit to silence this warning + // CHECK-NOTES: :[[@LINE-3]]:17: note: perform multiplication in a wider type +} + +char *t3(char *base, int a, unsigned int b) { + return base + a * b; + // CHECK-NOTES: :[[@LINE-1]]:10: warning: result of multiplication in type 'unsigned int' is used as a pointer offset after an implicit widening conversion to type 'size_t' + // CHECK-NOTES: :[[@LINE-2]]:17: note: make conversion explicit to silence this warning + // CHECK-NOTES: :[[@LINE-3]]:17: note: perform multiplication in a wider type +} + +char *t4(char *base, unsigned int a, unsigned int b) { + return base + a * b; + // CHECK-NOTES: :[[@LINE-1]]:10: warning: result of multiplication in type 'unsigned int' is used as a pointer offset after an implicit widening conversion to type 'size_t' + // CHECK-NOTES: :[[@LINE-2]]:17: note: make conversion explicit to silence this warning + // CHECK-NOTES: :[[@LINE-3]]:17: note: perform multiplication in a wider type +} + +char *t5(char *base, int a, int b, int c) { + return base + a * b + c; + // CHECK-NOTES: :[[@LINE-1]]:10: warning: result of multiplication in type 'int' is used as a pointer offset after an implicit widening conversion to type 'ssize_t' + // CHECK-NOTES: :[[@LINE-2]]:17: note: make conversion explicit to silence this warning + // CHECK-NOTES: :[[@LINE-3]]:17: note: perform multiplication in a wider type +} +char *t6(char *base, int a, int b, int c) { + return base + a + b * c; + // CHECK-NOTES: :[[@LINE-1]]:10: warning: result of multiplication in type 'int' is used as a pointer offset after an implicit widening conversion to type 'ssize_t' + // CHECK-NOTES: :[[@LINE-2]]:21: note: make conversion explicit to silence this warning + // CHECK-NOTES: :[[@LINE-3]]:21: note: perform multiplication in a wider type +} + +char *n7(char *base, int a, int b) { + return base + (a * b); + // CHECK-NOTES: :[[@LINE-1]]:10: warning: result of multiplication in type 'int' is used as a pointer offset after an implicit widening conversion to type 'ssize_t' + // CHECK-NOTES: :[[@LINE-2]]:18: note: make conversion explicit to silence this warning + // CHECK-NOTES: :[[@LINE-3]]:18: note: perform multiplication in a wider type +} +char *n8(char *base, int a, int b, int c) { + return base + (a * b) + c; + // CHECK-NOTES: :[[@LINE-1]]:10: warning: result of multiplication in type 'int' is used as a pointer offset after an implicit widening conversion to type 'ssize_t' + // CHECK-NOTES: :[[@LINE-2]]:18: note: make conversion explicit to silence this warning + // CHECK-NOTES: :[[@LINE-3]]:18: note: perform multiplication in a wider type +} +char *n9(char *base, int a, int b, int c) { + return base + (a * b + c); +} + +char *n10(char *base, int a, int b) { + return base + (long)(a * b); +} +char *n11(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 diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone-implicit-widening-of-multiplication-result-short.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone-implicit-widening-of-multiplication-result-short.cpp new file mode 100644 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone-implicit-widening-of-multiplication-result-short.cpp @@ -0,0 +1,15 @@ +// RUN: %check_clang_tidy -std=c99 %s bugprone-implicit-widening-of-multiplication-result %t -- -- -x c +// RUN: %check_clang_tidy %s bugprone-implicit-widening-of-multiplication-result %t -- -- -x c++ + +long t0(short a, int b) { + return a * b; + // CHECK-NOTES: :[[@LINE-1]]:10: warning: performing an implicit widening conversion to type 'long' of a multiplication performed in type 'int' + // CHECK-NOTES: :[[@LINE-2]]:10: note: make conversion explicit to silence this warning + // CHECK-NOTES: :[[@LINE-3]]:10: note: perform multiplication in a wider type +} +long t1(short a, short b) { + return a * b; + // CHECK-NOTES: :[[@LINE-1]]:10: warning: performing an implicit widening conversion to type 'long' of a multiplication performed in type 'int' + // CHECK-NOTES: :[[@LINE-2]]:10: note: make conversion explicit to silence this warning + // CHECK-NOTES: :[[@LINE-3]]:10: note: perform multiplication in a wider type +} 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 @@ -2748,6 +2748,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/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -10097,7 +10097,8 @@ return getVectorType(getCorrespondingUnsignedType(VTy->getElementType()), VTy->getNumElements(), VTy->getVectorKind()); - // For enums, we return the unsigned version of the base type. + // For enums, get the underlying integer type of the enum, and let the general + // integer type signchanging code handle it. if (const auto *ETy = T->getAs()) T = ETy->getDecl()->getIntegerType(); @@ -10150,6 +10151,70 @@ } } +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, get the underlying integer type of the enum, and let the general + // integer type signchanging code handle it. + 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,