Repository: rust-lang/rust-analyzer
Pull request #16913 (2024-03-21 19:35:21)

fix: Some file watching related vfs fixes
Fixes https://github.com/rust-lang/rust-analyzer/issues/15554, additionally it seems that client side file watching was broken on windows this entire time, this PR switches `DidChangeWatchedFilesRegistrationOptions` to use relative glob patterns which do work on windows in VSCode.

lnicola: Huh, it works for me :stuck_out_tongue:.
Veykril: Inbefore their file watcher works differently well between linux and windows
Veykril: Okay so VSCode just does not send a `workspace/didChangeWatchedFiles` notification when a file gets created even if it matches the glob pattern ... which I guess makes sense if you snapshot all existing files when we tell it what to watch? But at the same time, when will it ever send a created notification then ...?
Veykril: And no, this does not work for me whatsoever. Running `cargo add` yields no events from VSCode even 😕 
lnicola: WDYM?

```
$ touch src/foo.rs # =>

[Trace - 9:57:23 PM] Sending notification 'workspace/didChangeWatchedFiles'.
Params: {
    "changes": [
        {
            "uri": "file:///Projects/hello/src/foo.rs",
            "type": 1
        },
        {
            "uri": "file:///Projects/hello/src/foo.rs",
            "type": 1
        }
    ]
}
```
Veykril: I don't get any such events from vscode :)
lnicola: Sounds like file watching is completely broken on Windows?
Veykril: Looks like it, tried it on my linux machine quickly and there touching does sent an event
Veykril: Huh, switching over to using relative patterns works! That's weird 

Patch:
diff --git a/crates/hir-def/src/attr.rs b/crates/hir-def/src/attr.rs
index fa7730f302..21536098b8 100644
--- a/crates/hir-def/src/attr.rs
+++ b/crates/hir-def/src/attr.rs
@@ -148,12 +148,12 @@ impl Attrs {
         }
     }
 
-    pub fn lang(&self) -> Option<&str> {
+    pub fn lang(&self) -> Option<&SmolStr> {
         self.by_key("lang").string_value()
     }
 
     pub fn lang_item(&self) -> Option<LangItem> {
-        self.by_key("lang").string_value().and_then(LangItem::from_str)
+        self.by_key("lang").string_value().and_then(|it| LangItem::from_str(it))
     }
 
     pub fn has_doc_hidden(&self) -> bool {
@@ -178,7 +178,7 @@ impl Attrs {
         self.doc_exprs().flat_map(|doc_expr| doc_expr.aliases().to_vec())
     }
 
-    pub fn export_name(&self) -> Option<&str> {
+    pub fn export_name(&self) -> Option<&SmolStr> {
         self.by_key("export_name").string_value()
     }
 
@@ -565,7 +565,7 @@ impl<'attr> AttrQuery<'attr> {
         self.attrs().filter_map(|attr| attr.token_tree_value())
     }
 
-    pub fn string_value(self) -> Option<&'attr str> {
+    pub fn string_value(self) -> Option<&'attr SmolStr> {
         self.attrs().find_map(|attr| attr.string_value())
     }
 
diff --git a/crates/hir-def/src/lang_item.rs b/crates/hir-def/src/lang_item.rs
index 3a07c67842..7d98f6cfe8 100644
--- a/crates/hir-def/src/lang_item.rs
+++ b/crates/hir-def/src/lang_item.rs
@@ -192,7 +192,7 @@ impl LangItems {
 
 pub(crate) fn lang_attr(db: &dyn DefDatabase, item: AttrDefId) -> Option<LangItem> {
     let attrs = db.attrs(item);
-    attrs.by_key("lang").string_value().and_then(LangItem::from_str)
+    attrs.by_key("lang").string_value().and_then(|it| LangItem::from_str(it))
 }
 
 pub(crate) fn notable_traits_in_deps(
diff --git a/crates/hir-def/src/nameres/attr_resolution.rs b/crates/hir-def/src/nameres/attr_resolution.rs
index eb7f4c05ae..662c80edf3 100644
--- a/crates/hir-def/src/nameres/attr_resolution.rs
+++ b/crates/hir-def/src/nameres/attr_resolution.rs
@@ -136,7 +136,6 @@ pub(super) fn derive_macro_as_call_id(
     call_site: SyntaxContextId,
     krate: CrateId,
     resolver: impl Fn(path::ModPath) -> Option<(MacroId, MacroDefId)>,
-    derive_macro_id: MacroCallId,
 ) -> Result<(MacroId, MacroDefId, MacroCallId), UnresolvedMacro> {
     let (macro_id, def_id) = resolver(item_attr.path.clone())
         .filter(|(_, def_id)| def_id.is_derive())
@@ -148,7 +147,6 @@ pub(super) fn derive_macro_as_call_id(
             ast_id: item_attr.ast_id,
             derive_index: derive_pos,
             derive_attr_index,
-            derive_macro_id,
         },
         call_site,
     );
diff --git a/crates/hir-def/src/nameres/collector.rs b/crates/hir-def/src/nameres/collector.rs
index ef10b3d2da..312938e2c1 100644
--- a/crates/hir-def/src/nameres/collector.rs
+++ b/crates/hir-def/src/nameres/collector.rs
@@ -24,7 +24,7 @@ use limit::Limit;
 use rustc_hash::{FxHashMap, FxHashSet};
 use span::{Edition, ErasedFileAstId, FileAstId, Span, SyntaxContextId};
 use stdx::always;
-use syntax::ast;
+use syntax::{ast, SmolStr};
 use triomphe::Arc;
 
 use crate::{
@@ -237,8 +237,6 @@ enum MacroDirectiveKind {
         derive_attr: AttrId,
         derive_pos: usize,
         ctxt: SyntaxContextId,
-        /// The "parent" macro it is resolved to.
-        derive_macro_id: MacroCallId,
     },
     Attr {
         ast_id: AstIdWithPath<ast::Item>,
@@ -314,7 +312,7 @@ impl DefCollector<'_> {
                     }
                 }
                 () if *attr_name == hir_expand::name![crate_type] => {
-                    if let Some("proc-macro") = attr.string_value() {
+                    if let Some("proc-macro") = attr.string_value().map(SmolStr::as_str) {
                         self.is_proc_macro = true;
                     }
                 }
@@ -1148,13 +1146,7 @@ impl DefCollector<'_> {
                         return Resolved::Yes;
                     }
                 }
-                MacroDirectiveKind::Derive {
-                    ast_id,
-                    derive_attr,
-                    derive_pos,
-                    ctxt: call_site,
-                    derive_macro_id,
-                } => {
+                MacroDirectiveKind::Derive { ast_id, derive_attr, derive_pos, ctxt: call_site } => {
                     let id = derive_macro_as_call_id(
                         self.db,
                         ast_id,
@@ -1163,7 +1155,6 @@ impl DefCollector<'_> {
                         *call_site,
                         self.def_map.krate,
                         resolver,
-                        *derive_macro_id,
                     );
 
                     if let Ok((macro_id, def_id, call_id)) = id {
@@ -1233,8 +1224,6 @@ impl DefCollector<'_> {
                         _ => return Resolved::No,
                     };
 
-                    let call_id =
-                        attr_macro_as_call_id(self.db, file_ast_id, attr, self.def_map.krate, def);
                     if let MacroDefId {
                         kind:
                             MacroDefKind::BuiltInAttr(
@@ -1263,7 +1252,6 @@ impl DefCollector<'_> {
                                 return recollect_without(self);
                             }
                         };
-
                         let ast_id = ast_id.with_value(ast_adt_id);
 
                         match attr.parse_path_comma_token_tree(self.db.upcast()) {
@@ -1279,7 +1267,6 @@ impl DefCollector<'_> {
                                             derive_attr: attr.id,
                                             derive_pos: idx,
                                             ctxt: call_site.ctx,
-                                            derive_macro_id: call_id,
                                         },
                                         container: directive.container,
                                     });
@@ -1314,6 +1301,10 @@ impl DefCollector<'_> {
                         return recollect_without(self);
                     }
 
+                    // Not resolved to a derive helper or the derive attribute, so try to treat as a normal attribute.
+                    let call_id =
+                        attr_macro_as_call_id(self.db, file_ast_id, attr, self.def_map.krate, def);
+
                     // Skip #[test]/#[bench] expansion, which would merely result in more memory usage
                     // due to duplicating functions into macro expansions
                     if matches!(
@@ -1469,20 +1460,13 @@ impl DefCollector<'_> {
                         ));
                     }
                 }
