aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/completion/src/completions/attribute.rs35
-rw-r--r--crates/hir/src/semantics.rs46
-rw-r--r--crates/hir/src/source_analyzer.rs2
-rw-r--r--crates/hir_def/src/attr.rs8
-rw-r--r--crates/hir_def/src/builtin_attr.rs430
-rw-r--r--crates/hir_def/src/lib.rs1
-rw-r--r--crates/hir_ty/src/diagnostics.rs10
-rw-r--r--crates/hir_ty/src/infer/expr.rs22
-rw-r--r--crates/hir_ty/src/tests/simple.rs56
-rw-r--r--crates/ide/src/display/navigation_target.rs52
-rw-r--r--crates/ide/src/hover.rs16
-rw-r--r--crates/ide/src/runnables.rs18
-rw-r--r--crates/mbe/src/mbe_expander/matcher.rs32
-rw-r--r--crates/mbe/src/tests.rs13
-rw-r--r--crates/rust-analyzer/src/cli.rs23
-rw-r--r--crates/rust-analyzer/src/cli/analysis_bench.rs5
-rw-r--r--crates/rust-analyzer/src/cli/analysis_stats.rs8
-rw-r--r--crates/rust-analyzer/src/lib.rs21
18 files changed, 678 insertions, 120 deletions
diff --git a/crates/completion/src/completions/attribute.rs b/crates/completion/src/completions/attribute.rs
index 5404145d5..acce2e7e7 100644
--- a/crates/completion/src/completions/attribute.rs
+++ b/crates/completion/src/completions/attribute.rs
@@ -3,6 +3,7 @@
3//! This module uses a bit of static metadata to provide completions 3//! This module uses a bit of static metadata to provide completions
4//! for built-in attributes. 4//! for built-in attributes.
5 5
6use itertools::Itertools;
6use rustc_hash::FxHashSet; 7use rustc_hash::FxHashSet;
7use syntax::{ast, AstNode, SyntaxKind}; 8use syntax::{ast, AstNode, SyntaxKind};
8 9
@@ -162,19 +163,20 @@ const ATTRIBUTES: &[AttrCompletion] = &[
162fn complete_derive(acc: &mut Completions, ctx: &CompletionContext, derive_input: ast::TokenTree) { 163fn complete_derive(acc: &mut Completions, ctx: &CompletionContext, derive_input: ast::TokenTree) {
163 if let Ok(existing_derives) = parse_comma_sep_input(derive_input) { 164 if let Ok(existing_derives) = parse_comma_sep_input(derive_input) {
164 for derive_completion in DEFAULT_DERIVE_COMPLETIONS 165 for derive_completion in DEFAULT_DERIVE_COMPLETIONS
165 .into_iter() 166 .iter()
166 .filter(|completion| !existing_derives.contains(completion.label)) 167 .filter(|completion| !existing_derives.contains(completion.label))
167 { 168 {
168 let mut label = derive_completion.label.to_owned(); 169 let mut components = vec![derive_completion.label];
169 for dependency in derive_completion 170 components.extend(
170 .dependencies 171 derive_completion
171 .into_iter() 172 .dependencies
172 .filter(|&&dependency| !existing_derives.contains(dependency)) 173 .iter()
173 { 174 .filter(|&&dependency| !existing_derives.contains(dependency)),
174 label.push_str(", "); 175 );
175 label.push_str(dependency); 176 let lookup = components.join(", ");
176 } 177 let label = components.iter().rev().join(", ");
177 CompletionItem::new(CompletionKind::Attribute, ctx.source_range(), label) 178 CompletionItem::new(CompletionKind::Attribute, ctx.source_range(), label)
179 .lookup_by(lookup)
178 .kind(CompletionItemKind::Attribute) 180 .kind(CompletionItemKind::Attribute)
179 .add_to(acc) 181 .add_to(acc)
180 } 182 }
@@ -264,7 +266,6 @@ struct DeriveCompletion {
264 266
265/// Standard Rust derives and the information about their dependencies 267/// Standard Rust derives and the information about their dependencies
266/// (the dependencies are needed so that the main derive don't break the compilation when added) 268/// (the dependencies are needed so that the main derive don't break the compilation when added)
267#[rustfmt::skip]
268const DEFAULT_DERIVE_COMPLETIONS: &[DeriveCompletion] = &[ 269const DEFAULT_DERIVE_COMPLETIONS: &[DeriveCompletion] = &[
269 DeriveCompletion { label: "Clone", dependencies: &[] }, 270 DeriveCompletion { label: "Clone", dependencies: &[] },
270 DeriveCompletion { label: "Copy", dependencies: &["Clone"] }, 271 DeriveCompletion { label: "Copy", dependencies: &["Clone"] },
@@ -421,14 +422,14 @@ struct Test {}
421 "#, 422 "#,
422 expect![[r#" 423 expect![[r#"
423 at Clone 424 at Clone
424 at Copy, Clone 425 at Clone, Copy
425 at Debug 426 at Debug
426 at Default 427 at Default
427 at Eq, PartialEq
428 at Hash 428 at Hash
429 at Ord, PartialOrd, Eq, PartialEq
430 at PartialEq 429 at PartialEq
431 at PartialOrd, PartialEq 430 at PartialEq, Eq
431 at PartialEq, Eq, PartialOrd, Ord
432 at PartialEq, PartialOrd
432 "#]], 433 "#]],
433 ); 434 );
434 } 435 }
@@ -453,12 +454,12 @@ struct Test {}
453"#, 454"#,
454 expect![[r#" 455 expect![[r#"
455 at Clone 456 at Clone
456 at Copy, Clone 457 at Clone, Copy
457 at Debug 458 at Debug
458 at Default 459 at Default
459 at Eq 460 at Eq
461 at Eq, PartialOrd, Ord
460 at Hash 462 at Hash
461 at Ord, PartialOrd, Eq
462 at PartialOrd 463 at PartialOrd
463 "#]], 464 "#]],
464 ) 465 )
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs
index 4315ad48b..4bd22ed27 100644
--- a/crates/hir/src/semantics.rs
+++ b/crates/hir/src/semantics.rs
@@ -294,9 +294,8 @@ impl<'db> SemanticsImpl<'db> {
294 } 294 }
295 295
296 fn expand(&self, macro_call: &ast::MacroCall) -> Option<SyntaxNode> { 296 fn expand(&self, macro_call: &ast::MacroCall) -> Option<SyntaxNode> {
297 let macro_call = self.find_file(macro_call.syntax().clone()).with_value(macro_call); 297 let sa = self.analyze(macro_call.syntax());
298 let sa = self.analyze2(macro_call.map(|it| it.syntax()), None); 298 let file_id = sa.expand(self.db, InFile::new(sa.file_id, macro_call))?;
299 let file_id = sa.expand(self.db, macro_call)?;
300 let node = self.db.parse_or_expand(file_id)?; 299 let node = self.db.parse_or_expand(file_id)?;
301 self.cache(node.clone(), file_id); 300 self.cache(node.clone(), file_id);
302 Some(node) 301 Some(node)
@@ -308,9 +307,8 @@ impl<'db> SemanticsImpl<'db> {
308 hypothetical_args: &ast::TokenTree, 307 hypothetical_args: &ast::TokenTree,
309 token_to_map: SyntaxToken, 308 token_to_map: SyntaxToken,
310 ) -> Option<(SyntaxNode, SyntaxToken)> { 309 ) -> Option<(SyntaxNode, SyntaxToken)> {
311 let macro_call = 310 let sa = self.analyze(actual_macro_call.syntax());
312 self.find_file(actual_macro_call.syntax().clone()).with_value(actual_macro_call); 311 let macro_call = InFile::new(sa.file_id, actual_macro_call);
313 let sa = self.analyze2(macro_call.map(|it| it.syntax()), None);
314 let krate = sa.resolver.krate()?; 312 let krate = sa.resolver.krate()?;
315 let macro_call_id = macro_call.as_call_id(self.db.upcast(), krate, |path| { 313 let macro_call_id = macro_call.as_call_id(self.db.upcast(), krate, |path| {
316 sa.resolver.resolve_path_as_macro(self.db.upcast(), &path) 314 sa.resolver.resolve_path_as_macro(self.db.upcast(), &path)
@@ -326,10 +324,9 @@ impl<'db> SemanticsImpl<'db> {
326 fn descend_into_macros(&self, token: SyntaxToken) -> SyntaxToken { 324 fn descend_into_macros(&self, token: SyntaxToken) -> SyntaxToken {
327 let _p = profile::span("descend_into_macros"); 325 let _p = profile::span("descend_into_macros");
328 let parent = token.parent(); 326 let parent = token.parent();
329 let parent = self.find_file(parent); 327 let sa = self.analyze(&parent);
330 let sa = self.analyze2(parent.as_ref(), None);
331 328
332 let token = successors(Some(parent.with_value(token)), |token| { 329 let token = successors(Some(InFile::new(sa.file_id, token)), |token| {
333 self.db.check_canceled(); 330 self.db.check_canceled();
334 let macro_call = token.value.ancestors().find_map(ast::MacroCall::cast)?; 331 let macro_call = token.value.ancestors().find_map(ast::MacroCall::cast)?;
335 let tt = macro_call.token_tree()?; 332 let tt = macro_call.token_tree()?;
@@ -486,15 +483,13 @@ impl<'db> SemanticsImpl<'db> {
486 } 483 }
487 484
488 fn scope(&self, node: &SyntaxNode) -> SemanticsScope<'db> { 485 fn scope(&self, node: &SyntaxNode) -> SemanticsScope<'db> {
489 let node = self.find_file(node.clone()); 486 let sa = self.analyze(node);
490 let resolver = self.analyze2(node.as_ref(), None).resolver; 487 SemanticsScope { db: self.db, file_id: sa.file_id, resolver: sa.resolver }
491 SemanticsScope { db: self.db, file_id: node.file_id, resolver }
492 } 488 }
493 489
494 fn scope_at_offset(&self, node: &SyntaxNode, offset: TextSize) -> SemanticsScope<'db> { 490 fn scope_at_offset(&self, node: &SyntaxNode, offset: TextSize) -> SemanticsScope<'db> {
495 let node = self.find_file(node.clone()); 491 let sa = self.analyze_with_offset(node, offset);
496 let resolver = self.analyze2(node.as_ref(), Some(offset)).resolver; 492 SemanticsScope { db: self.db, file_id: sa.file_id, resolver: sa.resolver }
497 SemanticsScope { db: self.db, file_id: node.file_id, resolver }
498 } 493 }
499 494
500 fn scope_for_def(&self, def: Trait) -> SemanticsScope<'db> { 495 fn scope_for_def(&self, def: Trait) -> SemanticsScope<'db> {
@@ -504,21 +499,24 @@ impl<'db> SemanticsImpl<'db> {
504 } 499 }
505 500
506 fn analyze(&self, node: &SyntaxNode) -> SourceAnalyzer { 501 fn analyze(&self, node: &SyntaxNode) -> SourceAnalyzer {
507 let src = self.find_file(node.clone()); 502 self.analyze_impl(node, None)
508 self.analyze2(src.as_ref(), None)
509 } 503 }
504 fn analyze_with_offset(&self, node: &SyntaxNode, offset: TextSize) -> SourceAnalyzer {
505 self.analyze_impl(node, Some(offset))
506 }
507 fn analyze_impl(&self, node: &SyntaxNode, offset: Option<TextSize>) -> SourceAnalyzer {
508 let _p = profile::span("Semantics::analyze_impl");
509 let node = self.find_file(node.clone());
510 let node = node.as_ref();
510 511
511 fn analyze2(&self, src: InFile<&SyntaxNode>, offset: Option<TextSize>) -> SourceAnalyzer { 512 let container = match self.with_ctx(|ctx| ctx.find_container(node)) {
512 let _p = profile::span("Semantics::analyze2");
513
514 let container = match self.with_ctx(|ctx| ctx.find_container(src)) {
515 Some(it) => it, 513 Some(it) => it,
516 None => return SourceAnalyzer::new_for_resolver(Resolver::default(), src), 514 None => return SourceAnalyzer::new_for_resolver(Resolver::default(), node),
517 }; 515 };
518 516
519 let resolver = match container { 517 let resolver = match container {
520 ChildContainer::DefWithBodyId(def) => { 518 ChildContainer::DefWithBodyId(def) => {
521 return SourceAnalyzer::new_for_body(self.db, def, src, offset) 519 return SourceAnalyzer::new_for_body(self.db, def, node, offset)
522 } 520 }
523 ChildContainer::TraitId(it) => it.resolver(self.db.upcast()), 521 ChildContainer::TraitId(it) => it.resolver(self.db.upcast()),
524 ChildContainer::ImplId(it) => it.resolver(self.db.upcast()), 522 ChildContainer::ImplId(it) => it.resolver(self.db.upcast()),
@@ -528,7 +526,7 @@ impl<'db> SemanticsImpl<'db> {
528 ChildContainer::TypeAliasId(it) => it.resolver(self.db.upcast()), 526 ChildContainer::TypeAliasId(it) => it.resolver(self.db.upcast()),
529 ChildContainer::GenericDefId(it) => it.resolver(self.db.upcast()), 527 ChildContainer::GenericDefId(it) => it.resolver(self.db.upcast()),
530 }; 528 };
531 SourceAnalyzer::new_for_resolver(resolver, src) 529 SourceAnalyzer::new_for_resolver(resolver, node)
532 } 530 }
533 531
534 fn cache(&self, root_node: SyntaxNode, file_id: HirFileId) { 532 fn cache(&self, root_node: SyntaxNode, file_id: HirFileId) {
diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs
index 1aef0f33f..bf0c959fe 100644
--- a/crates/hir/src/source_analyzer.rs
+++ b/crates/hir/src/source_analyzer.rs
@@ -37,7 +37,7 @@ use base_db::CrateId;
37/// original source files. It should not be used inside the HIR itself. 37/// original source files. It should not be used inside the HIR itself.
38#[derive(Debug)] 38#[derive(Debug)]
39pub(crate) struct SourceAnalyzer { 39pub(crate) struct SourceAnalyzer {
40 file_id: HirFileId, 40 pub(crate) file_id: HirFileId,
41 pub(crate) resolver: Resolver, 41 pub(crate) resolver: Resolver,
42 body: Option<Arc<Body>>, 42 body: Option<Arc<Body>>,
43 body_source_map: Option<Arc<BodySourceMap>>, 43 body_source_map: Option<Arc<BodySourceMap>>,
diff --git a/crates/hir_def/src/attr.rs b/crates/hir_def/src/attr.rs
index 228d706db..c64b78445 100644
--- a/crates/hir_def/src/attr.rs
+++ b/crates/hir_def/src/attr.rs
@@ -23,7 +23,7 @@ use crate::{
23}; 23};
24 24
25/// Holds documentation 25/// Holds documentation
26#[derive(Debug, Clone, PartialEq, Eq)] 26#[derive(Debug, Clone, PartialEq, Eq, Hash)]
27pub struct Documentation(String); 27pub struct Documentation(String);
28 28
29impl Documentation { 29impl Documentation {
@@ -32,9 +32,9 @@ impl Documentation {
32 } 32 }
33} 33}
34 34
35impl Into<String> for Documentation { 35impl From<Documentation> for String {
36 fn into(self) -> String { 36 fn from(Documentation(string): Documentation) -> Self {
37 self.0 37 string
38 } 38 }
39} 39}
40 40
diff --git a/crates/hir_def/src/builtin_attr.rs b/crates/hir_def/src/builtin_attr.rs
new file mode 100644
index 000000000..2e989c504
--- /dev/null
+++ b/crates/hir_def/src/builtin_attr.rs
@@ -0,0 +1,430 @@
1//! Builtin attributes resolved by nameres.
2//!
3//! The actual definitions were copied from rustc's `compiler/rustc_feature/src/builtin_attrs.rs`.
4//!
5//! It was last synchronized with upstream commit 2225ee1b62ff089917434aefd9b2bf509cfa087f.
6//!
7//! The macros were adjusted to only expand to the attribute name, since that is all we need to do
8//! name resolution, and `BUILTIN_ATTRIBUTES` is almost entirely unchanged from the original, to
9//! ease updating.
10
11/// Ignored attribute namespaces used by tools.
12pub const TOOL_MODULES: &[&str] = &["rustfmt", "clippy"];
13
14type BuiltinAttribute = &'static str;
15
16macro_rules! ungated {
17 ($attr:ident, $typ:expr, $tpl:expr $(,)?) => {
18 stringify!($attr)
19 };
20}
21
22macro_rules! gated {
23 ($attr:ident $($rest:tt)*) => {
24 stringify!($attr)
25 };
26}
27
28macro_rules! rustc_attr {
29 (TEST, $attr:ident $($rest:tt)*) => {
30 stringify!($attr)
31 };
32 ($attr:ident $($rest:tt)*) => {
33 stringify!($attr)
34 };
35}
36
37/// Attributes that have a special meaning to rustc or rustdoc.
38#[rustfmt::skip]
39pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
40 // ==========================================================================
41 // Stable attributes:
42 // ==========================================================================
43
44 // Conditional compilation:
45 ungated!(cfg, Normal, template!(List: "predicate")),
46 ungated!(cfg_attr, Normal, template!(List: "predicate, attr1, attr2, ...")),
47
48 // Testing:
49 ungated!(ignore, Normal, template!(Word, NameValueStr: "reason")),
50 ungated!(
51 should_panic, Normal,
52 template!(Word, List: r#"expected = "reason"#, NameValueStr: "reason"),
53 ),
54 // FIXME(Centril): This can be used on stable but shouldn't.
55 ungated!(reexport_test_harness_main, Normal, template!(NameValueStr: "name")),
56
57 // Macros:
58 ungated!(derive, Normal, template!(List: "Trait1, Trait2, ...")),
59 ungated!(automatically_derived, Normal, template!(Word)),
60 // FIXME(#14407)
61 ungated!(macro_use, Normal, template!(Word, List: "name1, name2, ...")),
62 ungated!(macro_escape, Normal, template!(Word)), // Deprecated synonym for `macro_use`.
63 ungated!(macro_export, Normal, template!(Word, List: "local_inner_macros")),
64 ungated!(proc_macro, Normal, template!(Word)),
65 ungated!(
66 proc_macro_derive, Normal,
67 template!(List: "TraitName, /*opt*/ attributes(name1, name2, ...)"),
68 ),
69 ungated!(proc_macro_attribute, Normal, template!(Word)),
70
71 // Lints:
72 ungated!(warn, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#)),
73 ungated!(allow, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#)),
74 ungated!(forbid, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#)),
75 ungated!(deny, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#)),
76 ungated!(must_use, AssumedUsed, template!(Word, NameValueStr: "reason")),
77 // FIXME(#14407)
78 ungated!(
79 deprecated, Normal,
80 template!(
81 Word,
82 List: r#"/*opt*/ since = "version", /*opt*/ note = "reason""#,
83 NameValueStr: "reason"
84 ),
85 ),
86
87 // Crate properties:
88 ungated!(crate_name, CrateLevel, template!(NameValueStr: "name")),
89 ungated!(crate_type, CrateLevel, template!(NameValueStr: "bin|lib|...")),
90 ungated!(crate_id, CrateLevel, template!(NameValueStr: "ignored")),
91
92 // ABI, linking, symbols, and FFI
93 ungated!(
94 link, AssumedUsed,
95 template!(List: r#"name = "...", /*opt*/ kind = "dylib|static|...", /*opt*/ wasm_import_module = "...""#),
96 ),
97 ungated!(link_name, AssumedUsed, template!(NameValueStr: "name")),
98 ungated!(no_link, Normal, template!(Word)),
99 ungated!(repr, Normal, template!(List: "C")),
100 ungated!(export_name, AssumedUsed, template!(NameValueStr: "name")),
101 ungated!(link_section, AssumedUsed, template!(NameValueStr: "name")),
102 ungated!(no_mangle, AssumedUsed, template!(Word)),
103 ungated!(used, AssumedUsed, template!(Word)),
104
105 // Limits:
106 ungated!(recursion_limit, CrateLevel, template!(NameValueStr: "N")),
107 ungated!(type_length_limit, CrateLevel, template!(NameValueStr: "N")),
108 gated!(
109 const_eval_limit, CrateLevel, template!(NameValueStr: "N"), const_eval_limit,
110 experimental!(const_eval_limit)
111 ),
112
113 // Entry point:
114 ungated!(main, Normal, template!(Word)),
115 ungated!(start, Normal, template!(Word)),
116 ungated!(no_start, CrateLevel, template!(Word)),
117 ungated!(no_main, CrateLevel, template!(Word)),
118
119 // Modules, prelude, and resolution:
120 ungated!(path, Normal, template!(NameValueStr: "file")),
121 ungated!(no_std, CrateLevel, template!(Word)),
122 ungated!(no_implicit_prelude, Normal, template!(Word)),
123 ungated!(non_exhaustive, AssumedUsed, template!(Word)),
124
125 // Runtime
126 ungated!(windows_subsystem, AssumedUsed, template!(NameValueStr: "windows|console")),
127 ungated!(panic_handler, Normal, template!(Word)), // RFC 2070
128
129 // Code generation:
130 ungated!(inline, AssumedUsed, template!(Word, List: "always|never")),
131 ungated!(cold, AssumedUsed, template!(Word)),
132 ungated!(no_builtins, AssumedUsed, template!(Word)),
133 ungated!(target_feature, AssumedUsed, template!(List: r#"enable = "name""#)),
134 ungated!(track_caller, AssumedUsed, template!(Word)),
135 gated!(
136 no_sanitize, AssumedUsed,
137 template!(List: "address, memory, thread"),
138 experimental!(no_sanitize)
139 ),
140
141 // FIXME: #14408 assume docs are used since rustdoc looks at them.
142 ungated!(doc, AssumedUsed, template!(List: "hidden|inline|...", NameValueStr: "string")),
143
144 // ==========================================================================
145 // Unstable attributes:
146 // ==========================================================================
147
148 // Linking:
149 gated!(naked, AssumedUsed, template!(Word), naked_functions, experimental!(naked)),
150 gated!(
151 link_args, Normal, template!(NameValueStr: "args"),
152 "the `link_args` attribute is experimental and not portable across platforms, \
153 it is recommended to use `#[link(name = \"foo\")] instead",
154 ),
155 gated!(
156 link_ordinal, AssumedUsed, template!(List: "ordinal"), raw_dylib,
157 experimental!(link_ordinal)
158 ),
159
160 // Plugins:
161 // XXX Modified for use in rust-analyzer
162 gated!(plugin_registrar),
163 gated!(plugin),
164
165 // Testing:
166 gated!(allow_fail, Normal, template!(Word), experimental!(allow_fail)),
167 gated!(
168 test_runner, CrateLevel, template!(List: "path"), custom_test_frameworks,
169 "custom test frameworks are an unstable feature",
170 ),
171 // RFC #1268
172 gated!(marker, Normal, template!(Word), marker_trait_attr, experimental!(marker)),
173 gated!(
174 thread_local, AssumedUsed, template!(Word),
175 "`#[thread_local]` is an experimental feature, and does not currently handle destructors",
176 ),
177 gated!(no_core, CrateLevel, template!(Word), experimental!(no_core)),
178 // RFC 2412
179 gated!(
180 optimize, AssumedUsed, template!(List: "size|speed"), optimize_attribute,
181 experimental!(optimize),
182 ),
183 // RFC 2867
184 gated!(instruction_set, AssumedUsed, template!(List: "set"), isa_attribute, experimental!(instruction_set)),
185
186 gated!(ffi_returns_twice, AssumedUsed, template!(Word), experimental!(ffi_returns_twice)),
187 gated!(ffi_pure, AssumedUsed, template!(Word), experimental!(ffi_pure)),
188 gated!(ffi_const, AssumedUsed, template!(Word), experimental!(ffi_const)),
189 gated!(
190 register_attr, CrateLevel, template!(List: "attr1, attr2, ..."),
191 experimental!(register_attr),
192 ),
193 gated!(
194 register_tool, CrateLevel, template!(List: "tool1, tool2, ..."),
195 experimental!(register_tool),
196 ),
197
198 gated!(cmse_nonsecure_entry, AssumedUsed, template!(Word), experimental!(cmse_nonsecure_entry)),
199
200 // ==========================================================================
201 // Internal attributes: Stability, deprecation, and unsafe:
202 // ==========================================================================
203
204 ungated!(feature, CrateLevel, template!(List: "name1, name1, ...")),
205 // FIXME(#14407) -- only looked at on-demand so we can't
206 // guarantee they'll have already been checked.
207 ungated!(
208 rustc_deprecated, AssumedUsed,
209 template!(List: r#"since = "version", reason = "...""#)
210 ),
211 // FIXME(#14407)
212 ungated!(stable, AssumedUsed, template!(List: r#"feature = "name", since = "version""#)),
213 // FIXME(#14407)
214 ungated!(
215 unstable, AssumedUsed,
216 template!(List: r#"feature = "name", reason = "...", issue = "N""#),
217 ),
218 // FIXME(#14407)
219 ungated!(rustc_const_unstable, AssumedUsed, template!(List: r#"feature = "name""#)),
220 // FIXME(#14407)
221 ungated!(rustc_const_stable, AssumedUsed, template!(List: r#"feature = "name""#)),
222 gated!(
223 allow_internal_unstable, AssumedUsed, template!(Word, List: "feat1, feat2, ..."),
224 "allow_internal_unstable side-steps feature gating and stability checks",
225 ),
226 gated!(
227 rustc_allow_const_fn_unstable, AssumedUsed, template!(Word, List: "feat1, feat2, ..."),
228 "rustc_allow_const_fn_unstable side-steps feature gating and stability checks"
229 ),
230 gated!(
231 allow_internal_unsafe, Normal, template!(Word),
232 "allow_internal_unsafe side-steps the unsafe_code lint",
233 ),
234
235 // ==========================================================================
236 // Internal attributes: Type system related:
237 // ==========================================================================
238
239 gated!(fundamental, AssumedUsed, template!(Word), experimental!(fundamental)),
240 gated!(
241 may_dangle, Normal, template!(Word), dropck_eyepatch,
242 "`may_dangle` has unstable semantics and may be removed in the future",
243 ),
244
245 // ==========================================================================
246 // Internal attributes: Runtime related:
247 // ==========================================================================
248
249 rustc_attr!(rustc_allocator, AssumedUsed, template!(Word), IMPL_DETAIL),
250 rustc_attr!(rustc_allocator_nounwind, AssumedUsed, template!(Word), IMPL_DETAIL),
251 gated!(alloc_error_handler, Normal, template!(Word), experimental!(alloc_error_handler)),
252 gated!(
253 default_lib_allocator, AssumedUsed, template!(Word), allocator_internals,
254 experimental!(default_lib_allocator),
255 ),
256 gated!(
257 needs_allocator, Normal, template!(Word), allocator_internals,
258 experimental!(needs_allocator),
259 ),
260 gated!(panic_runtime, AssumedUsed, template!(Word), experimental!(panic_runtime)),
261 gated!(needs_panic_runtime, AssumedUsed, template!(Word), experimental!(needs_panic_runtime)),
262 gated!(
263 unwind, AssumedUsed, template!(List: "allowed|aborts"), unwind_attributes,
264 experimental!(unwind),
265 ),
266 gated!(
267 compiler_builtins, AssumedUsed, template!(Word),
268 "the `#[compiler_builtins]` attribute is used to identify the `compiler_builtins` crate \
269 which contains compiler-rt intrinsics and will never be stable",
270 ),
271 gated!(
272 profiler_runtime, AssumedUsed, template!(Word),
273 "the `#[profiler_runtime]` attribute is used to identify the `profiler_builtins` crate \
274 which contains the profiler runtime and will never be stable",
275 ),
276
277 // ==========================================================================
278 // Internal attributes, Linkage:
279 // ==========================================================================
280
281 gated!(
282 linkage, AssumedUsed, template!(NameValueStr: "external|internal|..."),
283 "the `linkage` attribute is experimental and not portable across platforms",
284 ),
285 rustc_attr!(rustc_std_internal_symbol, AssumedUsed, template!(Word), INTERNAL_UNSTABLE),
286
287 // ==========================================================================
288 // Internal attributes, Macro related:
289 // ==========================================================================
290
291 rustc_attr!(rustc_builtin_macro, AssumedUsed, template!(Word), IMPL_DETAIL),
292 rustc_attr!(rustc_proc_macro_decls, Normal, template!(Word), INTERNAL_UNSTABLE),
293 rustc_attr!(
294 rustc_macro_transparency, AssumedUsed,
295 template!(NameValueStr: "transparent|semitransparent|opaque"),
296 "used internally for testing macro hygiene",
297 ),
298
299 // ==========================================================================
300 // Internal attributes, Diagnostics related:
301 // ==========================================================================
302
303 rustc_attr!(
304 rustc_on_unimplemented, AssumedUsed,
305 template!(
306 List: r#"/*opt*/ message = "...", /*opt*/ label = "...", /*opt*/ note = "...""#,
307 NameValueStr: "message"
308 ),
309 INTERNAL_UNSTABLE
310 ),
311 // Enumerates "identity-like" conversion methods to suggest on type mismatch.
312 rustc_attr!(rustc_conversion_suggestion, AssumedUsed, template!(Word), INTERNAL_UNSTABLE),
313
314 // ==========================================================================
315 // Internal attributes, Const related:
316 // ==========================================================================
317
318 rustc_attr!(rustc_promotable, AssumedUsed, template!(Word), IMPL_DETAIL),
319 rustc_attr!(rustc_args_required_const, AssumedUsed, template!(List: "N"), INTERNAL_UNSTABLE),
320
321 // ==========================================================================
322 // Internal attributes, Layout related:
323 // ==========================================================================
324
325 rustc_attr!(
326 rustc_layout_scalar_valid_range_start, AssumedUsed, template!(List: "value"),
327 "the `#[rustc_layout_scalar_valid_range_start]` attribute is just used to enable \
328 niche optimizations in libcore and will never be stable",
329 ),
330 rustc_attr!(
331 rustc_layout_scalar_valid_range_end, AssumedUsed, template!(List: "value"),
332 "the `#[rustc_layout_scalar_valid_range_end]` attribute is just used to enable \
333 niche optimizations in libcore and will never be stable",
334 ),
335 rustc_attr!(
336 rustc_nonnull_optimization_guaranteed, AssumedUsed, template!(Word),
337 "the `#[rustc_nonnull_optimization_guaranteed]` attribute is just used to enable \
338 niche optimizations in libcore and will never be stable",
339 ),
340
341 // ==========================================================================
342 // Internal attributes, Misc:
343 // ==========================================================================
344 gated!(
345 lang, Normal, template!(NameValueStr: "name"), lang_items,
346 "language items are subject to change",
347 ),
348 gated!(rustc_diagnostic_item), // XXX modified in rust-analyzer
349 gated!(
350 // Used in resolve:
351 prelude_import, AssumedUsed, template!(Word),
352 "`#[prelude_import]` is for use by rustc only",
353 ),
354 gated!(
355 rustc_paren_sugar, Normal, template!(Word), unboxed_closures,
356 "unboxed_closures are still evolving",
357 ),
358 rustc_attr!(
359 rustc_inherit_overflow_checks, AssumedUsed, template!(Word),
360 "the `#[rustc_inherit_overflow_checks]` attribute is just used to control \
361 overflow checking behavior of several libcore functions that are inlined \
362 across crates and will never be stable",
363 ),
364 rustc_attr!(rustc_reservation_impl, Normal, template!(NameValueStr: "reservation message"),
365 "the `#[rustc_reservation_impl]` attribute is internally used \
366 for reserving for `for<T> From<!> for T` impl"
367 ),
368 rustc_attr!(
369 rustc_test_marker, Normal, template!(Word),
370 "the `#[rustc_test_marker]` attribute is used internally to track tests",
371 ),
372 rustc_attr!(
373 rustc_unsafe_specialization_marker, Normal, template!(Word),
374 "the `#[rustc_unsafe_specialization_marker]` attribute is used to check specializations"
375 ),
376 rustc_attr!(
377 rustc_specialization_trait, Normal, template!(Word),
378 "the `#[rustc_specialization_trait]` attribute is used to check specializations"
379 ),
380
381 // ==========================================================================
382 // Internal attributes, Testing:
383 // ==========================================================================
384
385 rustc_attr!(TEST, rustc_outlives, Normal, template!(Word)),
386 rustc_attr!(TEST, rustc_capture_analysis, Normal, template!(Word)),
387 rustc_attr!(TEST, rustc_variance, Normal, template!(Word)),
388 rustc_attr!(TEST, rustc_layout, Normal, template!(List: "field1, field2, ...")),
389 rustc_attr!(TEST, rustc_regions, Normal, template!(Word)),
390 rustc_attr!(
391 TEST, rustc_error, AssumedUsed,
392 template!(Word, List: "delay_span_bug_from_inside_query")
393 ),
394 rustc_attr!(TEST, rustc_dump_user_substs, AssumedUsed, template!(Word)),
395 rustc_attr!(TEST, rustc_if_this_changed, AssumedUsed, template!(Word, List: "DepNode")),
396 rustc_attr!(TEST, rustc_then_this_would_need, AssumedUsed, template!(List: "DepNode")),
397 rustc_attr!(
398 TEST, rustc_dirty, AssumedUsed,
399 template!(List: r#"cfg = "...", /*opt*/ label = "...", /*opt*/ except = "...""#),
400 ),
401 rustc_attr!(
402 TEST, rustc_clean, AssumedUsed,
403 template!(List: r#"cfg = "...", /*opt*/ label = "...", /*opt*/ except = "...""#),
404 ),
405 rustc_attr!(
406 TEST, rustc_partition_reused, AssumedUsed,
407 template!(List: r#"cfg = "...", module = "...""#),
408 ),
409 rustc_attr!(
410 TEST, rustc_partition_codegened, AssumedUsed,
411 template!(List: r#"cfg = "...", module = "...""#),
412 ),
413 rustc_attr!(
414 TEST, rustc_expected_cgu_reuse, AssumedUsed,
415 template!(List: r#"cfg = "...", module = "...", kind = "...""#),
416 ),
417 rustc_attr!(TEST, rustc_synthetic, AssumedUsed, template!(Word)),
418 rustc_attr!(TEST, rustc_symbol_name, AssumedUsed, template!(Word)),
419 rustc_attr!(TEST, rustc_polymorphize_error, AssumedUsed, template!(Word)),
420 rustc_attr!(TEST, rustc_def_path, AssumedUsed, template!(Word)),
421 rustc_attr!(TEST, rustc_mir, AssumedUsed, template!(List: "arg1, arg2, ...")),
422 rustc_attr!(TEST, rustc_dump_program_clauses, AssumedUsed, template!(Word)),
423 rustc_attr!(TEST, rustc_dump_env_program_clauses, AssumedUsed, template!(Word)),
424 rustc_attr!(TEST, rustc_object_lifetime_default, AssumedUsed, template!(Word)),
425 rustc_attr!(TEST, rustc_dummy, Normal, template!(Word /* doesn't matter*/)),
426 gated!(
427 omit_gdb_pretty_printer_section, AssumedUsed, template!(Word),
428 "the `#[omit_gdb_pretty_printer_section]` attribute is just used for the Rust test suite",
429 ),
430];
diff --git a/crates/hir_def/src/lib.rs b/crates/hir_def/src/lib.rs
index a1bbc729f..7e2199a9c 100644
--- a/crates/hir_def/src/lib.rs
+++ b/crates/hir_def/src/lib.rs
@@ -18,6 +18,7 @@ pub mod attr;
18pub mod path; 18pub mod path;
19pub mod type_ref; 19pub mod type_ref;
20pub mod builtin_type; 20pub mod builtin_type;
21pub mod builtin_attr;
21pub mod diagnostics; 22pub mod diagnostics;
22pub mod per_ns; 23pub mod per_ns;
23pub mod item_scope; 24pub mod item_scope;
diff --git a/crates/hir_ty/src/diagnostics.rs b/crates/hir_ty/src/diagnostics.rs
index e59487e54..1c72f766e 100644
--- a/crates/hir_ty/src/diagnostics.rs
+++ b/crates/hir_ty/src/diagnostics.rs
@@ -624,4 +624,14 @@ fn foo() { break; }
624"#, 624"#,
625 ); 625 );
626 } 626 }
627
628 #[test]
629 fn missing_semicolon() {
630 check_diagnostics(
631 r#"
632 fn test() -> i32 { 123; }
633 //^^^ Remove this semicolon
634 "#,
635 );
636 }
627} 637}
diff --git a/crates/hir_ty/src/infer/expr.rs b/crates/hir_ty/src/infer/expr.rs
index a89fff773..ca005bc99 100644
--- a/crates/hir_ty/src/infer/expr.rs
+++ b/crates/hir_ty/src/infer/expr.rs
@@ -137,10 +137,24 @@ impl<'a> InferenceContext<'a> {
137 137
138 self.coerce_merge_branch(&then_ty, &else_ty) 138 self.coerce_merge_branch(&then_ty, &else_ty)
139 } 139 }
140 Expr::Block { statements, tail, .. } => { 140 Expr::Block { statements, tail, label } => match label {
141 // FIXME: Breakable block inference 141 Some(_) => {
142 self.infer_block(statements, *tail, expected) 142 let break_ty = self.table.new_type_var();
143 } 143 self.breakables.push(BreakableContext {
144 may_break: false,
145 break_ty: break_ty.clone(),
146 label: label.clone(),
147 });
148 let ty = self.infer_block(statements, *tail, &Expectation::has_type(break_ty));
149 let ctxt = self.breakables.pop().expect("breakable stack broken");
150 if ctxt.may_break {
151 ctxt.break_ty
152 } else {
153 ty
154 }
155 }
156 None => self.infer_block(statements, *tail, expected),
157 },
144 Expr::Unsafe { body } => self.infer_expr(*body, expected), 158 Expr::Unsafe { body } => self.infer_expr(*body, expected),
145 Expr::TryBlock { body } => { 159 Expr::TryBlock { body } => {
146 let _inner = self.infer_expr(*body, expected); 160 let _inner = self.infer_expr(*body, expected);
diff --git a/crates/hir_ty/src/tests/simple.rs b/crates/hir_ty/src/tests/simple.rs
index 4f72582b6..a569223b4 100644
--- a/crates/hir_ty/src/tests/simple.rs
+++ b/crates/hir_ty/src/tests/simple.rs
@@ -2075,6 +2075,62 @@ fn infer_labelled_break_with_val() {
2075} 2075}
2076 2076
2077#[test] 2077#[test]
2078fn infer_labelled_block_break_with_val() {
2079 check_infer(
2080 r#"
2081 fn default<T>() -> T { loop {} }
2082 fn foo() {
2083 let _x = 'outer: {
2084 let inner = 'inner: {
2085 let i = default();
2086 if (break 'outer i) {
2087 break 'inner 5i8;
2088 } else if true {
2089 break 'inner 6;
2090 }
2091 break 'inner 'innermost: { 0 };
2092 42
2093 };
2094 break 'outer inner < 8;
2095 };
2096 }
2097 "#,
2098 expect![[r#"
2099 21..32 '{ loop {} }': T
2100 23..30 'loop {}': !
2101 28..30 '{}': ()
2102 42..381 '{ ... }; }': ()
2103 52..54 '_x': bool
2104 65..378 '{ ... }': bool
2105 79..84 'inner': i8
2106 95..339 '{ ... }': i8
2107 113..114 'i': bool
2108 117..124 'default': fn default<bool>() -> bool
2109 117..126 'default()': bool
2110 140..270 'if (br... }': ()
2111 144..158 'break 'outer i': !
2112 157..158 'i': bool
2113 160..209 '{ ... }': ()
2114 178..194 'break ...er 5i8': !
2115 191..194 '5i8': i8
2116 215..270 'if tru... }': ()
2117 218..222 'true': bool
2118 223..270 '{ ... }': ()
2119 241..255 'break 'inner 6': !
2120 254..255 '6': i8
2121 283..313 'break ... { 0 }': !
2122 308..313 '{ 0 }': i8
2123 310..311 '0': i8
2124 327..329 '42': i8
2125 349..371 'break ...er < 8': !
2126 362..367 'inner': i8
2127 362..371 'inner < 8': bool
2128 370..371 '8': i8
2129 "#]],
2130 );
2131}
2132
2133#[test]
2078fn generic_default() { 2134fn generic_default() {
2079 check_infer( 2135 check_infer(
2080 r#" 2136 r#"
diff --git a/crates/ide/src/display/navigation_target.rs b/crates/ide/src/display/navigation_target.rs
index 4790d648a..234f80a3a 100644
--- a/crates/ide/src/display/navigation_target.rs
+++ b/crates/ide/src/display/navigation_target.rs
@@ -1,11 +1,13 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3use either::Either; 3use either::Either;
4use hir::{AssocItem, FieldSource, HasSource, InFile, ModuleSource}; 4use hir::{
5 AssocItem, Documentation, FieldSource, HasAttrs, HasSource, HirFileId, InFile, ModuleSource,
6};
5use ide_db::base_db::{FileId, SourceDatabase}; 7use ide_db::base_db::{FileId, SourceDatabase};
6use ide_db::{defs::Definition, RootDatabase}; 8use ide_db::{defs::Definition, RootDatabase};
7use syntax::{ 9use syntax::{
8 ast::{self, DocCommentsOwner, NameOwner}, 10 ast::{self, NameOwner},
9 match_ast, AstNode, SmolStr, 11 match_ast, AstNode, SmolStr,
10 SyntaxKind::{self, IDENT_PAT, TYPE_PARAM}, 12 SyntaxKind::{self, IDENT_PAT, TYPE_PARAM},
11 TextRange, 13 TextRange,
@@ -43,7 +45,7 @@ pub struct NavigationTarget {
43 pub kind: SyntaxKind, 45 pub kind: SyntaxKind,
44 pub container_name: Option<SmolStr>, 46 pub container_name: Option<SmolStr>,
45 pub description: Option<String>, 47 pub description: Option<String>,
46 pub docs: Option<String>, 48 pub docs: Option<Documentation>,
47} 49}
48 50
49pub(crate) trait ToNav { 51pub(crate) trait ToNav {
@@ -71,7 +73,7 @@ impl NavigationTarget {
71 frange.range, 73 frange.range,
72 src.value.syntax().kind(), 74 src.value.syntax().kind(),
73 ); 75 );
74 res.docs = src.value.doc_comment_text(); 76 res.docs = module.attrs(db).docs();
75 res.description = src.value.short_label(); 77 res.description = src.value.short_label();
76 return res; 78 return res;
77 } 79 }
@@ -214,14 +216,14 @@ impl ToNavFromAst for hir::Trait {}
214 216
215impl<D> ToNav for D 217impl<D> ToNav for D
216where 218where
217 D: HasSource + ToNavFromAst + Copy, 219 D: HasSource + ToNavFromAst + Copy + HasAttrs,
218 D::Ast: ast::DocCommentsOwner + ast::NameOwner + ShortLabel, 220 D::Ast: ast::NameOwner + ShortLabel,
219{ 221{
220 fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { 222 fn to_nav(&self, db: &RootDatabase) -> NavigationTarget {
221 let src = self.source(db); 223 let src = self.source(db);
222 let mut res = 224 let mut res =
223 NavigationTarget::from_named(db, src.as_ref().map(|it| it as &dyn ast::NameOwner)); 225 NavigationTarget::from_named(db, src.as_ref().map(|it| it as &dyn ast::NameOwner));
224 res.docs = src.value.doc_comment_text(); 226 res.docs = self.docs(db);
225 res.description = src.value.short_label(); 227 res.description = src.value.short_label();
226 res 228 res
227 } 229 }
@@ -274,7 +276,7 @@ impl ToNav for hir::Field {
274 match &src.value { 276 match &src.value {
275 FieldSource::Named(it) => { 277 FieldSource::Named(it) => {
276 let mut res = NavigationTarget::from_named(db, src.with_value(it)); 278 let mut res = NavigationTarget::from_named(db, src.with_value(it));
277 res.docs = it.doc_comment_text(); 279 res.docs = self.docs(db);
278 res.description = it.short_label(); 280 res.description = it.short_label();
279 res 281 res
280 } 282 }
@@ -298,7 +300,7 @@ impl ToNav for hir::MacroDef {
298 log::debug!("nav target {:#?}", src.value.syntax()); 300 log::debug!("nav target {:#?}", src.value.syntax());
299 let mut res = 301 let mut res =
300 NavigationTarget::from_named(db, src.as_ref().map(|it| it as &dyn ast::NameOwner)); 302 NavigationTarget::from_named(db, src.as_ref().map(|it| it as &dyn ast::NameOwner));
301 res.docs = src.value.doc_comment_text(); 303 res.docs = self.docs(db);
302 res 304 res
303 } 305 }
304} 306}
@@ -374,26 +376,28 @@ impl ToNav for hir::TypeParam {
374 } 376 }
375} 377}
376 378
377pub(crate) fn docs_from_symbol(db: &RootDatabase, symbol: &FileSymbol) -> Option<String> { 379pub(crate) fn docs_from_symbol(db: &RootDatabase, symbol: &FileSymbol) -> Option<Documentation> {
378 let parse = db.parse(symbol.file_id); 380 let parse = db.parse(symbol.file_id);
379 let node = symbol.ptr.to_node(parse.tree().syntax()); 381 let node = symbol.ptr.to_node(parse.tree().syntax());
382 let file_id = HirFileId::from(symbol.file_id);
380 383
381 match_ast! { 384 let it = match_ast! {
382 match node { 385 match node {
383 ast::Fn(it) => it.doc_comment_text(), 386 ast::Fn(it) => hir::Attrs::from_attrs_owner(db, InFile::new(file_id, &it)),
384 ast::Struct(it) => it.doc_comment_text(), 387 ast::Struct(it) => hir::Attrs::from_attrs_owner(db, InFile::new(file_id, &it)),
385 ast::Enum(it) => it.doc_comment_text(), 388 ast::Enum(it) => hir::Attrs::from_attrs_owner(db, InFile::new(file_id, &it)),
386 ast::Trait(it) => it.doc_comment_text(), 389 ast::Trait(it) => hir::Attrs::from_attrs_owner(db, InFile::new(file_id, &it)),
387 ast::Module(it) => it.doc_comment_text(), 390 ast::Module(it) => hir::Attrs::from_attrs_owner(db, InFile::new(file_id, &it)),
388 ast::TypeAlias(it) => it.doc_comment_text(), 391 ast::TypeAlias(it) => hir::Attrs::from_attrs_owner(db, InFile::new(file_id, &it)),
389 ast::Const(it) => it.doc_comment_text(), 392 ast::Const(it) => hir::Attrs::from_attrs_owner(db, InFile::new(file_id, &it)),
390 ast::Static(it) => it.doc_comment_text(), 393 ast::Static(it) => hir::Attrs::from_attrs_owner(db, InFile::new(file_id, &it)),
391 ast::RecordField(it) => it.doc_comment_text(), 394 ast::RecordField(it) => hir::Attrs::from_attrs_owner(db, InFile::new(file_id, &it)),
392 ast::Variant(it) => it.doc_comment_text(), 395 ast::Variant(it) => hir::Attrs::from_attrs_owner(db, InFile::new(file_id, &it)),
393 ast::MacroCall(it) => it.doc_comment_text(), 396 ast::MacroCall(it) => hir::Attrs::from_attrs_owner(db, InFile::new(file_id, &it)),
394 _ => None, 397 _ => return None,
395 } 398 }
396 } 399 };
400 it.docs()
397} 401}
398 402
399/// Get a description of a symbol. 403/// Get a description of a symbol.
diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs
index cf04c3de0..ab017d2ad 100644
--- a/crates/ide/src/hover.rs
+++ b/crates/ide/src/hover.rs
@@ -139,6 +139,11 @@ pub(crate) fn hover(
139 } 139 }
140 } 140 }
141 141
142 if token.kind() == syntax::SyntaxKind::COMMENT {
143 // don't highlight the entire parent node on comment hover
144 return None;
145 }
146
142 let node = token.ancestors().find(|n| { 147 let node = token.ancestors().find(|n| {
143 ast::Expr::can_cast(n.kind()) 148 ast::Expr::can_cast(n.kind())
144 || ast::Pat::can_cast(n.kind()) 149 || ast::Pat::can_cast(n.kind())
@@ -3419,4 +3424,15 @@ mod Foo<|> {
3419 "#]], 3424 "#]],
3420 ); 3425 );
3421 } 3426 }
3427
3428 #[test]
3429 fn hover_comments_dont_highlight_parent() {
3430 check_hover_no_result(
3431 r#"
3432fn no_hover() {
3433 // no<|>hover
3434}
3435"#,
3436 );
3437 }
3422} 3438}
diff --git a/crates/ide/src/runnables.rs b/crates/ide/src/runnables.rs
index e15411777..646f63704 100644
--- a/crates/ide/src/runnables.rs
+++ b/crates/ide/src/runnables.rs
@@ -6,7 +6,7 @@ use hir::{AsAssocItem, Attrs, HirFileId, InFile, Semantics};
6use ide_db::RootDatabase; 6use ide_db::RootDatabase;
7use itertools::Itertools; 7use itertools::Itertools;
8use syntax::{ 8use syntax::{
9 ast::{self, AstNode, AttrsOwner, DocCommentsOwner, ModuleItemOwner, NameOwner}, 9 ast::{self, AstNode, AttrsOwner, ModuleItemOwner, NameOwner},
10 match_ast, SyntaxNode, 10 match_ast, SyntaxNode,
11}; 11};
12 12
@@ -118,6 +118,7 @@ fn runnable_fn(
118) -> Option<Runnable> { 118) -> Option<Runnable> {
119 let name_string = fn_def.name()?.text().to_string(); 119 let name_string = fn_def.name()?.text().to_string();
120 120
121 let attrs = Attrs::from_attrs_owner(sema.db, InFile::new(HirFileId::from(file_id), &fn_def));
121 let kind = if name_string == "main" { 122 let kind = if name_string == "main" {
122 RunnableKind::Bin 123 RunnableKind::Bin
123 } else { 124 } else {
@@ -162,14 +163,13 @@ fn runnable_fn(
162 RunnableKind::Test { test_id, attr } 163 RunnableKind::Test { test_id, attr }
163 } else if fn_def.has_atom_attr("bench") { 164 } else if fn_def.has_atom_attr("bench") {
164 RunnableKind::Bench { test_id } 165 RunnableKind::Bench { test_id }
165 } else if has_runnable_doc_test(&fn_def) { 166 } else if has_runnable_doc_test(&attrs) {
166 RunnableKind::DocTest { test_id } 167 RunnableKind::DocTest { test_id }
167 } else { 168 } else {
168 return None; 169 return None;
169 } 170 }
170 }; 171 };
171 172
172 let attrs = Attrs::from_attrs_owner(sema.db, InFile::new(HirFileId::from(file_id), &fn_def));
173 let cfg = attrs.cfg(); 173 let cfg = attrs.cfg();
174 174
175 let nav = if let RunnableKind::DocTest { .. } = kind { 175 let nav = if let RunnableKind::DocTest { .. } = kind {
@@ -189,13 +189,13 @@ fn runnable_struct(
189 struct_def: ast::Struct, 189 struct_def: ast::Struct,
190 file_id: FileId, 190 file_id: FileId,
191) -> Option<Runnable> { 191) -> Option<Runnable> {
192 if !has_runnable_doc_test(&struct_def) {
193 return None;
194 }
195 let name_string = struct_def.name()?.text().to_string(); 192 let name_string = struct_def.name()?.text().to_string();
196 193
197 let attrs = 194 let attrs =
198 Attrs::from_attrs_owner(sema.db, InFile::new(HirFileId::from(file_id), &struct_def)); 195 Attrs::from_attrs_owner(sema.db, InFile::new(HirFileId::from(file_id), &struct_def));
196 if !has_runnable_doc_test(&attrs) {
197 return None;
198 }
199 let cfg = attrs.cfg(); 199 let cfg = attrs.cfg();
200 200
201 let test_id = match sema.to_def(&struct_def).map(|def| def.module(sema.db)) { 201 let test_id = match sema.to_def(&struct_def).map(|def| def.module(sema.db)) {
@@ -240,11 +240,11 @@ const RUSTDOC_FENCE: &str = "```";
240const RUSTDOC_CODE_BLOCK_ATTRIBUTES_RUNNABLE: &[&str] = 240const RUSTDOC_CODE_BLOCK_ATTRIBUTES_RUNNABLE: &[&str] =
241 &["", "rust", "should_panic", "edition2015", "edition2018"]; 241 &["", "rust", "should_panic", "edition2015", "edition2018"];
242 242
243fn has_runnable_doc_test(def: &dyn DocCommentsOwner) -> bool { 243fn has_runnable_doc_test(attrs: &hir::Attrs) -> bool {
244 def.doc_comment_text().map_or(false, |comments_text| { 244 attrs.docs().map_or(false, |doc| {
245 let mut in_code_block = false; 245 let mut in_code_block = false;
246 246
247 for line in comments_text.lines() { 247 for line in String::from(doc).lines() {
248 if let Some(header) = line.strip_prefix(RUSTDOC_FENCE) { 248 if let Some(header) = line.strip_prefix(RUSTDOC_FENCE) {
249 in_code_block = !in_code_block; 249 in_code_block = !in_code_block;
250 250
diff --git a/crates/mbe/src/mbe_expander/matcher.rs b/crates/mbe/src/mbe_expander/matcher.rs
index 3f8445897..93ee77908 100644
--- a/crates/mbe/src/mbe_expander/matcher.rs
+++ b/crates/mbe/src/mbe_expander/matcher.rs
@@ -356,6 +356,18 @@ impl<'a> TtIter<'a> {
356 ExpandResult { value: _, err: Some(_) } => None, 356 ExpandResult { value: _, err: Some(_) } => None,
357 } 357 }
358 } 358 }
359
360 pub(crate) fn eat_char(&mut self, c: char) -> Option<tt::TokenTree> {
361 let mut fork = self.clone();
362 match fork.expect_char(c) {
363 Ok(_) => {
364 let tt = self.next().cloned();
365 *self = fork;
366 tt
367 }
368 Err(_) => None,
369 }
370 }
359} 371}
360 372
361pub(super) fn match_repeat( 373pub(super) fn match_repeat(
@@ -447,10 +459,22 @@ fn match_meta_var(kind: &str, input: &mut TtIter) -> ExpandResult<Option<Fragmen
447 .expect_lifetime() 459 .expect_lifetime()
448 .map(|tt| Some(tt)) 460 .map(|tt| Some(tt))
449 .map_err(|()| err!("expected lifetime")), 461 .map_err(|()| err!("expected lifetime")),
450 "literal" => input 462 "literal" => {
451 .expect_literal() 463 let neg = input.eat_char('-');
452 .map(|literal| Some(tt::Leaf::from(literal.clone()).into())) 464 input
453 .map_err(|()| err!()), 465 .expect_literal()
466 .map(|literal| {
467 let lit = tt::Leaf::from(literal.clone());
468 match neg {
469 None => Some(lit.into()),
470 Some(neg) => Some(tt::TokenTree::Subtree(tt::Subtree {
471 delimiter: None,
472 token_trees: vec![neg, lit.into()],
473 })),
474 }
475 })
476 .map_err(|()| err!())
477 }
454 // `vis` is optional 478 // `vis` is optional
455 "vis" => match input.eat_vis() { 479 "vis" => match input.eat_vis() {
456 Some(vis) => Ok(Some(vis)), 480 Some(vis) => Ok(Some(vis)),
diff --git a/crates/mbe/src/tests.rs b/crates/mbe/src/tests.rs
index 0796ceee1..843054fe8 100644
--- a/crates/mbe/src/tests.rs
+++ b/crates/mbe/src/tests.rs
@@ -1008,11 +1008,20 @@ fn test_literal() {
1008 parse_macro( 1008 parse_macro(
1009 r#" 1009 r#"
1010 macro_rules! foo { 1010 macro_rules! foo {
1011 ($ type:ty $ lit:literal) => { const VALUE: $ type = $ lit;}; 1011 ($ type:ty , $ lit:literal) => { const VALUE: $ type = $ lit;};
1012 } 1012 }
1013"#, 1013"#,
1014 ) 1014 )
1015 .assert_expand_items(r#"foo!(u8 0);"#, r#"const VALUE : u8 = 0 ;"#); 1015 .assert_expand_items(r#"foo!(u8,0);"#, r#"const VALUE : u8 = 0 ;"#);
1016
1017 parse_macro(
1018 r#"
1019 macro_rules! foo {
1020 ($ type:ty , $ lit:literal) => { const VALUE: $ type = $ lit;};
1021 }
1022"#,
1023 )
1024 .assert_expand_items(r#"foo!(i32,-1);"#, r#"const VALUE : i32 = - 1 ;"#);
1016} 1025}
1017 1026
1018#[test] 1027#[test]
diff --git a/crates/rust-analyzer/src/cli.rs b/crates/rust-analyzer/src/cli.rs
index 6966ee576..6879a462d 100644
--- a/crates/rust-analyzer/src/cli.rs
+++ b/crates/rust-analyzer/src/cli.rs
@@ -10,8 +10,9 @@ mod ssr;
10use std::io::Read; 10use std::io::Read;
11 11
12use anyhow::Result; 12use anyhow::Result;
13use ide::Analysis; 13use ide::{Analysis, AnalysisHost};
14use syntax::{AstNode, SourceFile}; 14use syntax::{AstNode, SourceFile};
15use vfs::Vfs;
15 16
16pub use self::{ 17pub use self::{
17 analysis_bench::{BenchCmd, BenchWhat, Position}, 18 analysis_bench::{BenchCmd, BenchWhat, Position},
@@ -82,3 +83,23 @@ fn report_metric(metric: &str, value: u64, unit: &str) {
82 } 83 }
83 println!("METRIC:{}:{}:{}", metric, value, unit) 84 println!("METRIC:{}:{}:{}", metric, value, unit)
84} 85}
86
87fn print_memory_usage(mut host: AnalysisHost, vfs: Vfs) {
88 let mut mem = host.per_query_memory_usage();
89
90 let before = profile::memory_usage();
91 drop(vfs);
92 let vfs = before.allocated - profile::memory_usage().allocated;
93 mem.push(("VFS".into(), vfs));
94
95 let before = profile::memory_usage();
96 drop(host);
97 mem.push(("Unaccounted".into(), before.allocated - profile::memory_usage().allocated));
98
99 mem.push(("Remaining".into(), profile::memory_usage().allocated));
100
101 for (name, bytes) in mem {
102 // NOTE: Not a debug print, so avoid going through the `eprintln` defined above.
103 eprintln!("{:>8} {}", bytes, name);
104 }
105}
diff --git a/crates/rust-analyzer/src/cli/analysis_bench.rs b/crates/rust-analyzer/src/cli/analysis_bench.rs
index 8e33986d5..5a8484c62 100644
--- a/crates/rust-analyzer/src/cli/analysis_bench.rs
+++ b/crates/rust-analyzer/src/cli/analysis_bench.rs
@@ -12,10 +12,7 @@ use ide_db::base_db::{
12}; 12};
13use vfs::AbsPathBuf; 13use vfs::AbsPathBuf;
14 14
15use crate::{ 15use crate::cli::{load_cargo::load_cargo, print_memory_usage, Verbosity};
16 cli::{load_cargo::load_cargo, Verbosity},
17 print_memory_usage,
18};
19 16
20pub struct BenchCmd { 17pub struct BenchCmd {
21 pub path: PathBuf, 18 pub path: PathBuf,
diff --git a/crates/rust-analyzer/src/cli/analysis_stats.rs b/crates/rust-analyzer/src/cli/analysis_stats.rs
index 58d284d47..a23fb7a33 100644
--- a/crates/rust-analyzer/src/cli/analysis_stats.rs
+++ b/crates/rust-analyzer/src/cli/analysis_stats.rs
@@ -23,11 +23,9 @@ use rustc_hash::FxHashSet;
23use stdx::format_to; 23use stdx::format_to;
24use syntax::AstNode; 24use syntax::AstNode;
25 25
26use crate::{ 26use crate::cli::{
27 cli::{ 27 load_cargo::load_cargo, print_memory_usage, progress_report::ProgressReport, report_metric,
28 load_cargo::load_cargo, progress_report::ProgressReport, report_metric, Result, Verbosity, 28 Result, Verbosity,
29 },
30 print_memory_usage,
31}; 29};
32use profile::StopWatch; 30use profile::StopWatch;
33 31
diff --git a/crates/rust-analyzer/src/lib.rs b/crates/rust-analyzer/src/lib.rs
index ad08f1afb..79fe30e53 100644
--- a/crates/rust-analyzer/src/lib.rs
+++ b/crates/rust-analyzer/src/lib.rs
@@ -37,10 +37,8 @@ mod document;
37pub mod lsp_ext; 37pub mod lsp_ext;
38pub mod config; 38pub mod config;
39 39
40use ide::AnalysisHost;
41use serde::de::DeserializeOwned; 40use serde::de::DeserializeOwned;
42use std::fmt; 41use std::fmt;
43use vfs::Vfs;
44 42
45pub use crate::{caps::server_capabilities, main_loop::main_loop}; 43pub use crate::{caps::server_capabilities, main_loop::main_loop};
46 44
@@ -72,22 +70,3 @@ impl fmt::Display for LspError {
72} 70}
73 71
74impl std::error::Error for LspError {} 72impl std::error::Error for LspError {}
75
76fn print_memory_usage(mut host: AnalysisHost, vfs: Vfs) {
77 let mut mem = host.per_query_memory_usage();
78
79 let before = profile::memory_usage();
80 drop(vfs);
81 let vfs = before.allocated - profile::memory_usage().allocated;
82 mem.push(("VFS".into(), vfs));
83
84 let before = profile::memory_usage();
85 drop(host);
86 mem.push(("Unaccounted".into(), before.allocated - profile::memory_usage().allocated));
87
88 mem.push(("Remaining".into(), profile::memory_usage().allocated));
89
90 for (name, bytes) in mem {
91 eprintln!("{:>8} {}", bytes, name);
92 }
93}