diff options
Diffstat (limited to 'clang/lib')
| -rw-r--r-- | clang/lib/Sema/AnalysisBasedWarnings.cpp | 362 | ||||
| -rw-r--r-- | clang/lib/Sema/AnalysisBasedWarnings.h | 35 | ||||
| -rw-r--r-- | clang/lib/Sema/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | clang/lib/Sema/Sema.h | 10 | ||||
| -rw-r--r-- | clang/lib/Sema/SemaChecking.cpp | 274 | ||||
| -rw-r--r-- | clang/lib/Sema/SemaDecl.cpp | 36 | ||||
| -rw-r--r-- | clang/lib/Sema/SemaExpr.cpp | 9 | 
7 files changed, 425 insertions, 302 deletions
diff --git a/clang/lib/Sema/AnalysisBasedWarnings.cpp b/clang/lib/Sema/AnalysisBasedWarnings.cpp new file mode 100644 index 00000000000..9eb5b7f5a58 --- /dev/null +++ b/clang/lib/Sema/AnalysisBasedWarnings.cpp @@ -0,0 +1,362 @@ +//=- AnalysisBasedWarnings.cpp - Sema warnings based on libAnalysis -*- C++ -*-=// +// +//                     The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines analysis_warnings::[Policy,Executor]. +// Together they are used by Sema to issue warnings based on inexpensive +// static analysis algorithms in libAnalysis. +// +//===----------------------------------------------------------------------===// + +#include "Sema.h" +#include "AnalysisBasedWarnings.h" +#include "clang/AST/ExprObjC.h" +#include "clang/AST/ExprCXX.h" +#include "clang/AST/StmtObjC.h" +#include "clang/AST/StmtCXX.h" +#include "clang/Analysis/AnalysisContext.h" +#include "clang/Analysis/CFG.h" +#include "clang/Analysis/Analyses/ReachableCode.h" +#include "llvm/ADT/BitVector.h" +#include "llvm/Support/Casting.h" +#include <queue> + +using namespace clang; + +//===----------------------------------------------------------------------===// +// Unreachable code analysis. +//===----------------------------------------------------------------------===// + +namespace { +  class UnreachableCodeHandler : public reachable_code::Callback { +    Sema &S; +  public: +    UnreachableCodeHandler(Sema &s) : S(s) {} + +    void HandleUnreachable(SourceLocation L, SourceRange R1, SourceRange R2) { +      S.Diag(L, diag::warn_unreachable) << R1 << R2; +    } +  }; +} + +/// CheckUnreachable - Check for unreachable code. +static void CheckUnreachable(Sema &S, AnalysisContext &AC) { +  UnreachableCodeHandler UC(S); +  reachable_code::FindUnreachableCode(AC, UC); +} + +//===----------------------------------------------------------------------===// +// Check for missing return value. +//===----------------------------------------------------------------------===// + +enum ControlFlowKind { NeverFallThrough = 0, MaybeFallThrough = 1, +  AlwaysFallThrough = 2, NeverFallThroughOrReturn = 3 }; + +/// 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. +static ControlFlowKind 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 = reachable_code::ScanReachableFromBlock(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 += reachable_code::ScanReachableFromBlock(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; +} + +struct CheckFallThroughDiagnostics { +  unsigned diag_MaybeFallThrough_HasNoReturn; +  unsigned diag_MaybeFallThrough_ReturnsNonVoid; +  unsigned diag_AlwaysFallThrough_HasNoReturn; +  unsigned diag_AlwaysFallThrough_ReturnsNonVoid; +  unsigned diag_NeverFallThroughOrReturn; +  bool funMode; +   +  static CheckFallThroughDiagnostics MakeForFunction() { +    CheckFallThroughDiagnostics D; +    D.diag_MaybeFallThrough_HasNoReturn = +      diag::warn_falloff_noreturn_function; +    D.diag_MaybeFallThrough_ReturnsNonVoid = +      diag::warn_maybe_falloff_nonvoid_function; +    D.diag_AlwaysFallThrough_HasNoReturn = +      diag::warn_falloff_noreturn_function; +    D.diag_AlwaysFallThrough_ReturnsNonVoid = +      diag::warn_falloff_nonvoid_function; +    D.diag_NeverFallThroughOrReturn = +      diag::warn_suggest_noreturn_function; +    D.funMode = true; +    return D; +  } +   +  static CheckFallThroughDiagnostics MakeForBlock() { +    CheckFallThroughDiagnostics D; +    D.diag_MaybeFallThrough_HasNoReturn = +      diag::err_noreturn_block_has_return_expr; +    D.diag_MaybeFallThrough_ReturnsNonVoid = +      diag::err_maybe_falloff_nonvoid_block; +    D.diag_AlwaysFallThrough_HasNoReturn = +      diag::err_noreturn_block_has_return_expr; +    D.diag_AlwaysFallThrough_ReturnsNonVoid = +      diag::err_falloff_nonvoid_block; +    D.diag_NeverFallThroughOrReturn = +      diag::warn_suggest_noreturn_block; +    D.funMode = false; +    return D; +  } +   +  bool checkDiagnostics(Diagnostic &D, bool ReturnsVoid, +                        bool HasNoReturn) const { +    if (funMode) { +      return (D.getDiagnosticLevel(diag::warn_maybe_falloff_nonvoid_function) +              == Diagnostic::Ignored || ReturnsVoid) +        && (D.getDiagnosticLevel(diag::warn_noreturn_function_has_return_expr) +              == Diagnostic::Ignored || !HasNoReturn) +        && (D.getDiagnosticLevel(diag::warn_suggest_noreturn_block) +              == Diagnostic::Ignored || !ReturnsVoid); +    } +     +    // For blocks. +    return  ReturnsVoid && !HasNoReturn +            && (D.getDiagnosticLevel(diag::warn_suggest_noreturn_block) +                == Diagnostic::Ignored || !ReturnsVoid); +  } +}; + +/// 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. +static void CheckFallThroughForBody(Sema &S, const Decl *D, const Stmt *Body, +                                    QualType BlockTy, +                                    const CheckFallThroughDiagnostics& CD, +                                    AnalysisContext &AC) { + +  bool ReturnsVoid = false; +  bool HasNoReturn = false; + +  if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) { +    ReturnsVoid = FD->getResultType()->isVoidType(); +    HasNoReturn = FD->hasAttr<NoReturnAttr>() || +                  FD->getType()->getAs<FunctionType>()->getNoReturnAttr(); +  } +  else if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) { +    ReturnsVoid = MD->getResultType()->isVoidType(); +    HasNoReturn = MD->hasAttr<NoReturnAttr>(); +  } +  else if (isa<BlockDecl>(D)) { +    if (const FunctionType *FT =  +          BlockTy->getPointeeType()->getAs<FunctionType>()) { +      if (FT->getResultType()->isVoidType()) +        ReturnsVoid = true; +      if (FT->getNoReturnAttr()) +        HasNoReturn = true; +    } +  } + +  Diagnostic &Diags = S.getDiagnostics(); + +  // Short circuit for compilation speed. +  if (CD.checkDiagnostics(Diags, ReturnsVoid, HasNoReturn)) +      return; +   +  // FIXME: Function try block +  if (const CompoundStmt *Compound = dyn_cast<CompoundStmt>(Body)) { +    switch (CheckFallThrough(AC)) { +      case MaybeFallThrough: +        if (HasNoReturn) +          S.Diag(Compound->getRBracLoc(), +                 CD.diag_MaybeFallThrough_HasNoReturn); +        else if (!ReturnsVoid) +          S.Diag(Compound->getRBracLoc(), +                 CD.diag_MaybeFallThrough_ReturnsNonVoid); +        break; +      case AlwaysFallThrough: +        if (HasNoReturn) +          S.Diag(Compound->getRBracLoc(), +                 CD.diag_AlwaysFallThrough_HasNoReturn); +        else if (!ReturnsVoid) +          S.Diag(Compound->getRBracLoc(), +                 CD.diag_AlwaysFallThrough_ReturnsNonVoid); +        break; +      case NeverFallThroughOrReturn: +        if (ReturnsVoid && !HasNoReturn) +          S.Diag(Compound->getLBracLoc(), +                 CD.diag_NeverFallThroughOrReturn); +        break; +      case NeverFallThrough: +        break; +    } +  } +} + +//===----------------------------------------------------------------------===// +// AnalysisBasedWarnings - Worker object used by Sema to execute analysis-based +//  warnings on a function, method, or block. +//===----------------------------------------------------------------------===// + +clang::sema::AnalysisBasedWarnings::AnalysisBasedWarnings(Sema &s) : S(s) { +  Diagnostic &D = S.getDiagnostics(); + +  enableCheckFallThrough = 1; + +  enableCheckUnreachable = (unsigned) +    (D.getDiagnosticLevel(diag::warn_unreachable) != Diagnostic::Ignored); +} + +void clang::sema::AnalysisBasedWarnings::IssueWarnings(const Decl *D, +                                                       QualType BlockTy) { +   +  assert(BlockTy.isNull() || isa<BlockDecl>(D)); +   +  // We avoid doing analysis-based warnings when there are errors for +  // two reasons: +  // (1) The CFGs often can't be constructed (if the body is invalid), so +  //     don't bother trying. +  // (2) The code already has problems; running the analysis just takes more +  //     time. +  if (S.getDiagnostics().hasErrorOccurred()) +      return; +   +  if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) { +    // For function templates, class templates and member function templates +    // we'll do the analysis at instantiation time. +    if (FD->isDependentContext()) +      return; +  } + +  const Stmt *Body = D->getBody(); +  assert(Body); + +  // Don't generate EH edges for CallExprs as we'd like to avoid the n^2 +  // explosion for destrutors that can result and the compile time hit. +  AnalysisContext AC(D, false); + +  // Warning: check missing 'return' +  if (enableCheckFallThrough) { +    const CheckFallThroughDiagnostics &CD = +      (isa<BlockDecl>(D) ? CheckFallThroughDiagnostics::MakeForBlock() +                         : CheckFallThroughDiagnostics::MakeForFunction()); +    CheckFallThroughForBody(S, D, Body, BlockTy, CD, AC); +  } + +  // Warning: check for unreachable code +  if (enableCheckUnreachable) +    CheckUnreachable(S, AC); +} diff --git a/clang/lib/Sema/AnalysisBasedWarnings.h b/clang/lib/Sema/AnalysisBasedWarnings.h new file mode 100644 index 00000000000..39da1b14d4a --- /dev/null +++ b/clang/lib/Sema/AnalysisBasedWarnings.h @@ -0,0 +1,35 @@ +//=- AnalysisBasedWarnings.h - Sema warnings based on libAnalysis -*- C++ -*-=// +// +//                     The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines AnalysisBasedWarnings, a worker object used by Sema +// that issues warnings based on dataflow-analysis. +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_SEMA_ANALYSIS_WARNINGS_H +#define LLVM_CLANG_SEMA_ANALYSIS_WARNINGS_H + +namespace clang { namespace sema { + +class AnalysisBasedWarnings { +  Sema &S; +  // The warnings to run. +  unsigned enableCheckFallThrough : 1; +  unsigned enableCheckUnreachable : 1; +   +public: + +  AnalysisBasedWarnings(Sema &s); +  void IssueWarnings(const Decl *D, QualType BlockTy = QualType()); +   +  void disableCheckFallThrough() { enableCheckFallThrough = 0; } +}; +   +}} // end namespace clang::sema + +#endif diff --git a/clang/lib/Sema/CMakeLists.txt b/clang/lib/Sema/CMakeLists.txt index 237803a1c45..ac0dfd6c3af 100644 --- a/clang/lib/Sema/CMakeLists.txt +++ b/clang/lib/Sema/CMakeLists.txt @@ -1,6 +1,7 @@  set(LLVM_NO_RTTI 1)  add_clang_library(clangSema +  AnalysisBasedWarnings.cpp    CodeCompleteConsumer.cpp    IdentifierResolver.cpp    JumpDiagnostics.cpp diff --git a/clang/lib/Sema/Sema.h b/clang/lib/Sema/Sema.h index be0fa322578..b529e5b640b 100644 --- a/clang/lib/Sema/Sema.h +++ b/clang/lib/Sema/Sema.h @@ -42,7 +42,6 @@ namespace llvm {  }  namespace clang { -  class AnalysisContext;    class ASTContext;    class ASTConsumer;    class CodeCompleteConsumer; @@ -1296,9 +1295,6 @@ public:    OwningExprResult BuildOverloadedArrowExpr(Scope *S, ExprArg Base,                                              SourceLocation OpLoc); -  /// CheckUnreachable - Check for unreachable code. -  void CheckUnreachable(AnalysisContext &); -    /// CheckCallReturnType - Checks that a call expression's return type is    /// complete. Returns true on failure. The location passed in is the location    /// that best represents the call. @@ -1306,15 +1302,9 @@ public:                             CallExpr *CE, FunctionDecl *FD);    /// Helpers for dealing with blocks and functions. -  void CheckFallThroughForFunctionDef(Decl *D, Stmt *Body, AnalysisContext &); -  void CheckFallThroughForBlock(QualType BlockTy, Stmt *, AnalysisContext &);    bool CheckParmsForFunctionDef(FunctionDecl *FD);    void CheckCXXDefaultArguments(FunctionDecl *FD);    void CheckExtraCXXDefaultArguments(Declarator &D); -  enum ControlFlowKind { NeverFallThrough = 0, MaybeFallThrough = 1, -                         AlwaysFallThrough = 2, NeverFallThroughOrReturn = 3 }; -  ControlFlowKind CheckFallThrough(AnalysisContext &); -    Scope *getNonFieldDeclScope(Scope *S);    /// \name Name lookup diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp index 522a7c10763..0a33485dd77 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -13,9 +13,6 @@  //===----------------------------------------------------------------------===//  #include "Sema.h" -#include "clang/Analysis/AnalysisContext.h" -#include "clang/Analysis/CFG.h" -#include "clang/Analysis/Analyses/ReachableCode.h"  #include "clang/Analysis/Analyses/PrintfFormatString.h"  #include "clang/AST/ASTContext.h"  #include "clang/AST/CharUnits.h" @@ -30,7 +27,6 @@  #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 @@ -2224,276 +2220,6 @@ void Sema::CheckImplicitConversion(Expr *E, QualType T) {    return;  } - - -namespace { -class UnreachableCodeHandler : public reachable_code::Callback { -  Sema &S; -public: -  UnreachableCodeHandler(Sema *s) : S(*s) {} -   -  void HandleUnreachable(SourceLocation L, SourceRange R1, SourceRange R2) { -    S.Diag(L, diag::warn_unreachable) << R1 << R2; -  } -};   -} - -/// CheckUnreachable - Check for unreachable code. -void Sema::CheckUnreachable(AnalysisContext &AC) { -  // We avoid checking when there are errors, as the CFG won't faithfully match -  // the user's code. -  if (getDiagnostics().hasErrorOccurred() || -      Diags.getDiagnosticLevel(diag::warn_unreachable) == Diagnostic::Ignored) -    return; - -  UnreachableCodeHandler UC(this); -  reachable_code::FindUnreachableCode(AC, UC); -} - -/// 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 = reachable_code::ScanReachableFromBlock(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 += reachable_code::ScanReachableFromBlock(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)) { -    // For function templates, class templates and member function templates -    // we'll do the analysis at instantiation time. -    if (FD->isDependentContext()) -      return; - -    ReturnsVoid = FD->getResultType()->isVoidType(); -    HasNoReturn = FD->hasAttr<NoReturnAttr>() || -                  FD->getType()->getAs<FunctionType>()->getNoReturnAttr(); - -  } else if (ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) { -    ReturnsVoid = MD->getResultType()->isVoidType(); -    HasNoReturn = MD->hasAttr<NoReturnAttr>(); -  } - -  // 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 diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index bab3d98e212..e11e161ffde 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -14,7 +14,7 @@  #include "Sema.h"  #include "SemaInit.h"  #include "Lookup.h" -#include "clang/Analysis/AnalysisContext.h" +#include "AnalysisBasedWarnings.h"  #include "clang/AST/APValue.h"  #include "clang/AST/ASTConsumer.h"  #include "clang/AST/ASTContext.h" @@ -4259,9 +4259,6 @@ Sema::DeclPtrTy Sema::ActOnFinishFunctionBody(DeclPtrTy D, StmtArg BodyArg,    Decl *dcl = D.getAs<Decl>();    Stmt *Body = BodyArg.takeAs<Stmt>(); -  // Don't generate EH edges for CallExprs as we'd like to avoid the n^2 -  // explosion for destrutors that can result and the compile time hit. -  AnalysisContext AC(dcl, false);    FunctionDecl *FD = 0;    FunctionTemplateDecl *FunTmpl = dyn_cast_or_null<FunctionTemplateDecl>(dcl);    if (FunTmpl) @@ -4269,14 +4266,16 @@ Sema::DeclPtrTy Sema::ActOnFinishFunctionBody(DeclPtrTy D, StmtArg BodyArg,    else      FD = dyn_cast_or_null<FunctionDecl>(dcl); +  sema::AnalysisBasedWarnings W(*this); +    if (FD) {      FD->setBody(Body); -    if (FD->isMain()) +    if (FD->isMain()) {        // C and C++ allow for main to automagically return 0.        // Implements C++ [basic.start.main]p5 and C99 5.1.2.2.3.        FD->setHasImplicitReturnZero(true); -    else -      CheckFallThroughForFunctionDef(FD, Body, AC); +      W.disableCheckFallThrough(); +    }      if (!FD->isInvalidDecl())        DiagnoseUnusedParameters(FD->param_begin(), FD->param_end()); @@ -4288,9 +4287,7 @@ Sema::DeclPtrTy Sema::ActOnFinishFunctionBody(DeclPtrTy D, StmtArg BodyArg,    } else if (ObjCMethodDecl *MD = dyn_cast_or_null<ObjCMethodDecl>(dcl)) {      assert(MD == getCurMethodDecl() && "Method parsing confused");      MD->setBody(Body); -    CheckFallThroughForFunctionDef(MD, Body, AC);      MD->setEndLoc(Body->getLocEnd()); -      if (!MD->isInvalidDecl())        DiagnoseUnusedParameters(MD->param_begin(), MD->param_end());    } else { @@ -4343,16 +4340,14 @@ Sema::DeclPtrTy Sema::ActOnFinishFunctionBody(DeclPtrTy D, StmtArg BodyArg,    }    if (Body) { -    CheckUnreachable(AC); -      // C++ constructors that have function-try-blocks can't have return      // statements in the handlers of that block. (C++ [except.handle]p14)      // Verify this.      if (FD && isa<CXXConstructorDecl>(FD) && isa<CXXTryStmt>(Body))        DiagnoseReturnInConstructorExceptionHandler(cast<CXXTryStmt>(Body)); -  // Verify that that gotos and switch cases don't jump into scopes illegally. -  // Verify that that gotos and switch cases don't jump into scopes illegally. +    // Verify that that gotos and switch cases don't jump into scopes illegally. +    // Verify that that gotos and switch cases don't jump into scopes illegally.      if (FunctionNeedsScopeChecking() && !hasAnyErrorsInThisFunction())        DiagnoseInvalidJumps(Body); @@ -4365,7 +4360,20 @@ Sema::DeclPtrTy Sema::ActOnFinishFunctionBody(DeclPtrTy D, StmtArg BodyArg,      // deletion in some later function.      if (PP.getDiagnostics().hasErrorOccurred())        ExprTemporaries.clear(); -     +    else if (!isa<FunctionTemplateDecl>(dcl)) { +      // Since the body is valid, issue any analysis-based warnings that are +      // enabled. +      QualType ResultType; +      if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(dcl)) { +        ResultType = FD->getResultType(); +      } +      else { +        ObjCMethodDecl *MD = cast<ObjCMethodDecl>(dcl); +        ResultType = MD->getResultType(); +      } +      W.IssueWarnings(dcl); +    } +      assert(ExprTemporaries.empty() && "Leftover temporaries in function");    } diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 28f921dacaf..fe6f3b9f560 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -14,7 +14,7 @@  #include "Sema.h"  #include "SemaInit.h"  #include "Lookup.h" -#include "clang/Analysis/AnalysisContext.h" +#include "AnalysisBasedWarnings.h"  #include "clang/AST/ASTContext.h"  #include "clang/AST/DeclObjC.h"  #include "clang/AST/DeclTemplate.h" @@ -7003,9 +7003,10 @@ Sema::OwningExprResult Sema::ActOnBlockStmtExpr(SourceLocation CaretLoc,      return ExprError();    } -  AnalysisContext AC(BSI->TheDecl); -  CheckFallThroughForBlock(BlockTy, BSI->TheDecl->getBody(), AC); -  CheckUnreachable(AC); +  // Issue any analysis-based warnings. +  sema::AnalysisBasedWarnings W(*this); +  W.IssueWarnings(BSI->TheDecl, BlockTy); +    Expr *Result = new (Context) BlockExpr(BSI->TheDecl, BlockTy,                                           BSI->hasBlockDeclRefExprs);    PopFunctionOrBlockScope();  | 

