diff options
Diffstat (limited to 'lib')
-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 |
4 files changed, 163 insertions, 83 deletions
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)); |