Skip to content

Commit 3af5bd1

Browse files
authored
Merge pull request #19950 from rintaro/ide-completion-accessor-rdar20957182
[CodeCompletion] Implement completion for 'get', 'set', 'willSet', 'didSet'
2 parents a640fe7 + 90fe0a7 commit 3af5bd1

File tree

6 files changed

+376
-56
lines changed

6 files changed

+376
-56
lines changed

include/swift/IDE/CodeCompletion.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -495,6 +495,7 @@ enum class CompletionKind {
495495
CaseStmtBeginning,
496496
CaseStmtDotPrefix,
497497
NominalMemberBeginning,
498+
AccessorBeginning,
498499
AttributeBegin,
499500
AttributeDeclParen,
500501
PoundAvailablePlatform,

include/swift/Parse/CodeCompletionCallbacks.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,9 @@ class CodeCompletionCallbacks {
176176
virtual void completeNominalMemberBeginning(
177177
SmallVectorImpl<StringRef> &Keywords) = 0;
178178

179+
/// Complete at the beginning of accessor in a accessor block.
180+
virtual void completeAccessorBeginning() = 0;
181+
179182
/// Complete the keyword in attribute, for instance, @available.
180183
virtual void completeDeclAttrKeyword(Decl *D, bool Sil, bool Param) = 0;
181184

include/swift/Parse/Parser.h

Lines changed: 14 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -894,30 +894,23 @@ class Parser {
894894
void consumeGetSetBody(AbstractFunctionDecl *AFD, SourceLoc LBLoc);
895895

896896
struct ParsedAccessors;
897-
bool parseGetSetImpl(ParseDeclOptions Flags,
898-
GenericParamList *GenericParams,
899-
ParameterList *Indices,
900-
TypeLoc ElementTy,
901-
ParsedAccessors &accessors,
902-
AbstractStorageDecl *storage,
903-
SourceLoc &LastValidLoc,
904-
SourceLoc StaticLoc, SourceLoc VarLBLoc);
905-
bool parseGetSet(ParseDeclOptions Flags,
906-
GenericParamList *GenericParams,
907-
ParameterList *Indices,
908-
TypeLoc ElementTy,
909-
ParsedAccessors &accessors,
910-
AbstractStorageDecl *storage,
911-
SourceLoc StaticLoc);
897+
ParserStatus parseGetSet(ParseDeclOptions Flags,
898+
GenericParamList *GenericParams,
899+
ParameterList *Indices,
900+
TypeLoc ElementTy,
901+
ParsedAccessors &accessors,
902+
AbstractStorageDecl *storage,
903+
SourceLoc StaticLoc);
912904
void recordAccessors(AbstractStorageDecl *storage, ParseDeclOptions flags,
913905
TypeLoc elementTy, const DeclAttributes &attrs,
914906
SourceLoc staticLoc, ParsedAccessors &accessors);
915-
void parseAccessorBodyDelayed(AbstractFunctionDecl *AFD);
916-
VarDecl *parseDeclVarGetSet(Pattern *pattern, ParseDeclOptions Flags,
917-
SourceLoc StaticLoc, SourceLoc VarLoc,
918-
bool hasInitializer,
919-
const DeclAttributes &Attributes,
920-
SmallVectorImpl<Decl *> &Decls);
907+
ParserResult<VarDecl> parseDeclVarGetSet(Pattern *pattern,
908+
ParseDeclOptions Flags,
909+
SourceLoc StaticLoc,
910+
SourceLoc VarLoc,
911+
bool hasInitializer,
912+
const DeclAttributes &Attributes,
913+
SmallVectorImpl<Decl *> &Decls);
921914

922915
void consumeAbstractFunctionBody(AbstractFunctionDecl *AFD,
923916
const DeclAttributes &Attrs);

lib/IDE/CodeCompletion.cpp

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1461,6 +1461,7 @@ class CodeCompletionCallbacksImpl : public CodeCompletionCallbacks {
14611461
void completeInPrecedenceGroup(SyntaxKind SK) override;
14621462
void completeNominalMemberBeginning(
14631463
SmallVectorImpl<StringRef> &Keywords) override;
1464+
void completeAccessorBeginning() override;
14641465

14651466
void completePoundAvailablePlatform() override;
14661467
void completeImportDecl(std::vector<std::pair<Identifier, SourceLoc>> &Path) override;
@@ -4819,6 +4820,11 @@ void CodeCompletionCallbacksImpl::completeNominalMemberBeginning(
48194820
CurDeclContext = P.CurDeclContext;
48204821
}
48214822

4823+
void CodeCompletionCallbacksImpl::completeAccessorBeginning() {
4824+
Kind = CompletionKind::AccessorBeginning;
4825+
CurDeclContext = P.CurDeclContext;
4826+
}
4827+
48224828
static bool isDynamicLookup(Type T) {
48234829
return T->getRValueType()->isAnyObject();
48244830
}
@@ -4887,6 +4893,16 @@ static void addLetVarKeywords(CodeCompletionResultSink &Sink) {
48874893
addKeyword(Sink, "var", CodeCompletionKeywordKind::kw_var);
48884894
}
48894895

4896+
static void addAccessorKeywords(CodeCompletionResultSink &Sink) {
4897+
addKeyword(Sink, "get", CodeCompletionKeywordKind::None);
4898+
addKeyword(Sink, "set", CodeCompletionKeywordKind::None);
4899+
}
4900+
4901+
static void addObserverKeywords(CodeCompletionResultSink &Sink) {
4902+
addKeyword(Sink, "willSet", CodeCompletionKeywordKind::None);
4903+
addKeyword(Sink, "didSet", CodeCompletionKeywordKind::None);
4904+
}
4905+
48904906
static void addExprKeywords(CodeCompletionResultSink &Sink) {
48914907
// Expr keywords.
48924908
addKeyword(Sink, "try", CodeCompletionKeywordKind::kw_try);
@@ -4927,6 +4943,25 @@ void CodeCompletionCallbacksImpl::addKeywords(CodeCompletionResultSink &Sink,
49274943
case CompletionKind::PrecedenceGroup:
49284944
break;
49294945

4946+
case CompletionKind::AccessorBeginning: {
4947+
// TODO: Omit already declared or mutally exclusive accessors.
4948+
// E.g. If 'get' is already declared, emit 'set' only.
4949+
addAccessorKeywords(Sink);
4950+
4951+
// Only 'var' for non-protocol context can have 'willSet' and 'didSet'.
4952+
assert(ParsedDecl);
4953+
VarDecl *var = dyn_cast<VarDecl>(ParsedDecl);
4954+
if (auto accessor = dyn_cast<AccessorDecl>(ParsedDecl))
4955+
var = dyn_cast<VarDecl>(accessor->getStorage());
4956+
if (var && !var->getDeclContext()->getSelfProtocolDecl())
4957+
addObserverKeywords(Sink);
4958+
4959+
if (!isa<AccessorDecl>(ParsedDecl))
4960+
break;
4961+
4962+
MaybeFuncBody = true;
4963+
LLVM_FALLTHROUGH;
4964+
}
49304965
case CompletionKind::StmtOrExpr:
49314966
addDeclKeywords(Sink);
49324967
addStmtKeywords(Sink, MaybeFuncBody);
@@ -5667,6 +5702,13 @@ void CodeCompletionCallbacksImpl::doneParsing() {
56675702
OverrideLookup.getOverrideCompletions(SourceLoc());
56685703
break;
56695704
}
5705+
5706+
case CompletionKind::AccessorBeginning: {
5707+
if (isa<AccessorDecl>(ParsedDecl))
5708+
DoPostfixExprBeginning();
5709+
break;
5710+
}
5711+
56705712
case CompletionKind::AttributeBegin: {
56715713
Lookup.getAttributeDeclCompletions(IsInSil, AttTargetDK);
56725714
break;

lib/Parse/ParseDecl.cpp

Lines changed: 66 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -4212,10 +4212,12 @@ struct Parser::ParsedAccessors {
42124212
}
42134213
};
42144214

4215-
static bool parseAccessorIntroducer(Parser &P, DeclAttributes &Attributes,
4215+
static bool parseAccessorIntroducer(Parser &P,
4216+
DeclAttributes &Attributes,
42164217
AccessorKind &Kind,
42174218
AddressorKind &addressorKind,
42184219
SourceLoc &Loc) {
4220+
assert(Attributes.isEmpty());
42194221
bool FoundCCToken;
42204222
P.parseDeclAttributeList(Attributes, FoundCCToken);
42214223

@@ -4258,12 +4260,12 @@ static bool parseAccessorIntroducer(Parser &P, DeclAttributes &Attributes,
42584260
return false;
42594261
}
42604262

4261-
bool Parser::parseGetSet(ParseDeclOptions Flags,
4262-
GenericParamList *GenericParams,
4263-
ParameterList *Indices,
4264-
TypeLoc ElementTy, ParsedAccessors &accessors,
4265-
AbstractStorageDecl *storage,
4266-
SourceLoc StaticLoc) {
4263+
ParserStatus Parser::parseGetSet(ParseDeclOptions Flags,
4264+
GenericParamList *GenericParams,
4265+
ParameterList *Indices,
4266+
TypeLoc ElementTy, ParsedAccessors &accessors,
4267+
AbstractStorageDecl *storage,
4268+
SourceLoc StaticLoc) {
42674269
assert(Tok.is(tok::l_brace));
42684270

42694271
// Properties in protocols use a very limited syntax.
@@ -4288,14 +4290,14 @@ bool Parser::parseGetSet(ParseDeclOptions Flags,
42884290

42894291
// In the limited syntax, fall out and let the caller handle it.
42904292
if (parsingLimitedSyntax)
4291-
return false;
4293+
return makeParserSuccess();
42924294

42934295
diagnose(accessors.RBLoc, diag::computed_property_no_accessors,
42944296
/*subscript*/ Indices != nullptr);
4295-
return true;
4297+
return makeParserError();
42964298
}
42974299

4298-
auto parseImplicitGetter = [&]() -> bool {
4300+
auto parseImplicitGetter = [&]() {
42994301
assert(Tok.is(tok::l_brace));
43004302
accessors.LBLoc = Tok.getLoc();
43014303
auto getter =
@@ -4306,14 +4308,14 @@ bool Parser::parseGetSet(ParseDeclOptions Flags,
43064308
accessors.add(getter);
43074309
parseAbstractFunctionBody(getter);
43084310
accessors.RBLoc = getter->getEndLoc();
4309-
return false;
43104311
};
43114312

43124313
// Prepare backtracking for implicit getter.
43134314
Optional<BacktrackingScope> backtrack;
43144315
backtrack.emplace(*this);
43154316

43164317
bool Invalid = false;
4318+
bool accessorHasCodeCompletion = false;
43174319
bool IsFirstAccessor = true;
43184320
accessors.LBLoc = consumeToken(tok::l_brace);
43194321
while (!Tok.isAny(tok::r_brace, tok::eof)) {
@@ -4331,6 +4333,29 @@ bool Parser::parseGetSet(ParseDeclOptions Flags,
43314333
AccessorCtx->setTransparent();
43324334
AccessorCtx.reset();
43334335

4336+
if (Tok.is(tok::code_complete)) {
4337+
if (CodeCompletion) {
4338+
if (IsFirstAccessor && !parsingLimitedSyntax) {
4339+
// If CC token is the first token after '{', it might be implicit
4340+
// getter. Set up dummy accessor as the decl context to populate
4341+
// 'self' decl.
4342+
auto getter = createAccessorFunc(
4343+
accessors.LBLoc, /*ValueNamePattern*/ nullptr, GenericParams,
4344+
Indices, ElementTy, StaticLoc, Flags, AccessorKind::Get,
4345+
AddressorKind::NotAddressor, storage, this,
4346+
/*AccessorKeywordLoc*/ SourceLoc());
4347+
accessors.add(getter);
4348+
CodeCompletion->setParsedDecl(getter);
4349+
} else {
4350+
CodeCompletion->setParsedDecl(storage);
4351+
}
4352+
CodeCompletion->completeAccessorBeginning();
4353+
}
4354+
consumeToken(tok::code_complete);
4355+
accessorHasCodeCompletion = true;
4356+
break;
4357+
}
4358+
43344359
// parsingLimitedSyntax mode cannot have a body.
43354360
if (parsingLimitedSyntax) {
43364361
diagnose(Tok, diag::expected_getset_in_protocol);
@@ -4350,7 +4375,8 @@ bool Parser::parseGetSet(ParseDeclOptions Flags,
43504375
// position.
43514376
backtrack.reset();
43524377
AccessorListCtx.setTransparent();
4353-
return parseImplicitGetter();
4378+
parseImplicitGetter();
4379+
return makeParserSuccess();
43544380
}
43554381
IsFirstAccessor = false;
43564382

@@ -4415,7 +4441,9 @@ bool Parser::parseGetSet(ParseDeclOptions Flags,
44154441

44164442
parseMatchingToken(tok::r_brace, accessors.RBLoc,
44174443
diag::expected_rbrace_in_getset, accessors.LBLoc);
4418-
return Invalid;
4444+
if (accessorHasCodeCompletion)
4445+
return makeParserCodeCompletionStatus();
4446+
return Invalid ? makeParserError() : makeParserSuccess();
44194447
}
44204448

44214449
static void fillInAccessorTypeErrors(Parser &P, FuncDecl *accessor,
@@ -4461,12 +4489,12 @@ static void fillInAccessorTypeErrors(Parser &P,
44614489
}
44624490

44634491
/// \brief Parse the brace-enclosed getter and setter for a variable.
4464-
VarDecl *Parser::parseDeclVarGetSet(Pattern *pattern,
4465-
ParseDeclOptions Flags,
4466-
SourceLoc StaticLoc, SourceLoc VarLoc,
4467-
bool hasInitializer,
4468-
const DeclAttributes &Attributes,
4469-
SmallVectorImpl<Decl *> &Decls) {
4492+
ParserResult<VarDecl>
4493+
Parser::parseDeclVarGetSet(Pattern *pattern, ParseDeclOptions Flags,
4494+
SourceLoc StaticLoc, SourceLoc VarLoc,
4495+
bool hasInitializer,
4496+
const DeclAttributes &Attributes,
4497+
SmallVectorImpl<Decl *> &Decls) {
44704498
bool Invalid = false;
44714499

44724500
// The grammar syntactically requires a simple identifier for the variable
@@ -4539,8 +4567,12 @@ VarDecl *Parser::parseDeclVarGetSet(Pattern *pattern,
45394567

45404568
// Parse getter and setter.
45414569
ParsedAccessors accessors;
4542-
if (parseGetSet(Flags, /*GenericParams=*/nullptr,
4543-
/*Indices=*/nullptr, TyLoc, accessors, storage, StaticLoc))
4570+
auto AccessorStatus = parseGetSet(Flags, /*GenericParams=*/nullptr,
4571+
/*Indices=*/nullptr, TyLoc, accessors,
4572+
storage, StaticLoc);
4573+
if (AccessorStatus.hasCodeCompletion())
4574+
return makeParserCodeCompletionStatus();
4575+
if (AccessorStatus.isError())
45444576
Invalid = true;
45454577

45464578
// If we have an invalid case, bail out now.
@@ -4588,7 +4620,7 @@ VarDecl *Parser::parseDeclVarGetSet(Pattern *pattern,
45884620
accessors.record(*this, PrimaryVar, Invalid, Flags, StaticLoc,
45894621
Attributes, TyLoc, /*indices*/ nullptr, Decls);
45904622

4591-
return PrimaryVar;
4623+
return makeParserResult(PrimaryVar);
45924624
}
45934625

45944626
/// Add the given accessor to the collection of parsed accessors. If
@@ -5124,16 +5156,16 @@ Parser::parseDeclVar(ParseDeclOptions Flags,
51245156
// var-get-set clause, parse the var-get-set clause.
51255157
} else if (Tok.is(tok::l_brace)) {
51265158
HasAccessors = true;
5127-
5128-
if (auto *boundVar = parseDeclVarGetSet(pattern, Flags,
5129-
StaticLoc, VarLoc,
5130-
PatternInit != nullptr,
5131-
Attributes, Decls)) {
5132-
if (PatternInit && !boundVar->hasStorage()) {
5133-
diagnose(pattern->getLoc(), diag::getset_init)
5159+
auto boundVar = parseDeclVarGetSet(pattern, Flags, StaticLoc, VarLoc,
5160+
PatternInit != nullptr,Attributes,
5161+
Decls);
5162+
if (boundVar.hasCodeCompletion())
5163+
return makeResult(makeParserCodeCompletionStatus());
5164+
if (PatternInit && boundVar.isNonNull() &&
5165+
!boundVar.get()->hasStorage()) {
5166+
diagnose(pattern->getLoc(), diag::getset_init)
51345167
.highlight(PatternInit->getSourceRange());
5135-
PatternInit = nullptr;
5136-
}
5168+
PatternInit = nullptr;
51375169
}
51385170
}
51395171

@@ -6225,10 +6257,9 @@ Parser::parseDeclSubscript(ParseDeclOptions Flags,
62256257
Status.setIsParseError();
62266258
}
62276259
} else {
6228-
if (parseGetSet(Flags, GenericParams,
6229-
Indices.get(), ElementTy.get(),
6230-
accessors, Subscript, /*StaticLoc=*/SourceLoc()))
6231-
Status.setIsParseError();
6260+
Status |= parseGetSet(Flags, GenericParams,
6261+
Indices.get(), ElementTy.get(),
6262+
accessors, Subscript, /*StaticLoc=*/SourceLoc());
62326263
}
62336264

62346265
bool Invalid = false;

0 commit comments

Comments
 (0)