Merge branch 'master' into sync-from-rust

This commit is contained in:
Laurențiu Nicola 2024-01-09 08:46:16 +02:00
commit f52f2f9c3c
17 changed files with 242 additions and 302 deletions

View file

@ -16,13 +16,12 @@ struct Foo;
#[derive(Copy)] #[derive(Copy)]
struct Foo; struct Foo;
impl < > core::marker::Copy for Foo< > where {}"#]], impl < > $crate::marker::Copy for Foo< > where {}"#]],
); );
} }
#[test] #[test]
fn test_copy_expand_in_core() { fn test_copy_expand_in_core() {
cov_mark::check!(test_copy_expand_in_core);
check( check(
r#" r#"
//- /lib.rs crate:core //- /lib.rs crate:core
@ -41,7 +40,7 @@ macro Copy {}
#[derive(Copy)] #[derive(Copy)]
struct Foo; struct Foo;
impl < > crate ::marker::Copy for Foo< > where {}"#]], impl < > $crate::marker::Copy for Foo< > where {}"#]],
); );
} }
@ -57,7 +56,7 @@ struct Foo<A, B>;
#[derive(Copy)] #[derive(Copy)]
struct Foo<A, B>; struct Foo<A, B>;
impl <A: core::marker::Copy, B: core::marker::Copy, > core::marker::Copy for Foo<A, B, > where {}"#]], impl <A: $crate::marker::Copy, B: $crate::marker::Copy, > $crate::marker::Copy for Foo<A, B, > where {}"#]],
); );
} }
@ -74,7 +73,7 @@ struct Foo<A, B, 'a, 'b>;
#[derive(Copy)] #[derive(Copy)]
struct Foo<A, B, 'a, 'b>; struct Foo<A, B, 'a, 'b>;
impl <A: core::marker::Copy, B: core::marker::Copy, > core::marker::Copy for Foo<A, B, > where {}"#]], impl <A: $crate::marker::Copy, B: $crate::marker::Copy, > $crate::marker::Copy for Foo<A, B, > where {}"#]],
); );
} }
@ -98,7 +97,7 @@ enum Command<A, B> {
Jump, Jump,
} }
impl <A: core::clone::Clone, B: core::clone::Clone, > core::clone::Clone for Command<A, B, > where { impl <A: $crate::clone::Clone, B: $crate::clone::Clone, > $crate::clone::Clone for Command<A, B, > where {
fn clone(&self ) -> Self { fn clone(&self ) -> Self {
match self { match self {
Command::Move { Command::Move {
@ -158,7 +157,7 @@ where
generic: Vec<T::InGenericArg>, generic: Vec<T::InGenericArg>,
} }
impl <T: core::clone::Clone, > core::clone::Clone for Foo<T, > where T: Trait, T::InFieldShorthand: core::clone::Clone, T::InGenericArg: core::clone::Clone, { impl <T: $crate::clone::Clone, > $crate::clone::Clone for Foo<T, > where T: Trait, T::InFieldShorthand: $crate::clone::Clone, T::InGenericArg: $crate::clone::Clone, {
fn clone(&self ) -> Self { fn clone(&self ) -> Self {
match self { match self {
Foo { Foo {
@ -186,7 +185,7 @@ struct Foo<const X: usize, T>(u32);
#[derive(Clone)] #[derive(Clone)]
struct Foo<const X: usize, T>(u32); struct Foo<const X: usize, T>(u32);
impl <const X: usize, T: core::clone::Clone, > core::clone::Clone for Foo<X, T, > where { impl <const X: usize, T: $crate::clone::Clone, > $crate::clone::Clone for Foo<X, T, > where {
fn clone(&self ) -> Self { fn clone(&self ) -> Self {
match self { match self {
Foo(f0, )=>Foo(f0.clone(), ), Foo(f0, )=>Foo(f0.clone(), ),
@ -226,14 +225,14 @@ enum Bar {
Bar, Bar,
} }
impl < > core::default::Default for Foo< > where { impl < > $crate::default::Default for Foo< > where {
fn default() -> Self { fn default() -> Self {
Foo { 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 { fn default() -> Self {
Bar::Bar Bar::Bar
} }
@ -261,7 +260,7 @@ enum Command {
Jump, Jump,
} }
impl < > core::cmp::PartialEq for Command< > where { impl < > $crate::cmp::PartialEq for Command< > where {
fn eq(&self , other: &Self ) -> bool { fn eq(&self , other: &Self ) -> bool {
match (self , other) { match (self , other) {
(Command::Move { (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, Jump,
} }
impl < > core::cmp::PartialEq for Command< > where { impl < > $crate::cmp::PartialEq for Command< > where {
fn eq(&self , other: &Self ) -> bool { fn eq(&self , other: &Self ) -> bool {
match (self , other) { match (self , other) {
(Command::Move { (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, Jump,
} }
impl < > core::cmp::PartialOrd for Command< > where { impl < > $crate::cmp::PartialOrd for Command< > where {
fn partial_cmp(&self , other: &Self ) -> core::option::Option::Option<core::cmp::Ordering> { fn partial_cmp(&self , other: &Self ) -> $crate::option::Option::Option<$crate::cmp::Ordering> {
match core::intrinsics::discriminant_value(self ).partial_cmp(&core::intrinsics::discriminant_value(other)) { match $crate::intrinsics::discriminant_value(self ).partial_cmp(&$crate::intrinsics::discriminant_value(other)) {
core::option::Option::Some(core::cmp::Ordering::Equal)=> { $crate::option::Option::Some($crate::cmp::Ordering::Equal)=> {
match (self , other) { match (self , other) {
(Command::Move { (Command::Move {
x: x_self, y: y_self, x: x_self, y: y_self,
@ -348,10 +347,10 @@ impl < > core::cmp::PartialOrd for Command< > where {
x: x_other, y: y_other, x: x_other, y: y_other,
} }
)=>match x_self.partial_cmp(&x_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) { match y_self.partial_cmp(&y_other) {
core::option::Option::Some(core::cmp::Ordering::Equal)=> { $crate::option::Option::Some($crate::cmp::Ordering::Equal)=> {
core::option::Option::Some(core::cmp::Ordering::Equal) $crate::option::Option::Some($crate::cmp::Ordering::Equal)
} }
c=>return c, c=>return c,
} }
@ -359,22 +358,22 @@ impl < > core::cmp::PartialOrd for Command< > where {
c=>return c, c=>return c,
} }
, (Command::Do(f0_self, ), Command::Do(f0_other, ))=>match f0_self.partial_cmp(&f0_other) { , (Command::Do(f0_self, ), Command::Do(f0_other, ))=>match f0_self.partial_cmp(&f0_other) {
core::option::Option::Some(core::cmp::Ordering::Equal)=> { $crate::option::Option::Some($crate::cmp::Ordering::Equal)=> {
core::option::Option::Some(core::cmp::Ordering::Equal) $crate::option::Option::Some($crate::cmp::Ordering::Equal)
} }
c=>return c, 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, c=>return c,
} }
} }
} }
impl < > core::cmp::Ord for Command< > where { impl < > $crate::cmp::Ord for Command< > where {
fn cmp(&self , other: &Self ) -> core::cmp::Ordering { fn cmp(&self , other: &Self ) -> $crate::cmp::Ordering {
match core::intrinsics::discriminant_value(self ).cmp(&core::intrinsics::discriminant_value(other)) { match $crate::intrinsics::discriminant_value(self ).cmp(&$crate::intrinsics::discriminant_value(other)) {
core::cmp::Ordering::Equal=> { $crate::cmp::Ordering::Equal=> {
match (self , other) { match (self , other) {
(Command::Move { (Command::Move {
x: x_self, y: y_self, x: x_self, y: y_self,
@ -383,10 +382,10 @@ impl < > core::cmp::Ord for Command< > where {
x: x_other, y: y_other, x: x_other, y: y_other,
} }
)=>match x_self.cmp(&x_other) { )=>match x_self.cmp(&x_other) {
core::cmp::Ordering::Equal=> { $crate::cmp::Ordering::Equal=> {
match y_self.cmp(&y_other) { match y_self.cmp(&y_other) {
core::cmp::Ordering::Equal=> { $crate::cmp::Ordering::Equal=> {
core::cmp::Ordering::Equal $crate::cmp::Ordering::Equal
} }
c=>return c, c=>return c,
} }
@ -394,12 +393,12 @@ impl < > core::cmp::Ord for Command< > where {
c=>return c, c=>return c,
} }
, (Command::Do(f0_self, ), Command::Do(f0_other, ))=>match f0_self.cmp(&f0_other) { , (Command::Do(f0_self, ), Command::Do(f0_other, ))=>match f0_self.cmp(&f0_other) {
core::cmp::Ordering::Equal=> { $crate::cmp::Ordering::Equal=> {
core::cmp::Ordering::Equal $crate::cmp::Ordering::Equal
} }
c=>return c, 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, c=>return c,
@ -433,8 +432,8 @@ struct Foo {
z: (i32, u64), z: (i32, u64),
} }
impl < > core::hash::Hash for Foo< > where { impl < > $crate::hash::Hash for Foo< > where {
fn hash<H: core::hash::Hasher>(&self , ra_expand_state: &mut H) { fn hash<H: $crate::hash::Hasher>(&self , ra_expand_state: &mut H) {
match self { match self {
Foo { Foo {
x: x, y: y, z: z, x: x, y: y, z: z,
@ -471,9 +470,9 @@ enum Command {
Jump, Jump,
} }
impl < > core::hash::Hash for Command< > where { impl < > $crate::hash::Hash for Command< > where {
fn hash<H: core::hash::Hasher>(&self , ra_expand_state: &mut H) { fn hash<H: $crate::hash::Hasher>(&self , ra_expand_state: &mut H) {
core::mem::discriminant(self ).hash(ra_expand_state); $crate::mem::discriminant(self ).hash(ra_expand_state);
match self { match self {
Command::Move { Command::Move {
x: x, y: y, x: x, y: y,
@ -517,8 +516,8 @@ enum Command {
Jump, Jump,
} }
impl < > core::fmt::Debug for Command< > where { impl < > $crate::fmt::Debug for Command< > where {
fn fmt(&self , f: &mut core::fmt::Formatter) -> core::fmt::Result { fn fmt(&self , f: &mut $crate::fmt::Formatter) -> $crate::fmt::Result {
match self { match self {
Command::Move { Command::Move {
x: x, y: y, x: x, y: y,

View file

@ -136,7 +136,7 @@ fn main() { option_env!("TEST_ENV_VAR"); }
#[rustc_builtin_macro] #[rustc_builtin_macro]
macro_rules! option_env {() => {}} macro_rules! option_env {() => {}}
fn main() { ::core::option::Option::None:: < &str>; } fn main() { $crate::option::Option::None:: < &str>; }
"#]], "#]],
); );
} }

View file

@ -1,6 +1,5 @@
//! Builtin derives. //! Builtin derives.
use base_db::{CrateOrigin, LangCrateOrigin};
use itertools::izip; use itertools::izip;
use rustc_hash::FxHashSet; use rustc_hash::FxHashSet;
use span::{MacroCallId, Span}; use span::{MacroCallId, Span};
@ -10,6 +9,7 @@ use tracing::debug;
use crate::{ use crate::{
hygiene::span_with_def_site_ctxt, hygiene::span_with_def_site_ctxt,
name::{AsName, Name}, name::{AsName, Name},
quote::dollar_crate,
span_map::SpanMapRef, span_map::SpanMapRef,
tt, tt,
}; };
@ -38,7 +38,7 @@ macro_rules! register_builtin {
let span = db.lookup_intern_macro_call(id).call_site; let span = db.lookup_intern_macro_call(id).call_site;
let span = span_with_def_site_ctxt(db, span, id); 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<Self> { fn find_by_name(name: &name::Name) -> Option<Self> {
@ -398,41 +398,13 @@ fn expand_simple_derive(
ExpandResult::ok(expanded) ExpandResult::ok(expanded)
} }
fn find_builtin_crate(db: &dyn ExpandDatabase, id: MacroCallId, span: Span) -> tt::TokenTree { fn copy_expand(span: Span, tt: &ast::Adt, tm: SpanMapRef<'_>) -> ExpandResult<tt::Subtree> {
// FIXME: make hygiene works for builtin derive macro let krate = dollar_crate(span);
// 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<tt::Subtree> {
let krate = find_builtin_crate(db, id, span);
expand_simple_derive(span, tt, tm, quote! {span => #krate::marker::Copy }, |_| quote! {span =>}) expand_simple_derive(span, tt, tm, quote! {span => #krate::marker::Copy }, |_| quote! {span =>})
} }
fn clone_expand( fn clone_expand(span: Span, tt: &ast::Adt, tm: SpanMapRef<'_>) -> ExpandResult<tt::Subtree> {
db: &dyn ExpandDatabase, let krate = dollar_crate(span);
id: MacroCallId,
span: Span,
tt: &ast::Adt,
tm: SpanMapRef<'_>,
) -> ExpandResult<tt::Subtree> {
let krate = find_builtin_crate(db, id, span);
expand_simple_derive(span, tt, tm, quote! {span => #krate::clone::Clone }, |adt| { expand_simple_derive(span, tt, tm, quote! {span => #krate::clone::Clone }, |adt| {
if matches!(adt.shape, AdtShape::Union) { if matches!(adt.shape, AdtShape::Union) {
let star = tt::Punct { char: '*', spacing: ::tt::Spacing::Alone, span }; let star = tt::Punct { char: '*', spacing: ::tt::Spacing::Alone, span };
@ -482,14 +454,8 @@ fn and_and(span: Span) -> tt::Subtree {
quote! {span => #and& } quote! {span => #and& }
} }
fn default_expand( fn default_expand(span: Span, tt: &ast::Adt, tm: SpanMapRef<'_>) -> ExpandResult<tt::Subtree> {
db: &dyn ExpandDatabase, let krate = &dollar_crate(span);
id: MacroCallId,
span: Span,
tt: &ast::Adt,
tm: SpanMapRef<'_>,
) -> ExpandResult<tt::Subtree> {
let krate = &find_builtin_crate(db, id, span);
expand_simple_derive(span, tt, tm, quote! {span => #krate::default::Default }, |adt| { expand_simple_derive(span, tt, tm, quote! {span => #krate::default::Default }, |adt| {
let body = match &adt.shape { let body = match &adt.shape {
AdtShape::Struct(fields) => { AdtShape::Struct(fields) => {
@ -527,14 +493,8 @@ fn default_expand(
}) })
} }
fn debug_expand( fn debug_expand(span: Span, tt: &ast::Adt, tm: SpanMapRef<'_>) -> ExpandResult<tt::Subtree> {
db: &dyn ExpandDatabase, let krate = &dollar_crate(span);
id: MacroCallId,
span: Span,
tt: &ast::Adt,
tm: SpanMapRef<'_>,
) -> ExpandResult<tt::Subtree> {
let krate = &find_builtin_crate(db, id, span);
expand_simple_derive(span, tt, tm, quote! {span => #krate::fmt::Debug }, |adt| { expand_simple_derive(span, tt, tm, quote! {span => #krate::fmt::Debug }, |adt| {
let for_variant = |name: String, v: &VariantShape| match v { let for_variant = |name: String, v: &VariantShape| match v {
VariantShape::Struct(fields) => { VariantShape::Struct(fields) => {
@ -605,14 +565,8 @@ fn debug_expand(
}) })
} }
fn hash_expand( fn hash_expand(span: Span, tt: &ast::Adt, tm: SpanMapRef<'_>) -> ExpandResult<tt::Subtree> {
db: &dyn ExpandDatabase, let krate = &dollar_crate(span);
id: MacroCallId,
span: Span,
tt: &ast::Adt,
tm: SpanMapRef<'_>,
) -> ExpandResult<tt::Subtree> {
let krate = &find_builtin_crate(db, id, span);
expand_simple_derive(span, tt, tm, quote! {span => #krate::hash::Hash }, |adt| { expand_simple_derive(span, tt, tm, quote! {span => #krate::hash::Hash }, |adt| {
if matches!(adt.shape, AdtShape::Union) { if matches!(adt.shape, AdtShape::Union) {
// FIXME: Return expand error here // FIXME: Return expand error here
@ -658,25 +612,13 @@ fn hash_expand(
}) })
} }
fn eq_expand( fn eq_expand(span: Span, tt: &ast::Adt, tm: SpanMapRef<'_>) -> ExpandResult<tt::Subtree> {
db: &dyn ExpandDatabase, let krate = dollar_crate(span);
id: MacroCallId,
span: Span,
tt: &ast::Adt,
tm: SpanMapRef<'_>,
) -> ExpandResult<tt::Subtree> {
let krate = find_builtin_crate(db, id, span);
expand_simple_derive(span, tt, tm, quote! {span => #krate::cmp::Eq }, |_| quote! {span =>}) expand_simple_derive(span, tt, tm, quote! {span => #krate::cmp::Eq }, |_| quote! {span =>})
} }
fn partial_eq_expand( fn partial_eq_expand(span: Span, tt: &ast::Adt, tm: SpanMapRef<'_>) -> ExpandResult<tt::Subtree> {
db: &dyn ExpandDatabase, let krate = dollar_crate(span);
id: MacroCallId,
span: Span,
tt: &ast::Adt,
tm: SpanMapRef<'_>,
) -> ExpandResult<tt::Subtree> {
let krate = find_builtin_crate(db, id, span);
expand_simple_derive(span, tt, tm, quote! {span => #krate::cmp::PartialEq }, |adt| { expand_simple_derive(span, tt, tm, quote! {span => #krate::cmp::PartialEq }, |adt| {
if matches!(adt.shape, AdtShape::Union) { if matches!(adt.shape, AdtShape::Union) {
// FIXME: Return expand error here // FIXME: Return expand error here
@ -747,17 +689,11 @@ fn self_and_other_patterns(
(self_patterns, other_patterns) (self_patterns, other_patterns)
} }
fn ord_expand( fn ord_expand(span: Span, tt: &ast::Adt, tm: SpanMapRef<'_>) -> ExpandResult<tt::Subtree> {
db: &dyn ExpandDatabase, let krate = &dollar_crate(span);
id: MacroCallId,
span: Span,
tt: &ast::Adt,
tm: SpanMapRef<'_>,
) -> ExpandResult<tt::Subtree> {
let krate = &find_builtin_crate(db, id, span);
expand_simple_derive(span, tt, tm, quote! {span => #krate::cmp::Ord }, |adt| { expand_simple_derive(span, tt, tm, quote! {span => #krate::cmp::Ord }, |adt| {
fn compare( fn compare(
krate: &tt::TokenTree, krate: &tt::Ident,
left: tt::Subtree, left: tt::Subtree,
right: tt::Subtree, right: tt::Subtree,
rest: tt::Subtree, rest: tt::Subtree,
@ -811,17 +747,11 @@ fn ord_expand(
}) })
} }
fn partial_ord_expand( fn partial_ord_expand(span: Span, tt: &ast::Adt, tm: SpanMapRef<'_>) -> ExpandResult<tt::Subtree> {
db: &dyn ExpandDatabase, let krate = &dollar_crate(span);
id: MacroCallId,
span: Span,
tt: &ast::Adt,
tm: SpanMapRef<'_>,
) -> ExpandResult<tt::Subtree> {
let krate = &find_builtin_crate(db, id, span);
expand_simple_derive(span, tt, tm, quote! {span => #krate::cmp::PartialOrd }, |adt| { expand_simple_derive(span, tt, tm, quote! {span => #krate::cmp::PartialOrd }, |adt| {
fn compare( fn compare(
krate: &tt::TokenTree, krate: &tt::Ident,
left: tt::Subtree, left: tt::Subtree,
right: tt::Subtree, right: tt::Subtree,
rest: tt::Subtree, rest: tt::Subtree,

View file

@ -6,16 +6,14 @@ use either::Either;
use itertools::Itertools; use itertools::Itertools;
use mbe::{parse_exprs_with_sep, parse_to_token_tree}; use mbe::{parse_exprs_with_sep, parse_to_token_tree};
use span::{Span, SpanAnchor, SyntaxContextId, ROOT_ERASED_FILE_AST_ID}; use span::{Span, SpanAnchor, SyntaxContextId, ROOT_ERASED_FILE_AST_ID};
use syntax::{ use syntax::ast::{self, AstToken};
ast::{self, AstToken},
SmolStr,
};
use crate::{ use crate::{
db::ExpandDatabase, db::ExpandDatabase,
hygiene::{span_with_call_site_ctxt, span_with_def_site_ctxt}, hygiene::{span_with_call_site_ctxt, span_with_def_site_ctxt},
name::{self, known}, name::{self, known},
quote, quote,
quote::dollar_crate,
tt::{self, DelimSpan}, tt::{self, DelimSpan},
ExpandError, ExpandResult, HirFileIdExt, MacroCallId, ExpandError, ExpandResult, HirFileIdExt, MacroCallId,
}; };
@ -205,7 +203,7 @@ fn assert_expand(
) -> ExpandResult<tt::Subtree> { ) -> ExpandResult<tt::Subtree> {
let call_site_span = span_with_call_site_ctxt(db, span, id); let call_site_span = span_with_call_site_ctxt(db, span, id);
let args = parse_exprs_with_sep(tt, ',', call_site_span); 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 { let expanded = match &*args {
[cond, panic_args @ ..] => { [cond, panic_args @ ..] => {
let comma = tt::Subtree { 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::Literal(lit)), tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct { char: ',', span: _, spacing: _ }))] => | [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);)); literals.push(quote!(span=>#dollar_krate::format_args!(#lit);));
} }
_ => break, _ => break,
@ -345,7 +343,7 @@ fn panic_expand(
tt: &tt::Subtree, tt: &tt::Subtree,
span: Span, span: Span,
) -> ExpandResult<tt::Subtree> { ) -> ExpandResult<tt::Subtree> {
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 call_site_span = span_with_call_site_ctxt(db, span, id);
let mac = let mac =
@ -371,7 +369,7 @@ fn unreachable_expand(
tt: &tt::Subtree, tt: &tt::Subtree,
span: Span, span: Span,
) -> ExpandResult<tt::Subtree> { ) -> ExpandResult<tt::Subtree> {
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 call_site_span = span_with_call_site_ctxt(db, span, id);
let mac = if use_panic_2021(db, call_site_span) { 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) 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) { let expanded = match get_env_inner(db, arg_id, &key) {
None => quote! {span => ::core::option::Option::None::<&str> }, None => quote! {span => #dollar_crate::option::Option::None::<&str> },
Some(s) => quote! {span => ::core::option::Option::Some(#s) }, Some(s) => quote! {span => #dollar_crate::option::Option::Some(#s) },
}; };
ExpandResult::ok(expanded) ExpandResult::ok(expanded)

View file

@ -4,6 +4,10 @@ use span::Span;
use crate::name::Name; use crate::name::Name;
pub(crate) fn dollar_crate(span: Span) -> tt::Ident<Span> {
tt::Ident { text: syntax::SmolStr::new_inline("$crate"), span }
}
// A helper macro quote macro // A helper macro quote macro
// FIXME: // FIXME:
// 1. Not all puncts are handled // 1. Not all puncts are handled

View file

@ -987,15 +987,12 @@ fn infer_builtin_macros_env() {
fn infer_builtin_macros_option_env() { fn infer_builtin_macros_option_env() {
check_types( check_types(
r#" r#"
//- minicore: option //- minicore: env
//- /main.rs env:foo=bar //- /main.rs env:foo=bar
#[rustc_builtin_macro] fn main() {
macro_rules! option_env {() => {}} let x = option_env!("foo");
//^ Option<&str>
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] #[test]
fn infer_derive_clone_with_params() { fn infer_derive_clone_with_params() {
check_types( check_types(

View file

@ -481,7 +481,7 @@ struct Foo {}
"#, "#,
expect![[r#" expect![[r#"
Clone Clone
impl < >core::clone::Clone for Foo< >where { impl < >$crate::clone::Clone for Foo< >where {
fn clone(&self) -> Self { fn clone(&self) -> Self {
match self { match self {
Foo{} Foo{}
@ -507,7 +507,7 @@ struct Foo {}
"#, "#,
expect![[r#" expect![[r#"
Copy Copy
impl < >core::marker::Copy for Foo< >where{}"#]], impl < >$crate::marker::Copy for Foo< >where{}"#]],
); );
} }
@ -522,7 +522,7 @@ struct Foo {}
"#, "#,
expect![[r#" expect![[r#"
Copy Copy
impl < >core::marker::Copy for Foo< >where{}"#]], impl < >$crate::marker::Copy for Foo< >where{}"#]],
); );
check( check(
r#" r#"
@ -533,7 +533,7 @@ struct Foo {}
"#, "#,
expect![[r#" expect![[r#"
Clone Clone
impl < >core::clone::Clone for Foo< >where { impl < >$crate::clone::Clone for Foo< >where {
fn clone(&self) -> Self { fn clone(&self) -> Self {
match self { match self {
Foo{} Foo{}

View file

@ -331,9 +331,8 @@ fn load_crate_graph(
} }
let changes = vfs.take_changes(); let changes = vfs.take_changes();
for file in changes { for file in changes {
if file.exists() { if let vfs::Change::Create(v) | vfs::Change::Modify(v) = file.change {
let contents = vfs.file_contents(file.file_id); if let Ok(text) = std::str::from_utf8(&v) {
if let Ok(text) = std::str::from_utf8(contents) {
analysis_change.change_file(file.file_id, Some(text.into())) analysis_change.change_file(file.file_id, Some(text.into()))
} }
} }

View file

@ -3,7 +3,7 @@
//! //!
//! Each tick provides an immutable snapshot of the state as `WorldSnapshot`. //! 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 crossbeam_channel::{unbounded, Receiver, Sender};
use flycheck::FlycheckHandle; use flycheck::FlycheckHandle;
@ -21,7 +21,7 @@ use proc_macro_api::ProcMacroServer;
use project_model::{CargoWorkspace, ProjectWorkspace, Target, WorkspaceBuildScripts}; use project_model::{CargoWorkspace, ProjectWorkspace, Target, WorkspaceBuildScripts};
use rustc_hash::{FxHashMap, FxHashSet}; use rustc_hash::{FxHashMap, FxHashSet};
use triomphe::Arc; use triomphe::Arc;
use vfs::{AnchoredPathBuf, Vfs}; use vfs::{AnchoredPathBuf, ChangedFile, Vfs};
use crate::{ use crate::{
config::{Config, ConfigError}, config::{Config, ConfigError},
@ -217,8 +217,8 @@ impl GlobalState {
pub(crate) fn process_changes(&mut self) -> bool { pub(crate) fn process_changes(&mut self) -> bool {
let _p = profile::span("GlobalState::process_changes"); let _p = profile::span("GlobalState::process_changes");
let mut file_changes = FxHashMap::default(); let mut file_changes = FxHashMap::<_, (bool, ChangedFile)>::default();
let (change, changed_files, workspace_structure_change) = { let (change, modified_files, workspace_structure_change) = {
let mut change = Change::new(); let mut change = Change::new();
let mut guard = self.vfs.write(); let mut guard = self.vfs.write();
let changed_files = guard.0.take_changes(); 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 // id that is followed by a delete we actually skip observing the file text from the
// earlier event, to avoid problems later on. // earlier event, to avoid problems later on.
for changed_file in changed_files { for changed_file in changed_files {
use vfs::ChangeKind::*; use vfs::Change::*;
match file_changes.entry(changed_file.file_id) {
file_changes Entry::Occupied(mut o) => {
.entry(changed_file.file_id) let (just_created, change) = o.get_mut();
.and_modify(|(change, just_created)| { match (&mut change.change, just_created, changed_file.change) {
// None -> Delete => keep
// Create -> Delete => collapse
//
match (change, just_created, changed_file.change_kind) {
// latter `Delete` wins // latter `Delete` wins
(change, _, Delete) => *change = Delete, (change, _, Delete) => *change = Delete,
// merge `Create` with `Create` or `Modify` // merge `Create` with `Create` or `Modify`
(Create, _, Create | Modify) => {} (Create(prev), _, Create(new) | Modify(new)) => *prev = new,
// collapse identical `Modify`es // collapse identical `Modify`es
(Modify, _, Modify) => {} (Modify(prev), _, Modify(new)) => *prev = new,
// equivalent to `Modify` // equivalent to `Modify`
(change @ Delete, just_created, Create) => { (change @ Delete, just_created, Create(new)) => {
*change = Modify; *change = Modify(new);
*just_created = true; *just_created = true;
} }
// shouldn't occur, but collapse into `Create` // shouldn't occur, but collapse into `Create`
(change @ Delete, just_created, Modify) => { (change @ Delete, just_created, Modify(new)) => {
*change = Create; *change = Create(new);
*just_created = true; *just_created = true;
} }
// shouldn't occur, but collapse into `Modify` // shouldn't occur, but collapse into `Modify`
(Modify, _, Create) => {} (Modify(prev), _, Create(new)) => *prev = new,
} }
}) }
.or_insert(( Entry::Vacant(v) => {
changed_file.change_kind, _ = v.insert((matches!(&changed_file.change, Create(_)), changed_file))
matches!(changed_file.change_kind, Create), }
)); }
} }
let changed_files: Vec<_> = file_changes let changed_files: Vec<_> = file_changes
.into_iter() .into_iter()
.filter(|(_, (change_kind, just_created))| { .filter(|(_, (just_created, change))| {
!matches!((change_kind, just_created), (vfs::ChangeKind::Delete, true)) !(*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(); .collect();
let mut workspace_structure_change = None; let mut workspace_structure_change = None;
// A file was added or deleted // A file was added or deleted
let mut has_structure_changes = false; let mut has_structure_changes = false;
let mut bytes = vec![]; 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); let vfs_path = &vfs.file_path(file.file_id);
if let Some(path) = vfs_path.as_path() { if let Some(path) = vfs_path.as_path() {
let path = path.to_path_buf(); 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)); workspace_structure_change = Some((path.clone(), false));
} }
if file.is_created_or_deleted() { if file.is_created_or_deleted() {
has_structure_changes = true; has_structure_changes = true;
workspace_structure_change = workspace_structure_change =
Some((path, self.crate_graph_file_dependencies.contains(vfs_path))); 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); self.diagnostics.clear_native_for(file.file_id);
} }
let text = if file.exists() { let text = if let vfs::Change::Create(v) | vfs::Change::Modify(v) = file.change {
let bytes = vfs.file_contents(file.file_id).to_vec(); String::from_utf8(v).ok().and_then(|text| {
String::from_utf8(bytes).ok().and_then(|text| {
// FIXME: Consider doing normalization in the `vfs` instead? That allows // FIXME: Consider doing normalization in the `vfs` instead? That allows
// getting rid of some locking // getting rid of some locking
let (text, line_endings) = LineEndings::normalize(text); let (text, line_endings) = LineEndings::normalize(text);
@ -327,11 +324,10 @@ impl GlobalState {
let roots = self.source_root_config.partition(vfs); let roots = self.source_root_config.partition(vfs);
change.set_roots(roots); change.set_roots(roots);
} }
(change, changed_files, workspace_structure_change) (change, modified_files, workspace_structure_change)
}; };
self.analysis_host.apply_change(change); self.analysis_host.apply_change(change);
{ {
let raw_database = self.analysis_host.raw_database(); let raw_database = self.analysis_host.raw_database();
// FIXME: ideally we should only trigger a workspace fetch for non-library changes // FIXME: ideally we should only trigger a workspace fetch for non-library changes
@ -343,13 +339,12 @@ impl GlobalState {
force_crate_graph_reload, force_crate_graph_reload,
); );
} }
self.proc_macro_changed = self.proc_macro_changed = modified_files.into_iter().any(|file_id| {
changed_files.iter().filter(|file| !file.is_created_or_deleted()).any(|file| { let crates = raw_database.relevant_crates(file_id);
let crates = raw_database.relevant_crates(file.file_id); let crate_graph = raw_database.crate_graph();
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 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 { pub(crate) fn file_exists(&self, file_id: FileId) -> bool {
self.vfs.read().0.exists(file_id) self.vfs.read().0.exists(file_id)
} }

View file

@ -59,7 +59,13 @@ pub(crate) fn handle_did_open_text_document(
if let Ok(path) = from_proto::vfs_path(&params.text_document.uri) { if let Ok(path) = from_proto::vfs_path(&params.text_document.uri) {
let already_exists = state let already_exists = state
.mem_docs .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(); .is_err();
if already_exists { if already_exists {
tracing::error!("duplicate DidOpenTextDocument: {}", path); 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"); let _p = profile::span("handle_did_change_text_document");
if let Ok(path) = from_proto::vfs_path(&params.text_document.uri) { if let Ok(path) = from_proto::vfs_path(&params.text_document.uri) {
match state.mem_docs.get_mut(&path) { let data = match state.mem_docs.get_mut(&path) {
Some(doc) => { Some(doc) => {
// The version passed in DidChangeTextDocument is the version after all edits are applied // The version passed in DidChangeTextDocument is the version after all edits are applied
// so we should apply it before the vfs is notified. // so we should apply it before the vfs is notified.
doc.version = params.text_document.version; doc.version = params.text_document.version;
&mut doc.data
} }
None => { None => {
tracing::error!("unexpected DidChangeTextDocument: {}", path); 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(), state.config.position_encoding(),
|| { std::str::from_utf8(data).unwrap(),
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()
},
params.content_changes, 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(()) Ok(())
} }

View file

@ -103,7 +103,6 @@ pub(crate) fn handle_analyzer_status(
.collect::<Vec<&AbsPath>>() .collect::<Vec<&AbsPath>>()
); );
} }
format_to!(buf, "\nVfs memory usage: {}\n", profile::Bytes::new(snap.vfs_memory_usage() as _));
buf.push_str("\nAnalysis:\n"); buf.push_str("\nAnalysis:\n");
buf.push_str( buf.push_str(
&snap &snap

View file

@ -168,7 +168,7 @@ impl GlobalState {
pub(crate) fn apply_document_changes( pub(crate) fn apply_document_changes(
encoding: PositionEncoding, encoding: PositionEncoding,
file_contents: impl FnOnce() -> String, file_contents: &str,
mut content_changes: Vec<lsp_types::TextDocumentContentChangeEvent>, mut content_changes: Vec<lsp_types::TextDocumentContentChangeEvent>,
) -> String { ) -> String {
// If at least one of the changes is a full document change, use the last // 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); let text = mem::take(&mut content_changes[idx].text);
(text, &content_changes[idx + 1..]) (text, &content_changes[idx + 1..])
} }
None => (file_contents(), &content_changes[..]), None => (file_contents.to_owned(), &content_changes[..]),
}; };
if content_changes.is_empty() { if content_changes.is_empty() {
return text; return text;
@ -276,11 +276,11 @@ mod tests {
} }
let encoding = PositionEncoding::Wide(WideEncoding::Utf16); 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, ""); assert_eq!(text, "");
let text = apply_document_changes( let text = apply_document_changes(
encoding, encoding,
|| text, &text,
vec![TextDocumentContentChangeEvent { vec![TextDocumentContentChangeEvent {
range: None, range: None,
range_length: None, range_length: None,
@ -288,49 +288,46 @@ mod tests {
}], }],
); );
assert_eq!(text, "the"); 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"); assert_eq!(text, "the quick");
let text = 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"); 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"); 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"); assert_eq!(text, "quick foxes\nhave dream");
let text = apply_document_changes( let text = apply_document_changes(
encoding, encoding,
|| text, &text,
c![0, 0; 0, 0 => "the ", 1, 4; 1, 4 => " quiet", 1, 16; 1, 16 => "s\n"], 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"); assert_eq!(text, "the quick foxes\nhave quiet dreams\n");
let text = apply_document_changes( let text =
encoding, apply_document_changes(encoding, &text, c![0, 15; 0, 15 => "\n", 2, 17; 2, 17 => "\n"]);
|| text,
c![0, 15; 0, 15 => "\n", 2, 17; 2, 17 => "\n"],
);
assert_eq!(text, "the quick foxes\n\nhave quiet dreams\n\n"); assert_eq!(text, "the quick foxes\n\nhave quiet dreams\n\n");
let text = apply_document_changes( let text = apply_document_changes(
encoding, encoding,
|| text, &text,
c![1, 0; 1, 0 => "DREAM", 2, 0; 2, 0 => "they ", 3, 0; 3, 0 => "DON'T THEY?"], 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"); assert_eq!(text, "the quick foxes\nDREAM\nthey have quiet dreams\nDON'T THEY?\n");
let text = 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"); assert_eq!(text, "the quick \nthey have quiet dreams\n");
let text = String::from("❤️"); 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❤"); assert_eq!(text, "a❤");
let text = String::from("a\nb"); let text = String::from("a\nb");
let text = 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"); assert_eq!(text, "adcb");
let text = String::from("a\nb"); let text = String::from("a\nb");
let text = 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"); assert_eq!(text, "ațc\ncb");
} }

View file

@ -576,6 +576,8 @@ impl GlobalState {
let vfs = &mut self.vfs.write().0; let vfs = &mut self.vfs.write().0;
for (path, contents) in files { for (path, contents) in files {
let path = VfsPath::from(path); 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) { if !self.mem_docs.contains(&path) {
vfs.set_file_contents(path, contents); vfs.set_file_contents(path, contents);
} }

View file

@ -62,10 +62,11 @@ impl MemDocs {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub(crate) struct DocumentData { pub(crate) struct DocumentData {
pub(crate) version: i32, pub(crate) version: i32,
pub(crate) data: Vec<u8>,
} }
impl DocumentData { impl DocumentData {
pub(crate) fn new(version: i32) -> Self { pub(crate) fn new(version: i32, data: Vec<u8>) -> Self {
DocumentData { version } DocumentData { version, data }
} }
} }

View file

@ -503,10 +503,9 @@ impl GlobalState {
match vfs.file_id(&vfs_path) { match vfs.file_id(&vfs_path) {
Some(file_id) => Some(file_id), Some(file_id) => Some(file_id),
None => { None => {
if !self.mem_docs.contains(&vfs_path) { // FIXME: Consider not loading this here?
let contents = loader.handle.load_sync(path); let contents = loader.handle.load_sync(path);
vfs.set_file_contents(vfs_path.clone(), contents); vfs.set_file_contents(vfs_path.clone(), contents);
}
vfs.file_id(&vfs_path) vfs.file_id(&vfs_path)
} }
} }

View file

@ -25,6 +25,7 @@
//! derive: //! derive:
//! discriminant: //! discriminant:
//! drop: //! drop:
//! env: option
//! eq: sized //! eq: sized
//! error: fmt //! error: fmt
//! fmt: result, transmute, coerce_unsized //! fmt: result, transmute, coerce_unsized
@ -1450,6 +1451,15 @@ mod macros {
#[macro_export] #[macro_export]
macro_rules! concat {} macro_rules! concat {}
// endregion: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 // region:non_zero

View file

@ -1,8 +1,8 @@
//! # Virtual File System //! # Virtual File System
//! //!
//! VFS stores all files read by rust-analyzer. Reading file contents from VFS //! VFS records all file changes pushed to it via [`set_file_contents`].
//! always returns the same contents, unless VFS was explicitly modified with //! As such it only ever stores changes, not the actual content of a file at any given moment.
//! [`set_file_contents`]. All changes to VFS are logged, and can be retrieved via //! All file changes are logged, and can be retrieved via
//! [`take_changes`] method. The pack of changes is then pushed to `salsa` and //! [`take_changes`] method. The pack of changes is then pushed to `salsa` and
//! triggers incremental recomputation. //! triggers incremental recomputation.
//! //!
@ -84,40 +84,65 @@ impl FileId {
/// safe because `FileId` is a newtype of `u32` /// safe because `FileId` is a newtype of `u32`
impl nohash_hasher::IsEnabled for FileId {} 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. /// For more information see the [crate-level](crate) documentation.
#[derive(Default)] #[derive(Default)]
pub struct Vfs { pub struct Vfs {
interner: PathInterner, interner: PathInterner,
data: Vec<Option<Vec<u8>>>, data: Vec<FileState>,
changes: Vec<ChangedFile>, changes: Vec<ChangedFile>,
} }
#[derive(Copy, PartialEq, PartialOrd, Clone)]
pub enum FileState {
Exists,
Deleted,
}
/// Changed file in the [`Vfs`]. /// Changed file in the [`Vfs`].
#[derive(Debug)] #[derive(Debug)]
pub struct ChangedFile { pub struct ChangedFile {
/// Id of the changed file /// Id of the changed file
pub file_id: FileId, pub file_id: FileId,
/// Kind of change /// Kind of change
pub change_kind: ChangeKind, pub change: Change,
} }
impl ChangedFile { impl ChangedFile {
/// Returns `true` if the change is not [`Delete`](ChangeKind::Delete). /// Returns `true` if the change is not [`Delete`](ChangeKind::Delete).
pub fn exists(&self) -> bool { pub fn exists(&self) -> bool {
self.change_kind != ChangeKind::Delete !matches!(self.change, Change::Delete)
} }
/// Returns `true` if the change is [`Create`](ChangeKind::Create) or /// Returns `true` if the change is [`Create`](ChangeKind::Create) or
/// [`Delete`](ChangeKind::Delete). /// [`Delete`](Change::Delete).
pub fn is_created_or_deleted(&self) -> bool { pub fn is_created_or_deleted(&self) -> bool {
matches!(self.change_kind, ChangeKind::Create | ChangeKind::Delete) matches!(self.change, Change::Create(_) | Change::Delete)
}
pub fn kind(&self) -> ChangeKind {
match self.change {
Change::Create(_) => ChangeKind::Create,
Change::Modify(_) => ChangeKind::Modify,
Change::Delete => ChangeKind::Delete,
}
} }
} }
/// Kind of [file change](ChangedFile). /// Kind of [file change](ChangedFile).
#[derive(Eq, PartialEq, Copy, Clone, Debug)] #[derive(Eq, PartialEq, Debug)]
pub enum Change {
/// The file was (re-)created
Create(Vec<u8>),
/// The file was modified
Modify(Vec<u8>),
/// The file was deleted
Delete,
}
/// Kind of [file change](ChangedFile).
#[derive(Eq, PartialEq, Debug)]
pub enum ChangeKind { pub enum ChangeKind {
/// The file was (re-)created /// The file was (re-)created
Create, Create,
@ -130,7 +155,7 @@ pub enum ChangeKind {
impl Vfs { impl Vfs {
/// Id of the given path if it exists in the `Vfs` and is not deleted. /// Id of the given path if it exists in the `Vfs` and is not deleted.
pub fn file_id(&self, path: &VfsPath) -> Option<FileId> { pub fn file_id(&self, path: &VfsPath) -> Option<FileId> {
self.interner.get(path).filter(|&it| self.get(it).is_some()) self.interner.get(path).filter(|&it| matches!(self.get(it), FileState::Exists))
} }
/// File path corresponding to the given `file_id`. /// File path corresponding to the given `file_id`.
@ -142,28 +167,13 @@ impl Vfs {
self.interner.lookup(file_id).clone() self.interner.lookup(file_id).clone()
} }
/// File content corresponding to the given `file_id`.
///
/// # Panics
///
/// Panics if the id is not present in the `Vfs`, or if the corresponding file is
/// deleted.
pub fn file_contents(&self, file_id: FileId) -> &[u8] {
self.get(file_id).as_deref().unwrap()
}
/// Returns the overall memory usage for the stored files.
pub fn memory_usage(&self) -> usize {
self.data.iter().flatten().map(|d| d.capacity()).sum()
}
/// Returns an iterator over the stored ids and their corresponding paths. /// Returns an iterator over the stored ids and their corresponding paths.
/// ///
/// This will skip deleted files. /// This will skip deleted files.
pub fn iter(&self) -> impl Iterator<Item = (FileId, &VfsPath)> + '_ { pub fn iter(&self) -> impl Iterator<Item = (FileId, &VfsPath)> + '_ {
(0..self.data.len()) (0..self.data.len())
.map(|it| FileId(it as u32)) .map(|it| FileId(it as u32))
.filter(move |&file_id| self.get(file_id).is_some()) .filter(move |&file_id| matches!(self.get(file_id), FileState::Exists))
.map(move |file_id| { .map(move |file_id| {
let path = self.interner.lookup(file_id); let path = self.interner.lookup(file_id);
(file_id, path) (file_id, path)
@ -176,28 +186,21 @@ impl Vfs {
/// ///
/// If the path does not currently exists in the `Vfs`, allocates a new /// If the path does not currently exists in the `Vfs`, allocates a new
/// [`FileId`] for it. /// [`FileId`] for it.
pub fn set_file_contents(&mut self, path: VfsPath, mut contents: Option<Vec<u8>>) -> bool { pub fn set_file_contents(&mut self, path: VfsPath, contents: Option<Vec<u8>>) -> bool {
let file_id = self.alloc_file_id(path); let file_id = self.alloc_file_id(path);
let change_kind = match (self.get(file_id), &contents) { let change_kind = match (self.get(file_id), contents) {
(None, None) => return false, (FileState::Deleted, None) => return false,
(Some(old), Some(new)) if old == new => return false, (FileState::Deleted, Some(v)) => Change::Create(v),
(None, Some(_)) => ChangeKind::Create, (FileState::Exists, None) => Change::Delete,
(Some(_), None) => ChangeKind::Delete, (FileState::Exists, Some(v)) => Change::Modify(v),
(Some(_), Some(_)) => ChangeKind::Modify,
}; };
if let Some(contents) = &mut contents { let changed_file = ChangedFile { file_id, change: change_kind };
contents.shrink_to_fit(); self.data[file_id.0 as usize] =
} if changed_file.exists() { FileState::Exists } else { FileState::Deleted };
*self.get_mut(file_id) = contents; self.changes.push(changed_file);
self.changes.push(ChangedFile { file_id, change_kind });
true true
} }
/// Returns `true` if the `Vfs` contains [changes](ChangedFile).
pub fn has_changes(&self) -> bool {
!self.changes.is_empty()
}
/// Drain and returns all the changes in the `Vfs`. /// Drain and returns all the changes in the `Vfs`.
pub fn take_changes(&mut self) -> Vec<ChangedFile> { pub fn take_changes(&mut self) -> Vec<ChangedFile> {
mem::take(&mut self.changes) mem::take(&mut self.changes)
@ -205,7 +208,7 @@ impl Vfs {
/// Provides a panic-less way to verify file_id validity. /// Provides a panic-less way to verify file_id validity.
pub fn exists(&self, file_id: FileId) -> bool { pub fn exists(&self, file_id: FileId) -> bool {
self.get(file_id).is_some() matches!(self.get(file_id), FileState::Exists)
} }
/// Returns the id associated with `path` /// Returns the id associated with `path`
@ -219,26 +222,17 @@ impl Vfs {
let file_id = self.interner.intern(path); let file_id = self.interner.intern(path);
let idx = file_id.0 as usize; let idx = file_id.0 as usize;
let len = self.data.len().max(idx + 1); let len = self.data.len().max(idx + 1);
self.data.resize_with(len, || None); self.data.resize(len, FileState::Deleted);
file_id file_id
} }
/// Returns the content associated with the given `file_id`. /// Returns the status of the file associated with the given `file_id`.
/// ///
/// # Panics /// # Panics
/// ///
/// Panics if no file is associated to that id. /// Panics if no file is associated to that id.
fn get(&self, file_id: FileId) -> &Option<Vec<u8>> { fn get(&self, file_id: FileId) -> FileState {
&self.data[file_id.0 as usize] self.data[file_id.0 as usize]
}
/// Mutably returns the content associated with the given `file_id`.
///
/// # Panics
///
/// Panics if no file is associated to that id.
fn get_mut(&mut self, file_id: FileId) -> &mut Option<Vec<u8>> {
&mut self.data[file_id.0 as usize]
} }
} }