[cfe-commits] r84053 - in /cfe/trunk: include/clang/Basic/DiagnosticSemaKinds.td include/clang/Parse/Action.h lib/Parse/ParseDecl.cpp lib/Sema/Sema.cpp lib/Sema/Sema.h lib/Sema/SemaDecl.cpp test/CXX/class/class.friend/p1.cpp test/SemaCXX/namespace.cpp test/SemaCXX/nested-name-spec.cpp test/SemaCXX/unknown-type-name.cpp test/SemaTemplate/instantiate-typedef.cpp test/SemaTemplate/typename-specifier.cpp

Douglas Gregor dgregor at apple.com
Tue Oct 13 16:27:23 PDT 2009


Author: dgregor
Date: Tue Oct 13 18:27:22 2009
New Revision: 84053

URL: http://llvm.org/viewvc/llvm-project?rev=84053&view=rev
Log:
Improve diagnostics when the parser encounters a declarator with an
unknown type name, e.g.,

  foo::bar x;

when "bar" does not refer to a type in "foo". 

With this change, the parser now calls into the action to perform
diagnostics and can try to recover by substituting in an appropriate
type. For example, this allows us to easily diagnose some missing
"typename" specifiers, which we now do:

  test/SemaCXX/unknown-type-name.cpp:29:1: error: missing 'typename'
        prior to dependent type name 'A<T>::type'
  A<T>::type A<T>::f() { return type(); }
  ^~~~~~~~~~
  typename 

Fixes PR3990.




Added:
    cfe/trunk/test/SemaCXX/unknown-type-name.cpp
Modified:
    cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
    cfe/trunk/include/clang/Parse/Action.h
    cfe/trunk/lib/Parse/ParseDecl.cpp
    cfe/trunk/lib/Sema/Sema.cpp
    cfe/trunk/lib/Sema/Sema.h
    cfe/trunk/lib/Sema/SemaDecl.cpp
    cfe/trunk/test/CXX/class/class.friend/p1.cpp
    cfe/trunk/test/SemaCXX/namespace.cpp
    cfe/trunk/test/SemaCXX/nested-name-spec.cpp
    cfe/trunk/test/SemaTemplate/instantiate-typedef.cpp
    cfe/trunk/test/SemaTemplate/typename-specifier.cpp

Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=84053&r1=84052&r2=84053&view=diff

==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Tue Oct 13 18:27:22 2009
@@ -104,7 +104,7 @@
 def err_using_typename_non_type : Error<
   "'typename' keyword used on a non-type">;
 def err_using_decl_nested_name_specifier_is_not_a_base_class : Error<
-  "using declaration refers into %0, which is not a base class of %1">;
+  "using declaration refers into '%0', which is not a base class of %1">;
 def err_using_decl_can_not_refer_to_class_member : Error<
   "using declaration can not refer to class member">;
  def err_using_decl_can_not_refer_to_namespace : Error<
@@ -990,7 +990,7 @@
   "extraneous template parameter list in template specialization or "
   "out-of-line template definition">;
 def err_template_qualified_declarator_no_match : Error<
-  "nested name specifier %0 for declaration does not refer into a class, "
+  "nested name specifier '%0' for declaration does not refer into a class, "
   "class template or class template partial specialization">;
 
 // C++ Class Template Partial Specialization
@@ -1111,6 +1111,8 @@
     "typename specifier refers to non-type member %0 in %1">;
 def note_typename_refers_here : Note<
     "referenced member %0 is declared here">;
+def err_typename_missing : Error<
+  "missing 'typename' prior to dependent type name '%0%1'">;
 
 def err_template_kw_refers_to_non_template : Error<
     "%0 following the 'template' keyword does not refer to a template">;

Modified: cfe/trunk/include/clang/Parse/Action.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Parse/Action.h?rev=84053&r1=84052&r2=84053&view=diff

==============================================================================
--- cfe/trunk/include/clang/Parse/Action.h (original)
+++ cfe/trunk/include/clang/Parse/Action.h Tue Oct 13 18:27:22 2009
@@ -201,6 +201,36 @@
     return DeclSpec::TST_unspecified;
   }
 
