Skip to content

[CodeCompletion] Implement completion for 'get', 'set', 'willSet', 'didSet' #19950

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions include/swift/IDE/CodeCompletion.h
Original file line number Diff line number Diff line change
Expand Up @@ -495,6 +495,7 @@ enum class CompletionKind {
CaseStmtBeginning,
CaseStmtDotPrefix,
NominalMemberBeginning,
AccessorBeginning,
AttributeBegin,
AttributeDeclParen,
PoundAvailablePlatform,
Expand Down
3 changes: 3 additions & 0 deletions include/swift/Parse/CodeCompletionCallbacks.h
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,9 @@ class CodeCompletionCallbacks {
virtual void completeNominalMemberBeginning(
SmallVectorImpl<StringRef> &Keywords) = 0;

/// Complete at the beginning of accessor in a accessor block.
virtual void completeAccessorBeginning() = 0;

/// Complete the keyword in attribute, for instance, @available.
virtual void completeDeclAttrKeyword(Decl *D, bool Sil, bool Param) = 0;

Expand Down
35 changes: 14 additions & 21 deletions include/swift/Parse/Parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -894,30 +894,23 @@ class Parser {
void consumeGetSetBody(AbstractFunctionDecl *AFD, SourceLoc LBLoc);

struct ParsedAccessors;
bool parseGetSetImpl(ParseDeclOptions Flags,
GenericParamList *GenericParams,
ParameterList *Indices,
TypeLoc ElementTy,
ParsedAccessors &accessors,
AbstractStorageDecl *storage,
SourceLoc &LastValidLoc,
SourceLoc StaticLoc, SourceLoc VarLBLoc);
bool parseGetSet(ParseDeclOptions Flags,
GenericParamList *GenericParams,
ParameterList *Indices,
TypeLoc ElementTy,
ParsedAccessors &accessors,
AbstractStorageDecl *storage,
SourceLoc StaticLoc);
ParserStatus parseGetSet(ParseDeclOptions Flags,
GenericParamList *GenericParams,
ParameterList *Indices,
TypeLoc ElementTy,
ParsedAccessors &accessors,
AbstractStorageDecl *storage,
SourceLoc StaticLoc);
void recordAccessors(AbstractStorageDecl *storage, ParseDeclOptions flags,
TypeLoc elementTy, const DeclAttributes &attrs,
SourceLoc staticLoc, ParsedAccessors &accessors);
void parseAccessorBodyDelayed(AbstractFunctionDecl *AFD);
VarDecl *parseDeclVarGetSet(Pattern *pattern, ParseDeclOptions Flags,
SourceLoc StaticLoc, SourceLoc VarLoc,
bool hasInitializer,
const DeclAttributes &Attributes,
SmallVectorImpl<Decl *> &Decls);
ParserResult<VarDecl> parseDeclVarGetSet(Pattern *pattern,
ParseDeclOptions Flags,
SourceLoc StaticLoc,
SourceLoc VarLoc,
bool hasInitializer,
const DeclAttributes &Attributes,
SmallVectorImpl<Decl *> &Decls);

void consumeAbstractFunctionBody(AbstractFunctionDecl *AFD,
const DeclAttributes &Attrs);
Expand Down
42 changes: 42 additions & 0 deletions lib/IDE/CodeCompletion.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1461,6 +1461,7 @@ class CodeCompletionCallbacksImpl : public CodeCompletionCallbacks {
void completeInPrecedenceGroup(SyntaxKind SK) override;
void completeNominalMemberBeginning(
SmallVectorImpl<StringRef> &Keywords) override;
void completeAccessorBeginning() override;

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

void CodeCompletionCallbacksImpl::completeAccessorBeginning() {
Kind = CompletionKind::AccessorBeginning;
CurDeclContext = P.CurDeclContext;
}

static bool isDynamicLookup(Type T) {
return T->getRValueType()->isAnyObject();
}
Expand Down Expand Up @@ -4887,6 +4893,16 @@ static void addLetVarKeywords(CodeCompletionResultSink &Sink) {
addKeyword(Sink, "var", CodeCompletionKeywordKind::kw_var);
}

static void addAccessorKeywords(CodeCompletionResultSink &Sink) {
addKeyword(Sink, "get", CodeCompletionKeywordKind::None);
addKeyword(Sink, "set", CodeCompletionKeywordKind::None);
}

static void addObserverKeywords(CodeCompletionResultSink &Sink) {
addKeyword(Sink, "willSet", CodeCompletionKeywordKind::None);
addKeyword(Sink, "didSet", CodeCompletionKeywordKind::None);
}

static void addExprKeywords(CodeCompletionResultSink &Sink) {
// Expr keywords.
addKeyword(Sink, "try", CodeCompletionKeywordKind::kw_try);
Expand Down Expand Up @@ -4927,6 +4943,25 @@ void CodeCompletionCallbacksImpl::addKeywords(CodeCompletionResultSink &Sink,
case CompletionKind::PrecedenceGroup:
break;

case CompletionKind::AccessorBeginning: {
// TODO: Omit already declared or mutally exclusive accessors.
// E.g. If 'get' is already declared, emit 'set' only.
addAccessorKeywords(Sink);

// Only 'var' for non-protocol context can have 'willSet' and 'didSet'.
assert(ParsedDecl);
VarDecl *var = dyn_cast<VarDecl>(ParsedDecl);
if (auto accessor = dyn_cast<AccessorDecl>(ParsedDecl))
var = dyn_cast<VarDecl>(accessor->getStorage());
if (var && !var->getDeclContext()->getSelfProtocolDecl())
addObserverKeywords(Sink);

if (!isa<AccessorDecl>(ParsedDecl))
break;

MaybeFuncBody = true;
LLVM_FALLTHROUGH;
}
case CompletionKind::StmtOrExpr:
addDeclKeywords(Sink);
addStmtKeywords(Sink, MaybeFuncBody);
Expand Down Expand Up @@ -5667,6 +5702,13 @@ void CodeCompletionCallbacksImpl::doneParsing() {
OverrideLookup.getOverrideCompletions(SourceLoc());
break;
}

case CompletionKind::AccessorBeginning: {
if (isa<AccessorDecl>(ParsedDecl))
DoPostfixExprBeginning();
break;
}

case CompletionKind::AttributeBegin: {
Lookup.getAttributeDeclCompletions(IsInSil, AttTargetDK);
break;
Expand Down
101 changes: 66 additions & 35 deletions lib/Parse/ParseDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4212,10 +4212,12 @@ struct Parser::ParsedAccessors {
}
};

static bool parseAccessorIntroducer(Parser &P, DeclAttributes &Attributes,
static bool parseAccessorIntroducer(Parser &P,
DeclAttributes &Attributes,
AccessorKind &Kind,
AddressorKind &addressorKind,
SourceLoc &Loc) {
assert(Attributes.isEmpty());
bool FoundCCToken;
P.parseDeclAttributeList(Attributes, FoundCCToken);

Expand Down Expand Up @@ -4258,12 +4260,12 @@ static bool parseAccessorIntroducer(Parser &P, DeclAttributes &Attributes,
return false;
}

bool Parser::parseGetSet(ParseDeclOptions Flags,
GenericParamList *GenericParams,
ParameterList *Indices,
TypeLoc ElementTy, ParsedAccessors &accessors,
AbstractStorageDecl *storage,
SourceLoc StaticLoc) {
ParserStatus Parser::parseGetSet(ParseDeclOptions Flags,
GenericParamList *GenericParams,
ParameterList *Indices,
TypeLoc ElementTy, ParsedAccessors &accessors,
AbstractStorageDecl *storage,
SourceLoc StaticLoc) {
assert(Tok.is(tok::l_brace));

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

// In the limited syntax, fall out and let the caller handle it.
if (parsingLimitedSyntax)
return false;
return makeParserSuccess();

diagnose(accessors.RBLoc, diag::computed_property_no_accessors,
/*subscript*/ Indices != nullptr);
return true;
return makeParserError();
}

auto parseImplicitGetter = [&]() -> bool {
auto parseImplicitGetter = [&]() {
assert(Tok.is(tok::l_brace));
accessors.LBLoc = Tok.getLoc();
auto getter =
Expand All @@ -4306,14 +4308,14 @@ bool Parser::parseGetSet(ParseDeclOptions Flags,
accessors.add(getter);
parseAbstractFunctionBody(getter);
accessors.RBLoc = getter->getEndLoc();
return false;
};

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

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

if (Tok.is(tok::code_complete)) {
if (CodeCompletion) {
if (IsFirstAccessor && !parsingLimitedSyntax) {
// If CC token is the first token after '{', it might be implicit
// getter. Set up dummy accessor as the decl context to populate
// 'self' decl.
auto getter = createAccessorFunc(
accessors.LBLoc, /*ValueNamePattern*/ nullptr, GenericParams,
Indices, ElementTy, StaticLoc, Flags, AccessorKind::Get,
AddressorKind::NotAddressor, storage, this,
/*AccessorKeywordLoc*/ SourceLoc());
accessors.add(getter);
CodeCompletion->setParsedDecl(getter);
} else {
CodeCompletion->setParsedDecl(storage);
}
CodeCompletion->completeAccessorBeginning();
}
consumeToken(tok::code_complete);
accessorHasCodeCompletion = true;
break;
}

// parsingLimitedSyntax mode cannot have a body.
if (parsingLimitedSyntax) {
diagnose(Tok, diag::expected_getset_in_protocol);
Expand All @@ -4350,7 +4375,8 @@ bool Parser::parseGetSet(ParseDeclOptions Flags,
// position.
backtrack.reset();
AccessorListCtx.setTransparent();
return parseImplicitGetter();
parseImplicitGetter();
return makeParserSuccess();
}
IsFirstAccessor = false;

Expand Down Expand Up @@ -4415,7 +4441,9 @@ bool Parser::parseGetSet(ParseDeclOptions Flags,

parseMatchingToken(tok::r_brace, accessors.RBLoc,
diag::expected_rbrace_in_getset, accessors.LBLoc);
return Invalid;
if (accessorHasCodeCompletion)
return makeParserCodeCompletionStatus();
return Invalid ? makeParserError() : makeParserSuccess();
}

static void fillInAccessorTypeErrors(Parser &P, FuncDecl *accessor,
Expand Down Expand Up @@ -4461,12 +4489,12 @@ static void fillInAccessorTypeErrors(Parser &P,
}

/// \brief Parse the brace-enclosed getter and setter for a variable.
VarDecl *Parser::parseDeclVarGetSet(Pattern *pattern,
ParseDeclOptions Flags,
SourceLoc StaticLoc, SourceLoc VarLoc,
bool hasInitializer,
const DeclAttributes &Attributes,
SmallVectorImpl<Decl *> &Decls) {
ParserResult<VarDecl>
Parser::parseDeclVarGetSet(Pattern *pattern, ParseDeclOptions Flags,
SourceLoc StaticLoc, SourceLoc VarLoc,
bool hasInitializer,
const DeclAttributes &Attributes,
SmallVectorImpl<Decl *> &Decls) {
bool Invalid = false;

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

// Parse getter and setter.
ParsedAccessors accessors;
if (parseGetSet(Flags, /*GenericParams=*/nullptr,
/*Indices=*/nullptr, TyLoc, accessors, storage, StaticLoc))
auto AccessorStatus = parseGetSet(Flags, /*GenericParams=*/nullptr,
/*Indices=*/nullptr, TyLoc, accessors,
storage, StaticLoc);
if (AccessorStatus.hasCodeCompletion())
return makeParserCodeCompletionStatus();
if (AccessorStatus.isError())
Invalid = true;

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

return PrimaryVar;
return makeParserResult(PrimaryVar);
}

/// Add the given accessor to the collection of parsed accessors. If
Expand Down Expand Up @@ -5124,16 +5156,16 @@ Parser::parseDeclVar(ParseDeclOptions Flags,
// var-get-set clause, parse the var-get-set clause.
} else if (Tok.is(tok::l_brace)) {
HasAccessors = true;

if (auto *boundVar = parseDeclVarGetSet(pattern, Flags,
StaticLoc, VarLoc,
PatternInit != nullptr,
Attributes, Decls)) {
if (PatternInit && !boundVar->hasStorage()) {
diagnose(pattern->getLoc(), diag::getset_init)
auto boundVar = parseDeclVarGetSet(pattern, Flags, StaticLoc, VarLoc,
PatternInit != nullptr,Attributes,
Decls);
if (boundVar.hasCodeCompletion())
return makeResult(makeParserCodeCompletionStatus());
if (PatternInit && boundVar.isNonNull() &&
!boundVar.get()->hasStorage()) {
diagnose(pattern->getLoc(), diag::getset_init)
.highlight(PatternInit->getSourceRange());
PatternInit = nullptr;
}
PatternInit = nullptr;
}
}

Expand Down Expand Up @@ -6225,10 +6257,9 @@ Parser::parseDeclSubscript(ParseDeclOptions Flags,
Status.setIsParseError();
}
} else {
if (parseGetSet(Flags, GenericParams,
Indices.get(), ElementTy.get(),
accessors, Subscript, /*StaticLoc=*/SourceLoc()))
Status.setIsParseError();
Status |= parseGetSet(Flags, GenericParams,
Indices.get(), ElementTy.get(),
accessors, Subscript, /*StaticLoc=*/SourceLoc());
}

bool Invalid = false;
Expand Down
Loading