Skip to content

Commit

Permalink
Check for duplicate aspects
Browse files Browse the repository at this point in the history
Closes #714
  • Loading branch information
senier committed Aug 23, 2022
1 parent 73c85f4 commit 72b14fc
Show file tree
Hide file tree
Showing 2 changed files with 118 additions and 7 deletions.
72 changes: 65 additions & 7 deletions rflx/specification/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -545,13 +545,40 @@ def create_channel_decl(
assert isinstance(declaration, lang.FormalChannelDecl)
readable = False
writable = False

grouped = defaultdict(list)
for p in declaration.f_parameters:
if p.kind_name == "Readable":
grouped[p.kind_name].append(node_location(p, filename))

for name, locations in grouped.items():
if len(locations) > 1:
error = RecordFluxError()
error.extend(
[
(
f'duplicate channel parameter "{name}"',
Subsystem.PARSER,
Severity.ERROR,
locations[-1],
),
*[
(
"previous location",
Subsystem.PARSER,
Severity.INFO,
l,
)
for l in locations[:-1]
],
],
)
error.propagate()
if name == "Readable":
readable = True
elif p.kind_name == "Writable":
elif name == "Writable":
writable = True
else:
raise NotImplementedError(f"channel parameter: {p.kind_name}")
raise NotImplementedError(f"channel parameter: {name}")
return decl.ChannelDeclaration(
create_id(declaration.f_identifier, filename),
readable=readable,
Expand Down Expand Up @@ -1023,21 +1050,52 @@ def create_message_structure(
error: RecordFluxError, fields: lang.MessageFields, filename: Path
) -> List[model.Link]:
def extract_aspect(aspects: lang.AspectList) -> Tuple[expr.Expr, expr.Expr]:

size: expr.Expr = expr.UNDEFINED
first: expr.Expr = expr.UNDEFINED

grouped = defaultdict(list)
for aspect in aspects:
if aspect.f_identifier.text == "Size":
grouped[aspect.f_identifier.text].append(
(aspect, node_location(aspect.f_identifier, filename))
)

for name, locations in grouped.items():
if len(locations) > 1:
error.extend(
[
(
f'duplicate aspect "{name}"',
Subsystem.PARSER,
Severity.ERROR,
locations[-1][1],
),
*[
(
"previous location",
Subsystem.PARSER,
Severity.INFO,
l,
)
for _, l in locations[:-1]
],
],
)

aspect, location = locations[0]

if name == "Size":
size = create_math_expression(aspect.f_value, filename)
elif aspect.f_identifier.text == "First":
elif name == "First":
first = create_math_expression(aspect.f_value, filename)
else:
error.extend(
[
(
f'invalid aspect "{aspect.f_identifier.text}"',
f'invalid aspect "{name}"',
Subsystem.PARSER,
Severity.ERROR,
node_location(aspect.f_identifier, filename),
location,
)
],
)
Expand Down
53 changes: 53 additions & 0 deletions tests/unit/specification/parser_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -2826,3 +2826,56 @@ def test_parse_reserved_word_as_channel_name() -> None:
end Test;
"""
)


def test_parse_error_duplicate_message_aspect() -> None:
assert_error_string(
"""\
package Test is
type T is mod 2 ** 8;
type M is
message
A : T;
B : Opaque
with First => A'First, Size => A'Size, First => A'First, Size => A'Size;
end message;
end Test;
""",
r'^<stdin>:7:52: parser: error: duplicate aspect "First"\n'
"<stdin>:7:18: parser: info: previous location\n"
'<stdin>:7:70: parser: error: duplicate aspect "Size"\n'
"<stdin>:7:36: parser: info: previous location$",
)


def test_parse_error_duplicate_channel_decl_aspect() -> None:
assert_error_string(
"""\
package Test is
type T is mod 2 ** 8;
type Message is
message
A : T;
end message;
generic
C : Channel with Readable, Writable, Readable;
session S with
Initial => I,
Final => F
is
M : Test::Message;
begin
state I
is
begin
C'Read (M);
transition
goto F
end I;
state F is null state;
end S;
end Test;
""",
r'^<stdin>:8:44: parser: error: duplicate channel parameter "Readable"\n'
"<stdin>:8:24: parser: info: previous location$",
)

0 comments on commit 72b14fc

Please sign in to comment.