Skip to content

Commit

Permalink
[Parse] Handle #if in brace skipping logic
Browse files Browse the repository at this point in the history
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
  • Loading branch information
hamishknight committed Jun 19, 2024
1 parent a85ca13 commit eeced06
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 1 deletion.
21 changes: 20 additions & 1 deletion lib/Parse/ParseDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5658,10 +5658,11 @@ static unsigned skipUntilMatchingRBrace(Parser &P,
HasPotentialRegexLiteral = false;

unsigned OpenBraces = 1;
unsigned OpenPoundIf = 0;

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.
if (LastTokenWasFunc) {
LastTokenWasFunc = false;
Expand Down Expand Up @@ -5692,6 +5693,24 @@ static unsigned skipUntilMatchingRBrace(Parser &P,
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)) {
++OpenBraces;
continue;
Expand Down
36 changes: 36 additions & 0 deletions test/Parse/rdar129195380.swift
Original file line number Diff line number Diff line change
@@ -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() {}
13 changes: 13 additions & 0 deletions test/SourceKit/CursorInfo/rdar129195380.swift
Original file line number Diff line number Diff line change
@@ -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() {
}
}

0 comments on commit eeced06

Please sign in to comment.