-
Notifications
You must be signed in to change notification settings - Fork 10.5k
[CodeCompletion] Parse #if block containing CC token as active #33475
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
[CodeCompletion] Parse #if block containing CC token as active #33475
Conversation
@swift-ci Please smoke test |
b74bc58
to
4192e18
Compare
while (Tok.isNot(T1, T2, tok::eof, tok::pound_endif, tok::code_complete)) | ||
|
||
while (Tok.isNot(T1, T2, tok::eof, tok::pound_endif, tok::pound_else, | ||
tok::pound_elseif)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
tok::code_complete
was here to prevent excessive skipping beyond the completion point. But we sometimes want to skip code_complete
token, for example canParse***
kind of functions. This doesn't cause any visible regression because skipUntilDeclRBrace()
family skipping functions still stop at tok::code_complete
.
As for tok::pound_else
and tok::pound_elseif
, they should be treated as the same as tok::pound_endif
in terms of "skipping" tokens.
4192e18
to
b6051b6
Compare
@swift-ci Please smoke test |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This approach only applies to the #if
immediately surrounding the completion, so it can lead to non-sensical completions
#if FOO
let x: Foo
#else
let x: NotFoo
#endif
...
#if FOO
x.<here>
#endif
If we moved #if
condition evaluation after parsing we could look at the #if
condition for the completion location and choose a config that is consistent with that for all other #if
s. If there are ||
branches we might choose something invalid, but seems overall reasonable. What do you think?
What impact does this change have on AST reuse if the #if
is outside of a function body? Will it still work? Will it cause the interface hash to change if you complete in the inactive block?
This is not so efficient because the Lexer runs up to 3 times for each IfConfig region
What if we do a single extra pass and track the #else
locations along the way so we can know directly which branch contains the completion?
lib/Parse/ParseIfConfig.cpp
Outdated
// See if this '#if ... #endif' directive contains code completion token. | ||
bool hasCCToken = false; | ||
if (SourceMgr.hasCodeCompletionBuffer() && | ||
SourceMgr.getCodeCompletionBufferID() == L->getBufferID()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could also check that this is not after the completion loc already.
rdar://problem/67027408
This flag has never been enabled. Now that, Parser treats IfConfig block containing CC token as "active", so code completion doesn't lookup from inactive blocks.
0c98a40
to
7198b3b
Compare
It is a long standing desire to separate #if os(macOS)
import AppKit
#else
import UIKit
#endif
#if os(macOS)
let color = NSColor.black
#else
let color = UIColor.#HERE#
#endif This fails if the user is working on macOS target because macOS SDK doesn't have UIKit anyway. |
My original patch had a bug where interface hash continued to be recorded while backtracking. After fixing that, it works as expected. Switching between active and inactive blocks changes the interface hash. |
Great idea! Implemented. |
lib/Parse/ParseIfConfig.cpp
Outdated
// If this directive contains code completion, 'isActive' is determined | ||
// solely by which block has the completion token. | ||
!hasCCToken; | ||
// If this directive contains code completion locaiton, 'isActive' is |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Typo: locaiton -> location
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🤦
8b3a27d
to
6cfdaf6
Compare
@swift-ci Please smoke test |
@swift-ci Please smoke test macOS |
Treat the region containing the completion location as "active" during parsing.
#if
is found, see if the completion location is between#if
and#endif
(using backtracking.) If not, parse the entire#if ... #endif
normallyThis is not so efficient because the Lexer runs up to 3 times for each IfConfig region. But Lexer is pretty fast, and doesn't consume much memory. So it shouldn't be a problem.
This also guarantees that the completion never happens inside "inactive" blocks. Since
Sema
usually don't expect type checking inside inactive blocks, this change might improve the overall stability of code-completion inside#if ... #endif
.The second commit is removing
includeInactiveIfConfigClauses
fromASTScope
. This flag was prepared for code-completion, but had never been enabled. Now that, we guarantee that name lookup never happen inside inactive blocks, we don't need this flag anymore. cc: @davidungarrdar://problem/67027408