Skip to content

Commit

Permalink
Add style checker
Browse files Browse the repository at this point in the history
Ref. #799
  • Loading branch information
treiher committed Oct 21, 2021
1 parent 54c508e commit bc702ef
Show file tree
Hide file tree
Showing 43 changed files with 597 additions and 250 deletions.
8 changes: 2 additions & 6 deletions ide/gnatstudio/recordflux.py
Original file line number Diff line number Diff line change
Expand Up @@ -221,18 +221,14 @@ def __on_gps_started():
r"(?P<filename>[^:]+):"
r"(?P<line>\d+):"
r"(?P<column>\d+): "
r"(?P<subsystem>core|parser|model|cli|internal|graph): "
r"(?P<subsystem>\w+): "
r"(?P<severity>info|warning|error): "
r"(?P<message>.*)"
r"$"
)

generic_message_re = re.compile(
r"^"
r"(?P<subsystem>core|parser|model|cli|internal|graph): "
r"(?P<severity>info|warning|error): "
r"(?P<message>.*)"
r"$"
r"^(?P<subsystem>\w+): (?P<severity>info|warning|error): (?P<message>.*)$"
)


Expand Down
1 change: 1 addition & 0 deletions rflx/error.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ class Subsystem(Enum):
PYRFLX = auto()
ID = auto()
VALIDATOR = auto()
STYLE = auto()

def __str__(self) -> str:
return str.lower(self.name)
Expand Down
2 changes: 1 addition & 1 deletion rflx/expression.py
Original file line number Diff line number Diff line change
Expand Up @@ -928,7 +928,7 @@ def simplified(self) -> Expr:

@property
def symbol(self) -> str:
return "**"
return " ** "

def ada_expr(self) -> ada.Expr:
return ada.Pow(self.left.ada_expr(), self.right.ada_expr())
Expand Down
2 changes: 2 additions & 0 deletions rflx/specification/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from rflx.model import declaration as decl, statement as stmt
from rflx.specification.const import RESERVED_WORDS

from . import style
from .cache import Cache

log = logging.getLogger(__name__)
Expand Down Expand Up @@ -1541,6 +1542,7 @@ def parse(self, *specfiles: Path) -> None:

for f in specfiles:
error.extend(self.__parse_specfile(f))
error.extend(style.check(f))
self.__sort_specs_topologically()
error.propagate()

Expand Down
173 changes: 173 additions & 0 deletions rflx/specification/style.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
from __future__ import annotations

import re
from pathlib import Path

from rflx.error import Location, RecordFluxError, Severity, Subsystem

INCORRECT_LINE_TERMINATORS = "\r"
ILLEGAL_WHITESPACE_CHARACTERS = "\t\x0b\x0c"
KEYWORD_INDENTATION = [
("begin", [3, 6]),
("end message", [6]),
("end", [0, 3, 6]),
("for", [3, 12, 15]),
("generic", [3]),
("goto", [9]),
("is", [3, 6]),
("message", [6]),
("package", [0]),
("session", [3]),
("state", [6]),
("then", [12]),
("transition", [6]),
("type", [3, 6]),
("with", [0, 3, 6, 9, 12, 15]),
]


def check(spec_file: Path) -> RecordFluxError:
error = RecordFluxError()

with open(spec_file, encoding="utf-8", newline="") as f:
specification = f.read()

if not specification:
return error

blank_lines = 0
lines = specification.split("\n")

for i, l in enumerate(lines, start=1):
strings = [range(m.start(), m.end()) for m in re.finditer(r'"[^"]*"', l.split("--")[0])]

blank_lines = _check_blank_lines(error, l, i, spec_file, blank_lines, len(lines))
_check_characters(error, l, i, spec_file, strings)
_check_indentation(error, l, i, spec_file)
_check_line_length(error, l, i, spec_file)
_check_token_spacing(error, l, i, spec_file, strings)
_check_trailing_spaces(error, l, i, spec_file)

return error


def _append(error: RecordFluxError, message: str, row: int, col: int, spec_file: Path) -> None:
error.extend(
[
(
message,
Subsystem.STYLE,
Severity.ERROR,
Location((row, col), spec_file),
)
]
)


def _check_blank_lines(
error: RecordFluxError, line: str, row: int, spec_file: Path, blank_lines: int, row_count: int
) -> int:
if line == "":
if row == 1:
_append(error, "leading blank line", row, 1, spec_file)
if blank_lines > 0 and row == row_count:
_append(error, "trailing blank line", row - 1, 1, spec_file)
blank_lines += 1
else:
if blank_lines > 1:
_append(error, "multiple blank lines", row - 1, 1, spec_file)
blank_lines = 0

