Skip to content

Commit a91cede

Browse files
author
Nathan Hawes
committed
[Parse][CodeCompletion] Don't special case code completion when forming single-expression closures/function bodies (NFC)
Code completion used to avoid forming single expression closures/function bodies when the single expression contained the code completion expression because a contextual type mismatch could result in types not being applied to the AST, giving no completions. Completions that have been migrated to the new solver-based completion mechanism don't need this behavior, however. Rather than trying to guess whether the type of completion we're going to end up performing is one of the ones that haven't been migrated to the solver yet when parsing, instead just always form single-expression closures/function bodies (like we do for regular compilation) and undo the transformation if and when we know we're going to perform a completion kind we haven't migrated yet. Once all completion kinds are migrated, the undo-ing code can be removed.
1 parent a665ba6 commit a91cede

File tree

5 files changed

+41
-71
lines changed

5 files changed

+41
-71
lines changed

include/swift/Parse/Parser.h

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -709,15 +709,6 @@ class Parser {
709709
Context.LangOpts.ParseForSyntaxTreeOnly;
710710
}
711711

712-
/// If a function or closure body consists of a single expression, determine
713-
/// whether we should turn wrap it in a return statement or not.
714-
///
715-
/// We don't do this transformation for non-solver-based code completion
716-
/// positions, as the source may be incomplete and the type mismatch in the
717-
/// return statement will just confuse the type checker.
718-
bool shouldSuppressSingleExpressionBodyTransform(
719-
ParserStatus Status, MutableArrayRef<ASTNode> BodyElems);
720-
721712
public:
722713
InFlightDiagnostic diagnose(SourceLoc Loc, Diagnostic Diag) {
723714
if (Diags.isDiagnosticPointsToFirstBadToken(Diag.getID()) &&

lib/IDE/CodeCompletion.cpp

Lines changed: 39 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1672,7 +1672,7 @@ class CodeCompletionCallbacksImpl : public CodeCompletionCallbacks {
16721672

16731673
private:
16741674
void addKeywords(CodeCompletionResultSink &Sink, bool MaybeFuncBody);
1675-
bool trySolverCompletion();
1675+
bool trySolverCompletion(bool MaybeFuncBody);
16761676
};
16771677
} // end anonymous namespace
16781678

@@ -6054,25 +6054,7 @@ void deliverDotExprResults(
60546054
deliverCompletionResults(CompletionCtx, Lookup, *SF, Consumer);
60556055
}
60566056

6057-
bool CodeCompletionCallbacksImpl::trySolverCompletion() {
6058-
CompletionContext.CodeCompletionKind = Kind;
6059-
6060-
if (Kind == CompletionKind::None)
6061-
return true;
6062-
6063-
bool MaybeFuncBody = true;
6064-
if (CurDeclContext) {
6065-
auto *CD = CurDeclContext->getLocalContext();
6066-
if (!CD || CD->getContextKind() == DeclContextKind::Initializer ||
6067-
CD->getContextKind() == DeclContextKind::TopLevelCodeDecl)
6068-
MaybeFuncBody = false;
6069-
}
6070-
6071-
if (auto *DC = dyn_cast_or_null<DeclContext>(ParsedDecl)) {
6072-
if (DC->isChildContextOf(CurDeclContext))
6073-
CurDeclContext = DC;
6074-
}
6075-
6057+
bool CodeCompletionCallbacksImpl::trySolverCompletion(bool MaybeFuncBody) {
60766058
assert(ParsedExpr || CurDeclContext);
60776059

60786060
SourceLoc CompletionLoc = ParsedExpr
@@ -6111,12 +6093,42 @@ bool CodeCompletionCallbacksImpl::trySolverCompletion() {
61116093
}
61126094
}
61136095

6096+
// Undoes the single-expression closure/function body transformation on the
6097+
// given DeclContext and its parent contexts if they have a single expression
6098+
// body that contains the code completion location.
6099+
//
6100+
// FIXME: Remove this once all expression position completions are migrated
6101+
// to work via TypeCheckCompletionCallback.
6102+
static void undoSingleExpressionReturn(DeclContext *DC) {
6103+
auto updateBody = [](BraceStmt *BS, ASTContext &Ctx) -> bool {
6104+
ASTNode FirstElem = BS->getFirstElement();
6105+
auto *RS = dyn_cast_or_null<ReturnStmt>(FirstElem.dyn_cast<Stmt*>());
6106+
6107+
if (!RS || !RS->isImplicit())
6108+
return false;
6109+
6110+
BS->setFirstElement(RS->getResult());
6111+
return true;
6112+
};
6113+
6114+
while (ClosureExpr *CE = dyn_cast_or_null<ClosureExpr>(DC)) {
6115+
if (CE->hasSingleExpressionBody()) {
6116+
if (updateBody(CE->getBody(), CE->getASTContext()))
6117+
CE->setBody(CE->getBody(), false);
6118+
}
6119+
DC = DC->getParent();
6120+
}
6121+
if (FuncDecl *FD = dyn_cast_or_null<FuncDecl>(DC)) {
6122+
if (FD->hasSingleExpressionBody()) {
6123+
if (updateBody(FD->getBody(), FD->getASTContext()))
6124+
FD->setHasSingleExpressionBody(false);
6125+
}
6126+
}
6127+
}
6128+
61146129
void CodeCompletionCallbacksImpl::doneParsing() {
61156130
CompletionContext.CodeCompletionKind = Kind;
61166131

6117-
if (trySolverCompletion())
6118-
return;
6119-
61206132
if (Kind == CompletionKind::None) {
61216133
return;
61226134
}
@@ -6134,6 +6146,10 @@ void CodeCompletionCallbacksImpl::doneParsing() {
61346146
CurDeclContext = DC;
61356147
}
61366148

6149+
if (trySolverCompletion(MaybeFuncBody))
6150+
return;
6151+
6152+
undoSingleExpressionReturn(CurDeclContext);
61376153
typeCheckContextAt(
61386154
CurDeclContext,
61396155
ParsedExpr

lib/Parse/ParseDecl.cpp

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6588,11 +6588,7 @@ BraceStmt *Parser::parseAbstractFunctionBodyImpl(AbstractFunctionDecl *AFD) {
65886588

65896589
// If the body consists of a single expression, turn it into a return
65906590
// statement.
6591-
//
6592-
// But don't do this transformation when performing certain kinds of code
6593-
// completion, as the source may be incomplete and the type mismatch in return
6594-
// statement will just confuse the type checker.
6595-
if (shouldSuppressSingleExpressionBodyTransform(Body, BS->getElements()))
6591+
if (BS->getNumElements() != 1)
65966592
return BS;
65976593

65986594
auto Element = BS->getFirstElement();

lib/Parse/ParseExpr.cpp

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2841,13 +2841,8 @@ ParserResult<Expr> Parser::parseExprClosure() {
28412841

28422842
// If the body consists of a single expression, turn it into a return
28432843
// statement.
2844-
//
2845-
// But don't do this transformation when performing certain kinds of code
2846-
// completion, as the source may be incomplete and the type mismatch in return
2847-
// statement will just confuse the type checker.
28482844
bool hasSingleExpressionBody = false;
2849-
if (!missingRBrace &&
2850-
!shouldSuppressSingleExpressionBodyTransform(Status, bodyElements)) {
2845+
if (!missingRBrace && bodyElements.size() == 1) {
28512846
// If the closure's only body element is a single return statement,
28522847
// use that instead of creating a new wrapping return expression.
28532848
Expr *returnExpr = nullptr;

lib/Parse/Parser.cpp

Lines changed: 0 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1185,34 +1185,6 @@ Parser::getStringLiteralIfNotInterpolated(SourceLoc Loc,
11851185
Segments.front().Length));
11861186
}
11871187

1188-
bool Parser::
1189-
shouldSuppressSingleExpressionBodyTransform(ParserStatus Status,
1190-
MutableArrayRef<ASTNode> BodyElems) {
1191-
if (BodyElems.size() != 1)
1192-
return true;
1193-
1194-
if (!Status.hasCodeCompletion())
1195-
return false;
1196-
1197-
struct HasMemberCompletion: public ASTWalker {
1198-
bool Value = false;
1199-
std::pair<bool, Expr *> walkToExprPre(Expr *E) override {
1200-
if (auto *CCE = dyn_cast<CodeCompletionExpr>(E)) {
1201-
// If it has a base expression this is member completion, which is
1202-
// performed using the new solver-based mechanism, so it's ok to go
1203-
// ahead with the transform (and necessary to pick up the correct
1204-
// expected type).
1205-
Value = CCE->getBase();
1206-
return {false, nullptr};
1207-
}
1208-
return {true, E};
1209-
}
1210-
};
1211-
HasMemberCompletion Check;
1212-
BodyElems.front().walk(Check);
1213-
return !Check.Value;
1214-
}
1215-
12161188
struct ParserUnit::Implementation {
12171189
std::shared_ptr<SyntaxParseActions> SPActions;
12181190
LangOptions LangOpts;

0 commit comments

Comments
 (0)