Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir-expand/src/hygiene.rs')
-rw-r--r--crates/hir-expand/src/hygiene.rs198
1 files changed, 134 insertions, 64 deletions
diff --git a/crates/hir-expand/src/hygiene.rs b/crates/hir-expand/src/hygiene.rs
index e0688178ff..f83a9bf2d6 100644
--- a/crates/hir-expand/src/hygiene.rs
+++ b/crates/hir-expand/src/hygiene.rs
@@ -2,32 +2,31 @@
//!
//! Specifically, `ast` + `Hygiene` allows you to create a `Name`. Note that, at
//! this moment, this is horribly incomplete and handles only `$crate`.
-use base_db::{span::SyntaxContextId, CrateId};
-use either::Either;
-use syntax::{
- ast::{self},
- TextRange,
-};
-use triomphe::Arc;
+use base_db::span::{MacroCallId, SyntaxContextId};
-use crate::{
- db::ExpandDatabase,
- name::{AsName, Name},
- HirFileId, InFile,
-};
+use crate::db::ExpandDatabase;
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub struct SyntaxContextData {
- // FIXME: This might only need to be Option<MacroCallId>?
- outer_expn: HirFileId,
- outer_transparency: Transparency,
- parent: SyntaxContextId,
+ pub outer_expn: Option<MacroCallId>,
+ pub outer_transparency: Transparency,
+ pub parent: SyntaxContextId,
/// This context, but with all transparent and semi-transparent expansions filtered away.
- opaque: SyntaxContextId,
+ pub opaque: SyntaxContextId,
/// This context, but with all transparent expansions filtered away.
- opaque_and_semitransparent: SyntaxContextId,
- /// Name of the crate to which `$crate` with this context would resolve.
- dollar_crate_name: Name,
+ pub opaque_and_semitransparent: SyntaxContextId,
+}
+
+impl SyntaxContextData {
+ pub fn root() -> Self {
+ SyntaxContextData {
+ outer_expn: None,
+ outer_transparency: Transparency::Opaque,
+ parent: SyntaxContextId::ROOT,
+ opaque: SyntaxContextId::ROOT,
+ opaque_and_semitransparent: SyntaxContextId::ROOT,
+ }
+ }
}
/// A property of a macro expansion that determines how identifiers
@@ -50,64 +49,135 @@ pub enum Transparency {
}
pub(super) fn apply_mark(
- _db: &dyn ExpandDatabase,
- _ctxt: SyntaxContextData,
- _file_id: HirFileId,
- _transparency: Transparency,
+ db: &dyn ExpandDatabase,
+ ctxt: SyntaxContextId,
+ call_id: MacroCallId,
+ transparency: Transparency,
) -> SyntaxContextId {
- _db.intern_syntax_context(_ctxt)
-}
-
-// pub(super) fn with_ctxt_from_mark(db: &ExpandDatabase, file_id: HirFileId) {
-// self.with_ctxt_from_mark(expn_id, Transparency::Transparent)
-// }
-// pub(super) fn with_call_site_ctxt(db: &ExpandDatabase, file_id: HirFileId) {
-// self.with_ctxt_from_mark(expn_id, Transparency::Transparent)
-// }
-
-#[derive(Clone, Debug)]
-pub struct Hygiene {}
-
-impl Hygiene {
- pub fn new(_: &dyn ExpandDatabase, _: HirFileId) -> Hygiene {
- Hygiene {}
+ if transparency == Transparency::Opaque {
+ return apply_mark_internal(db, ctxt, Some(call_id), transparency);
}
- pub fn new_unhygienic() -> Hygiene {
- Hygiene {}
- }
+ let call_site_ctxt = db.lookup_intern_macro_call(call_id).call_site;
+ let mut call_site_ctxt = if transparency == Transparency::SemiTransparent {
+ call_site_ctxt.normalize_to_macros_2_0(db)
+ } else {
+ call_site_ctxt.normalize_to_macro_rules(db)
+ };
- // FIXME: this should just return name
- pub fn name_ref_to_name(
- &self,
- _: &dyn ExpandDatabase,
- name_ref: ast::NameRef,
- ) -> Either<Name, CrateId> {
- Either::Left(name_ref.as_name())
+ if call_site_ctxt.is_root(db) {
+ return apply_mark_internal(db, ctxt, Some(call_id), transparency);
}
- pub fn local_inner_macros(&self, _: &dyn ExpandDatabase, _: ast::Path) -> Option<CrateId> {
- None
+ // Otherwise, `expn_id` is a macros 1.0 definition and the call site is in a
+ // macros 2.0 expansion, i.e., a macros 1.0 invocation is in a macros 2.0 definition.
+ //
+ // In this case, the tokens from the macros 1.0 definition inherit the hygiene
+ // at their invocation. That is, we pretend that the macros 1.0 definition
+ // was defined at its invocation (i.e., inside the macros 2.0 definition)
+ // so that the macros 2.0 definition remains hygienic.
+ //
+ // See the example at `test/ui/hygiene/legacy_interaction.rs`.
+ for (call_id, transparency) in ctxt.marks(db) {
+ call_site_ctxt = apply_mark_internal(db, call_site_ctxt, call_id, transparency);
}
+ apply_mark_internal(db, call_site_ctxt, Some(call_id), transparency)
}
-#[derive(Clone, Debug)]
-struct HygieneFrames(Arc<HygieneFrame>);
+fn apply_mark_internal(
+ db: &dyn ExpandDatabase,
+ ctxt: SyntaxContextId,
+ call_id: Option<MacroCallId>,
+ transparency: Transparency,
+) -> SyntaxContextId {
+ let syntax_context_data = db.lookup_intern_syntax_context(ctxt);
+ let mut opaque = syntax_context_data.opaque;
+ let mut opaque_and_semitransparent = syntax_context_data.opaque_and_semitransparent;
+
+ if transparency >= Transparency::Opaque {
+ let parent = opaque;
+ let new_opaque = SyntaxContextId::SELF_REF;
+ // But we can't just grab the to be allocated ID either as that would not deduplicate
+ // things!
+ // So we need a new salsa store type here ...
+ opaque = db.intern_syntax_context(SyntaxContextData {
+ outer_expn: call_id,
+ outer_transparency: transparency,
+ parent,
+ opaque: new_opaque,
+ opaque_and_semitransparent: new_opaque,
+ });
+ }
-#[derive(Clone, Debug, Eq, PartialEq)]
-pub struct HygieneFrame {}
+ if transparency >= Transparency::SemiTransparent {
+ let parent = opaque_and_semitransparent;
+ let new_opaque_and_semitransparent = SyntaxContextId::SELF_REF;
+ opaque_and_semitransparent = db.intern_syntax_context(SyntaxContextData {
+ outer_expn: call_id,
+ outer_transparency: transparency,
+ parent,
+ opaque,
+ opaque_and_semitransparent: new_opaque_and_semitransparent,
+ });
+ }
-#[derive(Debug, Clone, PartialEq, Eq)]
-struct HygieneInfo {}
+ let parent = ctxt;
+ db.intern_syntax_context(SyntaxContextData {
+ outer_expn: call_id,
+ outer_transparency: transparency,
+ parent,
+ opaque,
+ opaque_and_semitransparent,
+ })
+}
+pub trait SyntaxContextExt {
+ fn is_root(self, db: &dyn ExpandDatabase) -> bool;
+ fn normalize_to_macro_rules(self, db: &dyn ExpandDatabase) -> Self;
+ fn normalize_to_macros_2_0(self, db: &dyn ExpandDatabase) -> Self;
+ fn parent_ctxt(self, db: &dyn ExpandDatabase) -> Self;
+ fn outer_mark(self, db: &dyn ExpandDatabase) -> (Option<MacroCallId>, Transparency);
+ fn marks(self, db: &dyn ExpandDatabase) -> Vec<(Option<MacroCallId>, Transparency)>;
+}
-impl HygieneInfo {
- fn _map_ident_up(&self, _: &dyn ExpandDatabase, _: TextRange) -> Option<InFile<TextRange>> {
- None
+#[inline(always)]
+fn handle_self_ref(p: SyntaxContextId, n: SyntaxContextId) -> SyntaxContextId {
+ match n {
+ SyntaxContextId::SELF_REF => p,
+ _ => n,
}
}
-impl HygieneFrame {
- pub(crate) fn new(_: &dyn ExpandDatabase, _: HirFileId) -> HygieneFrame {
- HygieneFrame {}
+impl SyntaxContextExt for SyntaxContextId {
+ fn is_root(self, db: &dyn ExpandDatabase) -> bool {
+ db.lookup_intern_syntax_context(self).outer_expn.is_none()
+ }
+ fn normalize_to_macro_rules(self, db: &dyn ExpandDatabase) -> Self {
+ handle_self_ref(self, db.lookup_intern_syntax_context(self).opaque_and_semitransparent)
+ }
+ fn normalize_to_macros_2_0(self, db: &dyn ExpandDatabase) -> Self {
+ handle_self_ref(self, db.lookup_intern_syntax_context(self).opaque)
+ }
+ fn parent_ctxt(self, db: &dyn ExpandDatabase) -> Self {
+ db.lookup_intern_syntax_context(self).parent
+ }
+ fn outer_mark(self, db: &dyn ExpandDatabase) -> (Option<MacroCallId>, Transparency) {
+ let data = db.lookup_intern_syntax_context(self);
+ (data.outer_expn, data.outer_transparency)
+ }
+ fn marks(mut self, db: &dyn ExpandDatabase) -> Vec<(Option<MacroCallId>, Transparency)> {
+ let mut marks = Vec::new();
+ while self != SyntaxContextId::ROOT {
+ marks.push(self.outer_mark(db));
+ self = self.parent_ctxt(db);
+ }
+ marks.reverse();
+ marks
}
}
+
+// pub(super) fn with_ctxt_from_mark(db: &ExpandDatabase, file_id: HirFileId) {
+// self.with_ctxt_from_mark(expn_id, Transparency::Transparent)
+// }
+// pub(super) fn with_call_site_ctxt(db: &ExpandDatabase, file_id: HirFileId) {
+// self.with_ctxt_from_mark(expn_id, Transparency::Transparent)
+// }