Only perform CFG-based warnings on 'static inline' functions that
are called (transitively) by regular functions/blocks within a
translation untion.

llvm-svn: 99233
diff --git a/clang/lib/Sema/AnalysisBasedWarnings.cpp b/clang/lib/Sema/AnalysisBasedWarnings.cpp
index c4ceec0..a044576 100644
--- a/clang/lib/Sema/AnalysisBasedWarnings.cpp
+++ b/clang/lib/Sema/AnalysisBasedWarnings.cpp
@@ -189,7 +189,7 @@
   unsigned diag_AlwaysFallThrough_ReturnsNonVoid;
   unsigned diag_NeverFallThroughOrReturn;
   bool funMode;
-  
+
   static CheckFallThroughDiagnostics MakeForFunction() {
     CheckFallThroughDiagnostics D;
     D.diag_MaybeFallThrough_HasNoReturn =
@@ -205,7 +205,7 @@
     D.funMode = true;
     return D;
   }
-  
+
   static CheckFallThroughDiagnostics MakeForBlock() {
     CheckFallThroughDiagnostics D;
     D.diag_MaybeFallThrough_HasNoReturn =
@@ -221,7 +221,7 @@
     D.funMode = false;
     return D;
   }
-  
+
   bool checkDiagnostics(Diagnostic &D, bool ReturnsVoid,
                         bool HasNoReturn) const {
     if (funMode) {
@@ -232,7 +232,7 @@
         && (D.getDiagnosticLevel(diag::warn_suggest_noreturn_block)
               == Diagnostic::Ignored || !ReturnsVoid);
     }
-    
+
     // For blocks.
     return  ReturnsVoid && !HasNoReturn
             && (D.getDiagnosticLevel(diag::warn_suggest_noreturn_block)
@@ -262,7 +262,7 @@
     HasNoReturn = MD->hasAttr<NoReturnAttr>();
   }
   else if (isa<BlockDecl>(D)) {
-    if (const FunctionType *FT = 
+    if (const FunctionType *FT =
           BlockTy->getPointeeType()->getAs<FunctionType>()) {
       if (FT->getResultType()->isVoidType())
         ReturnsVoid = true;
@@ -276,7 +276,7 @@
   // 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)) {
@@ -312,25 +312,23 @@
 //  warnings on a function, method, or block.
 //===----------------------------------------------------------------------===//
 
+clang::sema::AnalysisBasedWarnings::Policy::Policy() {
+  enableCheckFallThrough = 1;
+  enableCheckUnreachable = 0;
+}
+
 clang::sema::AnalysisBasedWarnings::AnalysisBasedWarnings(Sema &s) : S(s) {
   Diagnostic &D = S.getDiagnostics();
-
-  enableCheckFallThrough = 1;
-
-  enableCheckUnreachable = (unsigned)
+  DefaultPolicy.enableCheckUnreachable = (unsigned)
     (D.getDiagnosticLevel(diag::warn_unreachable) != Diagnostic::Ignored);
 }
 
-void clang::sema::AnalysisBasedWarnings::IssueWarnings(const Decl *D,
-                                                       QualType BlockTy) {
-  
+void clang::sema::
+AnalysisBasedWarnings::IssueWarnings(sema::AnalysisBasedWarnings::Policy P,
+                                     const Decl *D, QualType BlockTy,
+                                     const bool analyzeStaticInline) {
+
   assert(BlockTy.isNull() || isa<BlockDecl>(D));
-  
-  // Do not do any analysis for declarations in system headers if we are
-  // going to just ignore them.
-  if (S.getDiagnostics().getSuppressSystemWarnings() &&
-      S.SourceMgr.isInSystemHeader(D->getLocation()))
-    return;
 
   // We avoid doing analysis-based warnings when there are errors for
   // two reasons:
@@ -339,13 +337,24 @@
   // (2) The code already has problems; running the analysis just takes more
   //     time.
   if (S.getDiagnostics().hasErrorOccurred())
-      return;
-  
+    return;
+
+  // Do not do any analysis for declarations in system headers if we are
+  // going to just ignore them.
+  if (S.getDiagnostics().getSuppressSystemWarnings() &&
+      S.SourceMgr.isInSystemHeader(D->getLocation()))
+    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;
+
+    // Only analyze 'static inline' functions when explicitly asked.
+    if (!analyzeStaticInline && FD->isInlineSpecified() &&
+        FD->getStorageClass() == FunctionDecl::Static)
+      return;
   }
 
   const Stmt *Body = D->getBody();
@@ -354,16 +363,53 @@
   // 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);
+  bool performedCheck = false;
 
   // Warning: check missing 'return'
-  if (enableCheckFallThrough) {
+  if (P.enableCheckFallThrough) {
     const CheckFallThroughDiagnostics &CD =
       (isa<BlockDecl>(D) ? CheckFallThroughDiagnostics::MakeForBlock()
                          : CheckFallThroughDiagnostics::MakeForFunction());
     CheckFallThroughForBody(S, D, Body, BlockTy, CD, AC);
+    performedCheck = true;
   }
 
   // Warning: check for unreachable code
-  if (enableCheckUnreachable)
+  if (P.enableCheckUnreachable) {
     CheckUnreachable(S, AC);
+    performedCheck = true;
+  }
+
+  // If this block or function calls a 'static inline' function,
+  // we should analyze those functions as well.
+  if (performedCheck) {
+    // The CFG should already be constructed, so this should not
+    // incur any extra cost.  We might not have a CFG, however, for
+    // invalid code.
+    if (const CFG *cfg = AC.getCFG()) {
+      // All CallExprs are block-level expressions in the CFG.  This means
+      // that walking the basic blocks in the CFG is more efficient
+      // than walking the entire AST to find all calls.
+      for (CFG::const_iterator I=cfg->begin(), E=cfg->end(); I!=E; ++I) {
+        const CFGBlock *B = *I;
+        for (CFGBlock::const_iterator BI=B->begin(), BE=B->end(); BI!=BE; ++BI)
+          if (const CallExpr *CE = dyn_cast<CallExpr>(*BI))
+            if (const DeclRefExpr *DR =
+                dyn_cast<DeclRefExpr>(CE->getCallee()->IgnoreParenCasts()))
+              if (const FunctionDecl *calleeD =
+                  dyn_cast<FunctionDecl>(DR->getDecl()))
+                if (calleeD->isInlineSpecified() &&
+                    calleeD->getStorageClass() == FunctionDecl::Static) {
+                  // Have we analyzed this static inline function before?
+                  unsigned &visited = VisitedFD[calleeD];
+                  if (!visited) {
+                    // Mark the callee visited prior to analyzing it
+                    // so we terminate in case of recursion.
+                    visited = 1;
+                    IssueWarnings(DefaultPolicy, calleeD, QualType(), true);
+                  }
+                }
+      }
+    }
+  }
 }