@@ -603,7 +603,7 @@ bool TypeChecker::typeCheckForCodeCompletion(
603
603
return false ;
604
604
605
605
// First of all, let's check whether given target expression
606
- // does indeed have a code completion token in it.
606
+ // does indeed have the code completion location in it.
607
607
{
608
608
auto range = expr->getSourceRange ();
609
609
if (range.isInvalid () ||
@@ -623,7 +623,8 @@ bool TypeChecker::typeCheckForCodeCompletion(
623
623
Application,
624
624
StringInterpolation,
625
625
SingleStmtClosure,
626
- MultiStmtClosure
626
+ MultiStmtClosure,
627
+ ErrorExpression
627
628
};
628
629
629
630
class ContextFinder : public ASTWalker {
@@ -632,7 +633,7 @@ bool TypeChecker::typeCheckForCodeCompletion(
632
633
// Stack of all "interesting" contexts up to code completion expression.
633
634
llvm::SmallVector<Context, 4 > Contexts;
634
635
635
- Expr *CompletionExpr;
636
+ Expr *CompletionExpr = nullptr ;
636
637
637
638
public:
638
639
ContextFinder (Expr *E) {
@@ -660,12 +661,20 @@ bool TypeChecker::typeCheckForCodeCompletion(
660
661
return std::make_pair (false , nullptr );
661
662
}
662
663
664
+ if (auto *Error = dyn_cast<ErrorExpr>(E)) {
665
+ Contexts.push_back (std::make_pair (ContextKind::ErrorExpression, E));
666
+ if (auto *OrigExpr = Error->getOriginalExpr ()) {
667
+ OrigExpr->walk (*this );
668
+ return std::make_pair (false , hasCompletionExpr () ? nullptr : E);
669
+ }
670
+ }
671
+
663
672
return std::make_pair (true , E);
664
673
}
665
674
666
675
Expr *walkToExprPost (Expr *E) override {
667
676
if (isa<ClosureExpr>(E) || isa<InterpolatedStringLiteralExpr>(E) ||
668
- isa<ApplyExpr>(E))
677
+ isa<ApplyExpr>(E) || isa<ErrorExpr>(E) )
669
678
Contexts.pop_back ();
670
679
return E;
671
680
}
@@ -680,11 +689,23 @@ bool TypeChecker::typeCheckForCodeCompletion(
680
689
return hasContext (ContextKind::StringInterpolation);
681
690
}
682
691
692
+ bool hasCompletionExpr () const {
693
+ return CompletionExpr;
694
+ }
695
+
683
696
Expr *getCompletionExpr () const {
684
697
assert (CompletionExpr);
685
698
return CompletionExpr;
686
699
}
687
700
701
+ ErrorExpr *getInnermostErrorExpr () const {
702
+ for (const Context &curr : llvm::reverse (Contexts)) {
703
+ if (curr.first == ContextKind::ErrorExpression)
704
+ return cast<ErrorExpr>(curr.second );
705
+ }
706
+ return nullptr ;
707
+ }
708
+
688
709
ClosureExpr *getOutermostMultiStmtClosure () const {
689
710
for (const Context &curr : Contexts) {
690
711
if (curr.first == ContextKind::MultiStmtClosure)
@@ -717,6 +738,25 @@ bool TypeChecker::typeCheckForCodeCompletion(
717
738
ContextFinder contextAnalyzer (expr);
718
739
expr->walk (contextAnalyzer);
719
740
741
+ // If there was no completion expr (e.g. if the code completion location is
742
+ // within an ErrorExpr without an valid subexpr due to parser error recovery)
743
+ // bail.
744
+ if (!contextAnalyzer.hasCompletionExpr ())
745
+ return false ;
746
+
747
+ // If the completion expression is in a valid subexpression of an ErrorExpr,
748
+ // fallback to trying the valid subexpression without any context. This can
749
+ // happen for cases like `expectsBoolArg(foo.<complete>).` which becomes an
750
+ // ErrorExpr due to the missing member name after the final dot.
751
+ if (auto *errorExpr = contextAnalyzer.getInnermostErrorExpr ()) {
752
+ if (auto *origExpr = errorExpr->getOriginalExpr ()) {
753
+ SolutionApplicationTarget completionTarget (origExpr, DC, CTP_Unused,
754
+ /* contextualType=*/ Type (),
755
+ /* isDiscarded=*/ true );
756
+ return typeCheckForCodeCompletion (completionTarget, callback);
757
+ }
758
+ }
759
+
720
760
// Interpolation components are type-checked separately.
721
761
if (contextAnalyzer.locatedInStringIterpolation ())
722
762
return false ;
0 commit comments