<div dir="ltr"><div dir="ltr">On Fri, 6 Nov 2020 at 13:30, Alex L <<a href="mailto:arphaman@gmail.com">arphaman@gmail.com</a>> wrote:<br></div><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div dir="ltr"><div dir="ltr"><div dir="ltr"><div dir="ltr">Hi Rchard,<div><br></div><div>Some of our code started triggering the warning you added in this code, but I'm not sure if the warning is actually valid or not in this case:</div><div><br></div><div><a href="https://godbolt.org/z/9fxs3K" target="_blank">https://godbolt.org/z/9fxs3K</a><br></div><div><br></div><div><div>namespace geo {</div><div>      template<typename T></div><div><span style="white-space:pre-wrap"> </span>class optional {</div><div><span style="white-space:pre-wrap">         </span>optional() { }</div><div><span style="white-space:pre-wrap">           </span>~optional();</div><div><span style="white-space:pre-wrap">     </span>};</div><div>}</div><div>template<typename T></div><div>geo::optional<T>::~optional() // Triggers  ISO C++ requires the name after '::~' to be found in the same scope as the name before '::~'</div><div>{</div><div>}</div></div><div><br></div><div>Could you confirm whether or not this warning is valid for our code?</div></div></div></div></div></blockquote><div><br></div><div>Technically, yes, the warning is correct; the C++ rules require that to be written as</div><div><br></div><div>geo::optional<T>::~optional<T>()</div><div><br></div><div>... but of course no-one does that, and it doesn't seem to be an intended restriction. I've been working with the C++ committee to get the language rule fixed, and the fix will be in P1787R6, which is due to be voted into C++23 (with at least this part as a DR against prior standards) next week. It's probably time to implement the new rule :)</div><div><br></div><div>In the meantime, using -Wno-dtor-name would be reasonable.</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div dir="ltr"><div dir="ltr"><div dir="ltr"><div dir="ltr"><div>Thanks,</div><div>Alex</div><div><br></div></div></div></div></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Thu, 13 Feb 2020 at 00:52, Richard Smith via cfe-commits <<a href="mailto:cfe-commits@lists.llvm.org" target="_blank">cfe-commits@lists.llvm.org</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div dir="ltr"><div>Thanks, yes, fixed in llvmorg-11-init-3043-gc1394afb8df.</div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Thu, 13 Feb 2020 at 02:58, Kostya Serebryany via cfe-commits <<a href="mailto:cfe-commits@lists.llvm.org" target="_blank">cfe-commits@lists.llvm.org</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div dir="ltr">Could this have caused a new ubsan failure? <div><pre style="margin-top:0px;margin-bottom:0px;padding:12px;border:0px;background-color:rgba(71,87,120,0.08);color:rgb(0,0,0);overflow:auto;border-radius:3px;white-space:pre-wrap;font-variant-numeric:normal;font-variant-east-asian:normal;font-stretch:normal;font-size:11px;line-height:15px;font-family:Menlo,Consolas,Monaco,monospace">clang/lib/AST/NestedNameSpecifier.cpp:485:23: runtime error: null pointer passed as argument 2, which is declared to never be null</pre><a href="http://lab.llvm.org:8011/builders/sanitizer-x86_64-linux-fast/builds/38698/steps/check-clang%20ubsan/logs/stdio" target="_blank">http://lab.llvm.org:8011/builders/sanitizer-x86_64-linux-fast/builds/38698/steps/check-clang%20ubsan/logs/stdio</a></div></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Fri, Feb 7, 2020 at 6:41 PM Richard Smith via cfe-commits <<a href="mailto:cfe-commits@lists.llvm.org" target="_blank">cfe-commits@lists.llvm.org</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><br>
Author: Richard Smith<br>
Date: 2020-02-07T18:40:41-08:00<br>
New Revision: 0e3a48778408b505946e465abf5c77a2ddd4918c<br>
<br>
URL: <a href="https://github.com/llvm/llvm-project/commit/0e3a48778408b505946e465abf5c77a2ddd4918c" rel="noreferrer" target="_blank">https://github.com/llvm/llvm-project/commit/0e3a48778408b505946e465abf5c77a2ddd4918c</a><br>
DIFF: <a href="https://github.com/llvm/llvm-project/commit/0e3a48778408b505946e465abf5c77a2ddd4918c.diff" rel="noreferrer" target="_blank">https://github.com/llvm/llvm-project/commit/0e3a48778408b505946e465abf5c77a2ddd4918c.diff</a><br>
<br>
LOG: PR12350: Handle remaining cases permitted by CWG DR 244.<br>
<br>
Also add extension warnings for the cases that are disallowed by the<br>
current rules for destructor name lookup, refactor and simplify the<br>
lookup code, and improve the diagnostic quality when lookup fails.<br>
<br>
The special case we previously supported for converting<br>
p->N::S<int>::~S() from naming a class template into naming a<br>
specialization thereof is subsumed by a more general rule here (which is<br>
also consistent with Clang's historical behavior and that of other<br>
compilers): if we can't find a suitable S in N, also look in N::S<int>.<br>
<br>
The extension warnings are off by default, except for a warning when<br>
lookup for p->N::S::~T() looks for T in scope instead of in N (or N::S).<br>
That seems sufficiently heinous to warn on by default, especially since<br>
we can't support it for a dependent nested-name-specifier.<br>
<br>
Added: <br>
<br>
<br>
Modified: <br>
    clang/include/clang/Basic/DiagnosticGroups.td<br>
    clang/include/clang/Basic/DiagnosticSemaKinds.td<br>
    clang/lib/AST/NestedNameSpecifier.cpp<br>
    clang/lib/Sema/DeclSpec.cpp<br>
    clang/lib/Sema/SemaExprCXX.cpp<br>
    clang/test/CXX/class/class.mem/p13.cpp<br>
    clang/test/CXX/drs/dr2xx.cpp<br>
    clang/test/CXX/drs/dr3xx.cpp<br>
    clang/test/FixIt/fixit.cpp<br>
    clang/test/Parser/cxx-decl.cpp<br>
    clang/test/SemaCXX/constructor.cpp<br>
    clang/test/SemaCXX/destructor.cpp<br>
    clang/test/SemaCXX/pseudo-destructors.cpp<br>
