Skip to content

Commit d190e3f

Browse files
committed
[CodeCompletion] Move CompletionContextFinder to its own file
1 parent 190ee6e commit d190e3f

File tree

5 files changed

+261
-216
lines changed

5 files changed

+261
-216
lines changed

include/swift/Sema/CodeCompletionTypeChecking.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,9 @@
1818
#ifndef SWIFT_SEMA_CODECOMPLETIONTYPECHECKING_H
1919
#define SWIFT_SEMA_CODECOMPLETIONTYPECHECKING_H
2020

21-
#include "swift/Basic/LLVM.h"
21+
#include "swift/AST/Expr.h"
2222
#include "swift/AST/Type.h"
23+
#include "swift/Basic/LLVM.h"
2324
#include "llvm/ADT/DenseMap.h"
2425
#include "llvm/ADT/SmallVector.h"
2526

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
//===--- CompletionContextFinder.h ----------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2022 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#ifndef SWIFT_SEMA_COMPLETIONCONTEXTFINDER_H
14+
#define SWIFT_SEMA_COMPLETIONCONTEXTFINDER_H
15+
16+
#include "swift/AST/ASTWalker.h"
17+
#include "swift/AST/Expr.h"
18+
#include "swift/Sema/CodeCompletionTypeChecking.h"
19+
20+
namespace swift {
21+
22+
class CompletionContextFinder : public ASTWalker {
23+
enum class ContextKind {
24+
FallbackExpression,
25+
StringInterpolation,
26+
SingleStmtClosure,
27+
MultiStmtClosure,
28+
ErrorExpression
29+
};
30+
31+
struct Context {
32+
ContextKind Kind;
33+
Expr *E;
34+
};
35+
36+
/// Stack of all "interesting" contexts up to code completion expression.
37+
llvm::SmallVector<Context, 4> Contexts;
38+
39+
/// If we are completing inside an expression, the \c CodeCompletionExpr that
40+
/// represents the code completion token.
41+
42+
/// The AST node that represents the code completion token, either as a
43+
/// \c CodeCompletionExpr or a \c KeyPathExpr which contains a code completion
44+
/// component.
45+
llvm::PointerUnion<CodeCompletionExpr *, const KeyPathExpr *> CompletionNode;
46+
47+
Expr *InitialExpr = nullptr;
48+
DeclContext *InitialDC;
49+
50+
public:
51+
/// Finder for completion contexts within the provided initial expression.
52+
CompletionContextFinder(Expr *initialExpr, DeclContext *DC)
53+
: InitialExpr(initialExpr), InitialDC(DC) {
54+
assert(DC);
55+
initialExpr->walk(*this);
56+
};
57+
58+
/// Finder for completion contexts within the outermost non-closure context of
59+
/// the code completion expression's direct context.
60+
CompletionContextFinder(DeclContext *completionDC) : InitialDC(completionDC) {
61+
while (auto *ACE = dyn_cast<AbstractClosureExpr>(InitialDC))
62+
InitialDC = ACE->getParent();
63+
InitialDC->walkContext(*this);
64+
}
65+
66+
std::pair<bool, Expr *> walkToExprPre(Expr *E) override;
67+
68+
Expr *walkToExprPost(Expr *E) override;
69+
70+
/// Check whether code completion expression is located inside of a
71+
/// multi-statement closure.
72+
bool locatedInMultiStmtClosure() const {
73+
return hasContext(ContextKind::MultiStmtClosure);
74+
}
75+
76+
bool locatedInStringIterpolation() const {
77+
return hasContext(ContextKind::StringInterpolation);
78+
}
79+
80+
bool hasCompletionExpr() const {
81+
return CompletionNode.dyn_cast<CodeCompletionExpr *>() != nullptr;
82+
}
83+
84+
CodeCompletionExpr *getCompletionExpr() const {
85+
assert(hasCompletionExpr());
86+
return CompletionNode.get<CodeCompletionExpr *>();
87+
}
88+
89+
bool hasCompletionKeyPathComponent() const {
90+
return CompletionNode.dyn_cast<const KeyPathExpr *>() != nullptr;
91+
}
92+
93+
/// If we are completing in a key path, returns the \c KeyPath that contains
94+
/// the code completion component.
95+
const KeyPathExpr *getKeyPathContainingCompletionComponent() const {
96+
assert(hasCompletionKeyPathComponent());
97+
return CompletionNode.get<const KeyPathExpr *>();
98+
}
99+
100+
/// If we are completing in a key path, returns the index at which the key
101+
/// path has the code completion component.
102+
size_t getKeyPathCompletionComponentIndex() const;
103+
104+
struct Fallback {
105+
Expr *E; ///< The fallback expression.
106+
DeclContext *DC; ///< The fallback expression's decl context.
107+
bool SeparatePrecheck; ///< True if the fallback may require prechecking.
108+
};
109+
110+
/// As a fallback sometimes its useful to not only type-check
111+
/// code completion expression directly but instead add some
112+
/// of the enclosing context e.g. when completion is an argument
113+
/// to a call.
114+
Optional<Fallback> getFallbackCompletionExpr() const;
115+
116+
private:
117+
bool hasContext(ContextKind kind) const {
118+
return llvm::find_if(Contexts, [&kind](const Context &currContext) {
119+
return currContext.Kind == kind;
120+
}) != Contexts.end();
121+
}
122+
};
123+
124+
} // end namespace swift
125+
126+
#endif // SWIFT_SEMA_COMPLETIONCONTEXTFINDER_H

