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,52 @@ +//===--- 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" +#include "../utils/IncludeInserter.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 { + const ast_matchers::MatchFinder::MatchResult *Result; + bool ShouldUseCXXStaticCast; + bool ShouldUseCXXHeader; + + llvm::Optional includeStddefHeader(SourceLocation File); + + void handleImplicitCastExpr(const ImplicitCastExpr *ICE); + void handlePointerOffsetting(const Expr *E); + +public: + ImplicitWideningOfMultiplicationResultCheck(StringRef Name, + ClangTidyContext *Context); + void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP, + Preprocessor *ModuleExpanderPP) override; + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; + void storeOptions(ClangTidyOptions::OptionMap &Opts) override; + +private: + const bool UseCXXStaticCastsInCppSources; + const bool UseCXXHeadersInCppSources; + utils::IncludeInserter IncludeInserter; +}; + +} // 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,277 @@ +//===--- 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(); +} + +ImplicitWideningOfMultiplicationResultCheck:: + ImplicitWideningOfMultiplicationResultCheck(StringRef Name, + ClangTidyContext *Context) + : ClangTidyCheck(Name, Context), + UseCXXStaticCastsInCppSources( + Options.get("UseCXXStaticCastsInCppSources", true)), + UseCXXHeadersInCppSources(Options.get("UseCXXHeadersInCppSources", true)), + IncludeInserter(Options.getLocalOrGlobal("IncludeStyle", + utils::IncludeSorter::IS_LLVM)) { +} + +void ImplicitWideningOfMultiplicationResultCheck::registerPPCallbacks( + const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) { + IncludeInserter.registerPreprocessor(PP); +} + +void ImplicitWideningOfMultiplicationResultCheck::storeOptions( + ClangTidyOptions::OptionMap &Opts) { + Options.store(Opts, "UseCXXStaticCastsInCppSources", + UseCXXStaticCastsInCppSources); + Options.store(Opts, "UseCXXHeadersInCppSources", UseCXXHeadersInCppSources); + Options.store(Opts, "IncludeStyle", IncludeInserter.getStyle()); +} + +llvm::Optional +ImplicitWideningOfMultiplicationResultCheck::includeStddefHeader( + SourceLocation File) { + return IncludeInserter.createIncludeInsertion( + Result->SourceManager->getFileID(File), + ShouldUseCXXHeader ? "" : ""); +} + +void ImplicitWideningOfMultiplicationResultCheck::handleImplicitCastExpr( + const ImplicitCastExpr *ICE) { + ASTContext *Context = Result->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(); + + { + auto Diag = diag(E->getBeginLoc(), + "make conversion explicit to silence this warning", + DiagnosticIDs::Note) + << E->getSourceRange(); + + if (ShouldUseCXXStaticCast) + Diag << FixItHint::CreateInsertion( + E->getBeginLoc(), "static_cast<" + Ty.getAsString() + ">(") + << FixItHint::CreateInsertion(E->getEndLoc(), ")"); + else + Diag << FixItHint::CreateInsertion(E->getBeginLoc(), + "(" + Ty.getAsString() + ")(") + << FixItHint::CreateInsertion(E->getEndLoc(), ")"); + Diag << includeStddefHeader(E->getBeginLoc()); + } + + 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); + } + + { + auto Diag = diag(E->getBeginLoc(), "perform multiplication in a wider type", + DiagnosticIDs::Note) + << LHS->getSourceRange(); + + if (ShouldUseCXXStaticCast) + Diag << FixItHint::CreateInsertion(LHS->getBeginLoc(), + "static_cast<" + + WideExprTy.getAsString() + ">(") + << FixItHint::CreateInsertion(LHS->getEndLoc(), ")"); + else + Diag << FixItHint::CreateInsertion(LHS->getBeginLoc(), + "(" + WideExprTy.getAsString() + ")"); + Diag << includeStddefHeader(LHS->getBeginLoc()); + } +} + +void ImplicitWideningOfMultiplicationResultCheck::handlePointerOffsetting( + const Expr *E) { + ASTContext *Context = Result->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->getPointerDiffType(); + QualType USizeTy = Context->getSizeType(); + QualType SizeTy = IndexExprType->isSignedIntegerType() ? SSizeTy : USizeTy; + // FIXME: is there a way to actually get the QualType for size_t/ptrdiff_t? + // Note that SizeTy.getAsString() will be unsigned long/..., NOT size_t! + StringRef TyAsString = + IndexExprType->isSignedIntegerType() ? "ptrdiff_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; + + { + auto Diag = diag(IndexExpr->getBeginLoc(), + "make conversion explicit to silence this warning", + DiagnosticIDs::Note) + << IndexExpr->getSourceRange(); + + if (ShouldUseCXXStaticCast) + Diag << FixItHint::CreateInsertion( + IndexExpr->getBeginLoc(), + (Twine("static_cast<") + TyAsString + ">(").str()) + << FixItHint::CreateInsertion(IndexExpr->getEndLoc(), ")"); + else + Diag << FixItHint::CreateInsertion(IndexExpr->getBeginLoc(), + (Twine("(") + TyAsString + ")(").str()) + << FixItHint::CreateInsertion(IndexExpr->getEndLoc(), ")"); + Diag << includeStddefHeader(IndexExpr->getBeginLoc()); + } + + { + auto Diag = + diag(IndexExpr->getBeginLoc(), "perform multiplication in a wider type", + DiagnosticIDs::Note) + << LHS->getSourceRange(); + + if (ShouldUseCXXStaticCast) + Diag << FixItHint::CreateInsertion( + LHS->getBeginLoc(), + (Twine("static_cast<") + TyAsString + ">(").str()) + << FixItHint::CreateInsertion(LHS->getEndLoc(), ")"); + else + Diag << FixItHint::CreateInsertion(LHS->getBeginLoc(), + (Twine("(") + TyAsString + ")").str()); + Diag << includeStddefHeader(LHS->getBeginLoc()); + } +} + +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) { + this->Result = &Result; + ShouldUseCXXStaticCast = + UseCXXStaticCastsInCppSources && Result.Context->getLangOpts().CPlusPlus; + ShouldUseCXXHeader = + UseCXXHeadersInCppSources && Result.Context->getLangOpts().CPlusPlus; + + if (const auto *MatchedDecl = Result.Nodes.getNodeAs("x")) + handleImplicitCastExpr(MatchedDecl); + else if (const auto *MatchedDecl = + Result.Nodes.getNodeAs("x")) + handlePointerOffsetting(MatchedDecl); + else if (const auto *MatchedDecl = + Result.Nodes.getNodeAs("x")) + handlePointerOffsetting(MatchedDecl); +} + +} // 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 @@ -84,6 +84,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,63 @@ +.. 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. + +This is mainly useful when operating on a very large buffers. +For example, consider: + +.. code-block:: c++ + + void zeroinit(char* base, unsigned width, unsigned height) { + for(unsigned row = 0; row != height; ++row) { + for(unsigned col = 0; col != width; ++col) { + char* ptr = base + row * width + col; + *ptr = 0; + } + } + } + +This is fine in general, but iff ``width * height`` overflows, +you end up wrapping back to the beginning of ``base`` +instead of processing the entire requested buffer. + +Indeed, this only matters for pretty large buffers (4GB+), +but that can happen very easily for example in image processing, +where for that to happen you "only" need a ~269MPix image. + + +Options +------- + +.. option:: UseCXXStaticCastsInCppSources + + When suggesting fix-its for C++ code, should C++-style ``static_cast<>()``'s + be suggested, or C-style casts. Defaults to ``true``. + +.. option:: UseCXXHeadersInCppSources + + When suggesting to include the appropriate header in C++ code, + should ```` header be suggested, or ````. + Defaults to ``true``. + + +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,90 @@ +// RUN: %check_clang_tidy -check-suffixes=ALL,C -std=c99 %s bugprone-implicit-widening-of-multiplication-result %t -- -- -target x86_64-unknown-unknown -x c +// RUN: %check_clang_tidy -check-suffixes=ALL,CXX %s bugprone-implicit-widening-of-multiplication-result %t -- -- -target x86_64-unknown-unknown -x c++ + +// RUN: %check_clang_tidy -check-suffixes=ALL,C -std=c99 %s bugprone-implicit-widening-of-multiplication-result %t -- \ +// RUN: -config='{CheckOptions: [ \ +// RUN: {key: bugprone-implicit-widening-of-multiplication-result.UseCXXStaticCastsInCppSources, value: 0} \ +// RUN: ]}' -- -target x86_64-unknown-unknown -x c +// RUN: %check_clang_tidy -check-suffixes=ALL,C %s bugprone-implicit-widening-of-multiplication-result %t -- \ +// RUN: -config='{CheckOptions: [ \ +// RUN: {key: bugprone-implicit-widening-of-multiplication-result.UseCXXStaticCastsInCppSources, value: 0} \ +// RUN: ]}' -- -target x86_64-unknown-unknown -x c++ + +char *t0(char *base, int a, int b) { + return &base[a * b]; + // CHECK-NOTES-ALL: :[[@LINE-1]]:11: warning: result of multiplication in type 'int' is used as a pointer offset after an implicit widening conversion to type 'ptrdiff_t' + // CHECK-NOTES-ALL: :[[@LINE-2]]:16: note: make conversion explicit to silence this warning + // CHECK-NOTES-C: (ptrdiff_t)( ) + // CHECK-NOTES-CXX: static_cast( ) + // CHECK-NOTES-ALL: :[[@LINE-5]]:16: note: perform multiplication in a wider type + // CHECK-NOTES-C: (ptrdiff_t) + // CHECK-NOTES-CXX: static_cast() +} +void *t1(char *base, int a, int b) { + return &((a * b)[base]); + // CHECK-NOTES-ALL: :[[@LINE-1]]:12: warning: result of multiplication in type 'int' is used as a pointer offset after an implicit widening conversion to type 'ptrdiff_t' + // CHECK-NOTES-ALL: :[[@LINE-2]]:13: note: make conversion explicit to silence this warning + // CHECK-NOTES-ALL: :[[@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-ALL: :[[@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-ALL: :[[@LINE-2]]:16: note: make conversion explicit to silence this warning + // CHECK-NOTES-C: (size_t) + // CHECK-NOTES-CXX: static_cast( ) + // CHECK-NOTES-ALL: :[[@LINE-5]]:16: note: perform multiplication in a wider type + // CHECK-NOTES-C: (size_t) + // CHECK-NOTES-CXX: static_cast() +} + +char *t3(char *base, int a, unsigned int b) { + return &base[a * b]; + // CHECK-NOTES-ALL: :[[@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-ALL: :[[@LINE-2]]:16: note: make conversion explicit to silence this warning + // CHECK-NOTES-ALL: :[[@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-ALL: :[[@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-ALL: :[[@LINE-2]]:16: note: make conversion explicit to silence this warning + // CHECK-NOTES-ALL: :[[@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-ALL: :[[@LINE-1]]:11: warning: result of multiplication in type 'int' is used as a pointer offset after an implicit widening conversion to type 'ptrdiff_t' + // CHECK-NOTES-ALL: :[[@LINE-2]]:17: note: make conversion explicit to silence this warning + // CHECK-NOTES-ALL: :[[@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 -- -- -target x86_64-unknown-unknown -x c +// RUN: %check_clang_tidy %s bugprone-implicit-widening-of-multiplication-result %t -- -- -target x86_64-unknown-unknown -x c++ + +// RUN: %check_clang_tidy -std=c99 %s bugprone-implicit-widening-of-multiplication-result %t -- -- -target x86_64-unknown-unknown -x c -fsigned-char +// RUN: %check_clang_tidy %s bugprone-implicit-widening-of-multiplication-result %t -- -- -target x86_64-unknown-unknown -x c++ -fsigned-char + +// RUN: %check_clang_tidy -std=c99 %s bugprone-implicit-widening-of-multiplication-result %t -- -- -target x86_64-unknown-unknown -x c -funsigned-char +// RUN: %check_clang_tidy %s bugprone-implicit-widening-of-multiplication-result %t -- -- -target x86_64-unknown-unknown-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-extint.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone-implicit-widening-of-multiplication-result-extint.cpp new file mode 100644 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone-implicit-widening-of-multiplication-result-extint.cpp @@ -0,0 +1,21 @@ +// RUN: %check_clang_tidy -std=c99 %s bugprone-implicit-widening-of-multiplication-result %t -- -- -target x86_64-unknown-unknown -x c +// RUN: %check_clang_tidy %s bugprone-implicit-widening-of-multiplication-result %t -- -- -target x86_64-unknown-unknown -x c++ + +_ExtInt(64) t0(_ExtInt(32) a, _ExtInt(32) b) { + return a * b; + // CHECK-NOTES: :[[@LINE-1]]:10: warning: performing an implicit widening conversion to type '_ExtInt(64)' of a multiplication performed in type '_ExtInt(32)' + // 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 _ExtInt(64) t1(_ExtInt(32) a, _ExtInt(32) b) { + return a * b; + // CHECK-NOTES: :[[@LINE-1]]:10: warning: performing an implicit widening conversion to type 'unsigned _ExtInt(64)' of a multiplication performed in type '_ExtInt(32)' + // 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 +} +_ExtInt(64) t2(unsigned _ExtInt(32) a, unsigned _ExtInt(32) b) { + return a * b; + // CHECK-NOTES: :[[@LINE-1]]:10: warning: performing an implicit widening conversion to type '_ExtInt(64)' of a multiplication performed in type 'unsigned _ExtInt(32)' + // 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,122 @@ +// RUN: %check_clang_tidy -check-suffixes=ALL,C -std=c99 %s bugprone-implicit-widening-of-multiplication-result %t -- -- -target x86_64-unknown-unknown -x c +// RUN: %check_clang_tidy -check-suffixes=ALL,CXX %s bugprone-implicit-widening-of-multiplication-result %t -- -- -target x86_64-unknown-unknown -x c++ + +// RUN: %check_clang_tidy -check-suffixes=ALL,C -std=c99 %s bugprone-implicit-widening-of-multiplication-result %t -- \ +// RUN: -config='{CheckOptions: [ \ +// RUN: {key: bugprone-implicit-widening-of-multiplication-result.UseCXXStaticCastsInCppSources, value: 0} \ +// RUN: ]}' -- -target x86_64-unknown-unknown -x c +// RUN: %check_clang_tidy -check-suffixes=ALL,C %s bugprone-implicit-widening-of-multiplication-result %t -- \ +// RUN: -config='{CheckOptions: [ \ +// RUN: {key: bugprone-implicit-widening-of-multiplication-result.UseCXXStaticCastsInCppSources, value: 0} \ +// RUN: ]}' -- -target x86_64-unknown-unknown -x c++ + +long t0(int a, int b) { + return a * b; + // CHECK-NOTES-ALL: :[[@LINE-1]]:10: warning: performing an implicit widening conversion to type 'long' of a multiplication performed in type 'int' + // CHECK-NOTES-ALL: :[[@LINE-2]]:10: note: make conversion explicit to silence this warning + // CHECK-NOTES-C: (long)( ) + // CHECK-NOTES-CXX: static_cast( ) + // CHECK-NOTES-ALL: :[[@LINE-5]]:10: note: perform multiplication in a wider type + // CHECK-NOTES-C: (long) + // CHECK-NOTES-CXX: static_cast() +} +unsigned long t1(int a, int b) { + return a * b; + // CHECK-NOTES-ALL: :[[@LINE-1]]:10: warning: performing an implicit widening conversion to type 'unsigned long' of a multiplication performed in type 'int' + // CHECK-NOTES-ALL: :[[@LINE-2]]:10: note: make conversion explicit to silence this warning + // CHECK-NOTES-C: (unsigned long)( ) + // CHECK-NOTES-CXX: static_cast( ) + // CHECK-NOTES-ALL: :[[@LINE-5]]:10: note: perform multiplication in a wider type + // CHECK-NOTES-C: (long) + // CHECK-NOTES-CXX: static_cast() +} + +long t2(unsigned int a, int b) { + return a * b; + // CHECK-NOTES-ALL: :[[@LINE-1]]:10: warning: performing an implicit widening conversion to type 'long' of a multiplication performed in type 'unsigned int' + // CHECK-NOTES-ALL: :[[@LINE-2]]:10: note: make conversion explicit to silence this warning + // CHECK-NOTES-C: (long)( ) + // CHECK-NOTES-CXX: static_cast( ) + // CHECK-NOTES-ALL: :[[@LINE-5]]:10: note: perform multiplication in a wider type + // CHECK-NOTES-C: (unsigned long) + // CHECK-NOTES-CXX: static_cast() +} +unsigned long t3(unsigned int a, int b) { + return a * b; + // CHECK-NOTES-ALL: :[[@LINE-1]]:10: warning: performing an implicit widening conversion to type 'unsigned long' of a multiplication performed in type 'unsigned int' + // CHECK-NOTES-ALL: :[[@LINE-2]]:10: note: make conversion explicit to silence this warning + // CHECK-NOTES-C: (unsigned long)( ) + // CHECK-NOTES-CXX: static_cast( ) + // CHECK-NOTES-ALL: :[[@LINE-5]]:10: note: perform multiplication in a wider type + // CHECK-NOTES-C: (unsigned long) + // CHECK-NOTES-CXX: static_cast() +} + +long t4(int a, unsigned int b) { + return a * b; + // CHECK-NOTES-ALL: :[[@LINE-1]]:10: warning: performing an implicit widening conversion to type 'long' of a multiplication performed in type 'unsigned int' + // CHECK-NOTES-ALL: :[[@LINE-2]]:10: note: make conversion explicit to silence this warning + // CHECK-NOTES-ALL: :[[@LINE-3]]:10: note: perform multiplication in a wider type +} +unsigned long t5(int a, unsigned int b) { + return a * b; + // CHECK-NOTES-ALL: :[[@LINE-1]]:10: warning: performing an implicit widening conversion to type 'unsigned long' of a multiplication performed in type 'unsigned int' + // CHECK-NOTES-ALL: :[[@LINE-2]]:10: note: make conversion explicit to silence this warning + // CHECK-NOTES-ALL: :[[@LINE-3]]:10: note: perform multiplication in a wider type +} + +long t6(unsigned int a, unsigned int b) { + return a * b; + // CHECK-NOTES-ALL: :[[@LINE-1]]:10: warning: performing an implicit widening conversion to type 'long' of a multiplication performed in type 'unsigned int' + // CHECK-NOTES-ALL: :[[@LINE-2]]:10: note: make conversion explicit to silence this warning + // CHECK-NOTES-ALL: :[[@LINE-3]]:10: note: perform multiplication in a wider type +} +unsigned long t7(unsigned int a, unsigned int b) { + return a * b; + // CHECK-NOTES-ALL: :[[@LINE-1]]:10: warning: performing an implicit widening conversion to type 'unsigned long' of a multiplication performed in type 'unsigned int' + // CHECK-NOTES-ALL: :[[@LINE-2]]:10: note: make conversion explicit to silence this warning + // CHECK-NOTES-ALL: :[[@LINE-3]]:10: note: perform multiplication in a wider type +} + +long t8(int a, int b) { + return (a * b); + // CHECK-NOTES-ALL: :[[@LINE-1]]:11: warning: performing an implicit widening conversion to type 'long' of a multiplication performed in type 'int' + // CHECK-NOTES-ALL: :[[@LINE-2]]:11: note: make conversion explicit to silence this warning + // CHECK-NOTES-ALL: :[[@LINE-3]]:11: note: perform multiplication in a wider type +} +long t9(int a, int b) { + return (a)*b; + // CHECK-NOTES-ALL: :[[@LINE-1]]:10: warning: performing an implicit widening conversion to type 'long' of a multiplication performed in type 'int' + // CHECK-NOTES-ALL: :[[@LINE-2]]:10: note: make conversion explicit to silence this warning + // CHECK-NOTES-ALL: :[[@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,99 @@ +// RUN: %check_clang_tidy -check-suffixes=ALL,C -std=c99 %s bugprone-implicit-widening-of-multiplication-result %t -- -- -target x86_64-unknown-unknown -x c +// RUN: %check_clang_tidy -check-suffixes=ALL,CXX %s bugprone-implicit-widening-of-multiplication-result %t -- -- -target x86_64-unknown-unknown -x c++ + +// RUN: %check_clang_tidy -check-suffixes=ALL,C -std=c99 %s bugprone-implicit-widening-of-multiplication-result %t -- \ +// RUN: -config='{CheckOptions: [ \ +// RUN: {key: bugprone-implicit-widening-of-multiplication-result.UseCXXStaticCastsInCppSources, value: 0} \ +// RUN: ]}' -- -target x86_64-unknown-unknown -x c +// RUN: %check_clang_tidy -check-suffixes=ALL,C %s bugprone-implicit-widening-of-multiplication-result %t -- \ +// RUN: -config='{CheckOptions: [ \ +// RUN: {key: bugprone-implicit-widening-of-multiplication-result.UseCXXStaticCastsInCppSources, value: 0} \ +// RUN: ]}' -- -target x86_64-unknown-unknown -x c++ + +char *t0(char *base, int a, int b) { + return base + a * b; + // CHECK-NOTES-ALL: :[[@LINE-1]]:10: warning: result of multiplication in type 'int' is used as a pointer offset after an implicit widening conversion to type 'ptrdiff_t' + // CHECK-NOTES-ALL: :[[@LINE-2]]:17: note: make conversion explicit to silence this warning + // CHECK-NOTES-C: (ptrdiff_t)( ) + // CHECK-NOTES-CXX: static_cast( ) + // CHECK-NOTES-ALL: :[[@LINE-5]]:17: note: perform multiplication in a wider type + // CHECK-NOTES-C: (ptrdiff_t) + // CHECK-NOTES-CXX: static_cast() +} +char *t1(char *base, int a, int b) { + return a * b + base; + // CHECK-NOTES-ALL: :[[@LINE-1]]:10: warning: result of multiplication in type 'int' is used as a pointer offset after an implicit widening conversion to type 'ptrdiff_t' + // CHECK-NOTES-ALL: :[[@LINE-2]]:10: note: make conversion explicit to silence this warning + // CHECK-NOTES-ALL: :[[@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-ALL: :[[@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-ALL: :[[@LINE-2]]:17: note: make conversion explicit to silence this warning + // CHECK-NOTES-C: (size_t)( ) + // CHECK-NOTES-CXX: static_cast( ) + // CHECK-NOTES-ALL: :[[@LINE-5]]:17: note: perform multiplication in a wider type + // CHECK-NOTES-C: (size_t) + // CHECK-NOTES-CXX: static_cast() +} + +char *t3(char *base, int a, unsigned int b) { + return base + a * b; + // CHECK-NOTES-ALL: :[[@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-ALL: :[[@LINE-2]]:17: note: make conversion explicit to silence this warning + // CHECK-NOTES-ALL: :[[@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-ALL: :[[@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-ALL: :[[@LINE-2]]:17: note: make conversion explicit to silence this warning + // CHECK-NOTES-ALL: :[[@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-ALL: :[[@LINE-1]]:10: warning: result of multiplication in type 'int' is used as a pointer offset after an implicit widening conversion to type 'ptrdiff_t' + // CHECK-NOTES-ALL: :[[@LINE-2]]:17: note: make conversion explicit to silence this warning + // CHECK-NOTES-ALL: :[[@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-ALL: :[[@LINE-1]]:10: warning: result of multiplication in type 'int' is used as a pointer offset after an implicit widening conversion to type 'ptrdiff_t' + // CHECK-NOTES-ALL: :[[@LINE-2]]:21: note: make conversion explicit to silence this warning + // CHECK-NOTES-ALL: :[[@LINE-3]]:21: note: perform multiplication in a wider type +} + +char *n7(char *base, int a, int b) { + return base + (a * b); + // CHECK-NOTES-ALL: :[[@LINE-1]]:10: warning: result of multiplication in type 'int' is used as a pointer offset after an implicit widening conversion to type 'ptrdiff_t' + // CHECK-NOTES-ALL: :[[@LINE-2]]:18: note: make conversion explicit to silence this warning + // CHECK-NOTES-ALL: :[[@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-ALL: :[[@LINE-1]]:10: warning: result of multiplication in type 'int' is used as a pointer offset after an implicit widening conversion to type 'ptrdiff_t' + // CHECK-NOTES-ALL: :[[@LINE-2]]:18: note: make conversion explicit to silence this warning + // CHECK-NOTES-ALL: :[[@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 -- -- -target x86_64-unknown-unknown -x c +// RUN: %check_clang_tidy %s bugprone-implicit-widening-of-multiplication-result %t -- -- -target x86_64-unknown-unknown -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,12 @@ return getVectorType(getCorrespondingUnsignedType(VTy->getElementType()), VTy->getNumElements(), VTy->getVectorKind()); - // For enums, we return the unsigned version of the base type. + // For _ExtInt, return an unsigned _ExtInt with same width. + if (const auto *EITy = T->getAs()) + return getExtIntType(/*IsUnsigned=*/true, EITy->getNumBits()); + + // 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 +10155,74 @@ } } +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 _ExtInt, return a signed _ExtInt with same width. + if (const auto *EITy = T->getAs()) + return getExtIntType(/*IsUnsigned=*/false, EITy->getNumBits()); + + // 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,