dgoldman updated this revision to Diff 202772.
dgoldman added a comment.

- Fix diagnostics for ignored TypoExprs


Repository:
  rC Clang

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D62648/new/

https://reviews.llvm.org/D62648

Files:
  lib/Sema/SemaExprCXX.cpp
  test/Sema/typo-correction-recursive.cpp

Index: test/Sema/typo-correction-recursive.cpp
===================================================================
--- /dev/null
+++ test/Sema/typo-correction-recursive.cpp
@@ -0,0 +1,65 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s
+
+// Check the following typo correction behavior:
+// - multiple typos in a single member call chain are all diagnosed
+// - no typos are diagnosed for multiple typos in an expression when not all
+//   typos can be corrected
+
+class DeepClass
+{
+public:
+  void trigger() const;  // expected-note {{'trigger' declared here}}
+};
+
+class Y
+{
+public:
+  const DeepClass& getX() const { return m_deepInstance; } // expected-note {{'getX' declared here}}
+  int getN() const { return m_n; }
+private:
+  DeepClass m_deepInstance;
+  int m_n;
+};
+
+class Z
+{
+public:
+  const Y& getY0() const { return m_y0; } // expected-note {{'getY0' declared here}}
+  const Y& getY1() const { return m_y1; }
+  const Y& getActiveY() const { return m_y0; }
+
+private:
+  Y m_y0;
+  Y m_y1;
+};
+
+Z z_obj;
+
+void testMultipleCorrections()
+{
+  z_obj.getY2(). // expected-error {{no member named 'getY2' in 'Z'; did you mean 'getY0'}}
+    getM().      // expected-error {{no member named 'getM' in 'Y'; did you mean 'getX'}}
+    triggee();   // expected-error {{no member named 'triggee' in 'DeepClass'; did you mean 'trigger'}}
+}
+
+void testNoCorrections()
+{
+  z_obj.getY2(). // expected-error {{no member named 'getY2' in 'Z'}}
+    getM().
+    thisDoesntSeemToMakeSense();
+}
+
+struct C {};
+struct D { int value; };
+struct A {
+  C get_me_a_C();
+};
+struct B {
+  D get_me_a_D();
+};
+A make_an_A();
+B make_an_B();
+int testDiscardedCorrections() {
+  return make_an_E().  // expected-error {{use of undeclared identifier 'make_an_E'}}
+    get_me_a_Z().value;
+}
Index: lib/Sema/SemaExprCXX.cpp
===================================================================
--- lib/Sema/SemaExprCXX.cpp
+++ lib/Sema/SemaExprCXX.cpp
@@ -7680,6 +7680,60 @@
     return ExprFilter(Res.get());
   }
 
+  // Try to transform the given expression, looping through the correction
+  // candidates with `CheckAndAdvanceTypoExprCorrectionStreams`.
+  //
+  // Since correcting typos may intoduce new TypoExprs, this function
+  // checks for new TypoExprs and recurses if it finds any. Note that it will
+  // only succeed if it is able to correct all typos in the given expression.
+  ExprResult RecursiveTransformLoop(Expr *E) {
+    ExprResult Res;
+    while (true) {
+      Res = TryTransform(E);
+
+      // The transform was valid: check if there any new TypoExprs were created.
+      // If so, we need to recurse to check their validity.
+      if (!Res.isInvalid() && !TypoExprs.empty()) {
+        Expr *FixedExpr = Res.get();
+        auto SavedTypoExprs = TypoExprs;
+        llvm::SmallSetVector<TypoExpr *, 2> RecursiveTypoExprs;
+        TypoExprs = RecursiveTypoExprs;
+        FindTypoExprs(TypoExprs).TraverseStmt(FixedExpr);
+
+        // Recurse to handle newly created TypoExprs. If we're not able to
+        // handle them, discard these TypoExprs.
+        ExprResult RecurResult = RecursiveTransformLoop(FixedExpr);
+        if (RecurResult.isInvalid()) {
+          Res = ExprError();
+          // Recursive corrections didn't work, wipe them away and don't add
+          // them to the TypoExprs set.
+          for (auto TE : TypoExprs) {
+            auto &State = SemaRef.getTypoExprState(TE);
+            TransformCache.erase(TE);
+            SemaRef.clearDelayedTypo(TE);
+          }
+        } else {
+          // TypoExpr is valid: add newly created TypoExprs since we were
+          // able to correct them.
+          Res = RecurResult;
+          SavedTypoExprs.set_union(TypoExprs);
+        }
+
+        TypoExprs = SavedTypoExprs;
+      }
+      // If the transform is still valid after checking for any new typos,
+      // it's good to go.
+      if (!Res.isInvalid())
+        break;
+
+      // The transform was invalid, see if we have any TypoExprs with untried
+      // correction candidates.
+      if (!CheckAndAdvanceTypoExprCorrectionStreams())
+        break;
+    }
+    return Res;
+  }
+
 public:
   TransformTypos(Sema &SemaRef, VarDecl *InitDecl, llvm::function_ref<ExprResult(Expr *)> Filter)
       : BaseTransform(SemaRef), InitDecl(InitDecl), ExprFilter(Filter) {}
@@ -7707,16 +7761,7 @@
   ExprResult TransformBlockExpr(BlockExpr *E) { return Owned(E); }
 
   ExprResult Transform(Expr *E) {
-    ExprResult Res;
-    while (true) {
-      Res = TryTransform(E);
-
-      // Exit if either the transform was valid or if there were no TypoExprs
-      // to transform that still have any untried correction candidates..
-      if (!Res.isInvalid() ||
-          !CheckAndAdvanceTypoExprCorrectionStreams())
-        break;
-    }
+    ExprResult Res = RecursiveTransformLoop(E);
 
     // Ensure none of the TypoExprs have multiple typo correction candidates
     // with the same edit length that pass all the checks and filters.
@@ -7745,7 +7790,8 @@
     }
     SemaRef.DisableTypoCorrection = false;
 
-    // Ensure that all of the TypoExprs within the current Expr have been found.
+    // Ensure that all of the TypoExprs within the current invalid Expr have
+    // been found.
     if (!Res.isUsable())
       FindTypoExprs(TypoExprs).TraverseStmt(E);
 
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to