lib/Sema/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ add_swift_host_library(swiftSema STATIC
1818
ConstraintGraph.cpp
1919
ConstraintLocator.cpp
2020
ConstraintSystem.cpp
21+
CompletionContextFinder.cpp
2122
DebuggerTestingTransform.cpp
2223
DerivedConformanceActor.cpp
2324
DerivedConformanceDistributedActor.cpp

lib/Sema/CompletionContextFinder.cpp

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
//===--- CompletionContextFinder.cpp --------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2022 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#include "swift/Sema/CompletionContextFinder.h"
14+
15+
using namespace swift;
16+
using Fallback = CompletionContextFinder::Fallback;
17+
18+
std::pair<bool, Expr *> CompletionContextFinder::walkToExprPre(Expr *E) {
19+
if (auto *closure = dyn_cast<ClosureExpr>(E)) {
20+
Contexts.push_back({closure->hasSingleExpressionBody()
21+
? ContextKind::SingleStmtClosure
22+
: ContextKind::MultiStmtClosure,
23+
closure});
24+
}
25+
26+
if (isa<InterpolatedStringLiteralExpr>(E)) {
27+
Contexts.push_back({ContextKind::StringInterpolation, E});
28+
}
29+
30+
if (isa<ApplyExpr>(E) || isa<SequenceExpr>(E)) {
31+
Contexts.push_back({ContextKind::FallbackExpression, E});
32+
}
33+
34+
if (auto *Error = dyn_cast<ErrorExpr>(E)) {
35+
Contexts.push_back({ContextKind::ErrorExpression, E});
36+
if (auto *OrigExpr = Error->getOriginalExpr()) {
37+
OrigExpr->walk(*this);
38+
if (hasCompletionExpr())
39+
return std::make_pair(false, nullptr);
40+
}
41+
}
42+
43+
if (auto *CCE = dyn_cast<CodeCompletionExpr>(E)) {
44+
CompletionNode = CCE;
45+
return std::make_pair(false, nullptr);
46+
}
47+
if (auto *KeyPath = dyn_cast<KeyPathExpr>(E)) {
48+
for (auto &component : KeyPath->getComponents()) {
49+
if (component.getKind() == KeyPathExpr::Component::Kind::CodeCompletion) {
50+
CompletionNode = KeyPath;
51+
return std::make_pair(false, nullptr);
52+
}
53+
}
54+
// Code completion in key paths is modelled by a code completion component
55+
// Don't walk the key path's parsed expressions.
56+
return std::make_pair(false, E);
57+
}
58+
59+
return std::make_pair(true, E);
60+
}
61+
62+
Expr *CompletionContextFinder::walkToExprPost(Expr *E) {
63+
if (isa<ClosureExpr>(E) || isa<InterpolatedStringLiteralExpr>(E) ||
64+
isa<ApplyExpr>(E) || isa<SequenceExpr>(E) || isa<ErrorExpr>(E)) {
65+
assert(Contexts.back().E == E);
66+
Contexts.pop_back();
67+
}
68+
return E;
69+
}
70+
71+
size_t CompletionContextFinder::getKeyPathCompletionComponentIndex() const {
72+
assert(hasCompletionKeyPathComponent());
73+
size_t ComponentIndex = 0;
74+
auto Components = getKeyPathContainingCompletionComponent()->getComponents();
75+
for (auto &Component : Components) {
76+
if (Component.getKind() == KeyPathExpr::Component::Kind::CodeCompletion) {
77+
break;
78+
} else {
79+
ComponentIndex++;
80+
}
81+
}
82+
assert(ComponentIndex < Components.size() &&
83+
"No completion component in the key path?");
84+
return ComponentIndex;
85+
}
86+
87+
Optional<Fallback> CompletionContextFinder::getFallbackCompletionExpr() const {
88+
if (!hasCompletionExpr()) {
89+
// Creating a fallback expression only makes sense if we are completing in
90+
// an expression, not when we're completing in a key path.
91+
return None;
92+
}
93+
94+
Optional<Fallback> fallback;
95+
bool separatePrecheck = false;
96+
DeclContext *fallbackDC = InitialDC;
97+
98+
// Find the outermost fallback expression within the innermost error
99+
// expression or multi-statement closure, keeping track of its decl context.
100+
for (auto context : Contexts) {
101+
switch (context.Kind) {
102+
case ContextKind::StringInterpolation:
103+
LLVM_FALLTHROUGH;
104+
case ContextKind::FallbackExpression:
105+
if (!fallback && context.E != InitialExpr)
106+
fallback = Fallback{context.E, fallbackDC, separatePrecheck};
107+
continue;
108+
109+
case ContextKind::SingleStmtClosure:
110+
if (!fallback && context.E != InitialExpr)
111+
fallback = Fallback{context.E, fallbackDC, separatePrecheck};
112+
fallbackDC = cast<AbstractClosureExpr>(context.E);
113+
continue;
114+
115+
case ContextKind::MultiStmtClosure:
116+
fallbackDC = cast<AbstractClosureExpr>(context.E);
117+
LLVM_FALLTHROUGH;
118+
case ContextKind::ErrorExpression:;
119+
fallback = None;
120+
separatePrecheck = true;
121+
continue;
122+
}
123+
}
124+
125+
if (fallback)
126+
return fallback;
127+
128+
if (getCompletionExpr()->getBase() && getCompletionExpr() != InitialExpr)
129+
return Fallback{getCompletionExpr(), fallbackDC, separatePrecheck};
130+
return None;
131+
}

0 commit comments

Comments
 (0)