mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-09-30 05:45:12 +00:00
Complete diagnostics in ty lowering groundwork
Implement diagnostics in all places left: generics (predicates, defaults, const params' types), fields, and type aliases. Unfortunately this results in a 20mb addition in `analysis-stats .` due to many type methods returning an addition diagnostics result now (even if it's `None` in most cases). I'm not sure if this can be improved. An alternative strategy that can prevent the memory usage growth is to never produce diagnostics in hir-ty methods. Instead, lower all types in the hir crate when computing diagnostics from scratch (with diagnostics this time). But this has two serious disadvantages: 1. This can cause code duplication (although it can probably be not that bad, it will still mean a lot more code). 2. I believe we eventually want to compute diagnostics for the *entire* workspace (either on-type or on-save or something alike), so users can know when they have diagnostics even in inactive files. Choosing this approach will mean we lose all precomputed salsa queries. For one file this is fine, for the whole workspace this will be very slow.
This commit is contained in:
parent
5f25ae3d1b
commit
21ad3b5b87
8 changed files with 621 additions and 103 deletions
|
@ -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);
|
||||
}
|
||||
|
@ -3350,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());
|
||||
}
|
||||
|
@ -3442,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.
|
||||
|
@ -5825,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)),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue