Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ Modifications by (in alphabetical order):
* P. Vitt, University of Siegen, Germany
* A. Voysey, UK Met Office

25/11/2025 PR #488 for #483. Do not recognize inline comments as Directives.

31/10/2025 PR #486 for #483. Recognize any comment that begins ``!$``, ``c$`` or ``*$`` followed by
a character as a Directive node.

Expand Down
26 changes: 20 additions & 6 deletions src/fparser/common/readfortran.py
Original file line number Diff line number Diff line change
Expand Up @@ -437,10 +437,10 @@ class Comment:
:param reader: The reader object being used to read the input \
source.
:type reader: :py:class:`fparser.common.readfortran.FortranReaderBase`

:param inline: whether this was an inline comment.
"""

def __init__(self, comment, linenospan, reader):
def __init__(self, comment, linenospan, reader, inline: bool = False):
self.comment = comment
self.span = linenospan
self.reader = reader
Expand All @@ -449,6 +449,7 @@ def __init__(self, comment, linenospan, reader):
# tests as a reader can return an instance of either class and
# we might want to check the contents in a consistent way.
self.line = comment
self.inline = inline

def __repr__(self):
return self.__class__.__name__ + "(%r,%s)" % (self.comment, self.span)
Expand Down Expand Up @@ -1010,9 +1011,15 @@ def multiline_item(
prefix, lines, suffix, (startlineno, endlineno), self, errmessage
)

def comment_item(self, comment, startlineno, endlineno):
"""Construct Comment item."""
return Comment(comment, (startlineno, endlineno), self)
def comment_item(
self, comment, startlineno, endlineno, inline_comment: bool = False
):
"""Construct Comment item.

:param inline_comment: whether the comment is an inline comment.
Defaults to False.
"""
return Comment(comment, (startlineno, endlineno), self, inline_comment)

def cpp_directive_item(self, line, startlineno, endlineno):
"""
Expand Down Expand Up @@ -1245,7 +1252,14 @@ def handle_inline_comment(
newline = line[:idx]
if '"' not in newline and "'" not in newline:
if self.format.is_f77 or not line[idx:].startswith("!f2py"):
put_item(self.comment_item(line[idx:], lineno, lineno))
# Its an inline comment if there is a non whitespace
# character before the comment.
is_inline = not (line.lstrip() == line[idx:])
put_item(
self.comment_item(
line[idx:], lineno, lineno, inline_comment=is_inline
)
)
return newline, quotechar, True

# We must allow for quotes...
Expand Down
3 changes: 3 additions & 0 deletions src/fparser/two/Fortran2003.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,9 @@ def __new__(cls, string: Union[str, FortranReaderBase], parent_cls=None):
from fparser.common import readfortran

if isinstance(string, readfortran.Comment):
# Inline comments cannot be directives.
if string.inline:
return
# Directives must start with one of the specified directive
# prefixes.
lower = string.comment.lower()
Expand Down
3 changes: 1 addition & 2 deletions src/fparser/two/tests/fortran2003/test_usestmt_r1109.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,7 @@


@pytest.fixture(autouse=True)
@pytest.mark.usefixtures("f2003_create")
def use_f2003():
def use_f2003(f2003_create):
"""
A pytest fixture that just sets things up so that the f2003_create
fixture is used automatically for every test in this file.
Expand Down
33 changes: 24 additions & 9 deletions src/fparser/two/tests/test_comments_and_directives.py
Original file line number Diff line number Diff line change
Expand Up @@ -431,13 +431,12 @@ def test_directive_stmts():
)
program = Program(reader)
out = walk(program, Directive)
assert len(out) == 4
assert out[0].items[0] == "!$dir inline"
assert out[1].items[0] == "!dir$ compiler directive"
assert out[2].items[0] == "!$omp target"
assert out[3].items[0] == "!$omp loop"
assert len(out) == 3
assert out[0].items[0] == "!dir$ compiler directive"
assert out[1].items[0] == "!$omp target"
assert out[2].items[0] == "!$omp loop"

assert out[3].tostr() == "!$omp loop"
assert out[2].tostr() == "!$omp loop"

# Check the restore_reader works correctly for directive.
old = reader.get_item()
Expand All @@ -451,9 +450,10 @@ def test_directive_stmts():
for comment in out:
if comment.items[0] != "":
comments = comments + 1
assert comments == 2
assert str(out[2]) == "! A comment!"
assert str(out[3]) == "!!$ Another comment"
assert comments == 3
assert str(out[1]) == "!$dir inline"
assert str(out[3]) == "! A comment!"
assert str(out[4]) == "!!$ Another comment"

# Check that passing something that isn't a comment into a Directive
# __new__ call doesn't create a Directive.
Expand Down Expand Up @@ -601,3 +601,18 @@ def test_directives_as_comments(directive, expected, free):
# Check that the comments contain the correct strings.
for i, direc in enumerate(out):
assert direc.items[0] == expected[i]


def test_inline_directive_is_comment():
"""Inline comments on statements should be comments even if containing
directive markers."""
source = """Program my_prog
integer :: x !$dir directive

x = 1 !$dir comment
end program
"""
reader = get_reader(source, ignore_comments=False, process_directives=True)
program = Program(reader)
out = walk(program, Directive)
assert len(out) == 0