Skip to content

Commit

Permalink
Improve naked literal handling of array indeces (#127)
Browse files Browse the repository at this point in the history
* Replace `setup.py` script with `stylist.toml` configuration file.

* Turned out the `.toml` file wasn't being used.

* Discovered mistake in pull request workflow.

* Since `setup.py` has been removed we can't check it with `mypy`

* Added Conda Forge badge to ReadMe.

* Constrain fParser version.

* Remove reference to setup.py from documentation.

* Added test for fault condition.

* Implement solution.

* Style issue.

* Problem with array indexing.

* Reworked for improved working.

* Fixed documentation.

* Nail Sphinx version to get around problem with Read-t-docs theme
  • Loading branch information
MatthewHambley authored Sep 26, 2023
1 parent c01def9 commit 8fdcf9f
Show file tree
Hide file tree
Showing 4 changed files with 106 additions and 14 deletions.
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ dynamic = ['version']
dev = ['check-manifest', 'flake8']
test = ['pytest', 'pytest-cov', 'mypy']
performance = ['pytest', 'pytest-benchmark', 'matplotlib']
docs = ['sphinx',
docs = ['sphinx < 7.0.0',
'sphinx-autodoc-typehints',
'sphinx-rtd-theme>=1.2.2']
release = ['setuptools', 'wheel', 'twine']
Expand Down
37 changes: 25 additions & 12 deletions source/stylist/fortran.py
Original file line number Diff line number Diff line change
Expand Up @@ -690,22 +690,35 @@ def examine_fortran(self, subject: FortranSource) -> List[Issue]:
candidates.extend(fp_walk(subject.get_tree(),
Fortran2003.Real_Literal_Constant))

for constant in candidates:
if constant.items[1] is None:
if isinstance(constant.parent, Fortran2003.Assignment_Stmt):
name = str(fp_get_child(constant.parent, Fortran2003.Name))
message = f'Literal value assigned to "{name}"' \
' without kind'
elif isinstance(constant.parent.parent,
(Fortran2003.Entity_Decl,
Fortran2003.Component_Decl)):
name = str(fp_get_child(constant.parent.parent,
for literal in candidates:
if literal.items[1] is not None: # Skip when kind is present
continue

name: Optional[str] = None
parent = literal.parent
while parent is not None:
if isinstance(parent, Fortran2003.Part_Ref):
name = str(fp_get_child(parent,
Fortran2003.Name))
message = f'Literal value index used with "{name}"' \
' without kind'
break
elif isinstance(parent, (Fortran2003.Assignment_Stmt,
Fortran2003.Entity_Decl,
Fortran2003.Component_Decl)):
array_slice = fp_get_child(parent, Fortran2003.Part_Ref)
if array_slice is None:
name = str(fp_get_child(parent, Fortran2003.Name))
else:
name = str(fp_get_child(array_slice, Fortran2003.Name))
message = f'Literal value assigned to "{name}"' \
' without kind'
break
else:
message = 'Literal value without "kind"'
issues.append(Issue(message, line=_line(constant)))
parent = parent.parent
if name is None:
message = 'Literal value without "kind"'
issues.append(Issue(message, line=_line(literal)))

return issues

Expand Down
2 changes: 1 addition & 1 deletion source/stylist/style.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class Style(ABC):

def __init__(self, *rules: Rule) -> None:
"""
:param *args: Rules which make up this style.
:param rules: Rules which make up this style.
"""
self.__rules = list(rules)
self.__name = f"Unnamed style {self.__unnamed_tally}"
Expand Down
79 changes: 79 additions & 0 deletions unit-tests/fortran_naked_literal_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -259,3 +259,82 @@ def test_float(self):
test_unit = NakedLiteral(integers=False)
issues = test_unit.examine(source)
assert sorted([str(issue) for issue in issues]) == self._expected_float

__array_text = dedent('''
module array_mod
use iso_fortran_env, only : int64, int32, real64, real32
implicit none
integer(int32) :: integer_32_good(2_int32), integer_32_bad(2)
integer(kind=int64) :: integer_64_good(2_int64), integer_64_bad(2)
real(real32) :: real_32_good(2_int32), real_32_bad(2)
real(kind=real64) :: real_64_good(2_int64), real_64_bad(2)
contains
subroutine assign_values()
implicit none
integer_32_good = (/12_int32, 24_int32/)
integer_32_bad = (/13, 26/)
integer_64_good = (/14_int64, 28_int64/)
integer_64_bad = (/15, 30/)
real_32_good = (/2.3_real32, 4.6_real32/)
real_32_bad = (/3.4, 6.8/)
real_64_good = (/4.5_real64, 6.0_real64/)
real_64_bad = (/5.6, 11.2/)
end subroutine assign_values
subroutine assign_elements()
integer_32_good(1_int32) = 4_int32
integer_32_bad(2) = 5
integer_64_good(1_int64) = 6_int64
integer_64_bad(2) = 7
real_32_good(1_int32) = 8.2_real32
real_32_bad(2) = 16.4
real_64_good(1_int64) = 9.3_real64
real_64_bad(2) = 18.6
end subroutine
subroutine array_slices()
integer_32_bad(1:1) = 20_int32
integer_64_bad(1:1) = 21_int64
real_32_bad(1:1) = 22.2_real32
real_64_bad(1:1) = 23.3_real64
end subroutine
end module array_mod
''')

__expected_array = [
'13: Literal value assigned to "integer_32_bad" without kind',
'13: Literal value assigned to "integer_32_bad" without kind',
'15: Literal value assigned to "integer_64_bad" without kind',
'15: Literal value assigned to "integer_64_bad" without kind',
'17: Literal value assigned to "real_32_bad" without kind',
'17: Literal value assigned to "real_32_bad" without kind',
'19: Literal value assigned to "real_64_bad" without kind',
'19: Literal value assigned to "real_64_bad" without kind',
'23: Literal value assigned to "integer_32_bad" without kind',
'23: Literal value index used with "integer_32_bad" without kind',
'25: Literal value assigned to "integer_64_bad" without kind',
'25: Literal value index used with "integer_64_bad" without kind',
'27: Literal value assigned to "real_32_bad" without kind',
'27: Literal value index used with "real_32_bad" without kind',
'29: Literal value assigned to "real_64_bad" without kind',
'29: Literal value index used with "real_64_bad" without kind',
'32: Literal value index used with "integer_32_bad" without kind',
'32: Literal value index used with "integer_32_bad" without kind',
'33: Literal value index used with "integer_64_bad" without kind',
'33: Literal value index used with "integer_64_bad" without kind',
'34: Literal value index used with "real_32_bad" without kind',
'34: Literal value index used with "real_32_bad" without kind',
'35: Literal value index used with "real_64_bad" without kind',
'35: Literal value index used with "real_64_bad" without kind',
'5: Literal value assigned to "integer_32_bad" without kind',
'6: Literal value assigned to "integer_64_bad" without kind',
'7: Literal value assigned to "real_32_bad" without kind',
'8: Literal value assigned to "real_64_bad" without kind'
]

def test_arrays(self):
reader = SourceStringReader(self.__array_text)
source = FortranSource(reader)

test_unit = NakedLiteral(integers=True, reals=True)
issues = test_unit.examine(source)
assert sorted([str(issue) for issue in issues]) \
== self.__expected_array

0 comments on commit 8fdcf9f

Please sign in to comment.