r194002 - Try to correct a mistyped "-" or ">" to "->" for some C++ cases.

Kaelyn Uhrain rikka at google.com
Thu Nov 7 14:15:59 PST 2013


Richard,

Thanks for the review feedback!

Here's an alternate version that better handles cases (such as the example
you gave) where the code is actually valid without the "-" or ">" being
changed to "->", by only trying the correction if an error diagnostic would
have been emitted. It is similar to what I had mentioned doing, except that
if the correction fails, ParseRHSOfBinaryExpr is called a second time to
emit the original diagnostic messages. I ended up doing it this way because
I encountered a problem with my previous idea: how to get the token stream
in the right state if the correction fails and the original errors should
be emitted. Preprocessor::EnableBacktrackAtThisPos() and friends work for
resetting the token stream after the call to ParseRHSOfBinaryExpr but don't
quite work for setting the token stream back to the state after the (first)
call to ParseRHSOfBinaryExpr after trying the correction and having it fail
(emitting the stored diagnostics from the call to ParseRHSOfBinaryExpr to
avoid having to call it a second time only works if the token stream can
also be restored to the correct state).

Cheers,
Kaelyn
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-commits/attachments/20131107/3d88d25b/attachment.html>
-------------- next part --------------
diff --git a/include/clang/Basic/Diagnostic.h b/include/clang/Basic/Diagnostic.h
index c057bdf..44cd957 100644
--- a/include/clang/Basic/Diagnostic.h
+++ b/include/clang/Basic/Diagnostic.h
@@ -1091,6 +1091,7 @@ public:
     : DiagObj(DO), StoredDiagMessage(storedDiagMessage) {}
 
   const DiagnosticsEngine *getDiags() const { return DiagObj; }
+  StringRef getStoredDiagMessage() const { return StoredDiagMessage; }
   unsigned getID() const { return DiagObj->CurDiagID; }
   const SourceLocation &getLocation() const { return DiagObj->CurDiagLoc; }
   bool hasSourceManager() const { return DiagObj->hasSourceManager(); }
diff --git a/include/clang/Basic/DiagnosticParseKinds.td b/include/clang/Basic/DiagnosticParseKinds.td
index 1df1713..f79a04c 100644
--- a/include/clang/Basic/DiagnosticParseKinds.td
+++ b/include/clang/Basic/DiagnosticParseKinds.td
@@ -499,6 +499,9 @@ def ext_abstract_pack_declarator_parens : ExtWarn<
 def err_function_is_not_record : Error<
   "unexpected '%select{.|->}0' in function call; perhaps remove the "
   "'%select{.|->}0'?">;
+def err_mistyped_arrow_in_member_access : Error<
+  "use of undeclared identifier %0; did you mean '->' instead of "
+  "'%select{-|>}1'?">;
 
 // C++ derived classes
 def err_dup_virtual : Error<"duplicate 'virtual' in base specifier">;
diff --git a/lib/Parse/ParseExpr.cpp b/lib/Parse/ParseExpr.cpp
index 65b9b82..99250d0 100644
--- a/lib/Parse/ParseExpr.cpp
+++ b/lib/Parse/ParseExpr.cpp
@@ -152,6 +152,52 @@ Parser::ParseExpressionWithLeadingExtension(SourceLocation ExtLoc) {
   return ParseRHSOfBinaryExpression(LHS, prec::Comma);
 }
 
