Skip to content

Commit 7e22297

Browse files
committed
[AST] Walk ErrorExpr's original expr in ASTWalker
We set an original expression on ErrorExpr for cases where we have something semantically invalid that doesn't fit into the AST, but is still something that the user has explicitly written. For example this is how we represent unresolved dots without member names (`x.`). We still want to type-check the underlying expression though since it can provide useful diagnostics and allows semantic functionality such as completion and cursor info to work correctly. rdar://130771574
1 parent b063947 commit 7e22297

File tree

10 files changed

+34
-30
lines changed

10 files changed

+34
-30
lines changed

include/swift/AST/Expr.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -610,7 +610,8 @@ class ErrorExpr : public Expr {
610610

611611
SourceRange getSourceRange() const { return Range; }
612612
Expr *getOriginalExpr() const { return OriginalExpr; }
613-
613+
void setOriginalExpr(Expr *newExpr) { OriginalExpr = newExpr; }
614+
614615
static bool classof(const Expr *E) {
615616
return E->getKind() == ExprKind::Error;
616617
}

lib/AST/ASTWalker.cpp

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -616,7 +616,15 @@ class Traversal : public ASTVisitor<Traversal, Expr*, Stmt*,
616616
} \
617617
} while (false)
618618

619-
Expr *visitErrorExpr(ErrorExpr *E) { return E; }
619+
Expr *visitErrorExpr(ErrorExpr *E) {
620+
if (auto *origExpr = E->getOriginalExpr()) {
621+
auto *newOrigExpr = doIt(origExpr);
622+
if (!newOrigExpr)
623+
return nullptr;
624+
E->setOriginalExpr(newOrigExpr);
625+
}
626+
return E;
627+
}
620628
Expr *visitCodeCompletionExpr(CodeCompletionExpr *E) {
621629
if (Expr *baseExpr = E->getBase()) {
622630
Expr *newBaseExpr = doIt(baseExpr);

lib/IDE/Formatting.cpp

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -587,15 +587,6 @@ class RangeWalker: protected ASTWalker {
587587
}
588588

589589
PreWalkResult<Expr *> walkToExprPre(Expr *E) override {
590-
// Walk through error expressions.
591-
if (auto *EE = dyn_cast<ErrorExpr>(E)) {
592-
if (auto *OE = EE->getOriginalExpr()) {
593-
llvm::SaveAndRestore<ASTWalker::ParentTy>(Parent, EE);
594-
OE->walk(*this);
595-
}
596-
return Action::Continue(E);
597-
}
598-
599590
if (E->isImplicit())
600591
return Action::Continue(E);
601592

@@ -1491,17 +1482,6 @@ class FormatWalker : public ASTWalker {
14911482
}
14921483
}
14931484

1494-
// Walk through error expressions.
1495-
if (auto *EE = dyn_cast<ErrorExpr>(E)) {
1496-
if (Action.shouldVisitChildren()) {
1497-
if (auto *OE = EE->getOriginalExpr()) {
1498-
llvm::SaveAndRestore<ASTWalker::ParentTy>(Parent, EE);
1499-
OE->walk(*this);
1500-
}
1501-
return Action::SkipNode(E);
1502-
}
1503-
}
1504-
15051485
// FIXME: We ought to be able to use Action::VisitChildrenIf here, but we'd
15061486
// need to ensure the AST is walked in source order (currently not the case
15071487
// for things like postfix operators).

lib/Sema/CompletionContextFinder.cpp

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -49,11 +49,6 @@ CompletionContextFinder::walkToExprPre(Expr *E) {
4949

5050
if (auto *Error = dyn_cast<ErrorExpr>(E)) {
5151
Contexts.push_back({ContextKind::ErrorExpression, E});
52-
if (auto *OrigExpr = Error->getOriginalExpr()) {
53-
OrigExpr->walk(*this);
54-
if (hasCompletionExpr())
55-
return Action::Stop();
56-
}
5752
}
5853

5954
if (auto *CCE = dyn_cast<CodeCompletionExpr>(E)) {

lib/Sema/PreCheckTarget.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1252,7 +1252,8 @@ class PreCheckTarget final : public ASTWalker {
12521252
}
12531253

12541254
if (isa<UnresolvedDotExpr>(parentExpr) ||
1255-
isa<MemberRefExpr>(parentExpr)) {
1255+
isa<MemberRefExpr>(parentExpr) ||
1256+
isa<ErrorExpr>(parentExpr)) {
12561257
return true;
12571258
} else if (auto *SE = dyn_cast<SubscriptExpr>(parentExpr)) {
12581259
// 'super[]' is valid, but 'x[super]' is not.

test/Constraints/diagnostics.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1578,3 +1578,10 @@ func testAddMemberVsRemoveCall() {
15781578
let b = Foo_74617()
15791579
let c = (a + b).bar() // expected-error {{cannot call value of non-function type 'Float'}} {{22-24=}}
15801580
}
1581+
1582+
// Make sure we can still type-check the closure.
1583+
do {
1584+
_ = {
1585+
let x: String = 0 // expected-error {{cannot convert value of type 'Int' to specified type 'String'}}
1586+
}. // expected-error {{expected member name following '.'}}
1587+
}

test/Parse/recovery.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -550,7 +550,8 @@ func exprPostfix2() {
550550

551551
class ExprSuper {
552552
init() {
553-
super. // expected-error {{expected member name following '.'}}
553+
super. // expected-error {{expected member name following '.'}}
554+
// expected-error@-1 {{'super' cannot be used in class 'ExprSuper' because it has no superclass}}
554555
}
555556
}
556557

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
struct S {
2+
var x: Int
3+
}
4+
5+
// Make sure we can still resolve 'x' even with the trailing dot.
6+
func foo(_ s: S) {
7+
s.x.
8+
// RUN: %sourcekitd-test -req=cursor -pos=%(line-1):5 %s -- %s -module-name main | %FileCheck %s
9+
}
10+
// CHECK: s:4main1SV1xSivp

test/StringProcessing/Parse/forward-slash-regex.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -445,6 +445,7 @@ _ = ^/"/"
445445
_ = ^/"[/"
446446
// expected-error@-1 {{'^' is not a prefix unary operator}}
447447
// expected-error@-2 {{unterminated string literal}}
448+
// expected-error@-3 {{cannot parse regular expression: expected custom character class members}}
448449

449450
_ = (^/)("/")
450451

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
// {"kind":"complete","original":"6384b7fc","signature":"swift::TypeChecker::typeCheckForCodeCompletion(swift::constraints::SyntacticElementTarget&, bool, llvm::function_ref<void (swift::constraints::Solution const&)>)","signatureAssert":"Assertion failed: (fallback->E != expr), function typeCheckForCodeCompletion"}
2-
// RUN: not --crash %target-swift-ide-test -code-completion -batch-code-completion -skip-filecheck -code-completion-diagnostics -source-filename %s
2+
// RUN: %target-swift-ide-test -code-completion -batch-code-completion -skip-filecheck -code-completion-diagnostics -source-filename %s
33
func a -> b
44
a > (c > a #^^# )'

0 commit comments

Comments
 (0)