aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChandler Carruth <chandlerc@gmail.com>2011-01-04 06:52:15 +0000
committerChandler Carruth <chandlerc@gmail.com>2011-01-04 06:52:15 +0000
commit9f7a6eeee441bcbb1b17208cb3abd65a0017525a (patch)
treeb5300481faa1ce3f47e23d2887b1db7f6591c85a
parentb2b5cc0cf908d516a107d373db963f692449a8a8 (diff)
Implement -Wself-assign, which warns on code such as:
int x = 42; x = x; // Warns here. The warning avoids macro expansions, templates, user-defined assignment operators, and volatile types, so false positives are expected to be low. The common (mis-)use of this code pattern is to silence unused variable warnings, but a more idiomatic way of doing that is '(void)x;'. A follow-up to this will add a note and fix-it hint suggesting this replacement in cases where the StmtExpr consists precisely of the self assignment. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@122804 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r--include/clang/Basic/DiagnosticGroups.td2
-rw-r--r--include/clang/Basic/DiagnosticSemaKinds.td4
-rw-r--r--lib/Sema/SemaExpr.cpp36
-rw-r--r--test/SemaCXX/warn-self-assign.cpp47
4 files changed, 89 insertions, 0 deletions
diff --git a/include/clang/Basic/DiagnosticGroups.td b/include/clang/Basic/DiagnosticGroups.td
index 141770a942..7b8dd93eb9 100644
--- a/include/clang/Basic/DiagnosticGroups.td
+++ b/include/clang/Basic/DiagnosticGroups.td
@@ -95,6 +95,7 @@ def : DiagGroup<"pointer-to-int-cast">;
def : DiagGroup<"redundant-decls">;
def ReturnType : DiagGroup<"return-type">;
def BindToTemporaryCopy : DiagGroup<"bind-to-temporary-copy">;
+def SelfAssignment : DiagGroup<"self-assign">;
def SemiBeforeMethodBody : DiagGroup<"semicolon-before-method-body">;
def : DiagGroup<"sequence-point">;
def Shadow : DiagGroup<"shadow">;
@@ -216,6 +217,7 @@ def Most : DiagGroup<"most", [
MultiChar,
Reorder,
ReturnType,
+ SelfAssignment,
Switch,
Trigraphs,
Uninitialized,
diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td
index bf28070f18..2559464298 100644
--- a/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/include/clang/Basic/DiagnosticSemaKinds.td
@@ -2243,6 +2243,10 @@ def warn_logical_and_in_logical_or : Warning<
def note_logical_and_in_logical_or_silence : Note<
"place parentheses around the '&&' expression to silence this warning">;
+def warn_self_assignment : Warning<
+ "explicitly assigning a variable of type %0 to itself">,
+ InGroup<SelfAssignment>, DefaultIgnore;
+
def err_sizeof_nonfragile_interface : Error<
"invalid application of '%select{alignof|sizeof}1' to interface %0 in "
"non-fragile ABI">;
diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp
index f0e2049646..1e82432ba1 100644
--- a/lib/Sema/SemaExpr.cpp
+++ b/lib/Sema/SemaExpr.cpp
@@ -7460,6 +7460,40 @@ static inline UnaryOperatorKind ConvertTokenKindToUnaryOpcode(
return Opc;
}
+/// DiagnoseSelfAssignment - Emits a warning if a value is assigned to itself.
+/// This warning is only emitted for builtin assignment operations. It is also
+/// suppressed in the event of macro expansions.
+static void DiagnoseSelfAssignment(Sema &S, Expr *lhs, Expr *rhs,
+ SourceLocation OpLoc) {
+ if (!S.ActiveTemplateInstantiations.empty())
+ return;
+ if (OpLoc.isInvalid() || OpLoc.isMacroID())
+ return;
+ lhs = lhs->IgnoreParenImpCasts();
+ rhs = rhs->IgnoreParenImpCasts();
+ const DeclRefExpr *LeftDeclRef = dyn_cast<DeclRefExpr>(lhs);
+ const DeclRefExpr *RightDeclRef = dyn_cast<DeclRefExpr>(rhs);
+ if (!LeftDeclRef || !RightDeclRef ||
+ LeftDeclRef->getLocation().isMacroID() ||
+ RightDeclRef->getLocation().isMacroID())
+ return;
+ const ValueDecl *LeftDecl =
+ cast<ValueDecl>(LeftDeclRef->getDecl()->getCanonicalDecl());
+ const ValueDecl *RightDecl =
+ cast<ValueDecl>(RightDeclRef->getDecl()->getCanonicalDecl());
+ if (LeftDecl != RightDecl)
+ return;
+ if (LeftDecl->getType().isVolatileQualified())
+ return;
+ if (const ReferenceType *RefTy = LeftDecl->getType()->getAs<ReferenceType>())
+ if (RefTy->getPointeeType().isVolatileQualified())
+ return;
+
+ S.Diag(OpLoc, diag::warn_self_assignment)
+ << LeftDeclRef->getType()
+ << lhs->getSourceRange() << rhs->getSourceRange();
+}
+
/// CreateBuiltinBinOp - Creates a new built-in binary operation with
/// operator @p Opc at location @c TokLoc. This routine only supports
/// built-in operations; ActOnBinOp handles overloaded operators.
@@ -7482,6 +7516,8 @@ ExprResult Sema::CreateBuiltinBinOp(SourceLocation OpLoc,
VK = lhs->getValueKind();
OK = lhs->getObjectKind();
}
+ if (!ResultTy.isNull())
+ DiagnoseSelfAssignment(*this, lhs, rhs, OpLoc);
break;
case BO_PtrMemD:
case BO_PtrMemI:
diff --git a/test/SemaCXX/warn-self-assign.cpp b/test/SemaCXX/warn-self-assign.cpp
new file mode 100644
index 0000000000..fcdb2ab6bc
--- /dev/null
+++ b/test/SemaCXX/warn-self-assign.cpp
@@ -0,0 +1,47 @@
+// RUN: %clang_cc1 -fsyntax-only -Wself-assign -verify %s
+
+void f() {
+ int a = 42, b = 42;
+ a = a; // expected-warning{{explicitly assigning}}
+ b = b; // expected-warning{{explicitly assigning}}
+ a = b;
+ b = a = b;
+ a = a = a; // expected-warning{{explicitly assigning}}
+ a = b = b = a;
+}
+
+// Dummy type.
+struct S {};
+
+void false_positives() {
+#define OP =
+#define LHS a
+#define RHS a
+ int a = 42;
+ // These shouldn't warn due to the use of the preprocessor.
+ a OP a;
+ LHS = a;
+ a = RHS;
+ LHS OP RHS;
+#undef OP
+#undef LHS
+#undef RHS
+
+ S s;
+ s = s; // Not a builtin assignment operator, no warning.
+
+ // Volatile stores aren't side-effect free.
+ volatile int vol_a;
+ vol_a = vol_a;
+ volatile int &vol_a_ref = vol_a;
+ vol_a_ref = vol_a_ref;
+}
+
+template <typename T> void g() {
+ T a;
+ a = a; // May or may not be a builtin assignment operator, no warning.
+}
+void instantiate() {
+ g<int>();
+ g<S>();
+}