diff --git a/crates/hir-def/src/macro_expansion_tests/builtin_derive_macro.rs b/crates/hir-def/src/macro_expansion_tests/builtin_derive_macro.rs
index abd84c6a46..553c0b7953 100644
--- a/crates/hir-def/src/macro_expansion_tests/builtin_derive_macro.rs
+++ b/crates/hir-def/src/macro_expansion_tests/builtin_derive_macro.rs
@@ -16,13 +16,12 @@ struct Foo;
#[derive(Copy)]
struct Foo;
-impl < > core::marker::Copy for Foo< > where {}"#]],
+impl < > $crate::marker::Copy for Foo< > where {}"#]],
);
}
#[test]
fn test_copy_expand_in_core() {
- cov_mark::check!(test_copy_expand_in_core);
check(
r#"
//- /lib.rs crate:core
@@ -41,7 +40,7 @@ macro Copy {}
#[derive(Copy)]
struct Foo;
-impl < > crate ::marker::Copy for Foo< > where {}"#]],
+impl < > $crate::marker::Copy for Foo< > where {}"#]],
);
}
@@ -57,7 +56,7 @@ struct Foo;
#[derive(Copy)]
struct Foo;
-impl core::marker::Copy for Foo where {}"#]],
+impl $crate::marker::Copy for Foo where {}"#]],
);
}
@@ -74,7 +73,7 @@ struct Foo;
#[derive(Copy)]
struct Foo;
-impl core::marker::Copy for Foo where {}"#]],
+impl $crate::marker::Copy for Foo where {}"#]],
);
}
@@ -98,7 +97,7 @@ enum Command {
Jump,
}
-impl core::clone::Clone for Command where {
+impl $crate::clone::Clone for Command where {
fn clone(&self ) -> Self {
match self {
Command::Move {
@@ -158,7 +157,7 @@ where
generic: Vec,
}
-impl core::clone::Clone for Foo where T: Trait, T::InFieldShorthand: core::clone::Clone, T::InGenericArg: core::clone::Clone, {
+impl $crate::clone::Clone for Foo where T: Trait, T::InFieldShorthand: $crate::clone::Clone, T::InGenericArg: $crate::clone::Clone, {
fn clone(&self ) -> Self {
match self {
Foo {
@@ -186,7 +185,7 @@ struct Foo(u32);
#[derive(Clone)]
struct Foo(u32);
-impl core::clone::Clone for Foo where {
+impl $crate::clone::Clone for Foo where {
fn clone(&self ) -> Self {
match self {
Foo(f0, )=>Foo(f0.clone(), ),
@@ -226,14 +225,14 @@ enum Bar {
Bar,
}
-impl < > core::default::Default for Foo< > where {
+impl < > $crate::default::Default for Foo< > where {
fn default() -> Self {
Foo {
- field1: core::default::Default::default(), field2: core::default::Default::default(),
+ field1: $crate::default::Default::default(), field2: $crate::default::Default::default(),
}
}
}
-impl < > core::default::Default for Bar< > where {
+impl < > $crate::default::Default for Bar< > where {
fn default() -> Self {
Bar::Bar
}
@@ -261,7 +260,7 @@ enum Command {
Jump,
}
-impl < > core::cmp::PartialEq for Command< > where {
+impl < > $crate::cmp::PartialEq for Command< > where {
fn eq(&self , other: &Self ) -> bool {
match (self , other) {
(Command::Move {
@@ -274,7 +273,7 @@ impl < > core::cmp::PartialEq for Command< > where {
}
}
}
-impl < > core::cmp::Eq for Command< > where {}"#]],
+impl < > $crate::cmp::Eq for Command< > where {}"#]],
);
}
@@ -299,7 +298,7 @@ enum Command {
Jump,
}
-impl < > core::cmp::PartialEq for Command< > where {
+impl < > $crate::cmp::PartialEq for Command< > where {
fn eq(&self , other: &Self ) -> bool {
match (self , other) {
(Command::Move {
@@ -312,7 +311,7 @@ impl < > core::cmp::PartialEq for Command< > where {
}
}
}
-impl < > core::cmp::Eq for Command< > where {}"#]],
+impl < > $crate::cmp::Eq for Command< > where {}"#]],
);
}
@@ -336,10 +335,10 @@ enum Command {
Jump,
}
-impl < > core::cmp::PartialOrd for Command< > where {
- fn partial_cmp(&self , other: &Self ) -> core::option::Option::Option {
- match core::intrinsics::discriminant_value(self ).partial_cmp(&core::intrinsics::discriminant_value(other)) {
- core::option::Option::Some(core::cmp::Ordering::Equal)=> {
+impl < > $crate::cmp::PartialOrd for Command< > where {
+ fn partial_cmp(&self , other: &Self ) -> $crate::option::Option::Option<$crate::cmp::Ordering> {
+ match $crate::intrinsics::discriminant_value(self ).partial_cmp(&$crate::intrinsics::discriminant_value(other)) {
+ $crate::option::Option::Some($crate::cmp::Ordering::Equal)=> {
match (self , other) {
(Command::Move {
x: x_self, y: y_self,
@@ -348,10 +347,10 @@ impl < > core::cmp::PartialOrd for Command< > where {
x: x_other, y: y_other,
}
)=>match x_self.partial_cmp(&x_other) {
- core::option::Option::Some(core::cmp::Ordering::Equal)=> {
+ $crate::option::Option::Some($crate::cmp::Ordering::Equal)=> {
match y_self.partial_cmp(&y_other) {
- core::option::Option::Some(core::cmp::Ordering::Equal)=> {
- core::option::Option::Some(core::cmp::Ordering::Equal)
+ $crate::option::Option::Some($crate::cmp::Ordering::Equal)=> {
+ $crate::option::Option::Some($crate::cmp::Ordering::Equal)
}
c=>return c,
}
@@ -359,22 +358,22 @@ impl < > core::cmp::PartialOrd for Command< > where {
c=>return c,
}
, (Command::Do(f0_self, ), Command::Do(f0_other, ))=>match f0_self.partial_cmp(&f0_other) {
- core::option::Option::Some(core::cmp::Ordering::Equal)=> {
- core::option::Option::Some(core::cmp::Ordering::Equal)
+ $crate::option::Option::Some($crate::cmp::Ordering::Equal)=> {
+ $crate::option::Option::Some($crate::cmp::Ordering::Equal)
}
c=>return c,
}
- , (Command::Jump, Command::Jump)=>core::option::Option::Some(core::cmp::Ordering::Equal), _unused=>core::option::Option::Some(core::cmp::Ordering::Equal)
+ , (Command::Jump, Command::Jump)=>$crate::option::Option::Some($crate::cmp::Ordering::Equal), _unused=>$crate::option::Option::Some($crate::cmp::Ordering::Equal)
}
}
c=>return c,
}
}
}
-impl < > core::cmp::Ord for Command< > where {
- fn cmp(&self , other: &Self ) -> core::cmp::Ordering {
- match core::intrinsics::discriminant_value(self ).cmp(&core::intrinsics::discriminant_value(other)) {
- core::cmp::Ordering::Equal=> {
+impl < > $crate::cmp::Ord for Command< > where {
+ fn cmp(&self , other: &Self ) -> $crate::cmp::Ordering {
+ match $crate::intrinsics::discriminant_value(self ).cmp(&$crate::intrinsics::discriminant_value(other)) {
+ $crate::cmp::Ordering::Equal=> {
match (self , other) {
(Command::Move {
x: x_self, y: y_self,
@@ -383,10 +382,10 @@ impl < > core::cmp::Ord for Command< > where {
x: x_other, y: y_other,
}
)=>match x_self.cmp(&x_other) {
- core::cmp::Ordering::Equal=> {
+ $crate::cmp::Ordering::Equal=> {
match y_self.cmp(&y_other) {
- core::cmp::Ordering::Equal=> {
- core::cmp::Ordering::Equal
+ $crate::cmp::Ordering::Equal=> {
+ $crate::cmp::Ordering::Equal
}
c=>return c,
}
@@ -394,12 +393,12 @@ impl < > core::cmp::Ord for Command< > where {
c=>return c,
}
, (Command::Do(f0_self, ), Command::Do(f0_other, ))=>match f0_self.cmp(&f0_other) {
- core::cmp::Ordering::Equal=> {
- core::cmp::Ordering::Equal
+ $crate::cmp::Ordering::Equal=> {
+ $crate::cmp::Ordering::Equal
}
c=>return c,
}
- , (Command::Jump, Command::Jump)=>core::cmp::Ordering::Equal, _unused=>core::cmp::Ordering::Equal
+ , (Command::Jump, Command::Jump)=>$crate::cmp::Ordering::Equal, _unused=>$crate::cmp::Ordering::Equal
}
}
c=>return c,
@@ -433,8 +432,8 @@ struct Foo {
z: (i32, u64),
}
-impl < > core::hash::Hash for Foo< > where {
- fn hash(&self , ra_expand_state: &mut H) {
+impl < > $crate::hash::Hash for Foo< > where {
+ fn hash(&self , ra_expand_state: &mut H) {
match self {
Foo {
x: x, y: y, z: z,
@@ -471,9 +470,9 @@ enum Command {
Jump,
}
-impl < > core::hash::Hash for Command< > where {
- fn hash(&self , ra_expand_state: &mut H) {
- core::mem::discriminant(self ).hash(ra_expand_state);
+impl < > $crate::hash::Hash for Command< > where {
+ fn hash(&self , ra_expand_state: &mut H) {
+ $crate::mem::discriminant(self ).hash(ra_expand_state);
match self {
Command::Move {
x: x, y: y,
@@ -517,8 +516,8 @@ enum Command {
Jump,
}
-impl < > core::fmt::Debug for Command< > where {
- fn fmt(&self , f: &mut core::fmt::Formatter) -> core::fmt::Result {
+impl < > $crate::fmt::Debug for Command< > where {
+ fn fmt(&self , f: &mut $crate::fmt::Formatter) -> $crate::fmt::Result {
match self {
Command::Move {
x: x, y: y,
diff --git a/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs b/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs
index d4798f4507..4690ca5d36 100644
--- a/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs
+++ b/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs
@@ -136,7 +136,7 @@ fn main() { option_env!("TEST_ENV_VAR"); }
#[rustc_builtin_macro]
macro_rules! option_env {() => {}}
-fn main() { ::core::option::Option::None:: < &str>; }
+fn main() { $crate::option::Option::None:: < &str>; }
"#]],
);
}
diff --git a/crates/hir-expand/src/builtin_derive_macro.rs b/crates/hir-expand/src/builtin_derive_macro.rs
index 8f240ef073..46bbb7f92c 100644
--- a/crates/hir-expand/src/builtin_derive_macro.rs
+++ b/crates/hir-expand/src/builtin_derive_macro.rs
@@ -1,6 +1,5 @@
//! Builtin derives.
-use base_db::{CrateOrigin, LangCrateOrigin};
use itertools::izip;
use rustc_hash::FxHashSet;
use span::{MacroCallId, Span};
@@ -10,6 +9,7 @@ use tracing::debug;
use crate::{
hygiene::span_with_def_site_ctxt,
name::{AsName, Name},
+ quote::dollar_crate,
span_map::SpanMapRef,
tt,
};
@@ -38,7 +38,7 @@ macro_rules! register_builtin {
let span = db.lookup_intern_macro_call(id).call_site;
let span = span_with_def_site_ctxt(db, span, id);
- expander(db, id, span, tt, token_map)
+ expander(span, tt, token_map)
}
fn find_by_name(name: &name::Name) -> Option {
@@ -398,41 +398,13 @@ fn expand_simple_derive(
ExpandResult::ok(expanded)
}
-fn find_builtin_crate(db: &dyn ExpandDatabase, id: MacroCallId, span: Span) -> tt::TokenTree {
- // FIXME: make hygiene works for builtin derive macro
- // such that $crate can be used here.
- let cg = db.crate_graph();
- let krate = db.lookup_intern_macro_call(id).krate;
-
- let tt = if matches!(cg[krate].origin, CrateOrigin::Lang(LangCrateOrigin::Core)) {
- cov_mark::hit!(test_copy_expand_in_core);
- quote! {span => crate }
- } else {
- quote! {span => core }
- };
-
- tt.token_trees[0].clone()
-}
-
-fn copy_expand(
- db: &dyn ExpandDatabase,
- id: MacroCallId,
- span: Span,
- tt: &ast::Adt,
- tm: SpanMapRef<'_>,
-) -> ExpandResult {
- let krate = find_builtin_crate(db, id, span);
+fn copy_expand(span: Span, tt: &ast::Adt, tm: SpanMapRef<'_>) -> ExpandResult {
+ let krate = dollar_crate(span);
expand_simple_derive(span, tt, tm, quote! {span => #krate::marker::Copy }, |_| quote! {span =>})
}
-fn clone_expand(
- db: &dyn ExpandDatabase,
- id: MacroCallId,
- span: Span,
- tt: &ast::Adt,
- tm: SpanMapRef<'_>,
-) -> ExpandResult {
- let krate = find_builtin_crate(db, id, span);
+fn clone_expand(span: Span, tt: &ast::Adt, tm: SpanMapRef<'_>) -> ExpandResult {
+ let krate = dollar_crate(span);
expand_simple_derive(span, tt, tm, quote! {span => #krate::clone::Clone }, |adt| {
if matches!(adt.shape, AdtShape::Union) {
let star = tt::Punct { char: '*', spacing: ::tt::Spacing::Alone, span };
@@ -482,14 +454,8 @@ fn and_and(span: Span) -> tt::Subtree {
quote! {span => #and& }
}
-fn default_expand(
- db: &dyn ExpandDatabase,
- id: MacroCallId,
- span: Span,
- tt: &ast::Adt,
- tm: SpanMapRef<'_>,
-) -> ExpandResult {
- let krate = &find_builtin_crate(db, id, span);
+fn default_expand(span: Span, tt: &ast::Adt, tm: SpanMapRef<'_>) -> ExpandResult {
+ let krate = &dollar_crate(span);
expand_simple_derive(span, tt, tm, quote! {span => #krate::default::Default }, |adt| {
let body = match &adt.shape {
AdtShape::Struct(fields) => {
@@ -527,14 +493,8 @@ fn default_expand(
})
}
-fn debug_expand(
- db: &dyn ExpandDatabase,
- id: MacroCallId,
- span: Span,
- tt: &ast::Adt,
- tm: SpanMapRef<'_>,
-) -> ExpandResult {
- let krate = &find_builtin_crate(db, id, span);
+fn debug_expand(span: Span, tt: &ast::Adt, tm: SpanMapRef<'_>) -> ExpandResult {
+ let krate = &dollar_crate(span);
expand_simple_derive(span, tt, tm, quote! {span => #krate::fmt::Debug }, |adt| {
let for_variant = |name: String, v: &VariantShape| match v {
VariantShape::Struct(fields) => {
@@ -605,14 +565,8 @@ fn debug_expand(
})
}
-fn hash_expand(
- db: &dyn ExpandDatabase,
- id: MacroCallId,
- span: Span,
- tt: &ast::Adt,
- tm: SpanMapRef<'_>,
-) -> ExpandResult {
- let krate = &find_builtin_crate(db, id, span);
+fn hash_expand(span: Span, tt: &ast::Adt, tm: SpanMapRef<'_>) -> ExpandResult {
+ let krate = &dollar_crate(span);
expand_simple_derive(span, tt, tm, quote! {span => #krate::hash::Hash }, |adt| {
if matches!(adt.shape, AdtShape::Union) {
// FIXME: Return expand error here
@@ -658,25 +612,13 @@ fn hash_expand(
})
}
-fn eq_expand(
- db: &dyn ExpandDatabase,
- id: MacroCallId,
- span: Span,
- tt: &ast::Adt,
- tm: SpanMapRef<'_>,
-) -> ExpandResult {
- let krate = find_builtin_crate(db, id, span);
+fn eq_expand(span: Span, tt: &ast::Adt, tm: SpanMapRef<'_>) -> ExpandResult {
+ let krate = dollar_crate(span);
expand_simple_derive(span, tt, tm, quote! {span => #krate::cmp::Eq }, |_| quote! {span =>})
}
-fn partial_eq_expand(
- db: &dyn ExpandDatabase,
- id: MacroCallId,
- span: Span,
- tt: &ast::Adt,
- tm: SpanMapRef<'_>,
-) -> ExpandResult {
- let krate = find_builtin_crate(db, id, span);
+fn partial_eq_expand(span: Span, tt: &ast::Adt, tm: SpanMapRef<'_>) -> ExpandResult {
+ let krate = dollar_crate(span);
expand_simple_derive(span, tt, tm, quote! {span => #krate::cmp::PartialEq }, |adt| {
if matches!(adt.shape, AdtShape::Union) {
// FIXME: Return expand error here
@@ -747,17 +689,11 @@ fn self_and_other_patterns(
(self_patterns, other_patterns)
}
-fn ord_expand(
- db: &dyn ExpandDatabase,
- id: MacroCallId,
- span: Span,
- tt: &ast::Adt,
- tm: SpanMapRef<'_>,
-) -> ExpandResult {
- let krate = &find_builtin_crate(db, id, span);
+fn ord_expand(span: Span, tt: &ast::Adt, tm: SpanMapRef<'_>) -> ExpandResult {
+ let krate = &dollar_crate(span);
expand_simple_derive(span, tt, tm, quote! {span => #krate::cmp::Ord }, |adt| {
fn compare(
- krate: &tt::TokenTree,
+ krate: &tt::Ident,
left: tt::Subtree,
right: tt::Subtree,
rest: tt::Subtree,
@@ -811,17 +747,11 @@ fn ord_expand(
})
}
-fn partial_ord_expand(
- db: &dyn ExpandDatabase,
- id: MacroCallId,
- span: Span,
- tt: &ast::Adt,
- tm: SpanMapRef<'_>,
-) -> ExpandResult {
- let krate = &find_builtin_crate(db, id, span);
+fn partial_ord_expand(span: Span, tt: &ast::Adt, tm: SpanMapRef<'_>) -> ExpandResult {
+ let krate = &dollar_crate(span);
expand_simple_derive(span, tt, tm, quote! {span => #krate::cmp::PartialOrd }, |adt| {
fn compare(
- krate: &tt::TokenTree,
+ krate: &tt::Ident,
left: tt::Subtree,
right: tt::Subtree,
rest: tt::Subtree,
diff --git a/crates/hir-expand/src/builtin_fn_macro.rs b/crates/hir-expand/src/builtin_fn_macro.rs
index f99a891762..65a55f8b5b 100644
--- a/crates/hir-expand/src/builtin_fn_macro.rs
+++ b/crates/hir-expand/src/builtin_fn_macro.rs
@@ -6,16 +6,14 @@ use either::Either;
use itertools::Itertools;
use mbe::{parse_exprs_with_sep, parse_to_token_tree};
use span::{Span, SpanAnchor, SyntaxContextId, ROOT_ERASED_FILE_AST_ID};
-use syntax::{
- ast::{self, AstToken},
- SmolStr,
-};
+use syntax::ast::{self, AstToken};
use crate::{
db::ExpandDatabase,
hygiene::{span_with_call_site_ctxt, span_with_def_site_ctxt},
name::{self, known},
quote,
+ quote::dollar_crate,
tt::{self, DelimSpan},
ExpandError, ExpandResult, HirFileIdExt, MacroCallId,
};
@@ -205,7 +203,7 @@ fn assert_expand(
) -> ExpandResult {
let call_site_span = span_with_call_site_ctxt(db, span, id);
let args = parse_exprs_with_sep(tt, ',', call_site_span);
- let dollar_crate = tt::Ident { text: SmolStr::new_inline("$crate"), span };
+ let dollar_crate = dollar_crate(span);
let expanded = match &*args {
[cond, panic_args @ ..] => {
let comma = tt::Subtree {
@@ -300,7 +298,7 @@ fn asm_expand(
[tt::TokenTree::Leaf(tt::Leaf::Literal(lit))]
| [tt::TokenTree::Leaf(tt::Leaf::Literal(lit)), tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct { char: ',', span: _, spacing: _ }))] =>
{
- let dollar_krate = tt::Ident { text: SmolStr::new_inline("$crate"), span };
+ let dollar_krate = dollar_crate(span);
literals.push(quote!(span=>#dollar_krate::format_args!(#lit);));
}
_ => break,
@@ -345,7 +343,7 @@ fn panic_expand(
tt: &tt::Subtree,
span: Span,
) -> ExpandResult {
- let dollar_crate = tt::Ident { text: SmolStr::new_inline("$crate"), span };
+ let dollar_crate = dollar_crate(span);
let call_site_span = span_with_call_site_ctxt(db, span, id);
let mac =
@@ -371,7 +369,7 @@ fn unreachable_expand(
tt: &tt::Subtree,
span: Span,
) -> ExpandResult {
- let dollar_crate = tt::Ident { text: SmolStr::new_inline("$crate"), span };
+ let dollar_crate = dollar_crate(span);
let call_site_span = span_with_call_site_ctxt(db, span, id);
let mac = if use_panic_2021(db, call_site_span) {
@@ -763,10 +761,10 @@ fn option_env_expand(
return ExpandResult::new(tt::Subtree::empty(DelimSpan { open: span, close: span }), e)
}
};
- // FIXME: Use `DOLLAR_CRATE` when that works in eager macros.
+ let dollar_crate = dollar_crate(span);
let expanded = match get_env_inner(db, arg_id, &key) {
- None => quote! {span => ::core::option::Option::None::<&str> },
- Some(s) => quote! {span => ::core::option::Option::Some(#s) },
+ None => quote! {span => #dollar_crate::option::Option::None::<&str> },
+ Some(s) => quote! {span => #dollar_crate::option::Option::Some(#s) },
};
ExpandResult::ok(expanded)
diff --git a/crates/hir-expand/src/quote.rs b/crates/hir-expand/src/quote.rs
index 9bdd75f9d2..a3b84afd2a 100644
--- a/crates/hir-expand/src/quote.rs
+++ b/crates/hir-expand/src/quote.rs
@@ -4,6 +4,10 @@ use span::Span;
use crate::name::Name;
+pub(crate) fn dollar_crate(span: Span) -> tt::Ident {
+ tt::Ident { text: syntax::SmolStr::new_inline("$crate"), span }
+}
+
// A helper macro quote macro
// FIXME:
// 1. Not all puncts are handled
diff --git a/crates/hir-ty/src/tests/macros.rs b/crates/hir-ty/src/tests/macros.rs
index d16e0eb013..622b4f56d4 100644
--- a/crates/hir-ty/src/tests/macros.rs
+++ b/crates/hir-ty/src/tests/macros.rs
@@ -987,15 +987,12 @@ fn infer_builtin_macros_env() {
fn infer_builtin_macros_option_env() {
check_types(
r#"
- //- minicore: option
- //- /main.rs env:foo=bar
- #[rustc_builtin_macro]
- macro_rules! option_env {() => {}}
-
- fn main() {
- let x = option_env!("foo");
- //^ Option<&str>
- }
+//- minicore: env
+//- /main.rs env:foo=bar
+fn main() {
+ let x = option_env!("foo");
+ //^ Option<&str>
+}
"#,
);
}
@@ -1014,6 +1011,21 @@ fn test() {
);
}
+#[test]
+fn infer_builtin_derive_resolves_with_core_module() {
+ check_types(
+ r#"
+//- minicore: derive, clone
+mod core {}
+#[derive(Clone)]
+struct S;
+fn test() {
+ S.clone();
+} //^^^^^^^^^ S
+"#,
+ );
+}
+
#[test]
fn infer_derive_clone_with_params() {
check_types(
diff --git a/crates/ide/src/expand_macro.rs b/crates/ide/src/expand_macro.rs
index 024053effe..17c701ad03 100644
--- a/crates/ide/src/expand_macro.rs
+++ b/crates/ide/src/expand_macro.rs
@@ -481,7 +481,7 @@ struct Foo {}
"#,
expect![[r#"
Clone
- impl < >core::clone::Clone for Foo< >where {
+ impl < >$crate::clone::Clone for Foo< >where {
fn clone(&self) -> Self {
match self {
Foo{}
@@ -507,7 +507,7 @@ struct Foo {}
"#,
expect![[r#"
Copy
- impl < >core::marker::Copy for Foo< >where{}"#]],
+ impl < >$crate::marker::Copy for Foo< >where{}"#]],
);
}
@@ -522,7 +522,7 @@ struct Foo {}
"#,
expect![[r#"
Copy
- impl < >core::marker::Copy for Foo< >where{}"#]],
+ impl < >$crate::marker::Copy for Foo< >where{}"#]],
);
check(
r#"
@@ -533,7 +533,7 @@ struct Foo {}
"#,
expect![[r#"
Clone
- impl < >core::clone::Clone for Foo< >where {
+ impl < >$crate::clone::Clone for Foo< >where {
fn clone(&self) -> Self {
match self {
Foo{}
diff --git a/crates/load-cargo/src/lib.rs b/crates/load-cargo/src/lib.rs
index 556ed73a04..ee0b9e6d50 100644
--- a/crates/load-cargo/src/lib.rs
+++ b/crates/load-cargo/src/lib.rs
@@ -331,9 +331,8 @@ fn load_crate_graph(
}
let changes = vfs.take_changes();
for file in changes {
- if file.exists() {
- let contents = vfs.file_contents(file.file_id);
- if let Ok(text) = std::str::from_utf8(contents) {
+ if let vfs::Change::Create(v) | vfs::Change::Modify(v) = file.change {
+ if let Ok(text) = std::str::from_utf8(&v) {
analysis_change.change_file(file.file_id, Some(text.into()))
}
}
diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs
index f57a27305f..da132f9396 100644
--- a/crates/rust-analyzer/src/global_state.rs
+++ b/crates/rust-analyzer/src/global_state.rs
@@ -3,7 +3,7 @@
//!
//! Each tick provides an immutable snapshot of the state as `WorldSnapshot`.
-use std::time::Instant;
+use std::{collections::hash_map::Entry, time::Instant};
use crossbeam_channel::{unbounded, Receiver, Sender};
use flycheck::FlycheckHandle;
@@ -21,7 +21,7 @@ use proc_macro_api::ProcMacroServer;
use project_model::{CargoWorkspace, ProjectWorkspace, Target, WorkspaceBuildScripts};
use rustc_hash::{FxHashMap, FxHashSet};
use triomphe::Arc;
-use vfs::{AnchoredPathBuf, Vfs};
+use vfs::{AnchoredPathBuf, ChangedFile, Vfs};
use crate::{
config::{Config, ConfigError},
@@ -217,8 +217,8 @@ impl GlobalState {
pub(crate) fn process_changes(&mut self) -> bool {
let _p = profile::span("GlobalState::process_changes");
- let mut file_changes = FxHashMap::default();
- let (change, changed_files, workspace_structure_change) = {
+ let mut file_changes = FxHashMap::<_, (bool, ChangedFile)>::default();
+ let (change, modified_files, workspace_structure_change) = {
let mut change = Change::new();
let mut guard = self.vfs.write();
let changed_files = guard.0.take_changes();
@@ -233,64 +233,63 @@ impl GlobalState {
// id that is followed by a delete we actually skip observing the file text from the
// earlier event, to avoid problems later on.
for changed_file in changed_files {
- use vfs::ChangeKind::*;
-
- file_changes
- .entry(changed_file.file_id)
- .and_modify(|(change, just_created)| {
- // None -> Delete => keep
- // Create -> Delete => collapse
- //
- match (change, just_created, changed_file.change_kind) {
+ use vfs::Change::*;
+ match file_changes.entry(changed_file.file_id) {
+ Entry::Occupied(mut o) => {
+ let (just_created, change) = o.get_mut();
+ match (&mut change.change, just_created, changed_file.change) {
// latter `Delete` wins
(change, _, Delete) => *change = Delete,
// merge `Create` with `Create` or `Modify`
- (Create, _, Create | Modify) => {}
+ (Create(prev), _, Create(new) | Modify(new)) => *prev = new,
// collapse identical `Modify`es
- (Modify, _, Modify) => {}
+ (Modify(prev), _, Modify(new)) => *prev = new,
// equivalent to `Modify`
- (change @ Delete, just_created, Create) => {
- *change = Modify;
+ (change @ Delete, just_created, Create(new)) => {
+ *change = Modify(new);
*just_created = true;
}
// shouldn't occur, but collapse into `Create`
- (change @ Delete, just_created, Modify) => {
- *change = Create;
+ (change @ Delete, just_created, Modify(new)) => {
+ *change = Create(new);
*just_created = true;
}
// shouldn't occur, but collapse into `Modify`
- (Modify, _, Create) => {}
+ (Modify(prev), _, Create(new)) => *prev = new,
}
- })
- .or_insert((
- changed_file.change_kind,
- matches!(changed_file.change_kind, Create),
- ));
+ }
+ Entry::Vacant(v) => {
+ _ = v.insert((matches!(&changed_file.change, Create(_)), changed_file))
+ }
+ }
}
let changed_files: Vec<_> = file_changes
.into_iter()
- .filter(|(_, (change_kind, just_created))| {
- !matches!((change_kind, just_created), (vfs::ChangeKind::Delete, true))
+ .filter(|(_, (just_created, change))| {
+ !(*just_created && matches!(change.change, vfs::Change::Delete))
})
- .map(|(file_id, (change_kind, _))| vfs::ChangedFile { file_id, change_kind })
+ .map(|(file_id, (_, change))| vfs::ChangedFile { file_id, ..change })
.collect();
let mut workspace_structure_change = None;
// A file was added or deleted
let mut has_structure_changes = false;
let mut bytes = vec![];
- for file in &changed_files {
+ let mut modified_files = vec![];
+ for file in changed_files {
let vfs_path = &vfs.file_path(file.file_id);
if let Some(path) = vfs_path.as_path() {
let path = path.to_path_buf();
- if reload::should_refresh_for_change(&path, file.change_kind) {
+ if reload::should_refresh_for_change(&path, file.kind()) {
workspace_structure_change = Some((path.clone(), false));
}
if file.is_created_or_deleted() {
has_structure_changes = true;
workspace_structure_change =
Some((path, self.crate_graph_file_dependencies.contains(vfs_path)));
+ } else {
+ modified_files.push(file.file_id);
}
}
@@ -299,10 +298,8 @@ impl GlobalState {
self.diagnostics.clear_native_for(file.file_id);
}
- let text = if file.exists() {
- let bytes = vfs.file_contents(file.file_id).to_vec();
-
- String::from_utf8(bytes).ok().and_then(|text| {
+ let text = if let vfs::Change::Create(v) | vfs::Change::Modify(v) = file.change {
+ String::from_utf8(v).ok().and_then(|text| {
// FIXME: Consider doing normalization in the `vfs` instead? That allows
// getting rid of some locking
let (text, line_endings) = LineEndings::normalize(text);
@@ -327,11 +324,10 @@ impl GlobalState {
let roots = self.source_root_config.partition(vfs);
change.set_roots(roots);
}
- (change, changed_files, workspace_structure_change)
+ (change, modified_files, workspace_structure_change)
};
self.analysis_host.apply_change(change);
-
{
let raw_database = self.analysis_host.raw_database();
// FIXME: ideally we should only trigger a workspace fetch for non-library changes
@@ -343,13 +339,12 @@ impl GlobalState {
force_crate_graph_reload,
);
}
- self.proc_macro_changed =
- changed_files.iter().filter(|file| !file.is_created_or_deleted()).any(|file| {
- let crates = raw_database.relevant_crates(file.file_id);
- let crate_graph = raw_database.crate_graph();
+ self.proc_macro_changed = modified_files.into_iter().any(|file_id| {
+ let crates = raw_database.relevant_crates(file_id);
+ let crate_graph = raw_database.crate_graph();
- crates.iter().any(|&krate| crate_graph[krate].is_proc_macro)
- });
+ crates.iter().any(|&krate| crate_graph[krate].is_proc_macro)
+ });
}
true
@@ -494,10 +489,6 @@ impl GlobalStateSnapshot {
})
}
- pub(crate) fn vfs_memory_usage(&self) -> usize {
- self.vfs_read().memory_usage()
- }
-
pub(crate) fn file_exists(&self, file_id: FileId) -> bool {
self.vfs.read().0.exists(file_id)
}
diff --git a/crates/rust-analyzer/src/handlers/notification.rs b/crates/rust-analyzer/src/handlers/notification.rs
index 7e6219991b..c0d35498c6 100644
--- a/crates/rust-analyzer/src/handlers/notification.rs
+++ b/crates/rust-analyzer/src/handlers/notification.rs
@@ -59,7 +59,13 @@ pub(crate) fn handle_did_open_text_document(
if let Ok(path) = from_proto::vfs_path(¶ms.text_document.uri) {
let already_exists = state
.mem_docs
- .insert(path.clone(), DocumentData::new(params.text_document.version))
+ .insert(
+ path.clone(),
+ DocumentData::new(
+ params.text_document.version,
+ params.text_document.text.clone().into_bytes(),
+ ),
+ )
.is_err();
if already_exists {
tracing::error!("duplicate DidOpenTextDocument: {}", path);
@@ -76,11 +82,12 @@ pub(crate) fn handle_did_change_text_document(
let _p = profile::span("handle_did_change_text_document");
if let Ok(path) = from_proto::vfs_path(¶ms.text_document.uri) {
- match state.mem_docs.get_mut(&path) {
+ let data = match state.mem_docs.get_mut(&path) {
Some(doc) => {
// The version passed in DidChangeTextDocument is the version after all edits are applied
// so we should apply it before the vfs is notified.
doc.version = params.text_document.version;
+ &mut doc.data
}
None => {
tracing::error!("unexpected DidChangeTextDocument: {}", path);
@@ -88,16 +95,14 @@ pub(crate) fn handle_did_change_text_document(
}
};
- let text = apply_document_changes(
+ let new_contents = apply_document_changes(
state.config.position_encoding(),
- || {
- let vfs = &state.vfs.read().0;
- let file_id = vfs.file_id(&path).unwrap();
- std::str::from_utf8(vfs.file_contents(file_id)).unwrap().into()
- },
+ std::str::from_utf8(data).unwrap(),
params.content_changes,
- );
- state.vfs.write().0.set_file_contents(path, Some(text.into_bytes()));
+ )
+ .into_bytes();
+ *data = new_contents.clone();
+ state.vfs.write().0.set_file_contents(path, Some(new_contents));
}
Ok(())
}
diff --git a/crates/rust-analyzer/src/handlers/request.rs b/crates/rust-analyzer/src/handlers/request.rs
index 13544558c5..22c7e9b050 100644
--- a/crates/rust-analyzer/src/handlers/request.rs
+++ b/crates/rust-analyzer/src/handlers/request.rs
@@ -103,7 +103,6 @@ pub(crate) fn handle_analyzer_status(
.collect::>()
);
}
- format_to!(buf, "\nVfs memory usage: {}\n", profile::Bytes::new(snap.vfs_memory_usage() as _));
buf.push_str("\nAnalysis:\n");
buf.push_str(
&snap
diff --git a/crates/rust-analyzer/src/lsp/utils.rs b/crates/rust-analyzer/src/lsp/utils.rs
index a4417e4d4a..fa5ea5b57d 100644
--- a/crates/rust-analyzer/src/lsp/utils.rs
+++ b/crates/rust-analyzer/src/lsp/utils.rs
@@ -168,7 +168,7 @@ impl GlobalState {
pub(crate) fn apply_document_changes(
encoding: PositionEncoding,
- file_contents: impl FnOnce() -> String,
+ file_contents: &str,
mut content_changes: Vec,
) -> String {
// If at least one of the changes is a full document change, use the last
@@ -179,7 +179,7 @@ pub(crate) fn apply_document_changes(
let text = mem::take(&mut content_changes[idx].text);
(text, &content_changes[idx + 1..])
}
- None => (file_contents(), &content_changes[..]),
+ None => (file_contents.to_owned(), &content_changes[..]),
};
if content_changes.is_empty() {
return text;
@@ -276,11 +276,11 @@ mod tests {
}
let encoding = PositionEncoding::Wide(WideEncoding::Utf16);
- let text = apply_document_changes(encoding, || String::new(), vec![]);
+ let text = apply_document_changes(encoding, "", vec![]);
assert_eq!(text, "");
let text = apply_document_changes(
encoding,
- || text,
+ &text,
vec![TextDocumentContentChangeEvent {
range: None,
range_length: None,
@@ -288,49 +288,46 @@ mod tests {
}],
);
assert_eq!(text, "the");
- let text = apply_document_changes(encoding, || text, c![0, 3; 0, 3 => " quick"]);
+ let text = apply_document_changes(encoding, &text, c![0, 3; 0, 3 => " quick"]);
assert_eq!(text, "the quick");
let text =
- apply_document_changes(encoding, || text, c![0, 0; 0, 4 => "", 0, 5; 0, 5 => " foxes"]);
+ apply_document_changes(encoding, &text, c![0, 0; 0, 4 => "", 0, 5; 0, 5 => " foxes"]);
assert_eq!(text, "quick foxes");
- let text = apply_document_changes(encoding, || text, c![0, 11; 0, 11 => "\ndream"]);
+ let text = apply_document_changes(encoding, &text, c![0, 11; 0, 11 => "\ndream"]);
assert_eq!(text, "quick foxes\ndream");
- let text = apply_document_changes(encoding, || text, c![1, 0; 1, 0 => "have "]);
+ let text = apply_document_changes(encoding, &text, c![1, 0; 1, 0 => "have "]);
assert_eq!(text, "quick foxes\nhave dream");
let text = apply_document_changes(
encoding,
- || text,
+ &text,
c![0, 0; 0, 0 => "the ", 1, 4; 1, 4 => " quiet", 1, 16; 1, 16 => "s\n"],
);
assert_eq!(text, "the quick foxes\nhave quiet dreams\n");
- let text = apply_document_changes(
- encoding,
- || text,
- c![0, 15; 0, 15 => "\n", 2, 17; 2, 17 => "\n"],
- );
+ let text =
+ apply_document_changes(encoding, &text, c![0, 15; 0, 15 => "\n", 2, 17; 2, 17 => "\n"]);
assert_eq!(text, "the quick foxes\n\nhave quiet dreams\n\n");
let text = apply_document_changes(
encoding,
- || text,
+ &text,
c![1, 0; 1, 0 => "DREAM", 2, 0; 2, 0 => "they ", 3, 0; 3, 0 => "DON'T THEY?"],
);
assert_eq!(text, "the quick foxes\nDREAM\nthey have quiet dreams\nDON'T THEY?\n");
let text =
- apply_document_changes(encoding, || text, c![0, 10; 1, 5 => "", 2, 0; 2, 12 => ""]);
+ apply_document_changes(encoding, &text, c![0, 10; 1, 5 => "", 2, 0; 2, 12 => ""]);
assert_eq!(text, "the quick \nthey have quiet dreams\n");
let text = String::from("❤️");
- let text = apply_document_changes(encoding, || text, c![0, 0; 0, 0 => "a"]);
+ let text = apply_document_changes(encoding, &text, c![0, 0; 0, 0 => "a"]);
assert_eq!(text, "a❤️");
let text = String::from("a\nb");
let text =
- apply_document_changes(encoding, || text, c![0, 1; 1, 0 => "\nțc", 0, 1; 1, 1 => "d"]);
+ apply_document_changes(encoding, &text, c![0, 1; 1, 0 => "\nțc", 0, 1; 1, 1 => "d"]);
assert_eq!(text, "adcb");
let text = String::from("a\nb");
let text =
- apply_document_changes(encoding, || text, c![0, 1; 1, 0 => "ț\nc", 0, 2; 0, 2 => "c"]);
+ apply_document_changes(encoding, &text, c![0, 1; 1, 0 => "ț\nc", 0, 2; 0, 2 => "c"]);
assert_eq!(text, "ațc\ncb");
}
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index cdf41c955d..356c332f90 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -576,6 +576,8 @@ impl GlobalState {
let vfs = &mut self.vfs.write().0;
for (path, contents) in files {
let path = VfsPath::from(path);
+ // if the file is in mem docs, it's managed by the client via notifications
+ // so only set it if its not in there
if !self.mem_docs.contains(&path) {
vfs.set_file_contents(path, contents);
}
diff --git a/crates/rust-analyzer/src/mem_docs.rs b/crates/rust-analyzer/src/mem_docs.rs
index 45a1dab977..6a93a0ebb4 100644
--- a/crates/rust-analyzer/src/mem_docs.rs
+++ b/crates/rust-analyzer/src/mem_docs.rs
@@ -62,10 +62,11 @@ impl MemDocs {
#[derive(Debug, Clone)]
pub(crate) struct DocumentData {
pub(crate) version: i32,
+ pub(crate) data: Vec,
}
impl DocumentData {
- pub(crate) fn new(version: i32) -> Self {
- DocumentData { version }
+ pub(crate) fn new(version: i32, data: Vec) -> Self {
+ DocumentData { version, data }
}
}
diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs
index 91dc6c2e4b..8e3fa7fa4d 100644
--- a/crates/rust-analyzer/src/reload.rs
+++ b/crates/rust-analyzer/src/reload.rs
@@ -503,10 +503,9 @@ impl GlobalState {
match vfs.file_id(&vfs_path) {
Some(file_id) => Some(file_id),
None => {
- if !self.mem_docs.contains(&vfs_path) {
- let contents = loader.handle.load_sync(path);
- vfs.set_file_contents(vfs_path.clone(), contents);
- }
+ // FIXME: Consider not loading this here?
+ let contents = loader.handle.load_sync(path);
+ vfs.set_file_contents(vfs_path.clone(), contents);
vfs.file_id(&vfs_path)
}
}
diff --git a/crates/test-utils/src/minicore.rs b/crates/test-utils/src/minicore.rs
index 1f3136404c..140bb08042 100644
--- a/crates/test-utils/src/minicore.rs
+++ b/crates/test-utils/src/minicore.rs
@@ -25,6 +25,7 @@
//! derive:
//! discriminant:
//! drop:
+//! env: option
//! eq: sized
//! error: fmt
//! fmt: result, transmute, coerce_unsized
@@ -1450,6 +1451,15 @@ mod macros {
#[macro_export]
macro_rules! concat {}
// endregion:concat
+
+ // region:env
+ #[rustc_builtin_macro]
+ #[macro_export]
+ macro_rules! env {}
+ #[rustc_builtin_macro]
+ #[macro_export]
+ macro_rules! option_env {}
+ // endregion:env
}
// region:non_zero
diff --git a/crates/vfs/src/lib.rs b/crates/vfs/src/lib.rs
index ef5b10ee9d..34a85818eb 100644
--- a/crates/vfs/src/lib.rs
+++ b/crates/vfs/src/lib.rs
@@ -1,8 +1,8 @@
//! # Virtual File System
//!
-//! VFS stores all files read by rust-analyzer. Reading file contents from VFS
-//! always returns the same contents, unless VFS was explicitly modified with
-//! [`set_file_contents`]. All changes to VFS are logged, and can be retrieved via
+//! VFS records all file changes pushed to it via [`set_file_contents`].
+//! As such it only ever stores changes, not the actual content of a file at any given moment.
+//! All file changes are logged, and can be retrieved via
//! [`take_changes`] method. The pack of changes is then pushed to `salsa` and
//! triggers incremental recomputation.
//!
@@ -84,40 +84,65 @@ impl FileId {
/// safe because `FileId` is a newtype of `u32`
impl nohash_hasher::IsEnabled for FileId {}
-/// Storage for all files read by rust-analyzer.
+/// Storage for all file changes and the file id to path mapping.
///
/// For more information see the [crate-level](crate) documentation.
#[derive(Default)]
pub struct Vfs {
interner: PathInterner,
- data: Vec