lebedev.ri updated this revision to Diff 314103.
lebedev.ri marked 2 inline comments as done.
lebedev.ri added a comment.
NFC, rebased, added test with multiplication of `short`s.
Repository:
rG LLVM Github Monorepo
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D93822/new/
https://reviews.llvm.org/D93822
Files:
clang/docs/ReleaseNotes.rst
clang/include/clang/AST/ASTContext.h
clang/include/clang/Basic/DiagnosticGroups.td
clang/include/clang/Basic/DiagnosticSemaKinds.td
clang/include/clang/Sema/Sema.h
clang/lib/AST/ASTContext.cpp
clang/lib/Sema/Sema.cpp
clang/lib/Sema/SemaExpr.cpp
clang/test/Misc/warning-wall.c
clang/test/Sema/implicit-widening-of-multiplication-result.c
clang/test/Sema/implicit-widening-of-pointer-offset-in-array-subscript-expression.c
clang/test/Sema/implicit-widening-of-pointer-offset.c
Index: clang/test/Sema/implicit-widening-of-pointer-offset.c
===================================================================
--- /dev/null
+++ 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 <typename T>
+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
Index: clang/test/Sema/implicit-widening-of-pointer-offset-in-array-subscript-expression.c
===================================================================
--- /dev/null
+++ 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)];
+}
Index: clang/test/Sema/implicit-widening-of-multiplication-result.c
===================================================================
--- /dev/null
+++ clang/test/Sema/implicit-widening-of-multiplication-result.c
@@ -0,0 +1,131 @@
+// 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 t8(short a, int b) {
+ return a * b; // #8
+ // expected-warning@#8 {{performing an implicit widening conversion to type 'long' of a multiplication performed in type 'int'}}
+ // expected-note@#8 {{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@#8 {{perform multiplication in a wider type}}
+ // CHECK: fix-it:"{{.*}}":{[[@LINE-6]]:10-[[@LINE-6]]:10}:"(long)"
+}
+long t9(short a, short b) {
+ return a * b; // #9
+ // expected-warning@#9 {{performing an implicit widening conversion to type 'long' of a multiplication performed in type 'int'}}
+ // expected-note@#9 {{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@#9 {{perform multiplication in a wider type}}
+ // CHECK: fix-it:"{{.*}}":{[[@LINE-6]]:10-[[@LINE-6]]:10}:"(long)"
+}
+
+long n10(int a, int b) {
+ return (a * b);
+}
+long n11(int a, int b) {
+ return (long)(a * b);
+}
+long n12(int a, int b) {
+ return (unsigned long)(a * b);
+}
+
+long n13(long a, int b) {
+ return a * b;
+}
+long n14(int a, long b) {
+ return a * b;
+}
+
+long n15(int a, int b, int c) {
+ return a + b * c;
+}
+long n16(int a, int b, int c) {
+ return a * b + c;
+}
Index: clang/test/Misc/warning-wall.c
===================================================================
--- clang/test/Misc/warning-wall.c
+++ 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
Index: clang/lib/Sema/SemaExpr.cpp
===================================================================
--- clang/lib/Sema/SemaExpr.cpp
+++ clang/lib/Sema/SemaExpr.cpp
@@ -4723,8 +4723,13 @@
ExprResult Res = CreateBuiltinArraySubscriptExpr(base, lbLoc, idx, rbLoc);
- if (!Res.isInvalid() && isa<ArraySubscriptExpr>(Res.get()))
- CheckSubscriptAccessOfNoDeref(cast<ArraySubscriptExpr>(Res.get()));
+ if (!Res.isInvalid()) {
+ diagnoseImplicitWideningConversionOfMultiplicationOfPointerOffset(
+ Res.get());
+
+ if (isa<ArraySubscriptExpr>(Res.get()))
+ CheckSubscriptAccessOfNoDeref(cast<ArraySubscriptExpr>(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,
Index: clang/lib/Sema/Sema.cpp
===================================================================
--- clang/lib/Sema/Sema.cpp
+++ 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<BinaryOperator>(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<BinaryOperator>(E)) {
+ PointerExpr = BO->getLHS();
+ IndexExpr = BO->getRHS();
+ } else if (auto *ASE = dyn_cast<ArraySubscriptExpr>(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);
Index: clang/lib/AST/ASTContext.cpp
===================================================================
--- clang/lib/AST/ASTContext.cpp
+++ 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<VectorType>())
+ 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<EnumType>())
+ T = ETy->getDecl()->getIntegerType();
+
+ switch (T->castAs<BuiltinType>()->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,
Index: clang/include/clang/Sema/Sema.h
===================================================================
--- clang/include/clang/Sema/Sema.h
+++ 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);
}
Index: clang/include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -3714,6 +3714,19 @@
"code; pointer may be assumed to always convert to true">,
InGroup<UndefinedBoolConversion>;
+def warn_imp_widening_of_mul_result : Warning<
+ "performing an implicit widening conversion to type %0 "
+ "of a multiplication performed in type %1">,
+ InGroup<ImplicitWideningOfMulResult>, 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<ImplicitWideningOfPtrOffset>, 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<XorUsedAsPow>;
Index: clang/include/clang/Basic/DiagnosticGroups.td
===================================================================
--- clang/include/clang/Basic/DiagnosticGroups.td
+++ 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]>;
Index: clang/include/clang/AST/ASTContext.h
===================================================================
--- clang/include/clang/AST/ASTContext.h
+++ 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;
Index: clang/docs/ReleaseNotes.rst
===================================================================
--- clang/docs/ReleaseNotes.rst
+++ 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
-------------------------------------------------
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits