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
63 changes: 60 additions & 3 deletions crates/spar-cli/src/lsp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1899,10 +1899,9 @@ fn make_swap_connection_edit(source: &str, uri: &Uri, diag_range: &Range) -> Opt
// Look for `src -> dst` or `src <-> dst` pattern
let (arrow, arrow_len) = if let Some(pos) = line.find(" -> ") {
(pos, 4)
} else if let Some(pos) = line.find(" <-> ") {
(pos, 5)
} else {
return None;
let pos = line.find(" <-> ")?;
(pos, 5)
};

// Find the source and destination parts
Expand Down Expand Up @@ -4064,4 +4063,62 @@ mod tests {
);
assert!(has_end_name, "should find end-name on line 5: {:?}", refs);
}

// ── make_swap_connection_edit ───────────────────────────────────
//
// Cover both arrow shapes; the `<->` branch exists because the AADL
// grammar permits bidirectional `->` and `<->` connections, and the
// quick-fix has to preserve whichever arrow the source used.

#[test]
#[allow(clippy::mutable_key_type)] // Uri has interior mutability; see make_swap_connection_edit
fn swap_connection_supports_unidirectional_arrow() {
let uri: Uri = "file:///test.aadl".parse().unwrap();
let source = "package P\npublic\n system S\n features\n p_in : in data port;\n p_out : out data port;\n end S;\n system implementation S.i\n subcomponents\n sub : process Pr;\n connections\n c1 : port sub.outp -> p_out;\n end S.i;\n process Pr\n features\n outp : out data port;\n end Pr;\nend P;\n";
// Position anywhere on the connection line.
let diag_range = Range::new(Position::new(11, 10), Position::new(11, 10));
let edit = make_swap_connection_edit(source, &uri, &diag_range)
.expect("-> connection must produce a swap edit");
let changes = edit.changes.expect("edit must carry changes");
let text_edits = changes.get(&uri).expect("edits keyed by URI");
assert_eq!(text_edits.len(), 1, "one TextEdit expected");
assert!(
text_edits[0].new_text.contains(" -> "),
"arrow must be preserved, got {:?}",
text_edits[0].new_text
);
}

#[test]
#[allow(clippy::mutable_key_type)] // Uri has interior mutability; see make_swap_connection_edit
fn swap_connection_supports_bidirectional_arrow() {
// Regression for codecov/patch gap on the `<->` branch of
// make_swap_connection_edit: a bidirectional connection must also
// produce a swap edit and preserve the `<->` arrow form.
let uri: Uri = "file:///test.aadl".parse().unwrap();
let source = "package P\npublic\n bus B\n end B;\n device D\n features\n ba : requires bus access;\n end D;\n system S\n end S;\n system implementation S.i\n subcomponents\n bus1 : bus B;\n dev : device D;\n connections\n c1 : bus access bus1 <-> dev.ba;\n end S.i;\nend P;\n";
let diag_range = Range::new(Position::new(15, 10), Position::new(15, 10));
let edit = make_swap_connection_edit(source, &uri, &diag_range)
.expect("<-> connection must produce a swap edit");
let changes = edit.changes.expect("edit must carry changes");
let text_edits = changes.get(&uri).expect("edits keyed by URI");
assert_eq!(text_edits.len(), 1, "one TextEdit expected");
assert!(
text_edits[0].new_text.contains(" <-> "),
"bidirectional arrow must be preserved, got {:?}",
text_edits[0].new_text
);
}

#[test]
fn swap_connection_returns_none_when_no_arrow() {
// When the targeted line has neither `->` nor `<->`, the helper
// must bail out cleanly with None — exercises the `?` shortcut
// in the `<->` fallback branch.
let uri: Uri = "file:///test.aadl".parse().unwrap();
let source = "package P\npublic\n system S\n end S;\nend P;\n";
let diag_range = Range::new(Position::new(2, 2), Position::new(2, 2));
let edit = make_swap_connection_edit(source, &uri, &diag_range);
assert!(edit.is_none(), "no-arrow line must not produce a swap edit");
}
}
5 changes: 2 additions & 3 deletions crates/spar-hir-def/src/item_tree/lower.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1976,7 +1976,8 @@ fn lower_range_from_tokens(node: &SyntaxNode) -> Option<PropertyExpr> {
}
}

let min = if let Some(num_text) = min_num {
let min = {
let num_text = min_num?;
if min_is_real {
let display = if min_sign < 0 {
format!("-{}", num_text)
Expand All @@ -1988,8 +1989,6 @@ fn lower_range_from_tokens(node: &SyntaxNode) -> Option<PropertyExpr> {
let val = parse_aadl_integer(&num_text).unwrap_or(0) * min_sign;
PropertyExpr::Integer(val, min_unit)
}
} else {
return None;
};

let max = max_expr.unwrap_or_else(|| PropertyExpr::Opaque("?".to_string()));
Expand Down
5 changes: 1 addition & 4 deletions crates/spar-render/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -166,10 +166,7 @@ fn ancestor_at_depth(
return None;
}
let comp = instance.component(current);
match comp.parent {
Some(p) => current = p,
None => return None,
}
current = comp.parent?;
}
}

Expand Down
Loading