Skip to content

Commit 22c157f

Browse files
committed
[CodeCompletion] Parse #if block containing CC token as active
rdar://problem/67027408
1 parent 21b91cd commit 22c157f

File tree

3 files changed

+105
-3
lines changed

3 files changed

+105
-3
lines changed

lib/Parse/ParseIfConfig.cpp

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -603,17 +603,33 @@ static Expr *findAnyLikelySimulatorEnvironmentTest(Expr *Condition) {
603603
/// Delegate callback function to parse elements in the blocks.
604604
ParserResult<IfConfigDecl> Parser::parseIfConfig(
605605
llvm::function_ref<void(SmallVectorImpl<ASTNode> &, bool)> parseElements) {
606+
assert(Tok.is(tok::pound_if));
606607
SyntaxParsingContext IfConfigCtx(SyntaxContext, SyntaxKind::IfConfigDecl);
607608

608609
SmallVector<IfConfigClause, 4> Clauses;
609610
Parser::StructureMarkerRAII ParsingDecl(
610611
*this, Tok.getLoc(), Parser::StructureMarkerKind::IfConfig);
611612

613+
// See if this '#if ... #endif' directive contains code completion token.
614+
bool hasCCToken = false;
615+
if (SourceMgr.hasCodeCompletionBuffer() &&
616+
SourceMgr.getCodeCompletionBufferID() == L->getBufferID()) {
617+
BacktrackingScope backtrack(*this);
618+
auto startLoc = Tok.getLoc();
619+
skipSingle();
620+
auto endLoc = PreviousLoc;
621+
hasCCToken = SourceMgr.rangeContainsTokenLoc(
622+
SourceRange(startLoc, endLoc), SourceMgr.getCodeCompletionLoc());
623+
}
624+
612625
bool shouldEvaluate =
613626
// Don't evaluate if it's in '-parse' mode, etc.
614627
shouldEvaluatePoundIfDecls() &&
615628
// If it's in inactive #if ... #endif block, there's no point to do it.
616-
!getScopeInfo().isInactiveConfigBlock();
629+
!getScopeInfo().isInactiveConfigBlock() &&
630+
// If this directive contains code completion, 'isActive' is determined
631+
// solely by which block has the completion token.
632+
!hasCCToken;
617633

618634
bool foundActive = false;
619635
bool isVersionCondition = false;
@@ -658,6 +674,16 @@ ParserResult<IfConfigDecl> Parser::parseIfConfig(
658674
}
659675
}
660676

677+
// Treat the region containing code completion token as "active".
678+
if (hasCCToken && !foundActive) {
679+
BacktrackingScope backtrack(*this);
680+
auto startLoc = Tok.getLoc();
681+
skipUntilConditionalBlockClose();
682+
auto endLoc = PreviousLoc;
683+
isActive = SourceMgr.rangeContainsTokenLoc(
684+
SourceRange(startLoc, endLoc), SourceMgr.getCodeCompletionLoc());
685+
}
686+
661687
foundActive |= isActive;
662688

663689
if (!Tok.isAtStartOfLine() && Tok.isNot(tok::eof)) {

lib/Parse/Parser.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -695,8 +695,9 @@ void Parser::skipSingle() {
695695
void Parser::skipUntil(tok T1, tok T2) {
696696
// tok::NUM_TOKENS is a sentinel that means "don't skip".
697697
if (T1 == tok::NUM_TOKENS && T2 == tok::NUM_TOKENS) return;
698-
699-
while (Tok.isNot(T1, T2, tok::eof, tok::pound_endif, tok::code_complete))
698+
699+
while (Tok.isNot(T1, T2, tok::eof, tok::pound_endif, tok::pound_else,
700+
tok::pound_elseif))
700701
skipSingle();
701702
}
702703

test/IDE/complete_in_ifconfig.swift

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-swift-ide-test -batch-code-completion -source-filename %s -filecheck %raw-FileCheck -completion-output-dir %t
3+
4+
struct MyStruct {
5+
init() {}
6+
var value: Int
7+
}
8+
9+
// MEMBER_MyStruct: Begin completions, 2 items
10+
// MEMBER_MyStruct-DAG: Keyword[self]/CurrNominal: self[#MyStruct#];
11+
// MEMBER_MyStruct-DAG: Decl[InstanceVar]/CurrNominal: value[#Int#];
12+
// MEMBER_MyStruct: End completions
13+
14+
#if true
15+
let toplevelActive = MyStruct()
16+
_ = toplevelActive.#^MEMBER_TOPLEVEL_ACTIVE?check=MEMBER_MyStruct^#
17+
#else
18+
let toplevelInactive = MyStruct()
19+
_ = toplevelInactive.#^MEMBER_TOPLEVEL_INACTIVE?check=MEMBER_MyStruct^#
20+
#endif
21+
22+
func foo() {
23+
#if true
24+
let infuncActive = MyStruct()
25+
_ = infuncActive.#^MEMBER_INFUNC_ACTIVE?check=MEMBER_MyStruct^#
26+
#else
27+
let infuncInactive = MyStruct()
28+
_ = infuncInactive.#^MEMBER_INFUNC_INACTIVE?check=MEMBER_MyStruct^#
29+
#endif
30+
}
31+
32+
protocol TestP {
33+
func foo()
34+
func bar()
35+
}
36+
struct TestStruct: TestP {
37+
#if true
38+
func foo() {}
39+
func #^OVERRIDE_ACTIVE^#
40+
// OVERRIDE_ACTIVE: Begin completions, 1 items
41+
// OVERRIDE_ACTIVE-DAG: Decl[InstanceMethod]/Super: bar() {|};
42+
// OVERRIDE_ACTIVE: End completions
43+
#else
44+
func bar() {}
45+
func #^OVERRIDE_INACTIVE^#
46+
// OVERRIDE_INACTIVE: Begin completions, 1 items
47+
// OVERRIDE_INACTIVE-DAG: Decl[InstanceMethod]/Super: foo() {|};
48+
// OVERRIDE_INACTIVE: End completions
49+
#endif
50+
}
51+
52+
struct TestStruct2 {
53+
#if true
54+
func activeFunc() {}
55+
func test() {
56+
self.#^SELF_ACTIVE^#
57+
}
58+
// SELF_ACTIVE: Begin completions, 3 items
59+
// SELF_ACTIVE-DAG: Keyword[self]/CurrNominal: self[#TestStruct2#];
60+
// SELF_ACTIVE-DAG: Decl[InstanceMethod]/CurrNominal: activeFunc()[#Void#];
61+
// SELF_ACTIVE-DAG: Decl[InstanceMethod]/CurrNominal: test()[#Void#];
62+
// SELF_ACTIVE: End completions
63+
#else
64+
func inactiveFunc() {}
65+
func test() {
66+
self.#^SELF_INACTIVE^#
67+
}
68+
// SELF_INACTIVE: Begin completions, 3 items
69+
// SELF_INACTIVE-DAG: Keyword[self]/CurrNominal: self[#TestStruct2#];
70+
// SELF_INACTIVE-DAG: Decl[InstanceMethod]/CurrNominal: inactiveFunc()[#Void#];
71+
// SELF_INACTIVE-DAG: Decl[InstanceMethod]/CurrNominal: test()[#Void#];
72+
// SELF_INACTIVE: End completions
73+
#endif
74+
}
75+

0 commit comments

Comments
 (0)