<br>
Removed: <br>
<br>
<br>
<br>
################################################################################<br>
diff  --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td<br>
index a2bc29986a07..8c54723cdbab 100644<br>
--- a/clang/include/clang/Basic/DiagnosticGroups.td<br>
+++ b/clang/include/clang/Basic/DiagnosticGroups.td<br>
@@ -192,6 +192,7 @@ def CXX2aDesignator : DiagGroup<"c++2a-designator">;<br>
 // designators (including the warning controlled by -Wc++2a-designator).<br>
 def C99Designator : DiagGroup<"c99-designator", [CXX2aDesignator]>;<br>
 def GNUDesignator : DiagGroup<"gnu-designator">;<br>
+def DtorName : DiagGroup<"dtor-name">;<br>
<br>
 def DynamicExceptionSpec<br>
     : DiagGroup<"dynamic-exception-spec", [DeprecatedDynamicExceptionSpec]>;<br>
<br>
diff  --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td<br>
index 9de60d3a8d27..82861f0d5d72 100644<br>
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td<br>
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td<br>
@@ -1911,17 +1911,33 @@ def err_destructor_with_params : Error<"destructor cannot have any parameters">;<br>
 def err_destructor_variadic : Error<"destructor cannot be variadic">;<br>
 def err_destructor_typedef_name : Error<<br>
   "destructor cannot be declared using a %select{typedef|type alias}1 %0 of the class name">;<br>
+def err_undeclared_destructor_name : Error<<br>
+  "undeclared identifier %0 in destructor name">;<br>
 def err_destructor_name : Error<<br>
   "expected the class name after '~' to name the enclosing class">;<br>
-def err_destructor_class_name : Error<<br>
-  "expected the class name after '~' to name a destructor">;<br>
-def err_ident_in_dtor_not_a_type : Error<<br>
+def err_destructor_name_nontype : Error<<br>
+  "identifier %0 after '~' in destructor name does not name a type">;<br>
+def err_destructor_expr_mismatch : Error<<br>
+  "identifier %0 in object destruction expression does not name the type "<br>
+  "%1 of the object being destroyed">;<br>
+def err_destructor_expr_nontype : Error<<br>
   "identifier %0 in object destruction expression does not name a type">;<br>
 def err_destructor_expr_type_mismatch : Error<<br>
   "destructor type %0 in object destruction expression does not match the "<br>
   "type %1 of the object being destroyed">;<br>
 def note_destructor_type_here : Note<<br>
-  "type %0 is declared here">;<br>
+  "type %0 found by destructor name lookup">;<br>
+def note_destructor_nontype_here : Note<<br>
+  "non-type declaration found by destructor name lookup">;<br>
+def ext_dtor_named_in_wrong_scope : Extension<<br>
+  "ISO C++ requires the name after '::~' to be found in the same scope as "<br>
+  "the name before '::~'">, InGroup<DtorName>;<br>
+def ext_dtor_name_missing_template_arguments : Extension<<br>
+  "ISO C++ requires template argument list in destructor name">,<br>
+  InGroup<DtorName>;<br>
+def ext_qualified_dtor_named_in_lexical_scope : ExtWarn<<br>
+  "qualified destructor name only found in lexical scope; omit the qualifier "<br>
+  "to find this type name by unqualified lookup">, InGroup<DtorName>;<br>
<br>
 def err_destroy_attr_on_non_static_var : Error<<br>
   "%select{no_destroy|always_destroy}0 attribute can only be applied to a"<br>
<br>
diff  --git a/clang/lib/AST/NestedNameSpecifier.cpp b/clang/lib/AST/NestedNameSpecifier.cpp<br>
index 137953fa8203..81130512bfe1 100644<br>
--- a/clang/lib/AST/NestedNameSpecifier.cpp<br>
+++ b/clang/lib/AST/NestedNameSpecifier.cpp<br>
@@ -482,10 +482,9 @@ static void Append(char *Start, char *End, char *&Buffer, unsigned &BufferSize,<br>
         (unsigned)(BufferCapacity ? BufferCapacity * 2 : sizeof(void *) * 2),<br>
         (unsigned)(BufferSize + (End - Start)));<br>
     char *NewBuffer = static_cast<char *>(llvm::safe_malloc(NewCapacity));<br>
-    if (BufferCapacity) {<br>
-      memcpy(NewBuffer, Buffer, BufferSize);<br>
+    memcpy(NewBuffer, Buffer, BufferSize);<br>
+    if (BufferCapacity)<br>
       free(Buffer);<br>
-    }<br>
     Buffer = NewBuffer;<br>
     BufferCapacity = NewCapacity;<br>
   }<br>
<br>
diff  --git a/clang/lib/Sema/DeclSpec.cpp b/clang/lib/Sema/DeclSpec.cpp<br>
index 94d87974624e..eca97734bc9d 100644<br>
--- a/clang/lib/Sema/DeclSpec.cpp<br>
+++ b/clang/lib/Sema/DeclSpec.cpp<br>
@@ -130,6 +130,8 @@ void CXXScopeSpec::Adopt(NestedNameSpecifierLoc Other) {<br>
<br>
   Range = Other.getSourceRange();<br>
   Builder.Adopt(Other);<br>
+  assert(Range == Builder.getSourceRange() &&<br>
+         "NestedNameSpecifierLoc range computation incorrect");<br>
 }<br>