+  /// \brief Action called as part of error recovery when the parser has 
+  /// determined that the given name must refer to a type, but 
+  /// \c getTypeName() did not return a result.
+  ///
+  /// This callback permits the action to give a detailed diagnostic when an
+  /// unknown type name is encountered and, potentially, to try to recover
+  /// by producing a new type in \p SuggestedType.
+  ///
+  /// \param II the name that should be a type.
+  ///
+  /// \param IILoc the location of the name in the source.
+  ///
+  /// \param S the scope in which name lookup was performed.
+  ///
+  /// \param SS if non-NULL, the C++ scope specifier that preceded the name.
+  ///
+  /// \param SuggestedType if the action sets this type to a non-NULL type,
+  /// the parser will recovery by consuming the type name token and then 
+  /// pretending that the given type was the type it parsed.
+  ///
+  /// \returns true if a diagnostic was emitted, false otherwise. When false,
+  /// the parser itself will emit a generic "unknown type name" diagnostic.
+  virtual bool DiagnoseUnknownTypeName(const IdentifierInfo &II, 
+                                       SourceLocation IILoc,
+                                       Scope *S,
+                                       const CXXScopeSpec *SS,
+                                       TypeTy *&SuggestedType) {
+    return false;
+  }
+                                       
   /// isCurrentClassName - Return true if the specified name is the
   /// name of the innermost C++ class type currently being defined.
   virtual bool isCurrentClassName(const IdentifierInfo &II, Scope *S,

Modified: cfe/trunk/lib/Parse/ParseDecl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/ParseDecl.cpp?rev=84053&r1=84052&r2=84053&view=diff

==============================================================================
--- cfe/trunk/lib/Parse/ParseDecl.cpp (original)
+++ cfe/trunk/lib/Parse/ParseDecl.cpp Tue Oct 13 18:27:22 2009
@@ -688,13 +688,36 @@
     }
   }
 
-  // Since this is almost certainly an invalid type name, emit a
-  // diagnostic that says it, eat the token, and mark the declspec as
-  // invalid.
-  SourceRange R;
-  if (SS) R = SS->getRange();
+  // This is almost certainly an invalid type name. Let the action emit a 
+  // diagnostic and attempt to recover.
+  Action::TypeTy *T = 0;
+  if (Actions.DiagnoseUnknownTypeName(*Tok.getIdentifierInfo(), Loc,
+                                      CurScope, SS, T)) {
+    // The action emitted a diagnostic, so we don't have to.
+    if (T) {
+      // The action has suggested that the type T could be used. Set that as
+      // the type in the declaration specifiers, consume the would-be type
+      // name token, and we're done.
+      const char *PrevSpec;
+      unsigned DiagID;
+      DS.SetTypeSpecType(DeclSpec::TST_typename, Loc, PrevSpec, DiagID, T, 
+                         false);
+      DS.SetRangeEnd(Tok.getLocation());
+      ConsumeToken();
+      
+      // There may be other declaration specifiers after this.
+      return true;
+    }
+    
+    // Fall through; the action had no suggestion for us.
+  } else {
+    // The action did not emit a diagnostic, so emit one now.
+    SourceRange R;
+    if (SS) R = SS->getRange();
+    Diag(Loc, diag::err_unknown_typename) << Tok.getIdentifierInfo() << R;
+  }
 
-  Diag(Loc, diag::err_unknown_typename) << Tok.getIdentifierInfo() << R;
+  // Mark this as an error.
   const char *PrevSpec;
   unsigned DiagID;
   DS.SetTypeSpecType(DeclSpec::TST_error, Loc, PrevSpec, DiagID);

Modified: cfe/trunk/lib/Sema/Sema.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/Sema.cpp?rev=84053&r1=84052&r2=84053&view=diff

==============================================================================
--- cfe/trunk/lib/Sema/Sema.cpp (original)
+++ cfe/trunk/lib/Sema/Sema.cpp Tue Oct 13 18:27:22 2009
@@ -117,6 +117,7 @@
     llvm::raw_string_ostream OS(S);
     reinterpret_cast<NestedNameSpecifier*> (Val)->print(OS,
                                                         Context.PrintingPolicy);
+    NeedQuotes = false;
   } else {
     assert(Kind == Diagnostic::ak_declcontext);
     DeclContext *DC = reinterpret_cast<DeclContext *> (Val);

Modified: cfe/trunk/lib/Sema/Sema.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/Sema.h?rev=84053&r1=84052&r2=84053&view=diff

==============================================================================
--- cfe/trunk/lib/Sema/Sema.h (original)
+++ cfe/trunk/lib/Sema/Sema.h Tue Oct 13 18:27:22 2009
@@ -523,7 +523,12 @@
                               Scope *S, const CXXScopeSpec *SS,
                               bool isClassName = false);
   virtual DeclSpec::TST isTagName(IdentifierInfo &II, Scope *S);
