diff options
-rw-r--r-- | include/clang/AST/Attr.h | 12 | ||||
-rw-r--r-- | include/clang/AST/Expr.h | 14 | ||||
-rw-r--r-- | include/clang/AST/Stmt.h | 13 | ||||
-rw-r--r-- | include/clang/Basic/DiagnosticSemaKinds.def | 3 | ||||
-rw-r--r-- | lib/AST/Expr.cpp | 141 | ||||
-rw-r--r-- | lib/Parse/AttributeList.cpp | 3 | ||||
-rw-r--r-- | lib/Sema/SemaDeclAttr.cpp | 78 | ||||
-rw-r--r-- | lib/Sema/SemaStmt.cpp | 24 | ||||
-rw-r--r-- | test/Sema/attr-warn_unused_result.c | 19 | ||||
-rw-r--r-- | test/Sema/dllimport-dllexport.c | 4 |
10 files changed, 214 insertions, 97 deletions
diff --git a/include/clang/AST/Attr.h b/include/clang/AST/Attr.h index 09492dba66..fcfc54e2f6 100644 --- a/include/clang/AST/Attr.h +++ b/include/clang/AST/Attr.h @@ -52,6 +52,7 @@ public: Unused, Used, Visibility, + WarnUnusedResult, Weak, Blocks, Const, @@ -219,7 +220,6 @@ public: 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; } }; @@ -229,7 +229,6 @@ public: 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; } }; @@ -514,6 +513,15 @@ public: 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 diff --git a/include/clang/AST/Expr.h b/include/clang/AST/Expr.h index 05d088b759..2baaa438d0 100644 --- a/include/clang/AST/Expr.h +++ b/include/clang/AST/Expr.h @@ -110,9 +110,12 @@ public: /// 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: @@ -734,6 +737,7 @@ public: return SourceRange(getLHS()->getLocStart(), RBracketLoc); } + SourceLocation getRBracketLoc() const { return RBracketLoc; } virtual SourceLocation getExprLoc() const { return getBase()->getExprLoc(); } static bool classof(const Stmt *T) { @@ -874,6 +878,7 @@ public: 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); @@ -1318,6 +1323,9 @@ public: return SourceRange(LParenLoc, RParenLoc); } + SourceLocation getLParenLoc() const { return LParenLoc; } + SourceLocation getRParenLoc() const { return RParenLoc; } + static bool classof(const Stmt *T) { return T->getStmtClass() == StmtExprClass; } diff --git a/include/clang/AST/Stmt.h b/include/clang/AST/Stmt.h index 66eb303c9d..3da42ff076 100644 --- a/include/clang/AST/Stmt.h +++ b/include/clang/AST/Stmt.h @@ -345,12 +345,13 @@ public: 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; } diff --git a/include/clang/Basic/DiagnosticSemaKinds.def b/include/clang/Basic/DiagnosticSemaKinds.def index d109539d0e..ee9a8d388b 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.def +++ b/include/clang/Basic/DiagnosticSemaKinds.def @@ -353,7 +353,8 @@ DIAG(warn_attribute_ignored, WARNING, 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, diff --git a/lib/AST/Expr.cpp b/lib/AST/Expr.cpp index e294f48029..7063b768c5 100644 --- a/lib/AST/Expr.cpp +++ b/lib/AST/Expr.cpp @@ -280,69 +280,118 @@ Stmt *BlockExpr::getBody() { return TheBlock->getBody(); } // 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. @@ -352,29 +401,45 @@ bool Expr::hasLocalSideEffect() const { 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; } } diff --git a/lib/Parse/AttributeList.cpp b/lib/Parse/AttributeList.cpp index 5b70a5908d..6ce55ea5c5 100644 --- a/lib/Parse/AttributeList.cpp +++ b/lib/Parse/AttributeList.cpp @@ -113,6 +113,9 @@ AttributeList::Kind AttributeList::getKind(const IdentifierInfo *Name) { 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; diff --git a/lib/Sema/SemaDeclAttr.cpp b/lib/Sema/SemaDeclAttr.cpp index 3ea2b5064c..347d8f5e07 100644 --- a/lib/Sema/SemaDeclAttr.cpp +++ b/lib/Sema/SemaDeclAttr.cpp @@ -298,7 +298,7 @@ static void HandleNonNullAttr(Decl *d, const AttributeList &Attr, Sema &S) { // 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; } @@ -408,7 +408,7 @@ static void HandleNoReturnAttr(Decl *d, const AttributeList &Attr, Sema &S) { if (!isFunctionOrMethod(d)) { S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) - << "noreturn" << "function"; + << "noreturn" << 0 /*function*/; return; } @@ -424,7 +424,7 @@ static void HandleUnusedAttr(Decl *d, const AttributeList &Attr, Sema &S) { 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; } @@ -445,7 +445,7 @@ static void HandleUsedAttr(Decl *d, const AttributeList &Attr, Sema &S) { } } else if (!isFunctionOrMethod(d)) { S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) - << "used" << "variable and function"; + << "used" << 2 /*variable and function*/; return; } @@ -475,7 +475,7 @@ static void HandleConstructorAttr(Decl *d, const AttributeList &Attr, Sema &S) { FunctionDecl *Fn = dyn_cast<FunctionDecl>(d); if (!Fn) { S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) - << "constructor" << "function"; + << "constructor" << 0 /*function*/; return; } @@ -504,7 +504,7 @@ static void HandleDestructorAttr(Decl *d, const AttributeList &Attr, Sema &S) { if (!isa<FunctionDecl>(d)) { S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) - << "destructor" << "function"; + << "destructor" << 0 /*function*/; return; } @@ -712,24 +712,42 @@ static void HandleSentinelAttr(Decl *d, const AttributeList &Attr, Sema &S) { } } 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; @@ -737,15 +755,15 @@ static void HandleDLLImportAttr(Decl *d, const AttributeList &Attr, Sema &S) { } // 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; } @@ -766,15 +784,15 @@ static void HandleDLLImportAttr(Decl *d, const AttributeList &Attr, Sema &S) { } } - 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; @@ -782,15 +800,15 @@ static void HandleDLLExportAttr(Decl *d, const AttributeList &Attr, Sema &S) { } // 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; } @@ -802,10 +820,10 @@ static void HandleDLLExportAttr(Decl *d, const AttributeList &Attr, Sema &S) { 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; @@ -821,7 +839,7 @@ static void HandleSectionAttr(Decl *d, const AttributeList &Attr, Sema &S) { 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()))); } @@ -835,7 +853,7 @@ static void HandleStdCallAttr(Decl *d, const AttributeList &Attr, Sema &S) { // 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; } @@ -858,7 +876,7 @@ static void HandleFastCallAttr(Decl *d, const AttributeList &Attr, Sema &S) { if (!isa<FunctionDecl>(d)) { S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) - << "fastcall" << "function"; + << "fastcall" << 0 /*function*/; return; } @@ -977,7 +995,7 @@ static void HandleFormatAttr(Decl *d, const AttributeList &Attr, Sema &S) { if (!isFunctionOrMethod(d) || !hasFunctionProto(d)) { S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) - << "format" << "function"; + << "format" << 0 /*function*/; return; } @@ -1116,7 +1134,7 @@ static void HandleTransparentUnionAttr(Decl *d, const AttributeList &Attr, 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; } @@ -1334,7 +1352,7 @@ static void HandleNodebugAttr(Decl *d, const AttributeList &Attr, Sema &S) { if (!isa<FunctionDecl>(d)) { S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) - << "nodebug" << "function"; + << "nodebug" << 0 /*function*/; return; } @@ -1381,6 +1399,8 @@ static void ProcessDeclAttribute(Decl *D, const AttributeList &Attr, Sema &S) { 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); diff --git a/lib/Sema/SemaStmt.cpp b/lib/Sema/SemaStmt.cpp index bfa1c5e6d0..c2c1a0f56f 100644 --- a/lib/Sema/SemaStmt.cpp +++ b/lib/Sema/SemaStmt.cpp @@ -95,25 +95,17 @@ Sema::ActOnCompoundStmt(SourceLocation L, SourceLocation R, 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)); diff --git a/test/Sema/attr-warn_unused_result.c b/test/Sema/attr-warn_unused_result.c new file mode 100644 index 0000000000..38f201ca82 --- /dev/null +++ b/test/Sema/attr-warn_unused_result.c @@ -0,0 +1,19 @@ +// 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}} + diff --git a/test/Sema/dllimport-dllexport.c b/test/Sema/dllimport-dllexport.c index 0f42ff388a..3f385d16c1 100644 --- a/test/Sema/dllimport-dllexport.c +++ b/test/Sema/dllimport-dllexport.c @@ -10,9 +10,9 @@ void __attribute__((dllimport, dllexport)) foo4(); // expected-warning{{dllimpor 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}} |