diff options
author | rdivacky <rdivacky@FreeBSD.org> | 2010-01-23 11:10:26 +0000 |
---|---|---|
committer | rdivacky <rdivacky@FreeBSD.org> | 2010-01-23 11:10:26 +0000 |
commit | 2fce988e86bc01829142e4362d4eff1af0925147 (patch) | |
tree | c69d3f4f13d508570bb5257a6aea735f88bdf09c /lib/Sema/SemaChecking.cpp | |
parent | a3fa5c7f1b5e2ba4d6ec033dc0e2376326b05824 (diff) | |
download | FreeBSD-src-2fce988e86bc01829142e4362d4eff1af0925147.zip FreeBSD-src-2fce988e86bc01829142e4362d4eff1af0925147.tar.gz |
Update clang to r94309.
Diffstat (limited to 'lib/Sema/SemaChecking.cpp')
-rw-r--r-- | lib/Sema/SemaChecking.cpp | 527 |
1 files changed, 527 insertions, 0 deletions
diff --git a/lib/Sema/SemaChecking.cpp b/lib/Sema/SemaChecking.cpp index 5f124e4..6ff8b1d 100644 --- a/lib/Sema/SemaChecking.cpp +++ b/lib/Sema/SemaChecking.cpp @@ -13,14 +13,22 @@ //===----------------------------------------------------------------------===// #include "Sema.h" +#include "clang/Analysis/CFG.h" +#include "clang/Analysis/PathSensitive/AnalysisContext.h" #include "clang/AST/ASTContext.h" #include "clang/AST/CharUnits.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/ExprCXX.h" #include "clang/AST/ExprObjC.h" +#include "clang/AST/DeclObjC.h" +#include "clang/AST/StmtCXX.h" +#include "clang/AST/StmtObjC.h" #include "clang/Lex/LiteralSupport.h" #include "clang/Lex/Preprocessor.h" +#include "llvm/ADT/BitVector.h" +#include "llvm/ADT/STLExtras.h" #include <limits> +#include <queue> using namespace clang; /// getLocationOfStringLiteralByte - Return a source location that points to the @@ -2029,3 +2037,522 @@ void Sema::CheckImplicitConversion(Expr *E, QualType T) { return; } +// MarkLive - Mark all the blocks reachable from e as live. Returns the total +// number of blocks just marked live. +static unsigned MarkLive(CFGBlock *e, llvm::BitVector &live) { + unsigned count = 0; + std::queue<CFGBlock*> workq; + // Prep work queue + live.set(e->getBlockID()); + ++count; + workq.push(e); + // Solve + while (!workq.empty()) { + CFGBlock *item = workq.front(); + workq.pop(); + for (CFGBlock::succ_iterator I=item->succ_begin(), + E=item->succ_end(); + I != E; + ++I) { + if ((*I) && !live[(*I)->getBlockID()]) { + live.set((*I)->getBlockID()); + ++count; + workq.push(*I); + } + } + } + return count; +} + +static SourceLocation GetUnreachableLoc(CFGBlock &b, SourceRange &R1, + SourceRange &R2) { + Stmt *S; + unsigned sn = 0; + R1 = R2 = SourceRange(); + + top: + if (sn < b.size()) + S = b[sn].getStmt(); + else if (b.getTerminator()) + S = b.getTerminator(); + else + return SourceLocation(); + + switch (S->getStmtClass()) { + case Expr::BinaryOperatorClass: { + BinaryOperator *BO = cast<BinaryOperator>(S); + if (BO->getOpcode() == BinaryOperator::Comma) { + if (sn+1 < b.size()) + return b[sn+1].getStmt()->getLocStart(); + CFGBlock *n = &b; + while (1) { + if (n->getTerminator()) + return n->getTerminator()->getLocStart(); + if (n->succ_size() != 1) + return SourceLocation(); + n = n[0].succ_begin()[0]; + if (n->pred_size() != 1) + return SourceLocation(); + if (!n->empty()) + return n[0][0].getStmt()->getLocStart(); + } + } + R1 = BO->getLHS()->getSourceRange(); + R2 = BO->getRHS()->getSourceRange(); + return BO->getOperatorLoc(); + } + case Expr::UnaryOperatorClass: { + const UnaryOperator *UO = cast<UnaryOperator>(S); + R1 = UO->getSubExpr()->getSourceRange(); + return UO->getOperatorLoc(); + } + case Expr::CompoundAssignOperatorClass: { + const CompoundAssignOperator *CAO = cast<CompoundAssignOperator>(S); + R1 = CAO->getLHS()->getSourceRange(); + R2 = CAO->getRHS()->getSourceRange(); + return CAO->getOperatorLoc(); + } + case Expr::ConditionalOperatorClass: { + const ConditionalOperator *CO = cast<ConditionalOperator>(S); + return CO->getQuestionLoc(); + } + case Expr::MemberExprClass: { + const MemberExpr *ME = cast<MemberExpr>(S); + R1 = ME->getSourceRange(); + return ME->getMemberLoc(); + } + case Expr::ArraySubscriptExprClass: { + const ArraySubscriptExpr *ASE = cast<ArraySubscriptExpr>(S); + R1 = ASE->getLHS()->getSourceRange(); + R2 = ASE->getRHS()->getSourceRange(); + return ASE->getRBracketLoc(); + } + case Expr::CStyleCastExprClass: { + const CStyleCastExpr *CSC = cast<CStyleCastExpr>(S); + R1 = CSC->getSubExpr()->getSourceRange(); + return CSC->getLParenLoc(); + } + case Expr::CXXFunctionalCastExprClass: { + const CXXFunctionalCastExpr *CE = cast <CXXFunctionalCastExpr>(S); + R1 = CE->getSubExpr()->getSourceRange(); + return CE->getTypeBeginLoc(); + } + case Expr::ImplicitCastExprClass: + ++sn; + goto top; + case Stmt::CXXTryStmtClass: { + return cast<CXXTryStmt>(S)->getHandler(0)->getCatchLoc(); + } + default: ; + } + R1 = S->getSourceRange(); + return S->getLocStart(); +} + +static SourceLocation MarkLiveTop(CFGBlock *e, llvm::BitVector &live, + SourceManager &SM) { + std::queue<CFGBlock*> workq; + // Prep work queue + workq.push(e); + SourceRange R1, R2; + SourceLocation top = GetUnreachableLoc(*e, R1, R2); + bool FromMainFile = false; + bool FromSystemHeader = false; + bool TopValid = false; + if (top.isValid()) { + FromMainFile = SM.isFromMainFile(top); + FromSystemHeader = SM.isInSystemHeader(top); + TopValid = true; + } + // Solve + while (!workq.empty()) { + CFGBlock *item = workq.front(); + workq.pop(); + SourceLocation c = GetUnreachableLoc(*item, R1, R2); + if (c.isValid() + && (!TopValid + || (SM.isFromMainFile(c) && !FromMainFile) + || (FromSystemHeader && !SM.isInSystemHeader(c)) + || SM.isBeforeInTranslationUnit(c, top))) { + top = c; + FromMainFile = SM.isFromMainFile(top); + FromSystemHeader = SM.isInSystemHeader(top); + } + live.set(item->getBlockID()); + for (CFGBlock::succ_iterator I=item->succ_begin(), + E=item->succ_end(); + I != E; + ++I) { + if ((*I) && !live[(*I)->getBlockID()]) { + live.set((*I)->getBlockID()); + workq.push(*I); + } + } + } + return top; +} + +static int LineCmp(const void *p1, const void *p2) { + SourceLocation *Line1 = (SourceLocation *)p1; + SourceLocation *Line2 = (SourceLocation *)p2; + return !(*Line1 < *Line2); +} + +namespace { + struct ErrLoc { + SourceLocation Loc; + SourceRange R1; + SourceRange R2; + ErrLoc(SourceLocation l, SourceRange r1, SourceRange r2) + : Loc(l), R1(r1), R2(r2) { } + }; +} + +/// CheckUnreachable - Check for unreachable code. +void Sema::CheckUnreachable(AnalysisContext &AC) { + unsigned count; + // We avoid checking when there are errors, as the CFG won't faithfully match + // the user's code. + if (getDiagnostics().hasErrorOccurred()) + return; + if (Diags.getDiagnosticLevel(diag::warn_unreachable) == Diagnostic::Ignored) + return; + + CFG *cfg = AC.getCFG(); + if (cfg == 0) + return; + + llvm::BitVector live(cfg->getNumBlockIDs()); + // Mark all live things first. + count = MarkLive(&cfg->getEntry(), live); + + if (count == cfg->getNumBlockIDs()) + // If there are no dead blocks, we're done. + return; + + SourceRange R1, R2; + + llvm::SmallVector<ErrLoc, 24> lines; + bool AddEHEdges = AC.getAddEHEdges(); + // First, give warnings for blocks with no predecessors, as they + // can't be part of a loop. + for (CFG::iterator I = cfg->begin(), E = cfg->end(); I != E; ++I) { + CFGBlock &b = **I; + if (!live[b.getBlockID()]) { + if (b.pred_begin() == b.pred_end()) { + if (!AddEHEdges && b.getTerminator() + && isa<CXXTryStmt>(b.getTerminator())) { + // When not adding EH edges from calls, catch clauses + // can otherwise seem dead. Avoid noting them as dead. + count += MarkLive(&b, live); + continue; + } + SourceLocation c = GetUnreachableLoc(b, R1, R2); + if (!c.isValid()) { + // Blocks without a location can't produce a warning, so don't mark + // reachable blocks from here as live. + live.set(b.getBlockID()); + ++count; + continue; + } + lines.push_back(ErrLoc(c, R1, R2)); + // Avoid excessive errors by marking everything reachable from here + count += MarkLive(&b, live); + } + } + } + + if (count < cfg->getNumBlockIDs()) { + // And then give warnings for the tops of loops. + for (CFG::iterator I = cfg->begin(), E = cfg->end(); I != E; ++I) { + CFGBlock &b = **I; + if (!live[b.getBlockID()]) + // Avoid excessive errors by marking everything reachable from here + lines.push_back(ErrLoc(MarkLiveTop(&b, live, Context.getSourceManager()), SourceRange(), SourceRange())); + } + } + + llvm::array_pod_sort(lines.begin(), lines.end(), LineCmp); + for (llvm::SmallVector<ErrLoc, 24>::iterator I = lines.begin(), + E = lines.end(); + I != E; + ++I) + if (I->Loc.isValid()) + Diag(I->Loc, diag::warn_unreachable) << I->R1 << I->R2; +} + +/// CheckFallThrough - Check that we don't fall off the end of a +/// Statement that should return a value. +/// +/// \returns AlwaysFallThrough iff we always fall off the end of the statement, +/// MaybeFallThrough iff we might or might not fall off the end, +/// NeverFallThroughOrReturn iff we never fall off the end of the statement or +/// return. We assume NeverFallThrough iff we never fall off the end of the +/// statement but we may return. We assume that functions not marked noreturn +/// will return. +Sema::ControlFlowKind Sema::CheckFallThrough(AnalysisContext &AC) { + CFG *cfg = AC.getCFG(); + if (cfg == 0) + // FIXME: This should be NeverFallThrough + return NeverFallThroughOrReturn; + + // The CFG leaves in dead things, and we don't want the dead code paths to + // confuse us, so we mark all live things first. + std::queue<CFGBlock*> workq; + llvm::BitVector live(cfg->getNumBlockIDs()); + unsigned count = MarkLive(&cfg->getEntry(), live); + + bool AddEHEdges = AC.getAddEHEdges(); + if (!AddEHEdges && count != cfg->getNumBlockIDs()) + // When there are things remaining dead, and we didn't add EH edges + // from CallExprs to the catch clauses, we have to go back and + // mark them as live. + for (CFG::iterator I = cfg->begin(), E = cfg->end(); I != E; ++I) { + CFGBlock &b = **I; + if (!live[b.getBlockID()]) { + if (b.pred_begin() == b.pred_end()) { + if (b.getTerminator() && isa<CXXTryStmt>(b.getTerminator())) + // When not adding EH edges from calls, catch clauses + // can otherwise seem dead. Avoid noting them as dead. + count += MarkLive(&b, live); + continue; + } + } + } + + // Now we know what is live, we check the live precessors of the exit block + // and look for fall through paths, being careful to ignore normal returns, + // and exceptional paths. + bool HasLiveReturn = false; + bool HasFakeEdge = false; + bool HasPlainEdge = false; + bool HasAbnormalEdge = false; + for (CFGBlock::pred_iterator I=cfg->getExit().pred_begin(), + E = cfg->getExit().pred_end(); + I != E; + ++I) { + CFGBlock& B = **I; + if (!live[B.getBlockID()]) + continue; + if (B.size() == 0) { + if (B.getTerminator() && isa<CXXTryStmt>(B.getTerminator())) { + HasAbnormalEdge = true; + continue; + } + + // A labeled empty statement, or the entry block... + HasPlainEdge = true; + continue; + } + Stmt *S = B[B.size()-1]; + if (isa<ReturnStmt>(S)) { + HasLiveReturn = true; + continue; + } + if (isa<ObjCAtThrowStmt>(S)) { + HasFakeEdge = true; + continue; + } + if (isa<CXXThrowExpr>(S)) { + HasFakeEdge = true; + continue; + } + if (const AsmStmt *AS = dyn_cast<AsmStmt>(S)) { + if (AS->isMSAsm()) { + HasFakeEdge = true; + HasLiveReturn = true; + continue; + } + } + if (isa<CXXTryStmt>(S)) { + HasAbnormalEdge = true; + continue; + } + + bool NoReturnEdge = false; + if (CallExpr *C = dyn_cast<CallExpr>(S)) { + if (B.succ_begin()[0] != &cfg->getExit()) { + HasAbnormalEdge = true; + continue; + } + Expr *CEE = C->getCallee()->IgnoreParenCasts(); + if (CEE->getType().getNoReturnAttr()) { + NoReturnEdge = true; + HasFakeEdge = true; + } else if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(CEE)) { + ValueDecl *VD = DRE->getDecl(); + if (VD->hasAttr<NoReturnAttr>()) { + NoReturnEdge = true; + HasFakeEdge = true; + } + } + } + // FIXME: Add noreturn message sends. + if (NoReturnEdge == false) + HasPlainEdge = true; + } + if (!HasPlainEdge) { + if (HasLiveReturn) + return NeverFallThrough; + return NeverFallThroughOrReturn; + } + if (HasAbnormalEdge || HasFakeEdge || HasLiveReturn) + return MaybeFallThrough; + // This says AlwaysFallThrough for calls to functions that are not marked + // noreturn, that don't return. If people would like this warning to be more + // accurate, such functions should be marked as noreturn. + return AlwaysFallThrough; +} + +/// CheckFallThroughForFunctionDef - Check that we don't fall off the end of a +/// function that should return a value. Check that we don't fall off the end +/// of a noreturn function. We assume that functions and blocks not marked +/// noreturn will return. +void Sema::CheckFallThroughForFunctionDef(Decl *D, Stmt *Body, + AnalysisContext &AC) { + // FIXME: Would be nice if we had a better way to control cascading errors, + // but for now, avoid them. The problem is that when Parse sees: + // int foo() { return a; } + // The return is eaten and the Sema code sees just: + // int foo() { } + // which this code would then warn about. + if (getDiagnostics().hasErrorOccurred()) + return; + + bool ReturnsVoid = false; + bool HasNoReturn = false; + if (FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) { + // If the result type of the function is a dependent type, we don't know + // whether it will be void or not, so don't + if (FD->getResultType()->isDependentType()) + return; + if (FD->getResultType()->isVoidType()) + ReturnsVoid = true; + if (FD->hasAttr<NoReturnAttr>()) + HasNoReturn = true; + } else if (ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) { + if (MD->getResultType()->isVoidType()) + ReturnsVoid = true; + if (MD->hasAttr<NoReturnAttr>()) + HasNoReturn = true; + } + + // Short circuit for compilation speed. + if ((Diags.getDiagnosticLevel(diag::warn_maybe_falloff_nonvoid_function) + == Diagnostic::Ignored || ReturnsVoid) + && (Diags.getDiagnosticLevel(diag::warn_noreturn_function_has_return_expr) + == Diagnostic::Ignored || !HasNoReturn) + && (Diags.getDiagnosticLevel(diag::warn_suggest_noreturn_block) + == Diagnostic::Ignored || !ReturnsVoid)) + return; + // FIXME: Function try block + if (CompoundStmt *Compound = dyn_cast<CompoundStmt>(Body)) { + switch (CheckFallThrough(AC)) { + case MaybeFallThrough: + if (HasNoReturn) + Diag(Compound->getRBracLoc(), diag::warn_falloff_noreturn_function); + else if (!ReturnsVoid) + Diag(Compound->getRBracLoc(),diag::warn_maybe_falloff_nonvoid_function); + break; + case AlwaysFallThrough: + if (HasNoReturn) + Diag(Compound->getRBracLoc(), diag::warn_falloff_noreturn_function); + else if (!ReturnsVoid) + Diag(Compound->getRBracLoc(), diag::warn_falloff_nonvoid_function); + break; + case NeverFallThroughOrReturn: + if (ReturnsVoid && !HasNoReturn) + Diag(Compound->getLBracLoc(), diag::warn_suggest_noreturn_function); + break; + case NeverFallThrough: + break; + } + } +} + +/// CheckFallThroughForBlock - Check that we don't fall off the end of a block +/// that should return a value. Check that we don't fall off the end of a +/// noreturn block. We assume that functions and blocks not marked noreturn +/// will return. +void Sema::CheckFallThroughForBlock(QualType BlockTy, Stmt *Body, + AnalysisContext &AC) { + // FIXME: Would be nice if we had a better way to control cascading errors, + // but for now, avoid them. The problem is that when Parse sees: + // int foo() { return a; } + // The return is eaten and the Sema code sees just: + // int foo() { } + // which this code would then warn about. + if (getDiagnostics().hasErrorOccurred()) + return; + bool ReturnsVoid = false; + bool HasNoReturn = false; + if (const FunctionType *FT =BlockTy->getPointeeType()->getAs<FunctionType>()){ + if (FT->getResultType()->isVoidType()) + ReturnsVoid = true; + if (FT->getNoReturnAttr()) + HasNoReturn = true; + } + + // Short circuit for compilation speed. + if (ReturnsVoid + && !HasNoReturn + && (Diags.getDiagnosticLevel(diag::warn_suggest_noreturn_block) + == Diagnostic::Ignored || !ReturnsVoid)) + return; + // FIXME: Funtion try block + if (CompoundStmt *Compound = dyn_cast<CompoundStmt>(Body)) { + switch (CheckFallThrough(AC)) { + case MaybeFallThrough: + if (HasNoReturn) + Diag(Compound->getRBracLoc(), diag::err_noreturn_block_has_return_expr); + else if (!ReturnsVoid) + Diag(Compound->getRBracLoc(), diag::err_maybe_falloff_nonvoid_block); + break; + case AlwaysFallThrough: + if (HasNoReturn) + Diag(Compound->getRBracLoc(), diag::err_noreturn_block_has_return_expr); + else if (!ReturnsVoid) + Diag(Compound->getRBracLoc(), diag::err_falloff_nonvoid_block); + break; + case NeverFallThroughOrReturn: + if (ReturnsVoid) + Diag(Compound->getLBracLoc(), diag::warn_suggest_noreturn_block); + break; + case NeverFallThrough: + break; + } + } +} + +/// CheckParmsForFunctionDef - Check that the parameters of the given +/// function are appropriate for the definition of a function. This +/// takes care of any checks that cannot be performed on the +/// declaration itself, e.g., that the types of each of the function +/// parameters are complete. +bool Sema::CheckParmsForFunctionDef(FunctionDecl *FD) { + bool HasInvalidParm = false; + for (unsigned p = 0, NumParams = FD->getNumParams(); p < NumParams; ++p) { + ParmVarDecl *Param = FD->getParamDecl(p); + + // C99 6.7.5.3p4: the parameters in a parameter type list in a + // function declarator that is part of a function definition of + // that function shall not have incomplete type. + // + // This is also C++ [dcl.fct]p6. + if (!Param->isInvalidDecl() && + RequireCompleteType(Param->getLocation(), Param->getType(), + diag::err_typecheck_decl_incomplete_type)) { + Param->setInvalidDecl(); + HasInvalidParm = true; + } + + // C99 6.9.1p5: If the declarator includes a parameter type list, the + // declaration of each parameter shall include an identifier. + if (Param->getIdentifier() == 0 && + !Param->isImplicit() && + !getLangOptions().CPlusPlus) + Diag(Param->getLocation(), diag::err_parameter_name_omitted); + } + + return HasInvalidParm; +} |