-
+  virtual bool DiagnoseUnknownTypeName(const IdentifierInfo &II, 
+                                       SourceLocation IILoc,
+                                       Scope *S,
+                                       const CXXScopeSpec *SS,
+                                       TypeTy *&SuggestedType);
+  
   virtual DeclPtrTy ActOnDeclarator(Scope *S, Declarator &D) {
     return HandleDeclarator(S, D, MultiTemplateParamsArg(*this), false);
   }

Modified: cfe/trunk/lib/Sema/SemaDecl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDecl.cpp?rev=84053&r1=84052&r2=84053&view=diff

==============================================================================
--- cfe/trunk/lib/Sema/SemaDecl.cpp (original)
+++ cfe/trunk/lib/Sema/SemaDecl.cpp Tue Oct 13 18:27:22 2009
@@ -23,6 +23,7 @@
 #include "clang/AST/StmtCXX.h"
 #include "clang/AST/StmtObjC.h"
 #include "clang/Parse/DeclSpec.h"
+#include "clang/Parse/ParseDiagnostic.h"
 #include "clang/Basic/PartialDiagnostic.h"
 #include "clang/Basic/SourceManager.h"
 #include "clang/Basic/TargetInfo.h"
@@ -199,6 +200,36 @@
   return DeclSpec::TST_unspecified;
 }
 
+bool Sema::DiagnoseUnknownTypeName(const IdentifierInfo &II, 
+                                   SourceLocation IILoc,
+                                   Scope *S,
+                                   const CXXScopeSpec *SS,
+                                   TypeTy *&SuggestedType) {
+  // We don't have anything to suggest (yet).
+  SuggestedType = 0;
+  
+  // FIXME: Should we move the logic that tries to recover from a missing tag
+  // (struct, union, enum) from Parser::ParseImplicitInt here, instead?
+  
+  if (!SS)
+    Diag(IILoc, diag::err_unknown_typename) << &II;
+  else if (DeclContext *DC = computeDeclContext(*SS, false))
+    Diag(IILoc, diag::err_typename_nested_not_found) 
+      << &II << DC << SS->getRange();
+  else if (isDependentScopeSpecifier(*SS)) {
+    Diag(SS->getRange().getBegin(), diag::err_typename_missing)
+      << (NestedNameSpecifier *)SS->getScopeRep() << II.getName() 
+      << SourceRange(SS->getRange().getBegin(), IILoc)
+      << CodeModificationHint::CreateInsertion(SS->getRange().getBegin(),
+                                               "typename ");
+    SuggestedType = ActOnTypenameType(SourceLocation(), *SS, II, IILoc).get();
+  } else {
+    assert(SS && SS->isInvalid() && 
+           "Invalid scope specifier has already been diagnosed");
+  }
+  
+  return true;
+}
 
 // Determines the context to return to after temporarily entering a
 // context.  This depends in an unnecessarily complicated way on the

Modified: cfe/trunk/test/CXX/class/class.friend/p1.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/class/class.friend/p1.cpp?rev=84053&r1=84052&r2=84053&view=diff

==============================================================================
--- cfe/trunk/test/CXX/class/class.friend/p1.cpp (original)
+++ cfe/trunk/test/CXX/class/class.friend/p1.cpp Tue Oct 13 18:27:22 2009
@@ -67,7 +67,7 @@
   class facet {};
 };
 
-A::UndeclaredSoFar y; // expected-error {{ unknown type name 'UndeclaredSoFar' }}
+A::UndeclaredSoFar y; // expected-error {{no type named 'UndeclaredSoFar' in 'class A'}}
 
 class PreDeclared;
 

Modified: cfe/trunk/test/SemaCXX/namespace.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/namespace.cpp?rev=84053&r1=84052&r2=84053&view=diff

==============================================================================
--- cfe/trunk/test/SemaCXX/namespace.cpp (original)
+++ cfe/trunk/test/SemaCXX/namespace.cpp Tue Oct 13 18:27:22 2009
@@ -67,4 +67,4 @@
 
 static foo::x  test1;  // ok
 
-static foo::X  test2;  // typo: expected-error {{unknown type name 'X'}}
+static foo::X  test2;  // typo: expected-error {{no type named 'X' in}}

Modified: cfe/trunk/test/SemaCXX/nested-name-spec.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/nested-name-spec.cpp?rev=84053&r1=84052&r2=84053&view=diff

==============================================================================
--- cfe/trunk/test/SemaCXX/nested-name-spec.cpp (original)
+++ cfe/trunk/test/SemaCXX/nested-name-spec.cpp Tue Oct 13 18:27:22 2009
@@ -13,8 +13,8 @@
 }
 
 A:: ; // expected-error {{expected unqualified-id}}
