Skip to content

Commit f40a824

Browse files
committed
wip diagnostic fixes
1 parent 774f0dd commit f40a824

13 files changed

Lines changed: 184 additions & 51 deletions

File tree

crates/hir-analysis/src/name_resolution/path_resolver.rs

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ impl<'db> PathResError<'db> {
147147
let ident = failed_at.ident(db).to_opt()?; // xxx PathKind::QualifiedType
148148

149149
let diag = match self.kind {
150-
PathResErrorKind::ParseError => unreachable!(),
150+
PathResErrorKind::ParseError => return None,
151151
PathResErrorKind::NotFound { parent, bucket } => {
152152
if let Some(nr) = bucket.iter_ok().next() {
153153
if path != self.failed_at {
@@ -494,8 +494,24 @@ where
494494
));
495495
}
496496
let ty = lower_hir_ty(db, type_, scope, assumptions);
497-
let trait_inst = lower_trait_ref(db, ty, trait_, scope, assumptions)
498-
.map_err(|_| PathResError::parse_err(path))?; // xxx
497+
let trait_inst = match lower_trait_ref(db, ty, trait_, scope, assumptions) {
498+
Ok(inst) => inst,
499+
Err(err) => {
500+
use crate::ty::trait_lower::TraitRefLowerError as E;
501+
let path = trait_.path(db).to_opt().unwrap_or(path);
502+
let err = match err {
503+
E::PathResError(e) => e,
504+
E::InvalidDomain(res) => {
505+
PathResError::new(PathResErrorKind::InvalidPathSegment(res), path)
506+
}
507+
E::ArgNumMismatch { .. }
508+
| E::ArgKindMisMatch { .. }
509+
| E::ArgTypeMismatch { .. }
510+
| E::Other => PathResError::parse_err(path),
511+
};
512+
return Err(err);
513+
}
514+
};
499515

500516
let qualified_ty = TyId::qualified_ty(db, trait_inst);
501517
let r = PathRes::Ty(qualified_ty);

crates/hir-analysis/src/ty/def_analysis.rs

Lines changed: 25 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -732,8 +732,6 @@ impl<'db> Visitor<'db> for DefAnalyzer<'db> {
732732
ctxt: &mut VisitorCtxt<'db, LazyTraitRefSpan<'db>>,
733733
trait_ref: TraitRefId<'db>,
734734
) {
735-
// Skip if we don't have a current type context
736-
737735
let current_ty = self
738736
.current_ty
739737
.as_ref()
@@ -745,7 +743,7 @@ impl<'db> Visitor<'db> for DefAnalyzer<'db> {
745743
return;
746744
}
747745

748-
if let (Some((ty, span)), Ok(trait_inst)) = (
746+
if let (Some((ty, _)), Ok(trait_inst)) = (
749747
&self.current_ty,
750748
lower_trait_ref(
751749
self.db,
@@ -759,7 +757,7 @@ impl<'db> Visitor<'db> for DefAnalyzer<'db> {
759757
if !expected_kind.does_match(ty.kind(self.db)) {
760758
self.diags.push(
761759
TraitConstraintDiag::TraitArgKindMismatch {
762-
span: span.clone(),
760+
span: ctxt.span().unwrap(),
763761
expected: expected_kind.clone(),
764762
actual: *ty,
765763
}
@@ -774,7 +772,7 @@ impl<'db> Visitor<'db> for DefAnalyzer<'db> {
774772
trait_ref,
775773
self.scope(),
776774
self.assumptions,
777-
ctxt.span().unwrap().into(),
775+
ctxt.span().unwrap(),
778776
) {
779777
self.diags.push(diag);
780778
} else {
@@ -1050,7 +1048,7 @@ fn analyze_trait_ref<'db>(
10501048
trait_ref: TraitRefId<'db>,
10511049
scope: ScopeId<'db>,
10521050
assumptions: PredicateListId<'db>,
1053-
span: DynLazySpan<'db>,
1051+
span: LazyTraitRefSpan<'db>,
10541052
) -> Option<TyDiagCollection<'db>> {
10551053
let trait_inst = match lower_trait_ref(db, self_ty, trait_ref, scope, assumptions) {
10561054
Ok(trait_ref) => trait_ref,
@@ -1081,7 +1079,7 @@ fn analyze_trait_ref<'db>(
10811079
(Some(expected), Some(given)) => {
10821080
return Some(
10831081
TyLowerDiag::ConstTyMismatch {
1084-
span,
1082+
span: span.into(),
10851083
expected,
10861084
given,
10871085
}
@@ -1090,11 +1088,23 @@ fn analyze_trait_ref<'db>(
10901088
}
10911089

10921090
(Some(expected), None) => {
1093-
return Some(TyLowerDiag::ConstTyExpected { span, expected }.into())
1091+
return Some(
1092+
TyLowerDiag::ConstTyExpected {
1093+
span: span.into(),
1094+
expected,
1095+
}
1096+
.into(),
1097+
)
10941098
}
10951099

10961100
(None, Some(given)) => {
1097-
return Some(TyLowerDiag::NormalTypeExpected { span, given }.into())
1101+
return Some(
1102+
TyLowerDiag::NormalTypeExpected {
1103+
span: span.into(),
1104+
given,
1105+
}
1106+
.into(),
1107+
)
10981108
}
10991109

11001110
(None, None) => unreachable!(),
@@ -1105,7 +1115,7 @@ fn analyze_trait_ref<'db>(
11051115
err.into_diag(
11061116
db,
11071117
*trait_ref.path(db).unwrap(),
1108-
span,
1118+
span.into(),
11091119
ExpectedPathKind::Trait,
11101120
)?
11111121
.into(),
@@ -1115,7 +1125,7 @@ fn analyze_trait_ref<'db>(
11151125
Err(TraitRefLowerError::InvalidDomain(res)) => {
11161126
return Some(
11171127
NameResDiag::ExpectedTrait(
1118-
span,
1128+
span.into(),
11191129
*trait_ref.path(db).unwrap().ident(db).unwrap(),
11201130
res.kind_name(),
11211131
)
@@ -1134,7 +1144,7 @@ fn analyze_trait_ref<'db>(
11341144
return None;
11351145
}
11361146

1137-
trait_inst.emit_sat_diag(db, scope.ingot(db), assumptions, span)
1147+
trait_inst.emit_sat_diag(db, scope.ingot(db), assumptions, span.into())
11381148
}
11391149

11401150
#[derive(Clone, Copy, Debug, derive_more::From)]
@@ -1226,7 +1236,7 @@ fn analyze_impl_trait_specific_error<'db>(
12261236
Err(TraitRefLowerError::ArgNumMismatch { expected, given }) => {
12271237
diags.push(
12281238
TraitConstraintDiag::TraitArgNumMismatch {
1229-
span: impl_trait.span().trait_ref().into(),
1239+
span: impl_trait.span().trait_ref(),
12301240
expected,
12311241
given,
12321242
}
@@ -1237,7 +1247,7 @@ fn analyze_impl_trait_specific_error<'db>(
12371247
Err(TraitRefLowerError::ArgKindMisMatch { expected, given }) => {
12381248
diags.push(
12391249
TraitConstraintDiag::TraitArgKindMismatch {
1240-
span: impl_trait.span().trait_ref().into(),
1250+
span: impl_trait.span().trait_ref(),
12411251
expected,
12421252
actual: given,
12431253
}
@@ -1344,7 +1354,7 @@ fn analyze_impl_trait_specific_error<'db>(
13441354
if ty.kind(db) != expected_kind {
13451355
diags.push(
13461356
TraitConstraintDiag::TraitArgKindMismatch {
1347-
span: impl_trait.span().ty().into(),
1357+
span: impl_trait.span().trait_ref(),
13481358
expected: expected_kind.clone(),
13491359
actual: implementor.instantiate_identity().self_ty(db),
13501360
}

crates/hir-analysis/src/ty/diagnostics.rs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,11 @@ use hir::{
1515
Enum, FieldIndex, FieldParent, Func, IdentId, ImplTrait, ItemKind, PathId, Trait,
1616
TypeAlias as HirTypeAlias,
1717
},
18-
span::{expr::LazyMethodCallExprSpan, params::LazyGenericParamSpan, DynLazySpan},
18+
span::{
19+
expr::LazyMethodCallExprSpan,
20+
params::{LazyGenericParamSpan, LazyTraitRefSpan},
21+
DynLazySpan,
22+
},
1923
};
2024
use salsa::Update;
2125
use smallvec1::SmallVec;
@@ -480,18 +484,18 @@ impl TraitLowerDiag<'_> {
480484
#[derive(Debug, Clone, PartialEq, Eq, Hash, Update)]
481485
pub enum TraitConstraintDiag<'db> {
482486
KindMismatch {
483-
primary: DynLazySpan<'db>,
487+
primary: LazyTraitRefSpan<'db>,
484488
trait_def: Trait<'db>,
485489
},
486490

487491
TraitArgNumMismatch {
488-
span: DynLazySpan<'db>,
492+
span: LazyTraitRefSpan<'db>,
489493
expected: usize,
490494
given: usize,
491495
},
492496

493497
TraitArgKindMismatch {
494-
span: DynLazySpan<'db>,
498+
span: LazyTraitRefSpan<'db>,
495499
expected: Kind,
496500
actual: TyId<'db>,
497501
},

crates/hir-analysis/src/ty/ty_check/mod.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ use super::{
3030
ty_lower::lower_hir_ty,
3131
unify::{InferenceKey, UnificationError, UnificationTable},
3232
};
33+
use crate::ty::ty_error::collect_ty_lower_errors;
3334
use crate::{
3435
name_resolution::{
3536
diagnostics::NameResDiag, resolve_path_with_observer, PathRes, PathResError,
@@ -132,6 +133,25 @@ impl<'db> TyChecker<'db> {
132133
star_kind_required: bool,
133134
) -> TyId<'db> {
134135
let ty = lower_hir_ty(self.db, hir_ty, self.env.scope(), self.env.assumptions());
136+
137+
// If lowering failed, try to produce precise diagnostics (e.g., path resolution errors)
138+
if ty.has_invalid(self.db) {
139+
let diags = collect_ty_lower_errors(
140+
self.db,
141+
self.env.scope(),
142+
hir_ty,
143+
span.clone(),
144+
self.env.assumptions(),
145+
);
146+
if !diags.is_empty() {
147+
for d in diags {
148+
self.push_diag(d);
149+
}
150+
// Avoid cascading kind errors for already-invalid types
151+
return TyId::invalid(self.db, InvalidCause::Other);
152+
}
153+
}
154+
135155
if let Some(diag) = ty.emit_diag(self.db, span.clone().into()) {
136156
self.push_diag(diag)
137157
}

crates/hir-analysis/src/ty/ty_error.rs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -113,9 +113,16 @@ impl<'db> Visitor<'db> for HirTyErrVisitor<'db> {
113113
Ok(res) => res,
114114

115115
Err(err) => {
116-
let segment_span = path_span
117-
.segment(err.failed_at.segment_index(self.db))
118-
.ident();
116+
let segment_idx = err.failed_at.segment_index(self.db);
117+
// Use the HIR path to check if the corresponding segment is a QualifiedType.
118+
let seg_hir = path.segment(self.db, segment_idx).unwrap_or(path);
119+
let segment = path_span.segment(segment_idx);
120+
let segment_span = match seg_hir.kind(self.db) {
121+
hir::hir_def::PathKind::QualifiedType { .. } => {
122+
segment.qualified_type().trait_qualifier().name()
123+
}
124+
_ => segment.ident(),
125+
};
119126

120127
if let Some(diag) =
121128
err.into_diag(self.db, path, segment_span.into(), ExpectedPathKind::Type)

crates/hir/src/span/params.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,30 @@ define_lazy_span_node!(
155155
}
156156
);
157157

158+
impl<'db> LazyTraitRefSpan<'db> {
159+
/// Returns the span atom for the trait name (last segment ident) in this trait ref.
160+
pub fn name(mut self) -> LazySpanAtom<'db> {
161+
use crate::span::transition::{LazyArg, LazyTransitionFn, ResolvedOrigin};
162+
use parser::ast::prelude::*;
163+
164+
fn f(origin: ResolvedOrigin, _: LazyArg) -> ResolvedOrigin {
165+
origin.map(|node| {
166+
ast::TraitRef::cast(node)
167+
.and_then(|tr| tr.path())
168+
.and_then(|p| p.into_iter().last())
169+
.and_then(|seg| seg.ident())
170+
.map(|tok| tok.into())
171+
})
172+
}
173+
174+
self.0.push(LazyTransitionFn {
175+
f,
176+
arg: LazyArg::None,
177+
});
178+
LazySpanAtom(self.0)
179+
}
180+
}
181+
158182
define_lazy_span_node!(
159183
LazyKindBoundSpan,
160184
ast::KindBound,

crates/hir/src/span/path.rs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
use parser::ast;
22

3-
use super::{define_lazy_span_node, params::LazyGenericArgListSpan, LazySpanAtom};
3+
use super::{
4+
define_lazy_span_node,
5+
params::{LazyGenericArgListSpan, LazyTraitRefSpan},
6+
types::LazyTySpan,
7+
LazySpanAtom,
8+
};
49

510
define_lazy_span_node!(
611
LazyPathSpan,
@@ -18,10 +23,20 @@ define_lazy_span_node!(
1823
}
1924
@node {
2025
(generic_args, generic_args, LazyGenericArgListSpan),
26+
(qualified_type, qualified_type, LazyQualifiedTypeSpan),
2127
}
2228
);
2329
impl<'db> LazyPathSegmentSpan<'db> {
2430
pub fn into_atom(self) -> LazySpanAtom<'db> {
2531
LazySpanAtom(self.0)
2632
}
2733
}
34+
35+
define_lazy_span_node!(
36+
LazyQualifiedTypeSpan,
37+
ast::QualifiedType,
38+
@node {
39+
(ty, ty, LazyTySpan),
40+
(trait_qualifier, trait_qualifier, LazyTraitRefSpan),
41+
}
42+
);

crates/hir/src/visitor.rs

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ use crate::{
66
ExprId, Field, FieldDef, FieldDefListId, FieldIndex, FieldParent, Func, FuncParam,
77
FuncParamListId, FuncParamName, GenericArg, GenericArgListId, GenericParam,
88
GenericParamListId, IdentId, Impl, ImplTrait, ItemKind, KindBound, LitKind, MatchArm, Mod,
9-
Partial, Pat, PatId, PathId, Stmt, StmtId, Struct, TopLevelMod, Trait, TraitRefId,
10-
TupleTypeId, TypeAlias, TypeBound, TypeId, TypeKind, Use, UseAlias, UsePathId,
9+
Partial, Pat, PatId, PathId, PathKind, Stmt, StmtId, Struct, TopLevelMod, Trait,
10+
TraitRefId, TupleTypeId, TypeAlias, TypeBound, TypeId, TypeKind, Use, UseAlias, UsePathId,
1111
UsePathSegment, VariantDef, VariantDefListId, VariantKind, WhereClauseId, WherePredicate,
1212
},
1313
span::{
@@ -1715,14 +1715,31 @@ where
17151715
} else {
17161716
0
17171717
};
1718-
// xxx handle PathKind::QualifiedType case
1719-
if let Some(ident) = path.ident(ctxt.db).to_opt() {
1720-
ctxt.with_new_ctxt(
1721-
|span| span.segment(idx).into_atom(),
1722-
|ctxt| {
1723-
visitor.visit_ident(ctxt, ident);
1724-
},
1725-
);
1718+
match path.kind(ctxt.db) {
1719+
PathKind::Ident { .. } => {
1720+
if let Some(ident) = path.ident(ctxt.db).to_opt() {
1721+
ctxt.with_new_ctxt(
1722+
|span| span.segment(idx).into_atom(),
1723+
|ctxt| {
1724+
visitor.visit_ident(ctxt, ident);
1725+
},
1726+
);
1727+
}
1728+
}
1729+
PathKind::QualifiedType { type_, trait_ } => {
1730+
ctxt.with_new_ctxt(
1731+
|span| span.segment(idx).qualified_type().ty(),
1732+
|ctxt| {
1733+
visitor.visit_ty(ctxt, type_);
1734+
},
1735+
);
1736+
ctxt.with_new_ctxt(
1737+
|span| span.segment(idx).qualified_type().trait_qualifier(),
1738+
|ctxt| {
1739+
visitor.visit_trait_ref(ctxt, trait_);
1740+
},
1741+
);
1742+
}
17261743
}
17271744
let generic_args = path.generic_args(ctxt.db);
17281745
ctxt.with_new_ctxt(

crates/parser/src/ast/path.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,15 @@ impl PathSegment {
4040
_ => None,
4141
}
4242
}
43+
44+
/// Returns the qualified type node if this segment is a qualified type
45+
/// like `<T as Trait>`.
46+
pub fn qualified_type(&self) -> Option<QualifiedType> {
47+
match self.kind()? {
48+
PathSegmentKind::QualifiedType(q) => Some(q),
49+
_ => None,
50+
}
51+
}
4352
/// Returns the identifier of the segment.
4453
pub fn ident(&self) -> Option<SyntaxToken> {
4554
match self.kind()? {

0 commit comments

Comments
 (0)