r193020 - Allow CorrectTypo to replace CXXScopeSpecifiers that refer to classes.

Kaelyn Uhrain rikka at google.com
Fri Oct 18 17:05:00 PDT 2013


Author: rikka
Date: Fri Oct 18 19:05:00 2013
New Revision: 193020

URL: http://llvm.org/viewvc/llvm-project?rev=193020&view=rev
Log:
Allow CorrectTypo to replace CXXScopeSpecifiers that refer to classes.

Now that CorrectTypo knows how to correctly search classes for typo
correction candidates, there is no good reason to only replace an
existing CXXScopeSpecifier if it refers to a namespace. While the actual
enablement was a matter of changing a single comparison, the fallout
from enabling the functionality required a lot more code changes
(including my two previous commits).

Modified:
    cfe/trunk/lib/Sema/SemaDeclCXX.cpp
    cfe/trunk/lib/Sema/SemaExprMember.cpp
    cfe/trunk/lib/Sema/SemaLookup.cpp
    cfe/trunk/test/CXX/class.access/class.friend/p1.cpp
    cfe/trunk/test/CXX/class.access/p6.cpp
    cfe/trunk/test/CXX/temp/temp.decls/temp.class/temp.mem.enum/p1.cpp
    cfe/trunk/test/Parser/cxx-using-directive.cpp
    cfe/trunk/test/Parser/switch-recovery.cpp
    cfe/trunk/test/SemaCXX/missing-members.cpp
    cfe/trunk/test/SemaCXX/typo-correction-pt2.cpp
    cfe/trunk/test/SemaCXX/typo-correction.cpp
    cfe/trunk/test/SemaTemplate/temp_arg_nontype.cpp

