Skip to content

Commit df39cd7

Browse files
authored
Merge pull request #23577 from rintaro/5.1-ide-completion-grabbag1
[5.1][CodeCompletion] Grab bag of recent changes in code completion
2 parents c35f430 + 4d888e5 commit df39cd7

17 files changed

+402
-72
lines changed

include/swift/IDE/CodeCompletion.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -492,6 +492,7 @@ enum class CompletionKind {
492492
TypeSimpleBeginning,
493493
TypeIdentifierWithDot,
494494
TypeIdentifierWithoutDot,
495+
CaseStmtKeyword,
495496
CaseStmtBeginning,
496497
CaseStmtDotPrefix,
497498
NominalMemberBeginning,

include/swift/Parse/CodeCompletionCallbacks.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,9 @@ class CodeCompletionCallbacks {
165165
/// Complete a given type-identifier when there is no trailing dot.
166166
virtual void completeTypeIdentifierWithoutDot(IdentTypeRepr *ITR) {};
167167

168+
/// Complete the beginning of a case statement at the top of switch stmt.
169+
virtual void completeCaseStmtKeyword() {};
170+
168171
/// Complete at the beginning of a case stmt pattern.
169172
virtual void completeCaseStmtBeginning() {};
170173

lib/IDE/CodeCompletion.cpp

Lines changed: 96 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1185,8 +1185,6 @@ void CodeCompletionString::getName(raw_ostream &OS) const {
11851185
}
11861186
}
11871187
}
1188-
assert((TextSize > 0) &&
1189-
"code completion string should have non-empty name!");
11901188
}
11911189