return blank_lines


def _check_characters(
error: RecordFluxError, line: str, row: int, spec_file: Path, strings: list[range]
) -> None:
for j, c in enumerate(line, start=1):
if c == INCORRECT_LINE_TERMINATORS:
s = repr(c).replace("'", '"')
_append(error, f"incorrect line terminator {s}", row, j, spec_file)
if c in ILLEGAL_WHITESPACE_CHARACTERS:
s = repr(c).replace("'", '"')
_append(error, f"illegal whitespace character {s}", row, j, spec_file)

for j, c in enumerate(line.split("--")[0], start=1):
if any(j in r for r in strings):
continue


def _check_indentation(error: RecordFluxError, line: str, row: int, spec_file: Path) -> None:
match = re.match(r" *", line)
assert match
indentation = match.end()

for keyword, indentations in KEYWORD_INDENTATION:
if re.match(rf" *{keyword}", line) and indentation not in indentations:
expected_indentation = (
", ".join(str(row) for row in indentations[:-1]) + f" or {indentations[-1]}"
if len(indentations) > 1
else str(indentations[0])
)
_append(
error,
f"unexpected keyword indentation (expected {expected_indentation})",
row,
match.end(),
spec_file,
)


def _check_token_spacing(
error: RecordFluxError, line: str, row: int, spec_file: Path, strings: list[range]
) -> None:
for match in re.finditer(
r"--+|/?=(?!>)|<=?|>=?|=>|:=|\+|[(]+\-\d+[)]+|\-|/|\*\*?|::?|'|;|,",
line,
):
if "--" in line and match.start() > line.find("--"):
continue

if any(match.start() in r for r in strings) or any(match.end() in r for r in strings):
# ISSUE: nedbat/coveragepy#772
# A dummy statement is needed to disable the peephole optimizer, so that the continue
# statement is detected during coverage analysis.
# CPython 3.8 and 3.9 are affected. The issue is fixed in CPython 3.10.
dummy = 0 # noqa: F841
continue

token = match.group(0)

if token in [";", ","]:
space_before = False
space_after = True
elif token in ["::", "'"]:
space_before = False
space_after = False
else:
space_before = True
space_after = True

if space_before:
assert token != ";"
if match.start() > 1 and line[match.start() - 1] not in " (":
_append(error, f'missing space before "{token}"', row, match.start() + 1, spec_file)
else:
if match.start() > 1 and line[match.start() - 1] == " ":
_append(error, f'space before "{token}"', row, match.start() + 1, spec_file)
if space_after:
if match.end() < len(line) and line[match.end()] not in " ;\n":
_append(error, f'missing space after "{token}"', row, match.end() + 1, spec_file)
else:
if match.end() < len(line) and line[match.end()] == " ":
_append(error, f'space after "{token}"', row, match.end() + 2, spec_file)


def _check_trailing_spaces(error: RecordFluxError, line: str, row: int, spec_file: Path) -> None:
if line.endswith(" "):
_append(error, "trailing whitespace", row, len(line), spec_file)


def _check_line_length(error: RecordFluxError, line: str, row: int, spec_file: Path) -> None:
if len(line) > 120:
_append(error, f"line too long ({len(line)}/120)", row, 121, spec_file)
2 changes: 1 addition & 1 deletion tests/data/specs/always_valid_aspect.rflx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ package Always_Valid_Aspect is
V6 => 6)
with Size => 8, Always_Valid;

type T is mod 2**8;
type T is mod 2 ** 8;

type Message is
message
Expand Down
6 changes: 3 additions & 3 deletions tests/data/specs/checksum_message.rflx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package Checksum_Message is

type A is mod 2**8;
type B is mod 2**8;
type C is mod 2**8;
type A is mod 2 ** 8;
type B is mod 2 ** 8;
type C is mod 2 ** 8;

type Message is
message
Expand Down
6 changes: 3 additions & 3 deletions tests/data/specs/ethernet.rflx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package Ethernet is

type Address is mod 2**48;
type Type_Length is range 46 .. 2**16 - 1 with Size => 16;
type Address is mod 2 ** 48;
type Type_Length is range 46 .. 2 ** 16 - 1 with Size => 16;
type TPID is range 16#8100# .. 16#8100# with Size => 16;
type TCI is mod 2**16;
type TCI is mod 2 ** 16;

type Frame is
message
Expand Down
12 changes: 6 additions & 6 deletions tests/data/specs/icmp.rflx
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,12 @@ package ICMP is
) with Size => 8;