-::A::ax::undef ex3; // expected-error {{no member named}} expected-error {{unknown type name 'undef'}}
-A::undef1::undef2 ex4; // expected-error {{no member named 'undef1'}} expected-error {{unknown type name 'undef2'}}
+::A::ax::undef ex3; // expected-error {{no member named}}
+A::undef1::undef2 ex4; // expected-error {{no member named 'undef1'}}
 
 int A::C::Ag1() { return 0; }
 
@@ -166,7 +166,7 @@
 
 struct Y;  // expected-note{{forward declaration of 'struct Y'}}
 Y::foo y; // expected-error{{incomplete type 'struct Y' named in nested name specifier}} \
-         // expected-error{{unknown type name 'foo'}}
+         // expected-error{{no type named 'foo' in}}
 
 X::X() : a(5) { } // expected-error{{use of undeclared identifier 'X'}} \
       // expected-error{{C++ requires a type specifier for all declarations}} \

Added: cfe/trunk/test/SemaCXX/unknown-type-name.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/unknown-type-name.cpp?rev=84053&view=auto

==============================================================================
--- cfe/trunk/test/SemaCXX/unknown-type-name.cpp (added)
+++ cfe/trunk/test/SemaCXX/unknown-type-name.cpp Tue Oct 13 18:27:22 2009
@@ -0,0 +1,29 @@
+// RUN: clang-cc -fsyntax-only -verify %s
+
+// PR3990
+namespace N {
+  struct Wibble {
+  };
+
+  typedef Wibble foo;
+}
+using namespace N;
+
+foo::bar x; // expected-error{{no type named 'bar' in 'struct N::Wibble'}}
+
+void f() {
+  foo::bar  = 4; // expected-error{{no member named 'bar' in 'struct N::Wibble'}}
+}
+
+template<typename T>
+struct A {
+  typedef T type;
+  
+  type f();
+};
+
+template<typename T>
+A<T>::type g(T t) { return t; } // expected-error{{missing 'typename'}}
+
+template<typename T>
+A<T>::type A<T>::f() { return type(); } // expected-error{{missing 'typename'}}

Modified: cfe/trunk/test/SemaTemplate/instantiate-typedef.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaTemplate/instantiate-typedef.cpp?rev=84053&r1=84052&r2=84053&view=diff

==============================================================================
--- cfe/trunk/test/SemaTemplate/instantiate-typedef.cpp (original)
+++ cfe/trunk/test/SemaTemplate/instantiate-typedef.cpp Tue Oct 13 18:27:22 2009
@@ -12,5 +12,5 @@
 }
 
 add_pointer<int&>::type // expected-note{{in instantiation of template class 'struct add_pointer<int &>' requested here}} \
-// expected-error {{unknown type name 'type'}}
+// expected-error {{no type named 'type' in 'struct add_pointer<int &>'}}
 test3(); 

Modified: cfe/trunk/test/SemaTemplate/typename-specifier.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaTemplate/typename-specifier.cpp?rev=84053&r1=84052&r2=84053&view=diff

==============================================================================
--- cfe/trunk/test/SemaTemplate/typename-specifier.cpp (original)
+++ cfe/trunk/test/SemaTemplate/typename-specifier.cpp Tue Oct 13 18:27:22 2009
@@ -43,12 +43,12 @@
 
 N::X<N::A>::type *ip4 = &i;
 N::X<N::B>::type *ip5 = &i; // expected-note{{in instantiation of template class 'struct N::X<struct N::B>' requested here}} \
-// expected-error{{unknown type name 'type'}}
+// expected-error{{no type named 'type' in}}
 N::X<N::C>::type *ip6 = &i; // expected-note{{in instantiation of template class 'struct N::X<struct N::C>' requested here}} \
-// expected-error{{unknown type name 'type'}}
+// expected-error{{no type named 'type' in}}
 
 N::X<int>::type fail1; // expected-note{{in instantiation of template class 'struct N::X<int>' requested here}} \
-// expected-error{{unknown type name 'type'}}
+// expected-error{{no type named 'type' in}}
 
 template<typename T>
 struct Y {
@@ -70,6 +70,6 @@
 
 ::Y<A>::type ip7 = &i;
 ::Y<B>::type ip8 = &i; // expected-note{{in instantiation of template class 'struct Y<struct B>' requested here}} \
-// expected-error{{unknown type name 'type'}}
+// expected-error{{no type named 'type' in}}
 ::Y<C>::type ip9 = &i; // expected-note{{in instantiation of template class 'struct Y<struct C>' requested here}} \
-// expected-error{{unknown type name 'type'}}
+// expected-error{{no type named 'type' in}}





More information about the cfe-commits mailing list