Index: clang/include/clang/Sema/Sema.h =================================================================== --- clang/include/clang/Sema/Sema.h +++ clang/include/clang/Sema/Sema.h @@ -7561,6 +7561,22 @@ } }; + /// RAII class used to disable typo correction temporarily. + class DisableTypoCorrectionScope { + Sema &SemaRef; + bool PrevDisableTypoCorrection; + + public: + explicit DisableTypoCorrectionScope(Sema &SemaRef) + : SemaRef(SemaRef), + PrevDisableTypoCorrection(SemaRef.DisableTypoCorrection) { + SemaRef.DisableTypoCorrection = true; + } + ~DisableTypoCorrectionScope() { + SemaRef.DisableTypoCorrection = PrevDisableTypoCorrection; + } + }; + /// The current instantiation scope used to store local /// variables. LocalInstantiationScope *CurrentInstantiationScope; Index: clang/lib/Sema/SemaExprCXX.cpp =================================================================== --- clang/lib/Sema/SemaExprCXX.cpp +++ clang/lib/Sema/SemaExprCXX.cpp @@ -7588,7 +7588,7 @@ // handling potentially ambiguous typo corrections as any new TypoExprs will // have been introduced by the application of one of the correction // candidates and add little to no value if corrected. - SemaRef.DisableTypoCorrection = true; + Sema::DisableTypoCorrectionScope Scope(SemaRef); while (!AmbiguousTypoExprs.empty()) { auto TE = AmbiguousTypoExprs.back(); auto Cached = TransformCache[TE]; @@ -7605,7 +7605,6 @@ State.Consumer->restoreSavedPosition(); TransformCache[TE] = Cached; } - SemaRef.DisableTypoCorrection = false; // Ensure that all of the TypoExprs within the current Expr have been found. if (!Res.isUsable()) @@ -7668,11 +7667,14 @@ if (E && !ExprEvalContexts.empty() && ExprEvalContexts.back().NumTypos && (E->isTypeDependent() || E->isValueDependent() || E->isInstantiationDependent())) { + DisableTypoCorrectionScope DisableRecurrentCorrectionScope(*this); auto TyposInContext = ExprEvalContexts.back().NumTypos; assert(TyposInContext < ~0U && "Recursive call of CorrectDelayedTyposInExpr"); ExprEvalContexts.back().NumTypos = ~0U; auto TyposResolved = DelayedTypos.size(); auto Result = TransformTypos(*this, InitDecl, Filter).Transform(E); + assert(ExprEvalContexts.back().NumTypos == ~0U && + "Unexpected NumTypos modification"); ExprEvalContexts.back().NumTypos = TyposInContext; TyposResolved -= DelayedTypos.size(); if (Result.isInvalid() || Result.get() != E) { Index: clang/test/Sema/typo-correction.c =================================================================== --- clang/test/Sema/typo-correction.c +++ clang/test/Sema/typo-correction.c @@ -87,3 +87,16 @@ void overloadable_callexpr(int arg) { func_overloadable(ar); //expected-error{{use of undeclared identifier}} } + +// rdar://problem/38642201 +struct rdar38642201 { + int fieldName; +}; + +void rdar38642201_callee(int x, int y); +void rdar38642201_caller() { + struct rdar38642201 structVar; + rdar38642201_callee( + structVarTypo.fieldNameTypo, //expected-error{{use of undeclared identifier 'structVarTypo'}} + structVarTypo2.fieldNameTypo2); //expected-error{{use of undeclared identifier 'structVarTypo2'}} +} Index: clang/test/SemaCXX/typo-correction-delayed.cpp =================================================================== --- clang/test/SemaCXX/typo-correction-delayed.cpp +++ clang/test/SemaCXX/typo-correction-delayed.cpp @@ -137,13 +137,12 @@ namespace PR21925 { struct X { - int get() { return 7; } // expected-note {{'get' declared here}} + int get() { return 7; } }; void test() { - X variable; // expected-note {{'variable' declared here}} + X variable; - // expected-error@+2 {{use of undeclared identifier 'variableX'; did you mean 'variable'?}} - // expected-error@+1 {{no member named 'getX' in 'PR21925::X'; did you mean 'get'?}} + // expected-error@+1 {{use of undeclared identifier 'variableX'}} int x = variableX.getX(); } }