Skip to content

Commit 45986a9

Browse files
committed
Hide unsatisfied-bound hint for non-visible traits
The "trait bound `X: Y` is not satisfied" sub-diagnostic only helps when the reader can name trait `Y`. For a bound on a trait that isn't visible from the error location -- e.g. a private sealed marker like `SolInt` behind a blanket impl in another module -- the hint is unactionable noise, so suppress it and keep just the primary "`X` doesn't implement `<public trait>`" line. Visibility is judged from the scope the error span originates from, via a new `DynLazySpan::scope` that maps a span's chain root to its HIR scope. So `pub` and same-scope subgoal traits keep their hint -- including when the error occurs inside the subgoal trait's own private module -- while a bound on a trait private to another module is dropped.
1 parent 848e23f commit 45986a9

6 files changed

Lines changed: 89 additions & 48 deletions

File tree

crates/hir/src/analysis/diagnostics.rs

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,10 @@
55
66
use crate::analysis::{
77
HirAnalysisDb,
8-
name_resolution::diagnostics::{ImportDiag, PathResDiag},
8+
name_resolution::{
9+
diagnostics::{ImportDiag, PathResDiag},
10+
is_scope_visible_from,
11+
},
912
ty::{
1013
diagnostics::{
1114
BodyDiag, CallConstraintDiagInfo, DefConflictError, FuncBodyDiag, ImplDiag,
@@ -4527,12 +4530,24 @@ impl DiagnosticVoucher for TraitConstraintDiag<'_> {
45274530
primary_goal.pretty_print(db, false)
45284531
);
45294532

4530-
let unsat_subgoal = unsat_subgoal.map(|unsat| {
4531-
format!(
4532-
"trait bound `{}` is not satisfied",
4533-
unsat.pretty_print(db, true)
4534-
)
4535-
});
4533+
// Only surface the specific unsatisfied sub-goal when its trait
4534+
// is visible from where the error is reported. A bound on a
4535+
// trait the reader cannot name (e.g. a private sealed marker in
4536+
// another module) is unactionable noise, so we keep just the
4537+
// primary goal in that case.
4538+
let unsat_subgoal = unsat_subgoal
4539+
.filter(|unsat| {
4540+
let Some(from_scope) = span.scope() else {
4541+
return true;
4542+
};
4543+
is_scope_visible_from(db, unsat.def(db).scope(), from_scope)
4544+
})
4545+
.map(|unsat| {
4546+
format!(
4547+
"trait bound `{}` is not satisfied",
4548+
unsat.pretty_print(db, true)
4549+
)
4550+
});
45364551

45374552
let mut sub_diagnostics = vec![SubDiagnostic {
45384553
style: LabelStyle::Primary,

crates/hir/src/core/span/mod.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use crate::{
66
HirDb, SpannedHirDb,
77
core::hir_def::{
88
Body, Const, Contract, Enum, Func, Impl, ImplTrait, Mod, StaticAssert, Struct, TopLevelMod,
9-
Trait, TypeAlias, Use,
9+
Trait, TypeAlias, Use, scope_graph::ScopeId,
1010
},
1111
core::lower::top_mod_ast,
1212
};
@@ -79,6 +79,12 @@ impl<'db> DynLazySpan<'db> {
7979
pub fn top_mod(&self, db: &'db dyn HirDb) -> Option<TopLevelMod<'db>> {
8080
self.0.as_ref().map(|chain| chain.top_mod(db))
8181
}
82+
83+
/// The HIR scope this span originates from (the item or body the span was
84+
/// built from), suitable as the "from scope" for visibility checks.
85+
pub fn scope(&self) -> Option<ScopeId<'db>> {
86+
self.0.as_ref().map(|chain| chain.scope())
87+
}
8288
}
8389
impl LazySpan for DynLazySpan<'_> {
8490
fn resolve(&self, db: &dyn SpannedHirDb) -> Option<Span> {

crates/hir/src/core/span/transition.rs

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use crate::{
1818
HirDb, SpannedHirDb,
1919
hir_def::{
2020
Body, Const, Contract, Enum, Func, Impl, ImplTrait, ItemKind, Mod, StaticAssert, Struct,
21-
TopLevelMod, Trait, TypeAlias, Use,
21+
TopLevelMod, Trait, TypeAlias, Use, scope_graph::ScopeId,
2222
},
2323
lower::top_mod_ast,
2424
};
@@ -88,6 +88,32 @@ impl<'db> SpanTransitionChain<'db> {
8888
}
8989
}
9090

91+
/// The HIR scope this span originates from, used e.g. for visibility
92+
/// checks at the error location. For body-relative roots this is the
93+
/// body's scope; for item roots it is the item's own scope.
94+
pub(super) fn scope(&self) -> ScopeId<'db> {
95+
match self.root {
96+
ChainRoot::ItemKind(item) => ScopeId::from_item(item),
97+
ChainRoot::TopMod(top_mod) => ScopeId::from_item(top_mod.into()),
98+
ChainRoot::Mod(m) => ScopeId::from_item(m.into()),
99+
ChainRoot::Func(f) => ScopeId::from_item(f.into()),
100+
ChainRoot::Struct(s) => ScopeId::from_item(s.into()),
101+
ChainRoot::Contract(c) => ScopeId::from_item(c.into()),
102+
ChainRoot::Enum(e) => ScopeId::from_item(e.into()),
103+
ChainRoot::TypeAlias(t) => ScopeId::from_item(t.into()),
104+
ChainRoot::Impl(i) => ScopeId::from_item(i.into()),
105+
ChainRoot::Trait(t) => ScopeId::from_item(t.into()),
106+
ChainRoot::ImplTrait(i) => ScopeId::from_item(i.into()),
107+
ChainRoot::Const(c) => ScopeId::from_item(c.into()),
108+
ChainRoot::StaticAssert(a) => ScopeId::from_item(a.into()),
109+
ChainRoot::Use(u) => ScopeId::from_item(u.into()),
110+
ChainRoot::Body(b) => b.scope(),
111+
ChainRoot::Stmt(s) => s.body.scope(),
112+
ChainRoot::Expr(e) => e.body.scope(),
113+
ChainRoot::Pat(p) => p.body.scope(),
114+
}
115+
}
116+
91117
pub(super) fn push(&mut self, transition: LazyTransitionFn) {
92118
self.chain.push(transition);
93119
}

crates/uitest/fixtures/ty_check/error_unsupported_field_type.snap

Lines changed: 15 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,11 @@ error[2-0010]: no method named `encode_to_ptr` found for struct `StorageMap`
1515
error[6-0003]: trait bound is not satisfied
1616
┌─ error_unsupported_field_type.fe:3:1
1717
18-
3 │ ╭ ╭ #[error]
19-
4 │ │ │ struct Bad {
20-
5 │ │ │ value: StorageMap<Address, u256, 0>,
21-
6 │ │ │ }
22-
│ ╰─│─^ `StorageMap<Address, u256, 0>` doesn't implement `AbiSize`
23-
│ ╰─' trait bound `StorageMap<Address, u256, 0>: SolInt` is not satisfied
18+
3 │ ╭ #[error]
19+
4 │ │ struct Bad {
20+
5 │ │ value: StorageMap<Address, u256, 0>,
21+
6 │ │ }
22+
│ ╰─^ `StorageMap<Address, u256, 0>` doesn't implement `AbiSize`
2423
2524
┌─ src/abi.fe:317:32
2625
@@ -30,12 +29,11 @@ error[6-0003]: trait bound is not satisfied
3029
error[6-0003]: trait bound is not satisfied
3130
┌─ error_unsupported_field_type.fe:3:1
3231
33-
3 │ ╭ ╭ #[error]
34-
4 │ │ │ struct Bad {
35-
5 │ │ │ value: StorageMap<Address, u256, 0>,
36-
6 │ │ │ }
37-
│ ╰─│─^ `StorageMap<Address, u256, 0>` doesn't implement `Encode<Sol>`
38-
│ ╰─' trait bound `StorageMap<Address, u256, 0>: SolInt` is not satisfied
32+
3 │ ╭ #[error]
33+
4 │ │ struct Bad {
34+
5 │ │ value: StorageMap<Address, u256, 0>,
35+
6 │ │ }
36+
│ ╰─^ `StorageMap<Address, u256, 0>` doesn't implement `Encode<Sol>`
3937
4038
┌─ src/abi.fe:437:14
4139
@@ -45,12 +43,11 @@ error[6-0003]: trait bound is not satisfied
4543
error[6-0003]: trait bound is not satisfied
4644
┌─ error_unsupported_field_type.fe:3:1
4745
48-
3 │ ╭ ╭ #[error]
49-
4 │ │ │ struct Bad {
50-
5 │ │ │ value: StorageMap<Address, u256, 0>,
51-
6 │ │ │ }
52-
│ ╰─│─^ `StorageMap<Address, u256, 0>` doesn't implement `AbiSize`
53-
│ ╰─' trait bound `StorageMap<Address, u256, 0>: SolInt` is not satisfied
46+
3 │ ╭ #[error]
47+
4 │ │ struct Bad {
48+
5 │ │ value: StorageMap<Address, u256, 0>,
49+
6 │ │ }
50+
│ ╰─^ `StorageMap<Address, u256, 0>` doesn't implement `AbiSize`
5451
5552
┌─ src/abi.fe:437:26
5653

crates/uitest/fixtures/ty_check/event_shadowed_address_type.snap

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,11 @@ input_file: fixtures/ty_check/event_shadowed_address_type.fe
66
error[6-0003]: trait bound is not satisfied
77
┌─ event_shadowed_address_type.fe:5:1
88
9-
5 │ ╭ ╭ #[event]
10-
6 │ │ │ struct BadEvent {
11-
7 │ │ │ #[indexed]
12-
8 │ │ │ from: Address,
13-
9 │ │ │ #[indexed]
14-
10 │ │ │ to: std::evm::Address,
15-
11 │ │ │ }
16-
│ ╰─│─^ `Address` doesn't implement `TopicValue`
17-
│ ╰─' trait bound `Address: SolInt` is not satisfied
9+
5 │ ╭ #[event]
10+
6 │ │ struct BadEvent {
11+
7 │ │ #[indexed]
12+
8 │ │ from: Address,
13+
9 │ │ #[indexed]
14+
10 │ │ to: std::evm::Address,
15+
11 │ │ }
16+
│ ╰─^ `Address` doesn't implement `TopicValue`

crates/uitest/fixtures/ty_check/event_unsupported_field_type.snap

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,11 @@ input_file: fixtures/ty_check/event_unsupported_field_type.fe
66
error[6-0003]: trait bound is not satisfied
77
┌─ event_unsupported_field_type.fe:3:1
88
9-
3 │ ╭ ╭ #[event]
10-
4 │ │ │ struct Bad {
11-
5 │ │ │ value: StorageMap<Address, u256, 0>,
12-
6 │ │ │ }
13-
│ ╰─│─^ `StorageMap<Address, u256, 0>` doesn't implement `Encode<Sol>`
14-
│ ╰─' trait bound `StorageMap<Address, u256, 0>: SolInt` is not satisfied
9+
3 │ ╭ #[event]
10+
4 │ │ struct Bad {
11+
5 │ │ value: StorageMap<Address, u256, 0>,
12+
6 │ │ }
13+
│ ╰─^ `StorageMap<Address, u256, 0>` doesn't implement `Encode<Sol>`
1514
1615
┌─ src/evm/effects.fe:431:14
1716
@@ -21,12 +20,11 @@ error[6-0003]: trait bound is not satisfied
2120
error[6-0003]: trait bound is not satisfied
2221
┌─ event_unsupported_field_type.fe:3:1
2322
24-
3 │ ╭ ╭ #[event]
25-
4 │ │ │ struct Bad {
26-
5 │ │ │ value: StorageMap<Address, u256, 0>,
27-
6 │ │ │ }
28-
│ ╰─│─^ `StorageMap<Address, u256, 0>` doesn't implement `AbiSize`
29-
│ ╰─' trait bound `StorageMap<Address, u256, 0>: SolInt` is not satisfied
23+
3 │ ╭ #[event]
24+
4 │ │ struct Bad {
25+
5 │ │ value: StorageMap<Address, u256, 0>,
26+
6 │ │ }
27+
│ ╰─^ `StorageMap<Address, u256, 0>` doesn't implement `AbiSize`
3028
3129
┌─ src/evm/effects.fe:431:28
3230

0 commit comments

Comments
 (0)