]> granicus.if.org Git - clang/commitdiff
Several related changes:
authorChris Lattner <sabre@nondot.org>
Sat, 14 Feb 2009 07:37:35 +0000 (07:37 +0000)
committerChris Lattner <sabre@nondot.org>
Sat, 14 Feb 2009 07:37:35 +0000 (07:37 +0000)
1) implement parser and sema support for reading and verifying attribute(warnunusedresult).
2) rename hasLocalSideEffect to isUnusedResultAWarning, inverting the sense
   of its result.
3) extend isUnusedResultAWarning to directly return the loc and range
   info that should be reported to the user.  Make it substantially more
   precise in some cases than what was previously reported.
4) teach isUnusedResultAWarning about CallExpr to decls that are
   pure/const/warnunusedresult, fixing a fixme.
5) change warn_attribute_wrong_decl_type to not pass in english strings, instead,
   pass in integers and use %select.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@64543 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/AST/Attr.h
include/clang/AST/Expr.h
include/clang/AST/Stmt.h
include/clang/Basic/DiagnosticSemaKinds.def
lib/AST/Expr.cpp
lib/Parse/AttributeList.cpp
lib/Sema/SemaDeclAttr.cpp
lib/Sema/SemaStmt.cpp
test/Sema/attr-warn_unused_result.c [new file with mode: 0644]
test/Sema/dllimport-dllexport.c

index 09492dba669ca8400cb926d970b53a2a4b2ab4a8..fcfc54e2f6a37c37a43f3c8ece985e3f3cd7d304 100644 (file)
@@ -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
index 05d088b75938363ed1e4855da4f3bd90a70f07d3..2baaa438d02bffe83db18a6de9de473bf6a5b1ba 100644 (file)
@@ -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; 
   }
index 66eb303c9d753ea4100d5fc4bf52284e3b89ae78..3da42ff0765bc0c8fd94b3146dc949b13e0c6960 100644 (file)
@@ -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; }
index d109539d0e7a25b7c21be8609cf1a9e6bd4a24d3..ee9a8d388bfe6060b6ed9ca3307d7d3a071717c6 100644 (file)
@@ -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,
index e294f480299d8523ecf7b1319fd8f4514a303272..7063b768c531ef5227bb1ea52dd1feae2ef95d52 100644 (file)
@@ -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;
   }
 }
 
index 5b70a5908db946304052e029a337fea085e51b9a..6ce55ea5c59de9be59abfad0c2d6e5499dbf42bc 100644 (file)
@@ -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;
index 3ea2b5064c482154615de6e693953a3e97ba121e..347d8f5e0739f978e2a0d3b519583e3cfe0e2983 100644 (file)
@@ -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);
index bfa1c5e6d0500eae6f08191848e394ad52fd02ed..c2c1a0f56f66bb76db73ce5a0650ad7e3ae28789 100644 (file)
@@ -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 (file)
index 0000000..38f201c
--- /dev/null
@@ -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}}
+
index 0f42ff388aace065e708cf7441293234b51c1f33..3f385d16c18c4a3ff817d604fa8541735fca053b 100644 (file)
@@ -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}}