11921190
void CodeCompletionContext::sortCompletionResults(
@@ -1348,6 +1346,7 @@ class CodeCompletionCallbacksImpl : public CodeCompletionCallbacks {
13481346
void completeTypeIdentifierWithDot(IdentTypeRepr *ITR) override;
13491347
void completeTypeIdentifierWithoutDot(IdentTypeRepr *ITR) override;
13501348

1349+
void completeCaseStmtKeyword() override;
13511350
void completeCaseStmtBeginning() override;
13521351
void completeCaseStmtDotPrefix() override;
13531352
void completeDeclAttrKeyword(Decl *D, bool Sil, bool Param) override;
@@ -1823,6 +1822,32 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
18231822
llvm_unreachable("unhandled kind");
18241823
}
18251824

1825+
void addValueBaseName(CodeCompletionResultBuilder &Builder,
1826+
DeclBaseName Name) {
1827+
auto NameStr = Name.userFacingName();
1828+
bool shouldEscapeKeywords;
1829+
if (Name.isSpecial()) {
1830+
// Special names (i.e. 'init') are always displayed as its user facing
1831+
// name.
1832+
shouldEscapeKeywords = false;
1833+
} else if (ExprType) {
1834+
// After dot. User can write any keyword after '.' except for `init` and
1835+
// `self`. E.g. 'func `init`()' must be called by 'expr.`init`()'.
1836+
shouldEscapeKeywords = NameStr == "self" || NameStr == "init";
1837+
} else {
1838+
// As primary expresson. We have to escape almost every keywords except
1839+
// for 'self' and 'Self'.
1840+
shouldEscapeKeywords = NameStr != "self" && NameStr != "Self";
1841+
}
1842+
1843+
if (!shouldEscapeKeywords) {
1844+
Builder.addTextChunk(NameStr);
1845+
} else {
1846+
SmallString<16> buffer;
1847+
Builder.addTextChunk(Builder.escapeKeyword(NameStr, true, buffer));
1848+
}
1849+
}
1850+
18261851
void addLeadingDot(CodeCompletionResultBuilder &Builder) {
18271852
if (NeedOptionalUnwrap) {
18281853
Builder.setNumBytesToErase(NumBytesToEraseForOptionalUnwrap);
@@ -1995,7 +2020,7 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
19952020
VD->shouldHideFromEditor())
19962021
return;
19972022

1998-
StringRef Name = VD->getName().get();
2023+
Identifier Name = VD->getName();
19992024
assert(!Name.empty() && "name should not be empty");
20002025

20012026
CommandWordsPairs Pairs;
@@ -2005,15 +2030,15 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
20052030
getSemanticContext(VD, Reason), ExpectedTypes);
20062031
Builder.setAssociatedDecl(VD);
20072032
addLeadingDot(Builder);
2008-
Builder.addTextChunk(Name);
2033+
addValueBaseName(Builder, Name);
20092034
setClangDeclKeywords(VD, Pairs, Builder);
20102035

20112036
if (!VD->hasValidSignature())
20122037
return;
20132038

20142039
// Add a type annotation.
20152040
Type VarType = getTypeOfMember(VD);
2016-
if (VD->getName() != Ctx.Id_self && VD->isInOut()) {
2041+
if (Name != Ctx.Id_self && VD->isInOut()) {
20172042
// It is useful to show inout for function parameters.
20182043
// But for 'self' it is just noise.
20192044
VarType = InOutType::get(VarType);
@@ -2222,14 +2247,8 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
22222247
else
22232248
Builder.addAnnotatedLeftParen();
22242249

2225-
bool anyParam = addCallArgumentPatterns(Builder, AFT->getParams(),
2226-
declParams, includeDefaultArgs);
2227-
2228-
if (HaveLParen && !anyParam) {
2229-
// Empty result, don't add it.
2230-
Builder.cancel();
2231-
return;
2232-
}
2250+
addCallArgumentPatterns(Builder, AFT->getParams(), declParams,
2251+
includeDefaultArgs);
22332252

22342253
// The rparen matches the lparen here so that we insert both or neither.
22352254
if (!HaveLParen)
@@ -2319,7 +2338,7 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
23192338
return;
23202339
foundFunction(FD);
23212340

2322-
StringRef Name = FD->getName().get();
2341+
Identifier Name = FD->getName();
23232342
assert(!Name.empty() && "name should not be empty");
23242343

23252344
Type FunctionType = getTypeOfMember(FD);
@@ -2350,7 +2369,7 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
23502369
setClangDeclKeywords(FD, Pairs, Builder);
23512370
Builder.setAssociatedDecl(FD);
23522371
addLeadingDot(Builder);
2353-
Builder.addTextChunk(Name);
2372+
addValueBaseName(Builder, Name);
23542373
if (IsDynamicLookup)
23552374
Builder.addDynamicLookupMethodCallTail();
23562375
else if (FD->getAttrs().hasAttribute<OptionalAttr>())
@@ -2475,14 +2494,8 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
24752494
else
24762495
Builder.addAnnotatedLeftParen();
24772496

2478-
bool anyParam = addCallArgumentPatterns(
2479-
Builder, ConstructorType, CD->getParameters(), includeDefaultArgs);
2480-
2481-
if (HaveLParen && !anyParam) {
2482-
// Empty result, don't add it.
2483-
Builder.cancel();
2484-
return;
2485-
}
2497+
addCallArgumentPatterns(Builder, ConstructorType, CD->getParameters(),
2498+
includeDefaultArgs);
24862499

24872500
// The rparen matches the lparen here so that we insert both or neither.
24882501
if (!HaveLParen)
@@ -2680,8 +2693,7 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
26802693
Builder.setAssociatedDecl(EED);
26812694
setClangDeclKeywords(EED, Pairs, Builder);
26822695
addLeadingDot(Builder);
2683-
2684-
Builder.addTextChunk(EED->getName().str());
2696+
addValueBaseName(Builder, EED->getName());
26852697

26862698
// Enum element is of function type; (Self.type) -> Self or
26872699
// (Self.Type) -> (Args...) -> Self.
@@ -2705,7 +2717,8 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
27052717
void addKeyword(StringRef Name, Type TypeAnnotation = Type(),
27062718
SemanticContextKind SK = SemanticContextKind::None,
27072719
CodeCompletionKeywordKind KeyKind
2708-
= CodeCompletionKeywordKind::None) {
2720+
= CodeCompletionKeywordKind::None,
2721+
unsigned NumBytesToErase = 0) {
27092722
CodeCompletionResultBuilder Builder(
27102723
Sink,
27112724
CodeCompletionResult::ResultKind::Keyword, SK, ExpectedTypes);
@@ -2714,6 +2727,8 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
27142727
Builder.setKeywordKind(KeyKind);
27152728
if (TypeAnnotation)
27162729
addTypeAnnotation(Builder, TypeAnnotation);
2730+
if (NumBytesToErase > 0)
2731+
Builder.setNumBytesToErase(NumBytesToErase);
27172732
}
27182733

27192734
void addKeyword(StringRef Name, StringRef TypeAnnotation,
@@ -2759,7 +2774,7 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
27592774

27602775
// Base name
27612776
addLeadingDot(Builder);
2762-
Builder.addTextChunk(AFD->getBaseName().userFacingName());
2777+
addValueBaseName(Builder, AFD->getBaseName());
27632778

27642779
// Add the argument labels.
27652780
auto ArgLabels = AFD->getFullName().getArgumentNames();
@@ -3600,6 +3615,15 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
36003615
return false;
36013616
}
36023617

3618+
if (T->getOptionalObjectType() &&
3619+
VD->getModuleContext()->isStdlibModule()) {
3620+
// In optional context, ignore '.init(<some>)', 'init(nilLiteral:)',
3621+
if (isa<ConstructorDecl>(VD))
3622+
return false;
3623+
// TODO: Ignore '.some(<Wrapped>)' and '.none' too *in expression
3624+
// context*. They are useful in pattern context though.
3625+
}
3626+
36033627
// Enum element decls can always be referenced by implicit member
36043628
// expression.
36053629
if (isa<EnumElementDecl>(VD))
@@ -3670,6 +3694,14 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
36703694
// If this is optional type, perform completion for the object type.
36713695
// i.e. 'let _: Enum??? = .enumMember' is legal.
36723696
getUnresolvedMemberCompletions(objT->lookThroughAllOptionalTypes());
3697+
3698+
// Add 'nil' keyword with erasing '.' instruction.
3699+
unsigned bytesToErase = 0;
3700+
auto &SM = CurrDeclContext->getASTContext().SourceMgr;
3701+
if (DotLoc.isValid())
3702+
bytesToErase = SM.getByteDistance(DotLoc, SM.getCodeCompletionLoc());
3703+
addKeyword("nil", T, SemanticContextKind::ExpressionSpecific,
3704+
CodeCompletionKeywordKind::kw_nil, bytesToErase);
36733705
}
36743706
getUnresolvedMemberCompletions(T);
36753707
}
@@ -4417,6 +4449,11 @@ void CodeCompletionCallbacksImpl::completeTypeIdentifierWithoutDot(
44174449
CurDeclContext = P.CurDeclContext;
44184450
}
44194451

4452+
void CodeCompletionCallbacksImpl::completeCaseStmtKeyword() {
4453+
Kind = CompletionKind::CaseStmtKeyword;
4454+
CurDeclContext = P.CurDeclContext;
4455+
}
4456+
44204457
void CodeCompletionCallbacksImpl::completeCaseStmtBeginning() {
44214458
assert(!InEnumElementRawValue);
44224459

@@ -4598,6 +4635,11 @@ static void addStmtKeywords(CodeCompletionResultSink &Sink, bool MaybeFuncBody)
45984635
#include "swift/Syntax/TokenKinds.def"
45994636
}
46004637

4638+
static void addCaseStmtKeywords(CodeCompletionResultSink &Sink) {
4639+
addKeyword(Sink, "case", CodeCompletionKeywordKind::kw_case);
4640+
addKeyword(Sink, "default", CodeCompletionKeywordKind::kw_default);
4641+
}
4642+
46014643
static void addLetVarKeywords(CodeCompletionResultSink &Sink) {
46024644
addKeyword(Sink, "let", CodeCompletionKeywordKind::kw_let);
46034645
addKeyword(Sink, "var", CodeCompletionKeywordKind::kw_var);
@@ -4687,6 +4729,10 @@ void CodeCompletionCallbacksImpl::addKeywords(CodeCompletionResultSink &Sink,
46874729
addAnyTypeKeyword(Sink);
46884730
break;
46894731

4732+
case CompletionKind::CaseStmtKeyword:
4733+
addCaseStmtKeywords(Sink);
4734+
break;
4735+
46904736
case CompletionKind::PostfixExpr:
46914737
case CompletionKind::PostfixExprParen:
46924738
case CompletionKind::SuperExpr:
@@ -4954,19 +5000,30 @@ void CodeCompletionCallbacksImpl::doneParsing() {
49545000
bool OnRoot = !KPE->getComponents().front().isValid();
49555001
Lookup.setIsSwiftKeyPathExpr(OnRoot);
49565002

4957-
auto ParsedType = BGT->getGenericArgs()[1];
4958-
auto Components = KPE->getComponents();
4959-
if (Components.back().getKind() ==
4960-
KeyPathExpr::Component::Kind::OptionalWrap) {
5003+
Type baseType = BGT->getGenericArgs()[OnRoot ? 0 : 1];
5004+
if (OnRoot && baseType->is<UnresolvedType>()) {
5005+
// Infer the root type of the keypath from the context type.
5006+
ExprContextInfo ContextInfo(CurDeclContext, ParsedExpr);
5007+
for (auto T : ContextInfo.getPossibleTypes()) {
5008+
if (auto unwrapped = T->getOptionalObjectType())
5009+
T = unwrapped;
5010+
if (!T->getAnyNominal() || !T->getAnyNominal()->getKeyPathTypeKind() ||
5011+
T->hasUnresolvedType() || !T->is<BoundGenericType>())
5012+
continue;
5013+
// Use the first KeyPath context type found.
5014+
baseType = T->castTo<BoundGenericType>()->getGenericArgs()[0];
5015+
break;
5016+
}
5017+
}
5018+
if (!OnRoot && KPE->getComponents().back().getKind() ==
5019+
KeyPathExpr::Component::Kind::OptionalWrap) {
49615020
// KeyPath expr with '?' (e.g. '\Ty.[0].prop?.another').
49625021
// Althogh expected type is optional, we should unwrap it because it's
49635022
// unwrapped.
4964-
ParsedType = ParsedType->getOptionalObjectType();
5023+
baseType = baseType->getOptionalObjectType();
49655024
}
49665025

4967-
// The second generic type argument of KeyPath<Root, Value> should be
4968-
// the value we pull code completion results from.
4969-
Lookup.getValueExprCompletions(ParsedType);
5026+
Lookup.getValueExprCompletions(baseType);
49705027
break;
49715028
}
49725029

@@ -5189,12 +5246,13 @@ void CodeCompletionCallbacksImpl::doneParsing() {
51895246
}
51905247
}
51915248
break;
5192-
case CompletionKind::AfterIfStmtElse:
5193-
// Handled earlier by keyword completions.
5194-
break;
51955249
case CompletionKind::PrecedenceGroup:
51965250
Lookup.getPrecedenceGroupCompletions(SyntxKind);
51975251
break;
5252+
case CompletionKind::AfterIfStmtElse:
5253+
case CompletionKind::CaseStmtKeyword:
5254+
// Handled earlier by keyword completions.
5255+
break;
51985256
}
51995257

52005258
for (auto &Request: Lookup.RequestedCachedResults) {

lib/IDE/CodeCompletionResultBuilder.h

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -267,9 +267,8 @@ class CodeCompletionResultBuilder {
267267
addTypeAnnotation(Annotation);
268268
}
269269

270-
StringRef escapeArgumentLabel(StringRef Word,
271-
bool escapeAllKeywords,
272-
llvm::SmallString<16> &EscapedKeyword) {
270+
StringRef escapeKeyword(StringRef Word, bool escapeAllKeywords,
271+
llvm::SmallString<16> &EscapedKeyword) {
273272
bool shouldEscape = false;
274273
if (escapeAllKeywords) {
275274
#define KEYWORD(kw) .Case(#kw, true)
@@ -325,15 +324,15 @@ class CodeCompletionResultBuilder {
325324
llvm::SmallString<16> EscapedKeyword;
326325
addChunkWithText(
327326
CodeCompletionString::Chunk::ChunkKind::CallParameterName,
328-
escapeArgumentLabel(Name.str(), false, EscapedKeyword));
327+
escapeKeyword(Name.str(), false, EscapedKeyword));
329328
addChunkWithTextNoCopy(
330329
CodeCompletionString::Chunk::ChunkKind::CallParameterColon, ": ");
331330
} else if (!LocalName.empty()) {
332331
// Use local (non-API) parameter name if we have nothing else.
333332
llvm::SmallString<16> EscapedKeyword;
334333
addChunkWithText(
335334
CodeCompletionString::Chunk::ChunkKind::CallParameterInternalName,
336-
escapeArgumentLabel(LocalName.str(), false, EscapedKeyword));
335+
escapeKeyword(LocalName.str(), false, EscapedKeyword));
337336
addChunkWithTextNoCopy(
338337
CodeCompletionString::Chunk::ChunkKind::CallParameterColon, ": ");
339338
}

lib/IDE/ExprContextAnalysis.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -176,8 +176,8 @@ class ExprFinder : public ASTWalker {
176176
Expr *get() const { return FoundExpr; }
177177

178178
std::pair<bool, Expr *> walkToExprPre(Expr *E) override {
179-
if (TargetRange == E->getSourceRange() && !isa<ImplicitConversionExpr>(E) &&
180-
!isa<AutoClosureExpr>(E) && !isa<ConstructorRefCallExpr>(E)) {
179+
if (TargetRange == E->getSourceRange() && !E->isImplicit() &&
180+
!isa<ConstructorRefCallExpr>(E)) {
181181
assert(!FoundExpr && "non-nullptr for found expr");
182182
FoundExpr = E;
183183
return {false, nullptr};

lib/Parse/ParseStmt.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2273,6 +2273,11 @@ Parser::parseStmtCases(SmallVectorImpl<ASTNode> &cases, bool IsActive) {
22732273
if (auto PDD = PoundDiagnosticResult.getPtrOrNull()) {
22742274
cases.emplace_back(PDD);
22752275
}
2276+
} else if (Tok.is(tok::code_complete)) {
2277+
if (CodeCompletion)
2278+
CodeCompletion->completeCaseStmtKeyword();
2279+
consumeToken(tok::code_complete);
2280+
return makeParserCodeCompletionStatus();
22762281
} else {
22772282
// If there are non-case-label statements at the start of the switch body,
22782283
// raise an error and recover by discarding them.

lib/Sema/LookupVisibleDecls.cpp

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -388,12 +388,18 @@ static void lookupDeclsFromProtocolsBeingConformedTo(
388388
NominalTypeDecl *CurrNominal = BaseTy->getAnyNominal();
389389
if (!CurrNominal)
390390
return;
391+
ModuleDecl *Module = FromContext->getParentModule();
391392

392393
for (auto Conformance : CurrNominal->getAllConformances()) {
393394
auto Proto = Conformance->getProtocol();
394395
if (!Proto->isAccessibleFrom(FromContext))
395396
continue;
396397

398+
// Skip unsatisfied conditional conformances.
399+
if (Conformance->getConditionalRequirementsIfAvailable() &&
400+
!Module->conformsToProtocol(BaseTy, Proto))
401+
continue;
402+
397403
DeclVisibilityKind ReasonForThisProtocol;
398404
if (Reason == DeclVisibilityKind::MemberOfCurrentNominal)
399405
ReasonForThisProtocol =
@@ -794,7 +800,10 @@ class OverrideFilteringConsumer : public VisibleDeclConsumer {
794800
OtherSignature, OtherSignatureType,
795801
/*wouldConflictInSwift5*/nullptr,
796802
/*skipProtocolExtensionCheck*/true)) {
797-
if (VD->getFormalAccess() > OtherVD->getFormalAccess()) {
803+
if (VD->getFormalAccess() > OtherVD->getFormalAccess() ||
804+
//Prefer available one.
805+
(!AvailableAttr::isUnavailable(VD) &&
806+
AvailableAttr::isUnavailable(OtherVD))) {
798807
PossiblyConflicting.erase(I);
799808
PossiblyConflicting.insert(VD);
800809

0 commit comments

Comments
 (0)