mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-09-30 13:51:31 +00:00
Merge pull request #18264 from ChayimFriedman2/semi-transparent
fix: Implement mixed site hygiene
This commit is contained in:
commit
f9935be013
27 changed files with 671 additions and 164 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -556,6 +556,7 @@ dependencies = [
|
||||||
"syntax-bridge",
|
"syntax-bridge",
|
||||||
"test-fixture",
|
"test-fixture",
|
||||||
"test-utils",
|
"test-utils",
|
||||||
|
"text-size",
|
||||||
"tracing",
|
"tracing",
|
||||||
"triomphe",
|
"triomphe",
|
||||||
"tt",
|
"tt",
|
||||||
|
|
|
@ -29,6 +29,7 @@ smallvec.workspace = true
|
||||||
hashbrown.workspace = true
|
hashbrown.workspace = true
|
||||||
triomphe.workspace = true
|
triomphe.workspace = true
|
||||||
rustc_apfloat = "0.2.0"
|
rustc_apfloat = "0.2.0"
|
||||||
|
text-size.workspace = true
|
||||||
|
|
||||||
ra-ap-rustc_parse_format.workspace = true
|
ra-ap-rustc_parse_format.workspace = true
|
||||||
ra-ap-rustc_abi.workspace = true
|
ra-ap-rustc_abi.workspace = true
|
||||||
|
|
|
@ -33,6 +33,22 @@ use crate::{
|
||||||
BlockId, DefWithBodyId, HasModule, Lookup,
|
BlockId, DefWithBodyId, HasModule, Lookup,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// A wrapper around [`span::SyntaxContextId`] that is intended only for comparisons.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
|
pub struct HygieneId(pub(crate) span::SyntaxContextId);
|
||||||
|
|
||||||
|
impl HygieneId {
|
||||||
|
pub const ROOT: Self = Self(span::SyntaxContextId::ROOT);
|
||||||
|
|
||||||
|
pub fn new(ctx: span::SyntaxContextId) -> Self {
|
||||||
|
Self(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn is_root(self) -> bool {
|
||||||
|
self.0.is_root()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// The body of an item (function, const etc.).
|
/// The body of an item (function, const etc.).
|
||||||
#[derive(Debug, Eq, PartialEq)]
|
#[derive(Debug, Eq, PartialEq)]
|
||||||
pub struct Body {
|
pub struct Body {
|
||||||
|
@ -55,6 +71,22 @@ pub struct Body {
|
||||||
pub body_expr: ExprId,
|
pub body_expr: ExprId,
|
||||||
/// Block expressions in this body that may contain inner items.
|
/// Block expressions in this body that may contain inner items.
|
||||||
block_scopes: Vec<BlockId>,
|
block_scopes: Vec<BlockId>,
|
||||||
|
|
||||||
|
/// A map from binding to its hygiene ID.
|
||||||
|
///
|
||||||
|
/// Bindings that don't come from macro expansion are not allocated to save space, so not all bindings appear here.
|
||||||
|
/// If a binding does not appear here it has `SyntaxContextId::ROOT`.
|
||||||
|
///
|
||||||
|
/// Note that this may not be the direct `SyntaxContextId` of the binding's expansion, because transparent
|
||||||
|
/// expansions are attributed to their parent expansion (recursively).
|
||||||
|
binding_hygiene: FxHashMap<BindingId, HygieneId>,
|
||||||
|
/// A map from an variable usages to their hygiene ID.
|
||||||
|
///
|
||||||
|
/// Expressions that can be recorded here are single segment path, although not all single segments path refer
|
||||||
|
/// to variables and have hygiene (some refer to items, we don't know at this stage).
|
||||||
|
expr_hygiene: FxHashMap<ExprId, HygieneId>,
|
||||||
|
/// A map from a destructuring assignment possible variable usages to their hygiene ID.
|
||||||
|
pat_hygiene: FxHashMap<PatId, HygieneId>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type ExprPtr = AstPtr<ast::Expr>;
|
pub type ExprPtr = AstPtr<ast::Expr>;
|
||||||
|
@ -107,10 +139,11 @@ pub struct BodySourceMap {
|
||||||
field_map_back: FxHashMap<ExprId, FieldSource>,
|
field_map_back: FxHashMap<ExprId, FieldSource>,
|
||||||
pat_field_map_back: FxHashMap<PatId, PatFieldSource>,
|
pat_field_map_back: FxHashMap<PatId, PatFieldSource>,
|
||||||
|
|
||||||
|
// FIXME: Make this a sane struct.
|
||||||
template_map: Option<
|
template_map: Option<
|
||||||
Box<(
|
Box<(
|
||||||
// format_args!
|
// format_args!
|
||||||
FxHashMap<ExprId, Vec<(syntax::TextRange, Name)>>,
|
FxHashMap<ExprId, (HygieneId, Vec<(syntax::TextRange, Name)>)>,
|
||||||
// asm!
|
// asm!
|
||||||
FxHashMap<ExprId, Vec<Vec<(syntax::TextRange, usize)>>>,
|
FxHashMap<ExprId, Vec<Vec<(syntax::TextRange, usize)>>>,
|
||||||
)>,
|
)>,
|
||||||
|
@ -268,6 +301,9 @@ impl Body {
|
||||||
pats,
|
pats,
|
||||||
bindings,
|
bindings,
|
||||||
binding_owners,
|
binding_owners,
|
||||||
|
binding_hygiene,
|
||||||
|
expr_hygiene,
|
||||||
|
pat_hygiene,
|
||||||
} = self;
|
} = self;
|
||||||
block_scopes.shrink_to_fit();
|
block_scopes.shrink_to_fit();
|
||||||
exprs.shrink_to_fit();
|
exprs.shrink_to_fit();
|
||||||
|
@ -275,6 +311,9 @@ impl Body {
|
||||||
pats.shrink_to_fit();
|
pats.shrink_to_fit();
|
||||||
bindings.shrink_to_fit();
|
bindings.shrink_to_fit();
|
||||||
binding_owners.shrink_to_fit();
|
binding_owners.shrink_to_fit();
|
||||||
|
binding_hygiene.shrink_to_fit();
|
||||||
|
expr_hygiene.shrink_to_fit();
|
||||||
|
pat_hygiene.shrink_to_fit();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn walk_bindings_in_pat(&self, pat_id: PatId, mut f: impl FnMut(BindingId)) {
|
pub fn walk_bindings_in_pat(&self, pat_id: PatId, mut f: impl FnMut(BindingId)) {
|
||||||
|
@ -381,7 +420,7 @@ impl Body {
|
||||||
self.walk_exprs_in_pat(*pat, &mut f);
|
self.walk_exprs_in_pat(*pat, &mut f);
|
||||||
}
|
}
|
||||||
Statement::Expr { expr: expression, .. } => f(*expression),
|
Statement::Expr { expr: expression, .. } => f(*expression),
|
||||||
Statement::Item => (),
|
Statement::Item(_) => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let &Some(expr) = tail {
|
if let &Some(expr) = tail {
|
||||||
|
@ -467,6 +506,25 @@ impl Body {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn binding_hygiene(&self, binding: BindingId) -> HygieneId {
|
||||||
|
self.binding_hygiene.get(&binding).copied().unwrap_or(HygieneId::ROOT)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn expr_path_hygiene(&self, expr: ExprId) -> HygieneId {
|
||||||
|
self.expr_hygiene.get(&expr).copied().unwrap_or(HygieneId::ROOT)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pat_path_hygiene(&self, pat: PatId) -> HygieneId {
|
||||||
|
self.pat_hygiene.get(&pat).copied().unwrap_or(HygieneId::ROOT)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn expr_or_pat_path_hygiene(&self, id: ExprOrPatId) -> HygieneId {
|
||||||
|
match id {
|
||||||
|
ExprOrPatId::ExprId(id) => self.expr_path_hygiene(id),
|
||||||
|
ExprOrPatId::PatId(id) => self.pat_path_hygiene(id),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Body {
|
impl Default for Body {
|
||||||
|
@ -481,6 +539,9 @@ impl Default for Body {
|
||||||
block_scopes: Default::default(),
|
block_scopes: Default::default(),
|
||||||
binding_owners: Default::default(),
|
binding_owners: Default::default(),
|
||||||
self_param: Default::default(),
|
self_param: Default::default(),
|
||||||
|
binding_hygiene: Default::default(),
|
||||||
|
expr_hygiene: Default::default(),
|
||||||
|
pat_hygiene: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -594,13 +655,11 @@ impl BodySourceMap {
|
||||||
pub fn implicit_format_args(
|
pub fn implicit_format_args(
|
||||||
&self,
|
&self,
|
||||||
node: InFile<&ast::FormatArgsExpr>,
|
node: InFile<&ast::FormatArgsExpr>,
|
||||||
) -> Option<&[(syntax::TextRange, Name)]> {
|
) -> Option<(HygieneId, &[(syntax::TextRange, Name)])> {
|
||||||
let src = node.map(AstPtr::new).map(AstPtr::upcast::<ast::Expr>);
|
let src = node.map(AstPtr::new).map(AstPtr::upcast::<ast::Expr>);
|
||||||
self.template_map
|
let (hygiene, names) =
|
||||||
.as_ref()?
|
self.template_map.as_ref()?.0.get(&self.expr_map.get(&src)?.as_expr()?)?;
|
||||||
.0
|
Some((*hygiene, &**names))
|
||||||
.get(&self.expr_map.get(&src)?.as_expr()?)
|
|
||||||
.map(std::ops::Deref::deref)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn asm_template_args(
|
pub fn asm_template_args(
|
||||||
|
@ -649,13 +708,4 @@ impl BodySourceMap {
|
||||||
diagnostics.shrink_to_fit();
|
diagnostics.shrink_to_fit();
|
||||||
binding_definitions.shrink_to_fit();
|
binding_definitions.shrink_to_fit();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn template_map(
|
|
||||||
&self,
|
|
||||||
) -> Option<&(
|
|
||||||
FxHashMap<Idx<Expr>, Vec<(tt::TextRange, Name)>>,
|
|
||||||
FxHashMap<Idx<Expr>, Vec<Vec<(tt::TextRange, usize)>>>,
|
|
||||||
)> {
|
|
||||||
self.template_map.as_deref()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,8 @@ use base_db::CrateId;
|
||||||
use either::Either;
|
use either::Either;
|
||||||
use hir_expand::{
|
use hir_expand::{
|
||||||
name::{AsName, Name},
|
name::{AsName, Name},
|
||||||
InFile,
|
span_map::{ExpansionSpanMap, SpanMap},
|
||||||
|
InFile, MacroDefId,
|
||||||
};
|
};
|
||||||
use intern::{sym, Interned, Symbol};
|
use intern::{sym, Interned, Symbol};
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
|
@ -22,10 +23,11 @@ use syntax::{
|
||||||
},
|
},
|
||||||
AstNode, AstPtr, AstToken as _, SyntaxNodePtr,
|
AstNode, AstPtr, AstToken as _, SyntaxNodePtr,
|
||||||
};
|
};
|
||||||
|
use text_size::TextSize;
|
||||||
use triomphe::Arc;
|
use triomphe::Arc;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
body::{Body, BodyDiagnostic, BodySourceMap, ExprPtr, LabelPtr, PatPtr},
|
body::{Body, BodyDiagnostic, BodySourceMap, ExprPtr, HygieneId, LabelPtr, PatPtr},
|
||||||
builtin_type::BuiltinUint,
|
builtin_type::BuiltinUint,
|
||||||
data::adt::StructKind,
|
data::adt::StructKind,
|
||||||
db::DefDatabase,
|
db::DefDatabase,
|
||||||
|
@ -37,8 +39,8 @@ use crate::{
|
||||||
FormatPlaceholder, FormatSign, FormatTrait,
|
FormatPlaceholder, FormatSign, FormatTrait,
|
||||||
},
|
},
|
||||||
Array, Binding, BindingAnnotation, BindingId, BindingProblems, CaptureBy, ClosureKind,
|
Array, Binding, BindingAnnotation, BindingId, BindingProblems, CaptureBy, ClosureKind,
|
||||||
Expr, ExprId, Label, LabelId, Literal, LiteralOrConst, MatchArm, Movability, OffsetOf, Pat,
|
Expr, ExprId, Item, Label, LabelId, Literal, LiteralOrConst, MatchArm, Movability,
|
||||||
PatId, RecordFieldPat, RecordLitField, Statement,
|
OffsetOf, Pat, PatId, RecordFieldPat, RecordLitField, Statement,
|
||||||
},
|
},
|
||||||
item_scope::BuiltinShadowMode,
|
item_scope::BuiltinShadowMode,
|
||||||
lang_item::LangItem,
|
lang_item::LangItem,
|
||||||
|
@ -46,7 +48,7 @@ use crate::{
|
||||||
nameres::{DefMap, MacroSubNs},
|
nameres::{DefMap, MacroSubNs},
|
||||||
path::{GenericArgs, Path},
|
path::{GenericArgs, Path},
|
||||||
type_ref::{Mutability, Rawness, TypeRef},
|
type_ref::{Mutability, Rawness, TypeRef},
|
||||||
AdtId, BlockId, BlockLoc, ConstBlockLoc, DefWithBodyId, ModuleDefId, UnresolvedMacro,
|
AdtId, BlockId, BlockLoc, ConstBlockLoc, DefWithBodyId, MacroId, ModuleDefId, UnresolvedMacro,
|
||||||
};
|
};
|
||||||
|
|
||||||
type FxIndexSet<K> = indexmap::IndexSet<K, std::hash::BuildHasherDefault<rustc_hash::FxHasher>>;
|
type FxIndexSet<K> = indexmap::IndexSet<K, std::hash::BuildHasherDefault<rustc_hash::FxHasher>>;
|
||||||
|
@ -60,6 +62,17 @@ pub(super) fn lower(
|
||||||
krate: CrateId,
|
krate: CrateId,
|
||||||
is_async_fn: bool,
|
is_async_fn: bool,
|
||||||
) -> (Body, BodySourceMap) {
|
) -> (Body, BodySourceMap) {
|
||||||
|
// We cannot leave the root span map empty and let any identifier from it be treated as root,
|
||||||
|
// because when inside nested macros `SyntaxContextId`s from the outer macro will be interleaved
|
||||||
|
// with the inner macro, and that will cause confusion because they won't be the same as `ROOT`
|
||||||
|
// even though they should be the same. Also, when the body comes from multiple expansions, their
|
||||||
|
// hygiene is different.
|
||||||
|
let span_map = expander.current_file_id().macro_file().map(|_| {
|
||||||
|
let SpanMap::ExpansionSpanMap(span_map) = expander.span_map(db) else {
|
||||||
|
panic!("in a macro file there should be `ExpansionSpanMap`");
|
||||||
|
};
|
||||||
|
Arc::clone(span_map)
|
||||||
|
});
|
||||||
ExprCollector {
|
ExprCollector {
|
||||||
db,
|
db,
|
||||||
owner,
|
owner,
|
||||||
|
@ -74,6 +87,8 @@ pub(super) fn lower(
|
||||||
label_ribs: Vec::new(),
|
label_ribs: Vec::new(),
|
||||||
current_binding_owner: None,
|
current_binding_owner: None,
|
||||||
awaitable_context: None,
|
awaitable_context: None,
|
||||||
|
current_span_map: span_map,
|
||||||
|
current_block_legacy_macro_defs_count: FxHashMap::default(),
|
||||||
}
|
}
|
||||||
.collect(params, body, is_async_fn)
|
.collect(params, body, is_async_fn)
|
||||||
}
|
}
|
||||||
|
@ -90,6 +105,12 @@ struct ExprCollector<'a> {
|
||||||
|
|
||||||
is_lowering_coroutine: bool,
|
is_lowering_coroutine: bool,
|
||||||
|
|
||||||
|
/// Legacy (`macro_rules!`) macros can have multiple definitions and shadow each other,
|
||||||
|
/// and we need to find the current definition. So we track the number of definitions we saw.
|
||||||
|
current_block_legacy_macro_defs_count: FxHashMap<Name, usize>,
|
||||||
|
|
||||||
|
current_span_map: Option<Arc<ExpansionSpanMap>>,
|
||||||
|
|
||||||
current_try_block_label: Option<LabelId>,
|
current_try_block_label: Option<LabelId>,
|
||||||
// points to the expression that a try expression will target (replaces current_try_block_label)
|
// points to the expression that a try expression will target (replaces current_try_block_label)
|
||||||
// catch_scope: Option<ExprId>,
|
// catch_scope: Option<ExprId>,
|
||||||
|
@ -108,31 +129,27 @@ struct ExprCollector<'a> {
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
struct LabelRib {
|
struct LabelRib {
|
||||||
kind: RibKind,
|
kind: RibKind,
|
||||||
// Once we handle macro hygiene this will need to be a map
|
|
||||||
label: Option<(Name, LabelId)>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LabelRib {
|
impl LabelRib {
|
||||||
fn new(kind: RibKind) -> Self {
|
fn new(kind: RibKind) -> Self {
|
||||||
LabelRib { kind, label: None }
|
LabelRib { kind }
|
||||||
}
|
|
||||||
fn new_normal(label: (Name, LabelId)) -> Self {
|
|
||||||
LabelRib { kind: RibKind::Normal, label: Some(label) }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
enum RibKind {
|
enum RibKind {
|
||||||
Normal,
|
Normal(Name, LabelId, HygieneId),
|
||||||
Closure,
|
Closure,
|
||||||
Constant,
|
Constant,
|
||||||
|
MacroDef(Box<MacroDefId>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RibKind {
|
impl RibKind {
|
||||||
/// This rib forbids referring to labels defined in upwards ribs.
|
/// This rib forbids referring to labels defined in upwards ribs.
|
||||||
fn is_label_barrier(self) -> bool {
|
fn is_label_barrier(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
RibKind::Normal => false,
|
RibKind::Normal(..) | RibKind::MacroDef(_) => false,
|
||||||
RibKind::Closure | RibKind::Constant => true,
|
RibKind::Closure | RibKind::Constant => true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -145,7 +162,7 @@ enum Awaitable {
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
struct BindingList {
|
struct BindingList {
|
||||||
map: FxHashMap<Name, BindingId>,
|
map: FxHashMap<(Name, HygieneId), BindingId>,
|
||||||
is_used: FxHashMap<BindingId, bool>,
|
is_used: FxHashMap<BindingId, bool>,
|
||||||
reject_new: bool,
|
reject_new: bool,
|
||||||
}
|
}
|
||||||
|
@ -155,9 +172,16 @@ impl BindingList {
|
||||||
&mut self,
|
&mut self,
|
||||||
ec: &mut ExprCollector<'_>,
|
ec: &mut ExprCollector<'_>,
|
||||||
name: Name,
|
name: Name,
|
||||||
|
hygiene: HygieneId,
|
||||||
mode: BindingAnnotation,
|
mode: BindingAnnotation,
|
||||||
) -> BindingId {
|
) -> BindingId {
|
||||||
let id = *self.map.entry(name).or_insert_with_key(|n| ec.alloc_binding(n.clone(), mode));
|
let id = *self.map.entry((name, hygiene)).or_insert_with_key(|(name, _)| {
|
||||||
|
let id = ec.alloc_binding(name.clone(), mode);
|
||||||
|
if !hygiene.is_root() {
|
||||||
|
ec.body.binding_hygiene.insert(id, hygiene);
|
||||||
|
}
|
||||||
|
id
|
||||||
|
});
|
||||||
if ec.body.bindings[id].mode != mode {
|
if ec.body.bindings[id].mode != mode {
|
||||||
ec.body.bindings[id].problems = Some(BindingProblems::BoundInconsistently);
|
ec.body.bindings[id].problems = Some(BindingProblems::BoundInconsistently);
|
||||||
}
|
}
|
||||||
|
@ -211,6 +235,13 @@ impl ExprCollector<'_> {
|
||||||
Name::new_symbol_root(sym::self_.clone()),
|
Name::new_symbol_root(sym::self_.clone()),
|
||||||
BindingAnnotation::new(is_mutable, false),
|
BindingAnnotation::new(is_mutable, false),
|
||||||
);
|
);
|
||||||
|
let hygiene = self_param
|
||||||
|
.name()
|
||||||
|
.map(|name| self.hygiene_id_for(name.syntax().text_range().start()))
|
||||||
|
.unwrap_or(HygieneId::ROOT);
|
||||||
|
if !hygiene.is_root() {
|
||||||
|
self.body.binding_hygiene.insert(binding_id, hygiene);
|
||||||
|
}
|
||||||
self.body.self_param = Some(binding_id);
|
self.body.self_param = Some(binding_id);
|
||||||
self.source_map.self_param = Some(self.expander.in_file(AstPtr::new(&self_param)));
|
self.source_map.self_param = Some(self.expander.in_file(AstPtr::new(&self_param)));
|
||||||
}
|
}
|
||||||
|
@ -288,13 +319,14 @@ impl ExprCollector<'_> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
Some(ast::BlockModifier::Label(label)) => {
|
Some(ast::BlockModifier::Label(label)) => {
|
||||||
let label = self.collect_label(label);
|
let label_hygiene = self.hygiene_id_for(label.syntax().text_range().start());
|
||||||
self.with_labeled_rib(label, |this| {
|
let label_id = self.collect_label(label);
|
||||||
|
self.with_labeled_rib(label_id, label_hygiene, |this| {
|
||||||
this.collect_block_(e, |id, statements, tail| Expr::Block {
|
this.collect_block_(e, |id, statements, tail| Expr::Block {
|
||||||
id,
|
id,
|
||||||
statements,
|
statements,
|
||||||
tail,
|
tail,
|
||||||
label: Some(label),
|
label: Some(label_id),
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -336,9 +368,14 @@ impl ExprCollector<'_> {
|
||||||
None => self.collect_block(e),
|
None => self.collect_block(e),
|
||||||
},
|
},
|
||||||
ast::Expr::LoopExpr(e) => {
|
ast::Expr::LoopExpr(e) => {
|
||||||
let label = e.label().map(|label| self.collect_label(label));
|
let label = e.label().map(|label| {
|
||||||
|
(
|
||||||
|
self.hygiene_id_for(label.syntax().text_range().start()),
|
||||||
|
self.collect_label(label),
|
||||||
|
)
|
||||||
|
});
|
||||||
let body = self.collect_labelled_block_opt(label, e.loop_body());
|
let body = self.collect_labelled_block_opt(label, e.loop_body());
|
||||||
self.alloc_expr(Expr::Loop { body, label }, syntax_ptr)
|
self.alloc_expr(Expr::Loop { body, label: label.map(|it| it.1) }, syntax_ptr)
|
||||||
}
|
}
|
||||||
ast::Expr::WhileExpr(e) => self.collect_while_loop(syntax_ptr, e),
|
ast::Expr::WhileExpr(e) => self.collect_while_loop(syntax_ptr, e),
|
||||||
ast::Expr::ForExpr(e) => self.collect_for_loop(syntax_ptr, e),
|
ast::Expr::ForExpr(e) => self.collect_for_loop(syntax_ptr, e),
|
||||||
|
@ -398,12 +435,15 @@ impl ExprCollector<'_> {
|
||||||
self.alloc_expr(Expr::Match { expr, arms }, syntax_ptr)
|
self.alloc_expr(Expr::Match { expr, arms }, syntax_ptr)
|
||||||
}
|
}
|
||||||
ast::Expr::PathExpr(e) => {
|
ast::Expr::PathExpr(e) => {
|
||||||
let path = e
|
let (path, hygiene) = self
|
||||||
.path()
|
.collect_expr_path(&e)
|
||||||
.and_then(|path| self.expander.parse_path(self.db, path))
|
.map(|(path, hygiene)| (Expr::Path(path), hygiene))
|
||||||
.map(Expr::Path)
|
.unwrap_or((Expr::Missing, HygieneId::ROOT));
|
||||||
.unwrap_or(Expr::Missing);
|
let expr_id = self.alloc_expr(path, syntax_ptr);
|
||||||
self.alloc_expr(path, syntax_ptr)
|
if !hygiene.is_root() {
|
||||||
|
self.body.expr_hygiene.insert(expr_id, hygiene);
|
||||||
|
}
|
||||||
|
expr_id
|
||||||
}
|
}
|
||||||
ast::Expr::ContinueExpr(e) => {
|
ast::Expr::ContinueExpr(e) => {
|
||||||
let label = self.resolve_label(e.lifetime()).unwrap_or_else(|e| {
|
let label = self.resolve_label(e.lifetime()).unwrap_or_else(|e| {
|
||||||
|
@ -677,6 +717,24 @@ impl ExprCollector<'_> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn collect_expr_path(&mut self, e: &ast::PathExpr) -> Option<(Path, HygieneId)> {
|
||||||
|
e.path().and_then(|path| {
|
||||||
|
let path = self.expander.parse_path(self.db, path)?;
|
||||||
|
let Path::Normal { type_anchor, mod_path, generic_args } = &path else {
|
||||||
|
panic!("path parsing produced a non-normal path");
|
||||||
|
};
|
||||||
|
// Need to enable `mod_path.len() < 1` for `self`.
|
||||||
|
let may_be_variable =
|
||||||
|
type_anchor.is_none() && mod_path.len() <= 1 && generic_args.is_none();
|
||||||
|
let hygiene = if may_be_variable {
|
||||||
|
self.hygiene_id_for(e.syntax().text_range().start())
|
||||||
|
} else {
|
||||||
|
HygieneId::ROOT
|
||||||
|
};
|
||||||
|
Some((path, hygiene))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
fn collect_expr_as_pat_opt(&mut self, expr: Option<ast::Expr>) -> PatId {
|
fn collect_expr_as_pat_opt(&mut self, expr: Option<ast::Expr>) -> PatId {
|
||||||
match expr {
|
match expr {
|
||||||
Some(expr) => self.collect_expr_as_pat(expr),
|
Some(expr) => self.collect_expr_as_pat(expr),
|
||||||
|
@ -740,8 +798,15 @@ impl ExprCollector<'_> {
|
||||||
self.alloc_pat_from_expr(Pat::TupleStruct { path, args, ellipsis }, syntax_ptr)
|
self.alloc_pat_from_expr(Pat::TupleStruct { path, args, ellipsis }, syntax_ptr)
|
||||||
}
|
}
|
||||||
ast::Expr::PathExpr(e) => {
|
ast::Expr::PathExpr(e) => {
|
||||||
let path = Box::new(self.expander.parse_path(self.db, e.path()?)?);
|
let (path, hygiene) = self
|
||||||
self.alloc_pat_from_expr(Pat::Path(path), syntax_ptr)
|
.collect_expr_path(e)
|
||||||
|
.map(|(path, hygiene)| (Pat::Path(Box::new(path)), hygiene))
|
||||||
|
.unwrap_or((Pat::Missing, HygieneId::ROOT));
|
||||||
|
let pat_id = self.alloc_pat_from_expr(path, syntax_ptr);
|
||||||
|
if !hygiene.is_root() {
|
||||||
|
self.body.pat_hygiene.insert(pat_id, hygiene);
|
||||||
|
}
|
||||||
|
pat_id
|
||||||
}
|
}
|
||||||
ast::Expr::MacroExpr(e) => {
|
ast::Expr::MacroExpr(e) => {
|
||||||
let e = e.macro_call()?;
|
let e = e.macro_call()?;
|
||||||
|
@ -889,7 +954,7 @@ impl ExprCollector<'_> {
|
||||||
let old_label = self.current_try_block_label.replace(label);
|
let old_label = self.current_try_block_label.replace(label);
|
||||||
|
|
||||||
let ptr = AstPtr::new(&e).upcast();
|
let ptr = AstPtr::new(&e).upcast();
|
||||||
let (btail, expr_id) = self.with_labeled_rib(label, |this| {
|
let (btail, expr_id) = self.with_labeled_rib(label, HygieneId::ROOT, |this| {
|
||||||
let mut btail = None;
|
let mut btail = None;
|
||||||
let block = this.collect_block_(e, |id, statements, tail| {
|
let block = this.collect_block_(e, |id, statements, tail| {
|
||||||
btail = tail;
|
btail = tail;
|
||||||
|
@ -933,7 +998,9 @@ impl ExprCollector<'_> {
|
||||||
/// FIXME: Rustc wraps the condition in a construct equivalent to `{ let _t = <cond>; _t }`
|
/// FIXME: Rustc wraps the condition in a construct equivalent to `{ let _t = <cond>; _t }`
|
||||||
/// to preserve drop semantics. We should probably do the same in future.
|
/// to preserve drop semantics. We should probably do the same in future.
|
||||||
fn collect_while_loop(&mut self, syntax_ptr: AstPtr<ast::Expr>, e: ast::WhileExpr) -> ExprId {
|
fn collect_while_loop(&mut self, syntax_ptr: AstPtr<ast::Expr>, e: ast::WhileExpr) -> ExprId {
|
||||||
let label = e.label().map(|label| self.collect_label(label));
|
let label = e.label().map(|label| {
|
||||||
|
(self.hygiene_id_for(label.syntax().text_range().start()), self.collect_label(label))
|
||||||
|
});
|
||||||
let body = self.collect_labelled_block_opt(label, e.loop_body());
|
let body = self.collect_labelled_block_opt(label, e.loop_body());
|
||||||
|
|
||||||
// Labels can also be used in the condition expression, like this:
|
// Labels can also be used in the condition expression, like this:
|
||||||
|
@ -950,9 +1017,9 @@ impl ExprCollector<'_> {
|
||||||
// }
|
// }
|
||||||
// ```
|
// ```
|
||||||
let condition = match label {
|
let condition = match label {
|
||||||
Some(label) => {
|
Some((label_hygiene, label)) => self.with_labeled_rib(label, label_hygiene, |this| {
|
||||||
self.with_labeled_rib(label, |this| this.collect_expr_opt(e.condition()))
|
this.collect_expr_opt(e.condition())
|
||||||
}
|
}),
|
||||||
None => self.collect_expr_opt(e.condition()),
|
None => self.collect_expr_opt(e.condition()),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -961,7 +1028,7 @@ impl ExprCollector<'_> {
|
||||||
Expr::If { condition, then_branch: body, else_branch: Some(break_expr) },
|
Expr::If { condition, then_branch: body, else_branch: Some(break_expr) },
|
||||||
syntax_ptr,
|
syntax_ptr,
|
||||||
);
|
);
|
||||||
self.alloc_expr(Expr::Loop { body: if_expr, label }, syntax_ptr)
|
self.alloc_expr(Expr::Loop { body: if_expr, label: label.map(|it| it.1) }, syntax_ptr)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Desugar `ast::ForExpr` from: `[opt_ident]: for <pat> in <head> <body>` into:
|
/// Desugar `ast::ForExpr` from: `[opt_ident]: for <pat> in <head> <body>` into:
|
||||||
|
@ -1005,7 +1072,9 @@ impl ExprCollector<'_> {
|
||||||
args: Box::new([self.collect_pat_top(e.pat())]),
|
args: Box::new([self.collect_pat_top(e.pat())]),
|
||||||
ellipsis: None,
|
ellipsis: None,
|
||||||
};
|
};
|
||||||
let label = e.label().map(|label| self.collect_label(label));
|
let label = e.label().map(|label| {
|
||||||
|
(self.hygiene_id_for(label.syntax().text_range().start()), self.collect_label(label))
|
||||||
|
});
|
||||||
let some_arm = MatchArm {
|
let some_arm = MatchArm {
|
||||||
pat: self.alloc_pat_desugared(some_pat),
|
pat: self.alloc_pat_desugared(some_pat),
|
||||||
guard: None,
|
guard: None,
|
||||||
|
@ -1037,7 +1106,8 @@ impl ExprCollector<'_> {
|
||||||
},
|
},
|
||||||
syntax_ptr,
|
syntax_ptr,
|
||||||
);
|
);
|
||||||
let loop_outer = self.alloc_expr(Expr::Loop { body: loop_inner, label }, syntax_ptr);
|
let loop_outer = self
|
||||||
|
.alloc_expr(Expr::Loop { body: loop_inner, label: label.map(|it| it.1) }, syntax_ptr);
|
||||||
let iter_binding = self.alloc_binding(iter_name, BindingAnnotation::Mutable);
|
let iter_binding = self.alloc_binding(iter_name, BindingAnnotation::Mutable);
|
||||||
let iter_pat = self.alloc_pat_desugared(Pat::Bind { id: iter_binding, subpat: None });
|
let iter_pat = self.alloc_pat_desugared(Pat::Bind { id: iter_binding, subpat: None });
|
||||||
self.add_definition_to_binding(iter_binding, iter_pat);
|
self.add_definition_to_binding(iter_binding, iter_pat);
|
||||||
|
@ -1194,7 +1264,14 @@ impl ExprCollector<'_> {
|
||||||
// FIXME: Report parse errors here
|
// FIXME: Report parse errors here
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let SpanMap::ExpansionSpanMap(new_span_map) = self.expander.span_map(self.db)
|
||||||
|
else {
|
||||||
|
panic!("just expanded a macro, ExpansionSpanMap should be available");
|
||||||
|
};
|
||||||
|
let old_span_map =
|
||||||
|
mem::replace(&mut self.current_span_map, Some(new_span_map.clone()));
|
||||||
let id = collector(self, Some(expansion.tree()));
|
let id = collector(self, Some(expansion.tree()));
|
||||||
|
self.current_span_map = old_span_map;
|
||||||
self.ast_id_map = prev_ast_id_map;
|
self.ast_id_map = prev_ast_id_map;
|
||||||
self.expander.exit(mark);
|
self.expander.exit(mark);
|
||||||
id
|
id
|
||||||
|
@ -1274,8 +1351,44 @@ impl ExprCollector<'_> {
|
||||||
statements.push(Statement::Expr { expr, has_semi });
|
statements.push(Statement::Expr { expr, has_semi });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ast::Stmt::Item(_item) => statements.push(Statement::Item),
|
ast::Stmt::Item(ast::Item::MacroDef(macro_)) => {
|
||||||
|
let Some(name) = macro_.name() else {
|
||||||
|
statements.push(Statement::Item(Item::Other));
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let name = name.as_name();
|
||||||
|
let macro_id = self.def_map.modules[DefMap::ROOT].scope.get(&name).take_macros();
|
||||||
|
self.collect_macro_def(statements, macro_id);
|
||||||
}
|
}
|
||||||
|
ast::Stmt::Item(ast::Item::MacroRules(macro_)) => {
|
||||||
|
let Some(name) = macro_.name() else {
|
||||||
|
statements.push(Statement::Item(Item::Other));
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let name = name.as_name();
|
||||||
|
let macro_defs_count =
|
||||||
|
self.current_block_legacy_macro_defs_count.entry(name.clone()).or_insert(0);
|
||||||
|
let macro_id = self.def_map.modules[DefMap::ROOT]
|
||||||
|
.scope
|
||||||
|
.get_legacy_macro(&name)
|
||||||
|
.and_then(|it| it.get(*macro_defs_count))
|
||||||
|
.copied();
|
||||||
|
*macro_defs_count += 1;
|
||||||
|
self.collect_macro_def(statements, macro_id);
|
||||||
|
}
|
||||||
|
ast::Stmt::Item(_item) => statements.push(Statement::Item(Item::Other)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn collect_macro_def(&mut self, statements: &mut Vec<Statement>, macro_id: Option<MacroId>) {
|
||||||
|
let Some(macro_id) = macro_id else {
|
||||||
|
never!("def map should have macro definition, but it doesn't");
|
||||||
|
statements.push(Statement::Item(Item::Other));
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let macro_id = self.db.macro_def(macro_id);
|
||||||
|
statements.push(Statement::Item(Item::MacroDef(Box::new(macro_id))));
|
||||||
|
self.label_ribs.push(LabelRib::new(RibKind::MacroDef(Box::new(macro_id))));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn collect_block(&mut self, block: ast::BlockExpr) -> ExprId {
|
fn collect_block(&mut self, block: ast::BlockExpr) -> ExprId {
|
||||||
|
@ -1323,6 +1436,7 @@ impl ExprCollector<'_> {
|
||||||
};
|
};
|
||||||
let prev_def_map = mem::replace(&mut self.def_map, def_map);
|
let prev_def_map = mem::replace(&mut self.def_map, def_map);
|
||||||
let prev_local_module = mem::replace(&mut self.expander.module, module);
|
let prev_local_module = mem::replace(&mut self.expander.module, module);
|
||||||
|
let prev_legacy_macros_count = mem::take(&mut self.current_block_legacy_macro_defs_count);
|
||||||
|
|
||||||
let mut statements = Vec::new();
|
let mut statements = Vec::new();
|
||||||
block.statements().for_each(|s| self.collect_stmt(&mut statements, s));
|
block.statements().for_each(|s| self.collect_stmt(&mut statements, s));
|
||||||
|
@ -1345,6 +1459,7 @@ impl ExprCollector<'_> {
|
||||||
|
|
||||||
self.def_map = prev_def_map;
|
self.def_map = prev_def_map;
|
||||||
self.expander.module = prev_local_module;
|
self.expander.module = prev_local_module;
|
||||||
|
self.current_block_legacy_macro_defs_count = prev_legacy_macros_count;
|
||||||
expr_id
|
expr_id
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1357,11 +1472,13 @@ impl ExprCollector<'_> {
|
||||||
|
|
||||||
fn collect_labelled_block_opt(
|
fn collect_labelled_block_opt(
|
||||||
&mut self,
|
&mut self,
|
||||||
label: Option<LabelId>,
|
label: Option<(HygieneId, LabelId)>,
|
||||||
expr: Option<ast::BlockExpr>,
|
expr: Option<ast::BlockExpr>,
|
||||||
) -> ExprId {
|
) -> ExprId {
|
||||||
match label {
|
match label {
|
||||||
Some(label) => self.with_labeled_rib(label, |this| this.collect_block_opt(expr)),
|
Some((hygiene, label)) => {
|
||||||
|
self.with_labeled_rib(label, hygiene, |this| this.collect_block_opt(expr))
|
||||||
|
}
|
||||||
None => self.collect_block_opt(expr),
|
None => self.collect_block_opt(expr),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1379,6 +1496,10 @@ impl ExprCollector<'_> {
|
||||||
let pattern = match &pat {
|
let pattern = match &pat {
|
||||||
ast::Pat::IdentPat(bp) => {
|
ast::Pat::IdentPat(bp) => {
|
||||||
let name = bp.name().map(|nr| nr.as_name()).unwrap_or_else(Name::missing);
|
let name = bp.name().map(|nr| nr.as_name()).unwrap_or_else(Name::missing);
|
||||||
|
let hygiene = bp
|
||||||
|
.name()
|
||||||
|
.map(|name| self.hygiene_id_for(name.syntax().text_range().start()))
|
||||||
|
.unwrap_or(HygieneId::ROOT);
|
||||||
|
|
||||||
let annotation =
|
let annotation =
|
||||||
BindingAnnotation::new(bp.mut_token().is_some(), bp.ref_token().is_some());
|
BindingAnnotation::new(bp.mut_token().is_some(), bp.ref_token().is_some());
|
||||||
|
@ -1414,12 +1535,12 @@ impl ExprCollector<'_> {
|
||||||
}
|
}
|
||||||
// shadowing statics is an error as well, so we just ignore that case here
|
// shadowing statics is an error as well, so we just ignore that case here
|
||||||
_ => {
|
_ => {
|
||||||
let id = binding_list.find(self, name, annotation);
|
let id = binding_list.find(self, name, hygiene, annotation);
|
||||||
(Some(id), Pat::Bind { id, subpat })
|
(Some(id), Pat::Bind { id, subpat })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let id = binding_list.find(self, name, annotation);
|
let id = binding_list.find(self, name, hygiene, annotation);
|
||||||
(Some(id), Pat::Bind { id, subpat })
|
(Some(id), Pat::Bind { id, subpat })
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1698,11 +1819,25 @@ impl ExprCollector<'_> {
|
||||||
lifetime: Option<ast::Lifetime>,
|
lifetime: Option<ast::Lifetime>,
|
||||||
) -> Result<Option<LabelId>, BodyDiagnostic> {
|
) -> Result<Option<LabelId>, BodyDiagnostic> {
|
||||||
let Some(lifetime) = lifetime else { return Ok(None) };
|
let Some(lifetime) = lifetime else { return Ok(None) };
|
||||||
|
let (mut hygiene_id, mut hygiene_info) = match &self.current_span_map {
|
||||||
|
None => (HygieneId::ROOT, None),
|
||||||
|
Some(span_map) => {
|
||||||
|
let span = span_map.span_at(lifetime.syntax().text_range().start());
|
||||||
|
let ctx = self.db.lookup_intern_syntax_context(span.ctx);
|
||||||
|
let hygiene_id = HygieneId::new(ctx.opaque_and_semitransparent);
|
||||||
|
let hygiene_info = ctx.outer_expn.map(|expansion| {
|
||||||
|
let expansion = self.db.lookup_intern_macro_call(expansion);
|
||||||
|
(ctx.parent, expansion.def)
|
||||||
|
});
|
||||||
|
(hygiene_id, hygiene_info)
|
||||||
|
}
|
||||||
|
};
|
||||||
let name = Name::new_lifetime(&lifetime);
|
let name = Name::new_lifetime(&lifetime);
|
||||||
|
|
||||||
for (rib_idx, rib) in self.label_ribs.iter().enumerate().rev() {
|
for (rib_idx, rib) in self.label_ribs.iter().enumerate().rev() {
|
||||||
if let Some((label_name, id)) = &rib.label {
|
match &rib.kind {
|
||||||
if *label_name == name {
|
RibKind::Normal(label_name, id, label_hygiene) => {
|
||||||
|
if *label_name == name && *label_hygiene == hygiene_id {
|
||||||
return if self.is_label_valid_from_rib(rib_idx) {
|
return if self.is_label_valid_from_rib(rib_idx) {
|
||||||
Ok(Some(*id))
|
Ok(Some(*id))
|
||||||
} else {
|
} else {
|
||||||
|
@ -1713,6 +1848,23 @@ impl ExprCollector<'_> {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
RibKind::MacroDef(macro_id) => {
|
||||||
|
if let Some((parent_ctx, label_macro_id)) = hygiene_info {
|
||||||
|
if label_macro_id == **macro_id {
|
||||||
|
// A macro is allowed to refer to labels from before its declaration.
|
||||||
|
// Therefore, if we got to the rib of its declaration, give up its hygiene
|
||||||
|
// and use its parent expansion.
|
||||||
|
let parent_ctx = self.db.lookup_intern_syntax_context(parent_ctx);
|
||||||
|
hygiene_id = HygieneId::new(parent_ctx.opaque_and_semitransparent);
|
||||||
|
hygiene_info = parent_ctx.outer_expn.map(|expansion| {
|
||||||
|
let expansion = self.db.lookup_intern_macro_call(expansion);
|
||||||
|
(parent_ctx.parent, expansion.def)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Err(BodyDiagnostic::UndeclaredLabel {
|
Err(BodyDiagnostic::UndeclaredLabel {
|
||||||
|
@ -1725,28 +1877,44 @@ impl ExprCollector<'_> {
|
||||||
!self.label_ribs[rib_index + 1..].iter().any(|rib| rib.kind.is_label_barrier())
|
!self.label_ribs[rib_index + 1..].iter().any(|rib| rib.kind.is_label_barrier())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn pop_label_rib(&mut self) {
|
||||||
|
// We need to pop all macro defs, plus one rib.
|
||||||
|
while let Some(LabelRib { kind: RibKind::MacroDef(_) }) = self.label_ribs.pop() {
|
||||||
|
// Do nothing.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn with_label_rib<T>(&mut self, kind: RibKind, f: impl FnOnce(&mut Self) -> T) -> T {
|
fn with_label_rib<T>(&mut self, kind: RibKind, f: impl FnOnce(&mut Self) -> T) -> T {
|
||||||
self.label_ribs.push(LabelRib::new(kind));
|
self.label_ribs.push(LabelRib::new(kind));
|
||||||
let res = f(self);
|
let res = f(self);
|
||||||
self.label_ribs.pop();
|
self.pop_label_rib();
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
fn with_labeled_rib<T>(&mut self, label: LabelId, f: impl FnOnce(&mut Self) -> T) -> T {
|
fn with_labeled_rib<T>(
|
||||||
self.label_ribs.push(LabelRib::new_normal((self.body[label].name.clone(), label)));
|
&mut self,
|
||||||
|
label: LabelId,
|
||||||
|
hygiene: HygieneId,
|
||||||
|
f: impl FnOnce(&mut Self) -> T,
|
||||||
|
) -> T {
|
||||||
|
self.label_ribs.push(LabelRib::new(RibKind::Normal(
|
||||||
|
self.body[label].name.clone(),
|
||||||
|
label,
|
||||||
|
hygiene,
|
||||||
|
)));
|
||||||
let res = f(self);
|
let res = f(self);
|
||||||
self.label_ribs.pop();
|
self.pop_label_rib();
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
fn with_opt_labeled_rib<T>(
|
fn with_opt_labeled_rib<T>(
|
||||||
&mut self,
|
&mut self,
|
||||||
label: Option<LabelId>,
|
label: Option<(HygieneId, LabelId)>,
|
||||||
f: impl FnOnce(&mut Self) -> T,
|
f: impl FnOnce(&mut Self) -> T,
|
||||||
) -> T {
|
) -> T {
|
||||||
match label {
|
match label {
|
||||||
None => f(self),
|
None => f(self),
|
||||||
Some(label) => self.with_labeled_rib(label, f),
|
Some((hygiene, label)) => self.with_labeled_rib(label, hygiene, f),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// endregion: labels
|
// endregion: labels
|
||||||
|
@ -1795,28 +1963,39 @@ impl ExprCollector<'_> {
|
||||||
_ => None,
|
_ => None,
|
||||||
});
|
});
|
||||||
let mut mappings = vec![];
|
let mut mappings = vec![];
|
||||||
let fmt = match template.and_then(|it| self.expand_macros_to_string(it)) {
|
let (fmt, hygiene) = match template.and_then(|it| self.expand_macros_to_string(it)) {
|
||||||
Some((s, is_direct_literal)) => {
|
Some((s, is_direct_literal)) => {
|
||||||
let call_ctx = self.expander.syntax_context();
|
let call_ctx = self.expander.syntax_context();
|
||||||
format_args::parse(
|
let hygiene = self.hygiene_id_for(s.syntax().text_range().start());
|
||||||
|
let fmt = format_args::parse(
|
||||||
&s,
|
&s,
|
||||||
fmt_snippet,
|
fmt_snippet,
|
||||||
args,
|
args,
|
||||||
is_direct_literal,
|
is_direct_literal,
|
||||||
|name| self.alloc_expr_desugared(Expr::Path(Path::from(name))),
|
|name| {
|
||||||
|
let expr_id = self.alloc_expr_desugared(Expr::Path(Path::from(name)));
|
||||||
|
if !hygiene.is_root() {
|
||||||
|
self.body.expr_hygiene.insert(expr_id, hygiene);
|
||||||
|
}
|
||||||
|
expr_id
|
||||||
|
},
|
||||||
|name, span| {
|
|name, span| {
|
||||||
if let Some(span) = span {
|
if let Some(span) = span {
|
||||||
mappings.push((span, name))
|
mappings.push((span, name))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
call_ctx,
|
call_ctx,
|
||||||
)
|
);
|
||||||
|
(fmt, hygiene)
|
||||||
}
|
}
|
||||||
None => FormatArgs {
|
None => (
|
||||||
|
FormatArgs {
|
||||||
template: Default::default(),
|
template: Default::default(),
|
||||||
arguments: args.finish(),
|
arguments: args.finish(),
|
||||||
orphans: Default::default(),
|
orphans: Default::default(),
|
||||||
},
|
},
|
||||||
|
HygieneId::ROOT,
|
||||||
|
),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Create a list of all _unique_ (argument, format trait) combinations.
|
// Create a list of all _unique_ (argument, format trait) combinations.
|
||||||
|
@ -1963,7 +2142,11 @@ impl ExprCollector<'_> {
|
||||||
},
|
},
|
||||||
syntax_ptr,
|
syntax_ptr,
|
||||||
);
|
);
|
||||||
self.source_map.template_map.get_or_insert_with(Default::default).0.insert(idx, mappings);
|
self.source_map
|
||||||
|
.template_map
|
||||||
|
.get_or_insert_with(Default::default)
|
||||||
|
.0
|
||||||
|
.insert(idx, (hygiene, mappings));
|
||||||
idx
|
idx
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2264,6 +2447,17 @@ impl ExprCollector<'_> {
|
||||||
self.awaitable_context = orig;
|
self.awaitable_context = orig;
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// If this returns `HygieneId::ROOT`, do not allocate to save space.
|
||||||
|
fn hygiene_id_for(&self, span_start: TextSize) -> HygieneId {
|
||||||
|
match &self.current_span_map {
|
||||||
|
None => HygieneId::ROOT,
|
||||||
|
Some(span_map) => {
|
||||||
|
let ctx = span_map.span_at(span_start).ctx;
|
||||||
|
HygieneId(self.db.lookup_intern_syntax_context(ctx).opaque_and_semitransparent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn comma_follows_token(t: Option<syntax::SyntaxToken>) -> bool {
|
fn comma_follows_token(t: Option<syntax::SyntaxToken>) -> bool {
|
||||||
|
|
|
@ -753,7 +753,7 @@ impl Printer<'_> {
|
||||||
}
|
}
|
||||||
wln!(self);
|
wln!(self);
|
||||||
}
|
}
|
||||||
Statement::Item => (),
|
Statement::Item(_) => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
//! Name resolution for expressions.
|
//! Name resolution for expressions.
|
||||||
use hir_expand::name::Name;
|
use hir_expand::{name::Name, MacroDefId};
|
||||||
use la_arena::{Arena, ArenaMap, Idx, IdxRange, RawIdx};
|
use la_arena::{Arena, ArenaMap, Idx, IdxRange, RawIdx};
|
||||||
use triomphe::Arc;
|
use triomphe::Arc;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
body::Body,
|
body::{Body, HygieneId},
|
||||||
db::DefDatabase,
|
db::DefDatabase,
|
||||||
hir::{Binding, BindingId, Expr, ExprId, LabelId, Pat, PatId, Statement},
|
hir::{Binding, BindingId, Expr, ExprId, Item, LabelId, Pat, PatId, Statement},
|
||||||
BlockId, ConstBlockId, DefWithBodyId,
|
BlockId, ConstBlockId, DefWithBodyId,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -22,6 +22,7 @@ pub struct ExprScopes {
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
pub struct ScopeEntry {
|
pub struct ScopeEntry {
|
||||||
name: Name,
|
name: Name,
|
||||||
|
hygiene: HygieneId,
|
||||||
binding: BindingId,
|
binding: BindingId,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,6 +31,10 @@ impl ScopeEntry {
|
||||||
&self.name
|
&self.name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn hygiene(&self) -> HygieneId {
|
||||||
|
self.hygiene
|
||||||
|
}
|
||||||
|
|
||||||
pub fn binding(&self) -> BindingId {
|
pub fn binding(&self) -> BindingId {
|
||||||
self.binding
|
self.binding
|
||||||
}
|
}
|
||||||
|
@ -40,6 +45,8 @@ pub struct ScopeData {
|
||||||
parent: Option<ScopeId>,
|
parent: Option<ScopeId>,
|
||||||
block: Option<BlockId>,
|
block: Option<BlockId>,
|
||||||
label: Option<(LabelId, Name)>,
|
label: Option<(LabelId, Name)>,
|
||||||
|
// FIXME: We can compress this with an enum for this and `label`/`block` if memory usage matters.
|
||||||
|
macro_def: Option<Box<MacroDefId>>,
|
||||||
entries: IdxRange<ScopeEntry>,
|
entries: IdxRange<ScopeEntry>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,6 +69,12 @@ impl ExprScopes {
|
||||||
self.scopes[scope].block
|
self.scopes[scope].block
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// If `scope` refers to a macro def scope, returns the corresponding `MacroId`.
|
||||||
|
#[allow(clippy::borrowed_box)] // If we return `&MacroDefId` we need to move it, this way we just clone the `Box`.
|
||||||
|
pub fn macro_def(&self, scope: ScopeId) -> Option<&Box<MacroDefId>> {
|
||||||
|
self.scopes[scope].macro_def.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
/// If `scope` refers to a labeled expression scope, returns the corresponding `Label`.
|
/// If `scope` refers to a labeled expression scope, returns the corresponding `Label`.
|
||||||
pub fn label(&self, scope: ScopeId) -> Option<(LabelId, Name)> {
|
pub fn label(&self, scope: ScopeId) -> Option<(LabelId, Name)> {
|
||||||
self.scopes[scope].label.clone()
|
self.scopes[scope].label.clone()
|
||||||
|
@ -102,7 +115,7 @@ impl ExprScopes {
|
||||||
};
|
};
|
||||||
let mut root = scopes.root_scope();
|
let mut root = scopes.root_scope();
|
||||||
if let Some(self_param) = body.self_param {
|
if let Some(self_param) = body.self_param {
|
||||||
scopes.add_bindings(body, root, self_param);
|
scopes.add_bindings(body, root, self_param, body.binding_hygiene(self_param));
|
||||||
}
|
}
|
||||||
scopes.add_params_bindings(body, root, &body.params);
|
scopes.add_params_bindings(body, root, &body.params);
|
||||||
compute_expr_scopes(body.body_expr, body, &mut scopes, &mut root, resolve_const_block);
|
compute_expr_scopes(body.body_expr, body, &mut scopes, &mut root, resolve_const_block);
|
||||||
|
@ -114,6 +127,7 @@ impl ExprScopes {
|
||||||
parent: None,
|
parent: None,
|
||||||
block: None,
|
block: None,
|
||||||
label: None,
|
label: None,
|
||||||
|
macro_def: None,
|
||||||
entries: empty_entries(self.scope_entries.len()),
|
entries: empty_entries(self.scope_entries.len()),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -123,6 +137,7 @@ impl ExprScopes {
|
||||||
parent: Some(parent),
|
parent: Some(parent),
|
||||||
block: None,
|
block: None,
|
||||||
label: None,
|
label: None,
|
||||||
|
macro_def: None,
|
||||||
entries: empty_entries(self.scope_entries.len()),
|
entries: empty_entries(self.scope_entries.len()),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -132,6 +147,7 @@ impl ExprScopes {
|
||||||
parent: Some(parent),
|
parent: Some(parent),
|
||||||
block: None,
|
block: None,
|
||||||
label,
|
label,
|
||||||
|
macro_def: None,
|
||||||
entries: empty_entries(self.scope_entries.len()),
|
entries: empty_entries(self.scope_entries.len()),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -146,21 +162,38 @@ impl ExprScopes {
|
||||||
parent: Some(parent),
|
parent: Some(parent),
|
||||||
block,
|
block,
|
||||||
label,
|
label,
|
||||||
|
macro_def: None,
|
||||||
entries: empty_entries(self.scope_entries.len()),
|
entries: empty_entries(self.scope_entries.len()),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_bindings(&mut self, body: &Body, scope: ScopeId, binding: BindingId) {
|
fn new_macro_def_scope(&mut self, parent: ScopeId, macro_id: Box<MacroDefId>) -> ScopeId {
|
||||||
|
self.scopes.alloc(ScopeData {
|
||||||
|
parent: Some(parent),
|
||||||
|
block: None,
|
||||||
|
label: None,
|
||||||
|
macro_def: Some(macro_id),
|
||||||
|
entries: empty_entries(self.scope_entries.len()),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_bindings(
|
||||||
|
&mut self,
|
||||||
|
body: &Body,
|
||||||
|
scope: ScopeId,
|
||||||
|
binding: BindingId,
|
||||||
|
hygiene: HygieneId,
|
||||||
|
) {
|
||||||
let Binding { name, .. } = &body.bindings[binding];
|
let Binding { name, .. } = &body.bindings[binding];
|
||||||
let entry = self.scope_entries.alloc(ScopeEntry { name: name.clone(), binding });
|
let entry = self.scope_entries.alloc(ScopeEntry { name: name.clone(), binding, hygiene });
|
||||||
self.scopes[scope].entries =
|
self.scopes[scope].entries =
|
||||||
IdxRange::new_inclusive(self.scopes[scope].entries.start()..=entry);
|
IdxRange::new_inclusive(self.scopes[scope].entries.start()..=entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_pat_bindings(&mut self, body: &Body, scope: ScopeId, pat: PatId) {
|
fn add_pat_bindings(&mut self, body: &Body, scope: ScopeId, pat: PatId) {
|
||||||
let pattern = &body[pat];
|
let pattern = &body[pat];
|
||||||
if let Pat::Bind { id, .. } = pattern {
|
if let Pat::Bind { id, .. } = *pattern {
|
||||||
self.add_bindings(body, scope, *id);
|
self.add_bindings(body, scope, id, body.binding_hygiene(id));
|
||||||
}
|
}
|
||||||
|
|
||||||
pattern.walk_child_pats(|pat| self.add_pat_bindings(body, scope, pat));
|
pattern.walk_child_pats(|pat| self.add_pat_bindings(body, scope, pat));
|
||||||
|
@ -206,7 +239,10 @@ fn compute_block_scopes(
|
||||||
Statement::Expr { expr, .. } => {
|
Statement::Expr { expr, .. } => {
|
||||||
compute_expr_scopes(*expr, body, scopes, scope, resolve_const_block);
|
compute_expr_scopes(*expr, body, scopes, scope, resolve_const_block);
|
||||||
}
|
}
|
||||||
Statement::Item => (),
|
Statement::Item(Item::MacroDef(macro_id)) => {
|
||||||
|
*scope = scopes.new_macro_def_scope(*scope, macro_id.clone());
|
||||||
|
}
|
||||||
|
Statement::Item(Item::Other) => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some(expr) = tail {
|
if let Some(expr) = tail {
|
||||||
|
|
|
@ -49,6 +49,10 @@ impl Expander {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn span_map(&self, db: &dyn DefDatabase) -> &SpanMap {
|
||||||
|
self.span_map.get_or_init(|| db.span_map(self.current_file_id))
|
||||||
|
}
|
||||||
|
|
||||||
pub fn krate(&self) -> CrateId {
|
pub fn krate(&self) -> CrateId {
|
||||||
self.module.krate
|
self.module.krate
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,7 @@ pub mod type_ref;
|
||||||
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
use hir_expand::name::Name;
|
use hir_expand::{name::Name, MacroDefId};
|
||||||
use intern::{Interned, Symbol};
|
use intern::{Interned, Symbol};
|
||||||
use la_arena::{Idx, RawIdx};
|
use la_arena::{Idx, RawIdx};
|
||||||
use rustc_apfloat::ieee::{Half as f16, Quad as f128};
|
use rustc_apfloat::ieee::{Half as f16, Quad as f128};
|
||||||
|
@ -492,9 +492,13 @@ pub enum Statement {
|
||||||
expr: ExprId,
|
expr: ExprId,
|
||||||
has_semi: bool,
|
has_semi: bool,
|
||||||
},
|
},
|
||||||
// At the moment, we only use this to figure out if a return expression
|
Item(Item),
|
||||||
// is really the last statement of a block. See #16566
|
}
|
||||||
Item,
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
pub enum Item {
|
||||||
|
MacroDef(Box<MacroDefId>),
|
||||||
|
Other,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Explicit binding annotations given in the HIR for a binding. Note
|
/// Explicit binding annotations given in the HIR for a binding. Note
|
||||||
|
|
|
@ -10,7 +10,10 @@ use smallvec::{smallvec, SmallVec};
|
||||||
use triomphe::Arc;
|
use triomphe::Arc;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
body::scope::{ExprScopes, ScopeId},
|
body::{
|
||||||
|
scope::{ExprScopes, ScopeId},
|
||||||
|
HygieneId,
|
||||||
|
},
|
||||||
builtin_type::BuiltinType,
|
builtin_type::BuiltinType,
|
||||||
data::ExternCrateDeclData,
|
data::ExternCrateDeclData,
|
||||||
db::DefDatabase,
|
db::DefDatabase,
|
||||||
|
@ -80,6 +83,8 @@ enum Scope {
|
||||||
AdtScope(AdtId),
|
AdtScope(AdtId),
|
||||||
/// Local bindings
|
/// Local bindings
|
||||||
ExprScope(ExprScope),
|
ExprScope(ExprScope),
|
||||||
|
/// Macro definition inside bodies that affects all paths after it in the same block.
|
||||||
|
MacroDefScope(Box<MacroDefId>),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
|
@ -188,7 +193,7 @@ impl Resolver {
|
||||||
|
|
||||||
for scope in self.scopes() {
|
for scope in self.scopes() {
|
||||||
match scope {
|
match scope {
|
||||||
Scope::ExprScope(_) => continue,
|
Scope::ExprScope(_) | Scope::MacroDefScope(_) => continue,
|
||||||
Scope::GenericParams { params, def } => {
|
Scope::GenericParams { params, def } => {
|
||||||
if let Some(id) = params.find_type_by_name(first_name, *def) {
|
if let Some(id) = params.find_type_by_name(first_name, *def) {
|
||||||
return Some((TypeNs::GenericParam(id), remaining_idx(), None));
|
return Some((TypeNs::GenericParam(id), remaining_idx(), None));
|
||||||
|
@ -257,6 +262,7 @@ impl Resolver {
|
||||||
&self,
|
&self,
|
||||||
db: &dyn DefDatabase,
|
db: &dyn DefDatabase,
|
||||||
path: &Path,
|
path: &Path,
|
||||||
|
mut hygiene_id: HygieneId,
|
||||||
) -> Option<ResolveValueResult> {
|
) -> Option<ResolveValueResult> {
|
||||||
let path = match path {
|
let path = match path {
|
||||||
Path::Normal { mod_path, .. } => mod_path,
|
Path::Normal { mod_path, .. } => mod_path,
|
||||||
|
@ -300,14 +306,22 @@ impl Resolver {
|
||||||
}
|
}
|
||||||
|
|
||||||
if n_segments <= 1 {
|
if n_segments <= 1 {
|
||||||
|
let mut hygiene_info = if !hygiene_id.is_root() {
|
||||||
|
let ctx = db.lookup_intern_syntax_context(hygiene_id.0);
|
||||||
|
ctx.outer_expn.map(|expansion| {
|
||||||
|
let expansion = db.lookup_intern_macro_call(expansion);
|
||||||
|
(ctx.parent, expansion.def)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
for scope in self.scopes() {
|
for scope in self.scopes() {
|
||||||
match scope {
|
match scope {
|
||||||
Scope::ExprScope(scope) => {
|
Scope::ExprScope(scope) => {
|
||||||
let entry = scope
|
let entry =
|
||||||
.expr_scopes
|
scope.expr_scopes.entries(scope.scope_id).iter().find(|entry| {
|
||||||
.entries(scope.scope_id)
|
entry.name() == first_name && entry.hygiene() == hygiene_id
|
||||||
.iter()
|
});
|
||||||
.find(|entry| entry.name() == first_name);
|
|
||||||
|
|
||||||
if let Some(e) = entry {
|
if let Some(e) = entry {
|
||||||
return Some(ResolveValueResult::ValueNs(
|
return Some(ResolveValueResult::ValueNs(
|
||||||
|
@ -316,6 +330,21 @@ impl Resolver {
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Scope::MacroDefScope(macro_id) => {
|
||||||
|
if let Some((parent_ctx, label_macro_id)) = hygiene_info {
|
||||||
|
if label_macro_id == **macro_id {
|
||||||
|
// A macro is allowed to refer to variables from before its declaration.
|
||||||
|
// Therefore, if we got to the rib of its declaration, give up its hygiene
|
||||||
|
// and use its parent expansion.
|
||||||
|
let parent_ctx = db.lookup_intern_syntax_context(parent_ctx);
|
||||||
|
hygiene_id = HygieneId::new(parent_ctx.opaque_and_semitransparent);
|
||||||
|
hygiene_info = parent_ctx.outer_expn.map(|expansion| {
|
||||||
|
let expansion = db.lookup_intern_macro_call(expansion);
|
||||||
|
(parent_ctx.parent, expansion.def)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Scope::GenericParams { params, def } => {
|
Scope::GenericParams { params, def } => {
|
||||||
if let Some(id) = params.find_const_by_name(first_name, *def) {
|
if let Some(id) = params.find_const_by_name(first_name, *def) {
|
||||||
let val = ValueNs::GenericParam(id);
|
let val = ValueNs::GenericParam(id);
|
||||||
|
@ -342,7 +371,7 @@ impl Resolver {
|
||||||
} else {
|
} else {
|
||||||
for scope in self.scopes() {
|
for scope in self.scopes() {
|
||||||
match scope {
|
match scope {
|
||||||
Scope::ExprScope(_) => continue,
|
Scope::ExprScope(_) | Scope::MacroDefScope(_) => continue,
|
||||||
Scope::GenericParams { params, def } => {
|
Scope::GenericParams { params, def } => {
|
||||||
if let Some(id) = params.find_type_by_name(first_name, *def) {
|
if let Some(id) = params.find_type_by_name(first_name, *def) {
|
||||||
let ty = TypeNs::GenericParam(id);
|
let ty = TypeNs::GenericParam(id);
|
||||||
|
@ -393,8 +422,9 @@ impl Resolver {
|
||||||
&self,
|
&self,
|
||||||
db: &dyn DefDatabase,
|
db: &dyn DefDatabase,
|
||||||
path: &Path,
|
path: &Path,
|
||||||
|
hygiene: HygieneId,
|
||||||
) -> Option<ValueNs> {
|
) -> Option<ValueNs> {
|
||||||
match self.resolve_path_in_value_ns(db, path)? {
|
match self.resolve_path_in_value_ns(db, path, hygiene)? {
|
||||||
ResolveValueResult::ValueNs(it, _) => Some(it),
|
ResolveValueResult::ValueNs(it, _) => Some(it),
|
||||||
ResolveValueResult::Partial(..) => None,
|
ResolveValueResult::Partial(..) => None,
|
||||||
}
|
}
|
||||||
|
@ -622,7 +652,7 @@ impl Resolver {
|
||||||
|
|
||||||
pub fn type_owner(&self) -> Option<TypeOwnerId> {
|
pub fn type_owner(&self) -> Option<TypeOwnerId> {
|
||||||
self.scopes().find_map(|scope| match scope {
|
self.scopes().find_map(|scope| match scope {
|
||||||
Scope::BlockScope(_) => None,
|
Scope::BlockScope(_) | Scope::MacroDefScope(_) => None,
|
||||||
&Scope::GenericParams { def, .. } => Some(def.into()),
|
&Scope::GenericParams { def, .. } => Some(def.into()),
|
||||||
&Scope::ImplDefScope(id) => Some(id.into()),
|
&Scope::ImplDefScope(id) => Some(id.into()),
|
||||||
&Scope::AdtScope(adt) => Some(adt.into()),
|
&Scope::AdtScope(adt) => Some(adt.into()),
|
||||||
|
@ -653,6 +683,9 @@ impl Resolver {
|
||||||
expr_scopes: &Arc<ExprScopes>,
|
expr_scopes: &Arc<ExprScopes>,
|
||||||
scope_id: ScopeId,
|
scope_id: ScopeId,
|
||||||
) {
|
) {
|
||||||
|
if let Some(macro_id) = expr_scopes.macro_def(scope_id) {
|
||||||
|
resolver.scopes.push(Scope::MacroDefScope(macro_id.clone()));
|
||||||
|
}
|
||||||
resolver.scopes.push(Scope::ExprScope(ExprScope {
|
resolver.scopes.push(Scope::ExprScope(ExprScope {
|
||||||
owner,
|
owner,
|
||||||
expr_scopes: expr_scopes.clone(),
|
expr_scopes: expr_scopes.clone(),
|
||||||
|
@ -670,7 +703,7 @@ impl Resolver {
|
||||||
}
|
}
|
||||||
|
|
||||||
let start = self.scopes.len();
|
let start = self.scopes.len();
|
||||||
let innermost_scope = self.scopes().next();
|
let innermost_scope = self.scopes().find(|scope| !matches!(scope, Scope::MacroDefScope(_)));
|
||||||
match innermost_scope {
|
match innermost_scope {
|
||||||
Some(&Scope::ExprScope(ExprScope { scope_id, ref expr_scopes, owner })) => {
|
Some(&Scope::ExprScope(ExprScope { scope_id, ref expr_scopes, owner })) => {
|
||||||
let expr_scopes = expr_scopes.clone();
|
let expr_scopes = expr_scopes.clone();
|
||||||
|
@ -794,6 +827,7 @@ impl Scope {
|
||||||
acc.add_local(e.name(), e.binding());
|
acc.add_local(e.name(), e.binding());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
Scope::MacroDefScope(_) => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -833,6 +867,9 @@ fn resolver_for_scope_(
|
||||||
// already traverses all parents, so this is O(n²). I think we could only store the
|
// already traverses all parents, so this is O(n²). I think we could only store the
|
||||||
// innermost module scope instead?
|
// innermost module scope instead?
|
||||||
}
|
}
|
||||||
|
if let Some(macro_id) = scopes.macro_def(scope) {
|
||||||
|
r = r.push_scope(Scope::MacroDefScope(macro_id.clone()));
|
||||||
|
}
|
||||||
|
|
||||||
r = r.push_expr_scope(owner, Arc::clone(&scopes), scope);
|
r = r.push_expr_scope(owner, Arc::clone(&scopes), scope);
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,8 @@ use syntax::utils::is_raw_identifier;
|
||||||
#[derive(Clone, PartialEq, Eq, Hash)]
|
#[derive(Clone, PartialEq, Eq, Hash)]
|
||||||
pub struct Name {
|
pub struct Name {
|
||||||
symbol: Symbol,
|
symbol: Symbol,
|
||||||
|
// If you are making this carry actual hygiene, beware that the special handling for variables and labels
|
||||||
|
// in bodies can go.
|
||||||
ctx: (),
|
ctx: (),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
use base_db::{ra_salsa::Cycle, CrateId};
|
use base_db::{ra_salsa::Cycle, CrateId};
|
||||||
use chalk_ir::{cast::Cast, BoundVar, DebruijnIndex};
|
use chalk_ir::{cast::Cast, BoundVar, DebruijnIndex};
|
||||||
use hir_def::{
|
use hir_def::{
|
||||||
body::Body,
|
body::{Body, HygieneId},
|
||||||
hir::{Expr, ExprId},
|
hir::{Expr, ExprId},
|
||||||
path::Path,
|
path::Path,
|
||||||
resolver::{Resolver, ValueNs},
|
resolver::{Resolver, ValueNs},
|
||||||
|
@ -80,7 +80,7 @@ pub(crate) fn path_to_const<'g>(
|
||||||
debruijn: DebruijnIndex,
|
debruijn: DebruijnIndex,
|
||||||
expected_ty: Ty,
|
expected_ty: Ty,
|
||||||
) -> Option<Const> {
|
) -> 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)) => {
|
Some(ValueNs::GenericParam(p)) => {
|
||||||
let ty = db.const_param_ty(p);
|
let ty = db.const_param_ty(p);
|
||||||
let value = match mode {
|
let value = match mode {
|
||||||
|
|
|
@ -289,10 +289,12 @@ impl ExprValidator {
|
||||||
match &self.body[scrutinee_expr] {
|
match &self.body[scrutinee_expr] {
|
||||||
Expr::UnaryOp { op: UnaryOp::Deref, .. } => false,
|
Expr::UnaryOp { op: UnaryOp::Deref, .. } => false,
|
||||||
Expr::Path(path) => {
|
Expr::Path(path) => {
|
||||||
let value_or_partial = self
|
let value_or_partial =
|
||||||
.owner
|
self.owner.resolver(db.upcast()).resolve_path_in_value_ns_fully(
|
||||||
.resolver(db.upcast())
|
db.upcast(),
|
||||||
.resolve_path_in_value_ns_fully(db.upcast(), path);
|
path,
|
||||||
|
self.body.expr_path_hygiene(scrutinee_expr),
|
||||||
|
);
|
||||||
value_or_partial.map_or(true, |v| !matches!(v, ValueNs::StaticId(_)))
|
value_or_partial.map_or(true, |v| !matches!(v, ValueNs::StaticId(_)))
|
||||||
}
|
}
|
||||||
Expr::Field { expr, .. } => match self.infer.type_of_expr[*expr].kind(Interner) {
|
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 mut mark_unsafe_path = |path, node| {
|
||||||
let g = resolver.update_to_inner_scope(db.upcast(), def, current);
|
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 {
|
if let Some(ResolveValueResult::ValueNs(ValueNs::StaticId(id), _)) = value_or_partial {
|
||||||
let static_data = db.static_data(id);
|
let static_data = db.static_data(id);
|
||||||
if static_data.mutable || (static_data.is_extern && !static_data.has_safe_kw) {
|
if static_data.mutable || (static_data.is_extern && !static_data.has_safe_kw) {
|
||||||
|
|
|
@ -33,7 +33,7 @@ use chalk_ir::{
|
||||||
};
|
};
|
||||||
use either::Either;
|
use either::Either;
|
||||||
use hir_def::{
|
use hir_def::{
|
||||||
body::Body,
|
body::{Body, HygieneId},
|
||||||
builtin_type::{BuiltinInt, BuiltinType, BuiltinUint},
|
builtin_type::{BuiltinInt, BuiltinType, BuiltinUint},
|
||||||
data::{ConstData, StaticData},
|
data::{ConstData, StaticData},
|
||||||
hir::{BindingAnnotation, BindingId, ExprId, ExprOrPatId, LabelId, PatId},
|
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 ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver, self.owner.into());
|
||||||
let (resolution, unresolved) = if value_ns {
|
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 {
|
Some(ResolveValueResult::ValueNs(value, _)) => match value {
|
||||||
ValueNs::EnumVariantId(var) => {
|
ValueNs::EnumVariantId(var) => {
|
||||||
let substs = ctx.substs_from_path(path, var.into(), true);
|
let substs = ctx.substs_from_path(path, var.into(), true);
|
||||||
|
|
|
@ -514,8 +514,11 @@ impl InferenceContext<'_> {
|
||||||
if path.type_anchor().is_some() {
|
if path.type_anchor().is_some() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let result = self.resolver.resolve_path_in_value_ns_fully(self.db.upcast(), path).and_then(
|
let hygiene = self.body.expr_or_pat_path_hygiene(id);
|
||||||
|result| match result {
|
let result = self
|
||||||
|
.resolver
|
||||||
|
.resolve_path_in_value_ns_fully(self.db.upcast(), path, hygiene)
|
||||||
|
.and_then(|result| match result {
|
||||||
ValueNs::LocalBinding(binding) => {
|
ValueNs::LocalBinding(binding) => {
|
||||||
let mir_span = match id {
|
let mir_span = match id {
|
||||||
ExprOrPatId::ExprId(id) => MirSpan::ExprId(id),
|
ExprOrPatId::ExprId(id) => MirSpan::ExprId(id),
|
||||||
|
@ -525,8 +528,7 @@ impl InferenceContext<'_> {
|
||||||
Some(HirPlace { local: binding, projections: Vec::new() })
|
Some(HirPlace { local: binding, projections: Vec::new() })
|
||||||
}
|
}
|
||||||
_ => None,
|
_ => None,
|
||||||
},
|
});
|
||||||
);
|
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -745,7 +747,7 @@ impl InferenceContext<'_> {
|
||||||
Statement::Expr { expr, has_semi: _ } => {
|
Statement::Expr { expr, has_semi: _ } => {
|
||||||
self.consume_expr(*expr);
|
self.consume_expr(*expr);
|
||||||
}
|
}
|
||||||
Statement::Item => (),
|
Statement::Item(_) => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some(tail) = tail {
|
if let Some(tail) = tail {
|
||||||
|
|
|
@ -201,7 +201,11 @@ impl InferenceContext<'_> {
|
||||||
Expr::Path(Path::Normal { type_anchor: Some(_), .. }) => false,
|
Expr::Path(Path::Normal { type_anchor: Some(_), .. }) => false,
|
||||||
Expr::Path(path) => self
|
Expr::Path(path) => self
|
||||||
.resolver
|
.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(_))),
|
.map_or(true, |res| matches!(res, ValueNs::LocalBinding(_) | ValueNs::StaticId(_))),
|
||||||
Expr::Underscore => true,
|
Expr::Underscore => true,
|
||||||
Expr::UnaryOp { op: UnaryOp::Deref, .. } => true,
|
Expr::UnaryOp { op: UnaryOp::Deref, .. } => true,
|
||||||
|
@ -1652,7 +1656,7 @@ impl InferenceContext<'_> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Statement::Item => (),
|
Statement::Item(_) => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -89,7 +89,7 @@ impl InferenceContext<'_> {
|
||||||
Statement::Expr { expr, has_semi: _ } => {
|
Statement::Expr { expr, has_semi: _ } => {
|
||||||
self.infer_mut_expr(*expr, Mutability::Not);
|
self.infer_mut_expr(*expr, Mutability::Not);
|
||||||
}
|
}
|
||||||
Statement::Item => (),
|
Statement::Item(_) => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some(tail) = tail {
|
if let Some(tail) = tail {
|
||||||
|
|
|
@ -164,9 +164,10 @@ impl InferenceContext<'_> {
|
||||||
let ty = self.table.normalize_associated_types_in(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)))?
|
self.resolve_ty_assoc_item(ty, last.name, id).map(|(it, substs)| (it, Some(substs)))?
|
||||||
} else {
|
} else {
|
||||||
|
let hygiene = self.body.expr_or_pat_path_hygiene(id);
|
||||||
// FIXME: report error, unresolved first path segment
|
// FIXME: report error, unresolved first path segment
|
||||||
let value_or_partial =
|
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 {
|
match value_or_partial {
|
||||||
ResolveValueResult::ValueNs(it, _) => (it, None),
|
ResolveValueResult::ValueNs(it, _) => (it, None),
|
||||||
|
|
|
@ -6,6 +6,7 @@ use base_db::CrateId;
|
||||||
use chalk_ir::{cast::Cast, Mutability};
|
use chalk_ir::{cast::Cast, Mutability};
|
||||||
use either::Either;
|
use either::Either;
|
||||||
use hir_def::{
|
use hir_def::{
|
||||||
|
body::HygieneId,
|
||||||
builtin_type::BuiltinType,
|
builtin_type::BuiltinType,
|
||||||
data::adt::{StructFlags, VariantData},
|
data::adt::{StructFlags, VariantData},
|
||||||
lang_item::LangItem,
|
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(
|
let Some(ValueNs::FunctionId(format_fn)) = resolver.resolve_path_in_value_ns_fully(
|
||||||
db.upcast(),
|
db.upcast(),
|
||||||
&hir_def::path::Path::from_known_path_with_no_generic(path![std::fmt::format]),
|
&hir_def::path::Path::from_known_path_with_no_generic(path![std::fmt::format]),
|
||||||
|
HygieneId::ROOT,
|
||||||
) else {
|
) else {
|
||||||
not_supported!("std::fmt::format not found");
|
not_supported!("std::fmt::format not found");
|
||||||
};
|
};
|
||||||
|
|
|
@ -5,7 +5,7 @@ use std::{fmt::Write, iter, mem};
|
||||||
use base_db::ra_salsa::Cycle;
|
use base_db::ra_salsa::Cycle;
|
||||||
use chalk_ir::{BoundVar, ConstData, DebruijnIndex, TyKind};
|
use chalk_ir::{BoundVar, ConstData, DebruijnIndex, TyKind};
|
||||||
use hir_def::{
|
use hir_def::{
|
||||||
body::Body,
|
body::{Body, HygieneId},
|
||||||
data::adt::{StructKind, VariantData},
|
data::adt::{StructKind, VariantData},
|
||||||
hir::{
|
hir::{
|
||||||
ArithOp, Array, BinaryOp, BindingAnnotation, BindingId, ExprId, LabelId, Literal,
|
ArithOp, Array, BinaryOp, BindingAnnotation, BindingId, ExprId, LabelId, Literal,
|
||||||
|
@ -446,9 +446,10 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
||||||
} else {
|
} else {
|
||||||
let resolver_guard =
|
let resolver_guard =
|
||||||
self.resolver.update_to_inner_scope(self.db.upcast(), self.owner, expr_id);
|
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
|
let result = self
|
||||||
.resolver
|
.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(|| {
|
.ok_or_else(|| {
|
||||||
MirLowerError::unresolved_path(self.db, p, self.edition())
|
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);
|
|| MirLowerError::unresolved_path(self.db, c.as_ref(), edition);
|
||||||
let pr = self
|
let pr = self
|
||||||
.resolver
|
.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)?;
|
.ok_or_else(unresolved_name)?;
|
||||||
match pr {
|
match pr {
|
||||||
ResolveValueResult::ValueNs(v, _) => {
|
ResolveValueResult::ValueNs(v, _) => {
|
||||||
|
@ -1782,7 +1783,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
||||||
self.push_fake_read(c, p, expr.into());
|
self.push_fake_read(c, p, expr.into());
|
||||||
current = scope2.pop_and_drop(self, c, expr.into());
|
current = scope2.pop_and_drop(self, c, expr.into());
|
||||||
}
|
}
|
||||||
hir_def::hir::Statement::Item => (),
|
hir_def::hir::Statement::Item(_) => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some(tail) = tail {
|
if let Some(tail) = tail {
|
||||||
|
|
|
@ -137,7 +137,9 @@ impl MirLowerCtx<'_> {
|
||||||
Expr::Path(p) => {
|
Expr::Path(p) => {
|
||||||
let resolver_guard =
|
let resolver_guard =
|
||||||
self.resolver.update_to_inner_scope(self.db.upcast(), self.owner, expr_id);
|
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);
|
self.resolver.reset_to_guard(resolver_guard);
|
||||||
let Some(pr) = resolved else {
|
let Some(pr) = resolved else {
|
||||||
return try_rvalue(self);
|
return try_rvalue(self);
|
||||||
|
|
|
@ -351,9 +351,10 @@ impl MirLowerCtx<'_> {
|
||||||
None => {
|
None => {
|
||||||
let unresolved_name =
|
let unresolved_name =
|
||||||
|| MirLowerError::unresolved_path(self.db, p, self.edition());
|
|| MirLowerError::unresolved_path(self.db, p, self.edition());
|
||||||
|
let hygiene = self.body.pat_path_hygiene(pattern);
|
||||||
let pr = self
|
let pr = self
|
||||||
.resolver
|
.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)?;
|
.ok_or_else(unresolved_name)?;
|
||||||
|
|
||||||
if let (
|
if let (
|
||||||
|
|
|
@ -3720,3 +3720,85 @@ 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
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn macro_expansion_can_refer_variables_defined_before_macro_definition() {
|
||||||
|
check_types(
|
||||||
|
r#"
|
||||||
|
fn foo() {
|
||||||
|
let v: i32 = 0;
|
||||||
|
macro_rules! m {
|
||||||
|
() => { v };
|
||||||
|
}
|
||||||
|
let v: bool = true;
|
||||||
|
m!();
|
||||||
|
// ^^^^ i32
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn macro_rules_shadowing_works_with_hygiene() {
|
||||||
|
check_types(
|
||||||
|
r#"
|
||||||
|
fn foo() {
|
||||||
|
let v: bool;
|
||||||
|
macro_rules! m { () => { v } }
|
||||||
|
m!();
|
||||||
|
// ^^^^ bool
|
||||||
|
|
||||||
|
let v: char;
|
||||||
|
macro_rules! m { () => { v } }
|
||||||
|
m!();
|
||||||
|
// ^^^^ char
|
||||||
|
|
||||||
|
{
|
||||||
|
let v: u8;
|
||||||
|
macro_rules! m { () => { v } }
|
||||||
|
m!();
|
||||||
|
// ^^^^ u8
|
||||||
|
|
||||||
|
let v: i8;
|
||||||
|
macro_rules! m { () => { v } }
|
||||||
|
m!();
|
||||||
|
// ^^^^ i8
|
||||||
|
|
||||||
|
let v: i16;
|
||||||
|
macro_rules! m { () => { v } }
|
||||||
|
m!();
|
||||||
|
// ^^^^ i16
|
||||||
|
|
||||||
|
{
|
||||||
|
let v: u32;
|
||||||
|
macro_rules! m { () => { v } }
|
||||||
|
m!();
|
||||||
|
// ^^^^ u32
|
||||||
|
|
||||||
|
let v: u64;
|
||||||
|
macro_rules! m { () => { v } }
|
||||||
|
m!();
|
||||||
|
// ^^^^ u64
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
@ -45,7 +45,7 @@ use syntax::{
|
||||||
use crate::{
|
use crate::{
|
||||||
db::HirDatabase,
|
db::HirDatabase,
|
||||||
semantics::source_to_def::{ChildContainer, SourceToDefCache, SourceToDefCtx},
|
semantics::source_to_def::{ChildContainer, SourceToDefCache, SourceToDefCtx},
|
||||||
source_analyzer::{resolve_hir_path, SourceAnalyzer},
|
source_analyzer::{name_hygiene, resolve_hir_path, SourceAnalyzer},
|
||||||
Access, Adjust, Adjustment, Adt, AutoBorrow, BindingMode, BuiltinAttr, Callable, Const,
|
Access, Adjust, Adjustment, Adt, AutoBorrow, BindingMode, BuiltinAttr, Callable, Const,
|
||||||
ConstParam, Crate, DeriveHelper, Enum, Field, Function, HasSource, HirFileId, Impl, InFile,
|
ConstParam, Crate, DeriveHelper, Enum, Field, Function, HasSource, HirFileId, Impl, InFile,
|
||||||
InlineAsmOperand, ItemInNs, Label, LifetimeParam, Local, Macro, Module, ModuleDef, Name,
|
InlineAsmOperand, ItemInNs, Label, LifetimeParam, Local, Macro, Module, ModuleDef, Name,
|
||||||
|
@ -1952,10 +1952,15 @@ impl SemanticsScope<'_> {
|
||||||
|
|
||||||
/// Resolve a path as-if it was written at the given scope. This is
|
/// Resolve a path as-if it was written at the given scope. This is
|
||||||
/// necessary a heuristic, as it doesn't take hygiene into account.
|
/// necessary a heuristic, as it doesn't take hygiene into account.
|
||||||
pub fn speculative_resolve(&self, path: &ast::Path) -> Option<PathResolution> {
|
pub fn speculative_resolve(&self, ast_path: &ast::Path) -> Option<PathResolution> {
|
||||||
let ctx = LowerCtx::new(self.db.upcast(), self.file_id);
|
let ctx = LowerCtx::new(self.db.upcast(), self.file_id);
|
||||||
let path = Path::from_src(&ctx, path.clone())?;
|
let path = Path::from_src(&ctx, ast_path.clone())?;
|
||||||
resolve_hir_path(self.db, &self.resolver, &path)
|
resolve_hir_path(
|
||||||
|
self.db,
|
||||||
|
&self.resolver,
|
||||||
|
&path,
|
||||||
|
name_hygiene(self.db, InFile::new(self.file_id, ast_path.syntax())),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Iterates over associated types that may be specified after the given path (using
|
/// Iterates over associated types that may be specified after the given path (using
|
||||||
|
|
|
@ -16,7 +16,7 @@ use either::Either;
|
||||||
use hir_def::{
|
use hir_def::{
|
||||||
body::{
|
body::{
|
||||||
scope::{ExprScopes, ScopeId},
|
scope::{ExprScopes, ScopeId},
|
||||||
Body, BodySourceMap,
|
Body, BodySourceMap, HygieneId,
|
||||||
},
|
},
|
||||||
hir::{BindingId, ExprId, ExprOrPatId, Pat, PatId},
|
hir::{BindingId, ExprId, ExprOrPatId, Pat, PatId},
|
||||||
lang_item::LangItem,
|
lang_item::LangItem,
|
||||||
|
@ -562,7 +562,8 @@ impl SourceAnalyzer {
|
||||||
let expr = ast::Expr::from(record_expr);
|
let expr = ast::Expr::from(record_expr);
|
||||||
let expr_id = self.body_source_map()?.node_expr(InFile::new(self.file_id, &expr))?;
|
let expr_id = self.body_source_map()?.node_expr(InFile::new(self.file_id, &expr))?;
|
||||||
|
|
||||||
let local_name = field.field_name()?.as_name();
|
let ast_name = field.field_name()?;
|
||||||
|
let local_name = ast_name.as_name();
|
||||||
let local = if field.name_ref().is_some() {
|
let local = if field.name_ref().is_some() {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
|
@ -571,7 +572,11 @@ impl SourceAnalyzer {
|
||||||
PathKind::Plain,
|
PathKind::Plain,
|
||||||
once(local_name.clone()),
|
once(local_name.clone()),
|
||||||
));
|
));
|
||||||
match self.resolver.resolve_path_in_value_ns_fully(db.upcast(), &path) {
|
match self.resolver.resolve_path_in_value_ns_fully(
|
||||||
|
db.upcast(),
|
||||||
|
&path,
|
||||||
|
name_hygiene(db, InFile::new(self.file_id, ast_name.syntax())),
|
||||||
|
) {
|
||||||
Some(ValueNs::LocalBinding(binding_id)) => {
|
Some(ValueNs::LocalBinding(binding_id)) => {
|
||||||
Some(Local { binding_id, parent: self.resolver.body_owner()? })
|
Some(Local { binding_id, parent: self.resolver.body_owner()? })
|
||||||
}
|
}
|
||||||
|
@ -627,7 +632,7 @@ impl SourceAnalyzer {
|
||||||
Pat::Path(path) => path,
|
Pat::Path(path) => path,
|
||||||
_ => return None,
|
_ => return None,
|
||||||
};
|
};
|
||||||
let res = resolve_hir_path(db, &self.resolver, path)?;
|
let res = resolve_hir_path(db, &self.resolver, path, HygieneId::ROOT)?;
|
||||||
match res {
|
match res {
|
||||||
PathResolution::Def(def) => Some(def),
|
PathResolution::Def(def) => Some(def),
|
||||||
_ => None,
|
_ => None,
|
||||||
|
@ -818,7 +823,13 @@ impl SourceAnalyzer {
|
||||||
if parent().map_or(false, |it| ast::Visibility::can_cast(it.kind())) {
|
if parent().map_or(false, |it| ast::Visibility::can_cast(it.kind())) {
|
||||||
resolve_hir_path_qualifier(db, &self.resolver, &hir_path)
|
resolve_hir_path_qualifier(db, &self.resolver, &hir_path)
|
||||||
} else {
|
} else {
|
||||||
resolve_hir_path_(db, &self.resolver, &hir_path, prefer_value_ns)
|
resolve_hir_path_(
|
||||||
|
db,
|
||||||
|
&self.resolver,
|
||||||
|
&hir_path,
|
||||||
|
prefer_value_ns,
|
||||||
|
name_hygiene(db, InFile::new(self.file_id, path.syntax())),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -944,7 +955,7 @@ impl SourceAnalyzer {
|
||||||
format_args: InFile<&ast::FormatArgsExpr>,
|
format_args: InFile<&ast::FormatArgsExpr>,
|
||||||
offset: TextSize,
|
offset: TextSize,
|
||||||
) -> Option<(TextRange, Option<PathResolution>)> {
|
) -> Option<(TextRange, Option<PathResolution>)> {
|
||||||
let implicits = self.body_source_map()?.implicit_format_args(format_args)?;
|
let (hygiene, implicits) = self.body_source_map()?.implicit_format_args(format_args)?;
|
||||||
implicits.iter().find(|(range, _)| range.contains_inclusive(offset)).map(|(range, name)| {
|
implicits.iter().find(|(range, _)| range.contains_inclusive(offset)).map(|(range, name)| {
|
||||||
(
|
(
|
||||||
*range,
|
*range,
|
||||||
|
@ -956,6 +967,7 @@ impl SourceAnalyzer {
|
||||||
PathKind::Plain,
|
PathKind::Plain,
|
||||||
Some(name.clone()),
|
Some(name.clone()),
|
||||||
)),
|
)),
|
||||||
|
hygiene,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
@ -982,8 +994,8 @@ impl SourceAnalyzer {
|
||||||
db: &'a dyn HirDatabase,
|
db: &'a dyn HirDatabase,
|
||||||
format_args: InFile<&ast::FormatArgsExpr>,
|
format_args: InFile<&ast::FormatArgsExpr>,
|
||||||
) -> Option<impl Iterator<Item = (TextRange, Option<PathResolution>)> + 'a> {
|
) -> Option<impl Iterator<Item = (TextRange, Option<PathResolution>)> + 'a> {
|
||||||
Some(self.body_source_map()?.implicit_format_args(format_args)?.iter().map(
|
let (hygiene, names) = self.body_source_map()?.implicit_format_args(format_args)?;
|
||||||
move |(range, name)| {
|
Some(names.iter().map(move |(range, name)| {
|
||||||
(
|
(
|
||||||
*range,
|
*range,
|
||||||
resolve_hir_value_path(
|
resolve_hir_value_path(
|
||||||
|
@ -994,10 +1006,10 @@ impl SourceAnalyzer {
|
||||||
PathKind::Plain,
|
PathKind::Plain,
|
||||||
Some(name.clone()),
|
Some(name.clone()),
|
||||||
)),
|
)),
|
||||||
|
hygiene,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
},
|
}))
|
||||||
))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn as_asm_parts(
|
pub(crate) fn as_asm_parts(
|
||||||
|
@ -1143,8 +1155,9 @@ pub(crate) fn resolve_hir_path(
|
||||||
db: &dyn HirDatabase,
|
db: &dyn HirDatabase,
|
||||||
resolver: &Resolver,
|
resolver: &Resolver,
|
||||||
path: &Path,
|
path: &Path,
|
||||||
|
hygiene: HygieneId,
|
||||||
) -> Option<PathResolution> {
|
) -> Option<PathResolution> {
|
||||||
resolve_hir_path_(db, resolver, path, false)
|
resolve_hir_path_(db, resolver, path, false, hygiene)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -1164,6 +1177,7 @@ fn resolve_hir_path_(
|
||||||
resolver: &Resolver,
|
resolver: &Resolver,
|
||||||
path: &Path,
|
path: &Path,
|
||||||
prefer_value_ns: bool,
|
prefer_value_ns: bool,
|
||||||
|
hygiene: HygieneId,
|
||||||
) -> Option<PathResolution> {
|
) -> Option<PathResolution> {
|
||||||
let types = || {
|
let types = || {
|
||||||
let (ty, unresolved) = match path.type_anchor() {
|
let (ty, unresolved) = match path.type_anchor() {
|
||||||
|
@ -1229,7 +1243,7 @@ fn resolve_hir_path_(
|
||||||
};
|
};
|
||||||
|
|
||||||
let body_owner = resolver.body_owner();
|
let body_owner = resolver.body_owner();
|
||||||
let values = || resolve_hir_value_path(db, resolver, body_owner, path);
|
let values = || resolve_hir_value_path(db, resolver, body_owner, path, hygiene);
|
||||||
|
|
||||||
let items = || {
|
let items = || {
|
||||||
resolver
|
resolver
|
||||||
|
@ -1254,8 +1268,9 @@ fn resolve_hir_value_path(
|
||||||
resolver: &Resolver,
|
resolver: &Resolver,
|
||||||
body_owner: Option<DefWithBodyId>,
|
body_owner: Option<DefWithBodyId>,
|
||||||
path: &Path,
|
path: &Path,
|
||||||
|
hygiene: HygieneId,
|
||||||
) -> Option<PathResolution> {
|
) -> Option<PathResolution> {
|
||||||
resolver.resolve_path_in_value_ns_fully(db.upcast(), path).and_then(|val| {
|
resolver.resolve_path_in_value_ns_fully(db.upcast(), path, hygiene).and_then(|val| {
|
||||||
let res = match val {
|
let res = match val {
|
||||||
ValueNs::LocalBinding(binding_id) => {
|
ValueNs::LocalBinding(binding_id) => {
|
||||||
let var = Local { parent: body_owner?, binding_id };
|
let var = Local { parent: body_owner?, binding_id };
|
||||||
|
@ -1360,3 +1375,13 @@ fn resolve_hir_path_qualifier(
|
||||||
.map(|it| PathResolution::Def(it.into()))
|
.map(|it| PathResolution::Def(it.into()))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn name_hygiene(db: &dyn HirDatabase, name: InFile<&SyntaxNode>) -> HygieneId {
|
||||||
|
let Some(macro_file) = name.file_id.macro_file() else {
|
||||||
|
return HygieneId::ROOT;
|
||||||
|
};
|
||||||
|
let span_map = db.expansion_span_map(macro_file);
|
||||||
|
let ctx = span_map.span_at(name.value.text_range().start()).ctx;
|
||||||
|
let ctx = db.lookup_intern_syntax_context(ctx);
|
||||||
|
HygieneId::new(ctx.opaque_and_semitransparent)
|
||||||
|
}
|
||||||
|
|
|
@ -104,6 +104,36 @@ async fn foo() {
|
||||||
async fn foo() {
|
async fn foo() {
|
||||||
|| None?;
|
|| None?;
|
||||||
}
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn macro_expansion_can_refer_label_defined_before_macro_definition() {
|
||||||
|
check_diagnostics(
|
||||||
|
r#"
|
||||||
|
fn foo() {
|
||||||
|
'bar: loop {
|
||||||
|
macro_rules! m {
|
||||||
|
() => { break 'bar };
|
||||||
|
}
|
||||||
|
m!();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
check_diagnostics(
|
||||||
|
r#"
|
||||||
|
fn foo() {
|
||||||
|
'bar: loop {
|
||||||
|
macro_rules! m {
|
||||||
|
() => { break 'bar };
|
||||||
|
}
|
||||||
|
'bar: loop {
|
||||||
|
m!();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2999,6 +2999,26 @@ mod bar {
|
||||||
mod m {}
|
mod m {}
|
||||||
|
|
||||||
use foo::m;
|
use foo::m;
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn macro_label_hygiene() {
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
macro_rules! m {
|
||||||
|
($x:stmt) => {
|
||||||
|
'bar: loop { $x }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn foo() {
|
||||||
|
'bar: loop {
|
||||||
|
// ^^^^
|
||||||
|
m!(continue 'bar$0);
|
||||||
|
}
|
||||||
|
}
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue