From: Douglas Gregor Date: Wed, 27 Apr 2011 04:48:22 +0000 (+0000) Subject: Extend Sema::ClassifyName() to support C++, ironing out a few issues X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=3b887354b1b667c97d070ddc67b5354353c4c07b;p=clang Extend Sema::ClassifyName() to support C++, ironing out a few issues in the classification of template names and using declarations. We now properly typo-correct the leading identifiers in statements to types, templates, values, etc. As an added bonus, this reduces the number of lookups required for disambiguation. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@130288 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/lib/Parse/ParseStmt.cpp b/lib/Parse/ParseStmt.cpp index a324bdc045..3aec4ea210 100644 --- a/lib/Parse/ParseStmt.cpp +++ b/lib/Parse/ParseStmt.cpp @@ -113,7 +113,7 @@ Retry: return ParseLabeledStatement(attrs); } - if (!getLang().CPlusPlus) { + if (Next.isNot(tok::coloncolon)) { // FIXME: Temporarily enable this code only for C. CXXScopeSpec SS; IdentifierInfo *Name = Tok.getIdentifierInfo(); @@ -147,7 +147,7 @@ Retry: // we're in a syntactic context we haven't handled yet. break; - case Sema::NC_Type: + case Sema::NC_Type: { // We have a type. In C, this means that we have a declaration. if (!getLang().CPlusPlus) { ParsedType Type = Classification.getType(); @@ -195,9 +195,15 @@ Retry: return Actions.ActOnDeclStmt(Decl, DeclStart, DeclEnd); } - // In C++, we might also have a functional-style cast. - // FIXME: Implement this! + // In C++, we might also have a functional-style cast. Just annotate + // this as a type token. + Tok.setKind(tok::annot_typename); + setTypeAnnotation(Tok, Classification.getType()); + Tok.setAnnotationEndLoc(NameLoc); + Tok.setLocation(NameLoc); + PP.AnnotateCachedTokens(Tok); break; + } case Sema::NC_Expression: ConsumeToken(); // the identifier @@ -211,7 +217,20 @@ Retry: if (AnnotateTemplateIdToken( TemplateTy::make(Classification.getTemplateName()), Classification.getTemplateNameKind(), - SS, Id)) { + SS, Id, SourceLocation(), + /*AllowTypeAnnotation=*/false)) { + // Handle errors here by skipping up to the next semicolon or '}', and + // eat the semicolon if that's what stopped us. + SkipUntil(tok::r_brace, /*StopAtSemi=*/true, /*DontConsume=*/true); + if (Tok.is(tok::semi)) + ConsumeToken(); + return StmtError(); + } + + // If the next token is '::', jump right into parsing a + // nested-name-specifier. We don't want to leave the template-id + // hanging. + if (NextToken().is(tok::coloncolon) && TryAnnotateCXXScopeToken(false)){ // Handle errors here by skipping up to the next semicolon or '}', and // eat the semicolon if that's what stopped us. SkipUntil(tok::r_brace, /*StopAtSemi=*/true, /*DontConsume=*/true); diff --git a/lib/Parse/Parser.cpp b/lib/Parse/Parser.cpp index debc7404b9..ee4ef4263a 100644 --- a/lib/Parse/Parser.cpp +++ b/lib/Parse/Parser.cpp @@ -1255,7 +1255,8 @@ bool Parser::TryAnnotateTypeOrScopeToken(bool EnteringContext) { bool Parser::TryAnnotateCXXScopeToken(bool EnteringContext) { assert(getLang().CPlusPlus && "Call sites of this function should be guarded by checking for C++"); - assert((Tok.is(tok::identifier) || Tok.is(tok::coloncolon)) && + assert((Tok.is(tok::identifier) || Tok.is(tok::coloncolon) || + (Tok.is(tok::annot_template_id) && NextToken().is(tok::coloncolon)))&& "Cannot be a type or scope token!"); CXXScopeSpec SS; diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index 200082f17b..f589b2d4a9 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -514,15 +514,16 @@ Corrected: unsigned QualifiedDiag = diag::err_no_member_suggest; NamedDecl *FirstDecl = Result.empty()? 0 : *Result.begin(); - + NamedDecl *UnderlyingFirstDecl + = FirstDecl? FirstDecl->getUnderlyingDecl() : 0; if (getLangOptions().CPlusPlus && NextToken.is(tok::less) && - FirstDecl && isa(FirstDecl)) { + UnderlyingFirstDecl && isa(UnderlyingFirstDecl)) { UnqualifiedDiag = diag::err_no_template_suggest; QualifiedDiag = diag::err_no_member_template_suggest; - } else if (FirstDecl && - (isa(FirstDecl) || - isa(FirstDecl) || - isa(FirstDecl))) { + } else if (UnderlyingFirstDecl && + (isa(UnderlyingFirstDecl) || + isa(UnderlyingFirstDecl) || + isa(UnderlyingFirstDecl))) { UnqualifiedDiag = diag::err_unknown_typename_suggest; QualifiedDiag = diag::err_unknown_nested_typename_suggest; } @@ -588,7 +589,8 @@ Corrected: break; case LookupResult::Ambiguous: - if (getLangOptions().CPlusPlus && NextToken.is(tok::less)) { + if (getLangOptions().CPlusPlus && NextToken.is(tok::less) && + hasAnyAcceptableTemplateNames(Result)) { // C++ [temp.local]p3: // A lookup that finds an injected-class-name (10.2) can result in an // ambiguity in certain cases (for example, if it is found in more than @@ -622,37 +624,40 @@ Corrected: if (!IsFilteredTemplateName) FilterAcceptableTemplateNames(Result); - bool IsFunctionTemplate; - TemplateName Template; - if (Result.end() - Result.begin() > 1) { - IsFunctionTemplate = true; - Template = Context.getOverloadedTemplateName(Result.begin(), - Result.end()); - } else { - TemplateDecl *TD = cast(Result.getFoundDecl()); - IsFunctionTemplate = isa(TD); - - if (SS.isSet() && !SS.isInvalid()) - Template = Context.getQualifiedTemplateName(SS.getScopeRep(), + if (!Result.empty()) { + bool IsFunctionTemplate; + TemplateName Template; + if (Result.end() - Result.begin() > 1) { + IsFunctionTemplate = true; + Template = Context.getOverloadedTemplateName(Result.begin(), + Result.end()); + } else { + TemplateDecl *TD + = cast((*Result.begin())->getUnderlyingDecl()); + IsFunctionTemplate = isa(TD); + + if (SS.isSet() && !SS.isInvalid()) + Template = Context.getQualifiedTemplateName(SS.getScopeRep(), /*TemplateKeyword=*/false, - TD); - else - Template = TemplateName(TD); - } - - if (IsFunctionTemplate) { - // Function templates always go through overload resolution, at which - // point we'll perform the various checks (e.g., accessibility) we need - // to based on which function we selected. - Result.suppressDiagnostics(); + TD); + else + Template = TemplateName(TD); + } + + if (IsFunctionTemplate) { + // Function templates always go through overload resolution, at which + // point we'll perform the various checks (e.g., accessibility) we need + // to based on which function we selected. + Result.suppressDiagnostics(); + + return NameClassification::FunctionTemplate(Template); + } - return NameClassification::FunctionTemplate(Template); + return NameClassification::TypeTemplate(Template); } - - return NameClassification::TypeTemplate(Template); } - NamedDecl *FirstDecl = *Result.begin(); + NamedDecl *FirstDecl = (*Result.begin())->getUnderlyingDecl(); if (TypeDecl *Type = dyn_cast(FirstDecl)) { DiagnoseUseOfDecl(Type, NameLoc); QualType T = Context.getTypeDeclType(Type); @@ -681,6 +686,9 @@ Corrected: return ParsedType::make(T); } + if (!Result.empty() && (*Result.begin())->isCXXClassMember()) + return BuildPossibleImplicitMemberExpr(SS, Result, 0); + bool ADL = UseArgumentDependentLookup(SS, Result, NextToken.is(tok::l_paren)); return BuildDeclarationNameExpr(SS, Result, ADL); } diff --git a/lib/Sema/SemaTemplate.cpp b/lib/Sema/SemaTemplate.cpp index 3123f5fc67..3deeedc90d 100644 --- a/lib/Sema/SemaTemplate.cpp +++ b/lib/Sema/SemaTemplate.cpp @@ -119,7 +119,7 @@ bool Sema::hasAnyAcceptableTemplateNames(LookupResult &R) { if (isAcceptableTemplateName(Context, *I)) return true; - return true; + return false; } TemplateNameKind Sema::isTemplateName(Scope *S, diff --git a/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p5.cpp b/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p5.cpp index c00d4b7023..09245cfd20 100644 --- a/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p5.cpp +++ b/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p5.cpp @@ -43,8 +43,7 @@ void j() { (void)sizeof(auto); // expected-error{{'auto' not allowed here}} (void)__alignof(auto); // expected-error{{'auto' not allowed here}} - // FIXME: don't issue the second diagnostic for this error. - U v; // expected-error{{'auto' not allowed in template argument}} unexpected-error{{C++ requires a type specifier}} + U v; // expected-error{{'auto' not allowed in template argument}} int n; (void)dynamic_cast(S()); // expected-error{{'auto' not allowed here}} diff --git a/test/FixIt/typo.cpp b/test/FixIt/typo.cpp index 440db45518..f8b5352374 100644 --- a/test/FixIt/typo.cpp +++ b/test/FixIt/typo.cpp @@ -65,3 +65,12 @@ struct Derived : public Base { // expected-note{{base class 'Base' specified her int &Derived::getMember() { return ember; // expected-error{{use of undeclared identifier 'ember'; did you mean 'member'?}} } + +typedef int Integer; // expected-note{{'Integer' declared here}} +int global_value; // expected-note{{'global_value' declared here}} + +int foo() { + integer * i = 0; // expected-error{{unknown type name 'integer'; did you mean 'Integer'?}} + unsinged *ptr = 0; // expected-error{{use of undeclared identifier 'unsinged'; did you mean 'unsigned'?}} + return *i + *ptr + global_val; // expected-error{{use of undeclared identifier 'global_val'; did you mean 'global_value'?}} +} diff --git a/test/SemaCXX/PR9459.cpp b/test/SemaCXX/PR9459.cpp index 33cb2e5bc2..dfb242dc59 100644 --- a/test/SemaCXX/PR9459.cpp +++ b/test/SemaCXX/PR9459.cpp @@ -2,8 +2,6 @@ // Don't crash. -templatestruct ae_same; // expected-note {{declared here}} +templatestruct ae_same; templatestruct ts{}ap() -{ts::ap::&ae_same<>>::p(a); }; // expected-error 2 {{undeclared identifier}} \ - // expected-error 2 {{expected}} expected-error {{a space is required}} \ - // expected-error 2 {{global}} expected-error {{too few}} +{ts::ap::&ae_same<>>::p(a); }; // expected-error {{use of undeclared identifier 'a'}} diff --git a/test/SemaTemplate/deduction-crash.cpp b/test/SemaTemplate/deduction-crash.cpp index 8a15605734..ec97311e5d 100644 --- a/test/SemaTemplate/deduction-crash.cpp +++ b/test/SemaTemplate/deduction-crash.cpp @@ -4,7 +4,7 @@ // Note that the error count below doesn't matter. We just want to // make sure that the parser doesn't crash. -// CHECK: 14 errors +// CHECK: 13 errors template struct int_;