In C, when we see a function declaration within a local scope, export
that declaration to global scope so that it can be found from other
scopes. This allows us to diagnose redeclaration errors for external
declarations across scopes. We also warn when name lookup finds such
an out-of-scope declaration. This is part of <rdar://problem/6127293>;
we'll also need to do the same thing for variables.



git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@65373 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp
index aef6661..a58752c 100644
--- a/lib/Sema/SemaExpr.cpp
+++ b/lib/Sema/SemaExpr.cpp
@@ -70,12 +70,13 @@
   }
 
   // See if this is a deleted function.
-  if (FunctionDecl *FD = dyn_cast<FunctionDecl>(D))
+  if (FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) {
     if (FD->isDeleted()) {
       Diag(Loc, diag::err_deleted_function_use);
       Diag(D->getLocation(), diag::note_unavailable_here) << true;
       return true;
     }
+  }
 
   // See if the decl is unavailable
   if (D->getAttr<UnavailableAttr>()) {
@@ -83,6 +84,28 @@
     Diag(D->getLocation(), diag::note_unavailable_here) << 0;
   }
 
+  if (D->getDeclContext()->isFunctionOrMethod() && 
+      !D->getDeclContext()->Encloses(CurContext)) {
+    // We've found the name of a function or variable that was
+    // declared with external linkage within another function (and,
+    // therefore, a scope where we wouldn't normally see the
+    // declaration). Once we've made sure that no previous declaration
+    // was properly made visible, produce a warning.
+    bool HasGlobalScopedDeclaration = false;
+    for (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D); FD;
+         FD = FD->getPreviousDeclaration()) {
+      if (FD->getDeclContext()->isFileContext()) {
+        HasGlobalScopedDeclaration = true;
+        break;
+      }
+    }
+    // FIXME: do the same thing for variable declarations
+    
+    if (!HasGlobalScopedDeclaration) {
+      Diag(Loc, diag::warn_use_out_of_scope_declaration) << D;
+      Diag(D->getLocation(), diag::note_previous_declaration);
+    }
+  }
 
   return false;
 }