From 1ec5f563c7297352e0ba64730bae6c3453e7c7ca Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Wed, 27 Jun 2007 05:38:08 +0000 Subject: [PATCH] First cut at warning about expressions whose results are ignored. For example, this produces: warn.c:4:3: warning: expression result unused X == Y; ^~~~~~ warn.c:5:3: warning: expression result unused (void)X; ^~~~~~~ warn.c:11:3: warning: expression result unused A == foo(1, 2); ^~~~~~~~~~~~~~ warn.c:13:3: warning: expression result unused foo(1,2)+foo(4,3); ^~~~~~~~~~~~~~~~~ llvm-svn: 39682 --- clang/AST/Expr.cpp | 63 +++++++++++++++++-- clang/Sema/Sema.h | 2 + clang/Sema/SemaStmt.cpp | 13 ++++ clang/include/clang/AST/Expr.h | 8 ++- clang/include/clang/Basic/DiagnosticKinds.def | 3 + 5 files changed, 84 insertions(+), 5 deletions(-) diff --git a/clang/AST/Expr.cpp b/clang/AST/Expr.cpp index b3916f49b286..5d109203cae2 100644 --- a/clang/AST/Expr.cpp +++ b/clang/AST/Expr.cpp @@ -122,6 +122,61 @@ const char *BinaryOperator::getOpcodeStr(Opcode Op) { } } + +//===----------------------------------------------------------------------===// +// Generic Expression Routines +//===----------------------------------------------------------------------===// + +/// hasLocalSideEffect - Return true if this immediate expression has side +/// effects, not counting any sub-expressions. +bool Expr::hasLocalSideEffect() const { + switch (getStmtClass()) { + default: + return false; + case ParenExprClass: + return cast(this)->getSubExpr()->hasLocalSideEffect(); + case UnaryOperatorClass: { + const UnaryOperator *UO = cast(this); + + switch (UO->getOpcode()) { + default: return false; + case UnaryOperator::PostInc: + case UnaryOperator::PostDec: + case UnaryOperator::PreInc: + case UnaryOperator::PreDec: + return true; // ++/-- + + // FIXME: real/imag volatile + // deref volatile; + + case UnaryOperator::Extension: + return UO->getSubExpr()->hasLocalSideEffect(); + } + } + case BinaryOperatorClass: + return cast(this)->isAssignmentOp(); + + case ArraySubscriptExprClass: + // volatile + return false; + + case CallExprClass: + // TODO: check attributes for pure/const. + return true; + + case MemberExprClass: + // volatile load. + return false; + + case CastExprClass: + // If this is a cast to void, check the operand. Otherwise, the result of + // the cast is unused. + if (getType()->isVoidType()) + return cast(this)->getSubExpr()->hasLocalSideEffect(); + return false; + } +} + /// isLvalue - C99 6.3.2.1: an lvalue is an expression with an object type or an /// incomplete type other than void. Nonarray expressions that can be lvalues: /// - name, where name must be a variable @@ -172,10 +227,10 @@ Expr::isModifiableLvalueResult Expr::isModifiableLvalue() { isLvalueResult lvalResult = isLvalue(); switch (lvalResult) { - case LV_Valid: break; - case LV_NotObjectType: return MLV_NotObjectType; - case LV_IncompleteVoidType: return MLV_IncompleteVoidType; - case LV_InvalidExpression: return MLV_InvalidExpression; + case LV_Valid: break; + case LV_NotObjectType: return MLV_NotObjectType; + case LV_IncompleteVoidType: return MLV_IncompleteVoidType; + case LV_InvalidExpression: return MLV_InvalidExpression; } if (TR.isConstQualified()) return MLV_ConstQualified; diff --git a/clang/Sema/Sema.h b/clang/Sema/Sema.h index e4b7f18d0d45..c2e2462daf80 100644 --- a/clang/Sema/Sema.h +++ b/clang/Sema/Sema.h @@ -157,6 +157,8 @@ private: //===--------------------------------------------------------------------===// // Statement Parsing Callbacks: SemaStmt.cpp. public: + virtual StmtResult ParseExprStmt(ExprTy *Expr); + virtual StmtResult ParseNullStmt(SourceLocation SemiLoc); virtual StmtResult ParseCompoundStmt(SourceLocation L, SourceLocation R, StmtTy **Elts, unsigned NumElts); diff --git a/clang/Sema/SemaStmt.cpp b/clang/Sema/SemaStmt.cpp index 4a270162fa7a..4288413788df 100644 --- a/clang/Sema/SemaStmt.cpp +++ b/clang/Sema/SemaStmt.cpp @@ -20,6 +20,19 @@ #include "clang/Lex/IdentifierTable.h" using namespace clang; +Sema::StmtResult Sema::ParseExprStmt(ExprTy *expr) { + Expr *E = static_cast(expr); + + // Exprs are statements, so there is no need to do a conversion here. However, + // diagnose some potentially bad code. + if (!E->hasLocalSideEffect()) + Diag(E->getLocStart()/* + getExprLoc()*/, diag::warn_unused_expr, E->getSourceRange()); + + return E; +} + + Sema::StmtResult Sema::ParseNullStmt(SourceLocation SemiLoc) { return new NullStmt(SemiLoc); } diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h index 82d8e3e86e4b..ee1c552d77e4 100644 --- a/clang/include/clang/AST/Expr.h +++ b/clang/include/clang/AST/Expr.h @@ -42,6 +42,10 @@ public: SourceLocation getLocStart() const { return getSourceRange().Begin(); } SourceLocation getLocEnd() const { return getSourceRange().End(); } + /// hasLocalSideEffect - Return true if this immediate expression has side + /// effects, not counting any sub-expressions. + bool hasLocalSideEffect() const; + /// isLvalue - C99 6.3.2.1: an lvalue is an expression with an object type or /// incomplete type other than void. Nonarray expressions that can be lvalues: /// - name, where name must be a variable @@ -114,6 +118,7 @@ public: const Decl *getDecl() const { return D; } virtual SourceRange getSourceRange() const { return SourceRange(Loc); } + virtual void visit(StmtVisitor &Visitor); static bool classof(const Stmt *T) { return T->getStmtClass() == DeclRefExprClass; @@ -400,6 +405,7 @@ public: Expr *getBase() const { return Base; } FieldDecl *getMemberDecl() const { return MemberDecl; } bool isArrow() const { return IsArrow; } + virtual SourceRange getSourceRange() const { return SourceRange(getBase()->getLocStart(), MemberLoc); } @@ -426,6 +432,7 @@ public: QualType getDestType() const { return Ty; } Expr *getSubExpr() const { return Op; } + virtual SourceRange getSourceRange() const { return SourceRange(Loc, getSubExpr()->getSourceRange().End()); } @@ -484,7 +491,6 @@ public: bool isLogicalOp() const { return Opc == LAnd || Opc == LOr; } bool isAssignmentOp() const { return Opc >= Assign && Opc <= OrAssign; } - virtual void visit(StmtVisitor &Visitor); static bool classof(const Stmt *T) { return T->getStmtClass() == BinaryOperatorClass; diff --git a/clang/include/clang/Basic/DiagnosticKinds.def b/clang/include/clang/Basic/DiagnosticKinds.def index 4ad5f03e034a..c7c98a93bfd9 100644 --- a/clang/include/clang/Basic/DiagnosticKinds.def +++ b/clang/include/clang/Basic/DiagnosticKinds.def @@ -633,6 +633,9 @@ DIAG(err_typecheck_cond_incompatible_operands, ERROR, DIAG(ext_typecheck_cond_incompatible_pointers, EXTENSION, "pointer type mismatch ('%0' and '%1')") +DIAG(warn_unused_expr, WARNING, + "expression result unused") + // Statements. DIAG(err_continue_not_in_loop, ERROR, "'continue' statement not in loop statement")