-                MacroDirectiveKind::Derive {
-                    ast_id,
-                    derive_attr,
-                    derive_pos,
-                    derive_macro_id,
-                    ..
-                } => {
+                MacroDirectiveKind::Derive { ast_id, derive_attr, derive_pos, ctxt: _ } => {
                     self.def_map.diagnostics.push(DefDiagnostic::unresolved_macro_call(
                         directive.module_id,
                         MacroCallKind::Derive {
                             ast_id: ast_id.ast_id,
                             derive_attr_index: *derive_attr,
                             derive_index: *derive_pos as u32,
-                            derive_macro_id: *derive_macro_id,
                         },
                         ast_id.path.clone(),
                     ));
@@ -1918,7 +1902,7 @@ impl ModCollector<'_, '_> {
     }
 
     fn collect_module(&mut self, module_id: FileItemTreeId<Mod>, attrs: &Attrs) {
-        let path_attr = attrs.by_key("path").string_value();
+        let path_attr = attrs.by_key("path").string_value().map(SmolStr::as_str);
         let is_macro_use = attrs.by_key("macro_use").exists();
         let module = &self.item_tree[module_id];
         match &module.kind {
@@ -2162,7 +2146,7 @@ impl ModCollector<'_, '_> {
                 Some(it) => {
                     // FIXME: a hacky way to create a Name from string.
                     name = tt::Ident {
-                        text: it.into(),
+                        text: it.clone(),
                         span: Span {
                             range: syntax::TextRange::empty(syntax::TextSize::new(0)),
                             anchor: span::SpanAnchor {
diff --git a/crates/hir-expand/src/attrs.rs b/crates/hir-expand/src/attrs.rs
index f1540498f2..af3ecdcd5e 100644
--- a/crates/hir-expand/src/attrs.rs
+++ b/crates/hir-expand/src/attrs.rs
@@ -8,8 +8,8 @@ use intern::Interned;
 use mbe::{syntax_node_to_token_tree, DelimiterKind, Punct};
 use smallvec::{smallvec, SmallVec};
 use span::{Span, SyntaxContextId};
-use syntax::{ast, format_smolstr, match_ast, AstNode, AstToken, SmolStr, SyntaxNode};
-use triomphe::ThinArc;
+use syntax::{ast, match_ast, AstNode, AstToken, SmolStr, SyntaxNode};
+use triomphe::Arc;
 
 use crate::{
     db::ExpandDatabase,
@@ -22,7 +22,8 @@ use crate::{
 /// Syntactical attributes, without filtering of `cfg_attr`s.
 #[derive(Default, Debug, Clone, PartialEq, Eq)]
 pub struct RawAttrs {
-    entries: Option<ThinArc<(), Attr>>,
+    // FIXME: Make this a ThinArc
+    entries: Option<Arc<[Attr]>>,
 }
 
 impl ops::Deref for RawAttrs {
@@ -30,7 +31,7 @@ impl ops::Deref for RawAttrs {
 
     fn deref(&self) -> &[Attr] {
         match &self.entries {
-            Some(it) => &it.slice,
+            Some(it) => it,
             None => &[],
         }
     }
@@ -44,34 +45,20 @@ impl RawAttrs {
         owner: &dyn ast::HasAttrs,
         span_map: SpanMapRef<'_>,
     ) -> Self {
-        let entries: Vec<_> = collect_attrs(owner)
-            .filter_map(|(id, attr)| match attr {
-                Either::Left(attr) => {
-                    attr.meta().and_then(|meta| Attr::from_src(db, meta, span_map, id))
-                }
-                Either::Right(comment) => comment.doc_comment().map(|doc| {
-                    let span = span_map.span_for_range(comment.syntax().text_range());
-                    Attr {
-                        id,
-                        input: Some(Interned::new(AttrInput::Literal(tt::Literal {
-                            // FIXME: Escape quotes from comment content
-                            text: SmolStr::new(format_smolstr!("\"{doc}\"",)),
-                            span,
-                        }))),
-                        path: Interned::new(ModPath::from(crate::name!(doc))),
-                        ctxt: span.ctx,
-                    }
-                }),
-            })
-            .collect();
-
-        let entries = if entries.is_empty() {
-            None
-        } else {
-            Some(ThinArc::from_header_and_iter((), entries.into_iter()))
-        };
+        let entries = collect_attrs(owner).filter_map(|(id, attr)| match attr {
+            Either::Left(attr) => {
+                attr.meta().and_then(|meta| Attr::from_src(db, meta, span_map, id))
+            }
+            Either::Right(comment) => comment.doc_comment().map(|doc| Attr {
+                id,
+                input: Some(Interned::new(AttrInput::Literal(SmolStr::new(doc)))),
+                path: Interned::new(ModPath::from(crate::name!(doc))),
+                ctxt: span_map.span_for_range(comment.syntax().text_range()).ctx,
+            }),
+        });
+        let entries: Arc<[Attr]> = Arc::from_iter(entries);
 
-        RawAttrs { entries }
+        Self { entries: if entries.is_empty() { None } else { Some(entries) } }
     }
 
     pub fn from_attrs_owner(
@@ -88,20 +75,16 @@ impl RawAttrs {
             (None, entries @ Some(_)) => Self { entries },
             (Some(entries), None) => Self { entries: Some(entries.clone()) },
             (Some(a), Some(b)) => {
-                let last_ast_index = a.slice.last().map_or(0, |it| it.id.ast_index() + 1) as u32;
-                let items = a
-                    .slice
-                    .iter()
-                    .cloned()
-                    .chain(b.slice.iter().map(|it| {
+                let last_ast_index = a.last().map_or(0, |it| it.id.ast_index() + 1) as u32;
+                Self {
+                    entries: Some(Arc::from_iter(a.iter().cloned().chain(b.iter().map(|it| {
                         let mut it = it.clone();
                         it.id.id = (it.id.ast_index() as u32 + last_ast_index)
                             | (it.id.cfg_attr_index().unwrap_or(0) as u32)
                                 << AttrId::AST_INDEX_BITS;
                         it
-                    }))
-                    .collect::<Vec<_>>();
-                Self { entries: Some(ThinArc::from_header_and_iter((), items.into_iter())) }
+                    })))),
+                }
             }
         }
     }
@@ -117,47 +100,41 @@ impl RawAttrs {
         }
 
         let crate_graph = db.crate_graph();
-        let new_attrs =
-            self.iter()
-                .flat_map(|attr| -> SmallVec<[_; 1]> {
-                    let is_cfg_attr =
-                        attr.path.as_ident().map_or(false, |name| *name == crate::name![cfg_attr]);
-                    if !is_cfg_attr {
-                        return smallvec![attr.clone()];
-                    }
+        let new_attrs = Arc::from_iter(self.iter().flat_map(|attr| -> SmallVec<[_; 1]> {
+            let is_cfg_attr =
+                attr.path.as_ident().map_or(false, |name| *name == crate::name![cfg_attr]);
+            if !is_cfg_attr {
+                return smallvec![attr.clone()];
+            }
 
-                    let subtree = match attr.token_tree_value() {
-                        Some(it) => it,
-                        _ => return smallvec![attr.clone()],
-                    };
-
-                    let (cfg, parts) = match parse_cfg_attr_input(subtree) {
-                        Some(it) => it,
-                        None => return smallvec![attr.clone()],
-                    };
-                    let index = attr.id;
-                    let attrs = parts.enumerate().take(1 << AttrId::CFG_ATTR_BITS).filter_map(
-                        |(idx, attr)| Attr::from_tt(db, attr, index.with_cfg_attr(idx)),
-                    );
-
-                    let cfg_options = &crate_graph[krate].cfg_options;
-                    let cfg = Subtree { delimiter: subtree.delimiter, token_trees: Box::from(cfg) };
-                    let cfg = CfgExpr::parse(&cfg);
-                    if cfg_options.check(&cfg) == Some(false) {
-                        smallvec![]
-                    } else {
-                        cov_mark::hit!(cfg_attr_active);
-
-                        attrs.collect()
-                    }
-                })
-                .collect::<Vec<_>>();
-        let entries = if new_attrs.is_empty() {
-            None
-        } else {
-            Some(ThinArc::from_header_and_iter((), new_attrs.into_iter()))
-        };
-        RawAttrs { entries }
+            let subtree = match attr.token_tree_value() {
+                Some(it) => it,
+                _ => return smallvec![attr.clone()],
+            };
+
+            let (cfg, parts) = match parse_cfg_attr_input(subtree) {
+                Some(it) => it,
+                None => return smallvec![attr.clone()],
+            };
+            let index = attr.id;
+            let attrs = parts
+                .enumerate()
+                .take(1 << AttrId::CFG_ATTR_BITS)
+                .filter_map(|(idx, attr)| Attr::from_tt(db, attr, index.with_cfg_attr(idx)));
+
+            let cfg_options = &crate_graph[krate].cfg_options;
+            let cfg = Subtree { delimiter: subtree.delimiter, token_trees: Box::from(cfg) };
+            let cfg = CfgExpr::parse(&cfg);
+            if cfg_options.check(&cfg) == Some(false) {
+                smallvec![]
+            } else {
+                cov_mark::hit!(cfg_attr_active);
+
+                attrs.collect()
+            }
+        }));
+
+        RawAttrs { entries: Some(new_attrs) }
     }
 }
 
@@ -202,7 +179,8 @@ pub struct Attr {
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
 pub enum AttrInput {
     /// `#[attr = "string"]`
-    Literal(tt::Literal),
+    // FIXME: This is losing span
+    Literal(SmolStr),
     /// `#[attr(subtree)]`
     TokenTree(Box<tt::Subtree>),
 }
@@ -210,7 +188,7 @@ pub enum AttrInput {
 impl fmt::Display for AttrInput {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         match self {
-            AttrInput::Literal(lit) => write!(f, " = {lit}"),
+            AttrInput::Literal(lit) => write!(f, " = \"{}\"", lit.escape_debug()),
             AttrInput::TokenTree(tt) => tt.fmt(f),
         }
     }
@@ -230,10 +208,11 @@ impl Attr {
         })?);
         let span = span_map.span_for_range(range);
         let input = if let Some(ast::Expr::Literal(lit)) = ast.expr() {
-            Some(Interned::new(AttrInput::Literal(tt::Literal {
-                text: lit.token().text().into(),
-                span,
-            })))
+            let value = match lit.kind() {
+                ast::LiteralKind::String(string) => string.value()?.into(),
+                _ => lit.syntax().first_token()?.text().trim_matches('"').into(),
+            };
+            Some(Interned::new(AttrInput::Literal(value)))
         } else if let Some(tt) = ast.token_tree() {
             let tree = syntax_node_to_token_tree(tt.syntax(), span_map, span);
             Some(Interned::new(AttrInput::TokenTree(Box::new(tree))))
@@ -266,8 +245,9 @@ impl Attr {
             }
             Some(tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct { char: '=', .. }))) => {
                 let input = match input.get(1) {
-                    Some(tt::TokenTree::Leaf(tt::Leaf::Literal(lit))) => {
-                        Some(Interned::new(AttrInput::Literal(lit.clone())))
+                    Some(tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal { text, .. }))) => {
+                        //FIXME the trimming here isn't quite right, raw strings are not handled
+                        Some(Interned::new(AttrInput::Literal(text.trim_matches('"').into())))
                     }
                     _ => None,
                 };
@@ -285,14 +265,9 @@ impl Attr {
 
 impl Attr {
     /// #[path = "string"]
-    pub fn string_value(&self) -> Option<&str> {
+    pub fn string_value(&self) -> Option<&SmolStr> {
         match self.input.as_deref()? {
-            AttrInput::Literal(it) => match it.text.strip_prefix('r') {
-                Some(it) => it.trim_matches('#'),
-                None => it.text.as_str(),
-            }
-            .strip_prefix('"')?
-            .strip_suffix('"'),
+            AttrInput::Literal(it) => Some(it),
             _ => None,
         }
     }
diff --git a/crates/hir-expand/src/cfg_process.rs b/crates/hir-expand/src/cfg_process.rs
index db3558a84e..68de05fafe 100644
--- a/crates/hir-expand/src/cfg_process.rs
+++ b/crates/hir-expand/src/cfg_process.rs
@@ -10,7 +10,7 @@ use syntax::{
 use tracing::{debug, warn};
 use tt::SmolStr;
 
-use crate::{db::ExpandDatabase, proc_macro::ProcMacroKind, MacroCallLoc, MacroDefKind};
+use crate::{db::ExpandDatabase, MacroCallKind, MacroCallLoc};
 
 fn check_cfg_attr(attr: &Attr, loc: &MacroCallLoc, db: &dyn ExpandDatabase) -> Option<bool> {
     if !attr.simple_name().as_deref().map(|v| v == "cfg")? {
@@ -180,13 +180,7 @@ pub(crate) fn process_cfg_attrs(
     db: &dyn ExpandDatabase,
 ) -> Option<FxHashSet<SyntaxElement>> {
     // FIXME: #[cfg_eval] is not implemented. But it is not stable yet
-    let is_derive = match loc.def.kind {
-        MacroDefKind::BuiltInDerive(..)
-        | MacroDefKind::ProcMacro(_, ProcMacroKind::CustomDerive, _) => true,
-        MacroDefKind::BuiltInAttr(expander, _) => expander.is_derive(),
-        _ => false,
-    };
-    if !is_derive {
+    if !matches!(loc.kind, MacroCallKind::Derive { .. }) {
         return None;
     }
     let mut remove = FxHashSet::default();
diff --git a/crates/hir-expand/src/db.rs b/crates/hir-expand/src/db.rs
index 5461c1c49a..ec68f2f96e 100644
--- a/crates/hir-expand/src/db.rs
+++ b/crates/hir-expand/src/db.rs
@@ -24,8 +24,7 @@ use crate::{
     HirFileId, HirFileIdRepr, MacroCallId, MacroCallKind, MacroCallLoc, MacroDefId, MacroDefKind,
     MacroFileId,
 };
-/// This is just to ensure the types of smart_macro_arg and macro_arg are the same
-type MacroArgResult = (Arc<tt::Subtree>, SyntaxFixupUndoInfo, Span);
+
 /// Total limit on the number of tokens produced by any macro invocation.
 ///
 /// If an invocation produces more tokens than this limit, it will not be stored in the database and
@@ -99,13 +98,7 @@ pub trait ExpandDatabase: SourceDatabase {
     /// Lowers syntactic macro call to a token tree representation. That's a firewall
     /// query, only typing in the macro call itself changes the returned
     /// subtree.
-    fn macro_arg(&self, id: MacroCallId) -> MacroArgResult;
-    #[salsa::transparent]
-    fn macro_arg_considering_derives(
-        &self,
-        id: MacroCallId,
-        kind: &MacroCallKind,
-    ) -> MacroArgResult;
+    fn macro_arg(&self, id: MacroCallId) -> (Arc<tt::Subtree>, SyntaxFixupUndoInfo, Span);
     /// Fetches the expander for this macro.
     #[salsa::transparent]
     #[salsa::invoke(TokenExpander::macro_expander)]
@@ -151,7 +144,7 @@ pub fn expand_speculative(
     let span_map = RealSpanMap::absolute(FileId::BOGUS);
     let span_map = SpanMapRef::RealSpanMap(&span_map);
 
-    let (_, _, span) = db.macro_arg_considering_derives(actual_macro_call, &loc.kind);
+    let (_, _, span) = db.macro_arg(actual_macro_call);
 
     // Build the subtree and token mapping for the speculative args
     let (mut tt, undo_info) = match loc.kind {
@@ -346,24 +339,12 @@ pub(crate) fn parse_with_map(
     }
 }
 
-/// This resolves the [MacroCallId] to check if it is a derive macro if so get the [macro_arg] for the derive.
-/// Other wise return the [macro_arg] for the macro_call_id.
-///
-/// This is not connected to the database so it does not cached the result. However, the inner [macro_arg] query is
-fn macro_arg_considering_derives(
+// FIXME: for derive attributes, this will return separate copies of the same structures! Though
+// they may differ in spans due to differing call sites...
+fn macro_arg(
     db: &dyn ExpandDatabase,
     id: MacroCallId,
-    kind: &MacroCallKind,
-) -> MacroArgResult {
-    match kind {
-        // Get the macro arg for the derive macro
-        MacroCallKind::Derive { derive_macro_id, .. } => db.macro_arg(*derive_macro_id),
-        // Normal macro arg
-        _ => db.macro_arg(id),
-    }
-}
-
-fn macro_arg(db: &dyn ExpandDatabase, id: MacroCallId) -> MacroArgResult {
+) -> (Arc<tt::Subtree>, SyntaxFixupUndoInfo, Span) {
     let loc = db.lookup_intern_macro_call(id);
 
     if let MacroCallLoc {
@@ -433,30 +414,29 @@ fn macro_arg(db: &dyn ExpandDatabase, id: MacroCallId) -> MacroArgResult {
             }
             return (Arc::new(tt), SyntaxFixupUndoInfo::NONE, span);
         }
-        // MacroCallKind::Derive should not be here. As we are getting the argument for the derive macro
-        MacroCallKind::Derive { .. } => {
-            unreachable!("`ExpandDatabase::macro_arg` called with `MacroCallKind::Derive`")
+        MacroCallKind::Derive { ast_id, derive_attr_index, .. } => {
+            let node = ast_id.to_ptr(db).to_node(&root);
+            let censor_derive_input = censor_derive_input(derive_attr_index, &node);
+            let item_node = node.into();
+            let attr_source = attr_source(derive_attr_index, &item_node);
+            // FIXME: This is wrong, this should point to the path of the derive attribute`
+            let span =
+                map.span_for_range(attr_source.as_ref().and_then(|it| it.path()).map_or_else(
+                    || item_node.syntax().text_range(),
+                    |it| it.syntax().text_range(),
+                ));
+            (censor_derive_input, item_node, span)
         }
         MacroCallKind::Attr { ast_id, invoc_attr_index, .. } => {
             let node = ast_id.to_ptr(db).to_node(&root);
             let attr_source = attr_source(invoc_attr_index, &node);
-
             let span = map.span_for_range(
                 attr_source
                     .as_ref()
                     .and_then(|it| it.path())
                     .map_or_else(|| node.syntax().text_range(), |it| it.syntax().text_range()),
             );
-            // If derive attribute we need to censor the derive input
-            if matches!(loc.def.kind, MacroDefKind::BuiltInAttr(expander, ..) if expander.is_derive())
-                && ast::Adt::can_cast(node.syntax().kind())
-            {
-                let adt = ast::Adt::cast(node.syntax().clone()).unwrap();
-                let censor_derive_input = censor_derive_input(invoc_attr_index, &adt);
-                (censor_derive_input, node, span)
-            } else {
-                (attr_source.into_iter().map(|it| it.syntax().clone().into()).collect(), node, span)
-            }
+            (attr_source.into_iter().map(|it| it.syntax().clone().into()).collect(), node, span)
         }
     };
 
@@ -546,8 +526,7 @@ fn macro_expand(
     let (ExpandResult { value: tt, err }, span) = match loc.def.kind {
         MacroDefKind::ProcMacro(..) => return db.expand_proc_macro(macro_call_id).map(CowArc::Arc),
         _ => {
-            let (macro_arg, undo_info, span) =
-                db.macro_arg_considering_derives(macro_call_id, &loc.kind);
+            let (macro_arg, undo_info, span) = db.macro_arg(macro_call_id);
 
             let arg = &*macro_arg;
             let res =
@@ -624,7 +603,7 @@ fn proc_macro_span(db: &dyn ExpandDatabase, ast: AstId<ast::Fn>) -> Span {
 
 fn expand_proc_macro(db: &dyn ExpandDatabase, id: MacroCallId) -> ExpandResult<Arc<tt::Subtree>> {
     let loc = db.lookup_intern_macro_call(id);
-    let (macro_arg, undo_info, span) = db.macro_arg_considering_derives(id, &loc.kind);
+    let (macro_arg, undo_info, span) = db.macro_arg(id);
 
     let (expander, ast) = match loc.def.kind {
         MacroDefKind::ProcMacro(expander, _, ast) => (expander, ast),
diff --git a/crates/hir-expand/src/lib.rs b/crates/hir-expand/src/lib.rs
index e7a313c68c..2233ae51fa 100644
--- a/crates/hir-expand/src/lib.rs
+++ b/crates/hir-expand/src/lib.rs
@@ -224,9 +224,6 @@ pub enum MacroCallKind {
         derive_attr_index: AttrId,
         /// Index of the derive macro in the derive attribute
         derive_index: u32,
-        /// The "parent" macro call.
-        /// We will resolve the same token tree for all derive macros in the same derive attribute.
-        derive_macro_id: MacroCallId,
     },
     Attr {
         ast_id: AstId<ast::Item>,
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index 13949fa78f..15a0967b3d 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -972,7 +972,7 @@ fn precise_macro_call_location(
                 MacroKind::ProcMacro,
             )
         }
-        MacroCallKind::Derive { ast_id, derive_attr_index, derive_index, .. } => {
+        MacroCallKind::Derive { ast_id, derive_attr_index, derive_index } => {
             let node = ast_id.to_node(db.upcast());
             // Compute the precise location of the macro name's token in the derive
             // list.
@@ -2008,7 +2008,8 @@ impl Function {
         }
         let data = db.function_data(self.id);
 
-        data.name.to_smol_str() == "main" || data.attrs.export_name() == Some("main")
+        data.name.to_smol_str() == "main"
+            || data.attrs.export_name().map(core::ops::Deref::deref) == Some("main")
     }
 
     /// Does this function have the ignore attribute?
@@ -3708,7 +3709,7 @@ impl Impl {
         let macro_file = src.file_id.macro_file()?;
         let loc = macro_file.macro_call_id.lookup(db.upcast());
         let (derive_attr, derive_index) = match loc.kind {
-            MacroCallKind::Derive { ast_id, derive_attr_index, derive_index, .. } => {
+            MacroCallKind::Derive { ast_id, derive_attr_index, derive_index } => {
                 let module_id = self.id.lookup(db.upcast()).container;
                 (
                     db.crate_def_map(module_id.krate())[module_id.local_id]
diff --git a/crates/ide-completion/src/context.rs b/crates/ide-completion/src/context.rs
index 995e3f4825..aa22155fef 100644
--- a/crates/ide-completion/src/context.rs
+++ b/crates/ide-completion/src/context.rs
@@ -540,7 +540,7 @@ impl CompletionContext<'_> {
     /// Whether the given trait is an operator trait or not.
     pub(crate) fn is_ops_trait(&self, trait_: hir::Trait) -> bool {
         match trait_.attrs(self.db).lang() {
-            Some(lang) => OP_TRAIT_LANG_NAMES.contains(&lang),
+            Some(lang) => OP_TRAIT_LANG_NAMES.contains(&lang.as_str()),
             None => false,
         }
     }
diff --git a/crates/ide/src/status.rs b/crates/ide/src/status.rs
index 8e7767c8e5..c3d85e3893 100644
--- a/crates/ide/src/status.rs
+++ b/crates/ide/src/status.rs
@@ -10,7 +10,7 @@ use ide_db::{
             debug::{DebugQueryTable, TableEntry},
             Query, QueryTable,
         },
-        CompressedFileTextQuery, CrateData, FileId, ParseQuery, SourceDatabase, SourceRootId,
+        CrateData, FileId, FileTextQuery, ParseQuery, SourceDatabase, SourceRootId,
     },
     symbol_index::ModuleSymbolsQuery,
 };
@@ -38,7 +38,7 @@ use triomphe::Arc;
 pub(crate) fn status(db: &RootDatabase, file_id: Option<FileId>) -> String {
     let mut buf = String::new();
 
-    format_to!(buf, "{}\n", collect_query(CompressedFileTextQuery.in_db(db)));
+    format_to!(buf, "{}\n", collect_query(FileTextQuery.in_db(db)));
     format_to!(buf, "{}\n", collect_query(ParseQuery.in_db(db)));
     format_to!(buf, "{}\n", collect_query(ParseMacroExpansionQuery.in_db(db)));
     format_to!(buf, "{}\n", collect_query(LibrarySymbolsQuery.in_db(db)));
@@ -160,7 +160,7 @@ impl QueryCollect for ParseMacroExpansionQuery {
     type Collector = SyntaxTreeStats<true>;
 }
 
-impl QueryCollect for CompressedFileTextQuery {
+impl QueryCollect for FileTextQuery {
     type Collector = FilesStats;
 }
 
@@ -188,8 +188,8 @@ impl fmt::Display for FilesStats {
     }
 }
 
-impl StatCollect<FileId, Arc<[u8]>> for FilesStats {
-    fn collect_entry(&mut self, _: FileId, value: Option<Arc<[u8]>>) {
+impl StatCollect<FileId, Arc<str>> for FilesStats {
+    fn collect_entry(&mut self, _: FileId, value: Option<Arc<str>>) {
         self.total += 1;
         self.size += value.unwrap().len();
     }
diff --git a/crates/rust-analyzer/src/handlers/notification.rs b/crates/rust-analyzer/src/handlers/notification.rs
index ff213748b4..b5c4a4f435 100644
--- a/crates/rust-analyzer/src/handlers/notification.rs
+++ b/crates/rust-analyzer/src/handlers/notification.rs
@@ -241,7 +241,7 @@ pub(crate) fn handle_did_change_watched_files(
     state: &mut GlobalState,
     params: DidChangeWatchedFilesParams,
 ) -> anyhow::Result<()> {
-    for change in params.changes {
+    for change in params.changes.iter().unique_by(|&it| &it.uri) {
         if let Ok(path) = from_proto::abs_path(&change.uri) {
             state.loader.handle.invalidate(path);
         }
diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs
index 7c7f5aa5ca..0214be006e 100644
--- a/crates/rust-analyzer/src/reload.rs
+++ b/crates/rust-analyzer/src/reload.rs
@@ -436,16 +436,19 @@ impl GlobalState {
                     .flat_map(|ws| ws.to_roots())
                     .filter(|it| it.is_local)
                     .flat_map(|root| {
-                        root.include.into_iter().flat_map(|it| {
-                            [
-                                format!("{it}/**/*.rs"),
-                                format!("{it}/**/Cargo.toml"),
-                                format!("{it}/**/Cargo.lock"),
-                            ]
-                        })
+                        root.include
+                            .into_iter()
+                            .flat_map(|it| [(it.clone(), "**/*.rs"), (it, "**/Cargo.{lock,toml}")])
                     })
-                    .map(|glob_pattern| lsp_types::FileSystemWatcher {
-                        glob_pattern: lsp_types::GlobPattern::String(glob_pattern),
+                    .map(|(base, pat)| lsp_types::FileSystemWatcher {
+                        glob_pattern: lsp_types::GlobPattern::Relative(
+                            lsp_types::RelativePattern {
+                                base_uri: lsp_types::OneOf::Right(
+                                    lsp_types::Url::from_file_path(base).unwrap(),
+                                ),
+                                pattern: pat.to_owned(),
+                            },
+                        ),
                         kind: None,
                     })
                     .collect(),
diff --git a/crates/vfs-notify/src/lib.rs b/crates/vfs-notify/src/lib.rs
index 94fd6cb78c..1f25b0e534 100644
--- a/crates/vfs-notify/src/lib.rs
+++ b/crates/vfs-notify/src/lib.rs
@@ -136,7 +136,7 @@ impl NotifyActor {
                     Message::Invalidate(path) => {
                         let contents = read(path.as_path());
                         let files = vec![(path, contents)];
-                        self.send(loader::Message::Loaded { files });
+                        self.send(loader::Message::Changed { files });
                     }
                 },
                 Event::NotifyEvent(event) => {
diff --git a/crates/vfs/src/lib.rs b/crates/vfs/src/lib.rs
index 9d723f8143..ddd6bbe3f7 100644
--- a/crates/vfs/src/lib.rs
+++ b/crates/vfs/src/lib.rs
@@ -91,12 +91,21 @@ impl nohash_hasher::IsEnabled for FileId {}
 pub struct Vfs {
     interner: PathInterner,
     data: Vec<FileState>,
+    // FIXME: This should be a HashMap<FileId, ChangeFile>
+    // right now we do a nasty deduplication in GlobalState::process_changes that would be a lot
+    // easier to handle here on insertion.
     changes: Vec<ChangedFile>,
+    // The above FIXME would then also get rid of this probably
+    created_this_cycle: Vec<FileId>,
 }
 
-#[derive(Copy, PartialEq, PartialOrd, Clone)]
+#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
 pub enum FileState {
+    /// The file has been created this cycle.
+    Created,
+    /// The file exists.
     Exists,
+    /// The file is deleted.
     Deleted,
 }
 
@@ -121,6 +130,11 @@ impl ChangedFile {
         matches!(self.change, Change::Create(_) | Change::Delete)
     }
 
+    /// Returns `true` if the change is [`Create`](ChangeKind::Create).
+    pub fn is_created(&self) -> bool {
+        matches!(self.change, Change::Create(_))
+    }
+
     /// Returns `true` if the change is [`Modify`](ChangeKind::Modify).
     pub fn is_modified(&self) -> bool {
         matches!(self.change, Change::Modify(_))
@@ -160,7 +174,9 @@ pub enum ChangeKind {
 impl Vfs {
     /// Id of the given path if it exists in the `Vfs` and is not deleted.
     pub fn file_id(&self, path: &VfsPath) -> Option<FileId> {
-        self.interner.get(path).filter(|&it| matches!(self.get(it), FileState::Exists))
+        self.interner
+            .get(path)
+            .filter(|&it| matches!(self.get(it), FileState::Exists | FileState::Created))
     }
 
     /// File path corresponding to the given `file_id`.
@@ -178,7 +194,9 @@ impl Vfs {
     pub fn iter(&self) -> impl Iterator<Item = (FileId, &VfsPath)> + '_ {
         (0..self.data.len())
             .map(|it| FileId(it as u32))
-            .filter(move |&file_id| matches!(self.get(file_id), FileState::Exists))
+            .filter(move |&file_id| {
+                matches!(self.get(file_id), FileState::Exists | FileState::Created)
+            })
             .map(move |file_id| {
                 let path = self.interner.lookup(file_id);
                 (file_id, path)
@@ -193,27 +211,43 @@ impl Vfs {
     /// [`FileId`] for it.
     pub fn set_file_contents(&mut self, path: VfsPath, contents: Option<Vec<u8>>) -> bool {
         let file_id = self.alloc_file_id(path);
-        let change_kind = match (self.get(file_id), contents) {
+        let state = self.get(file_id);
+        let change_kind = match (state, contents) {
             (FileState::Deleted, None) => return false,
             (FileState::Deleted, Some(v)) => Change::Create(v),
-            (FileState::Exists, None) => Change::Delete,
-            (FileState::Exists, Some(v)) => Change::Modify(v),
+            (FileState::Exists | FileState::Created, None) => Change::Delete,
+            (FileState::Exists | FileState::Created, Some(v)) => Change::Modify(v),
+        };
+        self.data[file_id.0 as usize] = match change_kind {
+            Change::Create(_) => {
+                self.created_this_cycle.push(file_id);
+                FileState::Created
+            }
+            // If the file got created this cycle, make sure we keep it that way even
+            // if a modify comes in
+            Change::Modify(_) if matches!(state, FileState::Created) => FileState::Created,
+            Change::Modify(_) => FileState::Exists,
+            Change::Delete => FileState::Deleted,
         };
         let changed_file = ChangedFile { file_id, change: change_kind };
-        self.data[file_id.0 as usize] =
-            if changed_file.exists() { FileState::Exists } else { FileState::Deleted };
         self.changes.push(changed_file);
         true
     }
 
     /// Drain and returns all the changes in the `Vfs`.
     pub fn take_changes(&mut self) -> Vec<ChangedFile> {
+        for file_id in self.created_this_cycle.drain(..) {
+            if self.data[file_id.0 as usize] == FileState::Created {
+                // downgrade the file from `Created` to `Exists` as the cycle is done
+                self.data[file_id.0 as usize] = FileState::Exists;
+            }
+        }
         mem::take(&mut self.changes)
     }
 
     /// Provides a panic-less way to verify file_id validity.
     pub fn exists(&self, file_id: FileId) -> bool {
-        matches!(self.get(file_id), FileState::Exists)
+        matches!(self.get(file_id), FileState::Exists | FileState::Created)
     }
 
     /// Returns the id associated with `path`
diff --git a/crates/vfs/src/loader.rs b/crates/vfs/src/loader.rs
index c3d1ff7271..3af91b1af8 100644
--- a/crates/vfs/src/loader.rs
+++ b/crates/vfs/src/loader.rs
@@ -58,7 +58,7 @@ pub enum Message {
         /// The [`Config`] version.
         config_version: u32,
     },
-    /// The handle loaded the following files' content.
+    /// The handle loaded the following files' content for the first time.
     Loaded { files: Vec<(AbsPathBuf, Option<Vec<u8>>)> },
     /// The handle loaded the following files' content.
     Changed { files: Vec<(AbsPathBuf, Option<Vec<u8>>)> },


Test patch:
diff --git a/crates/ide-diagnostics/src/tests.rs b/crates/ide-diagnostics/src/tests.rs
index bb5c2b7913..dcaa212089 100644
--- a/crates/ide-diagnostics/src/tests.rs
+++ b/crates/ide-diagnostics/src/tests.rs
@@ -283,10 +283,6 @@ fn test_disabled_diagnostics() {
 
 #[test]
 fn minicore_smoke_test() {
-    if test_utils::skip_slow_tests() {
-        return;
-    }
-
     fn check(minicore: MiniCore) {
         let source = minicore.source_code();
         let mut config = DiagnosticsConfig::test_sample();