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
This commit is contained in:
Chris Lattner 2007-06-27 05:38:08 +00:00
parent eb2def66be
commit 1ec5f563c7
5 changed files with 84 additions and 5 deletions

View File

@ -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<ParenExpr>(this)->getSubExpr()->hasLocalSideEffect();
case UnaryOperatorClass: {
const UnaryOperator *UO = cast<UnaryOperator>(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<BinaryOperator>(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<CastExpr>(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;

View File

@ -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);

View File

@ -20,6 +20,19 @@
#include "clang/Lex/IdentifierTable.h"
using namespace clang;
Sema::StmtResult Sema::ParseExprStmt(ExprTy *expr) {
Expr *E = static_cast<Expr*>(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);
}

View File

@ -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;

View File

@ -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")