Modified: cfe/trunk/lib/Sema/SemaDeclCXX.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDeclCXX.cpp?rev=193020&r1=193019&r2=193020&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaDeclCXX.cpp (original)
+++ cfe/trunk/lib/Sema/SemaDeclCXX.cpp Fri Oct 18 19:05:00 2013
@@ -7301,9 +7301,10 @@ void Sema::HideUsingShadowDecl(Scope *S,
 namespace {
 class UsingValidatorCCC : public CorrectionCandidateCallback {
 public:
-  UsingValidatorCCC(bool HasTypenameKeyword, bool IsInstantiation)
+  UsingValidatorCCC(bool HasTypenameKeyword, bool IsInstantiation,
+                    bool RequireMember)
       : HasTypenameKeyword(HasTypenameKeyword),
-        IsInstantiation(IsInstantiation) {}
+        IsInstantiation(IsInstantiation), RequireMember(RequireMember) {}
 
   bool ValidateCandidate(const TypoCorrection &Candidate) LLVM_OVERRIDE {
     NamedDecl *ND = Candidate.getCorrectionDecl();
@@ -7312,6 +7313,10 @@ public:
     if (!ND || isa<NamespaceDecl>(ND))
       return false;
 
+    if (RequireMember && !isa<FieldDecl>(ND) && !isa<CXXMethodDecl>(ND) &&
+        !isa<TypeDecl>(ND))
+      return false;
+
     // Completely unqualified names are invalid for a 'using' declaration.
     if (Candidate.WillReplaceSpecifier() && !Candidate.getCorrectionSpecifier())
       return false;
@@ -7325,6 +7330,7 @@ public:
 private:
   bool HasTypenameKeyword;
   bool IsInstantiation;
+  bool RequireMember;
 };
 } // end anonymous namespace
 
@@ -7440,7 +7446,8 @@ NamedDecl *Sema::BuildUsingDeclaration(S
 
   // Try to correct typos if possible.
   if (R.empty()) {
-    UsingValidatorCCC CCC(HasTypenameKeyword, IsInstantiation);
+    UsingValidatorCCC CCC(HasTypenameKeyword, IsInstantiation,
+                          CurContext->isRecord());
     if (TypoCorrection Corrected = CorrectTypo(R.getLookupNameInfo(),
                                                R.getLookupKind(), S, &SS, CCC)){
       // We reject any correction for which ND would be NULL.

Modified: cfe/trunk/lib/Sema/SemaExprMember.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaExprMember.cpp?rev=193020&r1=193019&r2=193020&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaExprMember.cpp (original)
+++ cfe/trunk/lib/Sema/SemaExprMember.cpp Fri Oct 18 19:05:00 2013
@@ -538,13 +538,42 @@ bool Sema::CheckQualifiedMemberReference
 namespace {
 
 // Callback to only accept typo corrections that are either a ValueDecl or a
-// FunctionTemplateDecl.
+// FunctionTemplateDecl and are declared in the current record or, for a C++
+// classes, one of its base classes.
 class RecordMemberExprValidatorCCC : public CorrectionCandidateCallback {
  public:
+  explicit RecordMemberExprValidatorCCC(const RecordType *RTy)
+      : Record(RTy->getDecl()) {}
+
   virtual bool ValidateCandidate(const TypoCorrection &candidate) {
     NamedDecl *ND = candidate.getCorrectionDecl();
-    return ND && (isa<ValueDecl>(ND) || isa<FunctionTemplateDecl>(ND));
+    // Don't accept candidates that cannot be member functions, constants,
+    // variables, or templates.
+    if (!ND || !(isa<ValueDecl>(ND) || isa<FunctionTemplateDecl>(ND)))
+      return false;
+
+    // Accept candidates that occur in the current record.
+    if (Record->containsDecl(ND))
+      return true;
+
+    if (const CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(Record)) {
+      // Accept candidates that occur in any of the current class' base classes.
+      for (CXXRecordDecl::base_class_const_iterator BS = RD->bases_begin(),
+                                                    BSEnd = RD->bases_end();
+           BS != BSEnd; ++BS) {
+        if (const RecordType *BSTy = dyn_cast_or_null<RecordType>(
+                BS->getType().getTypePtrOrNull())) {
+          if (BSTy->getDecl()->containsDecl(ND))
+            return true;
+        }
+      }
+    }
+
+    return false;
   }
+
+ private:
+  const RecordDecl *const Record;
 };
 
 }
@@ -600,7 +629,7 @@ LookupMemberExprInRecord(Sema &SemaRef,
   // We didn't find anything with the given name, so try to correct
   // for typos.
   DeclarationName Name = R.getLookupName();
-  RecordMemberExprValidatorCCC Validator;
+  RecordMemberExprValidatorCCC Validator(RTy);
   TypoCorrection Corrected = SemaRef.CorrectTypo(R.getLookupNameInfo(),
                                                  R.getLookupKind(), NULL,
                                                  &SS, Validator, DC);

Modified: cfe/trunk/lib/Sema/SemaLookup.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaLookup.cpp?rev=193020&r1=193019&r2=193020&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaLookup.cpp (original)
+++ cfe/trunk/lib/Sema/SemaLookup.cpp Fri Oct 18 19:05:00 2013
@@ -4157,7 +4157,7 @@ TypoCorrection Sema::CorrectTypo(const D
   // corrections.
   bool SearchNamespaces
     = getLangOpts().CPlusPlus &&
-      (IsUnqualifiedLookup || (QualifiedDC && QualifiedDC->isNamespace()));
+      (IsUnqualifiedLookup || (SS && SS->isSet()));
   // In a few cases we *only* want to search for corrections based on just
   // adding or changing the nested name specifier.
   unsigned TypoLen = Typo->getName().size();
@@ -4400,6 +4400,18 @@ retry_lookup:
           switch (TmpRes.getResultKind()) {
           case LookupResult::Found:
           case LookupResult::FoundOverloaded: {
+            if (SS && SS->isValid()) {
+              std::string NewQualified = TC.getAsString(getLangOpts());
+              std::string OldQualified;
+              llvm::raw_string_ostream OldOStream(OldQualified);
+              SS->getScopeRep()->print(OldOStream, getPrintingPolicy());
+              OldOStream << TypoName;
+              // If correction candidate would be an identical written qualified
+              // identifer, then the existing CXXScopeSpec probably included a
+              // typedef that didn't get accounted for properly.
+              if (OldOStream.str() == NewQualified)
+                break;
+            }
             for (LookupResult::iterator TRD = TmpRes.begin(),
                                      TRDEnd = TmpRes.end();
                  TRD != TRDEnd; ++TRD) {

Modified: cfe/trunk/test/CXX/class.access/class.friend/p1.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/class.access/class.friend/p1.cpp?rev=193020&r1=193019&r2=193020&view=diff
==============================================================================
--- cfe/trunk/test/CXX/class.access/class.friend/p1.cpp (original)
+++ cfe/trunk/test/CXX/class.access/class.friend/p1.cpp Fri Oct 18 19:05:00 2013
@@ -7,8 +7,8 @@
 //   special access rights to the friends, but they do not make the nominated
 //   friends members of the befriending class.
 
-struct S { static void f(); };
-S* g() { return 0; }
+struct S { static void f(); }; // expected-note 2 {{'S' declared here}}
+S* g() { return 0; } // expected-note 2 {{'g' declared here}}
 
 struct X {
   friend struct S;
@@ -19,8 +19,8 @@ void test1() {
   S s;
   g()->f();
   S::f();
-  X::g(); // expected-error{{no member named 'g' in 'X'}}
-  X::S x_s; // expected-error{{no type named 'S' in 'X'}}
+  X::g(); // expected-error{{no member named 'g' in 'X'; did you mean simply 'g'?}}
+  X::S x_s; // expected-error{{no type named 'S' in 'X'; did you mean simply 'S'?}}
   X x;
   x.g(); // expected-error{{no member named 'g' in 'X'}}
 }
@@ -36,24 +36,24 @@ namespace N {
     friend struct S2* g2();
   };
 
-  struct S2 { static void f2(); };
-  S2* g2() { return 0; }
+  struct S2 { static void f2(); }; // expected-note 2 {{'S2' declared here}}
+  S2* g2() { return 0; } // expected-note 2 {{'g2' declared here}}
 
   void test() {
     g()->f();
     S s;
     S::f();
-    X::g(); // expected-error{{no member named 'g' in 'N::X'}}
-    X::S x_s; // expected-error{{no type named 'S' in 'N::X'}}
+    X::g(); // expected-error{{no member named 'g' in 'N::X'; did you mean simply 'g'?}}
+    X::S x_s; // expected-error{{no type named 'S' in 'N::X'; did you mean simply 'S'?}}
     X x;
     x.g(); // expected-error{{no member named 'g' in 'N::X'}}
 
     g2();
     S2 s2;
-    ::g2(); // expected-error{{no member named 'g2' in the global namespace}}
-    ::S2 g_s2; // expected-error{{no type named 'S2' in the global namespace}}
-    X::g2(); // expected-error{{no member named 'g2' in 'N::X'}}
-    X::S2 x_s2; // expected-error{{no type named 'S2' in 'N::X'}}
+    ::g2(); // expected-error{{no member named 'g2' in the global namespace; did you mean simply 'g2'?}}
+    ::S2 g_s2; // expected-error{{no type named 'S2' in the global namespace; did you mean simply 'S2'?}}
+    X::g2(); // expected-error{{no member named 'g2' in 'N::X'; did you mean simply 'g2'?}}
+    X::S2 x_s2; // expected-error{{no type named 'S2' in 'N::X'; did you mean simply 'S2'?}}
     x.g2(); // expected-error{{no member named 'g2' in 'N::X'}}
   }
 }

Modified: cfe/trunk/test/CXX/class.access/p6.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/class.access/p6.cpp?rev=193020&r1=193019&r2=193020&view=diff
==============================================================================
--- cfe/trunk/test/CXX/class.access/p6.cpp (original)
+++ cfe/trunk/test/CXX/class.access/p6.cpp Fri Oct 18 19:05:00 2013
@@ -92,7 +92,7 @@ namespace test3 {
 
   template <class T> class Outer::A<T, typename T::nature> {
   public:
-    static void foo();
+    static void foo(); // expected-note {{'Outer::A<B, Green>::foo' declared here}}
   };
 
   class B {
@@ -102,7 +102,7 @@ namespace test3 {
 
   void test() {
     Outer::A<B, Green>::foo();
-    Outer::A<B, Blue>::foo(); // expected-error {{no member named 'foo'}}
+    Outer::A<B, Blue>::foo(); // expected-error {{no member named 'foo' in 'test3::Outer::A<test3::B, test3::Blue>'; did you mean 'Outer::A<B, Green>::foo'?}}
   }
 }
 

Modified: cfe/trunk/test/CXX/temp/temp.decls/temp.class/temp.mem.enum/p1.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/temp/temp.decls/temp.class/temp.mem.enum/p1.cpp?rev=193020&r1=193019&r2=193020&view=diff
==============================================================================
--- cfe/trunk/test/CXX/temp/temp.decls/temp.class/temp.mem.enum/p1.cpp (original)
+++ cfe/trunk/test/CXX/temp/temp.decls/temp.class/temp.mem.enum/p1.cpp Fri Oct 18 19:05:00 2013
@@ -12,7 +12,7 @@ A<int> a;
 A<int>::E a0 = A<int>().v;
 int n = A<int>::E::e1; // expected-error {{implicit instantiation of undefined member}}
 
-template<typename T> enum A<T>::E : T { e1, e2 };
+template<typename T> enum A<T>::E : T { e1, e2 }; // expected-note 2 {{declared here}}
 
 // FIXME: Now that A<T>::E is defined, we are supposed to inject its enumerators
 // into the already-instantiated class A<T>. This seems like a really bad idea,
@@ -20,7 +20,7 @@ template<typename T> enum A<T>::E : T {
 //
 // Either do as the standard says, or only include enumerators lexically defined
 // within the class in its scope.
-A<int>::E a1 = A<int>::e1; // expected-error {{no member named 'e1' in 'A<int>'}}
+A<int>::E a1 = A<int>::e1; // expected-error {{no member named 'e1' in 'A<int>'; did you mean simply 'e1'?}}
 
 A<char>::E a2 = A<char>::e2;
 
@@ -94,7 +94,7 @@ D<int>::E d1 = D<int>::E::e1; // expecte
 template<> enum class D<int>::E { e2 };
 D<int>::E d2 = D<int>::E::e2;
 D<char>::E d3 = D<char>::E::e1; // expected-note {{first required here}}
-D<char>::E d4 = D<char>::E::e2; // expected-error {{no member named 'e2'}}
+D<char>::E d4 = D<char>::E::e2; // expected-error {{no member named 'e2' in 'D<char>::E'; did you mean simply 'e2'?}}
 template<> enum class D<char>::E { e3 }; // expected-error {{explicit specialization of 'E' after instantiation}}
 
 template<> enum class D<short>::E;

Modified: cfe/trunk/test/Parser/cxx-using-directive.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Parser/cxx-using-directive.cpp?rev=193020&r1=193019&r2=193020&view=diff
==============================================================================
--- cfe/trunk/test/Parser/cxx-using-directive.cpp (original)
+++ cfe/trunk/test/Parser/cxx-using-directive.cpp Fri Oct 18 19:05:00 2013
@@ -4,7 +4,7 @@ class A {};
 
 namespace B {
   namespace A {} // expected-note{{namespace '::B::A' defined here}} \
-                 // expected-note{{namespace 'B::A' defined here}}
+                 // expected-note 2{{namespace 'B::A' defined here}}
   using namespace A ;
 }
 
@@ -28,7 +28,7 @@ namespace D {
 
 using namespace ! ; // expected-error{{expected namespace name}}
 using namespace A ; // expected-error{{no namespace named 'A'; did you mean 'B::A'?}}
-using namespace ::A // expected-error{{expected namespace name}} \
+using namespace ::A // expected-error{{no namespace named 'A' in the global namespace; did you mean 'B::A'?}} \
                     // expected-error{{expected ';' after namespace name}}
                     B ; 
 

Modified: cfe/trunk/test/Parser/switch-recovery.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Parser/switch-recovery.cpp?rev=193020&r1=193019&r2=193020&view=diff
==============================================================================
--- cfe/trunk/test/Parser/switch-recovery.cpp (original)
+++ cfe/trunk/test/Parser/switch-recovery.cpp Fri Oct 18 19:05:00 2013
@@ -95,7 +95,7 @@ int test8( foo x ) {
 }
 
 // Stress test to make sure Clang doesn't crash.
-void test9(int x) {
+void test9(int x) { // expected-note {{'x' declared here}}
   switch(x) {
     case 1: return;
     2: case; // expected-error {{expected 'case' keyword before expression}} \
@@ -104,8 +104,8 @@ void test9(int x) {
     7: :x; // expected-error {{expected 'case' keyword before expression}} \
               expected-error {{expected expression}}
     8:: x; // expected-error {{expected ';' after expression}} \
-              expected-error {{no member named 'x' in the global namespace}} \
-              expected-warning {{expression result unused}}
+              expected-error {{no member named 'x' in the global namespace; did you mean simply 'x'?}} \
+              expected-warning 2 {{expression result unused}}
     9:: :y; // expected-error {{expected ';' after expression}} \
                expected-error {{expected unqualified-id}} \
                expected-warning {{expression result unused}}

Modified: cfe/trunk/test/SemaCXX/missing-members.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/missing-members.cpp?rev=193020&r1=193019&r2=193020&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/missing-members.cpp (original)
+++ cfe/trunk/test/SemaCXX/missing-members.cpp Fri Oct 18 19:05:00 2013
@@ -1,7 +1,7 @@
 // RUN: %clang_cc1 -fsyntax-only -verify %s
 namespace A {
   namespace B {
-    class C { };
+    class C { }; // expected-note 2 {{'A::B::C' declared here}}
     struct S { };
     union U { };
   }
@@ -19,8 +19,12 @@ namespace B {
 
 void g() {
   A::B::D::E; // expected-error {{no member named 'D' in namespace 'A::B'}}
-  B::B::C::D; // expected-error {{no member named 'C' in 'B::B'}}
-  ::C::D; // expected-error {{no member named 'C' in the global namespace}}
+  // FIXME: The typo corrections below should be suppressed since A::B::C
+  // doesn't have a member named D.
+  B::B::C::D; // expected-error {{no member named 'C' in 'B::B'; did you mean 'A::B::C'?}} \
+              // expected-error {{no member named 'D' in 'A::B::C'}}
+  ::C::D; // expected-error {{no member named 'C' in the global namespace; did you mean 'A::B::C'?}}\
+          // expected-error {{no member named 'D' in 'A::B::C'}}
 }
 
 int A::B::i = 10; // expected-error {{no member named 'i' in namespace 'A::B'}}

Modified: cfe/trunk/test/SemaCXX/typo-correction-pt2.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/typo-correction-pt2.cpp?rev=193020&r1=193019&r2=193020&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/typo-correction-pt2.cpp (original)
+++ cfe/trunk/test/SemaCXX/typo-correction-pt2.cpp Fri Oct 18 19:05:00 2013
@@ -168,3 +168,16 @@ namespace PR17019 {
     evil<int> Q(0); // expected-note {{in instantiation of member function}}
   }
 }
+
+namespace fix_class_name_qualifier {
+class MessageHeaders {};
+class MessageUtils {
+ public:
+  static void ParseMessageHeaders(int, int); // expected-note {{'MessageUtils::ParseMessageHeaders' declared here}}
+};
+
+void test() {
+  // No, we didn't mean to call MessageHeaders::MessageHeaders.
+  MessageHeaders::ParseMessageHeaders(5, 4); // expected-error {{no member named 'ParseMessageHeaders' in 'fix_class_name_qualifier::MessageHeaders'; did you mean 'MessageUtils::ParseMessageHeaders'?}}
+}
+}

Modified: cfe/trunk/test/SemaCXX/typo-correction.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/typo-correction.cpp?rev=193020&r1=193019&r2=193020&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/typo-correction.cpp (original)
+++ cfe/trunk/test/SemaCXX/typo-correction.cpp Fri Oct 18 19:05:00 2013
@@ -217,10 +217,14 @@ namespace PR13051 {
     operator bool() const;
   };
 
-  void f() {
-    f(&S<int>::tempalte f<int>); // expected-error{{did you mean 'template'?}}
-    f(&S<int>::opeartor bool); // expected-error{{did you mean 'operator'?}}
-    f(&S<int>::foo); // expected-error-re{{no member named 'foo' in 'PR13051::S<int>'$}}
+  void foo(); // expected-note{{'foo' declared here}}
+  void g(void(*)());
+  void g(bool(S<int>::*)() const);
+
+  void test() {
+    g(&S<int>::tempalte f<int>); // expected-error{{did you mean 'template'?}}
+    g(&S<int>::opeartor bool); // expected-error{{did you mean 'operator'?}}
+    g(&S<int>::foo); // expected-error{{no member named 'foo' in 'PR13051::S<int>'; did you mean simply 'foo'?}}
   }
 }
 

Modified: cfe/trunk/test/SemaTemplate/temp_arg_nontype.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaTemplate/temp_arg_nontype.cpp?rev=193020&r1=193019&r2=193020&view=diff
==============================================================================
--- cfe/trunk/test/SemaTemplate/temp_arg_nontype.cpp (original)
+++ cfe/trunk/test/SemaTemplate/temp_arg_nontype.cpp Fri Oct 18 19:05:00 2013
@@ -252,16 +252,16 @@ namespace PR8372 {
 
 namespace PR9227 {
   template <bool B> struct enable_if_bool { };
-  template <> struct enable_if_bool<true> { typedef int type; };
-  void test_bool() { enable_if_bool<false>::type i; } // expected-error{{enable_if_bool<false>}}
+  template <> struct enable_if_bool<true> { typedef int type; }; // expected-note{{'enable_if_bool<true>::type' declared here}}
+  void test_bool() { enable_if_bool<false>::type i; } // expected-error{{enable_if_bool<false>'; did you mean 'enable_if_bool<true>::type'?}}
 
   template <char C> struct enable_if_char { };
-  template <> struct enable_if_char<'a'> { typedef int type; };
-  void test_char_0() { enable_if_char<0>::type i; } // expected-error{{enable_if_char<'\x00'>}}
-  void test_char_b() { enable_if_char<'b'>::type i; } // expected-error{{enable_if_char<'b'>}}
-  void test_char_possibly_negative() { enable_if_char<'\x02'>::type i; } // expected-error{{enable_if_char<'\x02'>}}
-  void test_char_single_quote() { enable_if_char<'\''>::type i; } // expected-error{{enable_if_char<'\''>}}
-  void test_char_backslash() { enable_if_char<'\\'>::type i; } // expected-error{{enable_if_char<'\\'>}}
+  template <> struct enable_if_char<'a'> { typedef int type; }; // expected-note 5{{'enable_if_char<'a'>::type' declared here}}
+  void test_char_0() { enable_if_char<0>::type i; } // expected-error{{enable_if_char<'\x00'>'; did you mean 'enable_if_char<'a'>::type'?}}
+  void test_char_b() { enable_if_char<'b'>::type i; } // expected-error{{enable_if_char<'b'>'; did you mean 'enable_if_char<'a'>::type'?}}
+  void test_char_possibly_negative() { enable_if_char<'\x02'>::type i; } // expected-error{{enable_if_char<'\x02'>'; did you mean 'enable_if_char<'a'>::type'?}}
+  void test_char_single_quote() { enable_if_char<'\''>::type i; } // expected-error{{enable_if_char<'\''>'; did you mean 'enable_if_char<'a'>::type'?}}
+  void test_char_backslash() { enable_if_char<'\\'>::type i; } // expected-error{{enable_if_char<'\\'>'; did you mean 'enable_if_char<'a'>::type'?}}
 }
 
 namespace PR10579 {





More information about the cfe-commits mailing list