<br>
 SourceLocation CXXScopeSpec::getLastQualifierNameLoc() const {<br>
<br>
diff  --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp<br>
index e53281d11755..a39b0b1f7766 100644<br>
--- a/clang/lib/Sema/SemaExprCXX.cpp<br>
+++ b/clang/lib/Sema/SemaExprCXX.cpp<br>
@@ -156,103 +156,48 @@ ParsedType Sema::getDestructorName(SourceLocation TildeLoc,<br>
   //   }<br>
   //<br>
   // See also PR6358 and PR6359.<br>
-  // For this reason, we're currently only doing the C++03 version of this<br>
-  // code; the C++0x version has to wait until we get a proper spec.<br>
-  QualType SearchType;<br>
-  DeclContext *LookupCtx = nullptr;<br>
-  bool isDependent = false;<br>
-  bool LookInScope = false;<br>
+  //<br>
+  // For now, we accept all the cases in which the name given could plausibly<br>
+  // be interpreted as a correct destructor name, issuing off-by-default<br>
+  // extension diagnostics on the cases that don't strictly conform to the<br>
+  // C++20 rules. This basically means we always consider looking in the<br>
+  // nested-name-specifier prefix, the complete nested-name-specifier, and<br>
+  // the scope, and accept if we find the expected type in any of the three<br>
+  // places.<br>
<br>
   if (SS.isInvalid())<br>
     return nullptr;<br>
<br>
+  // Whether we've failed with a diagnostic already.<br>
+  bool Failed = false;<br>
+<br>
+  llvm::SmallVector<NamedDecl*, 8> FoundDecls;<br>
+  llvm::SmallSet<CanonicalDeclPtr<Decl>, 8> FoundDeclSet;<br>
+<br>
   // If we have an object type, it's because we are in a<br>
   // pseudo-destructor-expression or a member access expression, and<br>
   // we know what type we're looking for.<br>
-  if (ObjectTypePtr)<br>
-    SearchType = GetTypeFromParser(ObjectTypePtr);<br>
-<br>
-  if (SS.isSet()) {<br>
-    NestedNameSpecifier *NNS = SS.getScopeRep();<br>
-<br>
-    bool AlreadySearched = false;<br>
-    bool LookAtPrefix = true;<br>
-    // C++11 [basic.lookup.qual]p6:<br>
-    //   If a pseudo-destructor-name (5.2.4) contains a nested-name-specifier,<br>
-    //   the type-names are looked up as types in the scope designated by the<br>
-    //   nested-name-specifier. Similarly, in a qualified-id of the form:<br>
-    //<br>
-    //     nested-name-specifier[opt] class-name :: ~ class-name<br>
-    //<br>
-    //   the second class-name is looked up in the same scope as the first.<br>
-    //<br>
-    // Here, we determine whether the code below is permitted to look at the<br>
-    // prefix of the nested-name-specifier.<br>
-    DeclContext *DC = computeDeclContext(SS, EnteringContext);<br>
-    if (DC && DC->isFileContext()) {<br>
-      AlreadySearched = true;<br>
-      LookupCtx = DC;<br>
-      isDependent = false;<br>
-    } else if (DC && isa<CXXRecordDecl>(DC)) {<br>
-      LookAtPrefix = false;<br>
-      LookInScope = true;<br>
-    }<br>
-<br>
-    // The second case from the C++03 rules quoted further above.<br>
-    NestedNameSpecifier *Prefix = nullptr;<br>
-    if (AlreadySearched) {<br>
-      // Nothing left to do.<br>
-    } else if (LookAtPrefix && (Prefix = NNS->getPrefix())) {<br>
-      CXXScopeSpec PrefixSS;<br>
-      PrefixSS.Adopt(NestedNameSpecifierLoc(Prefix, SS.location_data()));<br>
-      LookupCtx = computeDeclContext(PrefixSS, EnteringContext);<br>
-      isDependent = isDependentScopeSpecifier(PrefixSS);<br>
-    } else if (ObjectTypePtr) {<br>
-      LookupCtx = computeDeclContext(SearchType);<br>
-      isDependent = SearchType->isDependentType();<br>
-    } else {<br>
-      LookupCtx = computeDeclContext(SS, EnteringContext);<br>
-      isDependent = LookupCtx && LookupCtx->isDependentContext();<br>
-    }<br>
-  } else if (ObjectTypePtr) {<br>
-    // C++ [basic.lookup.classref]p3:<br>
-    //   If the unqualified-id is ~type-name, the type-name is looked up<br>
-    //   in the context of the entire postfix-expression. If the type T<br>
-    //   of the object expression is of a class type C, the type-name is<br>
-    //   also looked up in the scope of class C. At least one of the<br>
-    //   lookups shall find a name that refers to (possibly<br>
-    //   cv-qualified) T.<br>
-    LookupCtx = computeDeclContext(SearchType);<br>
-    isDependent = SearchType->isDependentType();<br>
-    assert((isDependent || !SearchType->isIncompleteType()) &&<br>
-           "Caller should have completed object type");<br>
-<br>
-    LookInScope = true;<br>
-  } else {<br>
-    // Perform lookup into the current scope (only).<br>
-    LookInScope = true;<br>
-  }<br>
-<br>
-  TypeDecl *NonMatchingTypeDecl = nullptr;<br>
-  LookupResult Found(*this, &II, NameLoc, LookupOrdinaryName);<br>
-  for (unsigned Step = 0; Step != 2; ++Step) {<br>
-    // Look for the name first in the computed lookup context (if we<br>
-    // have one) and, if that fails to find a match, in the scope (if<br>
-    // we're allowed to look there).<br>
-    Found.clear();<br>
-    if (Step == 0 && LookupCtx) {<br>
-      if (RequireCompleteDeclContext(SS, LookupCtx))<br>
-        return nullptr;<br>
-      LookupQualifiedName(Found, LookupCtx);<br>
-    } else if (Step == 1 && LookInScope && S) {<br>
-      LookupName(Found, S);<br>
-    } else {<br>
-      continue;<br>
-    }<br>
+  QualType SearchType =<br>
+      ObjectTypePtr ? GetTypeFromParser(ObjectTypePtr) : QualType();<br>
<br>
+  auto CheckLookupResult = [&](LookupResult &Found) -> ParsedType {<br>
     // FIXME: Should we be suppressing ambiguities here?<br>
-    if (Found.isAmbiguous())<br>
+    if (Found.isAmbiguous()) {<br>
+      Failed = true;<br>
       return nullptr;<br>
+    }<br>
+<br>
+    for (NamedDecl *D : Found) {<br>
+      // Don't list a class twice in the lookup failure diagnostic if it's<br>
+      // found by both its injected-class-name and by the name in the enclosing<br>
+      // scope.<br>
+      if (auto *RD = dyn_cast<CXXRecordDecl>(D))<br>
+        if (RD->isInjectedClassName())<br>
+          D = cast<NamedDecl>(RD->getParent());<br>
+<br>
+      if (FoundDeclSet.insert(D).second)<br>
+        FoundDecls.push_back(D);<br>
+    }<br>
<br>
     if (TypeDecl *Type = Found.getAsSingle<TypeDecl>()) {<br>
       QualType T = Context.getTypeDeclType(Type);<br>
@@ -261,91 +206,121 @@ ParsedType Sema::getDestructorName(SourceLocation TildeLoc,<br>
       if (SearchType.isNull() || SearchType->isDependentType() ||<br>
           Context.hasSameUnqualifiedType(T, SearchType)) {<br>
         // We found our type!<br>
-<br>
         return CreateParsedType(T,<br>
                                 Context.getTrivialTypeSourceInfo(T, NameLoc));<br>
       }<br>
+    }<br>
<br>
-      if (!SearchType.isNull())<br>
-        NonMatchingTypeDecl = Type;<br>
-    }<br>
-<br>
-    // If the name that we found is a class template name, and it is<br>
-    // the same name as the template name in the last part of the<br>
-    // nested-name-specifier (if present) or the object type, then<br>
-    // this is the destructor for that class.<br>
-    // FIXME: This is a workaround until we get real drafting for core<br>
-    // issue 399, for which there isn't even an obvious direction.<br>
-    if (ClassTemplateDecl *Template = Found.getAsSingle<ClassTemplateDecl>()) {<br>
-      QualType MemberOfType;<br>
-      if (SS.isSet()) {<br>
-        if (DeclContext *Ctx = computeDeclContext(SS, EnteringContext)) {<br>
-          // Figure out the type of the context, if it has one.<br>
-          if (CXXRecordDecl *Record = dyn_cast<CXXRecordDecl>(Ctx))<br>
-            MemberOfType = Context.getTypeDeclType(Record);<br>
-        }<br>
-      }<br>
-      if (MemberOfType.isNull())<br>
-        MemberOfType = SearchType;<br>
+    return nullptr;<br>
+  };<br>
<br>
-      if (MemberOfType.isNull())<br>
-        continue;<br>
+  bool IsDependent = false;<br>
<br>
-      // We're referring into a class template specialization. If the<br>
-      // class template we found is the same as the template being<br>
-      // specialized, we found what we are looking for.<br>
-      if (const RecordType *Record = MemberOfType->getAs<RecordType>()) {<br>
-        if (ClassTemplateSpecializationDecl *Spec<br>
-              = dyn_cast<ClassTemplateSpecializationDecl>(Record->getDecl())) {<br>
-          if (Spec->getSpecializedTemplate()->getCanonicalDecl() ==<br>
-                Template->getCanonicalDecl())<br>
-            return CreateParsedType(<br>
-                MemberOfType,<br>
-                Context.getTrivialTypeSourceInfo(MemberOfType, NameLoc));<br>
-        }<br>
+  auto LookupInObjectType = [&]() -> ParsedType {<br>
+    if (Failed || SearchType.isNull())<br>
+      return nullptr;<br>
<br>
-        continue;<br>
-      }<br>
+    IsDependent |= SearchType->isDependentType();<br>
<br>
-      // We're referring to an unresolved class template<br>
-      // specialization. Determine whether we class template we found<br>
-      // is the same as the template being specialized or, if we don't<br>
-      // know which template is being specialized, that it at least<br>
-      // has the same name.<br>
-      if (const TemplateSpecializationType *SpecType<br>
-            = MemberOfType->getAs<TemplateSpecializationType>()) {<br>
-        TemplateName SpecName = SpecType->getTemplateName();<br>
-<br>
-        // The class template we found is the same template being<br>
-        // specialized.<br>
-        if (TemplateDecl *SpecTemplate = SpecName.getAsTemplateDecl()) {<br>
-          if (SpecTemplate->getCanonicalDecl() == Template->getCanonicalDecl())<br>
-            return CreateParsedType(<br>
-                MemberOfType,<br>
-                Context.getTrivialTypeSourceInfo(MemberOfType, NameLoc));<br>
+    LookupResult Found(*this, &II, NameLoc, LookupOrdinaryName);<br>
+    DeclContext *LookupCtx = computeDeclContext(SearchType);<br>
+    if (!LookupCtx)<br>
+      return nullptr;<br>
+    LookupQualifiedName(Found, LookupCtx);<br>
+    return CheckLookupResult(Found);<br>
+  };<br>
<br>
-          continue;<br>
-        }<br>
+  auto LookupInNestedNameSpec = [&](CXXScopeSpec &LookupSS) -> ParsedType {<br>
+    if (Failed)<br>
+      return nullptr;<br>
<br>
-        // The class template we found has the same name as the<br>
-        // (dependent) template name being specialized.<br>
-        if (DependentTemplateName *DepTemplate<br>
-                                    = SpecName.getAsDependentTemplateName()) {<br>
-          if (DepTemplate->isIdentifier() &&<br>
-              DepTemplate->getIdentifier() == Template->getIdentifier())<br>
-            return CreateParsedType(<br>
-                MemberOfType,<br>
-                Context.getTrivialTypeSourceInfo(MemberOfType, NameLoc));<br>
+    IsDependent |= isDependentScopeSpecifier(LookupSS);<br>
+    DeclContext *LookupCtx = computeDeclContext(LookupSS, EnteringContext);<br>
+    if (!LookupCtx)<br>
+      return nullptr;<br>
<br>
-          continue;<br>
-        }<br>
-      }<br>
+    LookupResult Found(*this, &II, NameLoc, LookupOrdinaryName);<br>
+    if (RequireCompleteDeclContext(LookupSS, LookupCtx)) {<br>
+      Failed = true;<br>
+      return nullptr;<br>
     }<br>
+    LookupQualifiedName(Found, LookupCtx);<br>
+    return CheckLookupResult(Found);<br>
+  };<br>
+<br>
+  auto LookupInScope = [&]() -> ParsedType {<br>
+    if (Failed || !S)<br>
+      return nullptr;<br>
+<br>
+    LookupResult Found(*this, &II, NameLoc, LookupOrdinaryName);<br>
+    LookupName(Found, S);<br>
+    return CheckLookupResult(Found);<br>
+  };<br>
+<br>
+  // C++2a [basic.lookup.qual]p6:<br>
+  //   In a qualified-id of the form<br>
+  //<br>
+  //     nested-name-specifier[opt] type-name :: ~ type-name<br>
+  //<br>
+  //   the second type-name is looked up in the same scope as the first.<br>
+  //<br>
+  // We interpret this as meaning that if you do a dual-scope lookup for the<br>
+  // first name, you also do a dual-scope lookup for the second name, per<br>
+  // C++ [basic.lookup.classref]p4:<br>
+  //<br>
+  //   If the id-expression in a class member access is a qualified-id of the<br>
+  //   form<br>
+  //<br>
+  //     class-name-or-namespace-name :: ...<br>
+  //<br>
+  //   the class-name-or-namespace-name following the . or -> is first looked<br>
+  //   up in the class of the object expression and the name, if found, is used.<br>
+  //   Otherwise, it is looked up in the context of the entire<br>
+  //   postfix-expression.<br>
+  //<br>
+  // This looks in the same scopes as for an unqualified destructor name:<br>
+  //<br>
+  // C++ [basic.lookup.classref]p3:<br>
+  //   If the unqualified-id is ~ type-name, the type-name is looked up<br>
+  //   in the context of the entire postfix-expression. If the type T<br>
+  //   of the object expression is of a class type C, the type-name is<br>
+  //   also looked up in the scope of class C. At least one of the<br>
+  //   lookups shall find a name that refers to cv T.<br>
+  //<br>
+  // FIXME: The intent is unclear here. Should type-name::~type-name look in<br>
+  // the scope anyway if it finds a non-matching name declared in the class?<br>
+  // If both lookups succeed and find a dependent result, which result should<br>
+  // we retain? (Same question for p->~type-name().)<br>
+<br>
+  if (NestedNameSpecifier *Prefix =<br>
+      SS.isSet() ? SS.getScopeRep()->getPrefix() : nullptr) {<br>
+    // This is<br>
+    //<br>
+    //   nested-name-specifier type-name :: ~ type-name<br>
+    //<br>
+    // Look for the second type-name in the nested-name-specifier.<br>
+    CXXScopeSpec PrefixSS;<br>
+    PrefixSS.Adopt(NestedNameSpecifierLoc(Prefix, SS.location_data()));<br>
+    if (ParsedType T = LookupInNestedNameSpec(PrefixSS))<br>
+      return T;<br>
+  } else {<br>
+    // This is one of<br>
+    //<br>
+    //   type-name :: ~ type-name<br>
+    //   ~ type-name<br>
+    //<br>
+    // Look in the scope and (if any) the object type.<br>
+    if (ParsedType T = LookupInScope())<br>
+      return T;<br>
+    if (ParsedType T = LookupInObjectType())<br>
+      return T;<br>
   }<br>
<br>
-  if (isDependent) {<br>
-    // We didn't find our type, but that's okay: it's dependent<br>
-    // anyway.<br>
+  if (Failed)<br>
+    return nullptr;<br>
+<br>
+  if (IsDependent) {<br>
+    // We didn't find our type, but that's OK: it's dependent anyway.<br>
<br>
     // FIXME: What if we have no nested-name-specifier?<br>
     QualType T = CheckTypenameType(ETK_None, SourceLocation(),<br>
@@ -354,26 +329,98 @@ ParsedType Sema::getDestructorName(SourceLocation TildeLoc,<br>
     return ParsedType::make(T);<br>
   }<br>
<br>
-  if (NonMatchingTypeDecl) {<br>
-    QualType T = Context.getTypeDeclType(NonMatchingTypeDecl);<br>
-    Diag(NameLoc, diag::err_destructor_expr_type_mismatch)<br>
-      << T << SearchType;<br>
-    Diag(NonMatchingTypeDecl->getLocation(), diag::note_destructor_type_here)<br>
-      << T;<br>
-  } else if (ObjectTypePtr)<br>
-    Diag(NameLoc, diag::err_ident_in_dtor_not_a_type)<br>
-      << &II;<br>
-  else {<br>
-    SemaDiagnosticBuilder DtorDiag = Diag(NameLoc,<br>
-                                          diag::err_destructor_class_name);<br>
-    if (S) {<br>
-      const DeclContext *Ctx = S->getEntity();<br>
-      if (const CXXRecordDecl *Class = dyn_cast_or_null<CXXRecordDecl>(Ctx))<br>
-        DtorDiag << FixItHint::CreateReplacement(SourceRange(NameLoc),<br>
-                                                 Class->getNameAsString());<br>
+  // The remaining cases are all non-standard extensions imitating the behavior<br>
+  // of various other compilers.<br>
+  unsigned NumNonExtensionDecls = FoundDecls.size();<br>
+<br>
+  if (SS.isSet()) {<br>
+    // For compatibility with older broken C++ rules and existing code,<br>
+    //<br>
+    //   nested-name-specifier :: ~ type-name<br>
+    //<br>
+    // also looks for type-name within the nested-name-specifier.<br>
+    if (ParsedType T = LookupInNestedNameSpec(SS)) {<br>
+      Diag(SS.getEndLoc(), diag::ext_dtor_named_in_wrong_scope)<br>
+          << SS.getRange()<br>
+          << FixItHint::CreateInsertion(SS.getEndLoc(),<br>
+                                        ("::" + II.getName()).str());<br>
+      return T;<br>
+    }<br>
+<br>
+    // For compatibility with other compilers and older versions of Clang,<br>
+    //<br>
+    //   nested-name-specifier type-name :: ~ type-name<br>
+    //<br>
+    // also looks for type-name in the scope. Unfortunately, we can't<br>
+    // reasonably apply this fallback for dependent nested-name-specifiers.<br>
+    if (SS.getScopeRep()->getPrefix()) {<br>
+      if (ParsedType T = LookupInScope()) {<br>
+        Diag(SS.getEndLoc(), diag::ext_qualified_dtor_named_in_lexical_scope)<br>
+            << FixItHint::CreateRemoval(SS.getRange());<br>
+        Diag(FoundDecls.back()->getLocation(), diag::note_destructor_type_here)<br>
+            << GetTypeFromParser(T);<br>
+        return T;<br>
+      }<br>
     }<br>
   }<br>
<br>
+  // We didn't find anything matching; tell the user what we did find (if<br>
+  // anything).<br>
+<br>
+  // Don't tell the user about declarations we shouldn't have found.<br>
+  FoundDecls.resize(NumNonExtensionDecls);<br>
+<br>
+  // List types before non-types.<br>
+  std::stable_sort(FoundDecls.begin(), FoundDecls.end(),<br>
+                   [](NamedDecl *A, NamedDecl *B) {<br>
+                     return isa<TypeDecl>(A->getUnderlyingDecl()) ><br>
+                            isa<TypeDecl>(B->getUnderlyingDecl());<br>
+                   });<br>
+<br>
+  // Suggest a fixit to properly name the destroyed type.<br>
+  auto MakeFixItHint = [&]{<br>
+    const CXXRecordDecl *Destroyed = nullptr;<br>
+    // FIXME: If we have a scope specifier, suggest its last component?<br>
+    if (!SearchType.isNull())<br>
+      Destroyed = SearchType->getAsCXXRecordDecl();<br>
+    else if (S)<br>
+      Destroyed = dyn_cast_or_null<CXXRecordDecl>(S->getEntity());<br>
+    if (Destroyed)<br>
+      return FixItHint::CreateReplacement(SourceRange(NameLoc),<br>
+                                          Destroyed->getNameAsString());<br>
+    return FixItHint();<br>
+  };<br>
+<br>
+  if (FoundDecls.empty()) {<br>
+    // FIXME: Attempt typo-correction?<br>
+    Diag(NameLoc, diag::err_undeclared_destructor_name)<br>
+      << &II << MakeFixItHint();<br>
+  } else if (!SearchType.isNull() && FoundDecls.size() == 1) {<br>
+    if (auto *TD = dyn_cast<TypeDecl>(FoundDecls[0]->getUnderlyingDecl())) {<br>
+      assert(!SearchType.isNull() &&<br>
+             "should only reject a type result if we have a search type");<br>
+      QualType T = Context.getTypeDeclType(TD);<br>
+      Diag(NameLoc, diag::err_destructor_expr_type_mismatch)<br>
+          << T << SearchType << MakeFixItHint();<br>
+    } else {<br>
+      Diag(NameLoc, diag::err_destructor_expr_nontype)<br>
+          << &II << MakeFixItHint();<br>
+    }<br>
+  } else {<br>
+    Diag(NameLoc, SearchType.isNull() ? diag::err_destructor_name_nontype<br>
+                                      : diag::err_destructor_expr_mismatch)<br>
+        << &II << SearchType << MakeFixItHint();<br>
+  }<br>
+<br>
+  for (NamedDecl *FoundD : FoundDecls) {<br>
+    if (auto *TD = dyn_cast<TypeDecl>(FoundD->getUnderlyingDecl()))<br>
+      Diag(FoundD->getLocation(), diag::note_destructor_type_here)<br>
+          << Context.getTypeDeclType(TD);<br>
+    else<br>
+      Diag(FoundD->getLocation(), diag::note_destructor_nontype_here)<br>
+          << FoundD;<br>
+  }<br>
+<br>
   return nullptr;<br>
 }<br>
<br>
<br>
diff  --git a/clang/test/CXX/class/class.mem/p13.cpp b/clang/test/CXX/class/class.mem/p13.cpp<br>
index 1b1c0c7f8fc3..d947586c4194 100644<br>
--- a/clang/test/CXX/class/class.mem/p13.cpp<br>
+++ b/clang/test/CXX/class/class.mem/p13.cpp<br>
@@ -110,7 +110,7 @@ template<typename B> struct Dtemplate_with_ctors : B {<br>
 };<br>
<br>
 template<typename B> struct CtorDtorName : B {<br>
-  using B::CtorDtorName; // expected-error {{member 'CtorDtorName' has the same name as its class}}<br>
+  using B::CtorDtorName; // expected-error {{member 'CtorDtorName' has the same name as its class}} expected-note {{non-type declaration found by destructor name lookup}}<br>
   CtorDtorName();<br>
-  ~CtorDtorName(); // expected-error {{expected the class name after '~' to name a destructor}}<br>
+  ~CtorDtorName(); // expected-error {{identifier 'CtorDtorName' after '~' in destructor name does not name a type}}<br>
 };<br>
<br>
diff  --git a/clang/test/CXX/drs/dr2xx.cpp b/clang/test/CXX/drs/dr2xx.cpp<br>
index 1f625efe2b55..905a2b07888d 100644<br>
--- a/clang/test/CXX/drs/dr2xx.cpp<br>
+++ b/clang/test/CXX/drs/dr2xx.cpp<br>
@@ -474,45 +474,82 @@ namespace dr243 { // dr243: yes<br>
   A a2 = b; // expected-error {{ambiguous}}<br>
 }<br>
<br>
-namespace dr244 { // dr244: partial<br>
-  struct B {}; struct D : B {}; // expected-note {{here}}<br>
+namespace dr244 { // dr244: 11<br>
+  struct B {}; // expected-note {{type 'dr244::B' found by destructor name lookup}}<br>
+  struct D : B {};<br>
<br>
   D D_object;<br>
   typedef B B_alias;<br>
   B* B_ptr = &D_object;<br>
<br>
   void f() {<br>
-    D_object.~B(); // expected-error {{expression does not match the type}}<br>
+    D_object.~B(); // expected-error {{does not match the type 'dr244::D' of the object being destroyed}}<br>
     D_object.B::~B();<br>
+    D_object.D::~B(); // FIXME: Missing diagnostic for this.<br>
     B_ptr->~B();<br>
     B_ptr->~B_alias();<br>
     B_ptr->B_alias::~B();<br>
-    // This is valid under DR244.<br>
     B_ptr->B_alias::~B_alias();<br>
     B_ptr->dr244::~B(); // expected-error {{refers to a member in namespace}}<br>
     B_ptr->dr244::~B_alias(); // expected-error {{refers to a member in namespace}}<br>
   }<br>
<br>
+  template<typename T, typename U><br>
+  void f(T *B_ptr, U D_object) {<br>
+    D_object.~B(); // FIXME: Missing diagnostic for this.<br>
+    D_object.B::~B();<br>
+    D_object.D::~B(); // FIXME: Missing diagnostic for this.<br>
+    B_ptr->~B();<br>
+    B_ptr->~B_alias();<br>
+    B_ptr->B_alias::~B();<br>
+    B_ptr->B_alias::~B_alias();<br>
+    B_ptr->dr244::~B(); // expected-error {{does not refer to a type name}}<br>
+    B_ptr->dr244::~B_alias(); // expected-error {{does not refer to a type name}}<br>
+  }<br>
+  template void f<B, D>(B*, D);<br>
+<br>
   namespace N {<br>
     template<typename T> struct E {};<br>
     typedef E<int> F;<br>
   }<br>
   void g(N::F f) {<br>
-    typedef N::F G;<br>
+    typedef N::F G; // expected-note {{found by destructor name lookup}}<br>
     f.~G();<br>
-    f.G::~E();<br>
-    f.G::~F(); // expected-error {{expected the class name after '~' to name a destructor}}<br>
+    f.G::~E(); // expected-error {{ISO C++ requires the name after '::~' to be found in the same scope as the name before '::~'}}<br>
+    f.G::~F(); // expected-error {{undeclared identifier 'F' in destructor name}}<br>
     f.G::~G();<br>
     // This is technically ill-formed; E is looked up in 'N::' and names the<br>
     // class template, not the injected-class-name of the class. But that's<br>
     // probably a bug in the standard.<br>
-    f.N::F::~E();<br>
+    f.N::F::~E(); // expected-error {{ISO C++ requires the name after '::~' to be found in the same scope as the name before '::~'}}<br>
     // This is valid; we look up the second F in the same scope in which we<br>
     // found the first one, that is, 'N::'.<br>
-    f.N::F::~F(); // FIXME: expected-error {{expected the class name after '~' to name a destructor}}<br>
-    // This is technically ill-formed; G is looked up in 'N::' and is not found;<br>
-    // as above, this is probably a bug in the standard.<br>
-    f.N::F::~G();<br>
+    f.N::F::~F();<br>
+    // This is technically ill-formed; G is looked up in 'N::' and is not found.<br>
+    // Rejecting this seems correct, but most compilers accept, so we do also.<br>
+    f.N::F::~G(); // expected-error {{qualified destructor name only found in lexical scope; omit the qualifier to find this type name by unqualified lookup}}<br>
+  }<br>
+<br>
+  // Bizarrely, compilers perform lookup in the scope for qualified destructor<br>
+  // names, if the nested-name-specifier is non-dependent. Ensure we diagnose<br>
+  // this.<br>
+  namespace QualifiedLookupInScope {<br>
+    namespace N {<br>
+      template <typename> struct S { struct Inner {}; };<br>
+    }<br>
+    template <typename U> void f(typename N::S<U>::Inner *p) {<br>
+      typedef typename N::S<U>::Inner T;<br>
+      p->::dr244::QualifiedLookupInScope::N::S<U>::Inner::~T(); // expected-error {{no type named 'T' in}}<br>
+    }<br>
+    template void f<int>(N::S<int>::Inner *); // expected-note {{instantiation of}}<br>
+<br>
+    template <typename U> void g(U *p) {<br>
+      typedef U T;<br>
+      p->T::~T();<br>
+      p->U::~T();<br>
+      p->::dr244::QualifiedLookupInScope::N::S<int>::Inner::~T(); // expected-error {{'T' does not refer to a type name}}<br>
+    }<br>
+    template void g(N::S<int>::Inner *);<br>
   }<br>
 }<br>
<br>
<br>
diff  --git a/clang/test/CXX/drs/dr3xx.cpp b/clang/test/CXX/drs/dr3xx.cpp<br>
index d723c5b78cdf..4ce624974bbe 100644<br>
--- a/clang/test/CXX/drs/dr3xx.cpp<br>
+++ b/clang/test/CXX/drs/dr3xx.cpp<br>
@@ -98,7 +98,7 @@ namespace dr305 { // dr305: no<br>
     b->~C();<br>
   }<br>
   void h(B *b) {<br>
-    struct B {}; // expected-note {{declared here}}<br>
+    struct B {}; // expected-note {{type 'B' found by destructor name lookup}}<br>
     b->~B(); // expected-error {{does not match}}<br>
   }<br>
<br>
<br>
diff  --git a/clang/test/FixIt/fixit.cpp b/clang/test/FixIt/fixit.cpp<br>
index 92c561a20acc..6e3a41303af9 100644<br>
--- a/clang/test/FixIt/fixit.cpp<br>
+++ b/clang/test/FixIt/fixit.cpp<br>
@@ -2,7 +2,7 @@<br>
 // RUN: cp %s %t-98<br>
 // RUN: not %clang_cc1 -pedantic -Wall -Wno-comment -fcxx-exceptions -fixit -x c++ -std=c++98 %t-98<br>
 // RUN: %clang_cc1 -fsyntax-only -pedantic -Wall -Werror -Wno-comment -fcxx-exceptions -x c++ -std=c++98 %t-98<br>
-// RUN: not %clang_cc1 -fsyntax-only -fdiagnostics-parseable-fixits -x c++ -std=c++11 %s 2>&1 | FileCheck %s<br>
+// RUN: not %clang_cc1 -fsyntax-only -pedantic -fdiagnostics-parseable-fixits -x c++ -std=c++11 %s 2>&1 | FileCheck %s<br>
 // RUN: %clang_cc1 -pedantic -Wall -Wno-comment -verify -fcxx-exceptions -x c++ -std=c++11 %s<br>
 // RUN: cp %s %t-11<br>
 // RUN: not %clang_cc1 -pedantic -Wall -Wno-comment -fcxx-exceptions -fixit -x c++ -std=c++11 %t-11<br>
@@ -318,17 +318,43 @@ class foo {<br>
 };<br>
<br>
 namespace dtor_fixit {<br>
-  class foo {<br>
-    ~bar() { }  // expected-error {{expected the class name after '~' to name a destructor}}<br>
+  struct foo {<br>
+    ~bar() { }  // expected-error {{undeclared identifier 'bar' in destructor name}}<br>
     // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:6-[[@LINE-1]]:9}:"foo"<br>
   };<br>
<br>
-  class bar {<br>
+  class bar { // expected-note {{found}}<br>
     ~bar();<br>
   };<br>
   ~bar::bar() {} // expected-error {{'~' in destructor name should be after nested name specifier}}<br>
   // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:4}:""<br>
   // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:9-[[@LINE-2]]:9}:"~"<br>
+<br>
+  namespace N {<br>
+    typedef foo T;<br>
+    template <typename T> struct X {};<br>
+  }<br>
+  void f(foo *p, N::X<int> *x) {<br>
+    p->~undeclared(); // expected-error {{undeclared}}<br>
+    // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:9-[[@LINE-1]]:19}:"foo"<br>
+<br>
+    p->~bar(); // expected-error {{does not match}}<br>
+    // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:9-[[@LINE-1]]:12}:"foo"<br>
+<br>
+    // FIXME: This is a bad fixit; it'd be better to suggest replacing 'foo'<br>
+    // with 'T'.<br>
+    p->N::T::~foo(); // expected-warning {{requires the name after '::~' to be found in the same scope as the name before}}<br>
+    // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:12-[[@LINE-1]]:12}:"::foo"<br>
+<br>
+    typedef foo baz; // expected-note {{found}}<br>
+    p->dtor_fixit::foo::~baz(); // expected-warning {{only found in lexical scope}}<br>
+    // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:8-[[@LINE-1]]:25}:""<br>
+<br>
+    // FIXME: This is a bad fixit; it'd be better to suggest adding the<br>
+    // template arguments.<br>
+    x->N::X<int>::~X(); // expected-warning {{same scope}}<br>
+    // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:17-[[@LINE-1]]:17}:"::X"<br>
+  }<br>
 }<br>
<br>
 namespace PR5066 {<br>
<br>
diff  --git a/clang/test/Parser/cxx-decl.cpp b/clang/test/Parser/cxx-decl.cpp<br>
index 1914f347d9dd..ba1cce419a46 100644<br>
--- a/clang/test/Parser/cxx-decl.cpp<br>
+++ b/clang/test/Parser/cxx-decl.cpp<br>
@@ -249,10 +249,10 @@ void foo() {<br>
 namespace PR17567 {<br>
   struct Foobar { // expected-note 2{{declared here}}<br>
     FooBar(); // expected-error {{missing return type for function 'FooBar'; did you mean the constructor name 'Foobar'?}}<br>
-    ~FooBar(); // expected-error {{expected the class name after '~' to name a destructor}}<br>
+    ~FooBar(); // expected-error {{undeclared identifier 'FooBar' in destructor name}}<br>
   };<br>
   FooBar::FooBar() {} // expected-error {{undeclared}} expected-error {{missing return type}}<br>
-  FooBar::~FooBar() {} // expected-error {{undeclared}} expected-error {{expected the class name}}<br>
+  FooBar::~FooBar() {} // expected-error 2{{undeclared}}<br>
 }<br>
<br>
 namespace DuplicateFriend {<br>
<br>
diff  --git a/clang/test/SemaCXX/constructor.cpp b/clang/test/SemaCXX/constructor.cpp<br>
index 33ea49663491..d2133240cb14 100644<br>
--- a/clang/test/SemaCXX/constructor.cpp<br>
+++ b/clang/test/SemaCXX/constructor.cpp<br>
@@ -94,6 +94,6 @@ namespace PR38286 {<br>
   /*FIXME: needed to recover properly from previous error*/;<br>
   template<typename> struct B;<br>
   template<typename T> void B<T>::f() {} // expected-error {{out-of-line definition of 'f' from class 'B<type-parameter-0-0>'}}<br>
-  template<typename> struct C;<br>
-  template<typename T> C<T>::~C() {} // expected-error {{no type named 'C' in 'C<type-parameter-0-0>'}}<br>
+  template<typename> struct C; // expected-note {{non-type declaration found}}<br>
+  template<typename T> C<T>::~C() {} // expected-error {{identifier 'C' after '~' in destructor name does not name a type}}<br>
 }<br>
<br>
diff  --git a/clang/test/SemaCXX/destructor.cpp b/clang/test/SemaCXX/destructor.cpp<br>
index 2859953a0280..92afc1256ced 100644<br>
--- a/clang/test/SemaCXX/destructor.cpp<br>
+++ b/clang/test/SemaCXX/destructor.cpp<br>
@@ -75,7 +75,7 @@ struct F {<br>
 };<br>
<br>
 ~; // expected-error {{expected a class name after '~' to name a destructor}}<br>
-~undef(); // expected-error {{expected the class name after '~' to name a destructor}}<br>
+~undef(); // expected-error {{undeclared identifier 'undef' in destructor name}}<br>
 ~operator+(int, int);  // expected-error {{expected a class name after '~' to name a destructor}}<br>
 ~F(){} // expected-error {{destructor must be a non-static member function}}<br>
<br>
@@ -432,7 +432,7 @@ namespace PR9238 {<br>
 }<br>
<br>
 namespace PR7900 {<br>
-  struct A { // expected-note 2{{type 'PR7900::A' is declared here}}<br>
+  struct A { // expected-note 2{{type 'PR7900::A' found by destructor name lookup}}<br>
   };<br>
   struct B : public A {<br>
   };<br>
<br>
diff  --git a/clang/test/SemaCXX/pseudo-destructors.cpp b/clang/test/SemaCXX/pseudo-destructors.cpp<br>
index dfdd1174b8a4..0cd139047432 100644<br>
--- a/clang/test/SemaCXX/pseudo-destructors.cpp<br>
+++ b/clang/test/SemaCXX/pseudo-destructors.cpp<br>
@@ -2,7 +2,7 @@<br>
 struct A {};<br>
<br>
 enum Foo { F };<br>
-typedef Foo Bar; // expected-note{{type 'Bar' (aka 'Foo') is declared here}}<br>
+typedef Foo Bar; // expected-note{{type 'Bar' (aka 'Foo') found by destructor name lookup}}<br>
<br>
 typedef int Integer;<br>
 typedef double Double;<br>
@@ -23,7 +23,7 @@ void f(A* a, Foo *f, int *i, double *d, int ii) {<br>
   a->~A();<br>
   a->A::~A();<br>
<br>
-  a->~foo(); // expected-error{{identifier 'foo' in object destruction expression does not name a type}}<br>
+  a->~foo(); // expected-error{{undeclared identifier 'foo' in destructor name}}<br>
<br>
   a->~Bar(); // expected-error{{destructor type 'Bar' (aka 'Foo') in object destruction expression does not match the type 'A' of the object being destroyed}}<br>
<br>
@@ -83,7 +83,7 @@ namespace PR11339 {<br>
   template<class T><br>
   void destroy(T* p) {<br>
     p->~T(); // ok<br>
-    p->~oops(); // expected-error{{identifier 'oops' in object destruction expression does not name a type}}<br>
+    p->~oops(); // expected-error{{undeclared identifier 'oops' in destructor name}}<br>
   }<br>
<br>
   template void destroy(int*); // expected-note{{in instantiation of function template specialization}}<br>
<br>
<br>
<br>
_______________________________________________<br>
cfe-commits mailing list<br>
<a href="mailto:cfe-commits@lists.llvm.org" target="_blank">cfe-commits@lists.llvm.org</a><br>
<a href="https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits" rel="noreferrer" target="_blank">https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits</a><br>
</blockquote></div>
_______________________________________________<br>
cfe-commits mailing list<br>
<a href="mailto:cfe-commits@lists.llvm.org" target="_blank">cfe-commits@lists.llvm.org</a><br>
<a href="https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits" rel="noreferrer" target="_blank">https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits</a><br>
</blockquote></div></div>
_______________________________________________<br>
cfe-commits mailing list<br>
<a href="mailto:cfe-commits@lists.llvm.org" target="_blank">cfe-commits@lists.llvm.org</a><br>
<a href="https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits" rel="noreferrer" target="_blank">https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits</a><br>
</blockquote></div>
</blockquote></div></div>