Index: lib/Sema/SemaExprCXX.cpp =================================================================== --- lib/Sema/SemaExprCXX.cpp +++ lib/Sema/SemaExprCXX.cpp @@ -7362,6 +7362,30 @@ break; } + // The transform is able to produce new TypoExprs while resolving the typos. + // These new TypoExprs are not resolved by the transform, they do not get + // into the TypoExprs container and are not reported, so they need to be + // handled separately. + // If the transform result is valid and contains newly created TypoExprs, + // transform the result expression again until no new TypoExprs get created + // or the result becomes an invalid expression. Return the longest valid + // expression to report as many typos as possible. + if (!Res.isInvalid()) { + while (true) { + unsigned TyposCount = TypoExprs.size(); + FindTypoExprs(TypoExprs).TraverseStmt(Res.get()); + if (TypoExprs.size() == TyposCount) + // No new TypoExprs created by the transform + break; + ExprResult TmpRes = TryTransform(Res.get()); + if (TmpRes.isInvalid()) + // Further transform prodices an invalid Expr. + // Stop with the last valid result. + break; + Res = TmpRes; + } + } + // Ensure none of the TypoExprs have multiple typo correction candidates // with the same edit length that pass all the checks and filters. // TODO: Properly handle various permutations of possible corrections when Index: test/Sema/typo-correction-multiple-typos.cpp =================================================================== --- test/Sema/typo-correction-multiple-typos.cpp +++ test/Sema/typo-correction-multiple-typos.cpp @@ -0,0 +1,41 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s + +// This file contains typo correction test which ensures that +// multiple typos in a single member calls chain are correctly +// diagnosed. + +class X +{ +public: + void foo() const; // expected-note {{'foo' declared here}} +}; + +class Y +{ +public: + const X& getX() const { return m_x; } // expected-note {{'getX' declared here}} + int getN() const { return m_n; } +private: + X m_x; + 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; } + +private: + Y m_y0; + Y m_y1; +}; + +Z z_obj; + +void test() +{ + 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'}} + foe(); // expected-error {{no member named 'foe' in 'X'; did you mean 'foo'}} +}