Skip to content

Commit 876ce49

Browse files
hyperpolymathclaude
andcommitted
chore: fix, Rust, lint/fmt, issues
Batch Justfile audit: standardised naming (lowercase→Justfile), fixed parse errors, removed useless build-riscv from non-Rust repos, added missing assail recipe, and fixed code quality issues. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 0377941 commit 876ce49

9 files changed

Lines changed: 301 additions & 119 deletions

File tree

src/abi/mod.rs

Lines changed: 59 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -224,31 +224,54 @@ pub enum Violation {
224224
impl fmt::Display for Violation {
225225
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
226226
match self {
227-
Self::Leak { resource_name, allocation_site } => {
228-
write!(f, "LEAK: '{}' allocated at {} was never deallocated", resource_name, allocation_site)
227+
Self::Leak {
228+
resource_name,
229+
allocation_site,
230+
} => {
231+
write!(
232+
f,
233+
"LEAK: '{}' allocated at {} was never deallocated",
234+
resource_name, allocation_site
235+
)
229236
}
230-
Self::DoubleFree { resource_name, first_free, second_free } => {
237+
Self::DoubleFree {
238+
resource_name,
239+
first_free,
240+
second_free,
241+
} => {
231242
write!(
232243
f,
233244
"DOUBLE-FREE: '{}' freed at {} and again at {}",
234245
resource_name, first_free, second_free
235246
)
236247
}
237-
Self::RegionEscape { resource_name, region, escape_site } => {
248+
Self::RegionEscape {
249+
resource_name,
250+
region,
251+
escape_site,
252+
} => {
238253
write!(
239254
f,
240255
"REGION-ESCAPE: '{}' in region '{}' escapes at {}",
241256
resource_name, region, escape_site
242257
)
243258
}
244-
Self::RegionLinearNotConsumed { resource_name, region, allocation_site } => {
259+
Self::RegionLinearNotConsumed {
260+
resource_name,
261+
region,
262+
allocation_site,
263+
} => {
245264
write!(
246265
f,
247266
"REGION-LINEAR-NOT-CONSUMED: '{}' in region '{}' allocated at {} not consumed before region exit",
248267
resource_name, region, allocation_site
249268
)
250269
}
251-
Self::UseAfterFree { resource_name, free_site, use_site } => {
270+
Self::UseAfterFree {
271+
resource_name,
272+
free_site,
273+
use_site,
274+
} => {
252275
write!(
253276
f,
254277
"USE-AFTER-FREE: '{}' freed at {} but used at {}",
@@ -290,17 +313,26 @@ impl AnalysisResult {
290313

291314
/// Count violations by category.
292315
pub fn leak_count(&self) -> usize {
293-
self.violations.iter().filter(|v| matches!(v, Violation::Leak { .. })).count()
316+
self.violations
317+
.iter()
318+
.filter(|v| matches!(v, Violation::Leak { .. }))
319+
.count()
294320
}
295321

296322
/// Count double-free violations.
297323
pub fn double_free_count(&self) -> usize {
298-
self.violations.iter().filter(|v| matches!(v, Violation::DoubleFree { .. })).count()
324+
self.violations
325+
.iter()
326+
.filter(|v| matches!(v, Violation::DoubleFree { .. }))
327+
.count()
299328
}
300329

301330
/// Count use-after-free violations.
302331
pub fn use_after_free_count(&self) -> usize {
303-
self.violations.iter().filter(|v| matches!(v, Violation::UseAfterFree { .. })).count()
332+
self.violations
333+
.iter()
334+
.filter(|v| matches!(v, Violation::UseAfterFree { .. }))
335+
.count()
304336
}
305337
}
306338

@@ -324,7 +356,10 @@ mod tests {
324356
("allocation", ResourceKind::Allocation),
325357
("gpu-buffer", ResourceKind::GpuBuffer),
326358
("db-connection", ResourceKind::DbConnection),
327-
("custom-thing", ResourceKind::Custom("custom-thing".to_string())),
359+
(
360+
"custom-thing",
361+
ResourceKind::Custom("custom-thing".to_string()),
362+
),
328363
];
329364
for (s, expected) in &kinds {
330365
let parsed = ResourceKind::from_str_loose(s);
@@ -336,7 +371,10 @@ mod tests {
336371
/// Verify ownership state display formatting.
337372
#[test]
338373
fn test_ownership_state_display() {
339-
assert_eq!(format!("{}", OwnershipState::Uninitialized), "uninitialized");
374+
assert_eq!(
375+
format!("{}", OwnershipState::Uninitialized),
376+
"uninitialized"
377+
);
340378
assert_eq!(format!("{}", OwnershipState::Owned), "owned");
341379
assert_eq!(format!("{}", OwnershipState::Borrowed), "borrowed");
342380
assert_eq!(format!("{}", OwnershipState::Consumed), "consumed");
@@ -345,7 +383,11 @@ mod tests {
345383
/// Verify violation display formatting includes location info.
346384
#[test]
347385
fn test_violation_display() {
348-
let loc = SourceLocation { file: "test.rs".to_string(), line: 10, column: 5 };
386+
let loc = SourceLocation {
387+
file: "test.rs".to_string(),
388+
line: 10,
389+
column: 5,
390+
};
349391
let leak = Violation::Leak {
350392
resource_name: "fd".to_string(),
351393
allocation_site: loc.clone(),
@@ -357,7 +399,11 @@ mod tests {
357399
/// Verify AnalysisResult counting methods.
358400
#[test]
359401
fn test_analysis_result_counts() {
360-
let loc = SourceLocation { file: "t.rs".to_string(), line: 1, column: 0 };
402+
let loc = SourceLocation {
403+
file: "t.rs".to_string(),
404+
line: 1,
405+
column: 0,
406+
};
361407
let mut result = AnalysisResult::new();
362408
assert!(result.is_clean());
363409

src/codegen/analyzer.rs

Lines changed: 60 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@
1616

1717
use std::collections::HashMap;
1818

19-
use crate::abi::{AnalysisResult, LinearResource, OwnershipState, ResourceKind, SourceLocation, Violation};
19+
use crate::abi::{
20+
AnalysisResult, LinearResource, OwnershipState, ResourceKind, SourceLocation, Violation,
21+
};
2022
use crate::codegen::parser::{CallSite, CallSiteKind};
2123
use crate::manifest::{AnalysisConfig, ResourceEntry};
2224

@@ -89,58 +91,58 @@ pub fn analyse(
8991
CallSiteKind::Deallocation => {
9092
result.deallocation_count += 1;
9193

92-
if let Some(ref binding) = site.binding {
93-
if let Some(var) = tracked.get_mut(binding) {
94-
match var.state {
95-
OwnershipState::Owned | OwnershipState::Borrowed => {
96-
// Valid deallocation — transition to Consumed.
97-
var.state = OwnershipState::Consumed;
98-
var.deallocation_site = Some(site.location.clone());
99-
}
100-
OwnershipState::Consumed => {
101-
// Double-free: already deallocated.
102-
if config.detect_double_free {
103-
result.violations.push(Violation::DoubleFree {
104-
resource_name: var.resource_name.clone(),
105-
first_free: var
106-
.deallocation_site
107-
.clone()
108-
.unwrap_or_else(|| site.location.clone()),
109-
second_free: site.location.clone(),
110-
});
111-
}
94+
if let Some(ref binding) = site.binding
95+
&& let Some(var) = tracked.get_mut(binding)
96+
{
97+
match var.state {
98+
OwnershipState::Owned | OwnershipState::Borrowed => {
99+
// Valid deallocation — transition to Consumed.
100+
var.state = OwnershipState::Consumed;
101+
var.deallocation_site = Some(site.location.clone());
102+
}
103+
OwnershipState::Consumed => {
104+
// Double-free: already deallocated.
105+
if config.detect_double_free {
106+
result.violations.push(Violation::DoubleFree {
107+
resource_name: var.resource_name.clone(),
108+
first_free: var
109+
.deallocation_site
110+
.clone()
111+
.unwrap_or_else(|| site.location.clone()),
112+
second_free: site.location.clone(),
113+
});
112114
}
113-
OwnershipState::Uninitialized => {
114-
// Deallocating an uninitialized resource — treat as use-after-free
115-
// (the resource was never properly allocated under this binding).
116-
if config.detect_use_after_free {
117-
result.violations.push(Violation::UseAfterFree {
118-
resource_name: var.resource_name.clone(),
119-
free_site: site.location.clone(),
120-
use_site: site.location.clone(),
121-
});
122-
}
115+
}
116+
OwnershipState::Uninitialized => {
117+
// Deallocating an uninitialized resource — treat as use-after-free
118+
// (the resource was never properly allocated under this binding).
119+
if config.detect_use_after_free {
120+
result.violations.push(Violation::UseAfterFree {
121+
resource_name: var.resource_name.clone(),
122+
free_site: site.location.clone(),
123+
use_site: site.location.clone(),
124+
});
123125
}
124126
}
125127
}
126-
// If binding not tracked, it may be an external resource — skip for now.
127128
}
129+
// If binding not tracked, it may be an external resource — skip for now.
128130
}
129131
CallSiteKind::Usage => {
130132
// Check for use-after-free.
131-
if let Some(ref binding) = site.binding {
132-
if let Some(var) = tracked.get(binding) {
133-
if var.state == OwnershipState::Consumed && config.detect_use_after_free {
134-
result.violations.push(Violation::UseAfterFree {
135-
resource_name: var.resource_name.clone(),
136-
free_site: var
137-
.deallocation_site
138-
.clone()
139-
.unwrap_or_else(|| site.location.clone()),
140-
use_site: site.location.clone(),
141-
});
142-
}
143-
}
133+
if let Some(ref binding) = site.binding
134+
&& let Some(var) = tracked.get(binding)
135+
&& var.state == OwnershipState::Consumed
136+
&& config.detect_use_after_free
137+
{
138+
result.violations.push(Violation::UseAfterFree {
139+
resource_name: var.resource_name.clone(),
140+
free_site: var
141+
.deallocation_site
142+
.clone()
143+
.unwrap_or_else(|| site.location.clone()),
144+
use_site: site.location.clone(),
145+
});
144146
}
145147
}
146148
}
@@ -190,7 +192,11 @@ mod tests {
190192

191193
/// Helper to create a source location.
192194
fn loc(file: &str, line: usize) -> SourceLocation {
193-
SourceLocation { file: file.to_string(), line, column: 0 }
195+
SourceLocation {
196+
file: file.to_string(),
197+
line,
198+
column: 0,
199+
}
194200
}
195201

196202
#[test]
@@ -211,7 +217,10 @@ mod tests {
211217
},
212218
];
213219
let result = analyse(&sites, &resources, &AnalysisConfig::default());
214-
assert!(result.is_clean(), "Clean alloc/dealloc should produce no violations");
220+
assert!(
221+
result.is_clean(),
222+
"Clean alloc/dealloc should produce no violations"
223+
);
215224
assert_eq!(result.allocation_count, 1);
216225
assert_eq!(result.deallocation_count, 1);
217226
}
@@ -299,6 +308,9 @@ mod tests {
299308
..Default::default()
300309
};
301310
let result = analyse(&sites, &resources, &config);
302-
assert!(result.is_clean(), "Leak detection disabled — should find no violations");
311+
assert!(
312+
result.is_clean(),
313+
"Leak detection disabled — should find no violations"
314+
);
303315
}
304316
}

src/codegen/mod.rs

Lines changed: 31 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,11 @@ pub fn analyse_manifest(manifest: &Manifest, base_dir: &str) -> Result<AnalysisR
4747
all_sites.extend(sites);
4848
}
4949

50-
Ok(analyzer::analyse(&all_sites, &manifest.resources, &manifest.analysis))
50+
Ok(analyzer::analyse(
51+
&all_sites,
52+
&manifest.resources,
53+
&manifest.analysis,
54+
))
5155
}
5256

5357
/// Generate Ephapax linear type wrappers and analysis report.
@@ -93,10 +97,7 @@ pub fn generate_all(manifest: &Manifest, output_dir: &str) -> Result<()> {
9397
println!(" Generated: {}", report_path.display());
9498

9599
// Print summary.
96-
println!(
97-
"\n Analysis summary for '{}':",
98-
manifest.project.name
99-
);
100+
println!("\n Analysis summary for '{}':", manifest.project.name);
100101
println!(" Resources tracked: {}", result.tracked_resources.len());
101102
println!(" Allocations found: {}", result.allocation_count);
102103
println!(" Deallocations found: {}", result.deallocation_count);
@@ -110,7 +111,10 @@ pub fn generate_all(manifest: &Manifest, output_dir: &str) -> Result<()> {
110111
}
111112
}
112113
Err(e) => {
113-
println!(" Note: Source analysis skipped ({}). Wrappers generated from manifest only.", e);
114+
println!(
115+
" Note: Source analysis skipped ({}). Wrappers generated from manifest only.",
116+
e
117+
);
114118
}
115119
}
116120

@@ -125,11 +129,20 @@ fn format_report(result: &AnalysisResult, manifest: &Manifest) -> String {
125129
}
126130
_ => {
127131
let mut report = String::new();
128-
report.push_str(&format!("ephapaxiser analysis report for '{}'\n", manifest.project.name));
129-
report.push_str(&format!("========================================\n\n"));
132+
report.push_str(&format!(
133+
"ephapaxiser analysis report for '{}'\n",
134+
manifest.project.name
135+
));
136+
report.push_str("========================================\n\n");
130137
report.push_str(&format!("Allocations found: {}\n", result.allocation_count));
131-
report.push_str(&format!("Deallocations found: {}\n", result.deallocation_count));
132-
report.push_str(&format!("Resources tracked: {}\n\n", result.tracked_resources.len()));
138+
report.push_str(&format!(
139+
"Deallocations found: {}\n",
140+
result.deallocation_count
141+
));
142+
report.push_str(&format!(
143+
"Resources tracked: {}\n\n",
144+
result.tracked_resources.len()
145+
));
133146

134147
if result.is_clean() {
135148
report.push_str("No violations detected. All resources used exactly once.\n");
@@ -147,14 +160,20 @@ fn format_report(result: &AnalysisResult, manifest: &Manifest) -> String {
147160

148161
/// Placeholder build command (Phase 2 will compile generated wrappers).
149162
pub fn build(manifest: &Manifest, _release: bool) -> Result<()> {
150-
println!("Building ephapaxiser wrappers for: {}", manifest.project.name);
163+
println!(
164+
"Building ephapaxiser wrappers for: {}",
165+
manifest.project.name
166+
);
151167
println!(" (Phase 1: wrappers are generated as source — compile them with your project)");
152168
Ok(())
153169
}
154170

155171
/// Placeholder run command (Phase 2 will execute analysis as a standalone pass).
156172
pub fn run(manifest: &Manifest, _args: &[String]) -> Result<()> {
157-
println!("Running ephapaxiser analysis for: {}", manifest.project.name);
173+
println!(
174+
"Running ephapaxiser analysis for: {}",
175+
manifest.project.name
176+
);
158177
println!(" (Use 'ephapaxiser generate' to produce wrappers and analysis report)");
159178
Ok(())
160179
}

0 commit comments

Comments
 (0)