Unused,
Used,
Visibility,
+ WarnUnusedResult,
Weak,
Blocks,
Const,
NoReturnAttr() : Attr(NoReturn) {}
// Implement isa/cast/dyncast/etc.
-
static bool classof(const Attr *A) { return A->getKind() == NoReturn; }
static bool classof(const NoReturnAttr *A) { return true; }
};
DeprecatedAttr() : Attr(Deprecated) {}
// Implement isa/cast/dyncast/etc.
-
static bool classof(const Attr *A) { return A->getKind() == Deprecated; }
static bool classof(const DeprecatedAttr *A) { return true; }
};
static bool classof(const DeprecatedAttr *A) { return true; }
};
+class WarnUnusedResultAttr : public Attr {
+public:
+ WarnUnusedResultAttr() : Attr(WarnUnusedResult) {}
+
+ // Implement isa/cast/dyncast/etc.
+ static bool classof(const Attr *A) { return A->getKind() == WarnUnusedResult;}
+ static bool classof(const WarnUnusedResultAttr *A) { return true; }
+};
+
} // end namespace clang
#endif
/// a problem with a generic expression.
virtual SourceLocation getExprLoc() const { return getLocStart(); }
- /// hasLocalSideEffect - Return true if this immediate expression has side
- /// effects, not counting any sub-expressions.
- bool hasLocalSideEffect() const;
+ /// isUnusedResultAWarning - Return true if this immediate expression should
+ /// be warned about if the result is unused. If so, fill in Loc and Ranges
+ /// with location to warn on and the source range[s] to report with the
+ /// warning.
+ bool isUnusedResultAWarning(SourceLocation &Loc, SourceRange &R1,
+ SourceRange &R2) 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:
return SourceRange(getLHS()->getLocStart(), RBracketLoc);
}
+ SourceLocation getRBracketLoc() const { return RBracketLoc; }
virtual SourceLocation getExprLoc() const { return getBase()->getExprLoc(); }
static bool classof(const Stmt *T) {
NamedDecl *getMemberDecl() const { return MemberDecl; }
void setMemberDecl(NamedDecl *D) { MemberDecl = D; }
bool isArrow() const { return IsArrow; }
+ SourceLocation getMemberLoc() const { return MemberLoc; }
virtual SourceRange getSourceRange() const {
return SourceRange(getBase()->getLocStart(), MemberLoc);
return SourceRange(LParenLoc, RParenLoc);
}
+ SourceLocation getLParenLoc() const { return LParenLoc; }
+ SourceLocation getRParenLoc() const { return RParenLoc; }
+
static bool classof(const Stmt *T) {
return T->getStmtClass() == StmtExprClass;
}
CompoundStmt(ASTContext& C, Stmt **StmtStart, unsigned numStmts,
SourceLocation LB, SourceLocation RB)
: Stmt(CompoundStmtClass), NumStmts(numStmts), LBracLoc(LB), RBracLoc(RB) {
- if (NumStmts) {
- Body = new (C) Stmt*[NumStmts];
- memcpy(Body, StmtStart, numStmts * sizeof(*Body));
- }
- else
- Body = 0;
+ if (NumStmts == 0) {
+ Body = 0;
+ return;
+ }
+
+ Body = new (C) Stmt*[NumStmts];
+ memcpy(Body, StmtStart, numStmts * sizeof(*Body));
}
bool body_empty() const { return NumStmts == 0; }
DIAG(warn_attribute_weak_on_field, WARNING,
"__weak attribute cannot be specified on a field declaration")
DIAG(warn_attribute_wrong_decl_type, WARNING,
- "'%0' attribute only applies to %1 types")
+ "'%0' attribute only applies to %select{function|union|"
+ "variable and function|function or method}1 types")
DIAG(warn_attribute_ignored_for_field_of_type, WARNING,
"%0 attribute ignored for field of type %1")
DIAG(warn_transparent_union_attribute_field_size, WARNING,
// Generic Expression Routines
//===----------------------------------------------------------------------===//
-/// hasLocalSideEffect - Return true if this immediate expression has side
-/// effects, not counting any sub-expressions.
-bool Expr::hasLocalSideEffect() const {
+/// isUnusedResultAWarning - Return true if this immediate expression should
+/// be warned about if the result is unused. If so, fill in Loc and Ranges
+/// with location to warn on and the source range[s] to report with the
+/// warning.
+bool Expr::isUnusedResultAWarning(SourceLocation &Loc, SourceRange &R1,
+ SourceRange &R2) const {
switch (getStmtClass()) {
default:
- return false;
+ Loc = getExprLoc();
+ R1 = getSourceRange();
+ return true;
case ParenExprClass:
- return cast<ParenExpr>(this)->getSubExpr()->hasLocalSideEffect();
+ return cast<ParenExpr>(this)->getSubExpr()->
+ isUnusedResultAWarning(Loc, R1, R2);
case UnaryOperatorClass: {
const UnaryOperator *UO = cast<UnaryOperator>(this);
switch (UO->getOpcode()) {
- default: return false;
+ default: break;
case UnaryOperator::PostInc:
case UnaryOperator::PostDec:
case UnaryOperator::PreInc:
- case UnaryOperator::PreDec:
- return true; // ++/--
-
+ case UnaryOperator::PreDec: // ++/--
+ return false; // Not a warning.
case UnaryOperator::Deref:
// Dereferencing a volatile pointer is a side-effect.
- return getType().isVolatileQualified();
+ if (getType().isVolatileQualified())
+ return false;
+ break;
case UnaryOperator::Real:
case UnaryOperator::Imag:
// accessing a piece of a volatile complex is a side-effect.
- return UO->getSubExpr()->getType().isVolatileQualified();
-
+ if (UO->getSubExpr()->getType().isVolatileQualified())
+ return false;
+ break;
case UnaryOperator::Extension:
- return UO->getSubExpr()->hasLocalSideEffect();
+ return UO->getSubExpr()->isUnusedResultAWarning(Loc, R1, R2);
}
+ Loc = UO->getOperatorLoc();
+ R1 = UO->getSubExpr()->getSourceRange();
+ return true;
}
case BinaryOperatorClass: {
- const BinaryOperator *BinOp = cast<BinaryOperator>(this);
- // Consider comma to have side effects if the LHS and RHS both do.
- if (BinOp->getOpcode() == BinaryOperator::Comma)
- return BinOp->getLHS()->hasLocalSideEffect() &&
- BinOp->getRHS()->hasLocalSideEffect();
+ const BinaryOperator *BO = cast<BinaryOperator>(this);
+ // Consider comma to have side effects if the LHS or RHS does.
+ if (BO->getOpcode() == BinaryOperator::Comma)
+ return BO->getRHS()->isUnusedResultAWarning(Loc, R1, R2) ||
+ BO->getLHS()->isUnusedResultAWarning(Loc, R1, R2);
- return BinOp->isAssignmentOp();
+ if (BO->isAssignmentOp())
+ return false;
+ Loc = BO->getOperatorLoc();
+ R1 = BO->getLHS()->getSourceRange();
+ R2 = BO->getRHS()->getSourceRange();
+ return true;
}
case CompoundAssignOperatorClass:
- return true;
+ return false;
case ConditionalOperatorClass: {
+ // The condition must be evaluated, but if either the LHS or RHS is a
+ // warning, warn about them.
const ConditionalOperator *Exp = cast<ConditionalOperator>(this);
- return Exp->getCond()->hasLocalSideEffect()
- || (Exp->getLHS() && Exp->getLHS()->hasLocalSideEffect())
- || (Exp->getRHS() && Exp->getRHS()->hasLocalSideEffect());
+ if (Exp->getLHS()->isUnusedResultAWarning(Loc, R1, R2))
+ return true;
+ return Exp->getRHS()->isUnusedResultAWarning(Loc, R1, R2);
}
case MemberExprClass:
+ // If the base pointer or element is to a volatile pointer/field, accessing
+ // it is a side effect.
+ if (getType().isVolatileQualified())
+ return false;
+ Loc = cast<MemberExpr>(this)->getMemberLoc();
+ R1 = SourceRange(Loc, Loc);
+ R2 = cast<MemberExpr>(this)->getBase()->getSourceRange();
+ return true;
+
case ArraySubscriptExprClass:
// If the base pointer or element is to a volatile pointer/field, accessing
- // if is a side effect.
- return getType().isVolatileQualified();
+ // it is a side effect.
+ if (getType().isVolatileQualified())
+ return false;
+ Loc = cast<ArraySubscriptExpr>(this)->getRBracketLoc();
+ R1 = cast<ArraySubscriptExpr>(this)->getLHS()->getSourceRange();
+ R2 = cast<ArraySubscriptExpr>(this)->getRHS()->getSourceRange();
+ return true;
case CallExprClass:
- case CXXOperatorCallExprClass:
- // TODO: check attributes for pure/const. "void foo() { strlen("bar"); }"
- // should warn.
- return true;
+ case CXXOperatorCallExprClass: {
+ // If this is a direct call, get the callee.
+ const CallExpr *CE = cast<CallExpr>(this);
+ const Expr *CalleeExpr = CE->getCallee()->IgnoreParenCasts();
+ if (const DeclRefExpr *CalleeDRE = dyn_cast<DeclRefExpr>(CalleeExpr)) {
+ // If the callee has attribute pure, const, or warn_unused_result, warn
+ // about it. void foo() { strlen("bar"); } should warn.
+ if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(CalleeDRE->getDecl()))
+ if (FD->getAttr<WarnUnusedResultAttr>() ||
+ FD->getAttr<PureAttr>() || FD->getAttr<ConstAttr>()) {
+ Loc = CE->getCallee()->getLocStart();
+ R1 = CE->getCallee()->getSourceRange();
+
+ if (unsigned NumArgs = CE->getNumArgs())
+ R2 = SourceRange(CE->getArg(0)->getLocStart(),
+ CE->getArg(NumArgs-1)->getLocEnd());
+ return true;
+ }
+ }
+ return false;
+ }
case ObjCMessageExprClass:
- return true;
+ return false;
case StmtExprClass: {
// Statement exprs don't logically have side effects themselves, but are
// sometimes used in macros in ways that give them a type that is unused.
const CompoundStmt *CS = cast<StmtExpr>(this)->getSubStmt();
if (!CS->body_empty())
if (const Expr *E = dyn_cast<Expr>(CS->body_back()))
- return E->hasLocalSideEffect();
- return false;
+ return E->isUnusedResultAWarning(Loc, R1, R2);
+
+ Loc = cast<StmtExpr>(this)->getLParenLoc();
+ R1 = getSourceRange();
+ return true;
}
case CStyleCastExprClass:
+ // 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()->isUnusedResultAWarning(Loc,
+ R1, R2);
+ Loc = cast<CStyleCastExpr>(this)->getLParenLoc();
+ R1 = cast<CStyleCastExpr>(this)->getSubExpr()->getSourceRange();
+ return true;
case CXXFunctionalCastExprClass:
// 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;
-
+ return cast<CastExpr>(this)->getSubExpr()->isUnusedResultAWarning(Loc,
+ R1, R2);
+ Loc = cast<CXXFunctionalCastExpr>(this)->getTypeBeginLoc();
+ R1 = cast<CXXFunctionalCastExpr>(this)->getSubExpr()->getSourceRange();
+ return true;
+
case ImplicitCastExprClass:
// Check the operand, since implicit casts are inserted by Sema
- return cast<ImplicitCastExpr>(this)->getSubExpr()->hasLocalSideEffect();
+ return cast<ImplicitCastExpr>(this)
+ ->getSubExpr()->isUnusedResultAWarning(Loc, R1, R2);
case CXXDefaultArgExprClass:
- return cast<CXXDefaultArgExpr>(this)->getExpr()->hasLocalSideEffect();
+ return cast<CXXDefaultArgExpr>(this)
+ ->getExpr()->isUnusedResultAWarning(Loc, R1, R2);
case CXXNewExprClass:
// FIXME: In theory, there might be new expressions that don't have side
// effects (e.g. a placement new with an uninitialized POD).
case CXXDeleteExprClass:
- return true;
+ return false;
}
}
case 15:
if (!memcmp(Str, "ext_vector_type", 15)) return AT_ext_vector_type;
break;
+ case 16:
+ if (!memcmp(Str, "warnunusedresult", 16)) return AT_warn_unused_result;
+ break;
case 17:
if (!memcmp(Str, "transparent_union", 17)) return AT_transparent_union;
break;
// prototypes, so we ignore it as well
if (!isFunctionOrMethod(d) || !hasFunctionProto(d)) {
S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type)
- << "nonnull" << "function";
+ << "nonnull" << 0 /*function*/;
return;
}
if (!isFunctionOrMethod(d)) {
S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type)
- << "noreturn" << "function";
+ << "noreturn" << 0 /*function*/;
return;
}
if (!isa<VarDecl>(d) && !isFunctionOrMethod(d)) {
S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type)
- << "unused" << "variable and function";
+ << "unused" << 2 /*variable and function*/;
return;
}
}
} else if (!isFunctionOrMethod(d)) {
S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type)
- << "used" << "variable and function";
+ << "used" << 2 /*variable and function*/;
return;
}
FunctionDecl *Fn = dyn_cast<FunctionDecl>(d);
if (!Fn) {
S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type)
- << "constructor" << "function";
+ << "constructor" << 0 /*function*/;
return;
}
if (!isa<FunctionDecl>(d)) {
S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type)
- << "destructor" << "function";
+ << "destructor" << 0 /*function*/;
return;
}
}
} else {
S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type)
- << "sentinel" << "function or method";
+ << "sentinel" << 3 /*function or method*/;
return;
}
// FIXME: Actually create the attribute.
}
-static void HandleWeakAttr(Decl *d, const AttributeList &Attr, Sema &S) {
+static void HandleWarnUnusedResult(Decl *D, const AttributeList &Attr, Sema &S) {
+ // check the attribute arguments.
+ if (Attr.getNumArgs() != 0) {
+ S.Diag(Attr.getLoc(), diag::err_attribute_wrong_number_arguments) << 0;
+ return;
+ }
+
+ // TODO: could also be applied to methods?
+ FunctionDecl *Fn = dyn_cast<FunctionDecl>(D);
+ if (!Fn) {
+ S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type)
+ << "warn_unused_result" << 0 /*function*/;
+ return;
+ }
+
+ Fn->addAttr(new WarnUnusedResultAttr());
+}
+
+static void HandleWeakAttr(Decl *D, const AttributeList &Attr, Sema &S) {
// check the attribute arguments.
if (Attr.getNumArgs() != 0) {
S.Diag(Attr.getLoc(), diag::err_attribute_wrong_number_arguments) << 0;
return;
}
- d->addAttr(new WeakAttr());
+ D->addAttr(new WeakAttr());
}
-static void HandleDLLImportAttr(Decl *d, const AttributeList &Attr, Sema &S) {
+static void HandleDLLImportAttr(Decl *D, const AttributeList &Attr, Sema &S) {
// check the attribute arguments.
if (Attr.getNumArgs() != 0) {
S.Diag(Attr.getLoc(), diag::err_attribute_wrong_number_arguments) << 0;
}
// Attribute can be applied only to functions or variables.
- if (isa<VarDecl>(d)) {
- d->addAttr(new DLLImportAttr());
+ if (isa<VarDecl>(D)) {
+ D->addAttr(new DLLImportAttr());
return;
}
- FunctionDecl *FD = dyn_cast<FunctionDecl>(d);
+ FunctionDecl *FD = dyn_cast<FunctionDecl>(D);
if (!FD) {
S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type)
- << "dllimport" << "function or variable";
+ << "dllimport" << 2 /*variable and function*/;
return;
}
}
}
- if (d->getAttr<DLLExportAttr>()) {
+ if (D->getAttr<DLLExportAttr>()) {
S.Diag(Attr.getLoc(), diag::warn_attribute_ignored) << "dllimport";
return;
}
- d->addAttr(new DLLImportAttr());
+ D->addAttr(new DLLImportAttr());
}
-static void HandleDLLExportAttr(Decl *d, const AttributeList &Attr, Sema &S) {
+static void HandleDLLExportAttr(Decl *D, const AttributeList &Attr, Sema &S) {
// check the attribute arguments.
if (Attr.getNumArgs() != 0) {
S.Diag(Attr.getLoc(), diag::err_attribute_wrong_number_arguments) << 0;
}
// Attribute can be applied only to functions or variables.
- if (isa<VarDecl>(d)) {
- d->addAttr(new DLLExportAttr());
+ if (isa<VarDecl>(D)) {
+ D->addAttr(new DLLExportAttr());
return;
}
- FunctionDecl *FD = dyn_cast<FunctionDecl>(d);
+ FunctionDecl *FD = dyn_cast<FunctionDecl>(D);
if (!FD) {
S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type)
- << "dllexport" << "function or variable";
+ << "dllexport" << 2 /*variable and function*/;
return;
}
return;
}
- d->addAttr(new DLLExportAttr());
+ D->addAttr(new DLLExportAttr());
}
-static void HandleSectionAttr(Decl *d, const AttributeList &Attr, Sema &S) {
+static void HandleSectionAttr(Decl *D, const AttributeList &Attr, Sema &S) {
// Attribute has no arguments.
if (Attr.getNumArgs() != 1) {
S.Diag(Attr.getLoc(), diag::err_attribute_wrong_number_arguments) << 1;
S.Diag(Attr.getLoc(), diag::err_attribute_annotate_no_string);
return;
}
- d->addAttr(new SectionAttr(std::string(SE->getStrData(),
+ D->addAttr(new SectionAttr(std::string(SE->getStrData(),
SE->getByteLength())));
}
// Attribute can be applied only to functions.
if (!isa<FunctionDecl>(d)) {
S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type)
- << "stdcall" << "function";
+ << "stdcall" << 0 /*function*/;
return;
}
if (!isa<FunctionDecl>(d)) {
S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type)
- << "fastcall" << "function";
+ << "fastcall" << 0 /*function*/;
return;
}
if (!isFunctionOrMethod(d) || !hasFunctionProto(d)) {
S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type)
- << "format" << "function";
+ << "format" << 0 /*function*/;
return;
}
TypedefDecl *TD = dyn_cast<TypedefDecl>(d);
if (!TD || !TD->getUnderlyingType()->isUnionType()) {
S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type)
- << "transparent_union" << "union";
+ << "transparent_union" << 1 /*union*/;
return;
}
if (!isa<FunctionDecl>(d)) {
S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type)
- << "nodebug" << "function";
+ << "nodebug" << 0 /*function*/;
return;
}
case AttributeList::AT_used: HandleUsedAttr (D, Attr, S); break;
case AttributeList::AT_vector_size: HandleVectorSizeAttr(D, Attr, S); break;
case AttributeList::AT_visibility: HandleVisibilityAttr(D, Attr, S); break;
+ case AttributeList::AT_warn_unused_result: HandleWarnUnusedResult(D,Attr,S);
+ break;
case AttributeList::AT_weak: HandleWeakAttr (D, Attr, S); break;
case AttributeList::AT_transparent_union:
HandleTransparentUnionAttr(D, Attr, S);
Expr *E = dyn_cast<Expr>(Elts[i]);
if (!E) continue;
- // Warn about expressions with unused results.
- if (E->hasLocalSideEffect() || E->getType()->isVoidType())
+ // Warn about expressions with unused results if they are non-void and if
+ // this not the last stmt in a stmt expr.
+ if (E->getType()->isVoidType() || (isStmtExpr && i == NumElts-1))
continue;
- // The last expr in a stmt expr really is used.
- if (isStmtExpr && i == NumElts-1)
+ SourceLocation Loc;
+ SourceRange R1, R2;
+ if (!E->isUnusedResultAWarning(Loc, R1, R2))
continue;
-
- /// DiagnoseDeadExpr - This expression is side-effect free and evaluated in
- /// a context where the result is unused. Emit a diagnostic to warn about
- /// this.
- if (const BinaryOperator *BO = dyn_cast<BinaryOperator>(E))
- Diag(BO->getOperatorLoc(), diag::warn_unused_expr)
- << BO->getLHS()->getSourceRange() << BO->getRHS()->getSourceRange();
- else if (const UnaryOperator *UO = dyn_cast<UnaryOperator>(E))
- Diag(UO->getOperatorLoc(), diag::warn_unused_expr)
- << UO->getSubExpr()->getSourceRange();
- else
- Diag(E->getExprLoc(), diag::warn_unused_expr) << E->getSourceRange();
+
+ Diag(Loc, diag::warn_unused_expr) << R1 << R2;
}
return Owned(new (Context) CompoundStmt(Context, Elts, NumElts, L, R));
--- /dev/null
+// RUN: clang %s -fsyntax-only -verify
+// rdar://6587766
+
+int fn1() __attribute__ ((warn_unused_result));
+int fn2() __attribute__ ((pure));
+int fn3() __attribute__ ((const));
+
+int foo() {
+ if (fn1() < 0 || fn2(2,1) < 0 || fn3(2) < 0) // no warnings
+ return -1;
+
+ fn1(); // expected-warning {{expression result unused}}
+ fn2(92, 21); // expected-warning {{expression result unused}}
+ fn3(42); // expected-warning {{expression result unused}}
+ return 0;
+}
+
+int bar __attribute__ ((warn_unused_result)); // expected-warning {{warning: 'warn_unused_result' attribute only applies to function types}}
+
void __attribute__((dllexport)) foo5();
void __attribute__((dllimport)) foo5(); // expected-warning{{dllimport attribute ignored}}
-typedef int __attribute__((dllexport)) type6; // expected-warning{{'dllexport' attribute only applies to function or variable types}}
+typedef int __attribute__((dllexport)) type6; // expected-warning{{'dllexport' attribute only applies to variable and function types}}
-typedef int __attribute__((dllimport)) type7; // expected-warning{{'dllimport' attribute only applies to function or variable}}
+typedef int __attribute__((dllimport)) type7; // expected-warning{{'dllimport' attribute only applies to variable and function}}
void __attribute__((dllimport)) foo6();
void foo6(){} // expected-warning {{'foo6' redeclared without dllimport attribute: previous dllimport ignored}}