diff options
Diffstat (limited to 'clang/lib/Sema/SemaExprCXX.cpp')
-rw-r--r-- | clang/lib/Sema/SemaExprCXX.cpp | 194 |
1 files changed, 152 insertions, 42 deletions
diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp index 7c68e4d9145..7ea3c8f8341 100644 --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -7580,15 +7580,22 @@ class TransformTypos : public TreeTransform<TransformTypos> { llvm::SmallDenseMap<OverloadExpr *, Expr *, 4> OverloadResolution; /// Emit diagnostics for all of the TypoExprs encountered. + /// /// If the TypoExprs were successfully corrected, then the diagnostics should /// suggest the corrections. Otherwise the diagnostics will not suggest /// anything (having been passed an empty TypoCorrection). - void EmitAllDiagnostics() { + /// + /// If we've failed to correct due to ambiguous corrections, we need to + /// be sure to pass empty corrections and replacements. Otherwise it's + /// possible that the Consumer has a TypoCorrection that failed to ambiguity + /// and we don't want to report those diagnostics. + void EmitAllDiagnostics(bool IsAmbiguous) { for (TypoExpr *TE : TypoExprs) { auto &State = SemaRef.getTypoExprState(TE); if (State.DiagHandler) { - TypoCorrection TC = State.Consumer->getCurrentCorrection(); - ExprResult Replacement = TransformCache[TE]; + TypoCorrection TC = IsAmbiguous + ? TypoCorrection() : State.Consumer->getCurrentCorrection(); + ExprResult Replacement = IsAmbiguous ? ExprError() : TransformCache[TE]; // Extract the NamedDecl from the transformed TypoExpr and add it to the // TypoCorrection, replacing the existing decls. This ensures the right @@ -7650,6 +7657,145 @@ class TransformTypos : public TreeTransform<TransformTypos> { return ExprFilter(Res.get()); } + // 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 CheckForRecursiveTypos(ExprResult Res, bool &IsAmbiguous) { + if (Res.isInvalid()) { + return Res; + } + // Check to see if any new TypoExprs were created. If so, we need to recurse + // to check their validity. + Expr *FixedExpr = Res.get(); + + auto SavedTypoExprs = std::move(TypoExprs); + auto SavedAmbiguousTypoExprs = std::move(AmbiguousTypoExprs); + TypoExprs.clear(); + AmbiguousTypoExprs.clear(); + + FindTypoExprs(TypoExprs).TraverseStmt(FixedExpr); + if (!TypoExprs.empty()) { + // Recurse to handle newly created TypoExprs. If we're not able to + // handle them, discard these TypoExprs. + ExprResult RecurResult = + RecursiveTransformLoop(FixedExpr, IsAmbiguous); + if (RecurResult.isInvalid()) { + Res = ExprError(); + // Recursive corrections didn't work, wipe them away and don't add + // them to the TypoExprs set. Remove them from Sema's TypoExpr list + // since we don't want to clear them twice. Note: it's possible the + // TypoExprs were created recursively and thus won't be in our + // Sema's TypoExprs - they were created in our `RecursiveTransformLoop`. + auto &SemaTypoExprs = SemaRef.TypoExprs; + for (auto TE : TypoExprs) { + TransformCache.erase(TE); + SemaRef.clearDelayedTypo(TE); + + auto SI = find(SemaTypoExprs, TE); + if (SI != SemaTypoExprs.end()) { + SemaTypoExprs.erase(SI); + } + } + } else { + // TypoExpr is valid: add newly created TypoExprs since we were + // able to correct them. + Res = RecurResult; + SavedTypoExprs.set_union(TypoExprs); + } + } + + TypoExprs = std::move(SavedTypoExprs); + AmbiguousTypoExprs = std::move(SavedAmbiguousTypoExprs); + + return Res; + } + + // Try to transform the given expression, looping through the correction + // candidates with `CheckAndAdvanceTypoExprCorrectionStreams`. + // + // If valid ambiguous typo corrections are seen, `IsAmbiguous` is set to + // true and this method immediately will return an `ExprError`. + ExprResult RecursiveTransformLoop(Expr *E, bool &IsAmbiguous) { + ExprResult Res; + auto SavedTypoExprs = std::move(SemaRef.TypoExprs); + SemaRef.TypoExprs.clear(); + + while (true) { + Res = CheckForRecursiveTypos(TryTransform(E), IsAmbiguous); + + // Recursion encountered an ambiguous correction. This means that our + // correction itself is ambiguous, so stop now. + if (IsAmbiguous) + break; + + // 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; + } + + // If we found a valid result, double check to make sure it's not ambiguous. + if (!IsAmbiguous && !Res.isInvalid() && !AmbiguousTypoExprs.empty()) { + auto SavedTransformCache = std::move(TransformCache); + TransformCache.clear(); + // Ensure none of the TypoExprs have multiple typo correction candidates + // with the same edit length that pass all the checks and filters. + while (!AmbiguousTypoExprs.empty()) { + auto TE = AmbiguousTypoExprs.back(); + + // TryTransform itself can create new Typos, adding them to the TypoExpr map + // and invalidating our TypoExprState, so always fetch it instead of storing. + SemaRef.getTypoExprState(TE).Consumer->saveCurrentPosition(); + + TypoCorrection TC = SemaRef.getTypoExprState(TE).Consumer->peekNextCorrection(); + TypoCorrection Next; + do { + ExprResult AmbigRes = CheckForRecursiveTypos(TryTransform(E), IsAmbiguous); + + if (!AmbigRes.isInvalid() || IsAmbiguous) { + SemaRef.getTypoExprState(TE).Consumer->resetCorrectionStream(); + SavedTransformCache.erase(TE); + Res = ExprError(); + IsAmbiguous = true; + break; + } + } while ((Next = SemaRef.getTypoExprState(TE).Consumer->peekNextCorrection()) && + Next.getEditDistance(false) == TC.getEditDistance(false)); + + if (IsAmbiguous) + break; + + AmbiguousTypoExprs.remove(TE); + SemaRef.getTypoExprState(TE).Consumer->restoreSavedPosition(); + } + TransformCache = std::move(SavedTransformCache); + } + + // Wipe away any newly created TypoExprs that we don't know about. Since we + // clear any invalid TypoExprs in `CheckForRecursiveTypos`, this is only + // possible if a `TypoExpr` is created during a transformation but then + // fails before we can discover it. + auto &SemaTypoExprs = SemaRef.TypoExprs; + for (auto Iterator = SemaTypoExprs.begin(); Iterator != SemaTypoExprs.end();) { + auto TE = *Iterator; + auto FI = find(TypoExprs, TE); + if (FI != TypoExprs.end()) { + Iterator++; + continue; + } + SemaRef.clearDelayedTypo(TE); + Iterator = SemaTypoExprs.erase(Iterator); + } + SemaRef.TypoExprs = std::move(SavedTypoExprs); + + return Res; + } + public: TransformTypos(Sema &SemaRef, VarDecl *InitDecl, llvm::function_ref<ExprResult(Expr *)> Filter) : BaseTransform(SemaRef), InitDecl(InitDecl), ExprFilter(Filter) {} @@ -7677,49 +7823,13 @@ public: 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; - } - - // 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 - // there is more than one potentially ambiguous typo correction. - // Also, disable typo correction while attempting the transform when - // 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; - while (!AmbiguousTypoExprs.empty()) { - auto TE = AmbiguousTypoExprs.back(); - auto Cached = TransformCache[TE]; - auto &State = SemaRef.getTypoExprState(TE); - State.Consumer->saveCurrentPosition(); - TransformCache.erase(TE); - if (!TryTransform(E).isInvalid()) { - State.Consumer->resetCorrectionStream(); - TransformCache.erase(TE); - Res = ExprError(); - break; - } - AmbiguousTypoExprs.remove(TE); - State.Consumer->restoreSavedPosition(); - TransformCache[TE] = Cached; - } - SemaRef.DisableTypoCorrection = false; + bool IsAmbiguous = false; + ExprResult Res = RecursiveTransformLoop(E, IsAmbiguous); - // Ensure that all of the TypoExprs within the current Expr have been found. if (!Res.isUsable()) FindTypoExprs(TypoExprs).TraverseStmt(E); - EmitAllDiagnostics(); + EmitAllDiagnostics(IsAmbiguous); return Res; } |