forked from OSchip/llvm-project
				
			
		
			
				
	
	
		
			1520 lines
		
	
	
		
			54 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			1520 lines
		
	
	
		
			54 KiB
		
	
	
	
		
			C++
		
	
	
	
//=- 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 "clang/Sema/AnalysisBasedWarnings.h"
 | 
						|
#include "clang/Sema/SemaInternal.h"
 | 
						|
#include "clang/Sema/ScopeInfo.h"
 | 
						|
#include "clang/Basic/SourceManager.h"
 | 
						|
#include "clang/Lex/Preprocessor.h"
 | 
						|
#include "clang/AST/DeclObjC.h"
 | 
						|
#include "clang/AST/DeclCXX.h"
 | 
						|
#include "clang/AST/ExprObjC.h"
 | 
						|
#include "clang/AST/ExprCXX.h"
 | 
						|
#include "clang/AST/StmtObjC.h"
 | 
						|
#include "clang/AST/StmtCXX.h"
 | 
						|
#include "clang/AST/EvaluatedExprVisitor.h"
 | 
						|
#include "clang/AST/StmtVisitor.h"
 | 
						|
#include "clang/Analysis/AnalysisContext.h"
 | 
						|
#include "clang/Analysis/CFG.h"
 | 
						|
#include "clang/Analysis/Analyses/ReachableCode.h"
 | 
						|
#include "clang/Analysis/Analyses/CFGReachabilityAnalysis.h"
 | 
						|
#include "clang/Analysis/CFGStmtMap.h"
 | 
						|
#include "clang/Analysis/Analyses/UninitializedValues.h"
 | 
						|
#include "llvm/ADT/BitVector.h"
 | 
						|
#include "llvm/ADT/FoldingSet.h"
 | 
						|
#include "llvm/ADT/ImmutableMap.h"
 | 
						|
#include "llvm/ADT/PostOrderIterator.h"
 | 
						|
#include "llvm/ADT/SmallVector.h"
 | 
						|
#include "llvm/Support/Casting.h"
 | 
						|
#include <algorithm>
 | 
						|
#include <vector>
 | 
						|
 | 
						|
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 {
 | 
						|
  UnknownFallThrough,
 | 
						|
  NeverFallThrough,
 | 
						|
  MaybeFallThrough,
 | 
						|
  AlwaysFallThrough,
 | 
						|
  NeverFallThroughOrReturn
 | 
						|
};
 | 
						|
 | 
						|
/// 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) return UnknownFallThrough;
 | 
						|
 | 
						|
  // 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.
 | 
						|
  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;
 | 
						|
 | 
						|
  // Ignore default cases that aren't likely to be reachable because all
 | 
						|
  // enums in a switch(X) have explicit case statements.
 | 
						|
  CFGBlock::FilterOptions FO;
 | 
						|
  FO.IgnoreDefaultsWithCoveredEnums = 1;
 | 
						|
 | 
						|
  for (CFGBlock::filtered_pred_iterator
 | 
						|
	 I = cfg->getExit().filtered_pred_start_end(FO); I.hasMore(); ++I) {
 | 
						|
    const CFGBlock& B = **I;
 | 
						|
    if (!live[B.getBlockID()])
 | 
						|
      continue;
 | 
						|
 | 
						|
    // Destructors can appear after the 'return' in the CFG.  This is
 | 
						|
    // normal.  We need to look pass the destructors for the return
 | 
						|
    // statement (if it exists).
 | 
						|
    CFGBlock::const_reverse_iterator ri = B.rbegin(), re = B.rend();
 | 
						|
    bool hasNoReturnDtor = false;
 | 
						|
    
 | 
						|
    for ( ; ri != re ; ++ri) {
 | 
						|
      CFGElement CE = *ri;
 | 
						|
 | 
						|
      // FIXME: The right solution is to just sever the edges in the
 | 
						|
      // CFG itself.
 | 
						|
      if (const CFGImplicitDtor *iDtor = ri->getAs<CFGImplicitDtor>())
 | 
						|
        if (iDtor->isNoReturn(AC.getASTContext())) {
 | 
						|
          hasNoReturnDtor = true;
 | 
						|
          HasFakeEdge = true;
 | 
						|
          break;
 | 
						|
        }
 | 
						|
      
 | 
						|
      if (isa<CFGStmt>(CE))
 | 
						|
        break;
 | 
						|
    }
 | 
						|
    
 | 
						|
    if (hasNoReturnDtor)
 | 
						|
      continue;
 | 
						|
    
 | 
						|
    // No more CFGElements in the block?
 | 
						|
    if (ri == re) {
 | 
						|
      if (B.getTerminator() && isa<CXXTryStmt>(B.getTerminator())) {
 | 
						|
        HasAbnormalEdge = true;
 | 
						|
        continue;
 | 
						|
      }
 | 
						|
      // A labeled empty statement, or the entry block...
 | 
						|
      HasPlainEdge = true;
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
 | 
						|
    CFGStmt CS = cast<CFGStmt>(*ri);
 | 
						|
    const Stmt *S = CS.getStmt();
 | 
						|
    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 (const CallExpr *C = dyn_cast<CallExpr>(S)) {
 | 
						|
      if (std::find(B.succ_begin(), B.succ_end(), &cfg->getExit())
 | 
						|
            == B.succ_end()) {
 | 
						|
        HasAbnormalEdge = true;
 | 
						|
        continue;
 | 
						|
      }
 | 
						|
      const Expr *CEE = C->getCallee()->IgnoreParenCasts();
 | 
						|
      QualType calleeType = CEE->getType();
 | 
						|
      if (calleeType == AC.getASTContext().BoundMemberTy) {
 | 
						|
        calleeType = Expr::findBoundMemberType(CEE);
 | 
						|
        assert(!calleeType.isNull() && "analyzing unresolved call?");
 | 
						|
      }
 | 
						|
      if (getFunctionExtInfo(calleeType).getNoReturn()) {
 | 
						|
        NoReturnEdge = true;
 | 
						|
        HasFakeEdge = true;
 | 
						|
      } else if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(CEE)) {
 | 
						|
        const 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;
 | 
						|
}
 | 
						|
 | 
						|
namespace {
 | 
						|
 | 
						|
struct CheckFallThroughDiagnostics {
 | 
						|
  unsigned diag_MaybeFallThrough_HasNoReturn;
 | 
						|
  unsigned diag_MaybeFallThrough_ReturnsNonVoid;
 | 
						|
  unsigned diag_AlwaysFallThrough_HasNoReturn;
 | 
						|
  unsigned diag_AlwaysFallThrough_ReturnsNonVoid;
 | 
						|
  unsigned diag_NeverFallThroughOrReturn;
 | 
						|
  bool funMode;
 | 
						|
  SourceLocation FuncLoc;
 | 
						|
 | 
						|
  static CheckFallThroughDiagnostics MakeForFunction(const Decl *Func) {
 | 
						|
    CheckFallThroughDiagnostics D;
 | 
						|
    D.FuncLoc = Func->getLocation();
 | 
						|
    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;
 | 
						|
 | 
						|
    // Don't suggest that virtual functions be marked "noreturn", since they
 | 
						|
    // might be overridden by non-noreturn functions.
 | 
						|
    bool isVirtualMethod = false;
 | 
						|
    if (const CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(Func))
 | 
						|
      isVirtualMethod = Method->isVirtual();
 | 
						|
    
 | 
						|
    if (!isVirtualMethod)
 | 
						|
      D.diag_NeverFallThroughOrReturn =
 | 
						|
        diag::warn_suggest_noreturn_function;
 | 
						|
    else
 | 
						|
      D.diag_NeverFallThroughOrReturn = 0;
 | 
						|
    
 | 
						|
    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 (ReturnsVoid ||
 | 
						|
              D.getDiagnosticLevel(diag::warn_maybe_falloff_nonvoid_function,
 | 
						|
                                   FuncLoc) == Diagnostic::Ignored)
 | 
						|
        && (!HasNoReturn ||
 | 
						|
            D.getDiagnosticLevel(diag::warn_noreturn_function_has_return_expr,
 | 
						|
                                 FuncLoc) == Diagnostic::Ignored)
 | 
						|
        && (!ReturnsVoid ||
 | 
						|
            D.getDiagnosticLevel(diag::warn_suggest_noreturn_block, FuncLoc)
 | 
						|
              == Diagnostic::Ignored);
 | 
						|
    }
 | 
						|
 | 
						|
    // For blocks.
 | 
						|
    return  ReturnsVoid && !HasNoReturn
 | 
						|
            && (!ReturnsVoid ||
 | 
						|
                D.getDiagnosticLevel(diag::warn_suggest_noreturn_block, FuncLoc)
 | 
						|
                  == Diagnostic::Ignored);
 | 
						|
  }
 | 
						|
};
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
/// 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,
 | 
						|
                                    const BlockExpr *blkExpr,
 | 
						|
                                    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)) {
 | 
						|
    QualType BlockTy = blkExpr->getType();
 | 
						|
    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 UnknownFallThrough:
 | 
						|
        break;
 | 
						|
 | 
						|
      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 && CD.diag_NeverFallThroughOrReturn) {
 | 
						|
          if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) {
 | 
						|
            S.Diag(Compound->getLBracLoc(), CD.diag_NeverFallThroughOrReturn)
 | 
						|
              << FD;
 | 
						|
          } else {
 | 
						|
            S.Diag(Compound->getLBracLoc(), CD.diag_NeverFallThroughOrReturn);
 | 
						|
          }
 | 
						|
        }
 | 
						|
        break;
 | 
						|
      case NeverFallThrough:
 | 
						|
        break;
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
//===----------------------------------------------------------------------===//
 | 
						|