type Code_Zero is range 0 .. 0 with Size => 8;
type Checksum is mod 2**16;
type Identifier is mod 2**16;
type Sequence_Number is mod 2**16;
type Pointer is mod 2**8;
type Timestamp is mod 2**32;
type Gateway_Internet_Address is mod 2**32;
type Checksum is mod 2 ** 16;
type Identifier is mod 2 ** 16;
type Sequence_Number is mod 2 ** 16;
type Pointer is mod 2 ** 8;
type Timestamp is mod 2 ** 32;
type Gateway_Internet_Address is mod 2 ** 32;
type Unused_32 is range 0 .. 0 with Size => 32;
type Unused_24 is range 0 .. 0 with Size => 24;

Expand Down
20 changes: 10 additions & 10 deletions tests/data/specs/ipv4.rflx
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,19 @@ package IPv4 is

type Version is range 4 .. 4 with Size => 4;
type IHL is range 5 .. 15 with Size => 4;
type DCSP is mod 2**6;
type ECN is mod 2**2;
type Total_Length is mod 2**16;
type Identification is mod 2**16;
type Fragment_Offset is mod 2**13;
type TTL is mod 2**8;
type DCSP is mod 2 ** 6;
type ECN is mod 2 ** 2;
type Total_Length is mod 2 ** 16;
type Identification is mod 2 ** 16;
type Fragment_Offset is mod 2 ** 13;
type TTL is mod 2 ** 8;
type Protocol is (P_ICMP => 1, P_UDP => 17) with Size => 8, Always_Valid;
type Header_Checksum is mod 2**16;
type Address is mod 2**32;
type Header_Checksum is mod 2 ** 16;
type Address is mod 2 ** 32;

type Option_Class is (Control => 0, Debugging_And_Measurement => 2) with Size => 2;
type Option_Number is mod 2**5;
type Option_Length is range 2 .. 2**8 - 1 with Size => 8;
type Option_Number is mod 2 ** 5;
type Option_Length is range 2 .. 2 ** 8 - 1 with Size => 8;

type Option is
message
Expand Down
2 changes: 1 addition & 1 deletion tests/data/specs/message_in_message.rflx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package Message_In_Message is

type Length is mod 2**16;
type Length is mod 2 ** 16;

type Length_Value is
message
Expand Down
2 changes: 1 addition & 1 deletion tests/data/specs/message_size.rflx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package Message_Size is

type T is mod 2**8;
type T is mod 2 ** 8;

type Msg is
message
Expand Down
2 changes: 1 addition & 1 deletion tests/data/specs/message_type_size_condition.rflx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package Message_Type_Size_Condition is

type T1 is mod 2**8;
type T1 is mod 2 ** 8;
type Message is
message
F1 : T1
Expand Down
20 changes: 10 additions & 10 deletions tests/data/specs/multiple_errors.rflx
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
package Multiple_Errors is
type INVALID is mod 2**8;
type INVALID is mod 2**8;
type INVALID is mod 2**8;
type INVALID is mod 2**8;
type INVALID is mod 2**8;
type INVALID is mod 2**8;
type INVALID is mod 2**8;
type INVALID is mod 2**8;
type INVALID is mod 2**8;
type INVALID is mod 2**8;
type INVALID is mod 2 ** 8;
type INVALID is mod 2 ** 8;
type INVALID is mod 2 ** 8;
type INVALID is mod 2 ** 8;
type INVALID is mod 2 ** 8;
type INVALID is mod 2 ** 8;
type INVALID is mod 2 ** 8;
type INVALID is mod 2 ** 8;
type INVALID is mod 2 ** 8;
type INVALID is mod 2 ** 8;
end Multiple_Errors;
2 changes: 1 addition & 1 deletion tests/data/specs/no_conditionals.rflx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package No_Conditionals is

type Tag is mod 2**16;
type Tag is mod 2 ** 16;

type Message is
message
Expand Down
2 changes: 1 addition & 1 deletion tests/data/specs/p1.rflx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package P1 is

type Kind is mod 2**16;
type Kind is mod 2 ** 16;

type Frame is
message
Expand Down
2 changes: 1 addition & 1 deletion tests/data/specs/p2.rflx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ with P4;

package P2 is

type Length is mod 2**16;
type Length is mod 2 ** 16;

type Packet is
message
Expand Down
2 changes: 1 addition & 1 deletion tests/data/specs/p3.rflx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ with P2;

package P3 is

type T is mod 2**32;
type T is mod 2 ** 32;

type Packet is
message
Expand Down
2 changes: 1 addition & 1 deletion tests/data/specs/p4.rflx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
package P4 is

type Protocol_Number is mod 2**16;
type Protocol_Number is mod 2 ** 16;

end P4;
Loading

0 comments on commit bc702ef

Please sign in to comment.