Skip to content

Commit bc31977

Browse files
committed
[TypeChecker] Replace invalid refs with ErrorExpr only when explicitly allowed
Add a flag to `ConstraintSystem::preCheckExpression` and subsequently to `TypeChecker::resolveDeclRefExpr` to indicate whether it's allowed to replace invalid member refs with `ErrorExpr`. It is useful for diagnostics and code completion to preserve AST in it's original state otherwise it's impossible to diagnose errors post factum or extract `CodeCompletionExpr` when it's a child of an invalid reference.
1 parent 3ad8ea8 commit bc31977

File tree

8 files changed

+54
-23
lines changed

8 files changed

+54
-23
lines changed

lib/Sema/BuilderTransform.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1749,7 +1749,8 @@ class PreCheckFunctionBuilderApplication : public ASTWalker {
17491749
// Suppress any diangostics which could be produced by this expression.
17501750
DiagnosticTransaction transaction(diagEngine);
17511751

1752-
HasError |= ConstraintSystem::preCheckExpression(E, DC);
1752+
HasError |= ConstraintSystem::preCheckExpression(
1753+
E, DC, /*replaceInvalidRefsWithErrors=*/false);
17531754
HasError |= transaction.hasDiagnostics();
17541755

17551756
transaction.abort();

lib/Sema/CSFix.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1563,7 +1563,8 @@ bool IgnoreInvalidFunctionBuilderBody::diagnose(const Solution &solution,
15631563
PreCheckWalker(DeclContext *dc) : DC(dc) {}
15641564

15651565
std::pair<bool, Expr *> walkToExprPre(Expr *E) override {
1566-
auto hasError = ConstraintSystem::preCheckExpression(E, DC);
1566+
auto hasError = ConstraintSystem::preCheckExpression(
1567+
E, DC, /*replaceInvalidRefsWithErrors=*/true);
15671568
return std::make_pair(false, hasError ? nullptr : E);
15681569
}
15691570

lib/Sema/CSSolver.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1411,7 +1411,8 @@ void ConstraintSystem::solveForCodeCompletion(
14111411
llvm::function_ref<void(const Solution &)> callback) {
14121412
// First, pre-check the expression, validating any types that occur in the
14131413
// expression and folding sequence expressions.
1414-
if (ConstraintSystem::preCheckExpression(expr, DC))
1414+
if (ConstraintSystem::preCheckExpression(
1415+
expr, DC, /*replaceInvalidRefsWithErrors=*/true))
14151416
return;
14161417

14171418
ConstraintSystemOptions options;

lib/Sema/ConstraintSystem.h

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4955,8 +4955,12 @@ class ConstraintSystem {
49554955
public:
49564956
/// Pre-check the expression, validating any types that occur in the
49574957
/// expression and folding sequence expressions.
4958-
static bool preCheckExpression(Expr *&expr, DeclContext *dc);
4959-
4958+
///
4959+
/// \param replaceInvalidRefsWithErrors Indicates whether it's allowed
4960+
/// to replace any discovered invalid member references with `ErrorExpr`.
4961+
static bool preCheckExpression(Expr *&expr, DeclContext *dc,
4962+
bool replaceInvalidRefsWithErrors);
4963+
49604964
/// Solve the system of constraints generated from provided target.
49614965
///
49624966
/// \param target The target that we'll generate constraints from, which

lib/Sema/TypeCheckCodeCompletion.cpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -553,10 +553,10 @@ TypeChecker::getTypeOfCompletionOperator(DeclContext *DC, Expr *LHS,
553553
// We allocate these expressions on the stack because we know they can't
554554
// escape and there isn't a better way to allocate scratch Expr nodes.
555555
UnresolvedDeclRefExpr UDRE(DeclNameRef(opName), refKind, DeclNameLoc(Loc));
556-
auto *opExpr = TypeChecker::resolveDeclRefExpr(&UDRE, DC);
556+
auto *opExpr = TypeChecker::resolveDeclRefExpr(
557+
&UDRE, DC, /*replaceInvalidRefsWithErrors=*/true);
557558

558559
switch (refKind) {
559-
560560
case DeclRefKind::PostfixOperator: {
561561
// (postfix_unary_expr
562562
// (declref_expr name=<opName>)
@@ -611,7 +611,8 @@ static Optional<Type> getTypeOfCompletionContextExpr(
611611
CompletionTypeCheckKind kind,
612612
Expr *&parsedExpr,
613613
ConcreteDeclRef &referencedDecl) {
614-
if (constraints::ConstraintSystem::preCheckExpression(parsedExpr, DC))
614+
if (constraints::ConstraintSystem::preCheckExpression(
615+
parsedExpr, DC, /*replaceInvalidRefsWithErrors=*/true))
615616
return None;
616617

617618
switch (kind) {

lib/Sema/TypeCheckConstraints.cpp

Lines changed: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -481,11 +481,18 @@ static bool findNonMembers(ArrayRef<LookupResultEntry> lookupResults,
481481
/// returning the resultant expression. Context is the DeclContext used
482482
/// for the lookup.
483483
Expr *TypeChecker::resolveDeclRefExpr(UnresolvedDeclRefExpr *UDRE,
484-
DeclContext *DC) {
484+
DeclContext *DC,
485+
bool replaceInvalidRefsWithErrors) {
485486
// Process UnresolvedDeclRefExpr by doing an unqualified lookup.
486487
DeclNameRef Name = UDRE->getName();
487488
SourceLoc Loc = UDRE->getLoc();
488489

490+
auto errorResult = [&]() -> Expr * {
491+
if (replaceInvalidRefsWithErrors)
492+
return new (DC->getASTContext()) ErrorExpr(UDRE->getSourceRange());
493+
return UDRE;
494+
};
495+
489496
// Perform standard value name lookup.
490497
NameLookupOptions lookupOptions = defaultUnqualifiedLookupOptions;
491498
if (isa<AbstractFunctionDecl>(DC))
@@ -506,7 +513,7 @@ Expr *TypeChecker::resolveDeclRefExpr(UnresolvedDeclRefExpr *UDRE,
506513
if (diagnoseRangeOperatorMisspell(Context.Diags, UDRE) ||
507514
diagnoseIncDecOperator(Context.Diags, UDRE) ||
508515
diagnoseOperatorJuxtaposition(UDRE, DC)) {
509-
return new (Context) ErrorExpr(UDRE->getSourceRange());
516+
return errorResult();
510517
}
511518

512519
// Try ignoring access control.
@@ -531,7 +538,7 @@ Expr *TypeChecker::resolveDeclRefExpr(UnresolvedDeclRefExpr *UDRE,
531538

532539
// Don't try to recover here; we'll get more access-related diagnostics
533540
// downstream if the type of the inaccessible decl is also inaccessible.
534-
return new (Context) ErrorExpr(UDRE->getSourceRange());
541+
return errorResult();
535542
}
536543

537544
// TODO: Name will be a compound name if it was written explicitly as
@@ -625,7 +632,7 @@ Expr *TypeChecker::resolveDeclRefExpr(UnresolvedDeclRefExpr *UDRE,
625632
// TODO: consider recovering from here. We may want some way to suppress
626633
// downstream diagnostics, though.
627634

628-
return new (Context) ErrorExpr(UDRE->getSourceRange());
635+
return errorResult();
629636
}
630637

631638
// FIXME: Need to refactor the way we build an AST node from a lookup result!
@@ -916,6 +923,10 @@ namespace {
916923

917924
Expr *ParentExpr;
918925

926+
/// Indicates whether pre-check is allowed to insert
927+
/// implicit `ErrorExpr` in place of invalid references.
928+
bool UseErrorExprs;
929+
919930
/// A stack of expressions being walked, used to determine where to
920931
/// insert RebindSelfInConstructorExpr nodes.
921932
llvm::SmallVector<Expr *, 8> ExprStack;
@@ -1055,8 +1066,10 @@ namespace {
10551066
}
10561067

10571068
public:
1058-
PreCheckExpression(DeclContext *dc, Expr *parent)
1059-
: Ctx(dc->getASTContext()), DC(dc), ParentExpr(parent) {}
1069+
PreCheckExpression(DeclContext *dc, Expr *parent,
1070+
bool replaceInvalidRefsWithErrors)
1071+
: Ctx(dc->getASTContext()), DC(dc), ParentExpr(parent),
1072+
UseErrorExprs(replaceInvalidRefsWithErrors) {}
10601073

10611074
ASTContext &getASTContext() const { return Ctx; }
10621075

@@ -1122,7 +1135,8 @@ namespace {
11221135
if (auto unresolved = dyn_cast<UnresolvedDeclRefExpr>(expr)) {
11231136
TypeChecker::checkForForbiddenPrefix(
11241137
getASTContext(), unresolved->getName().getBaseName());
1125-
return finish(true, TypeChecker::resolveDeclRefExpr(unresolved, DC));
1138+
return finish(true, TypeChecker::resolveDeclRefExpr(unresolved, DC,
1139+
UseErrorExprs));
11261140
}
11271141

11281142
// Let's try to figure out if `InOutExpr` is out of place early
@@ -1979,8 +1993,9 @@ Expr *PreCheckExpression::simplifyTypeConstructionWithLiteralArg(Expr *E) {
19791993

19801994
/// Pre-check the expression, validating any types that occur in the
19811995
/// expression and folding sequence expressions.
1982-
bool ConstraintSystem::preCheckExpression(Expr *&expr, DeclContext *dc) {
1983-
PreCheckExpression preCheck(dc, expr);
1996+
bool ConstraintSystem::preCheckExpression(Expr *&expr, DeclContext *dc,
1997+
bool replaceInvalidRefsWithErrors) {
1998+
PreCheckExpression preCheck(dc, expr, replaceInvalidRefsWithErrors);
19841999
// Perform the pre-check.
19852000
if (auto result = expr->walk(preCheck)) {
19862001
expr = result;
@@ -2107,7 +2122,8 @@ TypeChecker::typeCheckExpression(
21072122

21082123
// First, pre-check the expression, validating any types that occur in the
21092124
// expression and folding sequence expressions.
2110-
if (ConstraintSystem::preCheckExpression(expr, dc)) {
2125+
if (ConstraintSystem::preCheckExpression(
2126+
expr, dc, /*replaceInvalidRefsWithErrors=*/true)) {
21112127
target.setExpr(expr);
21122128
return None;
21132129
}
@@ -2336,13 +2352,15 @@ bool TypeChecker::typeCheckForEachBinding(DeclContext *dc, ForEachStmt *stmt) {
23362352

23372353
// Precheck the sequence.
23382354
Expr *sequence = stmt->getSequence();
2339-
if (ConstraintSystem::preCheckExpression(sequence, dc))
2355+
if (ConstraintSystem::preCheckExpression(
2356+
sequence, dc, /*replaceInvalidRefsWithErrors=*/true))
23402357
return failed();
23412358
stmt->setSequence(sequence);
23422359

23432360
// Precheck the filtering condition.
23442361
if (Expr *whereExpr = stmt->getWhere()) {
2345-
if (ConstraintSystem::preCheckExpression(whereExpr, dc))
2362+
if (ConstraintSystem::preCheckExpression(
2363+
whereExpr, dc, /*replaceInvalidRefsWithErrors=*/true))
23462364
return failed();
23472365

23482366
stmt->setWhere(whereExpr);

lib/Sema/TypeCheckPattern.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -530,9 +530,10 @@ class ResolvePattern : public ASTVisitor<ResolvePattern,
530530

531531

532532
// Perform unqualified name lookup to find out what the UDRE is.
533-
return getSubExprPattern(TypeChecker::resolveDeclRefExpr(ude, DC));
533+
return getSubExprPattern(TypeChecker::resolveDeclRefExpr(
534+
ude, DC, /*replaceInvalidRefsWithErrors=*/true));
534535
}
535-
536+
536537
// Call syntax forms a pattern if:
537538
// - the callee in 'Element(x...)' or '.Element(x...)'
538539
// references an enum element. The arguments then form a tuple

lib/Sema/TypeChecker.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -315,7 +315,11 @@ Type getOptionalType(SourceLoc loc, Type elementType);
315315
/// Bind an UnresolvedDeclRefExpr by performing name lookup and
316316
/// returning the resultant expression. Context is the DeclContext used
317317
/// for the lookup.
318-
Expr *resolveDeclRefExpr(UnresolvedDeclRefExpr *UDRE, DeclContext *Context);
318+
///
319+
/// \param replaceInvalidRefsWithErrors Indicates whether it's allowed
320+
/// to replace any discovered invalid member references with `ErrorExpr`.
321+
Expr *resolveDeclRefExpr(UnresolvedDeclRefExpr *UDRE, DeclContext *Context,
322+
bool replaceInvalidRefsWithErrors);
319323

320324
/// Check for unsupported protocol types in the given declaration.
321325
void checkUnsupportedProtocolType(Decl *decl);

0 commit comments

Comments
 (0)