// -Wuninitialized
 | 
						|
//===----------------------------------------------------------------------===//
 | 
						|
 | 
						|
namespace {
 | 
						|
/// ContainsReference - A visitor class to search for references to
 | 
						|
/// a particular declaration (the needle) within any evaluated component of an
 | 
						|
/// expression (recursively).
 | 
						|
class ContainsReference : public EvaluatedExprVisitor<ContainsReference> {
 | 
						|
  bool FoundReference;
 | 
						|
  const DeclRefExpr *Needle;
 | 
						|
 | 
						|
public:
 | 
						|
  ContainsReference(ASTContext &Context, const DeclRefExpr *Needle)
 | 
						|
    : EvaluatedExprVisitor<ContainsReference>(Context),
 | 
						|
      FoundReference(false), Needle(Needle) {}
 | 
						|
 | 
						|
  void VisitExpr(Expr *E) {
 | 
						|
    // Stop evaluating if we already have a reference.
 | 
						|
    if (FoundReference)
 | 
						|
      return;
 | 
						|
 | 
						|
    EvaluatedExprVisitor<ContainsReference>::VisitExpr(E);
 | 
						|
  }
 | 
						|
 | 
						|
  void VisitDeclRefExpr(DeclRefExpr *E) {
 | 
						|
    if (E == Needle)
 | 
						|
      FoundReference = true;
 | 
						|
    else
 | 
						|
      EvaluatedExprVisitor<ContainsReference>::VisitDeclRefExpr(E);
 | 
						|
  }
 | 
						|
 | 
						|
  bool doesContainReference() const { return FoundReference; }
 | 
						|
};
 | 
						|
}
 | 
						|
 | 
						|
/// DiagnoseUninitializedUse -- Helper function for diagnosing uses of an
 | 
						|
/// uninitialized variable. This manages the different forms of diagnostic
 | 
						|
/// emitted for particular types of uses. Returns true if the use was diagnosed
 | 
						|
/// as a warning. If a pariticular use is one we omit warnings for, returns
 | 
						|
/// false.
 | 
						|
static bool DiagnoseUninitializedUse(Sema &S, const VarDecl *VD,
 | 
						|
                                     const Expr *E, bool isAlwaysUninit) {
 | 
						|
  bool isSelfInit = false;
 | 
						|
 | 
						|
  if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E)) {
 | 
						|
    if (isAlwaysUninit) {
 | 
						|
      // Inspect the initializer of the variable declaration which is
 | 
						|
      // being referenced prior to its initialization. We emit
 | 
						|
      // specialized diagnostics for self-initialization, and we
 | 
						|
      // specifically avoid warning about self references which take the
 | 
						|
      // form of:
 | 
						|
      //
 | 
						|
      //   int x = x;
 | 
						|
      //
 | 
						|
      // This is used to indicate to GCC that 'x' is intentionally left
 | 
						|
      // uninitialized. Proven code paths which access 'x' in
 | 
						|
      // an uninitialized state after this will still warn.
 | 
						|
      //
 | 
						|
      // TODO: Should we suppress maybe-uninitialized warnings for
 | 
						|
      // variables initialized in this way?
 | 
						|
      if (const Expr *Initializer = VD->getInit()) {
 | 
						|
        if (DRE == Initializer->IgnoreParenImpCasts())
 | 
						|
          return false;
 | 
						|
 | 
						|
        ContainsReference CR(S.Context, DRE);
 | 
						|
        CR.Visit(const_cast<Expr*>(Initializer));
 | 
						|
        isSelfInit = CR.doesContainReference();
 | 
						|
      }
 | 
						|
      if (isSelfInit) {
 | 
						|
        S.Diag(DRE->getLocStart(),
 | 
						|
               diag::warn_uninit_self_reference_in_init)
 | 
						|
        << VD->getDeclName() << VD->getLocation() << DRE->getSourceRange();
 | 
						|
      } else {
 | 
						|
        S.Diag(DRE->getLocStart(), diag::warn_uninit_var)
 | 
						|
          << VD->getDeclName() << DRE->getSourceRange();
 | 
						|
      }
 | 
						|
    } else {
 | 
						|
      S.Diag(DRE->getLocStart(), diag::warn_maybe_uninit_var)
 | 
						|
        << VD->getDeclName() << DRE->getSourceRange();
 | 
						|
    }
 | 
						|
  } else {
 | 
						|
    const BlockExpr *BE = cast<BlockExpr>(E);
 | 
						|
    S.Diag(BE->getLocStart(),
 | 
						|
           isAlwaysUninit ? diag::warn_uninit_var_captured_by_block
 | 
						|
                          : diag::warn_maybe_uninit_var_captured_by_block)
 | 
						|
      << VD->getDeclName();
 | 
						|
  }
 | 
						|
 | 
						|
  // Report where the variable was declared when the use wasn't within
 | 
						|
  // the initializer of that declaration.
 | 
						|
  if (!isSelfInit)
 | 
						|
    S.Diag(VD->getLocStart(), diag::note_uninit_var_def)
 | 
						|
      << VD->getDeclName();
 | 
						|
 | 
						|
  return true;
 | 
						|
}
 | 
						|
 | 
						|
