forked from OSchip/llvm-project
Use the full-Expr filter to disambiguate equidistant correction
candidates. llvm-svn: 222549
This commit is contained in:
parent
a126e462c2
commit
5ca2ecd2b2
|
@ -5992,7 +5992,7 @@ class TransformTypos : public TreeTransform<TransformTypos> {
|
||||||
typedef TreeTransform<TransformTypos> BaseTransform;
|
typedef TreeTransform<TransformTypos> BaseTransform;
|
||||||
|
|
||||||
llvm::function_ref<ExprResult(Expr *)> ExprFilter;
|
llvm::function_ref<ExprResult(Expr *)> ExprFilter;
|
||||||
llvm::SmallSetVector<TypoExpr *, 2> TypoExprs;
|
llvm::SmallSetVector<TypoExpr *, 2> TypoExprs, AmbiguousTypoExprs;
|
||||||
llvm::SmallDenseMap<TypoExpr *, ExprResult, 2> TransformCache;
|
llvm::SmallDenseMap<TypoExpr *, ExprResult, 2> TransformCache;
|
||||||
llvm::SmallDenseMap<OverloadExpr *, Expr *, 4> OverloadResolution;
|
llvm::SmallDenseMap<OverloadExpr *, Expr *, 4> OverloadResolution;
|
||||||
|
|
||||||
|
@ -6059,6 +6059,15 @@ class TransformTypos : public TreeTransform<TransformTypos> {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ExprResult TryTransform(Expr *E) {
|
||||||
|
Sema::SFINAETrap Trap(SemaRef);
|
||||||
|
ExprResult Res = TransformExpr(E);
|
||||||
|
if (Trap.hasErrorOccurred() || Res.isInvalid())
|
||||||
|
return ExprError();
|
||||||
|
|
||||||
|
return ExprFilter(Res.get());
|
||||||
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
TransformTypos(Sema &SemaRef, llvm::function_ref<ExprResult(Expr *)> Filter)
|
TransformTypos(Sema &SemaRef, llvm::function_ref<ExprResult(Expr *)> Filter)
|
||||||
: BaseTransform(SemaRef), ExprFilter(Filter) {}
|
: BaseTransform(SemaRef), ExprFilter(Filter) {}
|
||||||
|
@ -6079,30 +6088,42 @@ public:
|
||||||
ExprResult TransformLambdaExpr(LambdaExpr *E) { return Owned(E); }
|
ExprResult TransformLambdaExpr(LambdaExpr *E) { return Owned(E); }
|
||||||
|
|
||||||
ExprResult Transform(Expr *E) {
|
ExprResult Transform(Expr *E) {
|
||||||
ExprResult res;
|
ExprResult Res;
|
||||||
bool error = false;
|
|
||||||
while (true) {
|
while (true) {
|
||||||
Sema::SFINAETrap Trap(SemaRef);
|
Res = TryTransform(E);
|
||||||
res = TransformExpr(E);
|
|
||||||
error = Trap.hasErrorOccurred();
|
|
||||||
|
|
||||||
if (!(error || res.isInvalid()))
|
|
||||||
res = ExprFilter(res.get());
|
|
||||||
|
|
||||||
// Exit if either the transform was valid or if there were no TypoExprs
|
// Exit if either the transform was valid or if there were no TypoExprs
|
||||||
// to transform that still have any untried correction candidates..
|
// to transform that still have any untried correction candidates..
|
||||||
if (!(error || res.isInvalid()) ||
|
if (!Res.isInvalid() ||
|
||||||
!CheckAndAdvanceTypoExprCorrectionStreams())
|
!CheckAndAdvanceTypoExprCorrectionStreams())
|
||||||
break;
|
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.
|
||||||
|
while (!AmbiguousTypoExprs.empty()) {
|
||||||
|
auto TE = AmbiguousTypoExprs.back();
|
||||||
|
auto Cached = TransformCache[TE];
|
||||||
|
AmbiguousTypoExprs.pop_back();
|
||||||
|
TransformCache.erase(TE);
|
||||||
|
if (!TryTransform(E).isInvalid()) {
|
||||||
|
SemaRef.getTypoExprState(TE).Consumer->resetCorrectionStream();
|
||||||
|
TransformCache.erase(TE);
|
||||||
|
Res = ExprError();
|
||||||
|
break;
|
||||||
|
} else
|
||||||
|
TransformCache[TE] = Cached;
|
||||||
|
}
|
||||||
|
|
||||||
// Ensure that all of the TypoExprs within the current Expr have been found.
|
// Ensure that all of the TypoExprs within the current Expr have been found.
|
||||||
if (!res.isUsable())
|
if (!Res.isUsable())
|
||||||
FindTypoExprs(TypoExprs).TraverseStmt(E);
|
FindTypoExprs(TypoExprs).TraverseStmt(E);
|
||||||
|
|
||||||
EmitAllDiagnostics();
|
EmitAllDiagnostics();
|
||||||
|
|
||||||
return res;
|
return Res;
|
||||||
}
|
}
|
||||||
|
|
||||||
ExprResult TransformTypoExpr(TypoExpr *E) {
|
ExprResult TransformTypoExpr(TypoExpr *E) {
|
||||||
|
@ -6124,21 +6145,15 @@ public:
|
||||||
State.RecoveryHandler(SemaRef, E, TC) :
|
State.RecoveryHandler(SemaRef, E, TC) :
|
||||||
attemptRecovery(SemaRef, *State.Consumer, TC);
|
attemptRecovery(SemaRef, *State.Consumer, TC);
|
||||||
if (!NE.isInvalid()) {
|
if (!NE.isInvalid()) {
|
||||||
// Check whether there is a second viable correction with the same edit
|
// Check whether there may be a second viable correction with the same
|
||||||
// distance--in which case do not suggest anything since both are
|
// edit distance; if so, remember this TypoExpr may have an ambiguous
|
||||||
// equally good candidates for correcting the typo.
|
// correction so it can be more thoroughly vetted later.
|
||||||
Sema::SFINAETrap LocalTrap(SemaRef);
|
|
||||||
TypoCorrection Next;
|
TypoCorrection Next;
|
||||||
while ((Next = State.Consumer->peekNextCorrection()) &&
|
if ((Next = State.Consumer->peekNextCorrection()) &&
|
||||||
Next.getEditDistance(false) == TC.getEditDistance(false)) {
|
Next.getEditDistance(false) == TC.getEditDistance(false)) {
|
||||||
ExprResult Res =
|
AmbiguousTypoExprs.insert(E);
|
||||||
State.RecoveryHandler
|
} else {
|
||||||
? State.RecoveryHandler(SemaRef, E, Next)
|
AmbiguousTypoExprs.remove(E);
|
||||||
: attemptRecovery(SemaRef, *State.Consumer, Next);
|
|
||||||
if (!Res.isInvalid()) {
|
|
||||||
NE = ExprError();
|
|
||||||
State.Consumer->getNextCorrection();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
assert(!NE.isUnset() &&
|
assert(!NE.isUnset() &&
|
||||||
"Typo was transformed into a valid-but-null ExprResult");
|
"Typo was transformed into a valid-but-null ExprResult");
|
||||||
|
|
|
@ -48,3 +48,14 @@ void testNoCandidates() {
|
||||||
callee(xxxxxx, // expected-error-re {{use of undeclared identifier 'xxxxxx'{{$}}}}
|
callee(xxxxxx, // expected-error-re {{use of undeclared identifier 'xxxxxx'{{$}}}}
|
||||||
zzzzzz); // expected-error-re {{use of undeclared identifier 'zzzzzz'{{$}}}}
|
zzzzzz); // expected-error-re {{use of undeclared identifier 'zzzzzz'{{$}}}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class string {};
|
||||||
|
struct Item {
|
||||||
|
void Nest();
|
||||||
|
string text();
|
||||||
|
Item* next(); // expected-note {{'next' declared here}}
|
||||||
|
};
|
||||||
|
void testExprFilter(Item *i) {
|
||||||
|
Item *j;
|
||||||
|
j = i->Next(); // expected-error {{no member named 'Next' in 'Item'; did you mean 'next'?}}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue