[Parse] Handle #if in brace skipping logic

Previously we would strictly match `{` + `}`, but
that ignored the fact that when parsing we consider
`#if` + `#endif` to be a stronger delimiter than
`{` + `}`, so can ignore a stray `}` in a `#if`.
Update the logic to also track opening and closing
`#if` decls, ignoring any braces that happen within
them.

rdar://129195380
This commit is contained in:
Hamish Knight
2024-06-19 21:39:40 +01:00
parent a85ca1315b
commit eeced060fa
3 changed files with 69 additions and 1 deletions

View File

@@ -5658,10 +5658,11 @@ static unsigned skipUntilMatchingRBrace(Parser &P,
HasPotentialRegexLiteral = false; HasPotentialRegexLiteral = false;
unsigned OpenBraces = 1; unsigned OpenBraces = 1;
unsigned OpenPoundIf = 0;
bool LastTokenWasFunc = false; bool LastTokenWasFunc = false;
while (OpenBraces != 0 && P.Tok.isNot(tok::eof)) { while ((OpenBraces != 0 || OpenPoundIf != 0) && P.Tok.isNot(tok::eof)) {
// Detect 'func' followed by an operator identifier. // Detect 'func' followed by an operator identifier.
if (LastTokenWasFunc) { if (LastTokenWasFunc) {
LastTokenWasFunc = false; LastTokenWasFunc = false;
@@ -5692,6 +5693,24 @@ static unsigned skipUntilMatchingRBrace(Parser &P,
return OpenBraces; return OpenBraces;
} }
// Match opening `#if` with closing `#endif` to match what the parser does,
// `#if` + `#endif` are considered stronger delimiters than `{` + `}`.
if (P.consumeIf(tok::pound_if)) {
OpenPoundIf += 1;
continue;
}
if (OpenPoundIf != 0) {
// We're in a `#if`, check to see if we've reached the end.
if (P.consumeIf(tok::pound_endif)) {
OpenPoundIf -= 1;
continue;
}
// Consume the next token and continue iterating. We can swallow any
// amount of '{' and '}' while in the `#if`.
P.consumeToken();
continue;
}
if (P.consumeIf(tok::l_brace)) { if (P.consumeIf(tok::l_brace)) {
++OpenBraces; ++OpenBraces;
continue; continue;

View File

@@ -0,0 +1,36 @@
// RUN: not %target-swift-frontend -experimental-skip-all-function-bodies -dump-parse %s | %FileCheck %s
// rdar://129195380 - Make sure the skipping logic can handle #if.
struct S {
// CHECK: func_decl{{.*}}:[[@LINE+1]]:3 - line:[[@LINE+11]]:3{{.*}}"foo()"
func foo() {
#if true
}
#if true
func bar() {
#else
}
#endif
}
#endif
}
// CHECK: func_decl{{.*}}:[[@LINE+1]]:3 - line:[[@LINE+1]]:15{{.*}}"baz()"
func baz() {}
}
// The '#if' is unterminated here, so swallows the rest of the file.
// CHECK: struct_decl{{.*}}:[[@LINE+1]]:1 - line:[[@LINE+14]]:14{{.*}}"R"
struct R {
#if false
}
#if true
}
#endif
}
#else
}
// CHECK-NOT: qux
func qux() {}
}
func flim() {}

View File

@@ -0,0 +1,13 @@
// rdar://129195380 - Make sure we correctly handle '#if' when skipping function
// bodies.
class C {
func test1() {
#if FOOBAR
// RUN: %sourcekitd-test -req=cursor -pos=%(line + 2):5 %s -- %s -DFOOBAR
// RUN: %sourcekitd-test -req=cursor -pos=%(line + 1):5 %s -- %s
abc
}
func test2() {
}
}