Skip to content

Commit a94898d

Browse files
authored
fix: handle mixed quotes in find_paren_match (#569)
Track the single active quote character instead of separate single and double quote flags. An apostrophe inside a double quoted string (or vice versa) previously left a flag set, hiding the closing parenthesis.
1 parent 0392beb commit a94898d

3 files changed

Lines changed: 40 additions & 4 deletions

File tree

‎CHANGELOG.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
- Fixed crash at initialization when a glob in `source_dirs` or `include_dirs`
1010
matched a dangling symlink
1111
([#536](https://github.com/fortran-lang/fortls/issues/536))
12+
- Fixed declarations and `ASSOCIATE` blocks being dropped when a string inside
13+
parentheses contained the other quote character, e.g. `len("don't")`
1214

1315
## 3.2.2
1416

‎fortls/helper_functions.py‎

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -266,13 +266,24 @@ def find_paren_match(string: str) -> int:
266266
267267
>>> find_paren_match("nt(sin(0.5))+8+len('ab))c')-3) :: y")
268268
29
269+
270+
A quote of the other kind inside a string literal does not end the literal
271+
272+
>>> find_paren_match('''len("don't")) :: msg''')
273+
12
274+
275+
>>> find_paren_match('''len('say "hi')) :: msg''')
276+
14
269277
"""
270278
paren_count = 1
271-
quote_state = {"'": False, '"': False}
279+
quote = None
272280
for i, char in enumerate(string):
273-
if char in quote_state:
274-
quote_state[char] = not quote_state[char]
275-
if any(quote_state.values()):
281+
if quote is not None:
282+
if char == quote:
283+
quote = None
284+
continue
285+
if char in ("'", '"'):
286+
quote = char
276287
continue
277288

278289
if char == "(":

‎test/test_parser.py‎

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,3 +96,26 @@ def calc_result(res: tuple):
9696
res = file.get_code_line(line_no=ln_no, pp_content=pp)
9797
result = calc_result(res)
9898
assert result == reference
99+
100+
101+
def test_mixed_quotes_in_parenthesised_strings():
102+
file = FortranFile()
103+
file.set_contents(
104+
[
105+
"program test",
106+
" implicit none",
107+
""" character(len=len("don't")) :: msg""",
108+
" integer :: x",
109+
""" associate (a => "it's", b => x)""",
110+
" end associate",
111+
"end program test",
112+
]
113+
)
114+
ast = file.parse()
115+
program, assoc = ast.scope_list
116+
assert [child.name for child in program.get_children()] == [
117+
"msg",
118+
"x",
119+
"#ASSOC1",
120+
]
121+
assert [child.name for child in assoc.get_children()] == ["a", "b"]

0 commit comments

Comments
 (0)