Skip to content
AnalysisBasedWarnings.cpp 49.3 KiB
Newer Older
  // Helper functions
  void warnLockMismatch(unsigned DiagID, Name LockName, SourceLocation Loc) {
    // Gracefully handle rare cases when the analysis can't get a more
    // precise source location.
    if (!Loc.isValid())
      Loc = FunLocation;
Richard Smith's avatar
Richard Smith committed
    PartialDiagnosticAt Warning(Loc, S.PDiag(DiagID) << LockName);
    Warnings.push_back(DelayedDiag(Warning, OptionalNotes()));
Richard Smith's avatar
Richard Smith committed
  ThreadSafetyReporter(Sema &S, SourceLocation FL, SourceLocation FEL)
    : S(S), FunLocation(FL), FunEndLocation(FEL) {}

  /// \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.
  void emitDiagnostics() {
    Warnings.sort(SortDiagBySourceLocation(S.getSourceManager()));
    for (DiagList::iterator I = Warnings.begin(), E = Warnings.end();
Richard Smith's avatar
Richard Smith committed
         I != E; ++I) {
      S.Diag(I->first.first, I->first.second);
      const OptionalNotes &Notes = I->second;
      for (unsigned NoteI = 0, NoteN = Notes.size(); NoteI != NoteN; ++NoteI)
        S.Diag(Notes[NoteI].first, Notes[NoteI].second);
    }
  void handleInvalidLockExp(SourceLocation Loc) {
Richard Smith's avatar
Richard Smith committed
    PartialDiagnosticAt Warning(Loc,
                                S.PDiag(diag::warn_cannot_resolve_lock) << Loc);
    Warnings.push_back(DelayedDiag(Warning, OptionalNotes()));
  void handleUnmatchedUnlock(Name LockName, SourceLocation Loc) {
    warnLockMismatch(diag::warn_unlock_but_no_lock, LockName, Loc);
  }

  void handleDoubleLock(Name LockName, SourceLocation Loc) {
    warnLockMismatch(diag::warn_double_lock, LockName, Loc);
  }

Richard Smith's avatar
Richard Smith committed
  void handleMutexHeldEndOfScope(Name LockName, SourceLocation LocLocked,
                                 SourceLocation LocEndOfScope,
                                 LockErrorKind LEK){
    unsigned DiagID = 0;
    switch (LEK) {
      case LEK_LockedSomePredecessors:
Richard Smith's avatar
Richard Smith committed
        DiagID = diag::warn_lock_some_predecessors;
        break;
      case LEK_LockedSomeLoopIterations:
        DiagID = diag::warn_expecting_lock_held_on_loop;
        break;
      case LEK_LockedAtEndOfFunction:
        DiagID = diag::warn_no_unlock;
        break;
      case LEK_NotLockedAtEndOfFunction:
        DiagID = diag::warn_expecting_locked;
        break;
Richard Smith's avatar
Richard Smith committed
    if (LocEndOfScope.isInvalid())
      LocEndOfScope = FunEndLocation;

    PartialDiagnosticAt Warning(LocEndOfScope, S.PDiag(DiagID) << LockName);
    PartialDiagnosticAt Note(LocLocked, S.PDiag(diag::note_locked_here));
    Warnings.push_back(DelayedDiag(Warning, OptionalNotes(1, Note)));
  }


  void handleExclusiveAndShared(Name LockName, SourceLocation Loc1,
                                SourceLocation Loc2) {
Richard Smith's avatar
Richard Smith committed
    PartialDiagnosticAt Warning(
      Loc1, S.PDiag(diag::warn_lock_exclusive_and_shared) << LockName);
    PartialDiagnosticAt Note(
      Loc2, S.PDiag(diag::note_lock_exclusive_and_shared) << LockName);
    Warnings.push_back(DelayedDiag(Warning, OptionalNotes(1, Note)));
  }

  void handleNoMutexHeld(const NamedDecl *D, ProtectedOperationKind POK,
                         AccessKind AK, SourceLocation Loc) {
    assert((POK == POK_VarAccess || POK == POK_VarDereference)
             && "Only works for variables");
    unsigned DiagID = POK == POK_VarAccess?
                        diag::warn_variable_requires_any_lock:
                        diag::warn_var_deref_requires_any_lock;
Richard Smith's avatar
Richard Smith committed
    PartialDiagnosticAt Warning(Loc, S.PDiag(DiagID)
      << D->getName() << getLockKindFromAccessKind(AK));
    Warnings.push_back(DelayedDiag(Warning, OptionalNotes()));
  }

  void handleMutexNotHeld(const NamedDecl *D, ProtectedOperationKind POK,
                          Name LockName, LockKind LK, SourceLocation Loc) {
    switch (POK) {
      case POK_VarAccess:
        DiagID = diag::warn_variable_requires_lock;
        break;
      case POK_VarDereference:
        DiagID = diag::warn_var_deref_requires_lock;
        break;
      case POK_FunctionCall:
        DiagID = diag::warn_fun_requires_lock;
        break;
    }
Richard Smith's avatar
Richard Smith committed
    PartialDiagnosticAt Warning(Loc, S.PDiag(DiagID)
      << D->getName() << LockName << LK);
    Warnings.push_back(DelayedDiag(Warning, OptionalNotes()));
  }

  void handleFunExcludesLock(Name FunName, Name LockName, SourceLocation Loc) {
Richard Smith's avatar
Richard Smith committed
    PartialDiagnosticAt Warning(Loc,
      S.PDiag(diag::warn_fun_excludes_mutex) << FunName << LockName);
    Warnings.push_back(DelayedDiag(Warning, OptionalNotes()));
//===----------------------------------------------------------------------===//
// AnalysisBasedWarnings - Worker object used by Sema to execute analysis-based
//  warnings on a function, method, or block.
//===----------------------------------------------------------------------===//

clang::sema::AnalysisBasedWarnings::Policy::Policy() {
clang::sema::AnalysisBasedWarnings::AnalysisBasedWarnings(Sema &s)
  : S(s),
    NumFunctionsAnalyzed(0),
    MaxCFGBlocksPerFunction(0),
    NumUninitAnalysisFunctions(0),
    NumUninitAnalysisVariables(0),
    MaxUninitAnalysisVariablesPerFunction(0),
    NumUninitAnalysisBlockVisits(0),
    MaxUninitAnalysisBlockVisitsPerFunction(0) {
  DiagnosticsEngine &D = S.getDiagnostics();
  DefaultPolicy.enableCheckUnreachable = (unsigned)
    (D.getDiagnosticLevel(diag::warn_unreachable, SourceLocation()) !=
        DiagnosticsEngine::Ignored);
  DefaultPolicy.enableThreadSafetyAnalysis = (unsigned)
    (D.getDiagnosticLevel(diag::warn_double_lock, SourceLocation()) !=
     DiagnosticsEngine::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,
                                     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.
  DiagnosticsEngine &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;
  }
  
  AnalysisDeclContext AC(/* AnalysisDeclContextManager */ 0, D);
  // 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;
  AC.getCFGBuildOptions().AddTemporaryDtors = 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 || P.enableThreadSafetyAnalysis) {
    // Unreachable code analysis and thread safety require a linearized CFG.
    AC.getCFGBuildOptions().setAllAlwaysAdd();
  }
  else {
    AC.getCFGBuildOptions()
      .setAlwaysAdd(Stmt::BinaryOperatorClass)
      .setAlwaysAdd(Stmt::CompoundAssignOperatorClass)
      .setAlwaysAdd(Stmt::BlockExprClass)
      .setAlwaysAdd(Stmt::CStyleCastExprClass)
      .setAlwaysAdd(Stmt::DeclRefExprClass)
      .setAlwaysAdd(Stmt::ImplicitCastExprClass)
      .setAlwaysAdd(Stmt::UnaryOperatorClass)
      .setAlwaysAdd(Stmt::AttributedStmtClass);

  // Construct the analysis context with the specified CFG build options.
  
  if (!fscope->PossiblyUnreachableDiags.empty()) {

    // 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);
          CFGReverseBlockReachabilityAnalysis *cra =
              AC.getCFGReachablityAnalysis();
          // FIXME: We should be able to assert that block is non-null, but
          // the CFG analysis can skip potentially-evaluated expressions in
          // edge cases; see test/Sema/vla-2.c.
          if (block && cra) {
            // Can this block be reached from the entrance?
            if (cra->isReachable(&AC.getCFG()->getEntry(), block))
        }
        if (!processed) {
          // Emit the warning anyway if we cannot map to a basic block.
          S.Diag(D.Loc, D.PD);
    const CheckFallThroughDiagnostics &CD =
      (isa<BlockDecl>(D) ? CheckFallThroughDiagnostics::MakeForBlock()
       : (isa<CXXMethodDecl>(D) &&
          cast<CXXMethodDecl>(D)->getOverloadedOperator() == OO_Call &&
          cast<CXXMethodDecl>(D)->getParent()->isLambda())
            ? CheckFallThroughDiagnostics::MakeForLambda()
            : CheckFallThroughDiagnostics::MakeForFunction(D));
    CheckFallThroughForBody(S, D, Body, blkExpr, CD, AC);
  if (P.enableCheckUnreachable) {
    // Only check for unreachable code on non-template instantiations.
    // Different template instantiations can effectively change the control-flow
    // and it is very difficult to prove that a snippet of code in a template
    // is unreachable for all instantiations.
    bool isTemplateInstantiation = false;
    if (const FunctionDecl *Function = dyn_cast<FunctionDecl>(D))
      isTemplateInstantiation = Function->isTemplateInstantiation();
    if (!isTemplateInstantiation)
  // Check for thread safety violations
  if (P.enableThreadSafetyAnalysis) {
    SourceLocation FL = AC.getDecl()->getLocation();
Richard Smith's avatar
Richard Smith committed
    SourceLocation FEL = AC.getDecl()->getLocEnd();
    thread_safety::ThreadSafetyReporter Reporter(S, FL, FEL);
    thread_safety::runThreadSafetyAnalysis(AC, Reporter);
    Reporter.emitDiagnostics();
  }
  if (Diags.getDiagnosticLevel(diag::warn_uninit_var, D->getLocStart())
      != DiagnosticsEngine::Ignored ||
      Diags.getDiagnosticLevel(diag::warn_sometimes_uninit_var,D->getLocStart())
      != DiagnosticsEngine::Ignored ||
      Diags.getDiagnosticLevel(diag::warn_maybe_uninit_var, D->getLocStart())
      != DiagnosticsEngine::Ignored) {
      UninitValsDiagReporter reporter(S);
Fariborz Jahanian's avatar
Fariborz Jahanian committed
      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);
      }
    }
  }

  bool FallThroughDiagFull =
      Diags.getDiagnosticLevel(diag::warn_unannotated_fallthrough,
                               D->getLocStart()) != DiagnosticsEngine::Ignored;
  bool FallThroughDiagPerFunction =
      Diags.getDiagnosticLevel(diag::warn_unannotated_fallthrough_per_function,
                               D->getLocStart()) != DiagnosticsEngine::Ignored;
  if (FallThroughDiagFull || FallThroughDiagPerFunction) {
    DiagnoseSwitchLabelsFallthrough(S, AC, !FallThroughDiagFull);
  // 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,

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";
}