[cfe-commits] r129540 - in /cfe/trunk: include/clang/Basic/DiagnosticParseKinds.td lib/Parse/ParseExprCXX.cpp test/Parser/cxx-casting.cpp test/SemaTemplate/temp_arg_template.cpp

Richard Smith richard-llvm at metafoo.co.uk
Thu Apr 14 14:45:45 PDT 2011


Author: rsmith
Date: Thu Apr 14 16:45:45 2011
New Revision: 129540

URL: http://llvm.org/viewvc/llvm-project?rev=129540&view=rev
Log:
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.


Modified:
    cfe/trunk/include/clang/Basic/DiagnosticParseKinds.td
    cfe/trunk/lib/Parse/ParseExprCXX.cpp
    cfe/trunk/test/Parser/cxx-casting.cpp
    cfe/trunk/test/SemaTemplate/temp_arg_template.cpp

Modified: cfe/trunk/include/clang/Basic/DiagnosticParseKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticParseKinds.td?rev=129540&r1=129539&r2=129540&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticParseKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticParseKinds.td Thu Apr 14 16:45:45 2011
@@ -413,6 +413,10 @@
 // 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>;

Modified: cfe/trunk/lib/Parse/ParseExprCXX.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/ParseExprCXX.cpp?rev=129540&r1=129539&r2=129540&view=diff
==============================================================================
--- cfe/trunk/lib/Parse/ParseExprCXX.cpp (original)
+++ cfe/trunk/lib/Parse/ParseExprCXX.cpp Thu Apr 14 16:45:45 2011
@@ -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 @@
       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 @@
   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();
 

Modified: cfe/trunk/test/Parser/cxx-casting.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Parser/cxx-casting.cpp?rev=129540&r1=129539&r2=129540&view=diff
==============================================================================
--- cfe/trunk/test/Parser/cxx-casting.cpp (original)
+++ cfe/trunk/test/Parser/cxx-casting.cpp Thu Apr 14 16:45:45 2011
@@ -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 @@
 
 // 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 '< ::'?}}
+}

Modified: cfe/trunk/test/SemaTemplate/temp_arg_template.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaTemplate/temp_arg_template.cpp?rev=129540&r1=129539&r2=129540&view=diff
==============================================================================
--- cfe/trunk/test/SemaTemplate/temp_arg_template.cpp (original)
+++ cfe/trunk/test/SemaTemplate/temp_arg_template.cpp Thu Apr 14 16:45:45 2011
@@ -30,9 +30,12 @@
 
 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 {





More information about the cfe-commits mailing list