Skip to content

Commit 69967ca

Browse files
committed
[CodeCompletion] Identify known operators and force a fixed sort order
In the new code-completion code path, force any known operators to go through a fixed sort order. To identify operators unambiguously, add a new BuiltinOperator code-completion kind to handle non-decl operators (!, ., ?., and =). rdar://problem/25994246 rdar://problem/23440367
1 parent f1ba846 commit 69967ca

14 files changed

+432
-79
lines changed

include/swift/IDE/CodeCompletion.h

+97-23
Original file line numberDiff line numberDiff line change
@@ -306,7 +306,7 @@ class alignas(detail::CodeCompletionStringChunk) CodeCompletionString final :
306306
return {getTrailingObjects<Chunk>(), NumChunks};
307307
}
308308

309-
StringRef getFirstTextChunk() const;
309+
StringRef getFirstTextChunk(bool includeLeadingPunctuation = false) const;
310310
Optional<unsigned>
311311
getFirstTextChunkIndex(bool includeLeadingPunctuation = false) const;
312312

@@ -423,6 +423,52 @@ enum class CodeCompletionLiteralKind {
423423
Tuple,
424424
};
425425

426+
enum class CodeCompletionOperatorKind {
427+
None,
428+
Unknown,
429+
Bang, // !
430+
NotEq, // !=
431+
NotEqEq, // !==
432+
Modulo, // %
433+
ModuloEq, // %=
434+
Amp, // &
435+
AmpAmp, // &&
436+
AmpStar, // &*
437+
AmpPlus, // &+
438+
AmpMinus, // &-
439+
AmpEq, // &=
440+
LParen, // ( -- not really an operator, but treated as one in some cases.
441+
Star, // *
442+
StarEq, // *=
443+
Plus, // +
444+
PlusEq, // +=
445+
Minus, // -
446+
MinusEq, // -=
447+
Dot, // .
448+
DotDotDot, // ...
449+
DotDotLess, // ..<
450+
Slash, // /
451+
SlashEq, // /=
452+
Less, // <
453+
LessLess, // <<
454+
LessLessEq, // <<=
455+
LessEq, // <=
456+
Eq, // =
457+
EqEq, // ==
458+
EqEqEq, // ===
459+
Greater, // >
460+
GreaterEq, // >=
461+
GreaterGreater, // >>
462+
GreaterGreaterEq, // >>=
463+
QuestionDot, // ?.
464+
Caret, // ^
465+
CaretEq, // ^=
466+
Pipe, // |
467+
PipeEq, // |=
468+
PipePipe, // ||
469+
TildeEq, // ~=
470+
};
471+
426472
enum class CodeCompletionKeywordKind {
427473
None,
428474
#define KEYWORD(X) kw_##X,
@@ -467,6 +513,7 @@ class CodeCompletionResult {
467513
Keyword,
468514
Pattern,
469515
Literal,
516+
BuiltinOperator,
470517
};
471518

472519
/// Describes the relationship between the type of the completion results and
@@ -497,8 +544,9 @@ class CodeCompletionResult {
497544
};
498545

499546
private:
500-
unsigned Kind : 2;
547+
unsigned Kind : 3;
501548
unsigned AssociatedKind : 8;
549+
unsigned KnownOperatorKind : 6;
502550
unsigned SemanticContext : 3;
503551
unsigned NotRecommended : 1;
504552
unsigned NotRecReason : 3;
@@ -520,20 +568,27 @@ class CodeCompletionResult {
520568
unsigned TypeDistance : 3;
521569

522570
public:
523-
/// Constructs a \c Pattern or \c Keyword result.
571+
/// Constructs a \c Pattern, \c Keyword or \c BuiltinOperator result.
524572
///
525573
/// \note The caller must ensure \c CodeCompletionString outlives this result.
526-
CodeCompletionResult(ResultKind Kind,
527-
SemanticContextKind SemanticContext,
574+
CodeCompletionResult(ResultKind Kind, SemanticContextKind SemanticContext,
528575
unsigned NumBytesToErase,
529576
CodeCompletionString *CompletionString,
530-
ExpectedTypeRelation TypeDistance = Unrelated)
531-
: Kind(Kind), SemanticContext(unsigned(SemanticContext)),
532-
NotRecommended(false), NotRecReason(NotRecommendedReason::NoReason),
577+
ExpectedTypeRelation TypeDistance = Unrelated,
578+
CodeCompletionOperatorKind KnownOperatorKind =
579+
CodeCompletionOperatorKind::None)
580+
: Kind(Kind), KnownOperatorKind(unsigned(KnownOperatorKind)),
581+
SemanticContext(unsigned(SemanticContext)), NotRecommended(false),
582+
NotRecReason(NotRecommendedReason::NoReason),
533583
NumBytesToErase(NumBytesToErase), CompletionString(CompletionString),
534584
TypeDistance(TypeDistance) {
535585
assert(Kind != Declaration && "use the other constructor");
536586
assert(CompletionString);
587+
if (isOperator() && KnownOperatorKind == CodeCompletionOperatorKind::None)
588+
this->KnownOperatorKind =
589+
(unsigned)getCodeCompletionOperatorKind(CompletionString);
590+
assert(!isOperator() ||
591+
getOperatorKind() != CodeCompletionOperatorKind::None);
537592
AssociatedKind = 0;
538593
}
539594

@@ -545,8 +600,9 @@ class CodeCompletionResult {
545600
unsigned NumBytesToErase,
546601
CodeCompletionString *CompletionString,
547602
ExpectedTypeRelation TypeDistance = Unrelated)
548-
: Kind(Keyword), SemanticContext(unsigned(SemanticContext)),
549-
NotRecommended(false), NotRecReason(NotRecommendedReason::NoReason),
603+
: Kind(Keyword), KnownOperatorKind(0),
604+
SemanticContext(unsigned(SemanticContext)), NotRecommended(false),
605+
NotRecReason(NotRecommendedReason::NoReason),
550606
NumBytesToErase(NumBytesToErase), CompletionString(CompletionString),
551607
TypeDistance(TypeDistance) {
552608
assert(CompletionString);
@@ -561,8 +617,9 @@ class CodeCompletionResult {
561617
unsigned NumBytesToErase,
562618
CodeCompletionString *CompletionString,
563619
ExpectedTypeRelation TypeDistance)
564-
: Kind(Literal), SemanticContext(unsigned(SemanticContext)),
565-
NotRecommended(false), NotRecReason(NotRecommendedReason::NoReason),
620+
: Kind(Literal), KnownOperatorKind(0),
621+
SemanticContext(unsigned(SemanticContext)), NotRecommended(false),
622+
NotRecReason(NotRecommendedReason::NoReason),
566623
NumBytesToErase(NumBytesToErase), CompletionString(CompletionString),
567624
TypeDistance(TypeDistance) {
568625
AssociatedKind = static_cast<unsigned>(LiteralKind);
@@ -584,16 +641,21 @@ class CodeCompletionResult {
584641
ArrayRef<StringRef> AssociatedUSRs,
585642
ArrayRef<std::pair<StringRef, StringRef>> DocWords,
586643
enum ExpectedTypeRelation TypeDistance)
587-
: Kind(ResultKind::Declaration),
644+
: Kind(ResultKind::Declaration), KnownOperatorKind(0),
588645
SemanticContext(unsigned(SemanticContext)),
589646
NotRecommended(NotRecommended), NotRecReason(NotRecReason),
590-
NumBytesToErase(NumBytesToErase),
591-
CompletionString(CompletionString), ModuleName(ModuleName),
592-
BriefDocComment(BriefDocComment), AssociatedUSRs(AssociatedUSRs),
593-
DocWords(DocWords), TypeDistance(TypeDistance) {
647+
NumBytesToErase(NumBytesToErase), CompletionString(CompletionString),
648+
ModuleName(ModuleName), BriefDocComment(BriefDocComment),
649+
AssociatedUSRs(AssociatedUSRs), DocWords(DocWords),
650+
TypeDistance(TypeDistance) {
594651
assert(AssociatedDecl && "should have a decl");
595652
AssociatedKind = unsigned(getCodeCompletionDeclKind(AssociatedDecl));
596653
assert(CompletionString);
654+
if (isOperator())
655+
KnownOperatorKind =
656+
(unsigned)getCodeCompletionOperatorKind(CompletionString);
657+
assert(!isOperator() ||
658+
getOperatorKind() != CodeCompletionOperatorKind::None);
597659
}
598660

599661
// FIXME:
@@ -605,17 +667,20 @@ class CodeCompletionResult {
605667
CodeCompletionResult::NotRecommendedReason NotRecReason,
606668
StringRef BriefDocComment,
607669
ArrayRef<StringRef> AssociatedUSRs,
608-
ArrayRef<std::pair<StringRef, StringRef>> DocWords)
670+
ArrayRef<std::pair<StringRef, StringRef>> DocWords,
671+
CodeCompletionOperatorKind KnownOperatorKind)
609672
: Kind(ResultKind::Declaration),
673+
KnownOperatorKind(unsigned(KnownOperatorKind)),
610674
SemanticContext(unsigned(SemanticContext)),
611675
NotRecommended(NotRecommended), NotRecReason(NotRecReason),
612-
NumBytesToErase(NumBytesToErase),
613-
CompletionString(CompletionString), ModuleName(ModuleName),
614-
BriefDocComment(BriefDocComment), AssociatedUSRs(AssociatedUSRs),
615-
DocWords(DocWords) {
676+
NumBytesToErase(NumBytesToErase), CompletionString(CompletionString),
677+
ModuleName(ModuleName), BriefDocComment(BriefDocComment),
678+
AssociatedUSRs(AssociatedUSRs), DocWords(DocWords) {
616679
AssociatedKind = static_cast<unsigned>(DeclKind);
617680
assert(CompletionString);
618681
TypeDistance = ExpectedTypeRelation::Unrelated;
682+
assert(!isOperator() ||
683+
getOperatorKind() != CodeCompletionOperatorKind::None);
619684
}
620685

621686
ResultKind getKind() const { return static_cast<ResultKind>(Kind); }
@@ -637,7 +702,7 @@ class CodeCompletionResult {
637702

638703
bool isOperator() const {
639704
if (getKind() != Declaration)
640-
return false;
705+
return getKind() == BuiltinOperator;
641706
switch (getAssociatedDeclKind()) {
642707
case CodeCompletionDeclKind::PrefixOperatorFunction:
643708
case CodeCompletionDeclKind::PostfixOperatorFunction:
@@ -648,6 +713,11 @@ class CodeCompletionResult {
648713
}
649714
}
650715

716+
CodeCompletionOperatorKind getOperatorKind() const {
717+
assert(isOperator());
718+
return static_cast<CodeCompletionOperatorKind>(KnownOperatorKind);
719+
}
720+
651721
ExpectedTypeRelation getExpectedTypeRelation() const {
652722
return static_cast<ExpectedTypeRelation>(TypeDistance);
653723
}
@@ -691,6 +761,10 @@ class CodeCompletionResult {
691761
void dump() const;
692762

693763
static CodeCompletionDeclKind getCodeCompletionDeclKind(const Decl *D);
764+
static CodeCompletionOperatorKind
765+
getCodeCompletionOperatorKind(StringRef name);
766+
static CodeCompletionOperatorKind
767+
getCodeCompletionOperatorKind(CodeCompletionString *str);
694768
};
695769

696770
struct CodeCompletionResultSink {

lib/IDE/CodeCompletion.cpp

+78-4
Original file line numberDiff line numberDiff line change
@@ -659,6 +659,9 @@ void CodeCompletionResult::print(raw_ostream &OS) const {
659659
break;
660660
}
661661
break;
662+
case ResultKind::BuiltinOperator:
663+
Prefix.append("BuiltinOperator");
664+
break;
662665
}
663666
Prefix.append("/");
664667
switch (getSemanticContext()) {
@@ -880,6 +883,75 @@ static CodeCompletionResult::ExpectedTypeRelation calculateMaxTypeRelationForDec
880883
return Result;
881884
}
882885

886+
CodeCompletionOperatorKind
887+
CodeCompletionResult::getCodeCompletionOperatorKind(StringRef name) {
888+
using CCOK = CodeCompletionOperatorKind;
889+
using OpPair = std::pair<StringRef, CCOK>;
890+
891+
// This list must be kept in alphabetical order.
892+
static OpPair ops[] = {
893+
std::make_pair("!", CCOK::Bang),
894+
std::make_pair("!=", CCOK::NotEq),
895+
std::make_pair("!==", CCOK::NotEqEq),
896+
std::make_pair("%", CCOK::Modulo),
897+
std::make_pair("%=", CCOK::ModuloEq),
898+
std::make_pair("&", CCOK::Amp),
899+
std::make_pair("&&", CCOK::AmpAmp),
900+
std::make_pair("&*", CCOK::AmpStar),
901+
std::make_pair("&+", CCOK::AmpPlus),
902+
std::make_pair("&-", CCOK::AmpMinus),
903+
std::make_pair("&=", CCOK::AmpEq),
904+
std::make_pair("(", CCOK::LParen),
905+
std::make_pair("*", CCOK::Star),
906+
std::make_pair("*=", CCOK::StarEq),
907+
std::make_pair("+", CCOK::Plus),
908+
std::make_pair("+=", CCOK::PlusEq),
909+
std::make_pair("-", CCOK::Minus),
910+
std::make_pair("-=", CCOK::MinusEq),
911+
std::make_pair(".", CCOK::Dot),
912+
std::make_pair("...", CCOK::DotDotDot),
913+
std::make_pair("..<", CCOK::DotDotLess),
914+
std::make_pair("/", CCOK::Slash),
915+
std::make_pair("/=", CCOK::SlashEq),
916+
std::make_pair("<", CCOK::Less),
917+
std::make_pair("<<", CCOK::LessLess),
918+
std::make_pair("<<=", CCOK::LessLessEq),
919+
std::make_pair("<=", CCOK::LessEq),
920+
std::make_pair("=", CCOK::Eq),
921+
std::make_pair("==", CCOK::EqEq),
922+
std::make_pair("===", CCOK::EqEqEq),
923+
std::make_pair(">", CCOK::Greater),
924+
std::make_pair(">=", CCOK::GreaterEq),
925+
std::make_pair(">>", CCOK::GreaterGreater),
926+
std::make_pair(">>=", CCOK::GreaterGreaterEq),
927+
std::make_pair("?.", CCOK::QuestionDot),
928+
std::make_pair("^", CCOK::Caret),
929+
std::make_pair("^=", CCOK::CaretEq),
930+
std::make_pair("|", CCOK::Pipe),
931+
std::make_pair("|=", CCOK::PipeEq),
932+
std::make_pair("||", CCOK::PipePipe),
933+
std::make_pair("~=", CCOK::TildeEq),
934+
};
935+
static auto opsSize = sizeof(ops) / sizeof(ops[0]);
936+
937+
auto I = std::lower_bound(
938+
ops, &ops[opsSize], std::make_pair(name, CCOK::None),
939+
[](const OpPair &a, const OpPair &b) { return a.first < b.first; });
940+
941+
if (I == &ops[opsSize] || I->first != name)
942+
return CCOK::Unknown;
943+
return I->second;
944+
}
945+
946+
static StringRef getOperatorName(CodeCompletionString *str) {
947+
return str->getFirstTextChunk(/*includeLeadingPunctuation=*/true);
948+
}
949+
950+
CodeCompletionOperatorKind
951+
CodeCompletionResult::getCodeCompletionOperatorKind(CodeCompletionString *str) {
952+
return getCodeCompletionOperatorKind(getOperatorName(str));
953+
}
954+
883955
CodeCompletionResult *CodeCompletionResultBuilder::takeResult() {
884956
auto *CCS = CodeCompletionString::create(*Sink.Allocator, Chunks);
885957

@@ -938,6 +1010,7 @@ CodeCompletionResult *CodeCompletionResultBuilder::takeResult() {
9381010
CodeCompletionResult(KeywordKind, SemanticContext, NumBytesToErase,
9391011
CCS, ExpectedTypeRelation);
9401012

1013+
case CodeCompletionResult::ResultKind::BuiltinOperator:
9411014
case CodeCompletionResult::ResultKind::Pattern:
9421015
return new (*Sink.Allocator) CodeCompletionResult(
9431016
Kind, SemanticContext, NumBytesToErase, CCS, ExpectedTypeRelation);
@@ -1021,8 +1094,9 @@ Optional<unsigned> CodeCompletionString::getFirstTextChunkIndex(
10211094
return None;
10221095
}
10231096

1024-
StringRef CodeCompletionString::getFirstTextChunk() const {
1025-
Optional<unsigned> Idx = getFirstTextChunkIndex();
1097+
StringRef
1098+
CodeCompletionString::getFirstTextChunk(bool includeLeadingPunctuation) const {
1099+
Optional<unsigned> Idx = getFirstTextChunkIndex(includeLeadingPunctuation);
10261100
if (Idx.hasValue())
10271101
return getChunks()[*Idx].getText();
10281102
return StringRef();
@@ -2925,7 +2999,7 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
29252999

29263000
void addPostfixBang(Type resultType) {
29273001
CodeCompletionResultBuilder builder(
2928-
Sink, CodeCompletionResult::ResultKind::Pattern,
3002+
Sink, CodeCompletionResult::ResultKind::BuiltinOperator,
29293003
SemanticContextKind::None, {});
29303004
// FIXME: we can't use the exclamation mark chunk kind, or it isn't
29313005
// included in the completion name.
@@ -2969,7 +3043,7 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
29693043

29703044
void addAssignmentOperator(Type RHSType, Type resultType) {
29713045
CodeCompletionResultBuilder builder(
2972-
Sink, CodeCompletionResult::ResultKind::Pattern,
3046+
Sink, CodeCompletionResult::ResultKind::BuiltinOperator,
29733047
SemanticContextKind::None, {});
29743048

29753049
if (HaveLeadingSpace)

lib/IDE/CodeCompletionCache.cpp

+12-8
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,7 @@ static bool readCachedModule(llvm::MemoryBuffer *in,
198198
while (cursor != resultEnd) {
199199
auto kind = static_cast<CodeCompletionResult::ResultKind>(*cursor++);
200200
auto declKind = static_cast<CodeCompletionDeclKind>(*cursor++);
201+
auto opKind = static_cast<CodeCompletionOperatorKind>(*cursor++);
201202
auto context = static_cast<SemanticContextKind>(*cursor++);
202203
auto notRecommended = static_cast<bool>(*cursor++);
203204
auto numBytesToErase = static_cast<unsigned>(*cursor++);
@@ -232,16 +233,15 @@ static bool readCachedModule(llvm::MemoryBuffer *in,
232233

233234
CodeCompletionResult *result = nullptr;
234235
if (kind == CodeCompletionResult::Declaration) {
235-
result = new (*V.Sink.Allocator)
236-
CodeCompletionResult(context, numBytesToErase, string, declKind,
237-
moduleName, notRecommended,
238-
CodeCompletionResult::NotRecommendedReason::NoReason,
239-
briefDocComment,
240-
copyStringArray(*V.Sink.Allocator, assocUSRs),
241-
copyStringPairArray(*V.Sink.Allocator, declKeywords));
236+
result = new (*V.Sink.Allocator) CodeCompletionResult(
237+
context, numBytesToErase, string, declKind, moduleName,
238+
notRecommended, CodeCompletionResult::NotRecommendedReason::NoReason,
239+
briefDocComment, copyStringArray(*V.Sink.Allocator, assocUSRs),
240+
copyStringPairArray(*V.Sink.Allocator, declKeywords), opKind);
242241
} else {
243242
result = new (*V.Sink.Allocator)
244-
CodeCompletionResult(kind, context, numBytesToErase, string);
243+
CodeCompletionResult(kind, context, numBytesToErase, string,
244+
CodeCompletionResult::Unrelated, opKind);
245245
}
246246

247247
V.Sink.Results.push_back(result);
@@ -346,6 +346,10 @@ static void writeCachedModule(llvm::raw_ostream &out,
346346
LE.write(static_cast<uint8_t>(R->getAssociatedDeclKind()));
347347
else
348348
LE.write(static_cast<uint8_t>(~0u));
349+
if (R->isOperator())
350+
LE.write(static_cast<uint8_t>(R->getOperatorKind()));
351+
else
352+
LE.write(static_cast<uint8_t>(CodeCompletionOperatorKind::None));
349353
LE.write(static_cast<uint8_t>(R->getSemanticContext()));
350354
LE.write(static_cast<uint8_t>(R->isNotRecommended()));
351355
LE.write(static_cast<uint8_t>(R->getNumBytesToErase()));

0 commit comments

Comments
 (0)