treewide: enforce strict clippy check
This commit is contained in:
@@ -17,3 +17,6 @@ fix-error = { path = "../fix-error" }
|
||||
fix-lang = { path = "../fix-lang" }
|
||||
fix-runtime = { path = "../fix-runtime" }
|
||||
tracing = "0.1"
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
+42
-18
@@ -114,8 +114,13 @@ impl<'a, R: VmRuntimeCtx> CompilerCtx<'a, R> {
|
||||
source,
|
||||
);
|
||||
let ir = downgrade_ctx.downgrade_toplevel(expr)?;
|
||||
// SAFETY: `ir` borrows from `bump`, which is moved into the returned
|
||||
// `OwnedIr` alongside the reference. The `'static` lifetime is a
|
||||
// storage token that is re-narrowed to the `OwnedIr`'s own borrow in
|
||||
// `as_ref`, so no reference ever outlives the arena backing it.
|
||||
let ir = unsafe { std::mem::transmute::<RawIrRef<'_>, RawIrRef<'static>>(ir) };
|
||||
Ok(OwnedIr { _bump: bump, ir })
|
||||
// SAFETY: `ir` borrows from `bump`
|
||||
Ok(unsafe { OwnedIr::new(bump, ir) })
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -153,15 +158,7 @@ impl<'a, R: VmRuntimeCtx> BytecodeContext for CompilerCtx<'a, R> {
|
||||
Bool(x) => StaticValue::new(x),
|
||||
String(x) => StaticValue::new(x),
|
||||
Path(x) => StaticValue::new(fix_runtime::Path(x)),
|
||||
PrimOp {
|
||||
id,
|
||||
arity,
|
||||
dispatch_ip,
|
||||
} => StaticValue::new(fix_runtime::PrimOp {
|
||||
id,
|
||||
arity,
|
||||
dispatch_ip,
|
||||
}),
|
||||
PrimOp(id) => StaticValue::new(fix_runtime::PrimOp::from(id)),
|
||||
Null => StaticValue::default(),
|
||||
};
|
||||
self.runtime.add_const(val)
|
||||
@@ -288,6 +285,10 @@ impl<'ctx: 'ir, 'id, 'ir, R: VmRuntimeCtx> DowngradeContext<'id, 'ir>
|
||||
self.runtime.resolve_string(id).into()
|
||||
}
|
||||
|
||||
#[expect(
|
||||
clippy::unwrap_in_result,
|
||||
reason = "exceeding u8::MAX thunk scope layers is a compiler invariant violation, not a user-facing error"
|
||||
)]
|
||||
fn lookup(
|
||||
&mut self,
|
||||
sym: StringId,
|
||||
@@ -364,6 +365,10 @@ impl<'ctx: 'ir, 'id, 'ir, R: VmRuntimeCtx> DowngradeContext<'id, 'ir>
|
||||
self.source.clone()
|
||||
}
|
||||
|
||||
#[expect(
|
||||
clippy::panic_in_result_fn,
|
||||
reason = "assert_eq guards an internal invariant (bindings and values have equal length); a mismatch is a compiler bug, not a user error"
|
||||
)]
|
||||
fn with_let_scope<F, Ret>(&mut self, keys: &[StringId], f: F) -> Result<Ret>
|
||||
where
|
||||
F: FnOnce(
|
||||
@@ -425,6 +430,10 @@ impl<'ctx: 'ir, 'id, 'ir, R: VmRuntimeCtx> DowngradeContext<'id, 'ir>
|
||||
ret
|
||||
}
|
||||
|
||||
#[expect(
|
||||
clippy::panic,
|
||||
reason = "exceeding u8::MAX thunk scope layers is a compiler invariant violation, not a user-facing error"
|
||||
)]
|
||||
fn with_thunk_scope<F, Ret>(
|
||||
&mut self,
|
||||
f: F,
|
||||
@@ -490,7 +499,6 @@ enum Scope<'ctx, 'id, 'ir> {
|
||||
Repl(&'ctx HashSet<StringId>),
|
||||
ScopedImport {
|
||||
keys: HashSet<StringId>,
|
||||
#[allow(dead_code)]
|
||||
slot_id: u32,
|
||||
},
|
||||
Let(HashMap<StringId, GhostMaybeThunkRef<'id, 'ir>>),
|
||||
@@ -534,13 +542,29 @@ impl<'a, 'ctx, 'id, 'ir, R: VmRuntimeCtx> ScopeGuard<'a, 'ctx, 'id, 'ir, R> {
|
||||
}
|
||||
}
|
||||
|
||||
struct OwnedIr {
|
||||
_bump: Bump,
|
||||
ir: RawIrRef<'static>,
|
||||
}
|
||||
mod sealed {
|
||||
use super::*;
|
||||
|
||||
impl OwnedIr {
|
||||
fn as_ref<'ir>(&'ir self) -> RawIrRef<'ir> {
|
||||
unsafe { std::mem::transmute::<RawIrRef<'static>, RawIrRef<'ir>>(self.ir) }
|
||||
pub struct OwnedIr {
|
||||
_bump: Bump,
|
||||
ir: RawIrRef<'static>,
|
||||
}
|
||||
|
||||
impl OwnedIr {
|
||||
/// # Safety
|
||||
/// `ir` must borrows from `bump`
|
||||
pub unsafe fn new(bump: Bump, ir: RawIrRef<'static>) -> Self {
|
||||
Self { _bump: bump, ir }
|
||||
}
|
||||
|
||||
pub fn as_ref<'ir>(&'ir self) -> RawIrRef<'ir> {
|
||||
// SAFETY: `self.ir`'s `'static` lifetime is a storage token; the IR is
|
||||
// backed by `self._bump`, which lives as long as `self`. Narrowing to
|
||||
// `'ir` (tied to `&self`) hands out a reference that cannot outlive the
|
||||
// arena.
|
||||
unsafe { std::mem::transmute::<RawIrRef<'static>, RawIrRef<'ir>>(self.ir) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub use sealed::OwnedIr;
|
||||
|
||||
@@ -3,7 +3,7 @@ use std::marker::PhantomData;
|
||||
|
||||
use bumpalo::Bump;
|
||||
use bumpalo::collections::Vec;
|
||||
use fix_lang::{BUILTINS, BuiltinId, StringId};
|
||||
use fix_lang::{BuiltinId, StringId};
|
||||
use ghost_cell::{GhostCell, GhostToken};
|
||||
use rnix::{TextRange, ast};
|
||||
use string_interner::DefaultStringInterner;
|
||||
@@ -222,7 +222,6 @@ pub enum Ir<'ir, R: RefExt<'ir> + ?Sized + 'ir> {
|
||||
pub struct ThunkId(pub usize);
|
||||
|
||||
/// Represents a key in an attribute path.
|
||||
#[allow(unused)]
|
||||
#[derive(Debug)]
|
||||
pub enum Attr<Ref> {
|
||||
/// A dynamic attribute key, which is an expression that must evaluate to a string.
|
||||
@@ -291,9 +290,8 @@ pub fn new_global_env(
|
||||
let builtins_sym = StringId(strings.get_or_intern("builtins"));
|
||||
global_env.insert(builtins_sym, MaybeThunk::Builtins);
|
||||
|
||||
for (idx, &(name, _)) in BUILTINS.iter().enumerate() {
|
||||
let id = BuiltinId::try_from(idx as u8).expect("infallible");
|
||||
let name = StringId(strings.get_or_intern(name));
|
||||
for id in BuiltinId::ALL {
|
||||
let name = StringId(strings.get_or_intern(id.info().global_name));
|
||||
global_env.insert(name, MaybeThunk::Builtin(id));
|
||||
}
|
||||
|
||||
|
||||
@@ -156,6 +156,10 @@ impl<'id: 'ir, 'ir, Ctx: DowngradeContext<'id, 'ir>> Downgrade<'id, 'ir, Ctx> fo
|
||||
let path = {
|
||||
let temp = self.content().require(ctx, span)?;
|
||||
let text = temp.text();
|
||||
#[expect(
|
||||
clippy::string_slice,
|
||||
reason = "PathSearch text is <...> wrapped in ASCII angle brackets, so byte indices 1 and len-1 are char boundaries"
|
||||
)]
|
||||
let id = ctx.intern_string(&text[1..text.len() - 1]);
|
||||
let expr = ctx.new_expr(Ir::Str(id));
|
||||
ctx.maybe_thunk(expr)
|
||||
@@ -463,15 +467,13 @@ impl<'id: 'ir, 'ir, Ctx: DowngradeContext<'id, 'ir>> Downgrade<'id, 'ir, Ctx> fo
|
||||
}
|
||||
|
||||
let (ret, thunks) = ctx.with_thunk_scope(|ctx| {
|
||||
let param;
|
||||
let body;
|
||||
|
||||
match raw_param {
|
||||
let (param, body) = match raw_param {
|
||||
ast::Param::IdentParam(id) => {
|
||||
let param_sym = ctx.intern_string(id.to_string());
|
||||
param = None;
|
||||
|
||||
body = ctx.with_param_scope(param_sym, |ctx| body_ast.downgrade(ctx))?;
|
||||
(
|
||||
None,
|
||||
ctx.with_param_scope(param_sym, |ctx| body_ast.downgrade(ctx))?,
|
||||
)
|
||||
}
|
||||
ast::Param::Pattern(pattern) => {
|
||||
let alias = pattern
|
||||
@@ -493,15 +495,16 @@ impl<'id: 'ir, 'ir, Ctx: DowngradeContext<'id, 'ir>> Downgrade<'id, 'ir, Ctx> fo
|
||||
body_ast.clone().downgrade(ctx)
|
||||
})?;
|
||||
|
||||
param = Some(Param {
|
||||
required,
|
||||
optional,
|
||||
ellipsis,
|
||||
});
|
||||
|
||||
body = inner_body;
|
||||
(
|
||||
Some(Param {
|
||||
required,
|
||||
optional,
|
||||
ellipsis,
|
||||
}),
|
||||
inner_body,
|
||||
)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Result::Ok(Ret { param, body })
|
||||
});
|
||||
@@ -550,6 +553,10 @@ impl<'id: 'ir, 'ir> PendingAttrSet<'ir> {
|
||||
}
|
||||
}
|
||||
|
||||
#[expect(
|
||||
clippy::indexing_slicing,
|
||||
reason = "path is non-empty here: path.first() was just unwrapped above, so path[1..] is in bounds"
|
||||
)]
|
||||
fn insert(
|
||||
&mut self,
|
||||
path: &[ast::Attr],
|
||||
@@ -651,6 +658,10 @@ impl<'id: 'ir, 'ir> PendingAttrSet<'ir> {
|
||||
) -> Result<()> {
|
||||
if !path.is_empty() {
|
||||
let mut nested = PendingAttrSet::new_in(ctx.bump());
|
||||
#[expect(
|
||||
clippy::indexing_slicing,
|
||||
reason = "path is non-empty in this branch, so path[0] and path[1..] are in bounds"
|
||||
)]
|
||||
nested.insert_dynamic(
|
||||
path[0].clone(),
|
||||
path[0].syntax().text_range(),
|
||||
@@ -665,6 +676,10 @@ impl<'id: 'ir, 'ir> PendingAttrSet<'ir> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[expect(
|
||||
clippy::unreachable,
|
||||
reason = "value was just reassigned to PendingValue::Set on the line above, so the re-match always hits the Set arm"
|
||||
)]
|
||||
fn ensure_pending_set<'a>(
|
||||
value: &'a mut PendingValue<'ir>,
|
||||
ctx: &mut impl DowngradeContext<'id, 'ir>,
|
||||
|
||||
+32
-13
@@ -1,5 +1,5 @@
|
||||
use fix_bytecode::{Const, Continuation, InstructionPtr, Op, OperandType};
|
||||
use fix_lang::{BUILTINS, StringId};
|
||||
use fix_bytecode::{Const, InstructionPtr, Op, OperandType};
|
||||
use fix_lang::StringId;
|
||||
use hashbrown::HashMap;
|
||||
use rnix::TextRange;
|
||||
use string_interner::Symbol as _;
|
||||
@@ -54,7 +54,6 @@ impl<'a, Ctx: BytecodeContext> BytecodeEmitter<'a, Ctx> {
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
fn inline_maybe_thunk(&self, val: &MaybeThunk) -> InlineOperand {
|
||||
use MaybeThunk::*;
|
||||
match *val {
|
||||
@@ -75,14 +74,7 @@ impl<'a, Ctx: BytecodeContext> BytecodeEmitter<'a, Ctx> {
|
||||
InlineOperand::Local { layer, local }
|
||||
}
|
||||
Arg { layer } => InlineOperand::Local { layer, local: 0 },
|
||||
Builtin(id) => {
|
||||
let (_, arity) = BUILTINS[id as usize];
|
||||
InlineOperand::Const(Const::PrimOp {
|
||||
id,
|
||||
arity,
|
||||
dispatch_ip: Continuation::entry_for_builtin(id).ip(),
|
||||
})
|
||||
}
|
||||
Builtin(id) => InlineOperand::Const(Const::PrimOp(id)),
|
||||
BuiltinConst(id) => InlineOperand::BuiltinConst(id),
|
||||
Builtins => InlineOperand::Builtins,
|
||||
ReplBinding(id) => InlineOperand::ReplBinding(id),
|
||||
@@ -186,6 +178,10 @@ impl<'a, Ctx: BytecodeContext> BytecodeEmitter<'a, Ctx> {
|
||||
offset
|
||||
}
|
||||
#[inline]
|
||||
#[expect(
|
||||
clippy::indexing_slicing,
|
||||
reason = "offset addresses a 4-byte i32 placeholder this compiler previously emitted, so it is in bounds"
|
||||
)]
|
||||
fn patch_i32(&mut self, offset: usize, val: i32) {
|
||||
self.ctx.get_code_mut()[offset..offset + 4].copy_from_slice(&val.to_le_bytes());
|
||||
}
|
||||
@@ -214,6 +210,10 @@ impl<'a, Ctx: BytecodeContext> BytecodeEmitter<'a, Ctx> {
|
||||
self.scope_stack.last().map_or(0, |s| s.depth)
|
||||
}
|
||||
|
||||
#[expect(
|
||||
clippy::panic,
|
||||
reason = "a ThunkId must resolve in some enclosing scope; failure indicates a compiler bug"
|
||||
)]
|
||||
fn resolve_thunk(&self, id: ThunkId) -> (u8, u32) {
|
||||
for scope in self.scope_stack.iter().rev() {
|
||||
if let Some(&local_idx) = scope.thunk_map.get(&id) {
|
||||
@@ -520,6 +520,10 @@ impl<'a, Ctx: BytecodeContext> BytecodeEmitter<'a, Ctx> {
|
||||
}
|
||||
}
|
||||
|
||||
#[expect(
|
||||
clippy::unreachable,
|
||||
reason = "the outer match only reaches this arm for the binary operator kinds enumerated above"
|
||||
)]
|
||||
fn emit_binop(&mut self, lhs: RawIrRef<'_>, rhs: RawIrRef<'_>, kind: BinOpKind) {
|
||||
use BinOpKind::*;
|
||||
match kind {
|
||||
@@ -712,14 +716,25 @@ impl<'a, Ctx: BytecodeContext> BytecodeEmitter<'a, Ctx> {
|
||||
}
|
||||
|
||||
if let Some(default) = default {
|
||||
let before: i32 = self.ctx.get_code().len().try_into().unwrap();
|
||||
// FIXME: i32???
|
||||
let before: i32 = self
|
||||
.ctx
|
||||
.get_code()
|
||||
.len()
|
||||
.try_into()
|
||||
.expect("emitted code length fits in i32");
|
||||
for patch in dynamic_patches {
|
||||
self.patch_jump_target(patch);
|
||||
}
|
||||
self.emit_op(Op::JumpIfSelectSucceeded);
|
||||
let placeholder = self.emit_i32_placeholder();
|
||||
self.emit_expr(default);
|
||||
let after: i32 = self.ctx.get_code().len().try_into().unwrap();
|
||||
let after: i32 = self
|
||||
.ctx
|
||||
.get_code()
|
||||
.len()
|
||||
.try_into()
|
||||
.expect("emitted code length fits in i32");
|
||||
// Offset is relative to after the placeholder, so subtract the
|
||||
// size of JumpIfSelectSucceeded (1) + placeholder (4).
|
||||
self.patch_i32(placeholder, after - before - 5);
|
||||
@@ -730,6 +745,10 @@ impl<'a, Ctx: BytecodeContext> BytecodeEmitter<'a, Ctx> {
|
||||
}
|
||||
}
|
||||
|
||||
#[expect(
|
||||
clippy::panic,
|
||||
reason = "a hasAttr attrpath always has at least one attr by construction of the AST"
|
||||
)]
|
||||
fn emit_has_attr(&mut self, lhs: RawIrRef<'_>, rhs: &[Attr<RawIrRef<'_>>]) {
|
||||
self.emit_expr(lhs);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user