From: Richard Smith Date: Mon, 18 Jun 2012 06:11:04 +0000 (+0000) Subject: Extend the error recovery for a template-argument-list terminated by '>>' to X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=19a2702042b7e3ee838cca458b35f607111a3897;p=clang Extend the error recovery for a template-argument-list terminated by '>>' to also deal with '>>>' (in CUDA), '>=', and '>>='. Fix the FixItHints logic to deal with cases where the token is followed by an adjacent '=', '==', '>=', '>>=', or '>>>' token, where a naive fix-it would result in a differing token stream on a re-lex. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@158652 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Basic/DiagnosticParseKinds.td b/include/clang/Basic/DiagnosticParseKinds.td index 9811bab293..3908a37b1c 100644 --- a/include/clang/Basic/DiagnosticParseKinds.td +++ b/include/clang/Basic/DiagnosticParseKinds.td @@ -498,6 +498,9 @@ def err_id_after_template_in_nested_name_spec : Error< "expected template name after 'template' keyword in nested name specifier">; def err_two_right_angle_brackets_need_space : Error< "a space is required between consecutive right angle brackets (use '> >')">; +def err_right_angle_bracket_equal_needs_space : Error< + "a space is required between a right angle bracket and an equals sign " + "(use '> =')">; def warn_cxx0x_right_shift_in_template_arg : Warning< "use of right-shift operator ('>>') in template argument will require " "parentheses in C++11">, InGroup; diff --git a/include/clang/Parse/Parser.h b/include/clang/Parse/Parser.h index 931e0840ca..b977e7f834 100644 --- a/include/clang/Parse/Parser.h +++ b/include/clang/Parse/Parser.h @@ -1215,6 +1215,8 @@ private: // C++ Expressions ExprResult ParseCXXIdExpression(bool isAddressOfOperand = false); + bool areTokensAdjacent(const Token &A, const Token &B); + void CheckForTemplateAndDigraph(Token &Next, ParsedType ObjectTypePtr, bool EnteringContext, IdentifierInfo &II, CXXScopeSpec &SS); diff --git a/lib/Parse/ParseExprCXX.cpp b/lib/Parse/ParseExprCXX.cpp index 65e3f62f57..ef7133c8ae 100644 --- a/lib/Parse/ParseExprCXX.cpp +++ b/lib/Parse/ParseExprCXX.cpp @@ -36,7 +36,7 @@ static int SelectDigraphErrorMessage(tok::TokenKind Kind) { } // Are the two tokens adjacent in the same source file? -static bool AreTokensAdjacent(Preprocessor &PP, Token &First, Token &Second) { +bool Parser::areTokensAdjacent(const Token &First, const Token &Second) { SourceManager &SM = PP.getSourceManager(); SourceLocation FirstLoc = SM.getSpellingLoc(First.getLocation()); SourceLocation FirstEnd = FirstLoc.getLocWithOffset(First.getLength()); @@ -80,7 +80,7 @@ void Parser::CheckForTemplateAndDigraph(Token &Next, ParsedType ObjectType, return; Token SecondToken = GetLookAheadToken(2); - if (!SecondToken.is(tok::colon) || !AreTokensAdjacent(PP, Next, SecondToken)) + if (!SecondToken.is(tok::colon) || !areTokensAdjacent(Next, SecondToken)) return; TemplateTy Template; @@ -921,7 +921,7 @@ ExprResult Parser::ParseCXXCasts() { // diagnose error, suggest fix, and recover parsing. Token Next = NextToken(); if (Tok.is(tok::l_square) && Tok.getLength() == 2 && Next.is(tok::colon) && - AreTokensAdjacent(PP, Tok, Next)) + areTokensAdjacent(Tok, Next)) FixDigraph(*this, PP, Tok, Next, Kind, /*AtDigraph*/true); if (ExpectAndConsume(tok::less, diag::err_expected_less_after, CastName)) diff --git a/lib/Parse/ParseTemplate.cpp b/lib/Parse/ParseTemplate.cpp index 664ba8bc54..46a15c39c6 100644 --- a/lib/Parse/ParseTemplate.cpp +++ b/lib/Parse/ParseTemplate.cpp @@ -316,6 +316,11 @@ bool Parser::ParseTemplateParameters(unsigned Depth, Failed = ParseTemplateParameterList(Depth, TemplateParams); if (Tok.is(tok::greatergreater)) { + // No diagnostic required here: a template-parameter-list can only be + // followed by a declaration or, for a template template parameter, the + // 'class' keyword. Therefore, the second '>' will be diagnosed later. + // This matters for elegant diagnosis of: + // template> struct S; Tok.setKind(tok::greater); RAngleLoc = Tok.getLocation(); Tok.setLocation(Tok.getLocation().getLocWithOffset(1)); @@ -713,34 +718,104 @@ Parser::ParseTemplateIdAfterTemplateName(TemplateTy Template, } } - if (Tok.isNot(tok::greater) && Tok.isNot(tok::greatergreater)) { + // What will be left once we've consumed the '>'. + tok::TokenKind RemainingToken; + const char *ReplacementStr = "> >"; + + switch (Tok.getKind()) { + default: Diag(Tok.getLocation(), diag::err_expected_greater); return true; - } - // Determine the location of the '>' or '>>'. Only consume this - // token if the caller asked us to. - RAngleLoc = Tok.getLocation(); + case tok::greater: + // Determine the location of the '>' token. Only consume this token + // if the caller asked us to. + RAngleLoc = Tok.getLocation(); + if (ConsumeLastToken) + ConsumeToken(); + return false; - if (Tok.is(tok::greatergreater)) { - const char *ReplaceStr = "> >"; - if (NextToken().is(tok::greater) || NextToken().is(tok::greatergreater)) - ReplaceStr = "> > "; + case tok::greatergreater: + RemainingToken = tok::greater; + break; - Diag(Tok.getLocation(), getLangOpts().CPlusPlus0x ? - diag::warn_cxx98_compat_two_right_angle_brackets : - diag::err_two_right_angle_brackets_need_space) - << FixItHint::CreateReplacement(SourceRange(Tok.getLocation()), - ReplaceStr); + case tok::greatergreatergreater: + RemainingToken = tok::greatergreater; + break; - Tok.setKind(tok::greater); - if (!ConsumeLastToken) { - // Since we're not supposed to consume the '>>' token, we need - // to insert a second '>' token after the first. - PP.EnterToken(Tok); - } - } else if (ConsumeLastToken) + case tok::greaterequal: + RemainingToken = tok::equal; + ReplacementStr = "> ="; + break; + + case tok::greatergreaterequal: + RemainingToken = tok::greaterequal; + break; + } + + // This template-id is terminated by a token which starts with a '>'. Outside + // C++11, this is now error recovery, and in C++11, this is error recovery if + // the token isn't '>>'. + + RAngleLoc = Tok.getLocation(); + + // The source range of the '>>' or '>=' at the start of the token. + CharSourceRange ReplacementRange = + CharSourceRange::getCharRange(RAngleLoc, + Lexer::AdvanceToTokenCharacter(RAngleLoc, 2, PP.getSourceManager(), + getLangOpts())); + + // A hint to put a space between the '>>'s. In order to make the hint as + // clear as possible, we include the characters either side of the space in + // the replacement, rather than just inserting a space at SecondCharLoc. + FixItHint Hint1 = FixItHint::CreateReplacement(ReplacementRange, + ReplacementStr); + + // A hint to put another space after the token, if it would otherwise be + // lexed differently. + FixItHint Hint2; + Token Next = NextToken(); + if ((RemainingToken == tok::greater || + RemainingToken == tok::greatergreater) && + (Next.is(tok::greater) || Next.is(tok::greatergreater) || + Next.is(tok::greatergreatergreater) || Next.is(tok::equal) || + Next.is(tok::greaterequal) || Next.is(tok::greatergreaterequal) || + Next.is(tok::equalequal)) && + areTokensAdjacent(Tok, Next)) + Hint2 = FixItHint::CreateInsertion(Next.getLocation(), " "); + + unsigned DiagId = diag::err_two_right_angle_brackets_need_space; + if (getLangOpts().CPlusPlus0x && Tok.is(tok::greatergreater)) + DiagId = diag::warn_cxx98_compat_two_right_angle_brackets; + else if (Tok.is(tok::greaterequal)) + DiagId = diag::err_right_angle_bracket_equal_needs_space; + Diag(Tok.getLocation(), DiagId) << Hint1 << Hint2; + + // Strip the initial '>' from the token. + if (RemainingToken == tok::equal && Next.is(tok::equal) && + areTokensAdjacent(Tok, Next)) { + // Join two adjacent '=' tokens into one, for cases like: + // void (*p)() = f; + // return f==p; ConsumeToken(); + Tok.setKind(tok::equalequal); + Tok.setLength(Tok.getLength() + 1); + } else { + Tok.setKind(RemainingToken); + Tok.setLength(Tok.getLength() - 1); + } + Tok.setLocation(Lexer::AdvanceToTokenCharacter(RAngleLoc, 1, + PP.getSourceManager(), + getLangOpts())); + + if (!ConsumeLastToken) { + // Since we're not supposed to consume the '>' token, we need to push + // this token and revert the current token back to the '>'. + PP.EnterToken(Tok); + Tok.setKind(tok::greater); + Tok.setLength(1); + Tok.setLocation(RAngleLoc); + } return false; } diff --git a/test/FixIt/fixit.cpp b/test/FixIt/fixit.cpp index 6a081e745b..68ede1e43b 100644 --- a/test/FixIt/fixit.cpp +++ b/test/FixIt/fixit.cpp @@ -260,3 +260,34 @@ bool Foo::isGood() { // expected-error {{out-of-line definition of 'isGood' does } void Foo::beEvil() {} // expected-error {{out-of-line definition of 'beEvil' does not match any declaration in namespace 'redecl_typo::Foo'; did you mean 'BeEvil'?}} } + +// Test behavior when a template-id is ended by a token which starts with '>'. +namespace greatergreater { + template struct S { S(); S(T); }; + void f(S=0); // expected-error {{a space is required between a right angle bracket and an equals sign (use '> =')}} + + // FIXME: The fix-its here overlap so -fixit mode can't apply the second one. + //void f(S>=S()); + + struct Shr { + template Shr(T); + template void operator >>=(T); + }; + + template> struct TemplateTemplateParam; // expected-error {{requires 'class'}} + + template void t(); + void g() { + void (*p)() = &t; + (void)(&t==p); // expected-error {{use '> ='}} + (void)(&t>=p); // expected-error {{use '> >'}} + (void)(&t>>=p); // expected-error {{use '> >'}} + (Shr)&t>>>=p; // expected-error {{use '> >'}} + + // FIXME: We correct this to '&t > >= p;' not '&t >>= p;' + //(Shr)&t>>=p; + + // FIXME: The fix-its here overlap. + //(void)(&t>==p); + } +} diff --git a/test/Parser/cuda-kernel-call.cu b/test/Parser/cuda-kernel-call.cu index f95ae9e619..92e46e3aca 100644 --- a/test/Parser/cuda-kernel-call.cu +++ b/test/Parser/cuda-kernel-call.cu @@ -1,9 +1,16 @@ // RUN: %clang_cc1 -fsyntax-only -verify %s +template struct S {}; +template void f(); + void foo(void) { foo<<<1; // expected-error {{expected '>>>'}} expected-note {{to match this '<<<'}} foo<<<1,1>>>; // expected-error {{expected '('}} foo<<<>>>(); // expected-error {{expected expression}} + + S>> s; // expected-error 2{{use '> >'}} + + (void)(&f>>==0); // expected-error 2{{use '> >'}} } diff --git a/test/Parser/cxx-template-argument.cpp b/test/Parser/cxx-template-argument.cpp index c85b1c9281..5479961d56 100644 --- a/test/Parser/cxx-template-argument.cpp +++ b/test/Parser/cxx-template-argument.cpp @@ -10,3 +10,18 @@ A'}} // PR8912 template struct S {}; S 1)> s; + +// Test behavior when a template-id is ended by a token which starts with '>'. +namespace greatergreater { + template struct S { S(); S(T); }; + void f(S=0); // expected-error {{a space is required between a right angle bracket and an equals sign (use '> =')}} + void f(S>=S()); // expected-error {{use '> >'}} expected-error {{use '> ='}} + template void t(); + void g() { + void (*p)() = &t; + (void)(&t==p); // expected-error {{use '> ='}} + (void)(&t>=p); // expected-error {{use '> >'}} + (void)(&t>>=p); // expected-error {{use '> >'}} + (void)(&t>==p); // expected-error {{use '> >'}} expected-error {{use '> ='}} + } +}