static void SuggestInitializationFixit(Sema &S, const VarDecl *VD) {
 | 
						|
  // Don't issue a fixit if there is already an initializer.
 | 
						|
  if (VD->getInit())
 | 
						|
    return;
 | 
						|
 | 
						|
  // Suggest possible initialization (if any).
 | 
						|
  const char *initialization = 0;
 | 
						|
  QualType VariableTy = VD->getType().getCanonicalType();
 | 
						|
 | 
						|
  if (VariableTy->isObjCObjectPointerType() ||
 | 
						|
      VariableTy->isBlockPointerType()) {
 | 
						|
    // Check if 'nil' is defined.
 | 
						|
    if (S.PP.getMacroInfo(&S.getASTContext().Idents.get("nil")))
 | 
						|
      initialization = " = nil";
 | 
						|
    else
 | 
						|
      initialization = " = 0";
 | 
						|
  }
 | 
						|
  else if (VariableTy->isRealFloatingType())
 | 
						|
    initialization = " = 0.0";
 | 
						|
  else if (VariableTy->isBooleanType() && S.Context.getLangOptions().CPlusPlus)
 | 
						|
    initialization = " = false";
 | 
						|
  else if (VariableTy->isEnumeralType())
 | 
						|
    return;
 | 
						|
  else if (VariableTy->isPointerType() || VariableTy->isMemberPointerType()) {
 | 
						|
    if (S.Context.getLangOptions().CPlusPlus0x)
 | 
						|
      initialization = " = nullptr";
 | 
						|
    // Check if 'NULL' is defined.
 | 
						|
    else if (S.PP.getMacroInfo(&S.getASTContext().Idents.get("NULL")))
 | 
						|
      initialization = " = NULL";
 | 
						|
    else
 | 
						|
      initialization = " = 0";
 | 
						|
  }
 | 
						|
  else if (VariableTy->isScalarType())
 | 
						|
    initialization = " = 0";
 | 
						|
 | 
						|
  if (initialization) {
 | 
						|
    SourceLocation loc = S.PP.getLocForEndOfToken(VD->getLocEnd());
 | 
						|
    S.Diag(loc, diag::note_var_fixit_add_initialization)
 | 
						|
      << FixItHint::CreateInsertion(loc, initialization);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
typedef std::pair<const Expr*, bool> UninitUse;
 | 
						|
 | 
						|
namespace {
 | 
						|
struct SLocSort {
 | 
						|
  bool operator()(const UninitUse &a, const UninitUse &b) {
 | 
						|
    SourceLocation aLoc = a.first->getLocStart();
 | 
						|
    SourceLocation bLoc = b.first->getLocStart();
 | 
						|
    return aLoc.getRawEncoding() < bLoc.getRawEncoding();
 | 
						|
  }
 | 
						|
};
 | 
						|
 | 
						|
class UninitValsDiagReporter : public UninitVariablesHandler {
 | 
						|
  Sema &S;
 | 
						|
  typedef SmallVector<UninitUse, 2> UsesVec;
 | 
						|
  typedef llvm::DenseMap<const VarDecl *, UsesVec*> UsesMap;
 | 
						|
  UsesMap *uses;
 | 
						|
  
 | 
						|
public:
 | 
						|
  UninitValsDiagReporter(Sema &S) : S(S), uses(0) {}
 | 
						|
  ~UninitValsDiagReporter() { 
 | 
						|
    flushDiagnostics();
 | 
						|
  }
 | 
						|
  
 | 
						|
  void handleUseOfUninitVariable(const Expr *ex, const VarDecl *vd,
 | 
						|
                                 bool isAlwaysUninit) {
 | 
						|
    if (!uses)
 | 
						|
      uses = new UsesMap();
 | 
						|
    
 | 
						|
    UsesVec *&vec = (*uses)[vd];
 | 
						|
    if (!vec)
 | 
						|
      vec = new UsesVec();
 | 
						|
    
 | 
						|
    vec->push_back(std::make_pair(ex, isAlwaysUninit));
 | 
						|
  }
 | 
						|
  
 | 
						|
  void flushDiagnostics() {
 | 
						|
    if (!uses)
 | 
						|
      return;
 | 
						|
    
 | 
						|
    for (UsesMap::iterator i = uses->begin(), e = uses->end(); i != e; ++i) {
 | 
						|
      const VarDecl *vd = i->first;
 | 
						|
      UsesVec *vec = i->second;
 | 
						|
 | 
						|
      // Sort the uses by their SourceLocations.  While not strictly
 | 
						|
      // guaranteed to produce them in line/column order, this will provide
 | 
						|
      // a stable ordering.
 | 
						|
      std::sort(vec->begin(), vec->end(), SLocSort());
 | 
						|
      
 | 
						|
      for (UsesVec::iterator vi = vec->begin(), ve = vec->end(); vi != ve;
 | 
						|
           ++vi) {
 | 
						|
        if (!DiagnoseUninitializedUse(S, vd, vi->first,
 | 
						|
                                      /*isAlwaysUninit=*/vi->second))
 | 
						|
          continue;
 | 
						|
 | 
						|
        SuggestInitializationFixit(S, vd);
 | 
						|
 | 
						|
        // Skip further diagnostics for this variable. We try to warn only on
 | 
						|
        // the first point at which a variable is used uninitialized.
 | 
						|
        break;
 | 
						|
      }
 | 
						|
 | 
						|
      delete vec;
 | 
						|
    }
 | 
						|
    delete uses;
 | 
						|
  }
 | 
						|
};
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
//===----------------------------------------------------------------------===//
 | 
						|
// -Wthread-safety
 | 
						|
//===----------------------------------------------------------------------===//
 | 
						|
 | 
						|
namespace {
 | 
						|
/// \brief Implements a set of CFGBlocks using a BitVector.
 | 
						|
///
 | 
						|
/// This class contains a minimal interface, primarily dictated by the SetType
 | 
						|
/// template parameter of the llvm::po_iterator template, as used with external
 | 
						|
/// storage. We also use this set to keep track of which CFGBlocks we visit
 | 
						|
/// during the analysis.
 | 
						|
class CFGBlockSet {
 | 
						|
  llvm::BitVector VisitedBlockIDs;
 | 
						|
 | 
						|
public:
 | 
						|
  // po_iterator requires this iterator, but the only interface needed is the
 | 
						|
  // value_type typedef.
 | 
						|
  struct iterator {
 | 
						|
    typedef const CFGBlock *value_type;
 | 
						|
  };
 | 
						|
 | 
						|
  CFGBlockSet() {}
 | 
						|
  CFGBlockSet(const CFG *G) : VisitedBlockIDs(G->getNumBlockIDs(), false) {}
 | 
						|
 | 
						|
  /// \brief Set the bit associated with a particular CFGBlock.
 | 
						|
  /// This is the important method for the SetType template parameter.
 | 
						|
  bool insert(const CFGBlock *Block) {
 | 
						|
    // Note that insert() is called by po_iterator, which doesn't check to make
 | 
						|
    // sure that Block is non-null.  Moreover, the CFGBlock iterator will
 | 
						|
    // occasionally hand out null pointers for pruned edges, so we catch those
 | 
						|
    // here.
 | 
						|
    if (Block == 0)
 | 
						|
      return false;  // if an edge is trivially false.
 | 
						|
    if (VisitedBlockIDs.test(Block->getBlockID()))
 | 
						|
      return false;
 | 
						|
    VisitedBlockIDs.set(Block->getBlockID());
 | 
						|
    return true;
 | 
						|
  }
 | 
						|
 | 
						|
  /// \brief Check if the bit for a CFGBlock has been already set.
 | 
						|
  /// This method is for tracking visited blocks in the main threadsafety loop.
 | 
						|
  /// Block must not be null.
 | 
						|
  bool alreadySet(const CFGBlock *Block) {
 | 
						|
    return VisitedBlockIDs.test(Block->getBlockID());
 | 
						|
  }
 | 
						|
};
 | 
						|
 | 
						|
/// \brief We create a helper class which we use to iterate through CFGBlocks in
 | 
						|
/// the topological order.
 | 
						|
class TopologicallySortedCFG {
 | 
						|
  typedef llvm::po_iterator<const CFG*, CFGBlockSet, true>  po_iterator;
 | 
						|
 | 
						|
  std::vector<const CFGBlock*> Blocks;
 | 
						|
 | 
						|
public:
 | 
						|
  typedef std::vector<const CFGBlock*>::reverse_iterator iterator;
 | 
						|
 | 
						|
  TopologicallySortedCFG(const CFG *CFGraph) {
 | 
						|
    Blocks.reserve(CFGraph->getNumBlockIDs());
 | 
						|
    CFGBlockSet BSet(CFGraph);
 | 
						|
 | 
						|
    for (po_iterator I = po_iterator::begin(CFGraph, BSet),
 | 
						|
         E = po_iterator::end(CFGraph, BSet); I != E; ++I) {
 | 
						|
      Blocks.push_back(*I);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  iterator begin() {
 | 
						|
    return Blocks.rbegin();
 | 
						|
  }
 | 
						|
 | 
						|
  iterator end() {
 | 
						|
    return Blocks.rend();
 | 
						|
  }
 | 
						|
};
 | 
						|
 | 
						|
/// \brief A LockID object uniquely identifies a particular lock acquired, and
 | 
						|
/// is built from an Expr* (i.e. calling a lock function).
 | 
						|
///
 | 
						|
/// Thread-safety analysis works by comparing lock expressions.  Within the
 | 
						|
/// body of a function, an expression such as "x->foo->bar.mu" will resolve to
 | 
						|
/// a particular lock object at run-time.  Subsequent occurrences of the same
 | 
						|
/// expression (where "same" means syntactic equality) will refer to the same
 | 
						|
/// run-time object if three conditions hold:
 | 
						|
/// (1) Local variables in the expression, such as "x" have not changed.
 | 
						|
/// (2) Values on the heap that affect the expression have not changed.
 | 
						|
/// (3) The expression involves only pure function calls.
 | 
						|
/// The current implementation assumes, but does not verify, that multiple uses
 | 
						|
/// of the same lock expression satisfies these criteria.
 | 
						|
///
 | 
						|
/// Clang introduces an additional wrinkle, which is that it is difficult to
 | 
						|
/// derive canonical expressions, or compare expressions directly for equality.
 | 
						|
/// Thus, we identify a lock not by an Expr, but by the set of named
 | 
						|
/// declarations that are referenced by the Expr.  In other words,
 | 
						|
/// x->foo->bar.mu will be a four element vector with the Decls for
 | 
						|
/// mu, bar, and foo, and x.  The vector will uniquely identify the expression
 | 
						|
/// for all practical purposes.
 | 
						|
///
 | 
						|
/// Note we will need to perform substitution on "this" and function parameter
 | 
						|
/// names when constructing a lock expression.
 | 
						|
///
 | 
						|
/// For example:
 | 
						|
/// class C { Mutex Mu;  void lock() EXCLUSIVE_LOCK_FUNCTION(this->Mu); };
 | 
						|
/// void myFunc(C *X) { ... X->lock() ... }
 | 
						|
/// The original expression for the lock acquired by myFunc is "this->Mu", but
 | 
						|
/// "X" is substituted for "this" so we get X->Mu();
 | 
						|
///
 | 
						|
/// For another example:
 | 
						|
/// foo(MyList *L) EXCLUSIVE_LOCKS_REQUIRED(L->Mu) { ... }
 | 
						|
/// MyList *MyL;
 | 
						|
/// foo(MyL);  // requires lock MyL->Mu to be held
 | 
						|
///
 | 
						|
/// FIXME: In C++0x Mutexes are the objects that control access to shared
 | 
						|
/// variables, while Locks are the objects that acquire and release Mutexes. We
 | 
						|
/// may want to switch to this new terminology soon, in which case we should
 | 
						|
/// rename this class "Mutex" and rename "LockId" to "MutexId", as well as
 | 
						|
/// making sure that the terms Lock and Mutex throughout this code are
 | 
						|
/// consistent with C++0x
 | 
						|
///
 | 
						|
/// FIXME: We should also pick one and canonicalize all usage of lock vs acquire
 | 
						|
/// and unlock vs release as verbs.
 | 
						|
class LockID {
 | 
						|
  SmallVector<NamedDecl*, 2> DeclSeq;
 | 
						|
 
 | 
						|
  /// Build a Decl sequence representing the lock from the given expression.
 | 
						|
  /// Recursive function that bottoms out when the final DeclRefExpr is reached.
 | 
						|
  void buildLock(Expr *Exp) {
 | 
						|
    if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(Exp)) {
 | 
						|
      NamedDecl *ND = cast<NamedDecl>(DRE->getDecl()->getCanonicalDecl());
 | 
						|
      DeclSeq.push_back(ND);
 | 
						|
    } else if (MemberExpr *ME = dyn_cast<MemberExpr>(Exp)) {
 | 
						|
      NamedDecl *ND = ME->getMemberDecl();
 | 
						|
      DeclSeq.push_back(ND);
 | 
						|
      buildLock(ME->getBase());
 | 
						|
    } else {
 | 
						|
      // FIXME: add diagnostic
 | 
						|
      llvm::report_fatal_error("Expected lock expression!");
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
public:
 | 
						|
  LockID(Expr *LExpr) {
 | 
						|
    buildLock(LExpr);
 | 
						|
    assert(!DeclSeq.empty());
 | 
						|
  }
 | 
						|
 | 
						|
  bool operator==(const LockID &other) const {
 | 
						|
    return DeclSeq == other.DeclSeq;
 | 
						|
  }
 | 
						|
 | 
						|
  bool operator!=(const LockID &other) const {
 | 
						|
    return !(*this == other);
 | 
						|
  }
 | 
						|
 | 
						|
  // SmallVector overloads Operator< to do lexicographic ordering. Note that
 | 
						|
  // we use pointer equality (and <) to compare NamedDecls. This means the order
 | 
						|
  // of LockIDs in a lockset is nondeterministic. In order to output
 | 
						|
  // diagnostics in a deterministic ordering, we must order all diagnostics to
 | 
						|
  // output by SourceLocation when iterating through this lockset.
 | 
						|
  bool operator<(const LockID &other) const {
 | 
						|
    return DeclSeq < other.DeclSeq;
 | 
						|
  }
 | 
						|
 | 
						|
  /// \brief Returns the name of the first Decl in the list for a given LockID;
 | 
						|
  /// e.g. the lock expression foo.bar() has name "bar".
 | 
						|
  /// The caret will point unambiguously to the lock expression, so using this
 | 
						|
  /// name in diagnostics is a way to get simple, and consistent, lock names.
 | 
						|
  /// We do not want to output the entire expression text for security reasons.
 | 
						|
  StringRef getName() const {
 | 
						|
    return DeclSeq.front()->getName();
 | 
						|
  }
 | 
						|
 | 
						|
  void Profile(llvm::FoldingSetNodeID &ID) const {
 | 
						|
    for (SmallVectorImpl<NamedDecl*>::const_iterator I = DeclSeq.begin(),
 | 
						|
         E = DeclSeq.end(); I != E; ++I) {
 | 
						|
      ID.AddPointer(*I);
 | 
						|
    }
 | 
						|
  }
 | 
						|
};
 | 
						|
 | 
						|
/// \brief This is a helper class that stores info about the most recent
 | 
						|
/// accquire of a Lock.
 | 
						|
///
 | 
						|
/// The main body of the analysis maps LockIDs to LockDatas.
 | 
						|
struct LockData {
 | 
						|
  SourceLocation AcquireLoc;
 | 
						|
 | 
						|
  LockData(SourceLocation Loc) : AcquireLoc(Loc) {}
 | 
						|
 | 
						|
  bool operator==(const LockData &other) const {
 | 
						|
    return AcquireLoc == other.AcquireLoc;
 | 
						|
  }
 | 
						|
 | 
						|
  bool operator!=(const LockData &other) const {
 | 
						|
    return !(*this == other);
 | 
						|
  }
 | 
						|
 | 
						|
  void Profile(llvm::FoldingSetNodeID &ID) const {
 | 
						|
    ID.AddInteger(AcquireLoc.getRawEncoding());
 | 
						|
  }
 | 
						|
};
 | 
						|
 | 
						|
/// A Lockset maps each LockID (defined above) to information about how it has
 | 
						|
/// been locked.
 | 
						|
typedef llvm::ImmutableMap<LockID, LockData> Lockset;
 | 
						|
 | 
						|
/// \brief We use this class to visit different types of expressions in
 | 
						|
/// CFGBlocks, and build up the lockset.
 | 
						|
/// An expression may cause us to add or remove locks from the lockset, or else
 | 
						|
/// output error messages related to missing locks.
 | 
						|
/// FIXME: In future, we may be able to not inherit from a visitor.
 | 
						|
class BuildLockset : public StmtVisitor<BuildLockset> {
 | 
						|
  Sema &S;
 | 
						|
  Lockset LSet;
 | 
						|
  Lockset::Factory &LocksetFactory;
 | 
						|
 | 
						|
  // Helper functions
 | 
						|
  void removeLock(SourceLocation UnlockLoc, Expr *LockExp);
 | 
						|
  void addLock(SourceLocation LockLoc, Expr *LockExp);
 | 
						|
  const ValueDecl *getValueDecl(Expr *Exp);
 | 
						|
  void checkAccess(Expr *Exp);
 | 
						|
  void checkDereference(Expr *Exp);
 | 
						|
 | 
						|
public:
 | 
						|
  BuildLockset(Sema &S, Lockset LS, Lockset::Factory &F)
 | 
						|
    : StmtVisitor<BuildLockset>(), S(S), LSet(LS),
 | 
						|
      LocksetFactory(F) {}
 | 
						|
 | 
						|
  Lockset getLockset() {
 | 
						|
    return LSet;
 | 
						|
  }
 | 
						|
 | 
						|
  void VisitUnaryOperator(UnaryOperator *UO);
 | 
						|
  void VisitBinaryOperator(BinaryOperator *BO);
 | 
						|
  void VisitCastExpr(CastExpr *CE);
 | 
						|
  void VisitCXXMemberCallExpr(CXXMemberCallExpr *Exp);
 | 
						|
};
 | 
						|
 | 
						|
/// \brief Add a new lock to the lockset, warning if the lock is already there.
 | 
						|
/// \param LockLoc The source location of the acquire
 | 
						|
/// \param LockExp The lock expression corresponding to the lock to be added
 | 
						|
void BuildLockset::addLock(SourceLocation LockLoc, Expr *LockExp) {
 | 
						|
  LockID Lock(LockExp);
 | 
						|
  LockData NewLockData(LockLoc);
 | 
						|
 | 
						|
  if (LSet.contains(Lock))
 | 
						|
    S.Diag(LockLoc, diag::warn_double_lock) << Lock.getName();
 | 
						|
 | 
						|
  LSet = LocksetFactory.add(LSet, Lock, NewLockData);
 | 
						|
}
 | 
						|
 | 
						|
/// \brief Remove a lock from the lockset, warning if the lock is not there.
 | 
						|
/// \param LockExp The lock expression corresponding to the lock to be removed
 | 
						|
/// \param UnlockLoc The source location of the unlock (only used in error msg)
 | 
						|
void BuildLockset::removeLock(SourceLocation UnlockLoc, Expr *LockExp) {
 | 
						|
  LockID Lock(LockExp);
 | 
						|
 | 
						|
  Lockset NewLSet = LocksetFactory.remove(LSet, Lock);
 | 
						|
  if(NewLSet == LSet)
 | 
						|
    S.Diag(UnlockLoc, diag::warn_unlock_but_no_acquire) << Lock.getName();
 | 
						|
 | 
						|
  LSet = NewLSet;
 | 
						|
}
 | 
						|
 | 
						|
/// \brief Gets the value decl pointer from DeclRefExprs or MemberExprs
 | 
						|
const ValueDecl *BuildLockset::getValueDecl(Expr *Exp) {
 | 
						|
  if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(Exp))
 | 
						|
    return DR->getDecl();
 | 
						|
 | 
						|
  if (const MemberExpr *ME = dyn_cast<MemberExpr>(Exp))
 | 
						|
    return ME->getMemberDecl();
 | 
						|
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
/// \brief This method identifies variable dereferences and checks pt_guarded_by
 | 
						|
/// and pt_guarded_var annotations. Note that we only check these annotations
 | 
						|
/// at the time a pointer is dereferenced.
 | 
						|
/// FIXME: We need to check for other types of pointer dereferences
 | 
						|
/// (e.g. [], ->) and deal with them here.
 | 
						|
/// \param Exp An expression that has been read or written.
 | 
						|
void BuildLockset::checkDereference(Expr *Exp) {
 | 
						|
  UnaryOperator *UO = dyn_cast<UnaryOperator>(Exp);
 | 
						|
  if (!UO || UO->getOpcode() != clang::UO_Deref)
 | 
						|
    return;
 | 
						|
  Exp = UO->getSubExpr()->IgnoreParenCasts();
 | 
						|
 | 
						|
  const ValueDecl *D = getValueDecl(Exp);
 | 
						|
  if(!D || !D->hasAttrs())
 | 
						|
    return;
 | 
						|
 | 
						|
  if (D->getAttr<PtGuardedVarAttr>() && LSet.isEmpty())
 | 
						|
    S.Diag(Exp->getExprLoc(), diag::warn_var_deref_requires_any_lock)
 | 
						|
      << D->getName();
 | 
						|
 | 
						|
  const AttrVec &ArgAttrs = D->getAttrs();
 | 
						|
  for(unsigned i = 0, Size = ArgAttrs.size(); i < Size; ++i) {
 | 
						|
    if (ArgAttrs[i]->getKind() != attr::PtGuardedBy)
 | 
						|
      continue;
 | 
						|
    PtGuardedByAttr *PGBAttr = cast<PtGuardedByAttr>(ArgAttrs[i]);
 | 
						|
    LockID Lock(PGBAttr->getArg());
 | 
						|
    if (!LSet.contains(Lock))
 | 
						|
      S.Diag(Exp->getExprLoc(), diag::warn_var_deref_requires_lock)
 | 
						|
        << D->getName() << Lock.getName();
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
/// \brief Checks guarded_by and guarded_var attributes.
 | 
						|
/// Whenever we identify an access (read or write) of a DeclRefExpr or
 | 
						|
/// MemberExpr, we need to check whether there are any guarded_by or
 | 
						|
/// guarded_var attributes, and make sure we hold the appropriate locks.
 | 
						|
void BuildLockset::checkAccess(Expr *Exp) {
 | 
						|
  const ValueDecl *D = getValueDecl(Exp);
 | 
						|
  if(!D || !D->hasAttrs())
 | 
						|
    return;
 | 
						|
 | 
						|
  if (D->getAttr<GuardedVarAttr>() && LSet.isEmpty())
 | 
						|
    S.Diag(Exp->getExprLoc(), diag::warn_variable_requires_any_lock)
 | 
						|
      << D->getName();
 | 
						|
 | 
						|
  const AttrVec &ArgAttrs = D->getAttrs();
 | 
						|
  for(unsigned i = 0, Size = ArgAttrs.size(); i < Size; ++i) {
 | 
						|
    if (ArgAttrs[i]->getKind() != attr::GuardedBy)
 | 
						|
      continue;
 | 
						|
    GuardedByAttr *GBAttr = cast<GuardedByAttr>(ArgAttrs[i]);
 | 
						|
    LockID Lock(GBAttr->getArg());
 | 
						|
    if (!LSet.contains(Lock))
 | 
						|
      S.Diag(Exp->getExprLoc(), diag::warn_variable_requires_lock)
 | 
						|
        << D->getName() << Lock.getName();
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
/// \brief For unary operations which read and write a variable, we need to
 | 
						|
/// check whether we hold any required locks. Reads are checked in
 | 
						|
/// VisitCastExpr.
 | 
						|
void BuildLockset::VisitUnaryOperator(UnaryOperator *UO) {
 | 
						|
  switch (UO->getOpcode()) {
 | 
						|
    case clang::UO_PostDec:
 | 
						|
    case clang::UO_PostInc:
 | 
						|
    case clang::UO_PreDec:
 | 
						|
    case clang::UO_PreInc: {
 | 
						|
      Expr *SubExp = UO->getSubExpr()->IgnoreParenCasts();
 | 
						|
      checkAccess(SubExp);
 | 
						|
      checkDereference(SubExp);
 | 
						|
      break;
 | 
						|
    }
 | 
						|
    default:
 | 
						|
      break;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
/// For binary operations which assign to a variable (writes), we need to check
 | 
						|
/// whether we hold any required locks.
 | 
						|
/// FIXME: Deal with non-primitive types.
 | 
						|
void BuildLockset::VisitBinaryOperator(BinaryOperator *BO) {
 | 
						|
  if (!BO->isAssignmentOp())
 | 
						|
    return;
 | 
						|
  Expr *LHSExp = BO->getLHS()->IgnoreParenCasts();
 | 
						|
  checkAccess(LHSExp);
 | 
						|
  checkDereference(LHSExp);
 | 
						|
}
 | 
						|
 | 
						|
/// Whenever we do an LValue to Rvalue cast, we are reading a variable and
 | 
						|
/// need to ensure we hold any required locks. 
 | 
						|
/// FIXME: Deal with non-primitive types.
 | 
						|
void BuildLockset::VisitCastExpr(CastExpr *CE) {
 | 
						|
  if (CE->getCastKind() != CK_LValueToRValue)
 | 
						|
    return;
 | 
						|
  Expr *SubExp = CE->getSubExpr()->IgnoreParenCasts();
 | 
						|
  checkAccess(SubExp);
 | 
						|
  checkDereference(SubExp);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/// \brief When visiting CXXMemberCallExprs we need to examine the attributes on
 | 
						|
/// the method that is being called and add, remove or check locks in the
 | 
						|
/// lockset accordingly.
 | 
						|
/// 
 | 
						|
/// FIXME: For classes annotated with one of the guarded annotations, we need
 | 
						|
/// to treat const method calls as reads and non-const method calls as writes,
 | 
						|
/// and check that the appropriate locks are held. Non-const method calls with 
 | 
						|
/// the same signature as const method calls can be also treated as reads.
 | 
						|
void BuildLockset::VisitCXXMemberCallExpr(CXXMemberCallExpr *Exp) {
 | 
						|
  NamedDecl *D = dyn_cast_or_null<NamedDecl>(Exp->getCalleeDecl());
 | 
						|
 | 
						|
  SourceLocation ExpLocation = Exp->getExprLoc();
 | 
						|
  Expr *Parent = Exp->getImplicitObjectArgument();
 | 
						|
 | 
						|
  if(!D || !D->hasAttrs())
 | 
						|
    return;
 | 
						|
 | 
						|
  AttrVec &ArgAttrs = D->getAttrs();
 | 
						|
  for(unsigned i = 0; i < ArgAttrs.size(); ++i) {
 | 
						|
    Attr *Attr = ArgAttrs[i];
 | 
						|
    switch (Attr->getKind()) {
 | 
						|
      // When we encounter an exclusive lock function, we need to add the lock
 | 
						|
      // to our lockset.
 | 
						|
      case attr::ExclusiveLockFunction: {
 | 
						|
        ExclusiveLockFunctionAttr *ELFAttr =
 | 
						|
          cast<ExclusiveLockFunctionAttr>(Attr);
 | 
						|
 | 
						|
        if (ELFAttr->args_size() == 0) {// The lock held is the "this" object.
 | 
						|
          addLock(ExpLocation, Parent);
 | 
						|
          break;
 | 
						|
        }
 | 
						|
 | 
						|
        for (ExclusiveLockFunctionAttr::args_iterator I = ELFAttr->args_begin(),
 | 
						|
             E = ELFAttr->args_end(); I != E; ++I)
 | 
						|
          addLock(ExpLocation, *I);
 | 
						|
        // FIXME: acquired_after/acquired_before annotations
 | 
						|
        break;
 | 
						|
      }
 | 
						|
 | 
						|
      // When we encounter an unlock function, we need to remove unlocked locks
 | 
						|
      // from the lockset, and flag a warning if they are not there.
 | 
						|
      case attr::UnlockFunction: {
 | 
						|
        UnlockFunctionAttr *UFAttr = cast<UnlockFunctionAttr>(Attr);
 | 
						|
 | 
						|
        if (UFAttr->args_size() == 0) { // The lock held is the "this" object.
 | 
						|
          removeLock(ExpLocation, Parent);
 | 
						|
          break;
 | 
						|
        }
 | 
						|
 | 
						|
        for (UnlockFunctionAttr::args_iterator I = UFAttr->args_begin(),
 | 
						|
            E = UFAttr->args_end(); I != E; ++I)
 | 
						|
          removeLock(ExpLocation, *I);
 | 
						|
        break;
 | 
						|
      }
 | 
						|
 | 
						|
      // Ignore other (non thread-safety) attributes
 | 
						|
      default:
 | 
						|
        break;
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
typedef std::pair<SourceLocation, PartialDiagnostic> DelayedDiag;
 | 
						|
typedef llvm::SmallVector<DelayedDiag, 4> DiagList;
 | 
						|
 | 
						|
struct SortDiagBySourceLocation {
 | 
						|
  Sema &S;
 | 
						|
 | 
						|
  SortDiagBySourceLocation(Sema &S) : S(S) {}
 | 
						|
 | 
						|
  bool operator()(const DelayedDiag &left, const DelayedDiag &right) {
 | 
						|
    // Although this call will be slow, this is only called when outputting
 | 
						|
    // multiple warnings.
 | 
						|
    return S.getSourceManager().isBeforeInTranslationUnit(left.first,
 | 
						|
                                                          right.first);
 | 
						|
  }
 | 
						|
};
 | 
						|
} // end anonymous namespace
 | 
						|
 | 
						|
/// \brief Emit all buffered diagnostics in order of sourcelocation.
 | 
						|
/// We need to output diagnostics produced while iterating through
 | 
						|
/// the lockset in deterministic order, so this function orders diagnostics
 | 
						|
/// and outputs them.
 | 
						|
static void EmitDiagnostics(Sema &S, DiagList &D) {
 | 
						|
  SortDiagBySourceLocation SortDiagBySL(S);
 | 
						|
  sort(D.begin(), D.end(), SortDiagBySL);
 | 
						|
  for (DiagList::iterator I = D.begin(), E = D.end(); I != E; ++I)
 | 
						|
    S.Diag(I->first, I->second);
 | 
						|
}
 | 
						|
 | 
						|
/// \brief Compute the intersection of two locksets and issue warnings for any
 | 
						|
/// locks in the symmetric difference.
 | 
						|
///
 | 
						|
/// This function is used at a merge point in the CFG when comparing the lockset
 | 
						|
/// of each branch being merged. For example, given the following sequence:
 | 
						|
/// A; if () then B; else C; D; we need to check that the lockset after B and C
 | 
						|
/// are the same. In the event of a difference, we use the intersection of these
 | 
						|
/// two locksets at the start of D.
 | 
						|
static Lockset intersectAndWarn(Sema &S, Lockset LSet1, Lockset LSet2,
 | 
						|
                                Lockset::Factory &Fact) {
 | 
						|
  Lockset Intersection = LSet1;
 | 
						|
  DiagList Warnings;
 | 
						|
 | 
						|
  for (Lockset::iterator I = LSet2.begin(), E = LSet2.end(); I != E; ++I) {
 | 
						|
    if (!LSet1.contains(I.getKey())) {
 | 
						|
      const LockID &MissingLock = I.getKey();
 | 
						|
      const LockData &MissingLockData = I.getData();
 | 
						|
      PartialDiagnostic Warning =
 | 
						|
        S.PDiag(diag::warn_lock_not_released_in_scope) << MissingLock.getName();
 | 
						|
      Warnings.push_back(DelayedDiag(MissingLockData.AcquireLoc, Warning));
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  for (Lockset::iterator I = LSet1.begin(), E = LSet1.end(); I != E; ++I) {
 | 
						|
    if (!LSet2.contains(I.getKey())) {
 | 
						|
      const LockID &MissingLock = I.getKey();
 | 
						|
      const LockData &MissingLockData = I.getData();
 | 
						|
      PartialDiagnostic Warning =
 | 
						|
        S.PDiag(diag::warn_lock_not_released_in_scope) << MissingLock.getName();
 | 
						|
      Warnings.push_back(DelayedDiag(MissingLockData.AcquireLoc, Warning));
 | 
						|
      Intersection = Fact.remove(Intersection, MissingLock);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  EmitDiagnostics(S, Warnings);
 | 
						|
  return Intersection;
 | 
						|
}
 | 
						|
 | 
						|
/// \brief Returns the location of the first Stmt in a Block.
 | 
						|
static SourceLocation getFirstStmtLocation(CFGBlock *Block) {
 | 
						|
  SourceLocation Loc;
 | 
						|
  for (CFGBlock::const_iterator BI = Block->begin(), BE = Block->end();
 | 
						|
       BI != BE; ++BI) {
 | 
						|
    if (const CFGStmt *CfgStmt = dyn_cast<CFGStmt>(&(*BI))) {
 | 
						|
      Loc = CfgStmt->getStmt()->getLocStart();
 | 
						|
      if (Loc.isValid()) return Loc;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  if (Stmt *S = Block->getTerminator().getStmt()) {
 | 
						|
    Loc = S->getLocStart();
 | 
						|
    if (Loc.isValid()) return Loc;
 | 
						|
  }
 | 
						|
  return Loc;
 | 
						|
}
 | 
						|
 | 
						|
/// \brief Warn about different locksets along backedges of loops.
 | 
						|
/// This function is called when we encounter a back edge. At that point,
 | 
						|
/// we need to verify that the lockset before taking the backedge is the
 | 
						|
/// same as the lockset before entering the loop.
 | 
						|
///
 | 
						|
/// \param LoopEntrySet Locks held before starting the loop
 | 
						|
/// \param LoopReentrySet Locks held in the last CFG block of the loop
 | 
						|
static void warnBackEdgeUnequalLocksets(Sema &S, const Lockset LoopReentrySet,
 | 
						|
                                        const Lockset LoopEntrySet,
 | 
						|
                                        SourceLocation FirstLocInLoop) {
 | 
						|
  assert(FirstLocInLoop.isValid());
 | 
						|
  DiagList Warnings;
 | 
						|
 | 
						|
  // Warn for locks held at the start of the loop, but not the end.
 | 
						|
  for (Lockset::iterator I = LoopEntrySet.begin(), E = LoopEntrySet.end();
 | 
						|
       I != E; ++I) {
 | 
						|
    if (!LoopReentrySet.contains(I.getKey())) {
 | 
						|
      const LockID &MissingLock = I.getKey();
 | 
						|
      // We report this error at the location of the first statement in a loop
 | 
						|
      PartialDiagnostic Warning =
 | 
						|
        S.PDiag(diag::warn_expecting_lock_held_on_loop)
 | 
						|
          << MissingLock.getName();
 | 
						|
      Warnings.push_back(DelayedDiag(FirstLocInLoop, Warning));
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  // Warn for locks held at the end of the loop, but not at the start.
 | 
						|
  for (Lockset::iterator I = LoopReentrySet.begin(), E = LoopReentrySet.end();
 | 
						|
       I != E; ++I) {
 | 
						|
    if (!LoopEntrySet.contains(I.getKey())) {
 | 
						|
      const LockID &MissingLock = I.getKey();
 | 
						|
      const LockData &MissingLockData = I.getData();
 | 
						|
      PartialDiagnostic Warning =
 | 
						|
        S.PDiag(diag::warn_lock_not_released_in_scope) << MissingLock.getName();
 | 
						|
      Warnings.push_back(DelayedDiag(MissingLockData.AcquireLoc, Warning));
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  EmitDiagnostics(S, Warnings);
 | 
						|
}
 | 
						|
 | 
						|
/// \brief Check a function's CFG for thread-safety violations.
 | 
						|
///
 | 
						|
/// We traverse the blocks in the CFG, compute the set of locks that are held
 | 
						|
/// at the end of each block, and issue warnings for thread safety violations.
 | 
						|
/// Each block in the CFG is traversed exactly once.
 | 
						|
static void checkThreadSafety(Sema &S, AnalysisContext &AC) {
 | 
						|
  CFG *CFGraph = AC.getCFG();
 | 
						|
  if (!CFGraph) return;
 | 
						|
 | 
						|
  Lockset::Factory LocksetFactory;
 | 
						|
 | 
						|
  // FIXME: Swith to SmallVector? Otherwise improve performance impact?
 | 
						|
  std::vector<Lockset> EntryLocksets(CFGraph->getNumBlockIDs(),
 | 
						|
                                     LocksetFactory.getEmptyMap());
 | 
						|
  std::vector<Lockset> ExitLocksets(CFGraph->getNumBlockIDs(),
 | 
						|
                                    LocksetFactory.getEmptyMap());
 | 
						|
 | 
						|
  // We need to explore the CFG via a "topological" ordering.
 | 
						|
  // That way, we will be guaranteed to have information about required
 | 
						|
  // predecessor locksets when exploring a new block.
 | 
						|
  TopologicallySortedCFG SortedGraph(CFGraph);
 | 
						|
  CFGBlockSet VisitedBlocks(CFGraph);
 | 
						|
 | 
						|
  for (TopologicallySortedCFG::iterator I = SortedGraph.begin(),
 | 
						|
       E = SortedGraph.end(); I!= E; ++I) {
 | 
						|
    const CFGBlock *CurrBlock = *I;
 | 
						|
    int CurrBlockID = CurrBlock->getBlockID();
 | 
						|
 | 
						|
    VisitedBlocks.insert(CurrBlock);
 | 
						|
 | 
						|
    // Use the default initial lockset in case there are no predecessors.
 | 
						|
    Lockset &Entryset = EntryLocksets[CurrBlockID];
 | 
						|
    Lockset &Exitset = ExitLocksets[CurrBlockID];
 | 
						|
 | 
						|
    // Iterate through the predecessor blocks and warn if the lockset for all
 | 
						|
    // predecessors is not the same. We take the entry lockset of the current
 | 
						|
    // block to be the intersection of all previous locksets.
 | 
						|
    // FIXME: By keeping the intersection, we may output more errors in future
 | 
						|
    // for a lock which is not in the intersection, but was in the union. We
 | 
						|
    // may want to also keep the union in future. As an example, let's say
 | 
						|
    // the intersection contains Lock L, and the union contains L and M.
 | 
						|
    // Later we unlock M. At this point, we would output an error because we
 | 
						|
    // never locked M; although the real error is probably that we forgot to
 | 
						|
    // lock M on all code paths. Conversely, let's say that later we lock M.
 | 
						|
    // In this case, we should compare against the intersection instead of the
 | 
						|
    // union because the real error is probably that we forgot to unlock M on
 | 
						|
    // all code paths.
 | 
						|
    bool LocksetInitialized = false;
 | 
						|
    for (CFGBlock::const_pred_iterator PI = CurrBlock->pred_begin(),
 | 
						|
         PE  = CurrBlock->pred_end(); PI != PE; ++PI) {
 | 
						|
 | 
						|
      // if *PI -> CurrBlock is a back edge
 | 
						|
      if (*PI == 0 || !VisitedBlocks.alreadySet(*PI))
 | 
						|
        continue;
 | 
						|
 | 
						|
      int PrevBlockID = (*PI)->getBlockID();
 | 
						|
      if (!LocksetInitialized) {
 | 
						|
        Entryset = ExitLocksets[PrevBlockID];
 | 
						|
        LocksetInitialized = true;
 | 
						|
      } else {
 | 
						|
        Entryset = intersectAndWarn(S, Entryset, ExitLocksets[PrevBlockID],
 | 
						|
                                LocksetFactory);
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    BuildLockset LocksetBuilder(S, Entryset, LocksetFactory);
 | 
						|
    for (CFGBlock::const_iterator BI = CurrBlock->begin(),
 | 
						|
         BE = CurrBlock->end(); BI != BE; ++BI) {
 | 
						|
      if (const CFGStmt *CfgStmt = dyn_cast<CFGStmt>(&*BI))
 | 
						|
        LocksetBuilder.Visit(const_cast<Stmt*>(CfgStmt->getStmt()));
 | 
						|
    }
 | 
						|
    Exitset = LocksetBuilder.getLockset();
 | 
						|
 | 
						|
    // For every back edge from CurrBlock (the end of the loop) to another block
 | 
						|
    // (FirstLoopBlock) we need to check that the Lockset of Block is equal to
 | 
						|
    // the one held at the beginning of FirstLoopBlock. We can look up the
 | 
						|
    // Lockset held at the beginning of FirstLoopBlock in the EntryLockSets map.
 | 
						|
    for (CFGBlock::const_succ_iterator SI = CurrBlock->succ_begin(),
 | 
						|
         SE  = CurrBlock->succ_end(); SI != SE; ++SI) {
 | 
						|
 | 
						|
      // if CurrBlock -> *SI is *not* a back edge
 | 
						|
      if (*SI == 0 || !VisitedBlocks.alreadySet(*SI))
 | 
						|
        continue;
 | 
						|
 | 
						|
      CFGBlock *FirstLoopBlock = *SI;
 | 
						|
      SourceLocation FirstLoopLocation = getFirstStmtLocation(FirstLoopBlock);
 | 
						|
 | 
						|
      assert(FirstLoopLocation.isValid());
 | 
						|
      // Fail gracefully in release code.
 | 
						|
      if (!FirstLoopLocation.isValid())
 | 
						|
        continue;
 | 
						|
 | 
						|
      Lockset PreLoop = EntryLocksets[FirstLoopBlock->getBlockID()];
 | 
						|
      Lockset LoopEnd = ExitLocksets[CurrBlockID];
 | 
						|
      warnBackEdgeUnequalLocksets(S, LoopEnd, PreLoop, FirstLoopLocation);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  Lockset FinalLockset = ExitLocksets[CFGraph->getExit().getBlockID()];
 | 
						|
  if (!FinalLockset.isEmpty()) {
 | 
						|
    DiagList Warnings;
 | 
						|
    for (Lockset::iterator I=FinalLockset.begin(), E=FinalLockset.end();
 | 
						|
         I != E; ++I) {
 | 
						|
      const LockID &MissingLock = I.getKey();
 | 
						|
      const LockData &MissingLockData = I.getData();
 | 
						|
 | 
						|
      std::string FunName = "<unknown>";
 | 
						|
      if (const NamedDecl *ContextDecl = dyn_cast<NamedDecl>(AC.getDecl())) {
 | 
						|
        FunName = ContextDecl->getDeclName().getAsString();
 | 
						|
      }
 | 
						|
 | 
						|
      PartialDiagnostic Warning =
 | 
						|
        S.PDiag(diag::warn_locks_not_released)
 | 
						|
          << MissingLock.getName() << FunName;
 | 
						|
      Warnings.push_back(DelayedDiag(MissingLockData.AcquireLoc, Warning));
 | 
						|
    }
 | 
						|
    EmitDiagnostics(S, Warnings);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
//===----------------------------------------------------------------------===//
 | 
						|
// AnalysisBasedWarnings - Worker object used by Sema to execute analysis-based
 | 
						|
//  warnings on a function, method, or block.
 | 
						|
//===----------------------------------------------------------------------===//
 | 
						|
 | 
						|
clang::sema::AnalysisBasedWarnings::Policy::Policy() {
 | 
						|
  enableCheckFallThrough = 1;
 | 
						|
  enableCheckUnreachable = 0;
 | 
						|
  enableThreadSafetyAnalysis = 0;
 | 
						|
}
 | 
						|
 | 
						|
clang::sema::AnalysisBasedWarnings::AnalysisBasedWarnings(Sema &s)
 | 
						|
  : S(s),
 | 
						|
    NumFunctionsAnalyzed(0),
 | 
						|
    NumFunctionsWithBadCFGs(0),
 | 
						|
    NumCFGBlocks(0),
 | 
						|
    MaxCFGBlocksPerFunction(0),
 | 
						|
    NumUninitAnalysisFunctions(0),
 | 
						|
    NumUninitAnalysisVariables(0),
 | 
						|
    MaxUninitAnalysisVariablesPerFunction(0),
 | 
						|
    NumUninitAnalysisBlockVisits(0),
 | 
						|
    MaxUninitAnalysisBlockVisitsPerFunction(0) {
 | 
						|
  Diagnostic &D = S.getDiagnostics();
 | 
						|
  DefaultPolicy.enableCheckUnreachable = (unsigned)
 | 
						|
    (D.getDiagnosticLevel(diag::warn_unreachable, SourceLocation()) !=
 | 
						|
        Diagnostic::Ignored);
 | 
						|
  DefaultPolicy.enableThreadSafetyAnalysis = (unsigned)
 | 
						|
    (D.getDiagnosticLevel(diag::warn_double_lock, SourceLocation()) !=
 | 
						|
     Diagnostic::Ignored);
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
static void flushDiagnostics(Sema &S, sema::FunctionScopeInfo *fscope) {
 | 
						|
  for (SmallVectorImpl<sema::PossiblyUnreachableDiag>::iterator
 | 
						|
       i = fscope->PossiblyUnreachableDiags.begin(),
 | 
						|
       e = fscope->PossiblyUnreachableDiags.end();
 | 
						|
       i != e; ++i) {
 | 
						|
    const sema::PossiblyUnreachableDiag &D = *i;
 | 
						|
    S.Diag(D.Loc, D.PD);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void clang::sema::
 | 
						|
AnalysisBasedWarnings::IssueWarnings(sema::AnalysisBasedWarnings::Policy P,
 | 
						|
                                     sema::FunctionScopeInfo *fscope,
 | 
						|
                                     const Decl *D, const BlockExpr *blkExpr) {
 | 
						|
 | 
						|
  // 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.
 | 
						|
  Diagnostic &Diags = S.getDiagnostics();
 | 
						|
 | 
						|
  // Do not do any analysis for declarations in system headers if we are
 | 
						|
  // going to just ignore them.
 | 
						|
  if (Diags.getSuppressSystemWarnings() &&
 | 
						|
      S.SourceMgr.isInSystemHeader(D->getLocation()))
 | 
						|
    return;
 | 
						|
 | 
						|
  // For code in dependent contexts, we'll do this at instantiation time.
 | 
						|
  if (cast<DeclContext>(D)->isDependentContext())
 | 
						|
    return;
 | 
						|
 | 
						|
  if (Diags.hasErrorOccurred() || Diags.hasFatalErrorOccurred()) {
 | 
						|
    // Flush out any possibly unreachable diagnostics.
 | 
						|
    flushDiagnostics(S, fscope);
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  
 | 
						|
  const Stmt *Body = D->getBody();
 | 
						|
  assert(Body);
 | 
						|
 | 
						|
  AnalysisContext AC(D, 0);
 | 
						|
 | 
						|
  // 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.
 | 
						|
  AC.getCFGBuildOptions().PruneTriviallyFalseEdges = true;
 | 
						|
  AC.getCFGBuildOptions().AddEHEdges = false;
 | 
						|
  AC.getCFGBuildOptions().AddInitializers = true;
 | 
						|
  AC.getCFGBuildOptions().AddImplicitDtors = true;
 | 
						|
  
 | 
						|
  // Force that certain expressions appear as CFGElements in the CFG.  This
 | 
						|
  // is used to speed up various analyses.
 | 
						|
  // FIXME: This isn't the right factoring.  This is here for initial
 | 
						|
  // prototyping, but we need a way for analyses to say what expressions they
 | 
						|
  // expect to always be CFGElements and then fill in the BuildOptions
 | 
						|
  // appropriately.  This is essentially a layering violation.
 | 
						|
  if (P.enableCheckUnreachable) {
 | 
						|
    // Unreachable code analysis requires a linearized CFG.
 | 
						|
    AC.getCFGBuildOptions().setAllAlwaysAdd();
 | 
						|
  }
 | 
						|
  else {
 | 
						|
    AC.getCFGBuildOptions()
 | 
						|
      .setAlwaysAdd(Stmt::BinaryOperatorClass)
 | 
						|
      .setAlwaysAdd(Stmt::BlockExprClass)
 | 
						|
      .setAlwaysAdd(Stmt::CStyleCastExprClass)
 | 
						|
      .setAlwaysAdd(Stmt::DeclRefExprClass)
 | 
						|
      .setAlwaysAdd(Stmt::ImplicitCastExprClass)
 | 
						|
      .setAlwaysAdd(Stmt::UnaryOperatorClass);
 | 
						|
  }
 | 
						|
 | 
						|
  // Construct the analysis context with the specified CFG build options.
 | 
						|
  
 | 
						|
  // Emit delayed diagnostics.
 | 
						|
  if (!fscope->PossiblyUnreachableDiags.empty()) {
 | 
						|
    bool analyzed = false;
 | 
						|
 | 
						|
    // Register the expressions with the CFGBuilder.
 | 
						|
    for (SmallVectorImpl<sema::PossiblyUnreachableDiag>::iterator
 | 
						|
         i = fscope->PossiblyUnreachableDiags.begin(),
 | 
						|
         e = fscope->PossiblyUnreachableDiags.end();
 | 
						|
         i != e; ++i) {
 | 
						|
      if (const Stmt *stmt = i->stmt)
 | 
						|
        AC.registerForcedBlockExpression(stmt);
 | 
						|
    }
 | 
						|
 | 
						|
    if (AC.getCFG()) {
 | 
						|
      analyzed = true;
 | 
						|
      for (SmallVectorImpl<sema::PossiblyUnreachableDiag>::iterator
 | 
						|
            i = fscope->PossiblyUnreachableDiags.begin(),
 | 
						|
            e = fscope->PossiblyUnreachableDiags.end();
 | 
						|
            i != e; ++i)
 | 
						|
      {
 | 
						|
        const sema::PossiblyUnreachableDiag &D = *i;
 | 
						|
        bool processed = false;
 | 
						|
        if (const Stmt *stmt = i->stmt) {
 | 
						|
          const CFGBlock *block = AC.getBlockForRegisteredExpression(stmt);
 | 
						|
          assert(block);
 | 
						|
          if (CFGReverseBlockReachabilityAnalysis *cra = AC.getCFGReachablityAnalysis()) {
 | 
						|
            // Can this block be reached from the entrance?
 | 
						|
            if (cra->isReachable(&AC.getCFG()->getEntry(), block))
 | 
						|
              S.Diag(D.Loc, D.PD);
 | 
						|
            processed = true;
 | 
						|
          }
 | 
						|
        }
 | 
						|
        if (!processed) {
 | 
						|
          // Emit the warning anyway if we cannot map to a basic block.
 | 
						|
          S.Diag(D.Loc, D.PD);
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    if (!analyzed)
 | 
						|
      flushDiagnostics(S, fscope);
 | 
						|
  }
 | 
						|
  
 | 
						|
  
 | 
						|
  // Warning: check missing 'return'
 | 
						|
  if (P.enableCheckFallThrough) {
 | 
						|
    const CheckFallThroughDiagnostics &CD =
 | 
						|
      (isa<BlockDecl>(D) ? CheckFallThroughDiagnostics::MakeForBlock()
 | 
						|
                         : CheckFallThroughDiagnostics::MakeForFunction(D));
 | 
						|
    CheckFallThroughForBody(S, D, Body, blkExpr, CD, AC);
 | 
						|
  }
 | 
						|
 | 
						|
  // Warning: check for unreachable code
 | 
						|
  if (P.enableCheckUnreachable)
 | 
						|
    CheckUnreachable(S, AC);
 | 
						|
  
 | 
						|
  // Check for thread safety violations
 | 
						|
  if (P.enableThreadSafetyAnalysis)
 | 
						|
    checkThreadSafety(S, AC);
 | 
						|
 | 
						|
  if (Diags.getDiagnosticLevel(diag::warn_uninit_var, D->getLocStart())
 | 
						|
      != Diagnostic::Ignored ||
 | 
						|
      Diags.getDiagnosticLevel(diag::warn_maybe_uninit_var, D->getLocStart())
 | 
						|
      != Diagnostic::Ignored) {
 | 
						|
    if (CFG *cfg = AC.getCFG()) {
 | 
						|
      UninitValsDiagReporter reporter(S);
 | 
						|
      UninitVariablesAnalysisStats stats;
 | 
						|
      std::memset(&stats, 0, sizeof(UninitVariablesAnalysisStats));
 | 
						|
      runUninitializedVariablesAnalysis(*cast<DeclContext>(D), *cfg, AC,
 | 
						|
                                        reporter, stats);
 | 
						|
 | 
						|
      if (S.CollectStats && stats.NumVariablesAnalyzed > 0) {
 | 
						|
        ++NumUninitAnalysisFunctions;
 | 
						|
        NumUninitAnalysisVariables += stats.NumVariablesAnalyzed;
 | 
						|
        NumUninitAnalysisBlockVisits += stats.NumBlockVisits;
 | 
						|
        MaxUninitAnalysisVariablesPerFunction =
 | 
						|
            std::max(MaxUninitAnalysisVariablesPerFunction,
 | 
						|
                     stats.NumVariablesAnalyzed);
 | 
						|
        MaxUninitAnalysisBlockVisitsPerFunction =
 | 
						|
            std::max(MaxUninitAnalysisBlockVisitsPerFunction,
 | 
						|
                     stats.NumBlockVisits);
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  // Collect statistics about the CFG if it was built.
 | 
						|
  if (S.CollectStats && AC.isCFGBuilt()) {
 | 
						|
    ++NumFunctionsAnalyzed;
 | 
						|
    if (CFG *cfg = AC.getCFG()) {
 | 
						|
      // If we successfully built a CFG for this context, record some more
 | 
						|
      // detail information about it.
 | 
						|
      NumCFGBlocks += cfg->getNumBlockIDs();
 | 
						|
      MaxCFGBlocksPerFunction = std::max(MaxCFGBlocksPerFunction,
 | 
						|
                                         cfg->getNumBlockIDs());
 | 
						|
    } else {
 | 
						|
      ++NumFunctionsWithBadCFGs;
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void clang::sema::AnalysisBasedWarnings::PrintStats() const {
 | 
						|
  llvm::errs() << "\n*** Analysis Based Warnings Stats:\n";
 | 
						|
 | 
						|
  unsigned NumCFGsBuilt = NumFunctionsAnalyzed - NumFunctionsWithBadCFGs;
 | 
						|
  unsigned AvgCFGBlocksPerFunction =
 | 
						|
      !NumCFGsBuilt ? 0 : NumCFGBlocks/NumCFGsBuilt;
 | 
						|
  llvm::errs() << NumFunctionsAnalyzed << " functions analyzed ("
 | 
						|
               << NumFunctionsWithBadCFGs << " w/o CFGs).\n"
 | 
						|
               << "  " << NumCFGBlocks << " CFG blocks built.\n"
 | 
						|
               << "  " << AvgCFGBlocksPerFunction
 | 
						|
               << " average CFG blocks per function.\n"
 | 
						|
               << "  " << MaxCFGBlocksPerFunction
 | 
						|
               << " max CFG blocks per function.\n";
 | 
						|
 | 
						|
  unsigned AvgUninitVariablesPerFunction = !NumUninitAnalysisFunctions ? 0
 | 
						|
      : NumUninitAnalysisVariables/NumUninitAnalysisFunctions;
 | 
						|
  unsigned AvgUninitBlockVisitsPerFunction = !NumUninitAnalysisFunctions ? 0
 | 
						|
      : NumUninitAnalysisBlockVisits/NumUninitAnalysisFunctions;
 | 
						|
  llvm::errs() << NumUninitAnalysisFunctions
 | 
						|
               << " functions analyzed for uninitialiazed variables\n"
 | 
						|
               << "  " << NumUninitAnalysisVariables << " variables analyzed.\n"
 | 
						|
               << "  " << AvgUninitVariablesPerFunction
 | 
						|
               << " average variables per function.\n"
 | 
						|
               << "  " << MaxUninitAnalysisVariablesPerFunction
 | 
						|
               << " max variables per function.\n"
 | 
						|
               << "  " << NumUninitAnalysisBlockVisits << " block visits.\n"
 | 
						|
               << "  " << AvgUninitBlockVisitsPerFunction
 | 
						|
               << " average block visits per function.\n"
 | 
						|
               << "  " << MaxUninitAnalysisBlockVisitsPerFunction
 | 
						|
               << " max block visits per function.\n";
 | 
						|
}
 |