Implement the rules in C++ [basic.link] and C99 6.2.2 for computing
the linkage of a declaration. Switch the lame (and completely wrong)
NamedDecl::hasLinkage() over to using the new NamedDecl::getLinkage(),
along with the "can this declaration be a template argument?" check
that started all of this.

Fixes -fsyntax-only for PR5597.

llvm-svn: 89891
diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp
index bdc8047..b52c7ad 100644
--- a/clang/lib/AST/Decl.cpp
+++ b/clang/lib/AST/Decl.cpp
@@ -211,6 +211,203 @@
 // NamedDecl Implementation
 //===----------------------------------------------------------------------===//
 
+static NamedDecl::Linkage getLinkageForNamespaceScopeDecl(const NamedDecl *D) {
+  assert(D->getDeclContext()->getLookupContext()->isFileContext() &&
+         "Not a name having namespace scope");
+  ASTContext &Context = D->getASTContext();
+
+  // C++ [basic.link]p3:
+  //   A name having namespace scope (3.3.6) has internal linkage if it
+  //   is the name of
+  //     - an object, reference, function or function template that is
+  //       explicitly declared static; or,
+  // (This bullet corresponds to C99 6.2.2p3.)
+  if (const VarDecl *Var = dyn_cast<VarDecl>(D)) {
+    // Explicitly declared static.
+    if (Var->getStorageClass() == VarDecl::Static)
+      return NamedDecl::InternalLinkage;
+
+    // - an object or reference that is explicitly declared const
+    //   and neither explicitly declared extern nor previously
+    //   declared to have external linkage; or
+    // (there is no equivalent in C99)
+    if (Context.getLangOptions().CPlusPlus &&
+        Var->getType().isConstQualified() && 
+        Var->getStorageClass() != VarDecl::Extern &&
+        Var->getStorageClass() != VarDecl::PrivateExtern) {
+      bool FoundExtern = false;
+      for (const VarDecl *PrevVar = Var->getPreviousDeclaration();
+           PrevVar && !FoundExtern; 
+           PrevVar = PrevVar->getPreviousDeclaration())
+        if (PrevVar->getLinkage() == NamedDecl::ExternalLinkage)
+          FoundExtern = true;
+      
+      if (!FoundExtern)
+        return NamedDecl::InternalLinkage;
+    }
+  } else if (isa<FunctionDecl>(D) || isa<FunctionTemplateDecl>(D)) {
+    const FunctionDecl *Function = 0;
+    if (const FunctionTemplateDecl *FunTmpl
+                                        = dyn_cast<FunctionTemplateDecl>(D))
+      Function = FunTmpl->getTemplatedDecl();
+    else
+      Function = cast<FunctionDecl>(D);
+
+    // Explicitly declared static.
+    if (Function->getStorageClass() == FunctionDecl::Static)
+      return NamedDecl::InternalLinkage;
+  } else if (const FieldDecl *Field = dyn_cast<FieldDecl>(D)) {
+    //   - a data member of an anonymous union.
+    if (cast<RecordDecl>(Field->getDeclContext())->isAnonymousStructOrUnion())
+      return NamedDecl::InternalLinkage;
+  }
+
+  // C++ [basic.link]p4:
+  
+  //   A name having namespace scope has external linkage if it is the
+  //   name of
+  //
+  //     - an object or reference, unless it has internal linkage; or
+  if (const VarDecl *Var = dyn_cast<VarDecl>(D)) {
+    if (!Context.getLangOptions().CPlusPlus &&
+        (Var->getStorageClass() == VarDecl::Extern ||
+         Var->getStorageClass() == VarDecl::PrivateExtern)) {
+      // C99 6.2.2p4:
+      //   For an identifier declared with the storage-class specifier
+      //   extern in a scope in which a prior declaration of that
+      //   identifier is visible, if the prior declaration specifies
+      //   internal or external linkage, the linkage of the identifier
+      //   at the later declaration is the same as the linkage
+      //   specified at the prior declaration. If no prior declaration
+      //   is visible, or if the prior declaration specifies no
+      //   linkage, then the identifier has external linkage.
+      if (const VarDecl *PrevVar = Var->getPreviousDeclaration()) {
+        if (NamedDecl::Linkage L = PrevVar->getLinkage())
+          return L;
+      }
+    }
+
+    // C99 6.2.2p5:
+    //   If the declaration of an identifier for an object has file
+    //   scope and no storage-class specifier, its linkage is
+    //   external.
+    return NamedDecl::ExternalLinkage;
+  }
+
+  //     - a function, unless it has internal linkage; or
+  if (const FunctionDecl *Function = dyn_cast<FunctionDecl>(D)) {
+    // C99 6.2.2p5:
+    //   If the declaration of an identifier for a function has no
+    //   storage-class specifier, its linkage is determined exactly
+    //   as if it were declared with the storage-class specifier
+    //   extern.
+    if (!Context.getLangOptions().CPlusPlus &&
+        (Function->getStorageClass() == FunctionDecl::Extern ||
+         Function->getStorageClass() == FunctionDecl::PrivateExtern ||
+         Function->getStorageClass() == FunctionDecl::None)) {
+      // C99 6.2.2p4:
+      //   For an identifier declared with the storage-class specifier
+      //   extern in a scope in which a prior declaration of that
+      //   identifier is visible, if the prior declaration specifies
+      //   internal or external linkage, the linkage of the identifier
+      //   at the later declaration is the same as the linkage
+      //   specified at the prior declaration. If no prior declaration
+      //   is visible, or if the prior declaration specifies no
+      //   linkage, then the identifier has external linkage.
+      if (const FunctionDecl *PrevFunc = Function->getPreviousDeclaration()) {
+        if (NamedDecl::Linkage L = PrevFunc->getLinkage())
+          return L;
+      }
+    }
+
+    return NamedDecl::ExternalLinkage;
+  }
+
+  //     - a named class (Clause 9), or an unnamed class defined in a
+  //       typedef declaration in which the class has the typedef name
+  //       for linkage purposes (7.1.3); or
+  //     - a named enumeration (7.2), or an unnamed enumeration
+  //       defined in a typedef declaration in which the enumeration
+  //       has the typedef name for linkage purposes (7.1.3); or
+  if (const TagDecl *Tag = dyn_cast<TagDecl>(D))
+    if (Tag->getDeclName() || Tag->getTypedefForAnonDecl())
+      return NamedDecl::ExternalLinkage;
+
+  //     - an enumerator belonging to an enumeration with external linkage;
+  if (isa<EnumConstantDecl>(D))
+    if (cast<NamedDecl>(D->getDeclContext())->getLinkage() 
+                                                 == NamedDecl::ExternalLinkage)
+      return NamedDecl::ExternalLinkage;
+
+  //     - a template, unless it is a function template that has
+  //       internal linkage (Clause 14);
+  if (isa<TemplateDecl>(D))
+    return NamedDecl::ExternalLinkage;
+
+  //     - a namespace (7.3), unless it is declared within an unnamed
+  //       namespace.
+  if (isa<NamespaceDecl>(D) && !D->isInAnonymousNamespace())
+    return NamedDecl::ExternalLinkage;
+
+  return NamedDecl::NoLinkage;
+}
+
+NamedDecl::Linkage NamedDecl::getLinkage() const {
+  // Handle linkage for namespace-scope names.
+  if (getDeclContext()->getLookupContext()->isFileContext())
+    if (Linkage L = getLinkageForNamespaceScopeDecl(this))
+      return L;
+  
+  // C++ [basic.link]p5:
+  //   In addition, a member function, static data member, a named
+  //   class or enumeration of class scope, or an unnamed class or
+  //   enumeration defined in a class-scope typedef declaration such
+  //   that the class or enumeration has the typedef name for linkage
+  //   purposes (7.1.3), has external linkage if the name of the class
+  //   has external linkage.
+  if (getDeclContext()->isRecord() &&
+      (isa<CXXMethodDecl>(this) || isa<VarDecl>(this) ||
+       (isa<TagDecl>(this) &&
+        (getDeclName() || cast<TagDecl>(this)->getTypedefForAnonDecl()))) &&
+      cast<RecordDecl>(getDeclContext())->getLinkage() == ExternalLinkage)
+    return ExternalLinkage;
+
+  // C++ [basic.link]p6:
+  //   The name of a function declared in block scope and the name of
+  //   an object declared by a block scope extern declaration have
+  //   linkage. If there is a visible declaration of an entity with
+  //   linkage having the same name and type, ignoring entities
+  //   declared outside the innermost enclosing namespace scope, the
+  //   block scope declaration declares that same entity and receives
+  //   the linkage of the previous declaration. If there is more than
+  //   one such matching entity, the program is ill-formed. Otherwise,
+  //   if no matching entity is found, the block scope entity receives
+  //   external linkage.
+  if (getLexicalDeclContext()->isFunctionOrMethod()) {
+    if (const FunctionDecl *Function = dyn_cast<FunctionDecl>(this)) {
+      if (Function->getPreviousDeclaration())
+        if (Linkage L = Function->getPreviousDeclaration()->getLinkage())
+          return L;
+
+      return ExternalLinkage;
+    }
+
+    if (const VarDecl *Var = dyn_cast<VarDecl>(this))
+      if (Var->getStorageClass() == VarDecl::Extern ||
+          Var->getStorageClass() == VarDecl::PrivateExtern) {
+        if (Var->getPreviousDeclaration())
+          if (Linkage L = Var->getPreviousDeclaration()->getLinkage())
+            return L;
+
+        return ExternalLinkage;
+      }
+  }
+
+  // C++ [basic.link]p6:
+  //   Names not covered by these rules have no linkage.
+  return NoLinkage;
+}
+
 std::string NamedDecl::getQualifiedNameAsString() const {
   return getQualifiedNameAsString(getASTContext().getLangOptions());
 }
@@ -300,13 +497,7 @@
 }
 
 bool NamedDecl::hasLinkage() const {
-  if (const VarDecl *VD = dyn_cast<VarDecl>(this))
-    return VD->hasExternalStorage() || VD->isFileVarDecl();
-
-  if (isa<FunctionDecl>(this) && !isa<CXXMethodDecl>(this))
-    return true;
-
-  return false;
+  return getLinkage() != NoLinkage;
 }
 
 NamedDecl *NamedDecl::getUnderlyingDecl() {