For forward-declared static inline functions, delay CFG-based warnings until we
encounter a definition.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@99243 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/Sema/AnalysisBasedWarnings.cpp b/lib/Sema/AnalysisBasedWarnings.cpp
index a044576..45b08db 100644
--- a/lib/Sema/AnalysisBasedWarnings.cpp
+++ b/lib/Sema/AnalysisBasedWarnings.cpp
@@ -353,8 +353,14 @@
 
     // Only analyze 'static inline' functions when explicitly asked.
     if (!analyzeStaticInline && FD->isInlineSpecified() &&
-        FD->getStorageClass() == FunctionDecl::Static)
-      return;
+        FD->getStorageClass() == FunctionDecl::Static) {
+      FD = FD->getCanonicalDecl();
+      VisitFlag &visitFlag = VisitedFD[FD];
+      if (visitFlag == Pending)
+        visitFlag = Visited;
+      else
+        return;
+    }
   }
 
   const Stmt *Body = D->getBody();
@@ -397,18 +403,26 @@
             if (const DeclRefExpr *DR =
                 dyn_cast<DeclRefExpr>(CE->getCallee()->IgnoreParenCasts()))
               if (const FunctionDecl *calleeD =
-                  dyn_cast<FunctionDecl>(DR->getDecl()))
+                  dyn_cast<FunctionDecl>(DR->getDecl())) {
+                calleeD = calleeD->getCanonicalDecl();
                 if (calleeD->isInlineSpecified() &&
                     calleeD->getStorageClass() == FunctionDecl::Static) {
                   // Have we analyzed this static inline function before?
-                  unsigned &visited = VisitedFD[calleeD];
-                  if (!visited) {
+                  VisitFlag &visitFlag = VisitedFD[calleeD];
+                  if (visitFlag == NotVisited) {
                     // Mark the callee visited prior to analyzing it
                     // so we terminate in case of recursion.
-                    visited = 1;
-                    IssueWarnings(DefaultPolicy, calleeD, QualType(), true);
+                    if (calleeD->getBody()) {
+                      visitFlag = Visited;
+                      IssueWarnings(DefaultPolicy, calleeD, QualType(), true);
+                    }
+                    else {
+                      // Delay warnings until we encounter the definition.
+                      visitFlag = Pending;
+                    }
                   }
                 }
+              }
       }
     }
   }
diff --git a/lib/Sema/AnalysisBasedWarnings.h b/lib/Sema/AnalysisBasedWarnings.h
index 26e973a..b5db8af 100644
--- a/lib/Sema/AnalysisBasedWarnings.h
+++ b/lib/Sema/AnalysisBasedWarnings.h
@@ -39,7 +39,8 @@
   Sema &S;
   Policy DefaultPolicy;
 
-  llvm::DenseMap<const FunctionDecl*, unsigned> VisitedFD;
+  enum VisitFlag { NotVisited = 0, Visited = 1, Pending = 2 };
+  llvm::DenseMap<const FunctionDecl*, VisitFlag> VisitedFD;
 
 public:
   AnalysisBasedWarnings(Sema &s);
diff --git a/test/Sema/return.c b/test/Sema/return.c
index fad81ad..fab6a82 100644
--- a/test/Sema/return.c
+++ b/test/Sema/return.c
@@ -227,12 +227,15 @@
 // when they are used.
 static inline int si_has_missing_return() {} // no-warning
 static inline int si_has_missing_return_2() {}; // expected-warning{{control reaches end of non-void function}}
+static inline int si_forward();
 static inline int si_has_missing_return_3(int x) {
   if (x)
    return si_has_missing_return_3(x+1);
 } // expected-warning{{control may reach end of non-void function}}
 
 int test_static_inline(int x) {
+  si_forward();
   return x ? si_has_missing_return_2() : si_has_missing_return_3(x);
 }
+static inline int si_forward() {} // expected-warning{{control reaches end of non-void function}}