mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-09-28 12:54:58 +00:00
Auto merge of #14232 - HKalbasi:mir, r=Veykril
MIR episode 2 This PR adds: 1. `need-mut` and `unused-mut` diagnostics 2. `View mir` command which shows MIR for the body under cursor, useful for debugging 3. MIR lowering for or-patterns and for-loops
This commit is contained in:
commit
44ff3c407a
57 changed files with 3164 additions and 760 deletions
|
@ -10,7 +10,7 @@ use hir_def::path::ModPath;
|
|||
use hir_expand::{name::Name, HirFileId, InFile};
|
||||
use syntax::{ast, AstPtr, SyntaxNodePtr, TextRange};
|
||||
|
||||
use crate::{AssocItem, Field, MacroKind, Type};
|
||||
use crate::{AssocItem, Field, Local, MacroKind, Type};
|
||||
|
||||
macro_rules! diagnostics {
|
||||
($($diag:ident,)*) => {
|
||||
|
@ -41,6 +41,7 @@ diagnostics![
|
|||
MissingFields,
|
||||
MissingMatchArms,
|
||||
MissingUnsafe,
|
||||
NeedMut,
|
||||
NoSuchField,
|
||||
PrivateAssocItem,
|
||||
PrivateField,
|
||||
|
@ -54,6 +55,7 @@ diagnostics![
|
|||
UnresolvedMethodCall,
|
||||
UnresolvedModule,
|
||||
UnresolvedProcMacro,
|
||||
UnusedMut,
|
||||
];
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -209,4 +211,15 @@ pub struct TypeMismatch {
|
|||
pub actual: Type,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct NeedMut {
|
||||
pub local: Local,
|
||||
pub span: InFile<SyntaxNodePtr>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct UnusedMut {
|
||||
pub local: Local,
|
||||
}
|
||||
|
||||
pub use hir_ty::diagnostics::IncorrectCase;
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
//! are splitting the hir.
|
||||
|
||||
use hir_def::{
|
||||
expr::{LabelId, PatId},
|
||||
expr::{BindingId, LabelId},
|
||||
AdtId, AssocItemId, DefWithBodyId, EnumVariantId, FieldId, GenericDefId, GenericParamId,
|
||||
ModuleDefId, VariantId,
|
||||
};
|
||||
|
@ -251,9 +251,9 @@ impl From<AssocItem> for GenericDefId {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<(DefWithBodyId, PatId)> for Local {
|
||||
fn from((parent, pat_id): (DefWithBodyId, PatId)) -> Self {
|
||||
Local { parent, pat_id }
|
||||
impl From<(DefWithBodyId, BindingId)> for Local {
|
||||
fn from((parent, binding_id): (DefWithBodyId, BindingId)) -> Self {
|
||||
Local { parent, binding_id }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -10,8 +10,9 @@ use hir_expand::InFile;
|
|||
use syntax::ast;
|
||||
|
||||
use crate::{
|
||||
db::HirDatabase, Adt, Const, Enum, Field, FieldSource, Function, Impl, LifetimeParam, Macro,
|
||||
Module, Static, Struct, Trait, TraitAlias, TypeAlias, TypeOrConstParam, Union, Variant,
|
||||
db::HirDatabase, Adt, Const, Enum, Field, FieldSource, Function, Impl, LifetimeParam,
|
||||
LocalSource, Macro, Module, Static, Struct, Trait, TraitAlias, TypeAlias, TypeOrConstParam,
|
||||
Union, Variant,
|
||||
};
|
||||
|
||||
pub trait HasSource {
|
||||
|
@ -178,3 +179,11 @@ impl HasSource for LifetimeParam {
|
|||
Some(child_source.map(|it| it[self.id.local_id].clone()))
|
||||
}
|
||||
}
|
||||
|
||||
impl HasSource for LocalSource {
|
||||
type Ast = Either<ast::IdentPat, ast::SelfParam>;
|
||||
|
||||
fn source(self, _: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
|
||||
Some(self.source)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,7 +41,7 @@ use either::Either;
|
|||
use hir_def::{
|
||||
adt::VariantData,
|
||||
body::{BodyDiagnostic, SyntheticSyntax},
|
||||
expr::{BindingAnnotation, ExprOrPatId, LabelId, Pat, PatId},
|
||||
expr::{BindingAnnotation, BindingId, ExprOrPatId, LabelId, Pat},
|
||||
generics::{LifetimeParamData, TypeOrConstParamData, TypeParamProvenance},
|
||||
item_tree::ItemTreeNode,
|
||||
lang_item::{LangItem, LangItemTarget},
|
||||
|
@ -63,7 +63,7 @@ use hir_ty::{
|
|||
display::HexifiedConst,
|
||||
layout::layout_of_ty,
|
||||
method_resolution::{self, TyFingerprint},
|
||||
mir::interpret_mir,
|
||||
mir::{self, interpret_mir},
|
||||
primitive::UintTy,
|
||||
traits::FnTrait,
|
||||
AliasTy, CallableDefId, CallableSig, Canonical, CanonicalVarKinds, Cast, ClosureId,
|
||||
|
@ -77,7 +77,7 @@ use rustc_hash::FxHashSet;
|
|||
use stdx::{impl_from, never};
|
||||
use syntax::{
|
||||
ast::{self, HasAttrs as _, HasDocComments, HasName},
|
||||
AstNode, AstPtr, SmolStr, SyntaxNodePtr, TextRange, T,
|
||||
AstNode, AstPtr, SmolStr, SyntaxNode, SyntaxNodePtr, TextRange, T,
|
||||
};
|
||||
|
||||
use crate::db::{DefDatabase, HirDatabase};
|
||||
|
@ -87,10 +87,10 @@ pub use crate::{
|
|||
diagnostics::{
|
||||
AnyDiagnostic, BreakOutsideOfLoop, ExpectedFunction, InactiveCode, IncorrectCase,
|
||||
InvalidDeriveTarget, MacroError, MalformedDerive, MismatchedArgCount, MissingFields,
|
||||
MissingMatchArms, MissingUnsafe, NoSuchField, PrivateAssocItem, PrivateField,
|
||||
MissingMatchArms, MissingUnsafe, NeedMut, NoSuchField, PrivateAssocItem, PrivateField,
|
||||
ReplaceFilterMapNextWithFindMap, TypeMismatch, UnimplementedBuiltinMacro,
|
||||
UnresolvedExternCrate, UnresolvedField, UnresolvedImport, UnresolvedMacroCall,
|
||||
UnresolvedMethodCall, UnresolvedModule, UnresolvedProcMacro,
|
||||
UnresolvedMethodCall, UnresolvedModule, UnresolvedProcMacro, UnusedMut,
|
||||
},
|
||||
has_source::HasSource,
|
||||
semantics::{PathResolution, Semantics, SemanticsScope, TypeInfo, VisibleTraits},
|
||||
|
@ -1327,6 +1327,15 @@ impl DefWithBody {
|
|||
body.pretty_print(db.upcast(), self.id())
|
||||
}
|
||||
|
||||
/// A textual representation of the MIR of this def's body for debugging purposes.
|
||||
pub fn debug_mir(self, db: &dyn HirDatabase) -> String {
|
||||
let body = db.mir_body(self.id());
|
||||
match body {
|
||||
Ok(body) => body.pretty_print(db),
|
||||
Err(e) => format!("error:\n{e:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn diagnostics(self, db: &dyn HirDatabase, acc: &mut Vec<AnyDiagnostic>) {
|
||||
let krate = self.module(db).id.krate();
|
||||
|
||||
|
@ -1500,6 +1509,41 @@ impl DefWithBody {
|
|||
}
|
||||
}
|
||||
|
||||
let hir_body = db.body(self.into());
|
||||
|
||||
if let Ok(borrowck_result) = db.borrowck(self.into()) {
|
||||
let mir_body = &borrowck_result.mir_body;
|
||||
let mol = &borrowck_result.mutability_of_locals;
|
||||
for (binding_id, _) in hir_body.bindings.iter() {
|
||||
let need_mut = &mol[mir_body.binding_locals[binding_id]];
|
||||
let local = Local { parent: self.into(), binding_id };
|
||||
match (need_mut, local.is_mut(db)) {
|
||||
(mir::MutabilityReason::Mut { .. }, true)
|
||||
| (mir::MutabilityReason::Not, false) => (),
|
||||
(mir::MutabilityReason::Mut { spans }, false) => {
|
||||
for span in spans {
|
||||
let span: InFile<SyntaxNodePtr> = match span {
|
||||
mir::MirSpan::ExprId(e) => match source_map.expr_syntax(*e) {
|
||||
Ok(s) => s.map(|x| x.into()),
|
||||
Err(_) => continue,
|
||||
},
|
||||
mir::MirSpan::PatId(p) => match source_map.pat_syntax(*p) {
|
||||
Ok(s) => s.map(|x| match x {
|
||||
Either::Left(e) => e.into(),
|
||||
Either::Right(e) => e.into(),
|
||||
}),
|
||||
Err(_) => continue,
|
||||
},
|
||||
mir::MirSpan::Unknown => continue,
|
||||
};
|
||||
acc.push(NeedMut { local, span }.into());
|
||||
}
|
||||
}
|
||||
(mir::MutabilityReason::Not, true) => acc.push(UnusedMut { local }.into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for diagnostic in BodyValidationDiagnostic::collect(db, self.into()) {
|
||||
match diagnostic {
|
||||
BodyValidationDiagnostic::RecordMissingFields {
|
||||
|
@ -1786,8 +1830,8 @@ impl Param {
|
|||
let parent = DefWithBodyId::FunctionId(self.func.into());
|
||||
let body = db.body(parent);
|
||||
let pat_id = body.params[self.idx];
|
||||
if let Pat::Bind { .. } = &body[pat_id] {
|
||||
Some(Local { parent, pat_id: body.params[self.idx] })
|
||||
if let Pat::Bind { id, .. } = &body[pat_id] {
|
||||
Some(Local { parent, binding_id: *id })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
@ -2464,13 +2508,50 @@ impl GenericDef {
|
|||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
pub struct Local {
|
||||
pub(crate) parent: DefWithBodyId,
|
||||
pub(crate) pat_id: PatId,
|
||||
pub(crate) binding_id: BindingId,
|
||||
}
|
||||
|
||||
pub struct LocalSource {
|
||||
pub local: Local,
|
||||
pub source: InFile<Either<ast::IdentPat, ast::SelfParam>>,
|
||||
}
|
||||
|
||||
impl LocalSource {
|
||||
pub fn as_ident_pat(&self) -> Option<&ast::IdentPat> {
|
||||
match &self.source.value {
|
||||
Either::Left(x) => Some(x),
|
||||
Either::Right(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn into_ident_pat(self) -> Option<ast::IdentPat> {
|
||||
match self.source.value {
|
||||
Either::Left(x) => Some(x),
|
||||
Either::Right(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn original_file(&self, db: &dyn HirDatabase) -> FileId {
|
||||
self.source.file_id.original_file(db.upcast())
|
||||
}
|
||||
|
||||
pub fn name(&self) -> Option<ast::Name> {
|
||||
self.source.value.name()
|
||||
}
|
||||
|
||||
pub fn syntax(&self) -> &SyntaxNode {
|
||||
self.source.value.syntax()
|
||||
}
|
||||
|
||||
pub fn syntax_ptr(self) -> InFile<SyntaxNodePtr> {
|
||||
self.source.map(|x| SyntaxNodePtr::new(x.syntax()))
|
||||
}
|
||||
}
|
||||
|
||||
impl Local {
|
||||
pub fn is_param(self, db: &dyn HirDatabase) -> bool {
|
||||
let src = self.source(db);
|
||||
match src.value {
|
||||
let src = self.primary_source(db);
|
||||
match src.source.value {
|
||||
Either::Left(pat) => pat
|
||||
.syntax()
|
||||
.ancestors()
|
||||
|
@ -2490,13 +2571,7 @@ impl Local {
|
|||
|
||||
pub fn name(self, db: &dyn HirDatabase) -> Name {
|
||||
let body = db.body(self.parent);
|
||||
match &body[self.pat_id] {
|
||||
Pat::Bind { name, .. } => name.clone(),
|
||||
_ => {
|
||||
stdx::never!("hir::Local is missing a name!");
|
||||
Name::missing()
|
||||
}
|
||||
}
|
||||
body[self.binding_id].name.clone()
|
||||
}
|
||||
|
||||
pub fn is_self(self, db: &dyn HirDatabase) -> bool {
|
||||
|
@ -2505,15 +2580,12 @@ impl Local {
|
|||
|
||||
pub fn is_mut(self, db: &dyn HirDatabase) -> bool {
|
||||
let body = db.body(self.parent);
|
||||
matches!(&body[self.pat_id], Pat::Bind { mode: BindingAnnotation::Mutable, .. })
|
||||
body[self.binding_id].mode == BindingAnnotation::Mutable
|
||||
}
|
||||
|
||||
pub fn is_ref(self, db: &dyn HirDatabase) -> bool {
|
||||
let body = db.body(self.parent);
|
||||
matches!(
|
||||
&body[self.pat_id],
|
||||
Pat::Bind { mode: BindingAnnotation::Ref | BindingAnnotation::RefMut, .. }
|
||||
)
|
||||
matches!(body[self.binding_id].mode, BindingAnnotation::Ref | BindingAnnotation::RefMut)
|
||||
}
|
||||
|
||||
pub fn parent(self, _db: &dyn HirDatabase) -> DefWithBody {
|
||||
|
@ -2527,34 +2599,33 @@ impl Local {
|
|||
pub fn ty(self, db: &dyn HirDatabase) -> Type {
|
||||
let def = self.parent;
|
||||
let infer = db.infer(def);
|
||||
let ty = infer[self.pat_id].clone();
|
||||
let ty = infer[self.binding_id].clone();
|
||||
Type::new(db, def, ty)
|
||||
}
|
||||
|
||||
pub fn associated_locals(self, db: &dyn HirDatabase) -> Box<[Local]> {
|
||||
let body = db.body(self.parent);
|
||||
body.ident_patterns_for(&self.pat_id)
|
||||
/// All definitions for this local. Example: `let (a$0, _) | (_, a$0) = x;`
|
||||
pub fn sources(self, db: &dyn HirDatabase) -> Vec<LocalSource> {
|
||||
let (body, source_map) = db.body_with_source_map(self.parent);
|
||||
body[self.binding_id]
|
||||
.definitions
|
||||
.iter()
|
||||
.map(|&pat_id| Local { parent: self.parent, pat_id })
|
||||
.map(|&definition| {
|
||||
let src = source_map.pat_syntax(definition).unwrap(); // Hmm...
|
||||
let root = src.file_syntax(db.upcast());
|
||||
src.map(|ast| match ast {
|
||||
// Suspicious unwrap
|
||||
Either::Left(it) => Either::Left(it.cast().unwrap().to_node(&root)),
|
||||
Either::Right(it) => Either::Right(it.to_node(&root)),
|
||||
})
|
||||
})
|
||||
.map(|source| LocalSource { local: self, source })
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// If this local is part of a multi-local, retrieve the representative local.
|
||||
/// That is the local that references are being resolved to.
|
||||
pub fn representative(self, db: &dyn HirDatabase) -> Local {
|
||||
let body = db.body(self.parent);
|
||||
Local { pat_id: body.pattern_representative(self.pat_id), ..self }
|
||||
}
|
||||
|
||||
pub fn source(self, db: &dyn HirDatabase) -> InFile<Either<ast::IdentPat, ast::SelfParam>> {
|
||||
let (_body, source_map) = db.body_with_source_map(self.parent);
|
||||
let src = source_map.pat_syntax(self.pat_id).unwrap(); // Hmm...
|
||||
let root = src.file_syntax(db.upcast());
|
||||
src.map(|ast| match ast {
|
||||
// Suspicious unwrap
|
||||
Either::Left(it) => Either::Left(it.cast().unwrap().to_node(&root)),
|
||||
Either::Right(it) => Either::Right(it.to_node(&root)),
|
||||
})
|
||||
/// The leftmost definition for this local. Example: `let (a$0, _) | (_, a) = x;`
|
||||
pub fn primary_source(self, db: &dyn HirDatabase) -> LocalSource {
|
||||
let all_sources = self.sources(db);
|
||||
all_sources.into_iter().next().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1103,7 +1103,10 @@ impl<'db> SemanticsImpl<'db> {
|
|||
let kind = match adjust.kind {
|
||||
hir_ty::Adjust::NeverToAny => Adjust::NeverToAny,
|
||||
hir_ty::Adjust::Deref(Some(hir_ty::OverloadedDeref(m))) => {
|
||||
Adjust::Deref(Some(OverloadedDeref(mutability(m))))
|
||||
// FIXME: Should we handle unknown mutability better?
|
||||
Adjust::Deref(Some(OverloadedDeref(
|
||||
m.map(mutability).unwrap_or(Mutability::Shared),
|
||||
)))
|
||||
}
|
||||
hir_ty::Adjust::Deref(None) => Adjust::Deref(None),
|
||||
hir_ty::Adjust::Borrow(hir_ty::AutoBorrow::RawPtr(m)) => {
|
||||
|
@ -1654,8 +1657,8 @@ impl<'a> SemanticsScope<'a> {
|
|||
resolver::ScopeDef::ImplSelfType(it) => ScopeDef::ImplSelfType(it.into()),
|
||||
resolver::ScopeDef::AdtSelfType(it) => ScopeDef::AdtSelfType(it.into()),
|
||||
resolver::ScopeDef::GenericParam(id) => ScopeDef::GenericParam(id.into()),
|
||||
resolver::ScopeDef::Local(pat_id) => match self.resolver.body_owner() {
|
||||
Some(parent) => ScopeDef::Local(Local { parent, pat_id }),
|
||||
resolver::ScopeDef::Local(binding_id) => match self.resolver.body_owner() {
|
||||
Some(parent) => ScopeDef::Local(Local { parent, binding_id }),
|
||||
None => continue,
|
||||
},
|
||||
resolver::ScopeDef::Label(label_id) => match self.resolver.body_owner() {
|
||||
|
|
|
@ -89,7 +89,7 @@ use base_db::FileId;
|
|||
use hir_def::{
|
||||
child_by_source::ChildBySource,
|
||||
dyn_map::DynMap,
|
||||
expr::{LabelId, PatId},
|
||||
expr::{BindingId, LabelId},
|
||||
keys::{self, Key},
|
||||
AdtId, ConstId, ConstParamId, DefWithBodyId, EnumId, EnumVariantId, FieldId, FunctionId,
|
||||
GenericDefId, GenericParamId, ImplId, LifetimeParamId, MacroId, ModuleId, StaticId, StructId,
|
||||
|
@ -98,7 +98,7 @@ use hir_def::{
|
|||
use hir_expand::{attrs::AttrId, name::AsName, HirFileId, MacroCallId};
|
||||
use rustc_hash::FxHashMap;
|
||||
use smallvec::SmallVec;
|
||||
use stdx::impl_from;
|
||||
use stdx::{impl_from, never};
|
||||
use syntax::{
|
||||
ast::{self, HasName},
|
||||
AstNode, SyntaxNode,
|
||||
|
@ -216,14 +216,14 @@ impl SourceToDefCtx<'_, '_> {
|
|||
pub(super) fn bind_pat_to_def(
|
||||
&mut self,
|
||||
src: InFile<ast::IdentPat>,
|
||||
) -> Option<(DefWithBodyId, PatId)> {
|
||||
) -> Option<(DefWithBodyId, BindingId)> {
|
||||
let container = self.find_pat_or_label_container(src.syntax())?;
|
||||
let (body, source_map) = self.db.body_with_source_map(container);
|
||||
let src = src.map(ast::Pat::from);
|
||||
let pat_id = source_map.node_pat(src.as_ref())?;
|
||||
// the pattern could resolve to a constant, verify that that is not the case
|
||||
if let crate::Pat::Bind { .. } = body[pat_id] {
|
||||
Some((container, pat_id))
|
||||
if let crate::Pat::Bind { id, .. } = body[pat_id] {
|
||||
Some((container, id))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
@ -231,11 +231,16 @@ impl SourceToDefCtx<'_, '_> {
|
|||
pub(super) fn self_param_to_def(
|
||||
&mut self,
|
||||
src: InFile<ast::SelfParam>,
|
||||
) -> Option<(DefWithBodyId, PatId)> {
|
||||
) -> Option<(DefWithBodyId, BindingId)> {
|
||||
let container = self.find_pat_or_label_container(src.syntax())?;
|
||||
let (_body, source_map) = self.db.body_with_source_map(container);
|
||||
let (body, source_map) = self.db.body_with_source_map(container);
|
||||
let pat_id = source_map.node_self_param(src.as_ref())?;
|
||||
Some((container, pat_id))
|
||||
if let crate::Pat::Bind { id, .. } = body[pat_id] {
|
||||
Some((container, id))
|
||||
} else {
|
||||
never!();
|
||||
None
|
||||
}
|
||||
}
|
||||
pub(super) fn label_to_def(
|
||||
&mut self,
|
||||
|
|
|
@ -422,8 +422,8 @@ impl SourceAnalyzer {
|
|||
// Shorthand syntax, resolve to the local
|
||||
let path = ModPath::from_segments(PathKind::Plain, once(local_name.clone()));
|
||||
match self.resolver.resolve_path_in_value_ns_fully(db.upcast(), &path) {
|
||||
Some(ValueNs::LocalBinding(pat_id)) => {
|
||||
Some(Local { pat_id, parent: self.resolver.body_owner()? })
|
||||
Some(ValueNs::LocalBinding(binding_id)) => {
|
||||
Some(Local { binding_id, parent: self.resolver.body_owner()? })
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
|
@ -1018,8 +1018,8 @@ fn resolve_hir_path_(
|
|||
let values = || {
|
||||
resolver.resolve_path_in_value_ns_fully(db.upcast(), path.mod_path()).and_then(|val| {
|
||||
let res = match val {
|
||||
ValueNs::LocalBinding(pat_id) => {
|
||||
let var = Local { parent: body_owner?, pat_id };
|
||||
ValueNs::LocalBinding(binding_id) => {
|
||||
let var = Local { parent: body_owner?, binding_id };
|
||||
PathResolution::Local(var)
|
||||
}
|
||||
ValueNs::FunctionId(it) => PathResolution::Def(Function::from(it).into()),
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue