]> granicus.if.org Git - clang/commitdiff
Improve recovery for template-ids whose template-name doesn't actually
authorDouglas Gregor <dgregor@apple.com>
Tue, 12 Jan 2010 21:28:44 +0000 (21:28 +0000)
committerDouglas Gregor <dgregor@apple.com>
Tue, 12 Jan 2010 21:28:44 +0000 (21:28 +0000)
name a template, when they occur in a base-specifier. This is one of
the (few) places where we know for sure that an identifier followed by
a '<' must be a template name, so we can diagnose and recover well:

test/SemaTemplate/dependent-base-classes.cpp:9:16: error: missing
'template'
      keyword prior to dependent template name 'T::apply'
struct X1 : T::apply<U> { }; // expected-error{{missing 'template' ...
               ^
               template
test/SemaTemplate/dependent-base-classes.cpp:12:13: error: unknown
template name
      'vector'
struct X2 : vector<T> { }; // expected-error{{unknown template name
'vector'}}
            ^
2 diagnostics generated.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@93257 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/Basic/DiagnosticParseKinds.td
include/clang/Basic/DiagnosticSemaKinds.td
include/clang/Parse/Action.h
lib/Parse/ParseDeclCXX.cpp
lib/Sema/Sema.h
lib/Sema/SemaTemplate.cpp
test/SemaTemplate/dependent-base-classes.cpp

index 98a74a5a03952a2560d2b1b75167dc51809c6af4..51b6bffe007a6522a9ced5bb008ff536ea8d962e 100644 (file)
@@ -261,6 +261,8 @@ def err_attributes_not_allowed : Error<"an attribute list cannot appear here">;
 
 /// C++ Templates
 def err_expected_template : Error<"expected template">;
+def err_unknown_template_name : Error<
+  "unknown template name %0">;
 def err_expected_comma_greater : Error<
   "expected ',' or '>' in template-parameter-list">;
 def err_expected_type_id_after : Error<"expected type-id after '%0'">;
index 82c5544e9f3e8261445b76eeece4ee0682827e7a..8fcc97b42ccbb47da1145fcc5164a4f063a69457 100644 (file)
@@ -1309,6 +1309,8 @@ def err_template_kw_refers_to_class_template : Error<
     "'%0%1' instantiated to a class template, not a function template">;
 def note_referenced_class_template : Error<
     "class template declared here">;
+def err_template_kw_missing : Error<
+  "missing 'template' keyword prior to dependent template name '%0%1'">;
 
 // C++0x Variadic Templates
 def err_template_param_pack_default_arg : Error<
index be46851fe1addf908b88bfb7385fd24a9ee987d3..292a2df91cc4b04222437fb92d920fc515420494 100644 (file)
@@ -291,6 +291,42 @@ public:
                                           bool EnteringContext,
                                           TemplateTy &Template) = 0;
 
+  /// \brief Action called as part of error recovery when the parser has 
+  /// determined that the given name must refer to a template, but 
+  /// \c isTemplateName() did not return a result.
+  ///
+  /// This callback permits the action to give a detailed diagnostic when an
+  /// unknown template name is encountered and, potentially, to try to recover
+  /// by producing a new template in \p SuggestedTemplate.
+  ///
+  /// \param II the name that should be a template.
+  ///
+  /// \param IILoc the location of the name in the source.
+  ///
+  /// \param S the scope in which name lookup was performed.
+  ///
+  /// \param SS the C++ scope specifier that preceded the name.
+  ///
+  /// \param SuggestedTemplate if the action sets this template to a non-NULL,
+  /// template, the parser will recover by consuming the template name token
+  /// and the template argument list that follows.
+  ///
+  /// \param SuggestedTemplateKind as input, the kind of template that we
+  /// expect (e.g., \c TNK_Type_template or \c TNK_Function_template). If the
+  /// action provides a suggested template, this should be set to the kind of
+  /// template.
+  ///
+  /// \returns true if a diagnostic was emitted, false otherwise. When false,
+  /// the parser itself will emit a generic "unknown template name" diagnostic.
+  virtual bool DiagnoseUnknownTemplateName(const IdentifierInfo &II, 
+                                           SourceLocation IILoc,
+                                           Scope *S,
+                                           const CXXScopeSpec *SS,
+                                           TemplateTy &SuggestedTemplate,
+                                           TemplateNameKind &SuggestedKind) {
+    return false;
+  }
+  
   /// ActOnCXXGlobalScopeSpecifier - Return the object that represents the
   /// global scope ('::').
   virtual CXXScopeTy *ActOnCXXGlobalScopeSpecifier(Scope *S,
index 61a494193c79fc2a264df45674c6d4ffd362e253..90040c54bfb1ac37e9af62d332e6261b07b2c43e 100644 (file)
@@ -490,18 +490,57 @@ Parser::TypeResult Parser::ParseClassName(SourceLocation &EndLocation,
     return true;
   }
 
+  IdentifierInfo *Id = Tok.getIdentifierInfo();
+  SourceLocation IdLoc = ConsumeToken();
+
+  if (Tok.is(tok::less)) {
+    // It looks the user intended to write a template-id here, but the
+    // template-name was wrong. Try to fix that.
+    TemplateNameKind TNK = TNK_Type_template;
+    TemplateTy Template;
+    if (!Actions.DiagnoseUnknownTemplateName(*Id, IdLoc, CurScope,
+                                             SS, Template, TNK)) {
+      Diag(IdLoc, diag::err_unknown_template_name)
+        << Id;
+    }
+    
+    if (!Template)
+      return true;
+
+    // Form the template name 
+    UnqualifiedId TemplateName;
+    TemplateName.setIdentifier(Id, IdLoc);
+    
+    // Parse the full template-id, then turn it into a type.
+    if (AnnotateTemplateIdToken(Template, TNK, SS, TemplateName,
+                                SourceLocation(), true))
+      return true;
+    if (TNK == TNK_Dependent_template_name)
+      AnnotateTemplateIdTokenAsType(SS);
+    
+    // If we didn't end up with a typename token, there's nothing more we
+    // can do.
+    if (Tok.isNot(tok::annot_typename))
+      return true;
+    
+    // Retrieve the type from the annotation token, consume that token, and
+    // return.
+    EndLocation = Tok.getAnnotationEndLoc();
+    TypeTy *Type = Tok.getAnnotationValue();
+    ConsumeToken();
+    return Type;
+  }
+
   // We have an identifier; check whether it is actually a type.
-  TypeTy *Type = Actions.getTypeName(*Tok.getIdentifierInfo(),
-                                     Tok.getLocation(), CurScope, SS,
-                                     true);
-  if (!Type) {
-    Diag(Tok, DestrExpected ? diag::err_destructor_class_name
+  TypeTy *Type = Actions.getTypeName(*Id, IdLoc, CurScope, SS, true);
+  if (!Type) {    
+    Diag(IdLoc, DestrExpected ? diag::err_destructor_class_name
                             : diag::err_expected_class_name);
     return true;
   }
 
   // Consume the identifier.
-  EndLocation = ConsumeToken();
+  EndLocation = IdLoc;
   return Type;
 }
 
index d46bfc6cffddfe139eba1f9b687bb056e7fae0c0..d06cecc4678d0297d92f577decc342c6258982ab 100644 (file)
@@ -2367,6 +2367,14 @@ public:
                                           TypeTy *ObjectType,
                                           bool EnteringContext,
                                           TemplateTy &Template);
+  
+  virtual bool DiagnoseUnknownTemplateName(const IdentifierInfo &II, 
+                                           SourceLocation IILoc,
+                                           Scope *S,
+                                           const CXXScopeSpec *SS,
+                                           TemplateTy &SuggestedTemplate,
+                                           TemplateNameKind &SuggestedKind);
+    
   bool DiagnoseTemplateParameterShadow(SourceLocation Loc, Decl *PrevDecl);
   TemplateDecl *AdjustDeclIfTemplate(DeclPtrTy &Decl);
 
index d11bf3c0645851f89fffec0ddf03e6c08c02b33a..e75277e823e0ae156cbb05011eeb311fa407a81f 100644 (file)
@@ -143,6 +143,30 @@ TemplateNameKind Sema::isTemplateName(Scope *S,
   return TemplateKind;
 }
 
+bool Sema::DiagnoseUnknownTemplateName(const IdentifierInfo &II, 
+                                       SourceLocation IILoc,
+                                       Scope *S,
+                                       const CXXScopeSpec *SS,
+                                       TemplateTy &SuggestedTemplate,
+                                       TemplateNameKind &SuggestedKind) {
+  // We can't recover unless there's a dependent scope specifier preceding the
+  // template name.
+  if (!SS || !SS->isSet() || !isDependentScopeSpecifier(*SS) ||
+      computeDeclContext(*SS))
+    return false;
+  
+  // The code is missing a 'template' keyword prior to the dependent template
+  // name.
+  NestedNameSpecifier *Qualifier = (NestedNameSpecifier*)SS->getScopeRep();
+  Diag(IILoc, diag::err_template_kw_missing)
+    << Qualifier << II.getName()
+    << CodeModificationHint::CreateInsertion(IILoc, "template ");
+  SuggestedTemplate 
+    = TemplateTy::make(Context.getDependentTemplateName(Qualifier, &II));
+  SuggestedKind = TNK_Dependent_template_name;
+  return true;
+}
+
 void Sema::LookupTemplateName(LookupResult &Found,
                               Scope *S, const CXXScopeSpec &SS,
                               QualType ObjectType,
index 56979209ea1e9b95ff74c66c5e745494232d1992..242765894f7341236f544fcd96f6a8f58a9b6ed7 100644 (file)
@@ -4,3 +4,9 @@ template<typename T, typename U>
 struct X0 : T::template apply<U> { 
   X0(U u) : T::template apply<U>(u) { }
 };
+
+template<typename T, typename U>
+struct X1 : T::apply<U> { }; // expected-error{{missing 'template' keyword prior to dependent template name 'T::apply'}}
+
+template<typename T>
+struct X2 : vector<T> { }; // expected-error{{unknown template name 'vector'}}