diff options
| -rw-r--r-- | clang/include/clang/Basic/DiagnosticSemaKinds.td | 6 | ||||
| -rw-r--r-- | clang/lib/Sema/SemaDecl.cpp | 69 | ||||
| -rw-r--r-- | clang/test/SemaCXX/MicrosoftExtensions.cpp | 23 | ||||
| -rw-r--r-- | clang/test/SemaCXX/ms-friend-lookup.cpp | 104 | 
4 files changed, 172 insertions, 30 deletions
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 5ab9aef94e4..e48da103503 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -997,7 +997,11 @@ def warn_template_qualified_friend_ignored : Warning<    "dependent nested name specifier '%0' for friend template declaration is "    "not supported; ignoring this friend declaration">,    InGroup<UnsupportedFriend>; -   +def ext_friend_tag_redecl_outside_namespace : ExtWarn< +  "unqualified friend declaration referring to type outside of the nearest " +  "enclosing namespace is a Microsoft extension; add a nested name specifier">, +  InGroup<Microsoft>; +  def err_invalid_member_in_interface : Error<    "%select{data member |non-public member function |static member function |"            "user-declared constructor|user-declared destructor|operator |" diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 13a77f15eb2..51967627b0c 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -10718,6 +10718,50 @@ bool Sema::isAcceptableTagRedeclaration(const TagDecl *Previous,    return false;  } +/// Add a minimal nested name specifier fixit hint to allow lookup of a tag name +/// from an outer enclosing namespace or file scope inside a friend declaration. +/// This should provide the commented out code in the following snippet: +///   namespace N { +///     struct X; +///     namespace M { +///       struct Y { friend struct /*N::*/ X; }; +///     } +///   } +static void addFriendTagNNSFixIt(Sema &SemaRef, Sema::SemaDiagnosticBuilder &D, +                                 NamedDecl *ND, Scope *S, +                                 SourceLocation NameLoc) { +  // While the decl is in a namespace, do repeated lookup of that name and see +  // if we get the same namespace back.  If we do not, continue until +  // translation unit scope, at which point we have a fully qualified NNS. +  SmallVector<IdentifierInfo *, 4> Namespaces; +  DeclContext *DC = ND->getDeclContext()->getRedeclContext(); +  for (; !DC->isTranslationUnit(); DC = DC->getParent()) { +    // This tag should be declared in a namespace, which can only be enclosed by +    // other namespaces.  Bail if there's an anonymous namespace in the chain. +    NamespaceDecl *Namespace = dyn_cast<NamespaceDecl>(DC); +    if (!Namespace || Namespace->isAnonymousNamespace()) +      return; +    IdentifierInfo *II = Namespace->getIdentifier(); +    Namespaces.push_back(II); +    NamedDecl *Lookup = SemaRef.LookupSingleName( +        S, II, NameLoc, Sema::LookupNestedNameSpecifierName); +    if (Lookup == Namespace) +      break; +  } + +  // Once we have all the namespaces, reverse them to go outermost first, and +  // build an NNS. +  SmallString<64> Insertion; +  llvm::raw_svector_ostream OS(Insertion); +  if (DC->isTranslationUnit()) +    OS << "::"; +  std::reverse(Namespaces.begin(), Namespaces.end()); +  for (auto *II : Namespaces) +    OS << II->getName() << "::"; +  OS.flush(); +  D << FixItHint::CreateInsertion(NameLoc, Insertion); +} +  /// ActOnTag - This is invoked when we see 'struct foo' or 'struct {'.  In the  /// former case, Name will be non-null.  In the later case, Name will be null.  /// TagSpec indicates what kind of tag this is. TUK indicates whether this is a @@ -10827,7 +10871,6 @@ Decl *Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK,      Redecl = NotForRedeclaration;    LookupResult Previous(*this, Name, NameLoc, LookupTagName, Redecl); -  bool FriendSawTagOutsideEnclosingNamespace = false;    if (Name && SS.isNotEmpty()) {      // We have a nested-name tag ('struct foo::bar'). @@ -10912,23 +10955,38 @@ Decl *Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK,      //   the entity has been previously declared shall not consider      //   any scopes outside the innermost enclosing namespace.      // +    // MSVC doesn't implement the above rule for types, so a friend tag +    // declaration may be a redeclaration of a type declared in an enclosing +    // scope.  They do implement this rule for friend functions. +    //      // Does it matter that this should be by scope instead of by      // semantic context?      if (!Previous.empty() && TUK == TUK_Friend) {        DeclContext *EnclosingNS = SearchDC->getEnclosingNamespaceContext();        LookupResult::Filter F = Previous.makeFilter(); +      bool FriendSawTagOutsideEnclosingNamespace = false;        while (F.hasNext()) {          NamedDecl *ND = F.next();          DeclContext *DC = ND->getDeclContext()->getRedeclContext();          if (DC->isFileContext() &&              !EnclosingNS->Encloses(ND->getDeclContext())) { -          F.erase(); -          FriendSawTagOutsideEnclosingNamespace = true; +          if (getLangOpts().MSVCCompat) +            FriendSawTagOutsideEnclosingNamespace = true; +          else +            F.erase();          }        }        F.done(); + +      // Diagnose this MSVC extension in the easy case where lookup would have +      // unambiguously found something outside the enclosing namespace. +      if (Previous.isSingleResult() && FriendSawTagOutsideEnclosingNamespace) { +        NamedDecl *ND = Previous.getFoundDecl(); +        auto D = Diag(NameLoc, diag::ext_friend_tag_redecl_outside_namespace); +        addFriendTagNNSFixIt(*this, D, ND, S, NameLoc); +      }      } -     +      // Note:  there used to be some attempt at recovery here.      if (Previous.isAmbiguous())        return nullptr; @@ -11453,8 +11511,7 @@ CreateNewDecl:    // declaration so we always pass true to setObjectOfFriendDecl to make    // the tag name visible.    if (TUK == TUK_Friend) -    New->setObjectOfFriendDecl(!FriendSawTagOutsideEnclosingNamespace && -                               getLangOpts().MicrosoftExt); +    New->setObjectOfFriendDecl(getLangOpts().MSVCCompat);    // Set the access specifier.    if (!Invalid && SearchDC->isRecord()) diff --git a/clang/test/SemaCXX/MicrosoftExtensions.cpp b/clang/test/SemaCXX/MicrosoftExtensions.cpp index 3b54c281afe..6d221a409e7 100644 --- a/clang/test/SemaCXX/MicrosoftExtensions.cpp +++ b/clang/test/SemaCXX/MicrosoftExtensions.cpp @@ -176,29 +176,6 @@ void pointer_to_integral_type_conv(char* ptr) {     b = reinterpret_cast<bool>(ptr); // expected-error {{cast from pointer to smaller type 'bool' loses information}}  } -namespace friend_as_a_forward_decl { - -class A { -  class Nested { -    friend class B; -    B* b; -  }; -  B* b; -}; -B* global_b; - - -void f() -{ -  class Local { -    friend class Z; -    Z* b; -  }; -  Z* b; -} - -} -  struct PR11150 {    class X {      virtual void f() = 0; diff --git a/clang/test/SemaCXX/ms-friend-lookup.cpp b/clang/test/SemaCXX/ms-friend-lookup.cpp new file mode 100644 index 00000000000..c63160f5b9f --- /dev/null +++ b/clang/test/SemaCXX/ms-friend-lookup.cpp @@ -0,0 +1,104 @@ +// RUN: %clang_cc1 %s -triple i686-pc-win32 -std=c++11 -Wmicrosoft -fms-compatibility -verify +// RUN: not %clang_cc1 %s -triple i686-pc-win32 -std=c++11 -Wmicrosoft -fms-compatibility -fdiagnostics-parseable-fixits 2>&1 | FileCheck %s + +struct X; +namespace name_at_tu_scope { +struct Y { +  friend struct X; // expected-warning-re {{unqualified friend declaration {{.*}} is a Microsoft extension}} +  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:17-[[@LINE-1]]:17}:"::" +}; +} + +namespace enclosing_friend_decl { +struct B; +namespace ns { +struct A { +  friend struct B; // expected-warning-re {{unqualified friend declaration {{.*}} is a Microsoft extension}} +  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:17-[[@LINE-1]]:17}:"enclosing_friend_decl::" +protected: +  A(); +}; +} +struct B { +  static void f() { ns::A x; } +}; +} + +namespace enclosing_friend_qualified { +struct B; +namespace ns { +struct A { +  friend struct enclosing_friend_qualified::B; // Adding name specifiers fixes it. +protected: +  A(); +}; +} +struct B { +  static void f() { ns::A x; } +}; +} + +namespace enclosing_friend_no_tag { +struct B; +namespace ns { +struct A { +  friend B; // Removing the tag decl fixes it. +protected: +  A(); +}; +} +struct B { +  static void f() { ns::A x; } +}; +} + +namespace enclosing_friend_func { +void f(); +namespace ns { +struct A { +  // Amusingly, in MSVC, this declares ns::f(), and doesn't find the outer f(). +  friend void f(); +protected: +  A(); // expected-note {{declared protected here}} +}; +} +void f() { ns::A x; } // expected-error {{calling a protected constructor of class 'enclosing_friend_func::ns::A'}} +} + +namespace test_nns_fixit_hint { +namespace name1 { +namespace name2 { +struct X; +struct name2; +namespace name3 { +struct Y { +  friend struct X; // expected-warning-re {{unqualified friend declaration {{.*}} is a Microsoft extension}} +  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:17-[[@LINE-1]]:17}:"name1::name2::" +}; +} +} +} +} + +// A friend declaration injects a forward declaration into the nearest enclosing +// non-member scope. +namespace friend_as_a_forward_decl { + +class A { +  class Nested { +    friend class B; +    B *b; +  }; +  B *b; +}; +B *global_b; + +void f() { +  class Local { +    friend class Z; +    Z *b; +  }; +  Z *b; +} + +}  | 

