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
15 changes: 15 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,21 @@ jobs:
- name: cargo test --doc
run: cargo test --doc --workspace

msrv:
name: minimum supported Rust
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7

- uses: dtolnay/rust-toolchain@stable
with:
toolchain: 1.88.0

- uses: Swatinem/rust-cache@v2

- name: cargo check
run: cargo check --workspace --locked

mutants:
name: mutants on the diff, shard ${{ matrix.shard }}
runs-on: ubuntu-latest
Expand Down
5 changes: 4 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,10 @@ release notes.

### Tools

- None yet.
- The declared minimum Rust toolchain is now 1.88, matching the let-chain
syntax already used by the parser and resolver. Rust 1.87 and older failed
before `deed-lang` could compile. CI now builds the workspace with 1.88 so
the declaration cannot silently fall behind the source again.

### Measurements

Expand Down
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ version = "0.2.11"
edition = "2024"
license = "Apache-2.0"
repository = "https://github.com/deed-lang/deed"
rust-version = "1.85"
rust-version = "1.88"

# Publishing needs a version next to every path, or `cargo publish` refuses,
# and a version repeated in twenty manifests is nineteen places to forget. So
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ $ ./deed-v0.2.11-x86_64-unknown-linux-gnu/deed --version
deed 0.2.11
```

With Rust 1.85 or newer instead, from crates.io or from a clone:
With Rust 1.88 or newer instead, from crates.io or from a clone:

```
$ cargo install deed-lang
Expand Down
26 changes: 12 additions & 14 deletions crates/deed-cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2483,13 +2483,12 @@ fn resolve_imports(
let mut seed_texts: Vec<String> = Vec::new();
for path in files.iter() {
let text = std::fs::read_to_string(path).unwrap_or_default();
if let Some((module, _)) = deed_driver::imports_of(&text) {
if let Some(root) = root_of(path, &module) {
if known_roots.insert(root.clone()) {
read_manifest(&root, &mut component_roots, &mut fetched, manifests);
roots.push(root);
}
}
if let Some((module, _)) = deed_driver::imports_of(&text)
&& let Some(root) = root_of(path, &module)
&& known_roots.insert(root.clone())
{
read_manifest(&root, &mut component_roots, &mut fetched, manifests);
roots.push(root);
}
seed_texts.push(text);
}
Expand Down Expand Up @@ -2540,13 +2539,12 @@ fn resolve_imports(
let Ok(text) = std::fs::read_to_string(&candidate) else {
continue;
};
if let Some((m, _)) = deed_driver::imports_of(&text) {
if let Some(new_root) = root_of(&candidate, &m) {
if known_roots.insert(new_root.clone()) {
read_manifest(&new_root, &mut component_roots, &mut fetched, manifests);
roots.push(new_root);
}
}
if let Some((m, _)) = deed_driver::imports_of(&text)
&& let Some(new_root) = root_of(&candidate, &m)
&& known_roots.insert(new_root.clone())
{
read_manifest(&new_root, &mut component_roots, &mut fetched, manifests);
roots.push(new_root);
}
known_files.insert(candidate.clone());
new_files.push(candidate.clone());
Expand Down
2 changes: 1 addition & 1 deletion crates/deed-codegen/src/compile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -683,7 +683,7 @@ impl Strings {
let mut bytes = (text.chars().count() as i64).to_le_bytes().to_vec();
bytes.extend_from_slice(&(text.len() as i64).to_le_bytes());
bytes.extend_from_slice(text.as_bytes());
while bytes.len() % layout::WORD as usize != 0 {
while !bytes.len().is_multiple_of(layout::WORD as usize) {
bytes.push(0);
}
self.next += bytes.len() as u32;
Expand Down
28 changes: 13 additions & 15 deletions crates/deed-driver/tests/explain_pages.rs
Original file line number Diff line number Diff line change
Expand Up @@ -189,14 +189,13 @@ fn declared_codes(root: &Path) -> Vec<(String, String, String)> {
let content = rest.strip_prefix(' ').unwrap_or(rest);
doc_lines.push(content.to_string());
} else if let Some(rest) = trimmed.strip_prefix("pub const ") {
if let Some((name, rest)) = rest.split_once(':') {
if let Some(start) = rest.find("\"DEED") {
if let Some(end) = rest[start + 1..].find('"') {
let code = rest[start + 1..start + 1 + end].to_string();
let doc = doc_lines.join("\n");
codes.push((name.trim().to_string(), code, doc));
}
}
if let Some((name, rest)) = rest.split_once(':')
&& let Some(start) = rest.find("\"DEED")
&& let Some(end) = rest[start + 1..].find('"')
{
let code = rest[start + 1..start + 1 + end].to_string();
let doc = doc_lines.join("\n");
codes.push((name.trim().to_string(), code, doc));
}
doc_lines.clear();
} else if !trimmed.is_empty() && !trimmed.starts_with("//") {
Expand Down Expand Up @@ -240,14 +239,13 @@ fn collect_test_files(dir: &Path, root: &Path, out: &mut Vec<(String, String)>)
collect_test_files(&path, root, out);
} else if path.extension().is_some_and(|e| e == "rs")
&& path.file_name().is_some_and(|n| n != "codes.rs")
&& let Ok(text) = fs::read_to_string(&path)
{
if let Ok(text) = fs::read_to_string(&path) {
let rel = path
.strip_prefix(root)
.map(|p| p.to_string_lossy().replace('\\', "/"))
.unwrap_or_else(|_| path.to_string_lossy().into_owned());
out.push((rel, text));
}
let rel = path
.strip_prefix(root)
.map(|p| p.to_string_lossy().replace('\\', "/"))
.unwrap_or_else(|_| path.to_string_lossy().into_owned());
out.push((rel, text));
}
}
}
Expand Down
18 changes: 9 additions & 9 deletions crates/deed-driver/tests/publishing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -156,15 +156,15 @@ fn no_crate_names_a_sibling_by_path_alone() {
"{} names a sibling by path, which cannot be published",
path.display()
);
if let Some(name) = line.strip_suffix(".workspace = true") {
if name.starts_with("deed-") {
assert!(
names.iter().any(|declared| declared == name),
"{} inherits `{name}`, which the workspace does not declare",
path.display()
);
inherited += 1;
}
if let Some(name) = line.strip_suffix(".workspace = true")
&& name.starts_with("deed-")
{
assert!(
names.iter().any(|declared| declared == name),
"{} inherits `{name}`, which the workspace does not declare",
path.display()
);
inherited += 1;
}
}
}
Expand Down
6 changes: 2 additions & 4 deletions crates/deed-interp/src/interp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3378,10 +3378,8 @@ impl<'a> Interp<'a> {

// Keep the original failure; only replace a non-failing outcome
// with a `finally` failure.
if !body_failed {
if let Err(signal) = finally_result {
outcome = Err(signal);
}
if !body_failed && let Err(signal) = finally_result {
outcome = Err(signal);
}
}

Expand Down
4 changes: 2 additions & 2 deletions crates/deed-interp/src/property.rs
Original file line number Diff line number Diff line change
Expand Up @@ -837,10 +837,10 @@ impl<'a> TypeIndex<'a> {
match self.resolutions.def(def).kind {
DefKind::Builtin => match name.name.as_str() {
"Int" => Some(Value::Int(rng.int())),
"Bool" => Some(Value::Bool(rng.next() % 2 == 0)),
"Bool" => Some(Value::Bool(rng.next().is_multiple_of(2))),
"String" => Some(Value::Str(rng.word().into())),
"Result" => {
let ok = rng.next() % 2 == 0;
let ok = rng.next().is_multiple_of(2);
let inner =
self.generate(args.get(usize::from(!ok))?, rng, interp, depth + 1)?;
Some(if ok {
Expand Down
37 changes: 17 additions & 20 deletions crates/deed-lsp/src/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -256,11 +256,11 @@ impl Server {
// A `use` path is not a resolved name and has no type. Go to definition
// and documentLink already open the file; hover should still say what
// module it is and whether this workspace can open it.
if range.is_none() {
if let Some((path_span, about)) = self.hover_use_path(&checked, offset) {
lines.push(about);
range = Some(path_span);
}
if range.is_none()
&& let Some((path_span, about)) = self.hover_use_path(&checked, offset)
{
lines.push(about);
range = Some(path_span);
}

let Some(range) = range else {
Expand Down Expand Up @@ -2557,10 +2557,10 @@ fn comment_folds(document: &Document, trivia: &[deed_lexer::Trivia], ranges: &mu

for item in trivia {
if item.kind != deed_lexer::TriviaKind::Line {
if let Some((start, end)) = run.take() {
if end > start {
ranges.push(comment_fold(start, end));
}
if let Some((start, end)) = run.take()
&& end > start
{
ranges.push(comment_fold(start, end));
}
continue;
}
Expand All @@ -2581,10 +2581,10 @@ fn comment_folds(document: &Document, trivia: &[deed_lexer::Trivia], ranges: &mu
};
}

if let Some((start, end)) = run {
if end > start {
ranges.push(comment_fold(start, end));
}
if let Some((start, end)) = run
&& end > start
{
ranges.push(comment_fold(start, end));
}
}

Expand Down Expand Up @@ -2703,10 +2703,8 @@ fn collect_block_spans(offset: u32, block: &Block, spans: &mut Vec<Span>) {
.stmts
.iter()
.any(|stmt| collect_stmt_spans(offset, stmt, spans));
if !found {
if let Some(tail) = &block.tail {
collect_expr_spans(offset, tail, spans);
}
if !found && let Some(tail) = &block.tail {
collect_expr_spans(offset, tail, spans);
}
push_span_if_new(block.span, spans);
}
Expand Down Expand Up @@ -2797,10 +2795,9 @@ fn collect_expr_spans(offset: u32, expr: &Expr, spans: &mut Vec<Span>) -> bool {
} => {
if !collect_expr_spans(offset, condition, spans)
&& !collect_block_spans_checked(offset, then_branch, spans)
&& let Some(else_b) = else_branch
{
if let Some(else_b) = else_branch {
collect_expr_spans(offset, else_b, spans);
}
collect_expr_spans(offset, else_b, spans);
}
}
Expr::Match {
Expand Down
8 changes: 4 additions & 4 deletions crates/deed-mir/src/lower.rs
Original file line number Diff line number Diff line change
Expand Up @@ -800,10 +800,10 @@ fn instantiate_nominal(
nominals: &HashMap<String, Nominal<'_>>,
shapes: &mut Vec<crate::Layout>,
) -> Result<crate::LayoutId, Unlowered> {
if args.is_empty() {
if let Some(id) = layouts.get(name) {
return Ok(*id);
}
if args.is_empty()
&& let Some(id) = layouts.get(name)
{
return Ok(*id);
}

let nominal = match nominals.get(name) {
Expand Down
30 changes: 15 additions & 15 deletions crates/deed-parser/src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1220,18 +1220,18 @@ impl<'a> Parser<'a> {
// disambiguate and no reason to reserve the words for the rest
// of the language.
if self.eat_named("state") {
if let Some(field_name) = self.expect_ident("handler state") {
if self.expect(TokenKind::Colon, "handler state").is_some() {
let ty = self.parse_type();
if self.at(&TokenKind::Eq) {
self.state_has_no_initialiser();
}
state.push(FieldDecl {
span: field_name.span.to(ty.span()),
name: field_name,
ty,
});
if let Some(field_name) = self.expect_ident("handler state")
&& self.expect(TokenKind::Colon, "handler state").is_some()
{
let ty = self.parse_type();
if self.at(&TokenKind::Eq) {
self.state_has_no_initialiser();
}
state.push(FieldDecl {
span: field_name.span.to(ty.span()),
name: field_name,
ty,
});
}
} else if self.eat_named("finally") {
finally = Some(self.parse_block());
Expand Down Expand Up @@ -2334,10 +2334,10 @@ impl<'a> Parser<'a> {
// `xs ++ ys` and `x :: xs`, borrowed from languages where a list has
// operators. Doubled, both of them, and nothing in this grammar puts
// two of either in a row, so the shape is safe to read here.
if !self.continues_a_new_line() {
if let Some(borrowed) = self.borrowed_operator() {
lhs = self.no_such_list_operator(lhs, borrowed);
}
if !self.continues_a_new_line()
&& let Some(borrowed) = self.borrowed_operator()
{
lhs = self.no_such_list_operator(lhs, borrowed);
}

while let Some((op, bp, spelled)) = self.infix_operator() {
Expand Down
44 changes: 21 additions & 23 deletions crates/deed-typeck/src/check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1580,13 +1580,13 @@ impl<'a> Checker<'a> {
);
// The arity lives on the declaration. Without this pin the reader is
// left counting angle brackets against a name that is already known.
if let Some((file, at)) = declared_here {
if !at.is_empty() {
diagnostic = match file {
Some(other) => diagnostic.with_secondary_in(other, at, "declared here"),
None => diagnostic.with_secondary(at, "declared here"),
};
}
if let Some((file, at)) = declared_here
&& !at.is_empty()
{
diagnostic = match file {
Some(other) => diagnostic.with_secondary_in(other, at, "declared here"),
None => diagnostic.with_secondary(at, "declared here"),
};
}
self.emit(diagnostic);
false
Expand Down Expand Up @@ -4627,15 +4627,13 @@ impl<'a> Checker<'a> {
// Same pin MISSING_FIELDS already has: the declaration is
// where the real fields live, and a wrong name is the other
// half of the same question.
if let Some((file, at)) = declared_here {
if !at.is_empty() {
diagnostic = match file {
Some(other) => {
diagnostic.with_secondary_in(other, at, "declared here")
}
None => diagnostic.with_secondary(at, "declared here"),
};
}
if let Some((file, at)) = declared_here
&& !at.is_empty()
{
diagnostic = match file {
Some(other) => diagnostic.with_secondary_in(other, at, "declared here"),
None => diagnostic.with_secondary(at, "declared here"),
};
}
self.emit(diagnostic);
if let Some(value) = &init.value {
Expand Down Expand Up @@ -4686,13 +4684,13 @@ impl<'a> Checker<'a> {
.with_note(
"every field has to be given, because a partially built value is not a value",
);
if let Some((file, at)) = declared_here {
if !at.is_empty() {
diagnostic = match file {
Some(other) => diagnostic.with_secondary_in(other, at, "declared here"),
None => diagnostic.with_secondary(at, "declared here"),
};
}
if let Some((file, at)) = declared_here
&& !at.is_empty()
{
diagnostic = match file {
Some(other) => diagnostic.with_secondary_in(other, at, "declared here"),
None => diagnostic.with_secondary(at, "declared here"),
};
}
self.emit(diagnostic);
}
Expand Down