mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-10-03 07:04:49 +00:00
Implement semitransparent hygiene
Or macro_rules hygiene, or mixed site hygiene. In other words, hygiene for variables and labels but not items. The realization that made me implement this was that while "full" hygiene (aka. def site hygiene) is really hard for us to implement, and will likely involve intrusive changes and performance losses, since every `Name` will have to carry hygiene, mixed site hygiene is very local: it applies only to bodies, and we very well can save it in a side map with minor losses. This fixes one diagnostic in r-a that was about `izip!()` using hygiene (yay!) but it introduces a huge number of others, because of #18262. Up until now this issue wasn't a major problem because it only affected few cases, but with hygiene identifiers referred by macros like that are not resolved at all. The next commit will fix that.
This commit is contained in:
parent
c286786888
commit
8adcbdcc49
23 changed files with 394 additions and 124 deletions
|
@ -3,7 +3,7 @@
|
|||
use base_db::{ra_salsa::Cycle, CrateId};
|
||||
use chalk_ir::{cast::Cast, BoundVar, DebruijnIndex};
|
||||
use hir_def::{
|
||||
body::Body,
|
||||
body::{Body, HygieneId},
|
||||
hir::{Expr, ExprId},
|
||||
path::Path,
|
||||
resolver::{Resolver, ValueNs},
|
||||
|
@ -80,7 +80,7 @@ pub(crate) fn path_to_const<'g>(
|
|||
debruijn: DebruijnIndex,
|
||||
expected_ty: Ty,
|
||||
) -> Option<Const> {
|
||||
match resolver.resolve_path_in_value_ns_fully(db.upcast(), path) {
|
||||
match resolver.resolve_path_in_value_ns_fully(db.upcast(), path, HygieneId::ROOT) {
|
||||
Some(ValueNs::GenericParam(p)) => {
|
||||
let ty = db.const_param_ty(p);
|
||||
let value = match mode {
|
||||
|
|
|
@ -289,10 +289,12 @@ impl ExprValidator {
|
|||
match &self.body[scrutinee_expr] {
|
||||
Expr::UnaryOp { op: UnaryOp::Deref, .. } => false,
|
||||
Expr::Path(path) => {
|
||||
let value_or_partial = self
|
||||
.owner
|
||||
.resolver(db.upcast())
|
||||
.resolve_path_in_value_ns_fully(db.upcast(), path);
|
||||
let value_or_partial =
|
||||
self.owner.resolver(db.upcast()).resolve_path_in_value_ns_fully(
|
||||
db.upcast(),
|
||||
path,
|
||||
self.body.expr_path_hygiene(scrutinee_expr),
|
||||
);
|
||||
value_or_partial.map_or(true, |v| !matches!(v, ValueNs::StaticId(_)))
|
||||
}
|
||||
Expr::Field { expr, .. } => match self.infer.type_of_expr[*expr].kind(Interner) {
|
||||
|
|
|
@ -77,7 +77,8 @@ fn walk_unsafe(
|
|||
) {
|
||||
let mut mark_unsafe_path = |path, node| {
|
||||
let g = resolver.update_to_inner_scope(db.upcast(), def, current);
|
||||
let value_or_partial = resolver.resolve_path_in_value_ns(db.upcast(), path);
|
||||
let hygiene = body.expr_or_pat_path_hygiene(node);
|
||||
let value_or_partial = resolver.resolve_path_in_value_ns(db.upcast(), path, hygiene);
|
||||
if let Some(ResolveValueResult::ValueNs(ValueNs::StaticId(id), _)) = value_or_partial {
|
||||
let static_data = db.static_data(id);
|
||||
if static_data.mutable || (static_data.is_extern && !static_data.has_safe_kw) {
|
||||
|
|
|
@ -33,7 +33,7 @@ use chalk_ir::{
|
|||
};
|
||||
use either::Either;
|
||||
use hir_def::{
|
||||
body::Body,
|
||||
body::{Body, HygieneId},
|
||||
builtin_type::{BuiltinInt, BuiltinType, BuiltinUint},
|
||||
data::{ConstData, StaticData},
|
||||
hir::{BindingAnnotation, BindingId, ExprId, ExprOrPatId, LabelId, PatId},
|
||||
|
@ -1398,7 +1398,7 @@ impl<'a> InferenceContext<'a> {
|
|||
};
|
||||
let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver, self.owner.into());
|
||||
let (resolution, unresolved) = if value_ns {
|
||||
match self.resolver.resolve_path_in_value_ns(self.db.upcast(), path) {
|
||||
match self.resolver.resolve_path_in_value_ns(self.db.upcast(), path, HygieneId::ROOT) {
|
||||
Some(ResolveValueResult::ValueNs(value, _)) => match value {
|
||||
ValueNs::EnumVariantId(var) => {
|
||||
let substs = ctx.substs_from_path(path, var.into(), true);
|
||||
|
|
|
@ -514,8 +514,11 @@ impl InferenceContext<'_> {
|
|||
if path.type_anchor().is_some() {
|
||||
return None;
|
||||
}
|
||||
let result = self.resolver.resolve_path_in_value_ns_fully(self.db.upcast(), path).and_then(
|
||||
|result| match result {
|
||||
let hygiene = self.body.expr_or_pat_path_hygiene(id);
|
||||
let result = self
|
||||
.resolver
|
||||
.resolve_path_in_value_ns_fully(self.db.upcast(), path, hygiene)
|
||||
.and_then(|result| match result {
|
||||
ValueNs::LocalBinding(binding) => {
|
||||
let mir_span = match id {
|
||||
ExprOrPatId::ExprId(id) => MirSpan::ExprId(id),
|
||||
|
@ -525,8 +528,7 @@ impl InferenceContext<'_> {
|
|||
Some(HirPlace { local: binding, projections: Vec::new() })
|
||||
}
|
||||
_ => None,
|
||||
},
|
||||
);
|
||||
});
|
||||
result
|
||||
}
|
||||
|
||||
|
|
|
@ -201,7 +201,11 @@ impl InferenceContext<'_> {
|
|||
Expr::Path(Path::Normal { type_anchor: Some(_), .. }) => false,
|
||||
Expr::Path(path) => self
|
||||
.resolver
|
||||
.resolve_path_in_value_ns_fully(self.db.upcast(), path)
|
||||
.resolve_path_in_value_ns_fully(
|
||||
self.db.upcast(),
|
||||
path,
|
||||
self.body.expr_path_hygiene(expr),
|
||||
)
|
||||
.map_or(true, |res| matches!(res, ValueNs::LocalBinding(_) | ValueNs::StaticId(_))),
|
||||
Expr::Underscore => true,
|
||||
Expr::UnaryOp { op: UnaryOp::Deref, .. } => true,
|
||||
|
|
|
@ -164,9 +164,10 @@ impl InferenceContext<'_> {
|
|||
let ty = self.table.normalize_associated_types_in(ty);
|
||||
self.resolve_ty_assoc_item(ty, last.name, id).map(|(it, substs)| (it, Some(substs)))?
|
||||
} else {
|
||||
let hygiene = self.body.expr_or_pat_path_hygiene(id);
|
||||
// FIXME: report error, unresolved first path segment
|
||||
let value_or_partial =
|
||||
self.resolver.resolve_path_in_value_ns(self.db.upcast(), path)?;
|
||||
self.resolver.resolve_path_in_value_ns(self.db.upcast(), path, hygiene)?;
|
||||
|
||||
match value_or_partial {
|
||||
ResolveValueResult::ValueNs(it, _) => (it, None),
|
||||
|
|
|
@ -6,6 +6,7 @@ use base_db::CrateId;
|
|||
use chalk_ir::{cast::Cast, Mutability};
|
||||
use either::Either;
|
||||
use hir_def::{
|
||||
body::HygieneId,
|
||||
builtin_type::BuiltinType,
|
||||
data::adt::{StructFlags, VariantData},
|
||||
lang_item::LangItem,
|
||||
|
@ -2953,6 +2954,7 @@ pub fn render_const_using_debug_impl(
|
|||
let Some(ValueNs::FunctionId(format_fn)) = resolver.resolve_path_in_value_ns_fully(
|
||||
db.upcast(),
|
||||
&hir_def::path::Path::from_known_path_with_no_generic(path![std::fmt::format]),
|
||||
HygieneId::ROOT,
|
||||
) else {
|
||||
not_supported!("std::fmt::format not found");
|
||||
};
|
||||
|
|
|
@ -5,7 +5,7 @@ use std::{fmt::Write, iter, mem};
|
|||
use base_db::ra_salsa::Cycle;
|
||||
use chalk_ir::{BoundVar, ConstData, DebruijnIndex, TyKind};
|
||||
use hir_def::{
|
||||
body::Body,
|
||||
body::{Body, HygieneId},
|
||||
data::adt::{StructKind, VariantData},
|
||||
hir::{
|
||||
ArithOp, Array, BinaryOp, BindingAnnotation, BindingId, ExprId, LabelId, Literal,
|
||||
|
@ -446,9 +446,10 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
|||
} else {
|
||||
let resolver_guard =
|
||||
self.resolver.update_to_inner_scope(self.db.upcast(), self.owner, expr_id);
|
||||
let hygiene = self.body.expr_path_hygiene(expr_id);
|
||||
let result = self
|
||||
.resolver
|
||||
.resolve_path_in_value_ns_fully(self.db.upcast(), p)
|
||||
.resolve_path_in_value_ns_fully(self.db.upcast(), p, hygiene)
|
||||
.ok_or_else(|| {
|
||||
MirLowerError::unresolved_path(self.db, p, self.edition())
|
||||
})?;
|
||||
|
@ -1361,7 +1362,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
|||
|| MirLowerError::unresolved_path(self.db, c.as_ref(), edition);
|
||||
let pr = self
|
||||
.resolver
|
||||
.resolve_path_in_value_ns(self.db.upcast(), c.as_ref())
|
||||
.resolve_path_in_value_ns(self.db.upcast(), c.as_ref(), HygieneId::ROOT)
|
||||
.ok_or_else(unresolved_name)?;
|
||||
match pr {
|
||||
ResolveValueResult::ValueNs(v, _) => {
|
||||
|
|
|
@ -137,7 +137,9 @@ impl MirLowerCtx<'_> {
|
|||
Expr::Path(p) => {
|
||||
let resolver_guard =
|
||||
self.resolver.update_to_inner_scope(self.db.upcast(), self.owner, expr_id);
|
||||
let resolved = self.resolver.resolve_path_in_value_ns_fully(self.db.upcast(), p);
|
||||
let hygiene = self.body.expr_path_hygiene(expr_id);
|
||||
let resolved =
|
||||
self.resolver.resolve_path_in_value_ns_fully(self.db.upcast(), p, hygiene);
|
||||
self.resolver.reset_to_guard(resolver_guard);
|
||||
let Some(pr) = resolved else {
|
||||
return try_rvalue(self);
|
||||
|
|
|
@ -351,9 +351,10 @@ impl MirLowerCtx<'_> {
|
|||
None => {
|
||||
let unresolved_name =
|
||||
|| MirLowerError::unresolved_path(self.db, p, self.edition());
|
||||
let hygiene = self.body.pat_path_hygiene(pattern);
|
||||
let pr = self
|
||||
.resolver
|
||||
.resolve_path_in_value_ns(self.db.upcast(), p)
|
||||
.resolve_path_in_value_ns(self.db.upcast(), p, hygiene)
|
||||
.ok_or_else(unresolved_name)?;
|
||||
|
||||
if let (
|
||||
|
|
|
@ -3720,3 +3720,20 @@ fn test() -> bool {
|
|||
"#]],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn macro_semitransparent_hygiene() {
|
||||
check_types(
|
||||
r#"
|
||||
macro_rules! m {
|
||||
() => { let bar: i32; };
|
||||
}
|
||||
fn foo() {
|
||||
let bar: bool;
|
||||
m!();
|
||||
bar;
|
||||
// ^^^ bool
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue