@@ -603,7 +603,7 @@ bool TypeChecker::typeCheckForCodeCompletion(
603603 return false ;
604604
605605 // 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.
607607 {
608608 auto range = expr->getSourceRange ();
609609 if (range.isInvalid () ||
@@ -623,7 +623,8 @@ bool TypeChecker::typeCheckForCodeCompletion(
623623 Application,
624624 StringInterpolation,
625625 SingleStmtClosure,
626- MultiStmtClosure
626+ MultiStmtClosure,
627+ ErrorExpression
627628 };
628629
629630 class ContextFinder : public ASTWalker {
@@ -632,7 +633,7 @@ bool TypeChecker::typeCheckForCodeCompletion(
632633 // Stack of all "interesting" contexts up to code completion expression.
633634 llvm::SmallVector<Context, 4 > Contexts;
634635
635- Expr *CompletionExpr;
636+ Expr *CompletionExpr = nullptr ;
636637
637638 public:
638639 ContextFinder (Expr *E) {
@@ -660,12 +661,20 @@ bool TypeChecker::typeCheckForCodeCompletion(
660661 return std::make_pair (false , nullptr );
661662 }
662663
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+
663672 return std::make_pair (true , E);
664673 }
665674
666675 Expr *walkToExprPost (Expr *E) override {
667676 if (isa<ClosureExpr>(E) || isa<InterpolatedStringLiteralExpr>(E) ||
668- isa<ApplyExpr>(E))
677+ isa<ApplyExpr>(E) || isa<ErrorExpr>(E) )
669678 Contexts.pop_back ();
670679 return E;
671680 }
@@ -680,11 +689,23 @@ bool TypeChecker::typeCheckForCodeCompletion(
680689 return hasContext (ContextKind::StringInterpolation);
681690 }
682691
692+ bool hasCompletionExpr () const {
693+ return CompletionExpr;
694+ }
695+
683696 Expr *getCompletionExpr () const {
684697 assert (CompletionExpr);
685698 return CompletionExpr;
686699 }
687700
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+
688709 ClosureExpr *getOutermostMultiStmtClosure () const {
689710 for (const Context &curr : Contexts) {
690711 if (curr.first == ContextKind::MultiStmtClosure)
@@ -717,6 +738,25 @@ bool TypeChecker::typeCheckForCodeCompletion(
717738 ContextFinder contextAnalyzer (expr);
718739 expr->walk (contextAnalyzer);
719740
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+
720760 // Interpolation components are type-checked separately.
721761 if (contextAnalyzer.locatedInStringIterpolation ())
722762 return false ;
0 commit comments