+namespace {
+class DelayingDiagnosticConsumer : public DiagnosticConsumer {
+public:
+  DelayingDiagnosticConsumer() : HaveError(false) {}
+
+  virtual void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
+                                const Diagnostic &Info);
+
+  bool hasDiagnostics() const { return !Diags.empty(); }
+  bool hasError() const { return HaveError; }
+
+  void FlushDiagnostics(DiagnosticConsumer *handler);
+
+  void Reset() {
+    Diags.clear();
+    DiagEngines.clear();
+    HaveError = false;
+  }
+
+private:
+  typedef SmallVector<std::pair<DiagnosticsEngine::Level,
+                                const Diagnostic>, 4> DiagnosticList;
+  DiagnosticList Diags;
+  SmallVector<DiagnosticsEngine, 4> DiagEngines;
+  bool HaveError;
+};
+
+void
+DelayingDiagnosticConsumer::HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
+                                            const Diagnostic &Info) {
+  DiagEngines.push_back(*(Info.getDiags()));
+  Diags.push_back(std::make_pair(
+      DiagLevel, Diagnostic(&DiagEngines.back(), Info.getStoredDiagMessage())));
+  if (DiagLevel >= DiagnosticsEngine::Error)
+    HaveError = true;
+}
+
+void DelayingDiagnosticConsumer::FlushDiagnostics(DiagnosticConsumer *handler) {
+  for (DiagnosticList::iterator DI = Diags.begin(), DIEnd = Diags.end();
+       DI != DIEnd; ++DI) {
+    handler->HandleDiagnostic(DI->first, DI->second);
+  }
+  Reset();
+}
+}
+
 /// \brief Parse an expr that doesn't include (top-level) commas.
 ExprResult Parser::ParseAssignmentExpression(TypeCastState isTypeCast) {
   if (Tok.is(tok::code_completion)) {
@@ -166,6 +212,54 @@ ExprResult Parser::ParseAssignmentExpression(TypeCastState isTypeCast) {
   ExprResult LHS = ParseCastExpression(/*isUnaryExpression=*/false,
                                        /*isAddressOfOperand=*/false,
                                        isTypeCast);
+
+  // Check for a possible typo of "-" or ">" instead of "->" after a
+  // pointer to a struct or class, while recovery is still possible.
+  if (LHS.isUsable() && (Tok.is(tok::minus) || Tok.is(tok::greater))) {
+    QualType LHSType = LHS.get()->getType();
+    const RecordType *Pointee =
+        LHSType->isPointerType()
+            ? LHSType->getPointeeType()->getAsStructureType()
+            : 0;
+    const RecordDecl *RD = Pointee ? Pointee->getDecl() : 0;
+    const Token &NextTok = NextToken();
+    if (RD && NextTok.is(tok::identifier)) {
+      Token OpTok = Tok;
+      DelayingDiagnosticConsumer DDC;
+      bool OwnedPrimary = Diags.ownsClient();
+      DiagnosticConsumer *PrimaryConsumer = Diags.takeClient();
+      Diags.setClient(&DDC, false);
+      PP.EnableBacktrackAtThisPos();
+
+      ExprResult Res = ParseRHSOfBinaryExpression(LHS, prec::Assignment);
+
+      // If no diagnostics were generated, then nothing more needs to be done.
+      if (!DDC.hasError()) {
+        DDC.FlushDiagnostics(PrimaryConsumer);
+        Diags.setClient(PrimaryConsumer, OwnedPrimary);
+        PP.CommitBacktrackedTokens();
+        return Res;
+      }
+      DDC.Reset();
+      PP.Backtrack();
+
+      Tok.setKind(tok::arrow);
+      PP.EnableBacktrackAtThisPos();
+      Res = ParsePostfixExpressionSuffix(LHS);
+      Diags.setClient(PrimaryConsumer, OwnedPrimary);
+      if (!DDC.hasDiagnostics()) {
+        LHS = Res;
+        PP.CommitBacktrackedTokens();
+        Diag(OpTok, diag::err_mistyped_arrow_in_member_access)
+            << NextTok.getIdentifierInfo() << OpTok.is(tok::greater)
+            << FixItHint::CreateReplacement(OpTok.getLocation(), "->");
+      } else {
+        Tok = OpTok;
+        PP.Backtrack();
+      }
+    }
+  }
+
   return ParseRHSOfBinaryExpression(LHS, prec::Assignment);
 }
 
diff --git a/test/SemaCXX/member-expr.cpp b/test/SemaCXX/member-expr.cpp
index 239aecf..4dcea9d 100644
--- a/test/SemaCXX/member-expr.cpp
+++ b/test/SemaCXX/member-expr.cpp
@@ -224,3 +224,27 @@ namespace pr16676 {
         .i;  // expected-error {{member reference type 'pr16676::S *' is a pointer; maybe you meant to use '->'}}
   }
 }
+
+namespace PR9054 {
+struct Foo {
+  void bar(int);
+  int fiz;
+};
+
+int test(struct Foo *foo) {
+  foo-bar(5);  // expected-error {{use of undeclared identifier 'bar'; did you mean '->' instead of '-'?}}
+  foo>baz(4);  // expected-error-re {{use of undeclared identifier 'baz'$}}
+  return foo>fiz;  // expected-error {{use of undeclared identifier 'fiz'; did you mean '->' instead of '>'?}}
+}
+
+struct S {
+  int f(...);
+};
+namespace X {
+  struct T {};
+  int f(T);
+}
+S *g(S *p) {
+  return p - f(X::T());  // no-error
+}
+}


More information about the cfe-commits mailing list