diff options
author | Chandler Carruth <chandlerc@gmail.com> | 2011-01-04 06:52:15 +0000 |
---|---|---|
committer | Chandler Carruth <chandlerc@gmail.com> | 2011-01-04 06:52:15 +0000 |
commit | 9f7a6eeee441bcbb1b17208cb3abd65a0017525a (patch) | |
tree | b5300481faa1ce3f47e23d2887b1db7f6591c85a | |
parent | b2b5cc0cf908d516a107d373db963f692449a8a8 (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.td | 2 | ||||
-rw-r--r-- | include/clang/Basic/DiagnosticSemaKinds.td | 4 | ||||
-rw-r--r-- | lib/Sema/SemaExpr.cpp | 36 | ||||
-rw-r--r-- | test/SemaCXX/warn-self-assign.cpp | 47 |
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>(); +} |