Skip to content

Commit 63772d0

Browse files
authored
Merge pull request swiftlang#29789 from rintaro/ide-completion-rdar58851121
[CodeCompletion] Enable fast-completion at the top of implicit getter
2 parents 8c92cfd + 7b4db90 commit 63772d0

File tree

7 files changed

+97
-63
lines changed

7 files changed

+97
-63
lines changed

include/swift/AST/Decl.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6417,6 +6417,10 @@ class AccessorDecl final : public FuncDecl {
64176417
llvm_unreachable("bad accessor kind");
64186418
}
64196419

6420+
bool isImplicitGetter() const {
6421+
return isGetter() && getAccessorKeywordLoc().isInvalid();
6422+
}
6423+
64206424
void setIsTransparent(bool transparent) {
64216425
Bits.AccessorDecl.IsTransparent = transparent;
64226426
Bits.AccessorDecl.IsTransparentComputed = 1;

include/swift/Parse/Parser.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1122,6 +1122,8 @@ class Parser {
11221122
ParseDeclOptions Flags,
11231123
DeclAttributes &Attributes,
11241124
bool HasFuncKeyword = true);
1125+
ParserResult<BraceStmt>
1126+
parseAbstractFunctionBodyImpl(AbstractFunctionDecl *AFD);
11251127
void parseAbstractFunctionBody(AbstractFunctionDecl *AFD);
11261128
BraceStmt *parseAbstractFunctionBodyDelayed(AbstractFunctionDecl *AFD);
11271129
ParserResult<ProtocolDecl> parseDeclProtocol(ParseDeclOptions Flags,

lib/AST/Decl.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -520,7 +520,7 @@ SourceRange Decl::getSourceRangeIncludingAttrs() const {
520520
// e.g. 'override'.
521521
if (auto *AD = dyn_cast<AccessorDecl>(this)) {
522522
// If this is implicit getter, accessor range should not include attributes.
523-
if (!AD->getAccessorKeywordLoc().isValid())
523+
if (AD->isImplicitGetter())
524524
return Range;
525525

526526
// Otherwise, include attributes directly attached to the accessor.
@@ -5937,7 +5937,7 @@ void VarDecl::emitLetToVarNoteIfSimple(DeclContext *UseDC) const {
59375937
->hasReferenceSemantics()) {
59385938
// Do not suggest the fix-it in implicit getters
59395939
if (auto AD = dyn_cast<AccessorDecl>(FD)) {
5940-
if (AD->isGetter() && !AD->getAccessorKeywordLoc().isValid())
5940+
if (AD->isImplicitGetter())
59415941
return;
59425942
}
59435943

lib/Parse/ParseDecl.cpp

Lines changed: 52 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -5488,35 +5488,18 @@ ParserStatus Parser::parseGetSet(ParseDeclOptions Flags,
54885488
AccessorCtx.reset();
54895489

54905490
if (Tok.is(tok::code_complete)) {
5491-
if (CodeCompletion) {
5492-
CodeCompletionExpr *CCE = nullptr;
5493-
if (IsFirstAccessor && !parsingLimitedSyntax) {
5494-
// If CC token is the first token after '{', it might be implicit
5495-
// getter. Set up dummy accessor as the decl context to populate
5496-
// 'self' decl.
5497-
5498-
// FIXME: if there is already code inside the body, we should fall
5499-
// through to parseImplicitGetter and handle the completion there so
5500-
// that we can differentiate a single-expression body from the first
5501-
// expression in a multi-statement body.
5502-
auto getter = createAccessorFunc(
5503-
accessors.LBLoc, /*ValueNamePattern*/ nullptr, GenericParams,
5504-
Indices, StaticLoc, Flags, AccessorKind::Get,
5505-
storage, this, /*AccessorKeywordLoc*/ SourceLoc());
5506-
CCE = new (Context) CodeCompletionExpr(Tok.getLoc());
5507-
getter->setBodyParsed(BraceStmt::create(Context, Tok.getLoc(),
5508-
ASTNode(CCE), Tok.getLoc(),
5509-
/*implicit*/ true));
5510-
accessors.add(getter);
5511-
CodeCompletion->setParsedDecl(getter);
5512-
} else {
5491+
// Handle code completion here only if it's not the first accessor.
5492+
// If it's the first accessor, it's handled in function body parsing
5493+
// because it might be an implicit getter.
5494+
if (!IsFirstAccessor || parsingLimitedSyntax) {
5495+
if (CodeCompletion) {
55135496
CodeCompletion->setParsedDecl(storage);
5497+
CodeCompletion->completeAccessorBeginning(nullptr);
55145498
}
5515-
CodeCompletion->completeAccessorBeginning(CCE);
5499+
consumeToken(tok::code_complete);
5500+
accessorHasCodeCompletion = true;
5501+
break;
55165502
}
5517-
consumeToken(tok::code_complete);
5518-
accessorHasCodeCompletion = true;
5519-
break;
55205503
}
55215504

55225505
// parsingLimitedSyntax mode cannot have a body.
@@ -6359,7 +6342,47 @@ ParserResult<FuncDecl> Parser::parseDeclFunc(SourceLoc StaticLoc,
63596342
return DCC.fixupParserResult(FD);
63606343
}
63616344

6362-
/// Parse function body into \p AFD.
6345+
/// Parse a function body for \p AFD and returns it without setting the body
6346+
/// to \p AFD .
6347+
ParserResult<BraceStmt>
6348+
Parser::parseAbstractFunctionBodyImpl(AbstractFunctionDecl *AFD) {
6349+
assert(Tok.is(tok::l_brace));
6350+
6351+
// Enter the arguments for the function into a new function-body scope. We
6352+
// need this even if there is no function body to detect argument name
6353+
// duplication.
6354+
if (auto *P = AFD->getImplicitSelfDecl())
6355+
addToScope(P);
6356+
addParametersToScope(AFD->getParameters());
6357+
6358+
// Establish the new context.
6359+
ParseFunctionBody CC(*this, AFD);
6360+
setLocalDiscriminatorToParamList(AFD->getParameters());
6361+
6362+
if (Context.Stats)
6363+
Context.Stats->getFrontendCounters().NumFunctionsParsed++;
6364+
6365+
// In implicit getter, if a CC token is the first token after '{', it might
6366+
// be a start of an accessor block. Perform special completion for that.
6367+
if (auto accessor = dyn_cast<AccessorDecl>(AFD)) {
6368+
if (peekToken().is(tok::code_complete) && accessor->isImplicitGetter()) {
6369+
SourceLoc LBraceLoc, RBraceLoc;
6370+
LBraceLoc = consumeToken(tok::l_brace);
6371+
auto *CCE = new (Context) CodeCompletionExpr(Tok.getLoc());
6372+
CodeCompletion->setParsedDecl(accessor);
6373+
CodeCompletion->completeAccessorBeginning(CCE);
6374+
RBraceLoc = Tok.getLoc();
6375+
consumeToken(tok::code_complete);
6376+
return makeParserCodeCompletionResult(
6377+
BraceStmt::create(Context, LBraceLoc, ASTNode(CCE), RBraceLoc,
6378+
/*implicit*/ true));
6379+
}
6380+
}
6381+
6382+
return parseBraceItemList(diag::invalid_diagnostic);
6383+
}
6384+
6385+
/// Parse function body into \p AFD or skip it for delayed parsing.
63636386
void Parser::parseAbstractFunctionBody(AbstractFunctionDecl *AFD) {
63646387
if (!Tok.is(tok::l_brace)) {
63656388
checkForInputIncomplete();
@@ -6379,21 +6402,7 @@ void Parser::parseAbstractFunctionBody(AbstractFunctionDecl *AFD) {
63796402

63806403
Scope S(this, ScopeKind::FunctionBody);
63816404

6382-
// Enter the arguments for the function into a new function-body scope. We
6383-
// need this even if there is no function body to detect argument name
6384-
// duplication.
6385-
if (auto *P = AFD->getImplicitSelfDecl())
6386-
addToScope(P);
6387-
addParametersToScope(AFD->getParameters());
6388-
6389-
// Establish the new context.
6390-
ParseFunctionBody CC(*this, AFD);
6391-
setLocalDiscriminatorToParamList(AFD->getParameters());
6392-
6393-
if (Context.Stats)
6394-
Context.Stats->getFrontendCounters().NumFunctionsParsed++;
6395-
6396-
ParserResult<BraceStmt> Body = parseBraceItemList(diag::invalid_diagnostic);
6405+
ParserResult<BraceStmt> Body = parseAbstractFunctionBodyImpl(AFD);
63976406
if (!Body.isNull()) {
63986407
BraceStmt * BS = Body.get();
63996408
AFD->setBodyParsed(BS);
@@ -6476,13 +6485,8 @@ BraceStmt *Parser::parseAbstractFunctionBodyDelayed(AbstractFunctionDecl *AFD) {
64766485
// Re-enter the lexical scope.
64776486
Scope TopLevelScope(this, ScopeKind::TopLevel);
64786487
Scope S(this, ScopeKind::FunctionBody);
6479-
if (auto *P = AFD->getImplicitSelfDecl())
6480-
addToScope(P);
6481-
addParametersToScope(AFD->getParameters());
6482-
ParseFunctionBody CC(*this, AFD);
6483-
setLocalDiscriminatorToParamList(AFD->getParameters());
64846488

6485-
return parseBraceItemList(diag::func_decl_without_brace).getPtrOrNull();
6489+
return parseAbstractFunctionBodyImpl(AFD).getPtrOrNull();
64866490
}
64876491

64886492
/// Parse a 'enum' declaration, returning true (and doing no token

lib/Parse/Parser.cpp

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -197,16 +197,7 @@ void Parser::performCodeCompletionSecondPassImpl(
197197

198198
case CodeCompletionDelayedDeclKind::FunctionBody: {
199199
auto *AFD = cast<AbstractFunctionDecl>(DC);
200-
201-
if (auto *P = AFD->getImplicitSelfDecl())
202-
addToScope(P);
203-
addParametersToScope(AFD->getParameters());
204-
205-
ParseFunctionBody CC(*this, AFD);
206-
setLocalDiscriminatorToParamList(AFD->getParameters());
207-
208-
auto result = parseBraceItemList(diag::func_decl_without_brace);
209-
AFD->setBody(result.getPtrOrNull());
200+
AFD->setBodyParsed(parseAbstractFunctionBodyImpl(AFD).getPtrOrNull());
210201
break;
211202
}
212203
}

test/IDE/complete_single_expression_return.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -555,8 +555,8 @@ struct TestSingleExprSubscriptGlobal {
555555
}
556556

557557
// TestSingleExprSubscriptGlobal: Begin completions
558-
// TestSingleExprSubscriptGlobal-DAG: Decl[InstanceMethod]/CurrNominal: str()[#String#];
559-
// TestSingleExprSubscriptGlobal-DAG: Decl[InstanceMethod]/CurrNominal: int()[#Int#];
558+
// TestSingleExprSubscriptGlobal-DAG: Decl[InstanceMethod]/CurrNominal: str()[#String#];
559+
// TestSingleExprSubscriptGlobal-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Identical]: int()[#Int#];
560560
// TestSingleExprSubscriptGlobal-DAG: Decl[InstanceMethod]/CurrNominal: void()[#Void#];
561561
// TestSingleExprSubscriptGlobal: End completions
562562
}

test/SourceKit/CodeComplete/complete_sequence_accessor.swift

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,10 @@ enum S {
4747
// RUN: -req=complete -req-opts=reuseastcontext=1 -pos=27:15 %s -- %s == \
4848
// RUN: -req=complete -req-opts=reuseastcontext=1 -pos=30:9 %s -- %s == \
4949
// RUN: -req=complete -req-opts=reuseastcontext=1 -pos=33:15 %s -- %s == \
50-
// RUN: -req=complete -req-opts=reuseastcontext=1 -pos=34:15 %s -- %s > %t.response
50+
// RUN: -req=complete -req-opts=reuseastcontext=1 -pos=34:15 %s -- %s == \
51+
// RUN: -req=complete -req-opts=reuseastcontext=1 -pos=12:1 %s -- %s == \
52+
// RUN: -req=complete -req-opts=reuseastcontext=1 -pos=23:1 %s -- %s == \
53+
// RUN: -req=complete -req-opts=reuseastcontext=1 -pos=16:1 %s -- %s > %t.response
5154
// RUN: %FileCheck --check-prefix=RESULT %s < %t.response
5255
// RUN: %FileCheck --check-prefix=TRACE %s < %t.response
5356

@@ -114,6 +117,30 @@ enum S {
114117
// RESULT-DAG: key.name: "a"
115118
// RESULT-DAG: key.name: "b"
116119
// RESULT: ]
120+
// accessor top (global var)
121+
// RESULT-LABEL: key.results: [
122+
// RESULT-DAG: key.description: "get"
123+
// RESULT-DAG: key.description: "set"
124+
// RESULT-DAG: key.description: "willSet"
125+
// RESULT-DAG: key.description: "didSet"
126+
// RESULT-DAG: key.description: "Foo"
127+
// RESULT: ]
128+
// accessor top (property)
129+
// RESULT-LABEL: key.results: [
130+
// RESULT-DAG: key.description: "get"
131+
// RESULT-DAG: key.description: "set"
132+
// RESULT-DAG: key.description: "willSet"
133+
// RESULT-DAG: key.description: "didSet"
134+
// RESULT-DAG: key.description: "Foo"
135+
// RESULT: ]
136+
// accessor second (global var)
137+
// RESULT-LABEL: key.results: [
138+
// RESULT-NOT: key.description: "Foo"
139+
// RESULT-DAG: key.description: "get"
140+
// RESULT-DAG: key.description: "set"
141+
// RESULT-DAG: key.description: "willSet"
142+
// RESULT-DAG: key.description: "didSet"
143+
// RESULT: ]
117144

118145

119146
// TRACE-LABEL: key.notification: source.notification.compile-did-finish,
@@ -134,3 +161,9 @@ enum S {
134161
// TRACE: key.description: "completion reusing previous ASTContext (benign diagnostic)"
135162
// TRACE-LABEL: key.notification: source.notification.compile-did-finish,
136163
// TRACE: key.description: "completion reusing previous ASTContext (benign diagnostic)"
164+
// TRACE-LABEL: key.notification: source.notification.compile-did-finish,
165+
// TRACE: key.description: "completion reusing previous ASTContext (benign diagnostic)"
166+
// TRACE-LABEL: key.notification: source.notification.compile-did-finish,
167+
// TRACE: key.description: "completion reusing previous ASTContext (benign diagnostic)"
168+
// TRACE-LABEL: key.notification: source.notification.compile-did-finish,
169+
// TRACE-NOT: key.description: "completion reusing previous ASTContext (benign diagnostic)"

0 commit comments

Comments
 (0)