Merge pull request #18541 from ChayimFriedman2/different-generic-args

feat: Complete diagnostics in ty lowering groundwork and serve a first diagnostic 🎉
This commit is contained in:
Lukas Wirth 2024-12-04 13:17:05 +00:00 committed by GitHub
commit 39fd171808
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
34 changed files with 1550 additions and 234 deletions

4
Cargo.lock generated
View file

@ -509,6 +509,7 @@ dependencies = [
"base-db",
"cfg",
"either",
"expect-test",
"hir-def",
"hir-expand",
"hir-ty",
@ -519,6 +520,9 @@ dependencies = [
"span",
"stdx",
"syntax",
"syntax-bridge",
"test-fixture",
"test-utils",
"tracing",
"triomphe",
"tt",

View file

@ -31,7 +31,7 @@ use crate::{
path::{ModPath, Path},
src::HasSource,
type_ref::{TypeRef, TypeRefId, TypesMap, TypesSourceMap},
BlockId, DefWithBodyId, HasModule, Lookup,
BlockId, DefWithBodyId, HasModule, Lookup, SyntheticSyntax,
};
/// A wrapper around [`span::SyntaxContextId`] that is intended only for comparisons.
@ -141,7 +141,7 @@ pub struct BodySourceMap {
field_map_back: FxHashMap<ExprId, FieldSource>,
pat_field_map_back: FxHashMap<PatId, PatFieldSource>,
types: TypesSourceMap,
pub types: TypesSourceMap,
// FIXME: Make this a sane struct.
template_map: Option<
@ -160,9 +160,6 @@ pub struct BodySourceMap {
diagnostics: Vec<BodyDiagnostic>,
}
#[derive(Default, Debug, Eq, PartialEq, Clone, Copy)]
pub struct SyntheticSyntax;
#[derive(Debug, Eq, PartialEq)]
pub enum BodyDiagnostic {
InactiveCode { node: InFile<SyntaxNodePtr>, cfg: CfgExpr, opts: CfgOptions },

View file

@ -369,7 +369,7 @@ impl ImplData {
let item_tree = tree_id.item_tree(db);
let impl_def = &item_tree[tree_id.value];
let target_trait = impl_def.target_trait.clone();
let target_trait = impl_def.target_trait;
let self_ty = impl_def.self_ty;
let is_negative = impl_def.is_negative;
let is_unsafe = impl_def.is_unsafe;

View file

@ -26,8 +26,8 @@ use crate::{
nameres::{DefMap, MacroSubNs},
path::{AssociatedTypeBinding, GenericArg, GenericArgs, NormalPath, Path},
type_ref::{
ArrayType, ConstRef, FnType, LifetimeRef, RefType, TypeBound, TypeRef, TypeRefId, TypesMap,
TypesSourceMap,
ArrayType, ConstRef, FnType, LifetimeRef, PathId, RefType, TypeBound, TypeRef, TypeRefId,
TypesMap, TypesSourceMap,
},
AdtId, ConstParamId, GenericDefId, HasModule, ItemTreeLoc, LifetimeParamId,
LocalLifetimeParamId, LocalTypeOrConstParamId, Lookup, TypeOrConstParamId, TypeParamId,
@ -224,6 +224,11 @@ impl GenericParams {
self.len() == 0
}
#[inline]
pub fn no_predicates(&self) -> bool {
self.where_predicates.is_empty()
}
#[inline]
pub fn where_predicates(&self) -> std::slice::Iter<'_, WherePredicate> {
self.where_predicates.iter()
@ -874,14 +879,20 @@ fn copy_type_bound(
to: &mut TypesMap,
to_source_map: &mut TypesSourceMap,
) -> TypeBound {
match bound {
TypeBound::Path(path, modifier) => {
TypeBound::Path(copy_path(path, from, from_source_map, to, to_source_map), *modifier)
let mut copy_path_id = |path: PathId| {
let new_path = copy_path(&from[path], from, from_source_map, to, to_source_map);
let new_path_id = to.types.alloc(TypeRef::Path(new_path));
if let Some(&ptr) = from_source_map.types_map_back.get(path.type_ref()) {
to_source_map.types_map_back.insert(new_path_id, ptr);
}
PathId::from_type_ref_unchecked(new_path_id)
};
match bound {
&TypeBound::Path(path, modifier) => TypeBound::Path(copy_path_id(path), modifier),
TypeBound::ForLifetime(lifetimes, path) => {
TypeBound::ForLifetime(lifetimes.clone(), copy_path_id(*path))
}
TypeBound::ForLifetime(lifetimes, path) => TypeBound::ForLifetime(
lifetimes.clone(),
copy_path(path, from, from_source_map, to, to_source_map),
),
TypeBound::Lifetime(lifetime) => TypeBound::Lifetime(lifetime.clone()),
TypeBound::Use(use_args) => TypeBound::Use(use_args.clone()),
TypeBound::Error => TypeBound::Error,

View file

@ -23,6 +23,7 @@ use crate::{
hir::Literal,
lower::LowerCtx,
path::{GenericArg, Path},
SyntheticSyntax,
};
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
@ -91,19 +92,37 @@ impl Rawness {
}
}
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
/// A `TypeRefId` that is guaranteed to always be `TypeRef::Path`. We use this for things like
/// impl's trait, that are always paths but need to be traced back to source code.
pub struct PathId(TypeRefId);
impl PathId {
#[inline]
pub fn from_type_ref_unchecked(type_ref: TypeRefId) -> Self {
Self(type_ref)
}
#[inline]
pub fn type_ref(self) -> TypeRefId {
self.0
}
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub struct TraitRef {
pub path: Path,
pub path: PathId,
}
impl TraitRef {
/// Converts an `ast::PathType` to a `hir::TraitRef`.
pub(crate) fn from_ast(ctx: &mut LowerCtx<'_>, node: ast::Type) -> Option<Self> {
// FIXME: Use `Path::from_src`
match node {
ast::Type::PathType(path) => {
path.path().and_then(|it| ctx.lower_path(it)).map(|path| TraitRef { path })
}
match &node {
ast::Type::PathType(path) => path
.path()
.and_then(|it| ctx.lower_path(it))
.map(|path| TraitRef { path: ctx.alloc_path(path, AstPtr::new(&node)) }),
_ => None,
}
}
@ -173,11 +192,24 @@ impl TypesMap {
impl Index<TypeRefId> for TypesMap {
type Output = TypeRef;
#[inline]
fn index(&self, index: TypeRefId) -> &Self::Output {
&self.types[index]
}
}
impl Index<PathId> for TypesMap {
type Output = Path;
#[inline]
fn index(&self, index: PathId) -> &Self::Output {
let TypeRef::Path(path) = &self[index.type_ref()] else {
unreachable!("`PathId` always points to `TypeRef::Path`");
};
path
}
}
pub type TypePtr = AstPtr<ast::Type>;
pub type TypeSource = InFile<TypePtr>;
@ -187,6 +219,12 @@ pub struct TypesSourceMap {
}
impl TypesSourceMap {
pub const EMPTY: Self = Self { types_map_back: ArenaMap::new() };
pub fn type_syntax(&self, id: TypeRefId) -> Result<TypeSource, SyntheticSyntax> {
self.types_map_back.get(id).cloned().ok_or(SyntheticSyntax)
}
pub(crate) fn shrink_to_fit(&mut self) {
let TypesSourceMap { types_map_back } = self;
types_map_back.shrink_to_fit();
@ -214,15 +252,15 @@ impl LifetimeRef {
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub enum TypeBound {
Path(Path, TraitBoundModifier),
ForLifetime(Box<[Name]>, Path),
Path(PathId, TraitBoundModifier),
ForLifetime(Box<[Name]>, PathId),
Lifetime(LifetimeRef),
Use(Box<[UseArgRef]>),
Error,
}
#[cfg(target_pointer_width = "64")]
const _: [(); 32] = [(); ::std::mem::size_of::<TypeBound>()];
const _: [(); 24] = [(); ::std::mem::size_of::<TypeBound>()];
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub enum UseArgRef {
@ -365,8 +403,8 @@ impl TypeRef {
TypeRef::ImplTrait(bounds) | TypeRef::DynTrait(bounds) => {
for bound in bounds {
match bound {
TypeBound::Path(path, _) | TypeBound::ForLifetime(_, path) => {
go_path(path, f, map)
&TypeBound::Path(path, _) | &TypeBound::ForLifetime(_, path) => {
go_path(&map[path], f, map)
}
TypeBound::Lifetime(_) | TypeBound::Error | TypeBound::Use(_) => (),
}
@ -397,8 +435,8 @@ impl TypeRef {
}
for bound in binding.bounds.iter() {
match bound {
TypeBound::Path(path, _) | TypeBound::ForLifetime(_, path) => {
go_path(path, f, map)
&TypeBound::Path(path, _) | &TypeBound::ForLifetime(_, path) => {
go_path(&map[path], f, map)
}
TypeBound::Lifetime(_) | TypeBound::Error | TypeBound::Use(_) => (),
}
@ -425,7 +463,7 @@ pub(crate) fn type_bounds_from_ast(
impl TypeBound {
pub(crate) fn from_ast(ctx: &mut LowerCtx<'_>, node: ast::TypeBound) -> Self {
let mut lower_path_type = |path_type: ast::PathType| ctx.lower_path(path_type.path()?);
let mut lower_path_type = |path_type: &ast::PathType| ctx.lower_path(path_type.path()?);
match node.kind() {
ast::TypeBoundKind::PathType(path_type) => {
@ -433,8 +471,10 @@ impl TypeBound {
Some(_) => TraitBoundModifier::Maybe,
None => TraitBoundModifier::None,
};
lower_path_type(path_type)
.map(|p| TypeBound::Path(p, m))
lower_path_type(&path_type)
.map(|p| {
TypeBound::Path(ctx.alloc_path(p, AstPtr::new(&path_type).upcast()), m)
})
.unwrap_or(TypeBound::Error)
}
ast::TypeBoundKind::ForType(for_type) => {
@ -445,12 +485,14 @@ impl TypeBound {
.collect(),
None => Box::default(),
};
let path = for_type.ty().and_then(|ty| match ty {
ast::Type::PathType(path_type) => lower_path_type(path_type),
let path = for_type.ty().and_then(|ty| match &ty {
ast::Type::PathType(path_type) => lower_path_type(path_type).map(|p| (p, ty)),
_ => None,
});
match path {
Some(p) => TypeBound::ForLifetime(lt_refs, p),
Some((p, ty)) => {
TypeBound::ForLifetime(lt_refs, ctx.alloc_path(p, AstPtr::new(&ty)))
}
None => TypeBound::Error,
}
}
@ -470,10 +512,10 @@ impl TypeBound {
}
}
pub fn as_path(&self) -> Option<(&Path, &TraitBoundModifier)> {
pub fn as_path<'a>(&self, map: &'a TypesMap) -> Option<(&'a Path, TraitBoundModifier)> {
match self {
TypeBound::Path(p, m) => Some((p, m)),
TypeBound::ForLifetime(_, p) => Some((p, &TraitBoundModifier::None)),
&TypeBound::Path(p, m) => Some((&map[p], m)),
&TypeBound::ForLifetime(_, p) => Some((&map[p], TraitBoundModifier::None)),
TypeBound::Lifetime(_) | TypeBound::Error | TypeBound::Use(_) => None,
}
}

View file

@ -34,7 +34,7 @@ use crate::{
lower::LowerCtx,
path::AssociatedTypeBinding,
type_ref::{
LifetimeRef, RefType, TraitBoundModifier, TraitRef, TypeBound, TypeRef, TypeRefId,
LifetimeRef, PathId, RefType, TraitBoundModifier, TraitRef, TypeBound, TypeRef, TypeRefId,
TypesMap, TypesSourceMap,
},
visibility::RawVisibility,
@ -514,7 +514,7 @@ impl<'a> Ctx<'a> {
};
let ret_type = if func.async_token().is_some() {
let future_impl = desugar_future_path(ret_type);
let future_impl = desugar_future_path(&mut body_ctx, ret_type);
let ty_bound = TypeBound::Path(future_impl, TraitBoundModifier::None);
body_ctx.alloc_type_ref_desugared(TypeRef::ImplTrait(ThinVec::from_iter([ty_bound])))
} else {
@ -936,7 +936,7 @@ impl<'a> Ctx<'a> {
}
}
fn desugar_future_path(orig: TypeRefId) -> Path {
fn desugar_future_path(ctx: &mut LowerCtx<'_>, orig: TypeRefId) -> PathId {
let path = path![core::future::Future];
let mut generic_args: Vec<_> =
std::iter::repeat(None).take(path.segments().len() - 1).collect();
@ -948,7 +948,8 @@ fn desugar_future_path(orig: TypeRefId) -> Path {
};
generic_args.push(Some(GenericArgs { bindings: Box::new([binding]), ..GenericArgs::empty() }));
Path::from_known_path(path, generic_args)
let path = Path::from_known_path(path, generic_args);
PathId::from_type_ref_unchecked(ctx.alloc_type_ref_desugared(TypeRef::Path(path)))
}
enum HasImplicitSelf {

View file

@ -484,7 +484,7 @@ impl Printer<'_> {
w!(self, "!");
}
if let Some(tr) = target_trait {
self.print_path(&tr.path, types_map);
self.print_path(&types_map[tr.path], types_map);
w!(self, " for ");
}
self.print_type_ref(*self_ty, types_map);

View file

@ -1535,3 +1535,6 @@ fn macro_call_as_call_id_with_eager(
pub struct UnresolvedMacro {
pub path: hir_expand::mod_path::ModPath,
}
#[derive(Default, Debug, Eq, PartialEq, Clone, Copy)]
pub struct SyntheticSyntax;

View file

@ -10,7 +10,7 @@ use triomphe::Arc;
use crate::{
db::DefDatabase,
path::Path,
type_ref::{TypeBound, TypePtr, TypeRef, TypeRefId, TypesMap, TypesSourceMap},
type_ref::{PathId, TypeBound, TypePtr, TypeRef, TypeRefId, TypesMap, TypesSourceMap},
};
pub struct LowerCtx<'a> {
@ -142,4 +142,8 @@ impl<'a> LowerCtx<'a> {
pub(crate) fn alloc_error_type(&mut self) -> TypeRefId {
self.types_map.types.alloc(TypeRef::Error)
}
pub(crate) fn alloc_path(&mut self, path: Path, node: TypePtr) -> PathId {
PathId::from_type_ref_unchecked(self.alloc_type_ref(TypeRef::Path(path), node))
}
}

View file

@ -1,5 +1,7 @@
//! A desugared representation of paths like `crate::foo` or `<Type as Trait>::bar`.
mod lower;
#[cfg(test)]
mod tests;
use std::{
fmt::{self, Display},
@ -19,6 +21,8 @@ use syntax::ast;
pub use hir_expand::mod_path::{path, ModPath, PathKind};
pub use lower::hir_segment_to_ast_segment;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ImportAlias {
/// Unnamed alias, as in `use Foo as _;`
@ -230,7 +234,7 @@ impl Path {
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct PathSegment<'a> {
pub name: &'a Name,
pub args_and_bindings: Option<&'a GenericArgs>,
@ -274,6 +278,12 @@ impl<'a> PathSegments<'a> {
generic_args: self.generic_args.map(|it| it.get(..len).unwrap_or(it)),
}
}
pub fn strip_last(&self) -> PathSegments<'a> {
PathSegments {
segments: self.segments.split_last().map_or(&[], |it| it.1),
generic_args: self.generic_args.map(|it| it.split_last().map_or(&[][..], |it| it.1)),
}
}
pub fn iter(&self) -> impl Iterator<Item = PathSegment<'a>> {
self.segments
.iter()

View file

@ -17,13 +17,31 @@ use crate::{
type_ref::{LifetimeRef, TypeBound, TypeRef},
};
#[cfg(test)]
thread_local! {
/// This is used to test `hir_segment_to_ast_segment()`. It's a hack, but it makes testing much easier.
pub(super) static SEGMENT_LOWERING_MAP: std::cell::RefCell<rustc_hash::FxHashMap<ast::PathSegment, usize>> = std::cell::RefCell::default();
}
/// Converts an `ast::Path` to `Path`. Works with use trees.
/// It correctly handles `$crate` based path from macro call.
// If you modify the logic of the lowering, make sure to check if `hir_segment_to_ast_segment()`
// also needs an update.
pub(super) fn lower_path(ctx: &mut LowerCtx<'_>, mut path: ast::Path) -> Option<Path> {
let mut kind = PathKind::Plain;
let mut type_anchor = None;
let mut segments = Vec::new();
let mut generic_args = Vec::new();
#[cfg(test)]
let mut ast_segments = Vec::new();
#[cfg(test)]
let mut ast_segments_offset = 0;
#[allow(unused_mut)]
let mut push_segment = |_segment: &ast::PathSegment, segments: &mut Vec<Name>, name| {
#[cfg(test)]
ast_segments.push(_segment.clone());
segments.push(name);
};
loop {
let segment = path.segment()?;
@ -34,6 +52,10 @@ pub(super) fn lower_path(ctx: &mut LowerCtx<'_>, mut path: ast::Path) -> Option<
match segment.kind()? {
ast::PathSegmentKind::Name(name_ref) => {
if name_ref.text() == "$crate" {
if path.qualifier().is_some() {
// FIXME: Report an error.
return None;
}
break kind = resolve_crate_root(
ctx.db.upcast(),
ctx.span_map().span_for_range(name_ref.syntax().text_range()).ctx,
@ -56,10 +78,10 @@ pub(super) fn lower_path(ctx: &mut LowerCtx<'_>, mut path: ast::Path) -> Option<
generic_args.resize(segments.len(), None);
generic_args.push(args);
}
segments.push(name);
push_segment(&segment, &mut segments, name);
}
ast::PathSegmentKind::SelfTypeKw => {
segments.push(Name::new_symbol_root(sym::Self_.clone()));
push_segment(&segment, &mut segments, Name::new_symbol_root(sym::Self_.clone()));
}
ast::PathSegmentKind::Type { type_ref, trait_ref } => {
assert!(path.qualifier().is_none()); // this can only occur at the first segment
@ -81,6 +103,10 @@ pub(super) fn lower_path(ctx: &mut LowerCtx<'_>, mut path: ast::Path) -> Option<
kind = mod_path.kind;
segments.extend(mod_path.segments().iter().cloned().rev());
#[cfg(test)]
{
ast_segments_offset = mod_path.segments().len();
}
if let Some(path_generic_args) = path_generic_args {
generic_args.resize(segments.len() - num_segments, None);
generic_args.extend(Vec::from(path_generic_args).into_iter().rev());
@ -112,10 +138,18 @@ pub(super) fn lower_path(ctx: &mut LowerCtx<'_>, mut path: ast::Path) -> Option<
}
}
ast::PathSegmentKind::CrateKw => {
if path.qualifier().is_some() {
// FIXME: Report an error.
return None;
}
kind = PathKind::Crate;
break;
}
ast::PathSegmentKind::SelfKw => {
if path.qualifier().is_some() {
// FIXME: Report an error.
return None;
}
// don't break out if `self` is the last segment of a path, this mean we got a
// use tree like `foo::{self}` which we want to resolve as `foo`
if !segments.is_empty() {
@ -162,6 +196,13 @@ pub(super) fn lower_path(ctx: &mut LowerCtx<'_>, mut path: ast::Path) -> Option<
}
}
#[cfg(test)]
{
ast_segments.reverse();
SEGMENT_LOWERING_MAP
.with_borrow_mut(|map| map.extend(ast_segments.into_iter().zip(ast_segments_offset..)));
}
let mod_path = Interned::new(ModPath::from_segments(kind, segments));
if type_anchor.is_none() && generic_args.is_empty() {
return Some(Path::BarePath(mod_path));
@ -181,6 +222,41 @@ pub(super) fn lower_path(ctx: &mut LowerCtx<'_>, mut path: ast::Path) -> Option<
}
}
/// This function finds the AST segment that corresponds to the HIR segment
/// with index `segment_idx` on the path that is lowered from `path`.
pub fn hir_segment_to_ast_segment(path: &ast::Path, segment_idx: u32) -> Option<ast::PathSegment> {
// Too tightly coupled to `lower_path()`, but unfortunately we cannot decouple them,
// as keeping source maps for all paths segments will have a severe impact on memory usage.
let mut segments = path.segments();
if let Some(ast::PathSegmentKind::Type { trait_ref: Some(trait_ref), .. }) =
segments.clone().next().and_then(|it| it.kind())
{
segments.next();
return find_segment(trait_ref.path()?.segments().chain(segments), segment_idx);
}
return find_segment(segments, segment_idx);
fn find_segment(
segments: impl Iterator<Item = ast::PathSegment>,
segment_idx: u32,
) -> Option<ast::PathSegment> {
segments
.filter(|segment| match segment.kind() {
Some(
ast::PathSegmentKind::CrateKw
| ast::PathSegmentKind::SelfKw
| ast::PathSegmentKind::SuperKw
| ast::PathSegmentKind::Type { .. },
)
| None => false,
Some(ast::PathSegmentKind::Name(name)) => name.text() != "$crate",
Some(ast::PathSegmentKind::SelfTypeKw) => true,
})
.nth(segment_idx as usize)
}
}
pub(super) fn lower_generic_args(
lower_ctx: &mut LowerCtx<'_>,
node: ast::GenericArgList,

View file

@ -0,0 +1,126 @@
use expect_test::{expect, Expect};
use span::Edition;
use syntax::ast::{self, make};
use test_fixture::WithFixture;
use crate::{
lower::LowerCtx,
path::{
lower::{hir_segment_to_ast_segment, SEGMENT_LOWERING_MAP},
Path,
},
pretty,
test_db::TestDB,
type_ref::{TypesMap, TypesSourceMap},
};
fn lower_path(path: ast::Path) -> (TestDB, TypesMap, Option<Path>) {
let (db, file_id) = TestDB::with_single_file("");
let mut types_map = TypesMap::default();
let mut types_source_map = TypesSourceMap::default();
let mut ctx = LowerCtx::new(&db, file_id.into(), &mut types_map, &mut types_source_map);
let lowered_path = ctx.lower_path(path);
(db, types_map, lowered_path)
}
#[track_caller]
fn check_hir_to_ast(path: &str, ignore_segments: &[&str]) {
let path = make::path_from_text(path);
SEGMENT_LOWERING_MAP.with_borrow_mut(|map| map.clear());
let _ = lower_path(path.clone()).2.expect("failed to lower path");
SEGMENT_LOWERING_MAP.with_borrow(|map| {
for (segment, segment_idx) in map {
if ignore_segments.contains(&&*segment.to_string()) {
continue;
}
let restored_segment = hir_segment_to_ast_segment(&path, *segment_idx as u32)
.unwrap_or_else(|| {
panic!(
"failed to map back segment `{segment}` \
numbered {segment_idx} in HIR from path `{path}`"
)
});
assert_eq!(
segment, &restored_segment,
"mapping back `{segment}` numbered {segment_idx} in HIR \
from path `{path}` produced incorrect segment `{restored_segment}`"
);
}
});
}
#[test]
fn hir_to_ast_trait_ref() {
check_hir_to_ast("<A as B::C::D>::E::F", &["A"]);
}
#[test]
fn hir_to_ast_plain_path() {
check_hir_to_ast("A::B::C::D::E::F", &[]);
}
#[test]
fn hir_to_ast_crate_path() {
check_hir_to_ast("crate::A::B::C", &[]);
check_hir_to_ast("crate::super::super::A::B::C", &[]);
}
#[test]
fn hir_to_ast_self_path() {
check_hir_to_ast("self::A::B::C", &[]);
check_hir_to_ast("self::super::super::A::B::C", &[]);
}
#[test]
fn hir_to_ast_super_path() {
check_hir_to_ast("super::A::B::C", &[]);
check_hir_to_ast("super::super::super::A::B::C", &[]);
}
#[test]
fn hir_to_ast_type_anchor_path() {
check_hir_to_ast("<A::B>::C::D", &["A", "B"]);
}
#[test]
fn hir_to_ast_path_super_in_middle() {
check_hir_to_ast("A::super::B::super::super::C::D", &[]);
}
#[track_caller]
fn check_fail_lowering(path: &str) {
let (_, _, lowered_path) = lower_path(make::path_from_text(path));
assert!(lowered_path.is_none(), "path `{path}` should fail lowering");
}
#[test]
fn keywords_in_middle_fail_lowering1() {
check_fail_lowering("self::A::self::B::super::C::crate::D");
}
#[test]
fn keywords_in_middle_fail_lowering2() {
check_fail_lowering("A::super::self::C::D");
}
#[test]
fn keywords_in_middle_fail_lowering3() {
check_fail_lowering("A::crate::B::C::D");
}
#[track_caller]
fn check_path_lowering(path: &str, expected: Expect) {
let (db, types_map, lowered_path) = lower_path(make::path_from_text(path));
let lowered_path = lowered_path.expect("failed to lower path");
let mut buf = String::new();
pretty::print_path(&db, &lowered_path, &types_map, &mut buf, Edition::CURRENT)
.expect("failed to pretty-print path");
expected.assert_eq(&buf);
}
#[test]
fn fn_like_path_with_coloncolon() {
check_path_lowering("Fn::(A, B) -> C", expect![[r#"Fn::<(A, B), Output = C>"#]]);
check_path_lowering("Fn::(A, B)", expect![[r#"Fn::<(A, B), Output = ()>"#]]);
}

View file

@ -271,7 +271,7 @@ pub(crate) fn print_type_bounds(
TraitBoundModifier::None => (),
TraitBoundModifier::Maybe => write!(buf, "?")?,
}
print_path(db, path, map, buf, edition)?;
print_path(db, &map[*path], map, buf, edition)?;
}
TypeBound::ForLifetime(lifetimes, path) => {
write!(
@ -279,7 +279,7 @@ pub(crate) fn print_type_bounds(
"for<{}> ",
lifetimes.iter().map(|it| it.display(db.upcast(), edition)).format(", ")
)?;
print_path(db, path, map, buf, edition)?;
print_path(db, &map[*path], map, buf, edition)?;
}
TypeBound::Lifetime(lt) => write!(buf, "{}", lt.name.display(db.upcast(), edition))?,
TypeBound::Use(args) => {

View file

@ -576,10 +576,12 @@ impl Resolver {
match scope {
Scope::BlockScope(m) => traits.extend(m.def_map[m.module_id].scope.traits()),
&Scope::ImplDefScope(impl_) => {
if let Some(target_trait) = &db.impl_data(impl_).target_trait {
if let Some(TypeNs::TraitId(trait_)) =
self.resolve_path_in_type_ns_fully(db, &target_trait.path)
{
let impl_data = db.impl_data(impl_);
if let Some(target_trait) = impl_data.target_trait {
if let Some(TypeNs::TraitId(trait_)) = self.resolve_path_in_type_ns_fully(
db,
&impl_data.types_map[target_trait.path],
) {
traits.insert(trait_);
}
}

View file

@ -180,6 +180,13 @@ impl<FileId: FileIdToSyntax, T> InFileWrapper<FileId, T> {
}
}
#[allow(private_bounds)]
impl<FileId: FileIdToSyntax, N: AstNode> InFileWrapper<FileId, AstPtr<N>> {
pub fn to_node(&self, db: &dyn ExpandDatabase) -> N {
self.value.to_node(&self.file_syntax(db))
}
}
impl<FileId: Copy, N: AstNode> InFileWrapper<FileId, N> {
pub fn syntax(&self) -> InFileWrapper<FileId, &SyntaxNode> {
self.with_value(self.value.syntax())

View file

@ -22,7 +22,7 @@ use crate::{
consteval::ConstEvalError,
dyn_compatibility::DynCompatibilityViolation,
layout::{Layout, LayoutError},
lower::{GenericDefaults, GenericPredicates},
lower::{Diagnostics, GenericDefaults, GenericPredicates},
method_resolution::{InherentImpls, TraitImpls, TyFingerprint},
mir::{BorrowckResult, MirBody, MirLowerError},
Binders, ClosureId, Const, FnDefId, ImplTraitId, ImplTraits, InferenceResult, Interner,
@ -115,21 +115,35 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
#[ra_salsa::cycle(crate::lower::ty_recover)]
fn ty(&self, def: TyDefId) -> Binders<Ty>;
#[ra_salsa::invoke(crate::lower::type_for_type_alias_with_diagnostics_query)]
fn type_for_type_alias_with_diagnostics(&self, def: TypeAliasId) -> (Binders<Ty>, Diagnostics);
/// Returns the type of the value of the given constant, or `None` if the `ValueTyDefId` is
/// a `StructId` or `EnumVariantId` with a record constructor.
#[ra_salsa::invoke(crate::lower::value_ty_query)]
fn value_ty(&self, def: ValueTyDefId) -> Option<Binders<Ty>>;
#[ra_salsa::invoke(crate::lower::impl_self_ty_with_diagnostics_query)]
#[ra_salsa::cycle(crate::lower::impl_self_ty_with_diagnostics_recover)]
fn impl_self_ty_with_diagnostics(&self, def: ImplId) -> (Binders<Ty>, Diagnostics);
#[ra_salsa::invoke(crate::lower::impl_self_ty_query)]
#[ra_salsa::cycle(crate::lower::impl_self_ty_recover)]
fn impl_self_ty(&self, def: ImplId) -> Binders<Ty>;
#[ra_salsa::invoke(crate::lower::const_param_ty_with_diagnostics_query)]
fn const_param_ty_with_diagnostics(&self, def: ConstParamId) -> (Ty, Diagnostics);
#[ra_salsa::invoke(crate::lower::const_param_ty_query)]
fn const_param_ty(&self, def: ConstParamId) -> Ty;
#[ra_salsa::invoke(crate::lower::impl_trait_with_diagnostics_query)]
fn impl_trait_with_diagnostics(&self, def: ImplId) -> Option<(Binders<TraitRef>, Diagnostics)>;
#[ra_salsa::invoke(crate::lower::impl_trait_query)]
fn impl_trait(&self, def: ImplId) -> Option<Binders<TraitRef>>;
#[ra_salsa::invoke(crate::lower::field_types_with_diagnostics_query)]
fn field_types_with_diagnostics(
&self,
var: VariantId,
) -> (Arc<ArenaMap<LocalFieldId, Binders<Ty>>>, Diagnostics);
#[ra_salsa::invoke(crate::lower::field_types_query)]
fn field_types(&self, var: VariantId) -> Arc<ArenaMap<LocalFieldId, Binders<Ty>>>;
@ -154,6 +168,11 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
#[ra_salsa::invoke(crate::lower::generic_predicates_query)]
fn generic_predicates(&self, def: GenericDefId) -> GenericPredicates;
#[ra_salsa::invoke(crate::lower::generic_predicates_without_parent_with_diagnostics_query)]
fn generic_predicates_without_parent_with_diagnostics(
&self,
def: GenericDefId,
) -> (GenericPredicates, Diagnostics);
#[ra_salsa::invoke(crate::lower::generic_predicates_without_parent_query)]
fn generic_predicates_without_parent(&self, def: GenericDefId) -> GenericPredicates;
@ -164,8 +183,13 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
#[ra_salsa::invoke(crate::lower::trait_environment_query)]
fn trait_environment(&self, def: GenericDefId) -> Arc<TraitEnvironment>;
#[ra_salsa::invoke(crate::lower::generic_defaults_with_diagnostics_query)]
#[ra_salsa::cycle(crate::lower::generic_defaults_with_diagnostics_recover)]
fn generic_defaults_with_diagnostics(
&self,
def: GenericDefId,
) -> (GenericDefaults, Diagnostics);
#[ra_salsa::invoke(crate::lower::generic_defaults_query)]
#[ra_salsa::cycle(crate::lower::generic_defaults_recover)]
fn generic_defaults(&self, def: GenericDefId) -> GenericDefaults;
#[ra_salsa::invoke(InherentImpls::inherent_impls_in_crate_query)]

View file

@ -2062,12 +2062,12 @@ impl HirDisplayWithTypesMap for TypeBound {
types_map: &TypesMap,
) -> Result<(), HirDisplayError> {
match self {
TypeBound::Path(path, modifier) => {
&TypeBound::Path(path, modifier) => {
match modifier {
TraitBoundModifier::None => (),
TraitBoundModifier::Maybe => write!(f, "?")?,
}
path.hir_fmt(f, types_map)
types_map[path].hir_fmt(f, types_map)
}
TypeBound::Lifetime(lifetime) => {
write!(f, "{}", lifetime.name.display(f.db.upcast(), f.edition()))
@ -2079,7 +2079,7 @@ impl HirDisplayWithTypesMap for TypeBound {
"for<{}> ",
lifetimes.iter().map(|it| it.display(f.db.upcast(), edition)).format(", ")
)?;
path.hir_fmt(f, types_map)
types_map[*path].hir_fmt(f, types_map)
}
TypeBound::Use(args) => {
let edition = f.edition();

View file

@ -55,6 +55,10 @@ impl Generics {
self.def
}
pub(crate) fn self_types_map(&self) -> &TypesMap {
&self.params.types_map
}
pub(crate) fn iter_id(&self) -> impl Iterator<Item = GenericParamId> + '_ {
self.iter_self_id().chain(self.iter_parent_id())
}
@ -86,15 +90,13 @@ impl Generics {
self.iter_self().chain(self.iter_parent())
}
pub(crate) fn iter_with_types_map(
pub(crate) fn iter_parents_with_types_map(
&self,
) -> impl Iterator<Item = ((GenericParamId, GenericParamDataRef<'_>), &TypesMap)> + '_ {
self.iter_self().zip(std::iter::repeat(&self.params.types_map)).chain(
self.iter_parent().zip(
self.parent_generics()
.into_iter()
.flat_map(|it| std::iter::repeat(&it.params.types_map)),
),
)
}

View file

@ -58,7 +58,7 @@ use crate::{
fold_tys,
generics::Generics,
infer::{coerce::CoerceMany, expr::ExprIsRead, unify::InferenceTable},
lower::ImplTraitLoweringMode,
lower::{ImplTraitLoweringMode, TyLoweringDiagnostic},
mir::MirSpan,
to_assoc_type_id,
traits::FnTrait,
@ -191,6 +191,14 @@ impl<T> InferOk<T> {
}
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum InferenceTyDiagnosticSource {
/// Diagnostics that come from types in the body.
Body,
/// Diagnostics that come from types in fn parameters/return type, or static & const types.
Signature,
}
#[derive(Debug)]
pub(crate) struct TypeError;
pub(crate) type InferResult<T> = Result<InferOk<T>, TypeError>;
@ -264,6 +272,10 @@ pub enum InferenceDiagnostic {
expr_ty: Ty,
cast_ty: Ty,
},
TyDiagnostic {
source: InferenceTyDiagnosticSource,
diag: TyLoweringDiagnostic,
},
}
/// A mismatch between an expected and an inferred type.
@ -858,7 +870,8 @@ impl<'a> InferenceContext<'a> {
}
fn collect_const(&mut self, data: &ConstData) {
let return_ty = self.make_ty(data.type_ref, &data.types_map);
let return_ty =
self.make_ty(data.type_ref, &data.types_map, InferenceTyDiagnosticSource::Signature);
// Constants might be defining usage sites of TAITs.
self.make_tait_coercion_table(iter::once(&return_ty));
@ -867,7 +880,8 @@ impl<'a> InferenceContext<'a> {
}
fn collect_static(&mut self, data: &StaticData) {
let return_ty = self.make_ty(data.type_ref, &data.types_map);
let return_ty =
self.make_ty(data.type_ref, &data.types_map, InferenceTyDiagnosticSource::Signature);
// Statics might be defining usage sites of TAITs.
self.make_tait_coercion_table(iter::once(&return_ty));
@ -877,7 +891,8 @@ impl<'a> InferenceContext<'a> {
fn collect_fn(&mut self, func: FunctionId) {
let data = self.db.function_data(func);
let mut param_tys = self.with_ty_lowering(&data.types_map, |ctx| {
let mut param_tys =
self.with_ty_lowering(&data.types_map, InferenceTyDiagnosticSource::Signature, |ctx| {
ctx.type_param_mode(ParamLoweringMode::Placeholder)
.impl_trait_mode(ImplTraitLoweringMode::Param);
data.params.iter().map(|&type_ref| ctx.lower_ty(type_ref)).collect::<Vec<_>>()
@ -918,7 +933,8 @@ impl<'a> InferenceContext<'a> {
}
let return_ty = data.ret_type;
let return_ty = self.with_ty_lowering(&data.types_map, |ctx| {
let return_ty =
self.with_ty_lowering(&data.types_map, InferenceTyDiagnosticSource::Signature, |ctx| {
ctx.type_param_mode(ParamLoweringMode::Placeholder)
.impl_trait_mode(ImplTraitLoweringMode::Opaque)
.lower_ty(return_ty)
@ -1226,9 +1242,20 @@ impl<'a> InferenceContext<'a> {
self.result.diagnostics.push(diagnostic);
}
fn push_ty_diagnostics(
&mut self,
source: InferenceTyDiagnosticSource,
diagnostics: Vec<TyLoweringDiagnostic>,
) {
self.result.diagnostics.extend(
diagnostics.into_iter().map(|diag| InferenceDiagnostic::TyDiagnostic { source, diag }),
);
}
fn with_ty_lowering<R>(
&self,
&mut self,
types_map: &TypesMap,
types_source: InferenceTyDiagnosticSource,
f: impl FnOnce(&mut crate::lower::TyLoweringContext<'_>) -> R,
) -> R {
let mut ctx = crate::lower::TyLoweringContext::new(
@ -1237,32 +1264,41 @@ impl<'a> InferenceContext<'a> {
types_map,
self.owner.into(),
);
f(&mut ctx)
let result = f(&mut ctx);
self.push_ty_diagnostics(types_source, ctx.diagnostics);
result
}
fn with_body_ty_lowering<R>(
&self,
&mut self,
f: impl FnOnce(&mut crate::lower::TyLoweringContext<'_>) -> R,
) -> R {
self.with_ty_lowering(&self.body.types, f)
self.with_ty_lowering(&self.body.types, InferenceTyDiagnosticSource::Body, f)
}
fn make_ty(&mut self, type_ref: TypeRefId, types_map: &TypesMap) -> Ty {
let ty = self.with_ty_lowering(types_map, |ctx| ctx.lower_ty(type_ref));
fn make_ty(
&mut self,
type_ref: TypeRefId,
types_map: &TypesMap,
type_source: InferenceTyDiagnosticSource,
) -> Ty {
let ty = self.with_ty_lowering(types_map, type_source, |ctx| ctx.lower_ty(type_ref));
let ty = self.insert_type_vars(ty);
self.normalize_associated_types_in(ty)
}
fn make_body_ty(&mut self, type_ref: TypeRefId) -> Ty {
self.make_ty(type_ref, &self.body.types)
self.make_ty(type_ref, &self.body.types, InferenceTyDiagnosticSource::Body)
}
fn err_ty(&self) -> Ty {
self.result.standard_types.unknown.clone()
}
fn make_lifetime(&mut self, lifetime_ref: &LifetimeRef) -> Lifetime {
let lt = self.with_ty_lowering(TypesMap::EMPTY, |ctx| ctx.lower_lifetime(lifetime_ref));
fn make_body_lifetime(&mut self, lifetime_ref: &LifetimeRef) -> Lifetime {
let lt = self.with_ty_lowering(TypesMap::EMPTY, InferenceTyDiagnosticSource::Body, |ctx| {
ctx.lower_lifetime(lifetime_ref)
});
self.insert_type_vars(lt)
}
@ -1431,12 +1467,20 @@ impl<'a> InferenceContext<'a> {
Some(ResolveValueResult::ValueNs(value, _)) => match value {
ValueNs::EnumVariantId(var) => {
let substs = ctx.substs_from_path(path, var.into(), true);
self.push_ty_diagnostics(
InferenceTyDiagnosticSource::Body,
ctx.diagnostics,
);
let ty = self.db.ty(var.lookup(self.db.upcast()).parent.into());
let ty = self.insert_type_vars(ty.substitute(Interner, &substs));
return (ty, Some(var.into()));
}
ValueNs::StructId(strukt) => {
let substs = ctx.substs_from_path(path, strukt.into(), true);
self.push_ty_diagnostics(
InferenceTyDiagnosticSource::Body,
ctx.diagnostics,
);
let ty = self.db.ty(strukt.into());
let ty = self.insert_type_vars(ty.substitute(Interner, &substs));
return (ty, Some(strukt.into()));
@ -1462,18 +1506,21 @@ impl<'a> InferenceContext<'a> {
return match resolution {
TypeNs::AdtId(AdtId::StructId(strukt)) => {
let substs = ctx.substs_from_path(path, strukt.into(), true);
self.push_ty_diagnostics(InferenceTyDiagnosticSource::Body, ctx.diagnostics);
let ty = self.db.ty(strukt.into());
let ty = self.insert_type_vars(ty.substitute(Interner, &substs));
forbid_unresolved_segments((ty, Some(strukt.into())), unresolved)
}
TypeNs::AdtId(AdtId::UnionId(u)) => {
let substs = ctx.substs_from_path(path, u.into(), true);
self.push_ty_diagnostics(InferenceTyDiagnosticSource::Body, ctx.diagnostics);
let ty = self.db.ty(u.into());
let ty = self.insert_type_vars(ty.substitute(Interner, &substs));
forbid_unresolved_segments((ty, Some(u.into())), unresolved)
}
TypeNs::EnumVariantId(var) => {
let substs = ctx.substs_from_path(path, var.into(), true);
self.push_ty_diagnostics(InferenceTyDiagnosticSource::Body, ctx.diagnostics);
let ty = self.db.ty(var.lookup(self.db.upcast()).parent.into());
let ty = self.insert_type_vars(ty.substitute(Interner, &substs));
forbid_unresolved_segments((ty, Some(var.into())), unresolved)
@ -1519,6 +1566,9 @@ impl<'a> InferenceContext<'a> {
resolved_segment,
current_segment,
false,
&mut |_, _reason| {
// FIXME: Report an error.
},
);
ty = self.table.insert_type_vars(ty);
@ -1532,6 +1582,7 @@ impl<'a> InferenceContext<'a> {
remaining_idx += 1;
remaining_segments = remaining_segments.skip(1);
}
self.push_ty_diagnostics(InferenceTyDiagnosticSource::Body, ctx.diagnostics);
let variant = ty.as_adt().and_then(|(id, _)| match id {
AdtId::StructId(s) => Some(VariantId::StructId(s)),
@ -1550,6 +1601,7 @@ impl<'a> InferenceContext<'a> {
};
let substs =
ctx.substs_from_path_segment(resolved_seg, Some(it.into()), true, None);
self.push_ty_diagnostics(InferenceTyDiagnosticSource::Body, ctx.diagnostics);
let ty = self.db.ty(it.into());
let ty = self.insert_type_vars(ty.substitute(Interner, &substs));

View file

@ -2155,7 +2155,7 @@ impl InferenceContext<'_> {
DebruijnIndex::INNERMOST,
)
},
|this, lt_ref| this.make_lifetime(lt_ref),
|this, lt_ref| this.make_body_lifetime(lt_ref),
),
};

View file

@ -19,7 +19,7 @@ use crate::{
TyBuilder, TyExt, TyKind, ValueTyDefId,
};
use super::{ExprOrPatId, InferenceContext};
use super::{ExprOrPatId, InferenceContext, InferenceTyDiagnosticSource};
impl InferenceContext<'_> {
pub(super) fn infer_path(&mut self, path: &Path, id: ExprOrPatId) -> Option<Ty> {
@ -163,6 +163,7 @@ impl InferenceContext<'_> {
let remaining_segments_for_ty = path.segments().take(path.segments().len() - 1);
let (ty, _) = ctx.lower_ty_relative_path(ty, orig_ns, remaining_segments_for_ty);
self.push_ty_diagnostics(InferenceTyDiagnosticSource::Body, ctx.diagnostics);
let ty = self.table.insert_type_vars(ty);
let ty = self.table.normalize_associated_types_in(ty);
self.resolve_ty_assoc_item(ty, last.name, id).map(|(it, substs)| (it, Some(substs)))?
@ -265,6 +266,9 @@ impl InferenceContext<'_> {
resolved_segment,
remaining_segments_for_ty,
true,
&mut |_, _reason| {
// FIXME: Report an error.
},
)
});
if ty.is_unknown() {

View file

@ -84,12 +84,14 @@ pub use infer::{
cast::CastError,
closure::{CaptureKind, CapturedItem},
could_coerce, could_unify, could_unify_deeply, Adjust, Adjustment, AutoBorrow, BindingMode,
InferenceDiagnostic, InferenceResult, OverloadedDeref, PointerCast,
InferenceDiagnostic, InferenceResult, InferenceTyDiagnosticSource, OverloadedDeref,
PointerCast,
};
pub use interner::Interner;
pub use lower::{
associated_type_shorthand_candidates, ImplTraitLoweringMode, ParamLoweringMode, TyDefId,
TyLoweringContext, ValueTyDefId,
associated_type_shorthand_candidates, GenericArgsProhibitedReason, ImplTraitLoweringMode,
ParamLoweringMode, TyDefId, TyLoweringContext, TyLoweringDiagnostic, TyLoweringDiagnosticKind,
ValueTyDefId,
};
pub use mapping::{
from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id, from_placeholder_idx,

View file

@ -33,8 +33,8 @@ use hir_def::{
path::{GenericArg, GenericArgs, ModPath, Path, PathKind, PathSegment, PathSegments},
resolver::{HasResolver, LifetimeNs, Resolver, TypeNs},
type_ref::{
ConstRef, LifetimeRef, TraitBoundModifier, TraitRef as HirTraitRef, TypeBound, TypeRef,
TypeRefId, TypesMap, TypesSourceMap,
ConstRef, LifetimeRef, PathId, TraitBoundModifier, TraitRef as HirTraitRef, TypeBound,
TypeRef, TypeRefId, TypesMap, TypesSourceMap,
},
AdtId, AssocItemId, CallableDefId, ConstId, ConstParamId, DefWithBodyId, EnumId, EnumVariantId,
FunctionId, GenericDefId, GenericParamId, HasModule, ImplId, InTypeConstLoc, ItemContainerId,
@ -48,7 +48,7 @@ use rustc_pattern_analysis::Captures;
use smallvec::SmallVec;
use stdx::{impl_from, never};
use syntax::ast;
use triomphe::Arc;
use triomphe::{Arc, ThinArc};
use crate::{
all_super_traits,
@ -102,6 +102,31 @@ impl ImplTraitLoweringState {
}
}
type TypeSource = Either<TypeRefId, hir_def::type_ref::TypeSource>;
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct TyLoweringDiagnostic {
pub source: TypeSource,
pub kind: TyLoweringDiagnosticKind,
}
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum TyLoweringDiagnosticKind {
GenericArgsProhibited { segment: u32, reason: GenericArgsProhibitedReason },
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum GenericArgsProhibitedReason {
Module,
TyParam,
SelfTy,
PrimitiveTy,
/// When there is a generic enum, within the expression `Enum::Variant`,
/// either `Enum` or `Variant` are allowed to have generic arguments, but not both.
// FIXME: This is not used now but it should be.
EnumVariant,
}
#[derive(Debug)]
pub struct TyLoweringContext<'a> {
pub db: &'a dyn HirDatabase,
@ -125,6 +150,7 @@ pub struct TyLoweringContext<'a> {
expander: Option<Expander>,
/// Tracks types with explicit `?Sized` bounds.
pub(crate) unsized_types: FxHashSet<Ty>,
pub(crate) diagnostics: Vec<TyLoweringDiagnostic>,
}
impl<'a> TyLoweringContext<'a> {
@ -159,6 +185,7 @@ impl<'a> TyLoweringContext<'a> {
type_param_mode,
expander: None,
unsized_types: FxHashSet::default(),
diagnostics: Vec::new(),
}
}
@ -198,6 +225,20 @@ impl<'a> TyLoweringContext<'a> {
self.type_param_mode = type_param_mode;
self
}
pub fn push_diagnostic(&mut self, type_ref: TypeRefId, kind: TyLoweringDiagnosticKind) {
let source = match self.types_source_map {
Some(source_map) => {
let Ok(source) = source_map.type_syntax(type_ref) else {
stdx::never!("error in synthetic type");
return;
};
Either::Right(source)
}
None => Either::Left(type_ref),
};
self.diagnostics.push(TyLoweringDiagnostic { source, kind });
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]
@ -264,7 +305,8 @@ impl<'a> TyLoweringContext<'a> {
.intern(Interner)
}
TypeRef::Path(path) => {
let (ty, res_) = self.lower_path(path);
let (ty, res_) =
self.lower_path(path, PathId::from_type_ref_unchecked(type_ref_id));
res = res_;
ty
}
@ -463,6 +505,7 @@ impl<'a> TyLoweringContext<'a> {
impl_trait_mode: mem::take(&mut self.impl_trait_mode),
expander: self.expander.take(),
unsized_types: mem::take(&mut self.unsized_types),
diagnostics: mem::take(&mut self.diagnostics),
};
let ty = inner_ctx.lower_ty(type_ref);
@ -470,6 +513,7 @@ impl<'a> TyLoweringContext<'a> {
self.impl_trait_mode = inner_ctx.impl_trait_mode;
self.expander = inner_ctx.expander;
self.unsized_types = inner_ctx.unsized_types;
self.diagnostics = inner_ctx.diagnostics;
self.expander.as_mut().unwrap().exit(mark);
Some(ty)
@ -541,6 +585,10 @@ impl<'a> TyLoweringContext<'a> {
resolved_segment: PathSegment<'_>,
remaining_segments: PathSegments<'_>,
infer_args: bool,
on_prohibited_generics_for_resolved_segment: &mut dyn FnMut(
&mut Self,
GenericArgsProhibitedReason,
),
) -> (Ty, Option<TypeNs>) {
let ty = match resolution {
TypeNs::TraitId(trait_) => {
@ -607,7 +655,15 @@ impl<'a> TyLoweringContext<'a> {
// FIXME(trait_alias): Implement trait alias.
return (TyKind::Error.intern(Interner), None);
}
TypeNs::GenericParam(param_id) => match self.type_param_mode {
TypeNs::GenericParam(param_id) => {
if resolved_segment.args_and_bindings.is_some() {
on_prohibited_generics_for_resolved_segment(
self,
GenericArgsProhibitedReason::TyParam,
);
}
match self.type_param_mode {
ParamLoweringMode::Placeholder => {
TyKind::Placeholder(to_placeholder_idx(self.db, param_id.into()))
}
@ -627,8 +683,16 @@ impl<'a> TyLoweringContext<'a> {
TyKind::BoundVar(BoundVar::new(self.in_binders, idx))
}
}
.intern(Interner),
.intern(Interner)
}
TypeNs::SelfType(impl_id) => {
if resolved_segment.args_and_bindings.is_some() {
on_prohibited_generics_for_resolved_segment(
self,
GenericArgsProhibitedReason::SelfTy,
);
}
let generics = self.generics().expect("impl should have generic param scope");
match self.type_param_mode {
@ -654,6 +718,13 @@ impl<'a> TyLoweringContext<'a> {
}
}
TypeNs::AdtSelfType(adt) => {
if resolved_segment.args_and_bindings.is_some() {
on_prohibited_generics_for_resolved_segment(
self,
GenericArgsProhibitedReason::SelfTy,
);
}
let generics = generics(self.db.upcast(), adt.into());
let substs = match self.type_param_mode {
ParamLoweringMode::Placeholder => generics.placeholder_subst(self.db),
@ -666,6 +737,12 @@ impl<'a> TyLoweringContext<'a> {
TypeNs::AdtId(it) => self.lower_path_inner(resolved_segment, it.into(), infer_args),
TypeNs::BuiltinType(it) => {
if resolved_segment.args_and_bindings.is_some() {
on_prohibited_generics_for_resolved_segment(
self,
GenericArgsProhibitedReason::PrimitiveTy,
);
}
self.lower_path_inner(resolved_segment, it.into(), infer_args)
}
TypeNs::TypeAliasId(it) => {
@ -677,7 +754,7 @@ impl<'a> TyLoweringContext<'a> {
self.lower_ty_relative_path(ty, Some(resolution), remaining_segments)
}
pub(crate) fn lower_path(&mut self, path: &Path) -> (Ty, Option<TypeNs>) {
pub(crate) fn lower_path(&mut self, path: &Path, path_id: PathId) -> (Ty, Option<TypeNs>) {
// Resolve the path (in type namespace)
if let Some(type_ref) = path.type_anchor() {
let (ty, res) = self.lower_ty_ext(type_ref);
@ -692,19 +769,44 @@ impl<'a> TyLoweringContext<'a> {
if matches!(resolution, TypeNs::TraitId(_)) && remaining_index.is_none() {
// trait object type without dyn
let bound = TypeBound::Path(path.clone(), TraitBoundModifier::None);
let bound = TypeBound::Path(path_id, TraitBoundModifier::None);
let ty = self.lower_dyn_trait(&[bound]);
return (ty, None);
}
let (resolved_segment, remaining_segments) = match remaining_index {
let (module_segments, resolved_segment_idx, resolved_segment, remaining_segments) =
match remaining_index {
None => (
path.segments().strip_last(),
path.segments().len() - 1,
path.segments().last().expect("resolved path has at least one element"),
PathSegments::EMPTY,
),
Some(i) => (path.segments().get(i - 1).unwrap(), path.segments().skip(i)),
Some(i) => (
path.segments().take(i - 1),
i - 1,
path.segments().get(i - 1).unwrap(),
path.segments().skip(i),
),
};
self.lower_partly_resolved_path(resolution, resolved_segment, remaining_segments, false)
self.prohibit_generics(path_id, 0, module_segments, GenericArgsProhibitedReason::Module);
self.lower_partly_resolved_path(
resolution,
resolved_segment,
remaining_segments,
false,
&mut |this, reason| {
this.push_diagnostic(
path_id.type_ref(),
TyLoweringDiagnosticKind::GenericArgsProhibited {
segment: resolved_segment_idx as u32,
reason,
},
)
},
)
}
fn select_associated_type(&mut self, res: Option<TypeNs>, segment: PathSegment<'_>) -> Ty {
@ -741,12 +843,8 @@ impl<'a> TyLoweringContext<'a> {
// generic params. It's inefficient to splice the `Substitution`s, so we may want
// that method to optionally take parent `Substitution` as we already know them at
// this point (`t.substitution`).
let substs = self.substs_from_path_segment(
segment.clone(),
Some(associated_ty.into()),
false,
None,
);
let substs =
self.substs_from_path_segment(segment, Some(associated_ty.into()), false, None);
let len_self =
crate::generics::generics(self.db.upcast(), associated_ty.into()).len_self();
@ -998,12 +1096,41 @@ impl<'a> TyLoweringContext<'a> {
TraitRef { trait_id: to_chalk_trait_id(resolved), substitution: substs }
}
fn lower_trait_ref_from_path(&mut self, path: &Path, explicit_self_ty: Ty) -> Option<TraitRef> {
fn prohibit_generics(
&mut self,
path_id: PathId,
idx: u32,
segments: PathSegments<'_>,
reason: GenericArgsProhibitedReason,
) {
segments.iter().zip(idx..).for_each(|(segment, idx)| {
if segment.args_and_bindings.is_some() {
self.push_diagnostic(
path_id.type_ref(),
TyLoweringDiagnosticKind::GenericArgsProhibited { segment: idx, reason },
);
}
});
}
fn lower_trait_ref_from_path(
&mut self,
path_id: PathId,
explicit_self_ty: Ty,
) -> Option<TraitRef> {
let path = &self.types_map[path_id];
let resolved = match self.resolver.resolve_path_in_type_ns_fully(self.db.upcast(), path)? {
// FIXME(trait_alias): We need to handle trait alias here.
TypeNs::TraitId(tr) => tr,
_ => return None,
};
// Do this after we verify it's indeed a trait to not confuse the user if they're not modules.
self.prohibit_generics(
path_id,
0,
path.segments().strip_last(),
GenericArgsProhibitedReason::Module,
);
let segment = path.segments().last().expect("path should have at least one segment");
Some(self.lower_trait_ref_from_resolved_path(resolved, segment, explicit_self_ty))
}
@ -1013,7 +1140,7 @@ impl<'a> TyLoweringContext<'a> {
trait_ref: &HirTraitRef,
explicit_self_ty: Ty,
) -> Option<TraitRef> {
self.lower_trait_ref_from_path(&trait_ref.path, explicit_self_ty)
self.lower_trait_ref_from_path(trait_ref.path, explicit_self_ty)
}
fn trait_ref_substs_from_path(
@ -1072,11 +1199,11 @@ impl<'a> TyLoweringContext<'a> {
) -> impl Iterator<Item = QuantifiedWhereClause> + use<'b, 'a> {
let mut trait_ref = None;
let clause = match bound {
TypeBound::Path(path, TraitBoundModifier::None) => {
&TypeBound::Path(path, TraitBoundModifier::None) => {
trait_ref = self.lower_trait_ref_from_path(path, self_ty);
trait_ref.clone().map(WhereClause::Implemented).map(crate::wrap_empty_binders)
}
TypeBound::Path(path, TraitBoundModifier::Maybe) => {
&TypeBound::Path(path, TraitBoundModifier::Maybe) => {
let sized_trait = self
.db
.lang_item(self.resolver.krate(), LangItem::Sized)
@ -1092,7 +1219,7 @@ impl<'a> TyLoweringContext<'a> {
}
None
}
TypeBound::ForLifetime(_, path) => {
&TypeBound::ForLifetime(_, path) => {
// FIXME Don't silently drop the hrtb lifetimes here
trait_ref = self.lower_trait_ref_from_path(path, self_ty);
trait_ref.clone().map(WhereClause::Implemented).map(crate::wrap_empty_binders)
@ -1121,8 +1248,8 @@ impl<'a> TyLoweringContext<'a> {
trait_ref: TraitRef,
) -> impl Iterator<Item = QuantifiedWhereClause> + use<'b, 'a> {
let last_segment = match bound {
TypeBound::Path(path, TraitBoundModifier::None) | TypeBound::ForLifetime(_, path) => {
path.segments().last()
&TypeBound::Path(path, TraitBoundModifier::None) | &TypeBound::ForLifetime(_, path) => {
self.types_map[path].segments().last()
}
TypeBound::Path(_, TraitBoundModifier::Maybe)
| TypeBound::Use(_)
@ -1227,7 +1354,9 @@ impl<'a> TyLoweringContext<'a> {
}
_ => unreachable!(),
}
ext.lower_ty(type_ref)
let ty = ext.lower_ty(type_ref);
self.diagnostics.extend(ext.diagnostics);
ty
} else {
self.lower_ty(type_ref)
};
@ -1523,11 +1652,24 @@ fn named_associated_type_shorthand_candidates<R>(
}
}
/// Build the type of all specific fields of a struct or enum variant.
pub(crate) type Diagnostics = Option<ThinArc<(), TyLoweringDiagnostic>>;
fn create_diagnostics(diagnostics: Vec<TyLoweringDiagnostic>) -> Diagnostics {
(!diagnostics.is_empty()).then(|| ThinArc::from_header_and_iter((), diagnostics.into_iter()))
}
pub(crate) fn field_types_query(
db: &dyn HirDatabase,
variant_id: VariantId,
) -> Arc<ArenaMap<LocalFieldId, Binders<Ty>>> {
db.field_types_with_diagnostics(variant_id).0
}
/// Build the type of all specific fields of a struct or enum variant.
pub(crate) fn field_types_with_diagnostics_query(
db: &dyn HirDatabase,
variant_id: VariantId,
) -> (Arc<ArenaMap<LocalFieldId, Binders<Ty>>>, Diagnostics) {
let var_data = variant_id.variant_data(db.upcast());
let (resolver, def): (_, GenericDefId) = match variant_id {
VariantId::StructId(it) => (it.resolver(db.upcast()), it.into()),
@ -1543,7 +1685,7 @@ pub(crate) fn field_types_query(
for (field_id, field_data) in var_data.fields().iter() {
res.insert(field_id, make_binders(db, &generics, ctx.lower_ty(field_data.type_ref)));
}
Arc::new(res)
(Arc::new(res), create_diagnostics(ctx.diagnostics))
}
/// This query exists only to be used when resolving short-hand associated types
@ -1593,9 +1735,10 @@ pub(crate) fn generic_predicates_for_param_query(
}
match bound {
TypeBound::ForLifetime(_, path) | TypeBound::Path(path, _) => {
&TypeBound::ForLifetime(_, path) | &TypeBound::Path(path, _) => {
// Only lower the bound if the trait could possibly define the associated
// type we're looking for.
let path = &ctx.types_map[path];
let Some(assoc_name) = &assoc_name else { return true };
let Some(TypeNs::TraitId(tr)) =
@ -1743,15 +1886,22 @@ pub(crate) fn generic_predicates_query(
db: &dyn HirDatabase,
def: GenericDefId,
) -> GenericPredicates {
generic_predicates_filtered_by(db, def, |_, _| true)
generic_predicates_filtered_by(db, def, |_, _| true).0
}
/// Resolve the where clause(s) of an item with generics,
/// except the ones inherited from the parent
pub(crate) fn generic_predicates_without_parent_query(
db: &dyn HirDatabase,
def: GenericDefId,
) -> GenericPredicates {
db.generic_predicates_without_parent_with_diagnostics(def).0
}
/// Resolve the where clause(s) of an item with generics,
/// except the ones inherited from the parent
pub(crate) fn generic_predicates_without_parent_with_diagnostics_query(
db: &dyn HirDatabase,
def: GenericDefId,
) -> (GenericPredicates, Diagnostics) {
generic_predicates_filtered_by(db, def, |_, d| *d == def)
}
@ -1761,7 +1911,7 @@ fn generic_predicates_filtered_by<F>(
db: &dyn HirDatabase,
def: GenericDefId,
filter: F,
) -> GenericPredicates
) -> (GenericPredicates, Diagnostics)
where
F: Fn(&WherePredicate, &GenericDefId) -> bool,
{
@ -1802,7 +1952,10 @@ where
);
};
}
GenericPredicates(predicates.is_empty().not().then(|| predicates.into()))
(
GenericPredicates(predicates.is_empty().not().then(|| predicates.into())),
create_diagnostics(ctx.diagnostics),
)
}
/// Generate implicit `: Sized` predicates for all generics that has no `?Sized` bound.
@ -1855,21 +2008,56 @@ impl ops::Deref for GenericDefaults {
}
}
/// Resolve the default type params from generics
pub(crate) fn generic_defaults_query(db: &dyn HirDatabase, def: GenericDefId) -> GenericDefaults {
db.generic_defaults_with_diagnostics(def).0
}
/// Resolve the default type params from generics.
///
/// Diagnostics are only returned for this `GenericDefId` (returned defaults include parents).
pub(crate) fn generic_defaults_with_diagnostics_query(
db: &dyn HirDatabase,
def: GenericDefId,
) -> (GenericDefaults, Diagnostics) {
let generic_params = generics(db.upcast(), def);
if generic_params.len() == 0 {
return GenericDefaults(None);
return (GenericDefaults(None), None);
}
let resolver = def.resolver(db.upcast());
let parent_start_idx = generic_params.len_self();
let mut ctx = TyLoweringContext::new(db, &resolver, TypesMap::EMPTY, def.into())
let mut ctx =
TyLoweringContext::new(db, &resolver, generic_params.self_types_map(), def.into())
.with_impl_trait_mode(ImplTraitLoweringMode::Disallowed)
.with_type_param_mode(ParamLoweringMode::Variable);
GenericDefaults(Some(Arc::from_iter(generic_params.iter_with_types_map().enumerate().map(
|(idx, ((id, p), types_map))| {
let mut idx = 0;
let mut defaults = generic_params
.iter_self()
.map(|(id, p)| {
let result =
handle_generic_param(&mut ctx, idx, id, p, parent_start_idx, &generic_params);
idx += 1;
result
})
.collect::<Vec<_>>();
let diagnostics = create_diagnostics(mem::take(&mut ctx.diagnostics));
defaults.extend(generic_params.iter_parents_with_types_map().map(|((id, p), types_map)| {
ctx.types_map = types_map;
let result = handle_generic_param(&mut ctx, idx, id, p, parent_start_idx, &generic_params);
idx += 1;
result
}));
let defaults = GenericDefaults(Some(Arc::from_iter(defaults)));
return (defaults, diagnostics);
fn handle_generic_param(
ctx: &mut TyLoweringContext<'_>,
idx: usize,
id: GenericParamId,
p: GenericParamDataRef<'_>,
parent_start_idx: usize,
generic_params: &Generics,
) -> Binders<crate::GenericArg> {
match p {
GenericParamDataRef::TypeParamData(p) => {
let ty = p.default.as_ref().map_or(TyKind::Error.intern(Interner), |ty| {
@ -1878,7 +2066,7 @@ pub(crate) fn generic_defaults_query(db: &dyn HirDatabase, def: GenericDefId) ->
// after it is forbidden (FIXME: report diagnostic)
fallback_bound_vars(ctx.lower_ty(*ty), idx, parent_start_idx)
});
crate::make_binders(db, &generic_params, ty.cast(Interner))
crate::make_binders(ctx.db, generic_params, ty.cast(Interner))
}
GenericParamDataRef::ConstParamData(p) => {
let GenericParamId::ConstParamId(id) = id else {
@ -1886,7 +2074,7 @@ pub(crate) fn generic_defaults_query(db: &dyn HirDatabase, def: GenericDefId) ->
};
let mut val = p.default.as_ref().map_or_else(
|| unknown_const_as_generic(db.const_param_ty(id)),
|| unknown_const_as_generic(ctx.db.const_param_ty(id)),
|c| {
let param_ty = ctx.lower_ty(p.ty);
let c = ctx.lower_const(c, param_ty);
@ -1895,35 +2083,35 @@ pub(crate) fn generic_defaults_query(db: &dyn HirDatabase, def: GenericDefId) ->
);
// Each default can only refer to previous parameters, see above.
val = fallback_bound_vars(val, idx, parent_start_idx);
make_binders(db, &generic_params, val)
make_binders(ctx.db, generic_params, val)
}
GenericParamDataRef::LifetimeParamData(_) => {
make_binders(db, &generic_params, error_lifetime().cast(Interner))
make_binders(ctx.db, generic_params, error_lifetime().cast(Interner))
}
}
}
},
))))
}
pub(crate) fn generic_defaults_recover(
pub(crate) fn generic_defaults_with_diagnostics_recover(
db: &dyn HirDatabase,
_cycle: &Cycle,
def: &GenericDefId,
) -> GenericDefaults {
) -> (GenericDefaults, Diagnostics) {
let generic_params = generics(db.upcast(), *def);
if generic_params.len() == 0 {
return GenericDefaults(None);
return (GenericDefaults(None), None);
}
// FIXME: this code is not covered in tests.
// we still need one default per parameter
GenericDefaults(Some(Arc::from_iter(generic_params.iter_id().map(|id| {
let defaults = GenericDefaults(Some(Arc::from_iter(generic_params.iter_id().map(|id| {
let val = match id {
GenericParamId::TypeParamId(_) => TyKind::Error.intern(Interner).cast(Interner),
GenericParamId::ConstParamId(id) => unknown_const_as_generic(db.const_param_ty(id)),
GenericParamId::LifetimeParamId(_) => error_lifetime().cast(Interner),
};
crate::make_binders(db, &generic_params, val)
}))))
}))));
(defaults, None)
}
fn fn_sig_for_fn(db: &dyn HirDatabase, def: FunctionId) -> PolyFnSig {
@ -2066,7 +2254,10 @@ fn type_for_adt(db: &dyn HirDatabase, adt: AdtId) -> Binders<Ty> {
make_binders(db, &generics, ty)
}
fn type_for_type_alias(db: &dyn HirDatabase, t: TypeAliasId) -> Binders<Ty> {
pub(crate) fn type_for_type_alias_with_diagnostics_query(
db: &dyn HirDatabase,
t: TypeAliasId,
) -> (Binders<Ty>, Diagnostics) {
let generics = generics(db.upcast(), t.into());
let resolver = t.resolver(db.upcast());
let type_alias_data = db.type_alias_data(t);
@ -2081,7 +2272,7 @@ fn type_for_type_alias(db: &dyn HirDatabase, t: TypeAliasId) -> Binders<Ty> {
.map(|type_ref| ctx.lower_ty(type_ref))
.unwrap_or_else(|| TyKind::Error.intern(Interner))
};
make_binders(db, &generics, inner)
(make_binders(db, &generics, inner), create_diagnostics(ctx.diagnostics))
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
@ -2124,7 +2315,7 @@ pub(crate) fn ty_query(db: &dyn HirDatabase, def: TyDefId) -> Binders<Ty> {
match def {
TyDefId::BuiltinType(it) => Binders::empty(Interner, TyBuilder::builtin(it)),
TyDefId::AdtId(it) => type_for_adt(db, it),
TyDefId::TypeAliasId(it) => type_for_type_alias(db, it),
TyDefId::TypeAliasId(it) => db.type_for_type_alias_with_diagnostics(it).0,
}
}
@ -2149,47 +2340,73 @@ pub(crate) fn value_ty_query(db: &dyn HirDatabase, def: ValueTyDefId) -> Option<
}
pub(crate) fn impl_self_ty_query(db: &dyn HirDatabase, impl_id: ImplId) -> Binders<Ty> {
db.impl_self_ty_with_diagnostics(impl_id).0
}
pub(crate) fn impl_self_ty_with_diagnostics_query(
db: &dyn HirDatabase,
impl_id: ImplId,
) -> (Binders<Ty>, Diagnostics) {
let impl_data = db.impl_data(impl_id);
let resolver = impl_id.resolver(db.upcast());
let generics = generics(db.upcast(), impl_id.into());
let mut ctx = TyLoweringContext::new(db, &resolver, &impl_data.types_map, impl_id.into())
.with_type_param_mode(ParamLoweringMode::Variable);
make_binders(db, &generics, ctx.lower_ty(impl_data.self_ty))
(
make_binders(db, &generics, ctx.lower_ty(impl_data.self_ty)),
create_diagnostics(ctx.diagnostics),
)
}
pub(crate) fn const_param_ty_query(db: &dyn HirDatabase, def: ConstParamId) -> Ty {
db.const_param_ty_with_diagnostics(def).0
}
// returns None if def is a type arg
pub(crate) fn const_param_ty_query(db: &dyn HirDatabase, def: ConstParamId) -> Ty {
pub(crate) fn const_param_ty_with_diagnostics_query(
db: &dyn HirDatabase,
def: ConstParamId,
) -> (Ty, Diagnostics) {
let parent_data = db.generic_params(def.parent());
let data = &parent_data[def.local_id()];
let resolver = def.parent().resolver(db.upcast());
let mut ctx =
TyLoweringContext::new(db, &resolver, &parent_data.types_map, def.parent().into());
match data {
let ty = match data {
TypeOrConstParamData::TypeParamData(_) => {
never!();
Ty::new(Interner, TyKind::Error)
}
TypeOrConstParamData::ConstParamData(d) => ctx.lower_ty(d.ty),
}
};
(ty, create_diagnostics(ctx.diagnostics))
}
pub(crate) fn impl_self_ty_recover(
pub(crate) fn impl_self_ty_with_diagnostics_recover(
db: &dyn HirDatabase,
_cycle: &Cycle,
impl_id: &ImplId,
) -> Binders<Ty> {
) -> (Binders<Ty>, Diagnostics) {
let generics = generics(db.upcast(), (*impl_id).into());
make_binders(db, &generics, TyKind::Error.intern(Interner))
(make_binders(db, &generics, TyKind::Error.intern(Interner)), None)
}
pub(crate) fn impl_trait_query(db: &dyn HirDatabase, impl_id: ImplId) -> Option<Binders<TraitRef>> {
db.impl_trait_with_diagnostics(impl_id).map(|it| it.0)
}
pub(crate) fn impl_trait_with_diagnostics_query(
db: &dyn HirDatabase,
impl_id: ImplId,
) -> Option<(Binders<TraitRef>, Diagnostics)> {
let impl_data = db.impl_data(impl_id);
let resolver = impl_id.resolver(db.upcast());
let mut ctx = TyLoweringContext::new(db, &resolver, &impl_data.types_map, impl_id.into())
.with_type_param_mode(ParamLoweringMode::Variable);
let (self_ty, binders) = db.impl_self_ty(impl_id).into_value_and_skipped_binders();
let target_trait = impl_data.target_trait.as_ref()?;
Some(Binders::new(binders, ctx.lower_trait_ref(target_trait, self_ty)?))
let trait_ref = Binders::new(binders, ctx.lower_trait_ref(target_trait, self_ty)?);
Some((trait_ref, create_diagnostics(ctx.diagnostics)))
}
pub(crate) fn return_type_impl_traits(

View file

@ -18,13 +18,13 @@ use std::sync::LazyLock;
use base_db::SourceDatabaseFileInputExt as _;
use expect_test::Expect;
use hir_def::{
body::{Body, BodySourceMap, SyntheticSyntax},
body::{Body, BodySourceMap},
db::DefDatabase,
hir::{ExprId, Pat, PatId},
item_scope::ItemScope,
nameres::DefMap,
src::HasSource,
AssocItemId, DefWithBodyId, HasModule, LocalModuleId, Lookup, ModuleDefId,
AssocItemId, DefWithBodyId, HasModule, LocalModuleId, Lookup, ModuleDefId, SyntheticSyntax,
};
use hir_expand::{db::ExpandDatabase, FileRange, InFile};
use itertools::Itertools;

View file

@ -185,7 +185,7 @@ fn direct_super_traits_cb(db: &dyn DefDatabase, trait_: TraitId, cb: impl FnMut(
}
};
match is_trait {
true => bound.as_path(),
true => bound.as_path(&generic_params.types_map),
false => None,
}
}

View file

@ -33,6 +33,14 @@ syntax.workspace = true
tt.workspace = true
span.workspace = true
[dev-dependencies]
expect-test.workspace = true
# local deps
test-utils.workspace = true
test-fixture.workspace = true
syntax-bridge.workspace = true
[features]
in-rust-tree = ["hir-expand/in-rust-tree"]

View file

@ -3,23 +3,35 @@
//!
//! This probably isn't the best way to do this -- ideally, diagnostics should
//! be expressed in terms of hir types themselves.
pub use hir_ty::diagnostics::{CaseType, IncorrectCase};
use cfg::{CfgExpr, CfgOptions};
use either::Either;
use hir_def::{
hir::ExprOrPatId,
path::{hir_segment_to_ast_segment, ModPath},
type_ref::TypesSourceMap,
AssocItemId, DefWithBodyId, SyntheticSyntax,
};
use hir_expand::{name::Name, HirFileId, InFile};
use hir_ty::{
db::HirDatabase,
diagnostics::{BodyValidationDiagnostic, UnsafetyReason},
CastError, InferenceDiagnostic,
CastError, InferenceDiagnostic, InferenceTyDiagnosticSource, TyLoweringDiagnostic,
TyLoweringDiagnosticKind,
};
use syntax::{
ast::{self, HasGenericArgs},
AstPtr, SyntaxError, SyntaxNodePtr, TextRange,
};
use cfg::{CfgExpr, CfgOptions};
use either::Either;
pub use hir_def::VariantId;
use hir_def::{body::SyntheticSyntax, hir::ExprOrPatId, path::ModPath, AssocItemId, DefWithBodyId};
use hir_expand::{name::Name, HirFileId, InFile};
use syntax::{ast, AstPtr, SyntaxError, SyntaxNodePtr, TextRange};
use triomphe::Arc;
use crate::{AssocItem, Field, Local, Trait, Type};
pub use hir_def::VariantId;
pub use hir_ty::{
diagnostics::{CaseType, IncorrectCase},
GenericArgsProhibitedReason,
};
macro_rules! diagnostics {
($($diag:ident,)*) => {
#[derive(Debug)]
@ -98,6 +110,7 @@ diagnostics![
UnresolvedIdent,
UnusedMut,
UnusedVariable,
GenericArgsProhibited,
];
#[derive(Debug)]
@ -388,6 +401,12 @@ pub struct InvalidCast {
pub cast_ty: Type,
}
#[derive(Debug)]
pub struct GenericArgsProhibited {
pub args: InFile<AstPtr<Either<ast::GenericArgList, ast::ParenthesizedArgList>>>,
pub reason: GenericArgsProhibitedReason,
}
impl AnyDiagnostic {
pub(crate) fn body_validation_diagnostic(
db: &dyn HirDatabase,
@ -527,6 +546,7 @@ impl AnyDiagnostic {
db: &dyn HirDatabase,
def: DefWithBodyId,
d: &InferenceDiagnostic,
outer_types_source_map: &TypesSourceMap,
source_map: &hir_def::body::BodySourceMap,
) -> Option<AnyDiagnostic> {
let expr_syntax = |expr| {
@ -640,6 +660,44 @@ impl AnyDiagnostic {
let cast_ty = Type::new(db, def, cast_ty.clone());
InvalidCast { expr, error: *error, expr_ty, cast_ty }.into()
}
InferenceDiagnostic::TyDiagnostic { source, diag } => {
let source_map = match source {
InferenceTyDiagnosticSource::Body => &source_map.types,
InferenceTyDiagnosticSource::Signature => outer_types_source_map,
};
Self::ty_diagnostic(diag, source_map, db)?
}
})
}
pub(crate) fn ty_diagnostic(
diag: &TyLoweringDiagnostic,
source_map: &TypesSourceMap,
db: &dyn HirDatabase,
) -> Option<AnyDiagnostic> {
let source = match diag.source {
Either::Left(type_ref_id) => {
let Ok(source) = source_map.type_syntax(type_ref_id) else {
stdx::never!("error on synthetic type syntax");
return None;
};
source
}
Either::Right(source) => source,
};
let syntax = || source.value.to_node(&db.parse_or_expand(source.file_id));
Some(match diag.kind {
TyLoweringDiagnosticKind::GenericArgsProhibited { segment, reason } => {
let ast::Type::PathType(syntax) = syntax() else { return None };
let segment = hir_segment_to_ast_segment(&syntax.path()?, segment)?;
let args = if let Some(generics) = segment.generic_arg_list() {
AstPtr::new(&generics).wrap_left()
} else {
AstPtr::new(&segment.parenthesized_arg_list()?).wrap_right()
};
let args = source.with_value(args);
GenericArgsProhibited { args, reason }.into()
}
})
}
}

View file

@ -132,9 +132,15 @@ impl HirDisplay for Function {
} else {
match &data.types_map[data.ret_type] {
TypeRef::ImplTrait(bounds) => match &bounds[0] {
TypeBound::Path(path, _) => Some(
*path.segments().iter().last().unwrap().args_and_bindings.unwrap().bindings
[0]
&TypeBound::Path(path, _) => Some(
*data.types_map[path]
.segments()
.iter()
.last()
.unwrap()
.args_and_bindings
.unwrap()
.bindings[0]
.type_ref
.as_ref()
.unwrap(),

View file

@ -20,12 +20,11 @@
#![cfg_attr(feature = "in-rust-tree", feature(rustc_private))]
#![recursion_limit = "512"]
mod semantics;
mod source_analyzer;
mod attrs;
mod from_id;
mod has_source;
mod semantics;
mod source_analyzer;
pub mod db;
pub mod diagnostics;
@ -43,7 +42,7 @@ use arrayvec::ArrayVec;
use base_db::{CrateDisplayName, CrateId, CrateOrigin};
use either::Either;
use hir_def::{
body::{BodyDiagnostic, SyntheticSyntax},
body::BodyDiagnostic,
data::adt::VariantData,
generics::{LifetimeParamData, TypeOrConstParamData, TypeParamProvenance},
hir::{BindingAnnotation, BindingId, ExprId, ExprOrPatId, LabelId, Pat},
@ -54,11 +53,12 @@ use hir_def::{
path::ImportAlias,
per_ns::PerNs,
resolver::{HasResolver, Resolver},
type_ref::TypesSourceMap,
AssocItemId, AssocItemLoc, AttrDefId, CallableDefId, ConstId, ConstParamId, CrateRootModuleId,
DefWithBodyId, EnumId, EnumVariantId, ExternCrateId, FunctionId, GenericDefId, GenericParamId,
HasModule, ImplId, InTypeConstId, ItemContainerId, LifetimeParamId, LocalFieldId, Lookup,
MacroExpander, ModuleId, StaticId, StructId, TraitAliasId, TraitId, TupleId, TypeAliasId,
TypeOrConstParamId, TypeParamId, UnionId,
MacroExpander, ModuleId, StaticId, StructId, SyntheticSyntax, TraitAliasId, TraitId, TupleId,
TypeAliasId, TypeOrConstParamId, TypeParamId, UnionId,
};
use hir_expand::{
attrs::collect_attrs, proc_macro::ProcMacroKind, AstId, MacroCallKind, RenderedExpandError,
@ -76,8 +76,8 @@ use hir_ty::{
traits::FnTrait,
AliasTy, CallableSig, Canonical, CanonicalVarKinds, Cast, ClosureId, GenericArg,
GenericArgData, Interner, ParamKind, QuantifiedWhereClause, Scalar, Substitution,
TraitEnvironment, TraitRefExt, Ty, TyBuilder, TyDefId, TyExt, TyKind, ValueTyDefId,
WhereClause,
TraitEnvironment, TraitRefExt, Ty, TyBuilder, TyDefId, TyExt, TyKind, TyLoweringDiagnostic,
ValueTyDefId, WhereClause,
};
use itertools::Itertools;
use nameres::diagnostics::DefDiagnosticKind;
@ -89,7 +89,7 @@ use syntax::{
ast::{self, HasAttrs as _, HasGenericParams, HasName},
format_smolstr, AstNode, AstPtr, SmolStr, SyntaxNode, SyntaxNodePtr, TextRange, ToSmolStr, T,
};
use triomphe::Arc;
use triomphe::{Arc, ThinArc};
use crate::db::{DefDatabase, HirDatabase};
@ -411,6 +411,10 @@ impl ModuleDef {
}
}
if let Some(def) = self.as_self_generic_def() {
def.diagnostics(db, &mut acc);
}
acc
}
@ -431,6 +435,23 @@ impl ModuleDef {
}
}
/// Returns only defs that have generics from themselves, not their parent.
pub fn as_self_generic_def(self) -> Option<GenericDef> {
match self {
ModuleDef::Function(it) => Some(it.into()),
ModuleDef::Adt(it) => Some(it.into()),
ModuleDef::Trait(it) => Some(it.into()),
ModuleDef::TraitAlias(it) => Some(it.into()),
ModuleDef::TypeAlias(it) => Some(it.into()),
ModuleDef::Module(_)
| ModuleDef::Variant(_)
| ModuleDef::Static(_)
| ModuleDef::Const(_)
| ModuleDef::BuiltinType(_)
| ModuleDef::Macro(_) => None,
}
}
pub fn attrs(&self, db: &dyn HirDatabase) -> Option<AttrsWithOwner> {
Some(match self {
ModuleDef::Module(it) => it.attrs(db),
@ -605,17 +626,42 @@ impl Module {
ModuleDef::Adt(adt) => {
match adt {
Adt::Struct(s) => {
let tree_id = s.id.lookup(db.upcast()).id;
let tree_source_maps = tree_id.item_tree_with_source_map(db.upcast()).1;
push_ty_diagnostics(
db,
acc,
db.field_types_with_diagnostics(s.id.into()).1,
tree_source_maps.strukt(tree_id.value).item(),
);
for diag in db.struct_data_with_diagnostics(s.id).1.iter() {
emit_def_diagnostic(db, acc, diag, edition);
}
}
Adt::Union(u) => {
let tree_id = u.id.lookup(db.upcast()).id;
let tree_source_maps = tree_id.item_tree_with_source_map(db.upcast()).1;
push_ty_diagnostics(
db,
acc,
db.field_types_with_diagnostics(u.id.into()).1,
tree_source_maps.union(tree_id.value).item(),
);
for diag in db.union_data_with_diagnostics(u.id).1.iter() {
emit_def_diagnostic(db, acc, diag, edition);
}
}
Adt::Enum(e) => {
for v in e.variants(db) {
let tree_id = v.id.lookup(db.upcast()).id;
let tree_source_maps =
tree_id.item_tree_with_source_map(db.upcast()).1;
push_ty_diagnostics(
db,
acc,
db.field_types_with_diagnostics(v.id.into()).1,
tree_source_maps.variant(tree_id.value),
);
acc.extend(ModuleDef::Variant(v).diagnostics(db, style_lints));
for diag in db.enum_variant_data_with_diagnostics(v.id).1.iter() {
emit_def_diagnostic(db, acc, diag, edition);
@ -626,6 +672,17 @@ impl Module {
acc.extend(def.diagnostics(db, style_lints))
}
ModuleDef::Macro(m) => emit_macro_def_diagnostics(db, acc, m),
ModuleDef::TypeAlias(type_alias) => {
let tree_id = type_alias.id.lookup(db.upcast()).id;
let tree_source_maps = tree_id.item_tree_with_source_map(db.upcast()).1;
push_ty_diagnostics(
db,
acc,
db.type_for_type_alias_with_diagnostics(type_alias.id).1,
tree_source_maps.type_alias(tree_id.value).item(),
);
acc.extend(def.diagnostics(db, style_lints));
}
_ => acc.extend(def.diagnostics(db, style_lints)),
}
}
@ -635,8 +692,11 @@ impl Module {
let mut impl_assoc_items_scratch = vec![];
for impl_def in self.impl_defs(db) {
GenericDef::Impl(impl_def).diagnostics(db, acc);
let loc = impl_def.id.lookup(db.upcast());
let tree = loc.id.item_tree(db.upcast());
let (tree, tree_source_maps) = loc.id.item_tree_with_source_map(db.upcast());
let source_map = tree_source_maps.impl_(loc.id.value).item();
let node = &tree[loc.id.value];
let file_id = loc.id.file_id();
if file_id.macro_file().map_or(false, |it| it.is_builtin_derive(db.upcast())) {
@ -771,6 +831,19 @@ impl Module {
impl_assoc_items_scratch.clear();
}
push_ty_diagnostics(
db,
acc,
db.impl_self_ty_with_diagnostics(impl_def.id).1,
source_map,
);
push_ty_diagnostics(
db,
acc,
db.impl_trait_with_diagnostics(impl_def.id).and_then(|it| it.1),
source_map,
);
for &item in db.impl_data(impl_def.id).items.iter() {
AssocItem::from(item).diagnostics(db, acc, style_lints);
}
@ -1802,6 +1875,25 @@ impl DefWithBody {
let krate = self.module(db).id.krate();
let (body, source_map) = db.body_with_source_map(self.into());
let item_tree_source_maps;
let outer_types_source_map = match self {
DefWithBody::Function(function) => {
let function = function.id.lookup(db.upcast()).id;
item_tree_source_maps = function.item_tree_with_source_map(db.upcast()).1;
item_tree_source_maps.function(function.value).item()
}
DefWithBody::Static(statik) => {
let statik = statik.id.lookup(db.upcast()).id;
item_tree_source_maps = statik.item_tree_with_source_map(db.upcast()).1;
item_tree_source_maps.statik(statik.value)
}
DefWithBody::Const(konst) => {
let konst = konst.id.lookup(db.upcast()).id;
item_tree_source_maps = konst.item_tree_with_source_map(db.upcast()).1;
item_tree_source_maps.konst(konst.value)
}
DefWithBody::Variant(_) | DefWithBody::InTypeConst(_) => &TypesSourceMap::EMPTY,
};
for (_, def_map) in body.blocks(db.upcast()) {
Module { id: def_map.module_id(DefMap::ROOT) }.diagnostics(db, acc, style_lints);
@ -1861,7 +1953,13 @@ impl DefWithBody {
let infer = db.infer(self.into());
for d in &infer.diagnostics {
acc.extend(AnyDiagnostic::inference_diagnostic(db, self.into(), d, &source_map));
acc.extend(AnyDiagnostic::inference_diagnostic(
db,
self.into(),
d,
outer_types_source_map,
&source_map,
));
}
for (pat_or_expr, mismatch) in infer.type_mismatches() {
@ -3325,12 +3423,22 @@ impl AssocItem {
) {
match self {
AssocItem::Function(func) => {
GenericDef::Function(func).diagnostics(db, acc);
DefWithBody::from(func).diagnostics(db, acc, style_lints);
}
AssocItem::Const(const_) => {
DefWithBody::from(const_).diagnostics(db, acc, style_lints);
}
AssocItem::TypeAlias(type_alias) => {
GenericDef::TypeAlias(type_alias).diagnostics(db, acc);
let tree_id = type_alias.id.lookup(db.upcast()).id;
let tree_source_maps = tree_id.item_tree_with_source_map(db.upcast()).1;
push_ty_diagnostics(
db,
acc,
db.type_for_type_alias_with_diagnostics(type_alias.id).1,
tree_source_maps.type_alias(tree_id.value).item(),
);
for diag in hir_ty::diagnostics::incorrect_case(db, type_alias.id.into()) {
acc.push(diag.into());
}
@ -3417,6 +3525,97 @@ impl GenericDef {
})
.collect()
}
fn id(self) -> GenericDefId {
match self {
GenericDef::Function(it) => it.id.into(),
GenericDef::Adt(it) => it.into(),
GenericDef::Trait(it) => it.id.into(),
GenericDef::TraitAlias(it) => it.id.into(),
GenericDef::TypeAlias(it) => it.id.into(),
GenericDef::Impl(it) => it.id.into(),
GenericDef::Const(it) => it.id.into(),
}
}
pub fn diagnostics(self, db: &dyn HirDatabase, acc: &mut Vec<AnyDiagnostic>) {
let def = self.id();
let item_tree_source_maps;
let (generics, generics_source_map) = db.generic_params_with_source_map(def);
if generics.is_empty() && generics.no_predicates() {
return;
}
let source_map = match &generics_source_map {
Some(it) => it,
None => match def {
GenericDefId::FunctionId(it) => {
let id = it.lookup(db.upcast()).id;
item_tree_source_maps = id.item_tree_with_source_map(db.upcast()).1;
item_tree_source_maps.function(id.value).generics()
}
GenericDefId::AdtId(AdtId::EnumId(it)) => {
let id = it.lookup(db.upcast()).id;
item_tree_source_maps = id.item_tree_with_source_map(db.upcast()).1;
item_tree_source_maps.enum_generic(id.value)
}
GenericDefId::AdtId(AdtId::StructId(it)) => {
let id = it.lookup(db.upcast()).id;
item_tree_source_maps = id.item_tree_with_source_map(db.upcast()).1;
item_tree_source_maps.strukt(id.value).generics()
}
GenericDefId::AdtId(AdtId::UnionId(it)) => {
let id = it.lookup(db.upcast()).id;
item_tree_source_maps = id.item_tree_with_source_map(db.upcast()).1;
item_tree_source_maps.union(id.value).generics()
}
GenericDefId::TraitId(it) => {
let id = it.lookup(db.upcast()).id;
item_tree_source_maps = id.item_tree_with_source_map(db.upcast()).1;
item_tree_source_maps.trait_generic(id.value)
}
GenericDefId::TraitAliasId(it) => {
let id = it.lookup(db.upcast()).id;
item_tree_source_maps = id.item_tree_with_source_map(db.upcast()).1;
item_tree_source_maps.trait_alias_generic(id.value)
}
GenericDefId::TypeAliasId(it) => {
let id = it.lookup(db.upcast()).id;
item_tree_source_maps = id.item_tree_with_source_map(db.upcast()).1;
item_tree_source_maps.type_alias(id.value).generics()
}
GenericDefId::ImplId(it) => {
let id = it.lookup(db.upcast()).id;
item_tree_source_maps = id.item_tree_with_source_map(db.upcast()).1;
item_tree_source_maps.impl_(id.value).generics()
}
GenericDefId::ConstId(_) => return,
},
};
push_ty_diagnostics(db, acc, db.generic_defaults_with_diagnostics(def).1, source_map);
push_ty_diagnostics(
db,
acc,
db.generic_predicates_without_parent_with_diagnostics(def).1,
source_map,
);
for (param_id, param) in generics.iter_type_or_consts() {
if let TypeOrConstParamData::ConstParamData(_) = param {
push_ty_diagnostics(
db,
acc,
db.const_param_ty_with_diagnostics(ConstParamId::from_unchecked(
TypeOrConstParamId { parent: def, local_id: param_id },
))
.1,
source_map,
);
}
}
}
}
/// A single local definition.
@ -5800,3 +5999,19 @@ pub enum DocLinkDef {
Field(Field),
SelfType(Trait),
}
fn push_ty_diagnostics(
db: &dyn HirDatabase,
acc: &mut Vec<AnyDiagnostic>,
diagnostics: Option<ThinArc<(), TyLoweringDiagnostic>>,
source_map: &TypesSourceMap,
) {
if let Some(diagnostics) = diagnostics {
acc.extend(
diagnostics
.slice
.iter()
.filter_map(|diagnostic| AnyDiagnostic::ty_diagnostic(diagnostic, source_map, db)),
);
}
}

View file

@ -0,0 +1,442 @@
use either::Either;
use hir::GenericArgsProhibitedReason;
use ide_db::assists::Assist;
use ide_db::source_change::SourceChange;
use ide_db::text_edit::TextEdit;
use syntax::{ast, AstNode, TextRange};
use crate::{fix, Diagnostic, DiagnosticCode, DiagnosticsContext};
// Diagnostic: generic-args-prohibited
//
// This diagnostic is shown when generic arguments are provided for a type that does not accept
// generic arguments.
pub(crate) fn generic_args_prohibited(
ctx: &DiagnosticsContext<'_>,
d: &hir::GenericArgsProhibited,
) -> Diagnostic {
Diagnostic::new_with_syntax_node_ptr(
ctx,
DiagnosticCode::RustcHardError("E0109"),
describe_reason(d.reason),
d.args.map(Into::into),
)
.with_fixes(fixes(ctx, d))
}
fn describe_reason(reason: GenericArgsProhibitedReason) -> String {
let kind = match reason {
GenericArgsProhibitedReason::Module => "modules",
GenericArgsProhibitedReason::TyParam => "type parameters",
GenericArgsProhibitedReason::SelfTy => "`Self`",
GenericArgsProhibitedReason::PrimitiveTy => "builtin types",
GenericArgsProhibitedReason::EnumVariant => {
return "you can specify generic arguments on either the enum or the variant, but not both"
.to_owned();
}
};
format!("generic arguments are not allowed on {kind}")
}
fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::GenericArgsProhibited) -> Option<Vec<Assist>> {
let file_id = d.args.file_id.file_id()?;
let syntax = d.args.to_node(ctx.sema.db);
let range = match &syntax {
Either::Left(_) => syntax.syntax().text_range(),
Either::Right(param_list) => {
let path_segment = ast::PathSegment::cast(param_list.syntax().parent()?)?;
let start = if let Some(coloncolon) = path_segment.coloncolon_token() {
coloncolon.text_range().start()
} else {
param_list.syntax().text_range().start()
};
let end = if let Some(ret_type) = path_segment.ret_type() {
ret_type.syntax().text_range().end()
} else {
param_list.syntax().text_range().end()
};
TextRange::new(start, end)
}
};
Some(vec![fix(
"remove_generic_args",
"Remove these generics",
SourceChange::from_text_edit(file_id, TextEdit::delete(range)),
syntax.syntax().text_range(),
)])
}
#[cfg(test)]
mod tests {
// This diagnostic was the first to be emitted in ty lowering, so the tests here also test
// diagnostics in ty lowering in general (which is why there are so many of them).
use crate::tests::{check_diagnostics, check_fix};
#[test]
fn primitives() {
check_diagnostics(
r#"
//- /core.rs crate:core library
#![rustc_coherence_is_core]
impl str {
pub fn trim() {}
}
//- /lib.rs crate:foo deps:core
fn bar<T>() {}
fn foo() {
let _: (bool<()>, ());
// ^^^^ 💡 error: generic arguments are not allowed on builtin types
let _ = <str<'_>>::trim;
// ^^^^ 💡 error: generic arguments are not allowed on builtin types
bar::<u32<{ const { 1 + 1 } }>>();
// ^^^^^^^^^^^^^^^^^^^^^ 💡 error: generic arguments are not allowed on builtin types
}
"#,
);
}
#[test]
fn modules() {
check_diagnostics(
r#"
pub mod foo {
pub mod bar {
pub struct Baz;
impl Baz {
pub fn qux() {}
}
}
}
fn foo() {
let _: foo::<'_>::bar::Baz;
// ^^^^^^ 💡 error: generic arguments are not allowed on modules
let _ = <foo::bar<()>::Baz>::qux;
// ^^^^ 💡 error: generic arguments are not allowed on modules
}
"#,
);
}
#[test]
fn type_parameters() {
check_diagnostics(
r#"
fn foo<T, U>() {
let _: T<'a>;
// ^^^^ 💡 error: generic arguments are not allowed on type parameters
let _: U::<{ 1 + 2 }>;
// ^^^^^^^^^^^^^ 💡 error: generic arguments are not allowed on type parameters
}
"#,
);
}
#[test]
fn fn_like_generic_args() {
check_diagnostics(
r#"
fn foo() {
let _: bool(bool, i32) -> ();
// ^^^^^^^^^^^ 💡 error: generic arguments are not allowed on builtin types
}
"#,
);
}
#[test]
fn fn_signature() {
check_diagnostics(
r#"
fn foo(
_a: bool<'_>,
// ^^^^ 💡 error: generic arguments are not allowed on builtin types
_b: i32::<i64>,
// ^^^^^^^ 💡 error: generic arguments are not allowed on builtin types
_c: &(&str<1>)
// ^^^ 💡 error: generic arguments are not allowed on builtin types
) -> ((), i32<bool>) {
// ^^^^^^ 💡 error: generic arguments are not allowed on builtin types
((), 0)
}
"#,
);
}
#[test]
fn const_static_type() {
check_diagnostics(
r#"
const A: i32<bool> = 0;
// ^^^^^^ 💡 error: generic arguments are not allowed on builtin types
static A: i32::<{ 1 + 3 }> = 0;
// ^^^^^^^^^^^^^ 💡 error: generic arguments are not allowed on builtin types
"#,
);
}
#[test]
fn fix() {
check_fix(
r#"
fn foo() {
let _: bool<'_, (), { 1 + 1 }>$0;
}"#,
r#"
fn foo() {
let _: bool;
}"#,
);
check_fix(
r#"
fn foo() {
let _: bool::$0<'_, (), { 1 + 1 }>;
}"#,
r#"
fn foo() {
let _: bool;
}"#,
);
check_fix(
r#"
fn foo() {
let _: bool(i$032);
}"#,
r#"
fn foo() {
let _: bool;
}"#,
);
check_fix(
r#"
fn foo() {
let _: bool$0(i32) -> i64;
}"#,
r#"
fn foo() {
let _: bool;
}"#,
);
check_fix(
r#"
fn foo() {
let _: bool::(i$032) -> i64;
}"#,
r#"
fn foo() {
let _: bool;
}"#,
);
check_fix(
r#"
fn foo() {
let _: bool::(i32)$0;
}"#,
r#"
fn foo() {
let _: bool;
}"#,
);
}
#[test]
fn in_fields() {
check_diagnostics(
r#"
struct A(bool<i32>);
// ^^^^^ 💡 error: generic arguments are not allowed on builtin types
struct B { v: bool<(), 1> }
// ^^^^^^^ 💡 error: generic arguments are not allowed on builtin types
union C {
a: bool<i32>,
// ^^^^^ 💡 error: generic arguments are not allowed on builtin types
b: i32<bool>,
// ^^^^^^ 💡 error: generic arguments are not allowed on builtin types
}
enum D {
A(bool<i32>),
// ^^^^^ 💡 error: generic arguments are not allowed on builtin types
B { v: i32<bool> },
// ^^^^^^ 💡 error: generic arguments are not allowed on builtin types
}
"#,
);
}
#[test]
fn in_generics() {
check_diagnostics(
r#"
mod foo {
pub trait Trait {}
}
struct A<A: foo::<()>::Trait>(A)
// ^^^^^^ 💡 error: generic arguments are not allowed on modules
where bool<i32>: foo::Trait;
// ^^^^^ 💡 error: generic arguments are not allowed on builtin types
union B<A: foo::<()>::Trait>
// ^^^^^^ 💡 error: generic arguments are not allowed on modules
where bool<i32>: foo::Trait
// ^^^^^ 💡 error: generic arguments are not allowed on builtin types
{ a: A }
enum C<A: foo::<()>::Trait>
// ^^^^^^ 💡 error: generic arguments are not allowed on modules
where bool<i32>: foo::Trait
// ^^^^^ 💡 error: generic arguments are not allowed on builtin types
{}
fn f<A: foo::<()>::Trait>()
// ^^^^^^ 💡 error: generic arguments are not allowed on modules
where bool<i32>: foo::Trait
// ^^^^^ 💡 error: generic arguments are not allowed on builtin types
{}
type D<A: foo::<()>::Trait> = A
// ^^^^^^ 💡 error: generic arguments are not allowed on modules
where bool<i32>: foo::Trait;
// ^^^^^ 💡 error: generic arguments are not allowed on builtin types
trait E<A: foo::<()>::Trait>
// ^^^^^^ 💡 error: generic arguments are not allowed on modules
where bool<i32>: foo::Trait
// ^^^^^ 💡 error: generic arguments are not allowed on builtin types
{
fn f<B: foo::<()>::Trait>()
// ^^^^^^ 💡 error: generic arguments are not allowed on modules
where bool<i32>: foo::Trait
// ^^^^^ 💡 error: generic arguments are not allowed on builtin types
{}
type D<B: foo::<()>::Trait> = A
// ^^^^^^ 💡 error: generic arguments are not allowed on modules
where bool<i32>: foo::Trait;
// ^^^^^ 💡 error: generic arguments are not allowed on builtin types
}
impl<A: foo::<()>::Trait> E for ()
// ^^^^^^ 💡 error: generic arguments are not allowed on modules
where bool<i32>: foo::Trait
// ^^^^^ 💡 error: generic arguments are not allowed on builtin types
{
fn f<B: foo::<()>::Trait>()
// ^^^^^^ 💡 error: generic arguments are not allowed on modules
where bool<i32>: foo::Trait
// ^^^^^ 💡 error: generic arguments are not allowed on builtin types
{}
type D<B: foo::<()>::Trait> = A
// ^^^^^^ 💡 error: generic arguments are not allowed on modules
where bool<i32>: foo::Trait;
// ^^^^^ 💡 error: generic arguments are not allowed on builtin types
}
"#,
);
}
#[test]
fn assoc_items() {
check_diagnostics(
r#"
struct Foo;
trait Trait {
fn f() -> bool<i32> { true }
// ^^^^^ 💡 error: generic arguments are not allowed on builtin types
type T = i32<bool>;
// ^^^^^^ 💡 error: generic arguments are not allowed on builtin types
}
impl Trait for Foo {
fn f() -> bool<i32> { true }
// ^^^^^ 💡 error: generic arguments are not allowed on builtin types
type T = i32<bool>;
// ^^^^^^ 💡 error: generic arguments are not allowed on builtin types
}
impl Foo {
fn f() -> bool<i32> { true }
// ^^^^^ 💡 error: generic arguments are not allowed on builtin types
type T = i32<bool>;
// ^^^^^^ 💡 error: generic arguments are not allowed on builtin types
}
"#,
);
}
#[test]
fn const_param_ty() {
check_diagnostics(
r#"
fn foo<
const A: bool<i32>,
// ^^^^^ 💡 error: generic arguments are not allowed on builtin types
B,
C,
const D: bool<i32>,
// ^^^^^ 💡 error: generic arguments are not allowed on builtin types
const E: bool<i32>,
// ^^^^^ 💡 error: generic arguments are not allowed on builtin types
>() {}
"#,
);
}
#[test]
fn generic_defaults() {
check_diagnostics(
r#"
struct Foo<A = bool<i32>>(A);
// ^^^^^ 💡 error: generic arguments are not allowed on builtin types
"#,
);
}
#[test]
fn impl_self_ty() {
check_diagnostics(
r#"
struct Foo<A>(A);
trait Trait {}
impl Foo<bool<i32>> {}
// ^^^^^ 💡 error: generic arguments are not allowed on builtin types
impl Trait for Foo<bool<i32>> {}
// ^^^^^ 💡 error: generic arguments are not allowed on builtin types
"#,
);
}
#[test]
fn impl_trait() {
check_diagnostics(
r#"
mod foo {
pub trait Trait {}
}
impl foo::<()>::Trait for () {}
// ^^^^^^ 💡 error: generic arguments are not allowed on modules
"#,
);
}
#[test]
fn type_alias() {
check_diagnostics(
r#"
pub trait Trait {
type Assoc;
}
type T = bool<i32>;
// ^^^^^ 💡 error: generic arguments are not allowed on builtin types
impl Trait for () {
type Assoc = i32<bool>;
// ^^^^^^ 💡 error: generic arguments are not allowed on builtin types
}
"#,
);
}
}

View file

@ -167,9 +167,9 @@ fn assoc_func_fix(ctx: &DiagnosticsContext<'_>, d: &hir::UnresolvedMethodCall) -
}
let method_name = call.name_ref()?;
let assoc_func_call = format!("{receiver_type_adt_name}::{method_name}()");
let assoc_func_path = format!("{receiver_type_adt_name}::{method_name}");
let assoc_func_call = make::expr_path(make::path_from_text(&assoc_func_call));
let assoc_func_path = make::expr_path(make::path_from_text(&assoc_func_path));
let args: Vec<_> = if need_to_take_receiver_as_first_arg {
std::iter::once(receiver).chain(call.arg_list()?.args()).collect()
@ -178,7 +178,7 @@ fn assoc_func_fix(ctx: &DiagnosticsContext<'_>, d: &hir::UnresolvedMethodCall) -
};
let args = make::arg_list(args);
let assoc_func_call_expr_string = make::expr_call(assoc_func_call, args).to_string();
let assoc_func_call_expr_string = make::expr_call(assoc_func_path, args).to_string();
let file_id = ctx.sema.original_range_opt(call.receiver()?.syntax())?.file_id;

View file

@ -27,6 +27,7 @@ mod handlers {
pub(crate) mod await_outside_of_async;
pub(crate) mod break_outside_of_loop;
pub(crate) mod expected_function;
pub(crate) mod generic_args_prohibited;
pub(crate) mod inactive_code;
pub(crate) mod incoherent_impl;
pub(crate) mod incorrect_case;
@ -468,6 +469,7 @@ pub fn semantic_diagnostics(
Some(it) => it,
None => continue,
},
AnyDiagnostic::GenericArgsProhibited(d) => handlers::generic_args_prohibited::generic_args_prohibited(&ctx, &d)
};
res.push(d)
}

View file

@ -13,8 +13,9 @@ use hir::{
ModuleDef, Name,
};
use hir_def::{
body::{BodySourceMap, SyntheticSyntax},
body::BodySourceMap,
hir::{ExprId, PatId},
SyntheticSyntax,
};
use hir_ty::{Interner, Substitution, TyExt, TypeFlags};
use ide::{

View file

@ -402,7 +402,7 @@ pub fn join_paths(paths: impl IntoIterator<Item = ast::Path>) -> ast::Path {
// FIXME: should not be pub
pub fn path_from_text(text: &str) -> ast::Path {
ast_from_text(&format!("fn main() {{ let test = {text}; }}"))
ast_from_text(&format!("fn main() {{ let test: {text}; }}"))
}
pub fn use_tree_glob() -> ast::UseTree {