diff options
author | Richard Smith <richard-llvm@metafoo.co.uk> | 2011-04-14 21:45:45 +0000 |
---|---|---|
committer | Richard Smith <richard-llvm@metafoo.co.uk> | 2011-04-14 21:45:45 +0000 |
commit | ea698b3f6cad84f7f583282dce3e03e24fe80e98 (patch) | |
tree | 851f460d8d231911fafd2df0fac4422a57fcc22d | |
parent | 06d9b1ad0bca7230cbae57e3e3207dda77a9eac0 (diff) |
Detect when the string "<::" is found in code after a cast or template name and is interpreted as "[:" because of the digraph "<:". When found, give an error with a fix-it to add whitespace between the "<" and "::".
Patch by Richard Trieu! Plus a small tweak from me to deal with one of the tokens coming from a macro.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@129540 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r-- | include/clang/Basic/DiagnosticParseKinds.td | 4 | ||||
-rw-r--r-- | lib/Parse/ParseExprCXX.cpp | 79 | ||||
-rw-r--r-- | test/Parser/cxx-casting.cpp | 34 | ||||
-rw-r--r-- | test/SemaTemplate/temp_arg_template.cpp | 9 |
4 files changed, 121 insertions, 5 deletions
diff --git a/include/clang/Basic/DiagnosticParseKinds.td b/include/clang/Basic/DiagnosticParseKinds.td index 273d6cc088..7baceb50bd 100644 --- a/include/clang/Basic/DiagnosticParseKinds.td +++ b/include/clang/Basic/DiagnosticParseKinds.td @@ -413,6 +413,10 @@ def err_ctor_init_missing_comma : Error< // C++ declarations def err_friend_decl_defines_class : Error< "cannot define a type in a friend declaration">; +def err_missing_whitespace_digraph : Error< + "found '<::' after a " + "%select{template name|const_cast|dynamic_cast|reinterpret_cast|static_cast}0" + " which forms the digraph '<:' (aka '[') and a ':', did you mean '< ::'?">; def warn_deleted_function_accepted_as_extension: ExtWarn< "deleted function definition accepted as a C++0x extension">, InGroup<CXX0x>; diff --git a/lib/Parse/ParseExprCXX.cpp b/lib/Parse/ParseExprCXX.cpp index 385185eb3a..c8f674175b 100644 --- a/lib/Parse/ParseExprCXX.cpp +++ b/lib/Parse/ParseExprCXX.cpp @@ -20,6 +20,55 @@ using namespace clang; +static int SelectDigraphErrorMessage(tok::TokenKind Kind) { + switch (Kind) { + case tok::kw_template: return 0; + case tok::kw_const_cast: return 1; + case tok::kw_dynamic_cast: return 2; + case tok::kw_reinterpret_cast: return 3; + case tok::kw_static_cast: return 4; + default: + assert(0 && "Unknown type for digraph error message."); + return -1; + } +} + +// Are the two tokens adjacent in the same source file? +static bool AreTokensAdjacent(Preprocessor &PP, Token &First, Token &Second) { + SourceManager &SM = PP.getSourceManager(); + SourceLocation FirstLoc = SM.getSpellingLoc(First.getLocation()); + SourceLocation FirstEnd = FirstLoc.getFileLocWithOffset(First.getLength()); + return FirstEnd == SM.getSpellingLoc(Second.getLocation()); +} + +// Suggest fixit for "<::" after a cast. +static void FixDigraph(Parser &P, Preprocessor &PP, Token &DigraphToken, + Token &ColonToken, tok::TokenKind Kind, bool AtDigraph) { + // Pull '<:' and ':' off token stream. + if (!AtDigraph) + PP.Lex(DigraphToken); + PP.Lex(ColonToken); + + SourceRange Range; + Range.setBegin(DigraphToken.getLocation()); + Range.setEnd(ColonToken.getLocation()); + P.Diag(DigraphToken.getLocation(), diag::err_missing_whitespace_digraph) + << SelectDigraphErrorMessage(Kind) + << FixItHint::CreateReplacement(Range, "< ::"); + + // Update token information to reflect their change in token type. + ColonToken.setKind(tok::coloncolon); + ColonToken.setLocation(ColonToken.getLocation().getFileLocWithOffset(-1)); + ColonToken.setLength(2); + DigraphToken.setKind(tok::less); + DigraphToken.setLength(1); + + // Push new tokens back to token stream. + PP.EnterToken(ColonToken); + if (!AtDigraph) + PP.EnterToken(DigraphToken); +} + /// \brief Parse global scope or nested-name-specifier if present. /// /// Parses a C++ global scope specifier ('::') or nested-name-specifier (which @@ -287,6 +336,29 @@ bool Parser::ParseOptionalCXXScopeSpecifier(CXXScopeSpec &SS, continue; } + // Check for '<::' which should be '< ::' instead of '[:' when following + // a template name. + if (Next.is(tok::l_square) && Next.getLength() == 2) { + Token SecondToken = GetLookAheadToken(2); + if (SecondToken.is(tok::colon) && + AreTokensAdjacent(PP, Next, SecondToken)) { + TemplateTy Template; + UnqualifiedId TemplateName; + TemplateName.setIdentifier(&II, Tok.getLocation()); + bool MemberOfUnknownSpecialization; + if (Actions.isTemplateName(getCurScope(), SS, + /*hasTemplateKeyword=*/false, + TemplateName, + ObjectType, + EnteringContext, + Template, + MemberOfUnknownSpecialization)) { + FixDigraph(*this, PP, Next, SecondToken, tok::kw_template, + /*AtDigraph*/false); + } + } + } + // nested-name-specifier: // type-name '<' if (Next.is(tok::less)) { @@ -453,6 +525,13 @@ ExprResult Parser::ParseCXXCasts() { SourceLocation OpLoc = ConsumeToken(); SourceLocation LAngleBracketLoc = Tok.getLocation(); + // Check for "<::" which is parsed as "[:". If found, fix token stream, + // 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)) + FixDigraph(*this, PP, Tok, Next, Kind, /*AtDigraph*/true); + if (ExpectAndConsume(tok::less, diag::err_expected_less_after, CastName)) return ExprError(); diff --git a/test/Parser/cxx-casting.cpp b/test/Parser/cxx-casting.cpp index 98d962ad09..4a0bb4d1e4 100644 --- a/test/Parser/cxx-casting.cpp +++ b/test/Parser/cxx-casting.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -fsyntax-only %s +// RUN: %clang_cc1 -fsyntax-only -verify %s char *const_cast_test(const char *var) { @@ -34,6 +34,36 @@ char postfix_expr_test() // This was being incorrectly tentatively parsed. namespace test1 { - template <class T> class A {}; + template <class T> class A {}; // expected-note 2{{here}} void foo() { A<int>(*(A<int>*)0); } } + +typedef char* c; +typedef A* a; +void test2(char x, struct B * b) { + (void)const_cast<::c>(&x); // expected-error{{found '<::' after a const_cast which forms the digraph '<:' (aka '[') and a ':', did you mean '< ::'?}} + (void)dynamic_cast<::a>(b); // expected-error{{found '<::' after a dynamic_cast which forms the digraph '<:' (aka '[') and a ':', did you mean '< ::'?}} + (void)reinterpret_cast<::c>(x); // expected-error{{found '<::' after a reinterpret_cast which forms the digraph '<:' (aka '[') and a ':', did you mean '< ::'?}} + (void)static_cast<::c>(&x); // expected-error{{found '<::' after a static_cast which forms the digraph '<:' (aka '[') and a ':', did you mean '< ::'?}} + + // Do not do digraph correction. + (void)static_cast<: :c>(&x); //\ + expected-error {{expected '<' after 'static_cast'}} \ + expected-error {{expected expression}}\ + expected-error {{expected ']'}}\ + expected-note {{to match this '['}} + (void)static_cast<: // expected-error {{expected '<' after 'static_cast'}} \ + expected-note {{to match this '['}} + :c>(&x); // expected-error {{expected expression}} \ + expected-error {{expected ']'}} +#define LC <: +#define C : + test1::A LC:B> c; // expected-error {{cannot refer to class template 'A' without a template argument list}} expected-error 2{{}} expected-note{{}} + (void)static_cast LC:c>(&x); // expected-error {{expected '<' after 'static_cast'}} expected-error 2{{}} expected-note{{}} + test1::A<:C B> d; // expected-error {{cannot refer to class template 'A' without a template argument list}} expected-error 2{{}} expected-note{{}} + (void)static_cast<:C c>(&x); // expected-error {{expected '<' after 'static_cast'}} expected-error 2{{}} expected-note{{}} + +#define LCC <:: + test1::A LCC B> e; // expected-error{{found '<::' after a template name which forms the digraph '<:' (aka '[') and a ':', did you mean '< ::'?}} + (void)static_cast LCC c>(&x); // expected-error{{found '<::' after a static_cast which forms the digraph '<:' (aka '[') and a ':', did you mean '< ::'?}} +} diff --git a/test/SemaTemplate/temp_arg_template.cpp b/test/SemaTemplate/temp_arg_template.cpp index 944acacd84..9c34089e61 100644 --- a/test/SemaTemplate/temp_arg_template.cpp +++ b/test/SemaTemplate/temp_arg_template.cpp @@ -30,9 +30,12 @@ template<typename T> void f(int); A<f> *a9; // expected-error{{must be a class template}} -// FIXME: The code below is ill-formed, because of the evil digraph '<:'. -// We should provide a much better error message than we currently do. -// A<::N::Z> *a10; +// Evil digraph '<:' is parsed as '[', expect error. +A<::N::Z> *a10; // expected-error{{found '<::' after a template name which forms the digraph '<:' (aka '[') and a ':', did you mean '< ::'?}} + +// Do not do a digraph correction here. +A<: :N::Z> *a11; // expected-error{{expected expression}} \ + expected-error{{C++ requires a type specifier for all declarations}} // PR7807 namespace N { |