mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
If `CompletionContextFinder` fails to find a `CompletionNode`, skip trying to filter and add solutions to the completion callback. This prevents an assertion/crash in `filterSolutionsForCodeCompletion` which assumes `CompletionNode` is non-null (either an expression or keypath). Resolves rdar://99966094.
133 lines
4.3 KiB
C++
133 lines
4.3 KiB
C++
//===--- CompletionContextFinder.cpp --------------------------------------===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2022 Apple Inc. and the Swift project authors
|
|
// Licensed under Apache License v2.0 with Runtime Library Exception
|
|
//
|
|
// See https://swift.org/LICENSE.txt for license information
|
|
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "swift/Sema/CompletionContextFinder.h"
|
|
|
|
using namespace swift;
|
|
using Fallback = CompletionContextFinder::Fallback;
|
|
|
|
ASTWalker::PreWalkResult<Expr *>
|
|
CompletionContextFinder::walkToExprPre(Expr *E) {
|
|
if (auto *closure = dyn_cast<ClosureExpr>(E)) {
|
|
Contexts.push_back({closure->hasSingleExpressionBody()
|
|
? ContextKind::SingleStmtClosure
|
|
: ContextKind::MultiStmtClosure,
|
|
closure});
|
|
}
|
|
|
|
if (isa<InterpolatedStringLiteralExpr>(E)) {
|
|
Contexts.push_back({ContextKind::StringInterpolation, E});
|
|
}
|
|
|
|
if (isa<ApplyExpr>(E) || isa<SequenceExpr>(E)) {
|
|
Contexts.push_back({ContextKind::FallbackExpression, E});
|
|
}
|
|
|
|
if (auto *Error = dyn_cast<ErrorExpr>(E)) {
|
|
Contexts.push_back({ContextKind::ErrorExpression, E});
|
|
if (auto *OrigExpr = Error->getOriginalExpr()) {
|
|
OrigExpr->walk(*this);
|
|
if (hasCompletionExpr())
|
|
return Action::Stop();
|
|
}
|
|
}
|
|
|
|
if (auto *CCE = dyn_cast<CodeCompletionExpr>(E)) {
|
|
CompletionNode = CCE;
|
|
return Action::Stop();
|
|
}
|
|
if (auto *KeyPath = dyn_cast<KeyPathExpr>(E)) {
|
|
for (auto &component : KeyPath->getComponents()) {
|
|
if (component.getKind() == KeyPathExpr::Component::Kind::CodeCompletion) {
|
|
CompletionNode = KeyPath;
|
|
return Action::Stop();
|
|
}
|
|
}
|
|
// Code completion in key paths is modelled by a code completion component
|
|
// Don't walk the key path's parsed expressions.
|
|
return Action::SkipChildren(E);
|
|
}
|
|
|
|
return Action::Continue(E);
|
|
}
|
|
|
|
ASTWalker::PostWalkResult<Expr *>
|
|
CompletionContextFinder::walkToExprPost(Expr *E) {
|
|
if (isa<ClosureExpr>(E) || isa<InterpolatedStringLiteralExpr>(E) ||
|
|
isa<ApplyExpr>(E) || isa<SequenceExpr>(E) || isa<ErrorExpr>(E)) {
|
|
assert(Contexts.back().E == E);
|
|
Contexts.pop_back();
|
|
}
|
|
return Action::Continue(E);
|
|
}
|
|
|
|
size_t CompletionContextFinder::getKeyPathCompletionComponentIndex() const {
|
|
size_t ComponentIndex = 0;
|
|
auto Components = getKeyPathContainingCompletionComponent()->getComponents();
|
|
for (auto &Component : Components) {
|
|
if (Component.getKind() == KeyPathExpr::Component::Kind::CodeCompletion) {
|
|
break;
|
|
} else {
|
|
ComponentIndex++;
|
|
}
|
|
}
|
|
assert(ComponentIndex < Components.size() &&
|
|
"No completion component in the key path?");
|
|
return ComponentIndex;
|
|
}
|
|
|
|
Optional<Fallback> CompletionContextFinder::getFallbackCompletionExpr() const {
|
|
if (!hasCompletionExpr()) {
|
|
// Creating a fallback expression only makes sense if we are completing in
|
|
// an expression, not when we're completing in a key path.
|
|
return None;
|
|
}
|
|
|
|
Optional<Fallback> fallback;
|
|
bool separatePrecheck = false;
|
|
DeclContext *fallbackDC = InitialDC;
|
|
|
|
// Find the outermost fallback expression within the innermost error
|
|
// expression or multi-statement closure, keeping track of its decl context.
|
|
for (auto context : Contexts) {
|
|
switch (context.Kind) {
|
|
case ContextKind::StringInterpolation:
|
|
LLVM_FALLTHROUGH;
|
|
case ContextKind::FallbackExpression:
|
|
if (!fallback && context.E != InitialExpr)
|
|
fallback = Fallback{context.E, fallbackDC, separatePrecheck};
|
|
continue;
|
|
|
|
case ContextKind::SingleStmtClosure:
|
|
if (!fallback && context.E != InitialExpr)
|
|
fallback = Fallback{context.E, fallbackDC, separatePrecheck};
|
|
fallbackDC = cast<AbstractClosureExpr>(context.E);
|
|
continue;
|
|
|
|
case ContextKind::MultiStmtClosure:
|
|
fallbackDC = cast<AbstractClosureExpr>(context.E);
|
|
LLVM_FALLTHROUGH;
|
|
case ContextKind::ErrorExpression:;
|
|
fallback = None;
|
|
separatePrecheck = true;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (fallback)
|
|
return fallback;
|
|
|
|
if (getCompletionExpr() != InitialExpr)
|
|
return Fallback{getCompletionExpr(), fallbackDC, separatePrecheck};
|
|
return None;
|
|
}
|