diff options
-rw-r--r-- | Cargo.lock | 24 | ||||
-rw-r--r-- | crates/completion/src/completions/attribute.rs | 30 | ||||
-rw-r--r-- | crates/completion/src/completions/mod_.rs | 69 | ||||
-rw-r--r-- | crates/hir/src/db.rs | 4 | ||||
-rw-r--r-- | crates/hir/src/diagnostics.rs | 2 | ||||
-rw-r--r-- | crates/hir_def/src/path.rs | 1 | ||||
-rw-r--r-- | crates/hir_def/src/path/lower.rs | 2 | ||||
-rw-r--r-- | crates/hir_expand/src/db.rs | 12 | ||||
-rw-r--r-- | crates/hir_expand/src/hygiene.rs | 210 | ||||
-rw-r--r-- | crates/hir_expand/src/name.rs | 2 | ||||
-rw-r--r-- | crates/hir_ty/src/diagnostics.rs | 15 | ||||
-rw-r--r-- | crates/hir_ty/src/diagnostics/expr.rs | 27 | ||||
-rw-r--r-- | crates/hir_ty/src/tests/macros.rs | 31 | ||||
-rw-r--r-- | crates/ide/src/diagnostics.rs | 53 | ||||
-rw-r--r-- | crates/ide/src/diagnostics/fixes.rs | 10 | ||||
-rw-r--r-- | crates/ide_db/src/apply_change.rs | 1 | ||||
-rw-r--r-- | crates/mbe/src/mbe_expander/matcher.rs | 2 | ||||
-rw-r--r-- | crates/mbe/src/mbe_expander/transcriber.rs | 27 | ||||
-rw-r--r-- | crates/mbe/src/parser.rs | 15 | ||||
-rw-r--r-- | crates/mbe/src/tests.rs | 13 |
20 files changed, 399 insertions, 151 deletions
diff --git a/Cargo.lock b/Cargo.lock index 57383491a..bc38c5a37 100644 --- a/Cargo.lock +++ b/Cargo.lock | |||
@@ -262,9 +262,9 @@ dependencies = [ | |||
262 | 262 | ||
263 | [[package]] | 263 | [[package]] |
264 | name = "const_fn" | 264 | name = "const_fn" |
265 | version = "0.4.4" | 265 | version = "0.4.5" |
266 | source = "registry+https://github.com/rust-lang/crates.io-index" | 266 | source = "registry+https://github.com/rust-lang/crates.io-index" |
267 | checksum = "cd51eab21ab4fd6a3bf889e2d0958c0a6e3a61ad04260325e919e652a2a62826" | 267 | checksum = "28b9d6de7f49e22cf97ad17fc4036ece69300032f45f78f30b4a4482cdc3f4a6" |
268 | 268 | ||
269 | [[package]] | 269 | [[package]] |
270 | name = "crc32fast" | 270 | name = "crc32fast" |
@@ -774,9 +774,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" | |||
774 | 774 | ||
775 | [[package]] | 775 | [[package]] |
776 | name = "libc" | 776 | name = "libc" |
777 | version = "0.2.81" | 777 | version = "0.2.82" |
778 | source = "registry+https://github.com/rust-lang/crates.io-index" | 778 | source = "registry+https://github.com/rust-lang/crates.io-index" |
779 | checksum = "1482821306169ec4d07f6aca392a4681f66c75c9918aa49641a2595db64053cb" | 779 | checksum = "89203f3fba0a3795506acaad8ebce3c80c0af93f994d5a1d7a0b1eeb23271929" |
780 | 780 | ||
781 | [[package]] | 781 | [[package]] |
782 | name = "libloading" | 782 | name = "libloading" |
@@ -1134,9 +1134,9 @@ checksum = "28b9b4df73455c861d7cbf8be42f01d3b373ed7f02e378d55fa84eafc6f638b1" | |||
1134 | 1134 | ||
1135 | [[package]] | 1135 | [[package]] |
1136 | name = "pin-project-lite" | 1136 | name = "pin-project-lite" |
1137 | version = "0.2.0" | 1137 | version = "0.2.1" |
1138 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1138 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1139 | checksum = "6b063f57ec186e6140e2b8b6921e5f1bd89c7356dda5b33acc5401203ca6131c" | 1139 | checksum = "e36743d754ccdf9954c2e352ce2d4b106e024c814f6499c2dadff80da9a442d8" |
1140 | 1140 | ||
1141 | [[package]] | 1141 | [[package]] |
1142 | name = "proc-macro2" | 1142 | name = "proc-macro2" |
@@ -1449,9 +1449,9 @@ dependencies = [ | |||
1449 | 1449 | ||
1450 | [[package]] | 1450 | [[package]] |
1451 | name = "semver-parser" | 1451 | name = "semver-parser" |
1452 | version = "0.10.1" | 1452 | version = "0.10.2" |
1453 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1453 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1454 | checksum = "42ef146c2ad5e5f4b037cd6ce2ebb775401729b19a82040c1beac9d36c7d1428" | 1454 | checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7" |
1455 | dependencies = [ | 1455 | dependencies = [ |
1456 | "pest", | 1456 | "pest", |
1457 | ] | 1457 | ] |
@@ -1558,9 +1558,9 @@ version = "0.0.0" | |||
1558 | 1558 | ||
1559 | [[package]] | 1559 | [[package]] |
1560 | name = "syn" | 1560 | name = "syn" |
1561 | version = "1.0.57" | 1561 | version = "1.0.58" |
1562 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1562 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1563 | checksum = "4211ce9909eb971f111059df92c45640aad50a619cf55cd76476be803c4c68e6" | 1563 | checksum = "cc60a3d73ea6594cd712d830cc1f0390fd71542d8c8cd24e70cc54cdfd5e05d5" |
1564 | dependencies = [ | 1564 | dependencies = [ |
1565 | "proc-macro2", | 1565 | "proc-macro2", |
1566 | "quote", | 1566 | "quote", |
@@ -1643,9 +1643,9 @@ checksum = "db3c46be180f1af9673ebb27bc1235396f61ef6965b3fe0dbb2e624deb604f0e" | |||
1643 | 1643 | ||
1644 | [[package]] | 1644 | [[package]] |
1645 | name = "thread_local" | 1645 | name = "thread_local" |
1646 | version = "1.0.1" | 1646 | version = "1.1.0" |
1647 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1647 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1648 | checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14" | 1648 | checksum = "bb9bc092d0d51e76b2b19d9d85534ffc9ec2db959a2523cdae0697e2972cd447" |
1649 | dependencies = [ | 1649 | dependencies = [ |
1650 | "lazy_static", | 1650 | "lazy_static", |
1651 | ] | 1651 | ] |
diff --git a/crates/completion/src/completions/attribute.rs b/crates/completion/src/completions/attribute.rs index 10739750c..3a29b5203 100644 --- a/crates/completion/src/completions/attribute.rs +++ b/crates/completion/src/completions/attribute.rs | |||
@@ -21,20 +21,15 @@ pub(crate) fn complete_attribute(acc: &mut Completions, ctx: &CompletionContext) | |||
21 | 21 | ||
22 | let attribute = ctx.attribute_under_caret.as_ref()?; | 22 | let attribute = ctx.attribute_under_caret.as_ref()?; |
23 | match (attribute.path(), attribute.token_tree()) { | 23 | match (attribute.path(), attribute.token_tree()) { |
24 | (Some(path), Some(token_tree)) if path.to_string() == "derive" => { | 24 | (Some(path), Some(token_tree)) => match path.to_string().as_str() { |
25 | complete_derive(acc, ctx, token_tree) | 25 | "derive" => complete_derive(acc, ctx, token_tree), |
26 | } | 26 | "feature" => complete_lint(acc, ctx, token_tree, FEATURES), |
27 | (Some(path), Some(token_tree)) if path.to_string() == "feature" => { | 27 | "allow" | "warn" | "deny" | "forbid" => { |
28 | complete_lint(acc, ctx, token_tree, FEATURES); | 28 | complete_lint(acc, ctx, token_tree.clone(), DEFAULT_LINT_COMPLETIONS); |
29 | } | 29 | complete_lint(acc, ctx, token_tree, CLIPPY_LINTS); |
30 | (Some(path), Some(token_tree)) | 30 | } |
31 | if ["allow", "warn", "deny", "forbid"] | 31 | _ => {} |
32 | .iter() | 32 | }, |
33 | .any(|lint_level| lint_level == &path.to_string()) => | ||
34 | { | ||
35 | complete_lint(acc, ctx, token_tree.clone(), DEFAULT_LINT_COMPLETIONS); | ||
36 | complete_lint(acc, ctx, token_tree, CLIPPY_LINTS); | ||
37 | } | ||
38 | (_, Some(_token_tree)) => {} | 33 | (_, Some(_token_tree)) => {} |
39 | _ => complete_attribute_start(acc, ctx, attribute), | 34 | _ => complete_attribute_start(acc, ctx, attribute), |
40 | } | 35 | } |
@@ -54,11 +49,8 @@ fn complete_attribute_start(acc: &mut Completions, ctx: &CompletionContext, attr | |||
54 | item = item.lookup_by(lookup); | 49 | item = item.lookup_by(lookup); |
55 | } | 50 | } |
56 | 51 | ||
57 | match (attr_completion.snippet, ctx.config.snippet_cap) { | 52 | if let Some((snippet, cap)) = attr_completion.snippet.zip(ctx.config.snippet_cap) { |
58 | (Some(snippet), Some(cap)) => { | 53 | item = item.insert_snippet(cap, snippet); |
59 | item = item.insert_snippet(cap, snippet); | ||
60 | } | ||
61 | _ => {} | ||
62 | } | 54 | } |
63 | 55 | ||
64 | if attribute.kind() == ast::AttrKind::Inner || !attr_completion.prefer_inner { | 56 | if attribute.kind() == ast::AttrKind::Inner || !attr_completion.prefer_inner { |
diff --git a/crates/completion/src/completions/mod_.rs b/crates/completion/src/completions/mod_.rs index 8c8eaeaa6..00e951ca9 100644 --- a/crates/completion/src/completions/mod_.rs +++ b/crates/completion/src/completions/mod_.rs | |||
@@ -1,5 +1,7 @@ | |||
1 | //! Completes mod declarations. | 1 | //! Completes mod declarations. |
2 | 2 | ||
3 | use std::iter; | ||
4 | |||
3 | use hir::{Module, ModuleSource}; | 5 | use hir::{Module, ModuleSource}; |
4 | use ide_db::base_db::{SourceDatabaseExt, VfsPath}; | 6 | use ide_db::base_db::{SourceDatabaseExt, VfsPath}; |
5 | use ide_db::RootDatabase; | 7 | use ide_db::RootDatabase; |
@@ -12,9 +14,8 @@ use crate::{context::CompletionContext, item::CompletionKind, Completions}; | |||
12 | /// Complete mod declaration, i.e. `mod $0 ;` | 14 | /// Complete mod declaration, i.e. `mod $0 ;` |
13 | pub(crate) fn complete_mod(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> { | 15 | pub(crate) fn complete_mod(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> { |
14 | let mod_under_caret = match &ctx.mod_declaration_under_caret { | 16 | let mod_under_caret = match &ctx.mod_declaration_under_caret { |
15 | Some(mod_under_caret) if mod_under_caret.item_list().is_some() => return None, | 17 | Some(mod_under_caret) if mod_under_caret.item_list().is_none() => mod_under_caret, |
16 | Some(mod_under_caret) => mod_under_caret, | 18 | _ => return None, |
17 | None => return None, | ||
18 | }; | 19 | }; |
19 | 20 | ||
20 | let _p = profile::span("completion::complete_mod"); | 21 | let _p = profile::span("completion::complete_mod"); |
@@ -49,9 +50,13 @@ pub(crate) fn complete_mod(acc: &mut Completions, ctx: &CompletionContext) -> Op | |||
49 | .filter_map(|submodule_file| { | 50 | .filter_map(|submodule_file| { |
50 | let submodule_path = source_root.path_for_file(&submodule_file)?; | 51 | let submodule_path = source_root.path_for_file(&submodule_file)?; |
51 | let directory_with_submodule = submodule_path.parent()?; | 52 | let directory_with_submodule = submodule_path.parent()?; |
52 | match submodule_path.name_and_extension()? { | 53 | let (name, ext) = submodule_path.name_and_extension()?; |
53 | ("lib", Some("rs")) | ("main", Some("rs")) => None, | 54 | if ext != Some("rs") { |
54 | ("mod", Some("rs")) => { | 55 | return None; |
56 | } | ||
57 | match name { | ||
58 | "lib" | "main" => None, | ||
59 | "mod" => { | ||
55 | if directory_with_submodule.parent()? == directory_to_look_for_submodules { | 60 | if directory_with_submodule.parent()? == directory_to_look_for_submodules { |
56 | match directory_with_submodule.name_and_extension()? { | 61 | match directory_with_submodule.name_and_extension()? { |
57 | (directory_name, None) => Some(directory_name.to_owned()), | 62 | (directory_name, None) => Some(directory_name.to_owned()), |
@@ -61,9 +66,7 @@ pub(crate) fn complete_mod(acc: &mut Completions, ctx: &CompletionContext) -> Op | |||
61 | None | 66 | None |
62 | } | 67 | } |
63 | } | 68 | } |
64 | (file_name, Some("rs")) | 69 | file_name if directory_with_submodule == directory_to_look_for_submodules => { |
65 | if directory_with_submodule == directory_to_look_for_submodules => | ||
66 | { | ||
67 | Some(file_name.to_owned()) | 70 | Some(file_name.to_owned()) |
68 | } | 71 | } |
69 | _ => None, | 72 | _ => None, |
@@ -73,7 +76,7 @@ pub(crate) fn complete_mod(acc: &mut Completions, ctx: &CompletionContext) -> Op | |||
73 | .for_each(|submodule_name| { | 76 | .for_each(|submodule_name| { |
74 | let mut label = submodule_name; | 77 | let mut label = submodule_name; |
75 | if mod_under_caret.semicolon_token().is_none() { | 78 | if mod_under_caret.semicolon_token().is_none() { |
76 | label.push(';') | 79 | label.push(';'); |
77 | } | 80 | } |
78 | CompletionItem::new(CompletionKind::Magic, ctx.source_range(), &label) | 81 | CompletionItem::new(CompletionKind::Magic, ctx.source_range(), &label) |
79 | .kind(CompletionItemKind::Module) | 82 | .kind(CompletionItemKind::Module) |
@@ -89,11 +92,13 @@ fn directory_to_look_for_submodules( | |||
89 | module_file_path: &VfsPath, | 92 | module_file_path: &VfsPath, |
90 | ) -> Option<VfsPath> { | 93 | ) -> Option<VfsPath> { |
91 | let directory_with_module_path = module_file_path.parent()?; | 94 | let directory_with_module_path = module_file_path.parent()?; |
92 | let base_directory = match module_file_path.name_and_extension()? { | 95 | let (name, ext) = module_file_path.name_and_extension()?; |
93 | ("mod", Some("rs")) | ("lib", Some("rs")) | ("main", Some("rs")) => { | 96 | if ext != Some("rs") { |
94 | Some(directory_with_module_path) | 97 | return None; |
95 | } | 98 | } |
96 | (regular_rust_file_name, Some("rs")) => { | 99 | let base_directory = match name { |
100 | "mod" | "lib" | "main" => Some(directory_with_module_path), | ||
101 | regular_rust_file_name => { | ||
97 | if matches!( | 102 | if matches!( |
98 | ( | 103 | ( |
99 | directory_with_module_path | 104 | directory_with_module_path |
@@ -110,37 +115,25 @@ fn directory_to_look_for_submodules( | |||
110 | directory_with_module_path.join(regular_rust_file_name) | 115 | directory_with_module_path.join(regular_rust_file_name) |
111 | } | 116 | } |
112 | } | 117 | } |
113 | _ => None, | ||
114 | }?; | 118 | }?; |
115 | 119 | ||
116 | let mut resulting_path = base_directory; | 120 | module_chain_to_containing_module_file(module, db) |
117 | for module in module_chain_to_containing_module_file(module, db) { | 121 | .into_iter() |
118 | if let Some(name) = module.name(db) { | 122 | .filter_map(|module| module.name(db)) |
119 | resulting_path = resulting_path.join(&name.to_string())?; | 123 | .try_fold(base_directory, |path, name| path.join(&name.to_string())) |
120 | } | ||
121 | } | ||
122 | |||
123 | Some(resulting_path) | ||
124 | } | 124 | } |
125 | 125 | ||
126 | fn module_chain_to_containing_module_file( | 126 | fn module_chain_to_containing_module_file( |
127 | current_module: Module, | 127 | current_module: Module, |
128 | db: &RootDatabase, | 128 | db: &RootDatabase, |
129 | ) -> Vec<Module> { | 129 | ) -> Vec<Module> { |
130 | let mut path = Vec::new(); | 130 | let mut path = |
131 | 131 | iter::successors(Some(current_module), |current_module| current_module.parent(db)) | |
132 | let mut current_module = Some(current_module); | 132 | .take_while(|current_module| { |
133 | while let Some(ModuleSource::Module(_)) = | 133 | matches!(current_module.definition_source(db).value, ModuleSource::Module(_)) |
134 | current_module.map(|module| module.definition_source(db).value) | 134 | }) |
135 | { | 135 | .collect::<Vec<_>>(); |
136 | if let Some(module) = current_module { | 136 | path.reverse(); |
137 | path.insert(0, module); | ||
138 | current_module = module.parent(db); | ||
139 | } else { | ||
140 | current_module = None; | ||
141 | } | ||
142 | } | ||
143 | |||
144 | path | 137 | path |
145 | } | 138 | } |
146 | 139 | ||
diff --git a/crates/hir/src/db.rs b/crates/hir/src/db.rs index d01e1b33d..d5d4cf5b6 100644 --- a/crates/hir/src/db.rs +++ b/crates/hir/src/db.rs | |||
@@ -10,8 +10,8 @@ pub use hir_def::db::{ | |||
10 | TypeAliasDataQuery, UnionDataQuery, | 10 | TypeAliasDataQuery, UnionDataQuery, |
11 | }; | 11 | }; |
12 | pub use hir_expand::db::{ | 12 | pub use hir_expand::db::{ |
13 | AstDatabase, AstDatabaseStorage, AstIdMapQuery, InternEagerExpansionQuery, InternMacroQuery, | 13 | AstDatabase, AstDatabaseStorage, AstIdMapQuery, HygieneFrameQuery, InternEagerExpansionQuery, |
14 | MacroArgTextQuery, MacroDefQuery, MacroExpandQuery, ParseMacroExpansionQuery, | 14 | InternMacroQuery, MacroArgTextQuery, MacroDefQuery, MacroExpandQuery, ParseMacroExpansionQuery, |
15 | }; | 15 | }; |
16 | pub use hir_ty::db::*; | 16 | pub use hir_ty::db::*; |
17 | 17 | ||
diff --git a/crates/hir/src/diagnostics.rs b/crates/hir/src/diagnostics.rs index b1c924167..447faa04f 100644 --- a/crates/hir/src/diagnostics.rs +++ b/crates/hir/src/diagnostics.rs | |||
@@ -4,6 +4,6 @@ pub use hir_expand::diagnostics::{ | |||
4 | Diagnostic, DiagnosticCode, DiagnosticSink, DiagnosticSinkBuilder, | 4 | Diagnostic, DiagnosticCode, DiagnosticSink, DiagnosticSinkBuilder, |
5 | }; | 5 | }; |
6 | pub use hir_ty::diagnostics::{ | 6 | pub use hir_ty::diagnostics::{ |
7 | IncorrectCase, MismatchedArgCount, MissingFields, MissingMatchArms, MissingOkInTailExpr, | 7 | IncorrectCase, MismatchedArgCount, MissingFields, MissingMatchArms, MissingOkOrSomeInTailExpr, |
8 | NoSuchField, RemoveThisSemicolon, | 8 | NoSuchField, RemoveThisSemicolon, |
9 | }; | 9 | }; |
diff --git a/crates/hir_def/src/path.rs b/crates/hir_def/src/path.rs index e2bf85bbc..3dd7c3cbb 100644 --- a/crates/hir_def/src/path.rs +++ b/crates/hir_def/src/path.rs | |||
@@ -305,6 +305,7 @@ pub use hir_expand::name as __name; | |||
305 | macro_rules! __known_path { | 305 | macro_rules! __known_path { |
306 | (core::iter::IntoIterator) => {}; | 306 | (core::iter::IntoIterator) => {}; |
307 | (core::result::Result) => {}; | 307 | (core::result::Result) => {}; |
308 | (core::option::Option) => {}; | ||
308 | (core::ops::Range) => {}; | 309 | (core::ops::Range) => {}; |
309 | (core::ops::RangeFrom) => {}; | 310 | (core::ops::RangeFrom) => {}; |
310 | (core::ops::RangeFull) => {}; | 311 | (core::ops::RangeFull) => {}; |
diff --git a/crates/hir_def/src/path/lower.rs b/crates/hir_def/src/path/lower.rs index 8a01e6eea..9518ac109 100644 --- a/crates/hir_def/src/path/lower.rs +++ b/crates/hir_def/src/path/lower.rs | |||
@@ -123,7 +123,7 @@ pub(super) fn lower_path(mut path: ast::Path, hygiene: &Hygiene) -> Option<Path> | |||
123 | // We follow what it did anyway :) | 123 | // We follow what it did anyway :) |
124 | if segments.len() == 1 && kind == PathKind::Plain { | 124 | if segments.len() == 1 && kind == PathKind::Plain { |
125 | if let Some(_macro_call) = path.syntax().parent().and_then(ast::MacroCall::cast) { | 125 | if let Some(_macro_call) = path.syntax().parent().and_then(ast::MacroCall::cast) { |
126 | if let Some(crate_id) = hygiene.local_inner_macros() { | 126 | if let Some(crate_id) = hygiene.local_inner_macros(path) { |
127 | kind = PathKind::DollarCrate(crate_id); | 127 | kind = PathKind::DollarCrate(crate_id); |
128 | } | 128 | } |
129 | } | 129 | } |
diff --git a/crates/hir_expand/src/db.rs b/crates/hir_expand/src/db.rs index 0a0d021e0..ab2637b8c 100644 --- a/crates/hir_expand/src/db.rs +++ b/crates/hir_expand/src/db.rs | |||
@@ -8,9 +8,9 @@ use parser::FragmentKind; | |||
8 | use syntax::{algo::diff, ast::NameOwner, AstNode, GreenNode, Parse, SyntaxKind::*, SyntaxNode}; | 8 | use syntax::{algo::diff, ast::NameOwner, AstNode, GreenNode, Parse, SyntaxKind::*, SyntaxNode}; |
9 | 9 | ||
10 | use crate::{ | 10 | use crate::{ |
11 | ast_id_map::AstIdMap, BuiltinDeriveExpander, BuiltinFnLikeExpander, EagerCallLoc, EagerMacroId, | 11 | ast_id_map::AstIdMap, hygiene::HygieneFrame, BuiltinDeriveExpander, BuiltinFnLikeExpander, |
12 | HirFileId, HirFileIdRepr, LazyMacroId, MacroCallId, MacroCallLoc, MacroDefId, MacroDefKind, | 12 | EagerCallLoc, EagerMacroId, HirFileId, HirFileIdRepr, LazyMacroId, MacroCallId, MacroCallLoc, |
13 | MacroFile, ProcMacroExpander, | 13 | MacroDefId, MacroDefKind, MacroFile, ProcMacroExpander, |
14 | }; | 14 | }; |
15 | 15 | ||
16 | /// Total limit on the number of tokens produced by any macro invocation. | 16 | /// Total limit on the number of tokens produced by any macro invocation. |
@@ -94,6 +94,8 @@ pub trait AstDatabase: SourceDatabase { | |||
94 | fn intern_eager_expansion(&self, eager: EagerCallLoc) -> EagerMacroId; | 94 | fn intern_eager_expansion(&self, eager: EagerCallLoc) -> EagerMacroId; |
95 | 95 | ||
96 | fn expand_proc_macro(&self, call: MacroCallId) -> Result<tt::Subtree, mbe::ExpandError>; | 96 | fn expand_proc_macro(&self, call: MacroCallId) -> Result<tt::Subtree, mbe::ExpandError>; |
97 | |||
98 | fn hygiene_frame(&self, file_id: HirFileId) -> Arc<HygieneFrame>; | ||
97 | } | 99 | } |
98 | 100 | ||
99 | /// This expands the given macro call, but with different arguments. This is | 101 | /// This expands the given macro call, but with different arguments. This is |
@@ -369,6 +371,10 @@ fn parse_macro_with_arg( | |||
369 | } | 371 | } |
370 | } | 372 | } |
371 | 373 | ||
374 | fn hygiene_frame(db: &dyn AstDatabase, file_id: HirFileId) -> Arc<HygieneFrame> { | ||
375 | Arc::new(HygieneFrame::new(db, file_id)) | ||
376 | } | ||
377 | |||
372 | /// Given a `MacroCallId`, return what `FragmentKind` it belongs to. | 378 | /// Given a `MacroCallId`, return what `FragmentKind` it belongs to. |
373 | /// FIXME: Not completed | 379 | /// FIXME: Not completed |
374 | fn to_fragment_kind(db: &dyn AstDatabase, id: MacroCallId) -> FragmentKind { | 380 | fn to_fragment_kind(db: &dyn AstDatabase, id: MacroCallId) -> FragmentKind { |
diff --git a/crates/hir_expand/src/hygiene.rs b/crates/hir_expand/src/hygiene.rs index 7ab0a5e52..8db581b77 100644 --- a/crates/hir_expand/src/hygiene.rs +++ b/crates/hir_expand/src/hygiene.rs | |||
@@ -2,65 +2,209 @@ | |||
2 | //! | 2 | //! |
3 | //! Specifically, `ast` + `Hygiene` allows you to create a `Name`. Note that, at | 3 | //! Specifically, `ast` + `Hygiene` allows you to create a `Name`. Note that, at |
4 | //! this moment, this is horribly incomplete and handles only `$crate`. | 4 | //! this moment, this is horribly incomplete and handles only `$crate`. |
5 | use std::sync::Arc; | ||
6 | |||
5 | use base_db::CrateId; | 7 | use base_db::CrateId; |
6 | use either::Either; | 8 | use either::Either; |
7 | use syntax::ast; | 9 | use mbe::Origin; |
10 | use parser::SyntaxKind; | ||
11 | use syntax::{ast, AstNode, SyntaxNode, TextRange, TextSize}; | ||
8 | 12 | ||
9 | use crate::{ | 13 | use crate::{ |
10 | db::AstDatabase, | 14 | db::{self, AstDatabase}, |
11 | name::{AsName, Name}, | 15 | name::{AsName, Name}, |
12 | HirFileId, HirFileIdRepr, MacroCallId, MacroDefKind, | 16 | HirFileId, HirFileIdRepr, InFile, MacroCallId, MacroCallLoc, MacroDefKind, MacroFile, |
13 | }; | 17 | }; |
14 | 18 | ||
15 | #[derive(Clone, Debug)] | 19 | #[derive(Clone, Debug)] |
16 | pub struct Hygiene { | 20 | pub struct Hygiene { |
17 | // This is what `$crate` expands to | 21 | frames: Option<HygieneFrames>, |
18 | def_crate: Option<CrateId>, | ||
19 | |||
20 | // Indicate this is a local inner macro | ||
21 | local_inner: bool, | ||
22 | } | 22 | } |
23 | 23 | ||
24 | impl Hygiene { | 24 | impl Hygiene { |
25 | pub fn new(db: &dyn AstDatabase, file_id: HirFileId) -> Hygiene { | 25 | pub fn new(db: &dyn AstDatabase, file_id: HirFileId) -> Hygiene { |
26 | let (def_crate, local_inner) = match file_id.0 { | 26 | Hygiene { frames: Some(HygieneFrames::new(db, file_id.clone())) } |
27 | HirFileIdRepr::FileId(_) => (None, false), | ||
28 | HirFileIdRepr::MacroFile(macro_file) => match macro_file.macro_call_id { | ||
29 | MacroCallId::LazyMacro(id) => { | ||
30 | let loc = db.lookup_intern_macro(id); | ||
31 | match loc.def.kind { | ||
32 | MacroDefKind::Declarative => (Some(loc.def.krate), loc.def.local_inner), | ||
33 | MacroDefKind::BuiltIn(_) => (Some(loc.def.krate), false), | ||
34 | MacroDefKind::BuiltInDerive(_) => (None, false), | ||
35 | MacroDefKind::BuiltInEager(_) => (None, false), | ||
36 | MacroDefKind::ProcMacro(_) => (None, false), | ||
37 | } | ||
38 | } | ||
39 | MacroCallId::EagerMacro(_id) => (None, false), | ||
40 | }, | ||
41 | }; | ||
42 | Hygiene { def_crate, local_inner } | ||
43 | } | 27 | } |
44 | 28 | ||
45 | pub fn new_unhygienic() -> Hygiene { | 29 | pub fn new_unhygienic() -> Hygiene { |
46 | Hygiene { def_crate: None, local_inner: false } | 30 | Hygiene { frames: None } |
47 | } | 31 | } |
48 | 32 | ||
49 | // FIXME: this should just return name | 33 | // FIXME: this should just return name |
50 | pub fn name_ref_to_name(&self, name_ref: ast::NameRef) -> Either<Name, CrateId> { | 34 | pub fn name_ref_to_name(&self, name_ref: ast::NameRef) -> Either<Name, CrateId> { |
51 | if let Some(def_crate) = self.def_crate { | 35 | if let Some(frames) = &self.frames { |
52 | if name_ref.text() == "$crate" { | 36 | if name_ref.text() == "$crate" { |
53 | return Either::Right(def_crate); | 37 | if let Some(krate) = frames.root_crate(name_ref.syntax()) { |
38 | return Either::Right(krate); | ||
39 | } | ||
54 | } | 40 | } |
55 | } | 41 | } |
42 | |||
56 | Either::Left(name_ref.as_name()) | 43 | Either::Left(name_ref.as_name()) |
57 | } | 44 | } |
58 | 45 | ||
59 | pub fn local_inner_macros(&self) -> Option<CrateId> { | 46 | pub fn local_inner_macros(&self, path: ast::Path) -> Option<CrateId> { |
60 | if self.local_inner { | 47 | let mut token = path.syntax().first_token()?.text_range(); |
61 | self.def_crate | 48 | let frames = self.frames.as_ref()?; |
62 | } else { | 49 | let mut current = frames.0.clone(); |
63 | None | 50 | |
51 | loop { | ||
52 | let (mapped, origin) = current.expansion.as_ref()?.map_ident_up(token)?; | ||
53 | if origin == Origin::Def { | ||
54 | return if current.local_inner { frames.root_crate(path.syntax()) } else { None }; | ||
55 | } | ||
56 | current = current.call_site.as_ref()?.clone(); | ||
57 | token = mapped.value; | ||
58 | } | ||
59 | } | ||
60 | } | ||
61 | |||
62 | #[derive(Clone, Debug)] | ||
63 | struct HygieneFrames(Arc<HygieneFrame>); | ||
64 | |||
65 | #[derive(Clone, Debug, Eq, PartialEq)] | ||
66 | pub struct HygieneFrame { | ||
67 | expansion: Option<HygieneInfo>, | ||
68 | |||
69 | // Indicate this is a local inner macro | ||
70 | local_inner: bool, | ||
71 | krate: Option<CrateId>, | ||
72 | |||
73 | call_site: Option<Arc<HygieneFrame>>, | ||
74 | def_site: Option<Arc<HygieneFrame>>, | ||
75 | } | ||
76 | |||
77 | impl HygieneFrames { | ||
78 | fn new(db: &dyn AstDatabase, file_id: HirFileId) -> Self { | ||
79 | HygieneFrames(Arc::new(HygieneFrame::new(db, file_id))) | ||
80 | } | ||
81 | |||
82 | fn root_crate(&self, node: &SyntaxNode) -> Option<CrateId> { | ||
83 | let mut token = node.first_token()?.text_range(); | ||
84 | let mut result = self.0.krate; | ||
85 | let mut current = self.0.clone(); | ||
86 | |||
87 | while let Some((mapped, origin)) = | ||
88 | current.expansion.as_ref().and_then(|it| it.map_ident_up(token)) | ||
89 | { | ||
90 | result = current.krate; | ||
91 | |||
92 | let site = match origin { | ||
93 | Origin::Def => ¤t.def_site, | ||
94 | Origin::Call => ¤t.call_site, | ||
95 | }; | ||
96 | |||
97 | let site = match site { | ||
98 | None => break, | ||
99 | Some(it) => it, | ||
100 | }; | ||
101 | |||
102 | current = site.clone(); | ||
103 | token = mapped.value; | ||
64 | } | 104 | } |
105 | |||
106 | result | ||
107 | } | ||
108 | } | ||
109 | |||
110 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
111 | struct HygieneInfo { | ||
112 | arg_start: InFile<TextSize>, | ||
113 | /// The `macro_rules!` arguments. | ||
114 | def_start: Option<InFile<TextSize>>, | ||
115 | |||
116 | macro_def: Arc<(db::TokenExpander, mbe::TokenMap)>, | ||
117 | macro_arg: Arc<(tt::Subtree, mbe::TokenMap)>, | ||
118 | exp_map: Arc<mbe::TokenMap>, | ||
119 | } | ||
120 | |||
121 | impl HygieneInfo { | ||
122 | fn map_ident_up(&self, token: TextRange) -> Option<(InFile<TextRange>, Origin)> { | ||
123 | let token_id = self.exp_map.token_by_range(token)?; | ||
124 | |||
125 | let (token_id, origin) = self.macro_def.0.map_id_up(token_id); | ||
126 | let (token_map, tt) = match origin { | ||
127 | mbe::Origin::Call => (&self.macro_arg.1, self.arg_start), | ||
128 | mbe::Origin::Def => ( | ||
129 | &self.macro_def.1, | ||
130 | self.def_start | ||
131 | .as_ref() | ||
132 | .expect("`Origin::Def` used with non-`macro_rules!` macro") | ||
133 | .clone(), | ||
134 | ), | ||
135 | }; | ||
136 | |||
137 | let range = token_map.range_by_token(token_id)?.by_kind(SyntaxKind::IDENT)?; | ||
138 | Some((tt.with_value(range + tt.value), origin)) | ||
139 | } | ||
140 | } | ||
141 | |||
142 | fn make_hygiene_info( | ||
143 | db: &dyn AstDatabase, | ||
144 | macro_file: MacroFile, | ||
145 | loc: &MacroCallLoc, | ||
146 | ) -> Option<HygieneInfo> { | ||
147 | let arg_tt = loc.kind.arg(db)?; | ||
148 | |||
149 | let def_offset = loc.def.ast_id.and_then(|id| { | ||
150 | let def_tt = match id.to_node(db) { | ||
151 | ast::Macro::MacroRules(mac) => mac.token_tree()?.syntax().text_range().start(), | ||
152 | ast::Macro::MacroDef(_) => return None, | ||
153 | }; | ||
154 | Some(InFile::new(id.file_id, def_tt)) | ||
155 | }); | ||
156 | |||
157 | let macro_def = db.macro_def(loc.def)?; | ||
158 | let (_, exp_map) = db.parse_macro_expansion(macro_file).value?; | ||
159 | let macro_arg = db.macro_arg(macro_file.macro_call_id)?; | ||
160 | |||
161 | Some(HygieneInfo { | ||
162 | arg_start: InFile::new(loc.kind.file_id(), arg_tt.text_range().start()), | ||
163 | def_start: def_offset, | ||
164 | macro_arg, | ||
165 | macro_def, | ||
166 | exp_map, | ||
167 | }) | ||
168 | } | ||
169 | |||
170 | impl HygieneFrame { | ||
171 | pub(crate) fn new(db: &dyn AstDatabase, file_id: HirFileId) -> HygieneFrame { | ||
172 | let (info, krate, local_inner) = match file_id.0 { | ||
173 | HirFileIdRepr::FileId(_) => (None, None, false), | ||
174 | HirFileIdRepr::MacroFile(macro_file) => match macro_file.macro_call_id { | ||
175 | MacroCallId::EagerMacro(_id) => (None, None, false), | ||
176 | MacroCallId::LazyMacro(id) => { | ||
177 | let loc = db.lookup_intern_macro(id); | ||
178 | let info = make_hygiene_info(db, macro_file, &loc); | ||
179 | match loc.def.kind { | ||
180 | MacroDefKind::Declarative => { | ||
181 | (info, Some(loc.def.krate), loc.def.local_inner) | ||
182 | } | ||
183 | MacroDefKind::BuiltIn(_) => (info, Some(loc.def.krate), false), | ||
184 | MacroDefKind::BuiltInDerive(_) => (info, None, false), | ||
185 | MacroDefKind::BuiltInEager(_) => (info, None, false), | ||
186 | MacroDefKind::ProcMacro(_) => (info, None, false), | ||
187 | } | ||
188 | } | ||
189 | }, | ||
190 | }; | ||
191 | |||
192 | let info = match info { | ||
193 | None => { | ||
194 | return HygieneFrame { | ||
195 | expansion: None, | ||
196 | local_inner, | ||
197 | krate, | ||
198 | call_site: None, | ||
199 | def_site: None, | ||
200 | }; | ||
201 | } | ||
202 | Some(it) => it, | ||
203 | }; | ||
204 | |||
205 | let def_site = info.def_start.map(|it| db.hygiene_frame(it.file_id)); | ||
206 | let call_site = Some(db.hygiene_frame(info.arg_start.file_id)); | ||
207 | |||
208 | HygieneFrame { expansion: Some(info), local_inner, krate, call_site, def_site } | ||
65 | } | 209 | } |
66 | } | 210 | } |
diff --git a/crates/hir_expand/src/name.rs b/crates/hir_expand/src/name.rs index 2f44876a8..95d853b6d 100644 --- a/crates/hir_expand/src/name.rs +++ b/crates/hir_expand/src/name.rs | |||
@@ -164,6 +164,7 @@ pub mod known { | |||
164 | future, | 164 | future, |
165 | result, | 165 | result, |
166 | boxed, | 166 | boxed, |
167 | option, | ||
167 | // Components of known path (type name) | 168 | // Components of known path (type name) |
168 | Iterator, | 169 | Iterator, |
169 | IntoIterator, | 170 | IntoIterator, |
@@ -172,6 +173,7 @@ pub mod known { | |||
172 | Ok, | 173 | Ok, |
173 | Future, | 174 | Future, |
174 | Result, | 175 | Result, |
176 | Option, | ||
175 | Output, | 177 | Output, |
176 | Target, | 178 | Target, |
177 | Box, | 179 | Box, |
diff --git a/crates/hir_ty/src/diagnostics.rs b/crates/hir_ty/src/diagnostics.rs index 14e18f5a1..c67a289f2 100644 --- a/crates/hir_ty/src/diagnostics.rs +++ b/crates/hir_ty/src/diagnostics.rs | |||
@@ -186,9 +186,10 @@ impl Diagnostic for MissingMatchArms { | |||
186 | } | 186 | } |
187 | } | 187 | } |
188 | 188 | ||
189 | // Diagnostic: missing-ok-in-tail-expr | 189 | // Diagnostic: missing-ok-or-some-in-tail-expr |
190 | // | 190 | // |
191 | // This diagnostic is triggered if block that should return `Result` returns a value not wrapped in `Ok`. | 191 | // This diagnostic is triggered if a block that should return `Result` returns a value not wrapped in `Ok`, |
192 | // or if a block that should return `Option` returns a value not wrapped in `Some`. | ||
192 | // | 193 | // |
193 | // Example: | 194 | // Example: |
194 | // | 195 | // |
@@ -198,17 +199,19 @@ impl Diagnostic for MissingMatchArms { | |||
198 | // } | 199 | // } |
199 | // ``` | 200 | // ``` |
200 | #[derive(Debug)] | 201 | #[derive(Debug)] |
201 | pub struct MissingOkInTailExpr { | 202 | pub struct MissingOkOrSomeInTailExpr { |
202 | pub file: HirFileId, | 203 | pub file: HirFileId, |
203 | pub expr: AstPtr<ast::Expr>, | 204 | pub expr: AstPtr<ast::Expr>, |
205 | // `Some` or `Ok` depending on whether the return type is Result or Option | ||
206 | pub required: String, | ||
204 | } | 207 | } |
205 | 208 | ||
206 | impl Diagnostic for MissingOkInTailExpr { | 209 | impl Diagnostic for MissingOkOrSomeInTailExpr { |
207 | fn code(&self) -> DiagnosticCode { | 210 | fn code(&self) -> DiagnosticCode { |
208 | DiagnosticCode("missing-ok-in-tail-expr") | 211 | DiagnosticCode("missing-ok-or-some-in-tail-expr") |
209 | } | 212 | } |
210 | fn message(&self) -> String { | 213 | fn message(&self) -> String { |
211 | "wrap return expression in Ok".to_string() | 214 | format!("wrap return expression in {}", self.required) |
212 | } | 215 | } |
213 | fn display_source(&self) -> InFile<SyntaxNodePtr> { | 216 | fn display_source(&self) -> InFile<SyntaxNodePtr> { |
214 | InFile { file_id: self.file, value: self.expr.clone().into() } | 217 | InFile { file_id: self.file, value: self.expr.clone().into() } |
diff --git a/crates/hir_ty/src/diagnostics/expr.rs b/crates/hir_ty/src/diagnostics/expr.rs index b4e453411..a1c484fdf 100644 --- a/crates/hir_ty/src/diagnostics/expr.rs +++ b/crates/hir_ty/src/diagnostics/expr.rs | |||
@@ -11,8 +11,8 @@ use crate::{ | |||
11 | db::HirDatabase, | 11 | db::HirDatabase, |
12 | diagnostics::{ | 12 | diagnostics::{ |
13 | match_check::{is_useful, MatchCheckCtx, Matrix, PatStack, Usefulness}, | 13 | match_check::{is_useful, MatchCheckCtx, Matrix, PatStack, Usefulness}, |
14 | MismatchedArgCount, MissingFields, MissingMatchArms, MissingOkInTailExpr, MissingPatFields, | 14 | MismatchedArgCount, MissingFields, MissingMatchArms, MissingOkOrSomeInTailExpr, |
15 | RemoveThisSemicolon, | 15 | MissingPatFields, RemoveThisSemicolon, |
16 | }, | 16 | }, |
17 | utils::variant_data, | 17 | utils::variant_data, |
18 | ApplicationTy, InferenceResult, Ty, TypeCtor, | 18 | ApplicationTy, InferenceResult, Ty, TypeCtor, |
@@ -306,27 +306,40 @@ impl<'a, 'b> ExprValidator<'a, 'b> { | |||
306 | }; | 306 | }; |
307 | 307 | ||
308 | let core_result_path = path![core::result::Result]; | 308 | let core_result_path = path![core::result::Result]; |
309 | let core_option_path = path![core::option::Option]; | ||
309 | 310 | ||
310 | let resolver = self.owner.resolver(db.upcast()); | 311 | let resolver = self.owner.resolver(db.upcast()); |
311 | let core_result_enum = match resolver.resolve_known_enum(db.upcast(), &core_result_path) { | 312 | let core_result_enum = match resolver.resolve_known_enum(db.upcast(), &core_result_path) { |
312 | Some(it) => it, | 313 | Some(it) => it, |
313 | _ => return, | 314 | _ => return, |
314 | }; | 315 | }; |
316 | let core_option_enum = match resolver.resolve_known_enum(db.upcast(), &core_option_path) { | ||
317 | Some(it) => it, | ||
318 | _ => return, | ||
319 | }; | ||
315 | 320 | ||
316 | let core_result_ctor = TypeCtor::Adt(AdtId::EnumId(core_result_enum)); | 321 | let core_result_ctor = TypeCtor::Adt(AdtId::EnumId(core_result_enum)); |
317 | let params = match &mismatch.expected { | 322 | let core_option_ctor = TypeCtor::Adt(AdtId::EnumId(core_option_enum)); |
323 | |||
324 | let (params, required) = match &mismatch.expected { | ||
318 | Ty::Apply(ApplicationTy { ctor, parameters }) if ctor == &core_result_ctor => { | 325 | Ty::Apply(ApplicationTy { ctor, parameters }) if ctor == &core_result_ctor => { |
319 | parameters | 326 | (parameters, "Ok".to_string()) |
327 | } | ||
328 | Ty::Apply(ApplicationTy { ctor, parameters }) if ctor == &core_option_ctor => { | ||
329 | (parameters, "Some".to_string()) | ||
320 | } | 330 | } |
321 | _ => return, | 331 | _ => return, |
322 | }; | 332 | }; |
323 | 333 | ||
324 | if params.len() == 2 && params[0] == mismatch.actual { | 334 | if params.len() > 0 && params[0] == mismatch.actual { |
325 | let (_, source_map) = db.body_with_source_map(self.owner.into()); | 335 | let (_, source_map) = db.body_with_source_map(self.owner.into()); |
326 | 336 | ||
327 | if let Ok(source_ptr) = source_map.expr_syntax(id) { | 337 | if let Ok(source_ptr) = source_map.expr_syntax(id) { |
328 | self.sink | 338 | self.sink.push(MissingOkOrSomeInTailExpr { |
329 | .push(MissingOkInTailExpr { file: source_ptr.file_id, expr: source_ptr.value }); | 339 | file: source_ptr.file_id, |
340 | expr: source_ptr.value, | ||
341 | required, | ||
342 | }); | ||
330 | } | 343 | } |
331 | } | 344 | } |
332 | } | 345 | } |
diff --git a/crates/hir_ty/src/tests/macros.rs b/crates/hir_ty/src/tests/macros.rs index 1953da7be..c64f0b5b5 100644 --- a/crates/hir_ty/src/tests/macros.rs +++ b/crates/hir_ty/src/tests/macros.rs | |||
@@ -371,6 +371,37 @@ expand!(); | |||
371 | } | 371 | } |
372 | 372 | ||
373 | #[test] | 373 | #[test] |
374 | fn infer_macro_with_dollar_crate_in_def_site() { | ||
375 | check_types( | ||
376 | r#" | ||
377 | //- /main.rs crate:main deps:foo | ||
378 | use foo::expand; | ||
379 | |||
380 | macro_rules! list { | ||
381 | ($($tt:tt)*) => { $($tt)* } | ||
382 | } | ||
383 | |||
384 | fn test() { | ||
385 | let r = expand!(); | ||
386 | r; | ||
387 | //^ u128 | ||
388 | } | ||
389 | |||
390 | //- /lib.rs crate:foo | ||
391 | #[macro_export] | ||
392 | macro_rules! expand { | ||
393 | () => { list!($crate::m!()) }; | ||
394 | } | ||
395 | |||
396 | #[macro_export] | ||
397 | macro_rules! m { | ||
398 | () => { 0u128 }; | ||
399 | } | ||
400 | "#, | ||
401 | ); | ||
402 | } | ||
403 | |||
404 | #[test] | ||
374 | fn infer_type_value_non_legacy_macro_use_as() { | 405 | fn infer_type_value_non_legacy_macro_use_as() { |
375 | check_infer( | 406 | check_infer( |
376 | r#" | 407 | r#" |
diff --git a/crates/ide/src/diagnostics.rs b/crates/ide/src/diagnostics.rs index 6931a6190..055c0a79c 100644 --- a/crates/ide/src/diagnostics.rs +++ b/crates/ide/src/diagnostics.rs | |||
@@ -125,7 +125,7 @@ pub(crate) fn diagnostics( | |||
125 | .on::<hir::diagnostics::MissingFields, _>(|d| { | 125 | .on::<hir::diagnostics::MissingFields, _>(|d| { |
126 | res.borrow_mut().push(diagnostic_with_fix(d, &sema)); | 126 | res.borrow_mut().push(diagnostic_with_fix(d, &sema)); |
127 | }) | 127 | }) |
128 | .on::<hir::diagnostics::MissingOkInTailExpr, _>(|d| { | 128 | .on::<hir::diagnostics::MissingOkOrSomeInTailExpr, _>(|d| { |
129 | res.borrow_mut().push(diagnostic_with_fix(d, &sema)); | 129 | res.borrow_mut().push(diagnostic_with_fix(d, &sema)); |
130 | }) | 130 | }) |
131 | .on::<hir::diagnostics::NoSuchField, _>(|d| { | 131 | .on::<hir::diagnostics::NoSuchField, _>(|d| { |
@@ -305,6 +305,40 @@ mod tests { | |||
305 | } | 305 | } |
306 | 306 | ||
307 | #[test] | 307 | #[test] |
308 | fn test_wrap_return_type_option() { | ||
309 | check_fix( | ||
310 | r#" | ||
311 | //- /main.rs crate:main deps:core | ||
312 | use core::option::Option::{self, Some, None}; | ||
313 | |||
314 | fn div(x: i32, y: i32) -> Option<i32> { | ||
315 | if y == 0 { | ||
316 | return None; | ||
317 | } | ||
318 | x / y$0 | ||
319 | } | ||
320 | //- /core/lib.rs crate:core | ||
321 | pub mod result { | ||
322 | pub enum Result<T, E> { Ok(T), Err(E) } | ||
323 | } | ||
324 | pub mod option { | ||
325 | pub enum Option<T> { Some(T), None } | ||
326 | } | ||
327 | "#, | ||
328 | r#" | ||
329 | use core::option::Option::{self, Some, None}; | ||
330 | |||
331 | fn div(x: i32, y: i32) -> Option<i32> { | ||
332 | if y == 0 { | ||
333 | return None; | ||
334 | } | ||
335 | Some(x / y) | ||
336 | } | ||
337 | "#, | ||
338 | ); | ||
339 | } | ||
340 | |||
341 | #[test] | ||
308 | fn test_wrap_return_type() { | 342 | fn test_wrap_return_type() { |
309 | check_fix( | 343 | check_fix( |
310 | r#" | 344 | r#" |
@@ -321,6 +355,9 @@ fn div(x: i32, y: i32) -> Result<i32, ()> { | |||
321 | pub mod result { | 355 | pub mod result { |
322 | pub enum Result<T, E> { Ok(T), Err(E) } | 356 | pub enum Result<T, E> { Ok(T), Err(E) } |
323 | } | 357 | } |
358 | pub mod option { | ||
359 | pub enum Option<T> { Some(T), None } | ||
360 | } | ||
324 | "#, | 361 | "#, |
325 | r#" | 362 | r#" |
326 | use core::result::Result::{self, Ok, Err}; | 363 | use core::result::Result::{self, Ok, Err}; |
@@ -352,6 +389,9 @@ fn div<T>(x: T) -> Result<T, i32> { | |||
352 | pub mod result { | 389 | pub mod result { |
353 | pub enum Result<T, E> { Ok(T), Err(E) } | 390 | pub enum Result<T, E> { Ok(T), Err(E) } |
354 | } | 391 | } |
392 | pub mod option { | ||
393 | pub enum Option<T> { Some(T), None } | ||
394 | } | ||
355 | "#, | 395 | "#, |
356 | r#" | 396 | r#" |
357 | use core::result::Result::{self, Ok, Err}; | 397 | use core::result::Result::{self, Ok, Err}; |
@@ -385,6 +425,9 @@ fn div(x: i32, y: i32) -> MyResult<i32> { | |||
385 | pub mod result { | 425 | pub mod result { |
386 | pub enum Result<T, E> { Ok(T), Err(E) } | 426 | pub enum Result<T, E> { Ok(T), Err(E) } |
387 | } | 427 | } |
428 | pub mod option { | ||
429 | pub enum Option<T> { Some(T), None } | ||
430 | } | ||
388 | "#, | 431 | "#, |
389 | r#" | 432 | r#" |
390 | use core::result::Result::{self, Ok, Err}; | 433 | use core::result::Result::{self, Ok, Err}; |
@@ -414,12 +457,15 @@ fn foo() -> Result<(), i32> { 0 } | |||
414 | pub mod result { | 457 | pub mod result { |
415 | pub enum Result<T, E> { Ok(T), Err(E) } | 458 | pub enum Result<T, E> { Ok(T), Err(E) } |
416 | } | 459 | } |
460 | pub mod option { | ||
461 | pub enum Option<T> { Some(T), None } | ||
462 | } | ||
417 | "#, | 463 | "#, |
418 | ); | 464 | ); |
419 | } | 465 | } |
420 | 466 | ||
421 | #[test] | 467 | #[test] |
422 | fn test_wrap_return_type_not_applicable_when_return_type_is_not_result() { | 468 | fn test_wrap_return_type_not_applicable_when_return_type_is_not_result_or_option() { |
423 | check_no_diagnostics( | 469 | check_no_diagnostics( |
424 | r#" | 470 | r#" |
425 | //- /main.rs crate:main deps:core | 471 | //- /main.rs crate:main deps:core |
@@ -433,6 +479,9 @@ fn foo() -> SomeOtherEnum { 0 } | |||
433 | pub mod result { | 479 | pub mod result { |
434 | pub enum Result<T, E> { Ok(T), Err(E) } | 480 | pub enum Result<T, E> { Ok(T), Err(E) } |
435 | } | 481 | } |
482 | pub mod option { | ||
483 | pub enum Option<T> { Some(T), None } | ||
484 | } | ||
436 | "#, | 485 | "#, |
437 | ); | 486 | ); |
438 | } | 487 | } |
diff --git a/crates/ide/src/diagnostics/fixes.rs b/crates/ide/src/diagnostics/fixes.rs index 71ec4df92..d7ad88ed5 100644 --- a/crates/ide/src/diagnostics/fixes.rs +++ b/crates/ide/src/diagnostics/fixes.rs | |||
@@ -3,7 +3,7 @@ | |||
3 | use hir::{ | 3 | use hir::{ |
4 | db::AstDatabase, | 4 | db::AstDatabase, |
5 | diagnostics::{ | 5 | diagnostics::{ |
6 | Diagnostic, IncorrectCase, MissingFields, MissingOkInTailExpr, NoSuchField, | 6 | Diagnostic, IncorrectCase, MissingFields, MissingOkOrSomeInTailExpr, NoSuchField, |
7 | RemoveThisSemicolon, UnresolvedModule, | 7 | RemoveThisSemicolon, UnresolvedModule, |
8 | }, | 8 | }, |
9 | HasSource, HirDisplay, InFile, Semantics, VariantDef, | 9 | HasSource, HirDisplay, InFile, Semantics, VariantDef, |
@@ -94,15 +94,17 @@ impl DiagnosticWithFix for MissingFields { | |||
94 | } | 94 | } |
95 | } | 95 | } |
96 | 96 | ||
97 | impl DiagnosticWithFix for MissingOkInTailExpr { | 97 | impl DiagnosticWithFix for MissingOkOrSomeInTailExpr { |
98 | fn fix(&self, sema: &Semantics<RootDatabase>) -> Option<Fix> { | 98 | fn fix(&self, sema: &Semantics<RootDatabase>) -> Option<Fix> { |
99 | let root = sema.db.parse_or_expand(self.file)?; | 99 | let root = sema.db.parse_or_expand(self.file)?; |
100 | let tail_expr = self.expr.to_node(&root); | 100 | let tail_expr = self.expr.to_node(&root); |
101 | let tail_expr_range = tail_expr.syntax().text_range(); | 101 | let tail_expr_range = tail_expr.syntax().text_range(); |
102 | let edit = TextEdit::replace(tail_expr_range, format!("Ok({})", tail_expr.syntax())); | 102 | let replacement = format!("{}({})", self.required, tail_expr.syntax()); |
103 | let edit = TextEdit::replace(tail_expr_range, replacement); | ||
103 | let source_change = | 104 | let source_change = |
104 | SourceFileEdit { file_id: self.file.original_file(sema.db), edit }.into(); | 105 | SourceFileEdit { file_id: self.file.original_file(sema.db), edit }.into(); |
105 | Some(Fix::new("Wrap with ok", source_change, tail_expr_range)) | 106 | let name = if self.required == "Ok" { "Wrap with Ok" } else { "Wrap with Some" }; |
107 | Some(Fix::new(name, source_change, tail_expr_range)) | ||
106 | } | 108 | } |
107 | } | 109 | } |
108 | 110 | ||
diff --git a/crates/ide_db/src/apply_change.rs b/crates/ide_db/src/apply_change.rs index 71c19ed13..c770a236b 100644 --- a/crates/ide_db/src/apply_change.rs +++ b/crates/ide_db/src/apply_change.rs | |||
@@ -145,6 +145,7 @@ impl RootDatabase { | |||
145 | hir::db::MacroDefQuery | 145 | hir::db::MacroDefQuery |
146 | hir::db::ParseMacroExpansionQuery | 146 | hir::db::ParseMacroExpansionQuery |
147 | hir::db::MacroExpandQuery | 147 | hir::db::MacroExpandQuery |
148 | hir::db::HygieneFrameQuery | ||
148 | 149 | ||
149 | // DefDatabase | 150 | // DefDatabase |
150 | hir::db::ItemTreeQuery | 151 | hir::db::ItemTreeQuery |
diff --git a/crates/mbe/src/mbe_expander/matcher.rs b/crates/mbe/src/mbe_expander/matcher.rs index fdc8844ce..c6d615c81 100644 --- a/crates/mbe/src/mbe_expander/matcher.rs +++ b/crates/mbe/src/mbe_expander/matcher.rs | |||
@@ -150,7 +150,7 @@ fn match_subtree( | |||
150 | res.add_err(err!("leftover tokens")); | 150 | res.add_err(err!("leftover tokens")); |
151 | } | 151 | } |
152 | } | 152 | } |
153 | Op::Var { name, kind } => { | 153 | Op::Var { name, kind, .. } => { |
154 | let kind = match kind { | 154 | let kind = match kind { |
155 | Some(k) => k, | 155 | Some(k) => k, |
156 | None => { | 156 | None => { |
diff --git a/crates/mbe/src/mbe_expander/transcriber.rs b/crates/mbe/src/mbe_expander/transcriber.rs index 720531237..27b2ac777 100644 --- a/crates/mbe/src/mbe_expander/transcriber.rs +++ b/crates/mbe/src/mbe_expander/transcriber.rs | |||
@@ -100,8 +100,8 @@ fn expand_subtree( | |||
100 | err = err.or(e); | 100 | err = err.or(e); |
101 | arena.push(tt.into()); | 101 | arena.push(tt.into()); |
102 | } | 102 | } |
103 | Op::Var { name, .. } => { | 103 | Op::Var { name, id, .. } => { |
104 | let ExpandResult { value: fragment, err: e } = expand_var(ctx, &name); | 104 | let ExpandResult { value: fragment, err: e } = expand_var(ctx, &name, *id); |
105 | err = err.or(e); | 105 | err = err.or(e); |
106 | push_fragment(arena, fragment); | 106 | push_fragment(arena, fragment); |
107 | } | 107 | } |
@@ -118,14 +118,11 @@ fn expand_subtree( | |||
118 | ExpandResult { value: tt::Subtree { delimiter: template.delimiter, token_trees: tts }, err } | 118 | ExpandResult { value: tt::Subtree { delimiter: template.delimiter, token_trees: tts }, err } |
119 | } | 119 | } |
120 | 120 | ||
121 | fn expand_var(ctx: &mut ExpandCtx, v: &SmolStr) -> ExpandResult<Fragment> { | 121 | fn expand_var(ctx: &mut ExpandCtx, v: &SmolStr, id: tt::TokenId) -> ExpandResult<Fragment> { |
122 | if v == "crate" { | 122 | // We already handle $crate case in mbe parser |
123 | // We simply produce identifier `$crate` here. And it will be resolved when lowering ast to Path. | 123 | debug_assert!(v != "crate"); |
124 | let tt = | 124 | |
125 | tt::Leaf::from(tt::Ident { text: "$crate".into(), id: tt::TokenId::unspecified() }) | 125 | if !ctx.bindings.contains(v) { |
126 | .into(); | ||
127 | ExpandResult::ok(Fragment::Tokens(tt)) | ||
128 | } else if !ctx.bindings.contains(v) { | ||
129 | // Note that it is possible to have a `$var` inside a macro which is not bound. | 126 | // Note that it is possible to have a `$var` inside a macro which is not bound. |
130 | // For example: | 127 | // For example: |
131 | // ``` | 128 | // ``` |
@@ -142,14 +139,8 @@ fn expand_var(ctx: &mut ExpandCtx, v: &SmolStr) -> ExpandResult<Fragment> { | |||
142 | let tt = tt::Subtree { | 139 | let tt = tt::Subtree { |
143 | delimiter: None, | 140 | delimiter: None, |
144 | token_trees: vec![ | 141 | token_trees: vec![ |
145 | tt::Leaf::from(tt::Punct { | 142 | tt::Leaf::from(tt::Punct { char: '$', spacing: tt::Spacing::Alone, id }).into(), |
146 | char: '$', | 143 | tt::Leaf::from(tt::Ident { text: v.clone(), id }).into(), |
147 | spacing: tt::Spacing::Alone, | ||
148 | id: tt::TokenId::unspecified(), | ||
149 | }) | ||
150 | .into(), | ||
151 | tt::Leaf::from(tt::Ident { text: v.clone(), id: tt::TokenId::unspecified() }) | ||
152 | .into(), | ||
153 | ], | 144 | ], |
154 | } | 145 | } |
155 | .into(); | 146 | .into(); |
diff --git a/crates/mbe/src/parser.rs b/crates/mbe/src/parser.rs index 2f3ebc831..f3047972d 100644 --- a/crates/mbe/src/parser.rs +++ b/crates/mbe/src/parser.rs | |||
@@ -8,7 +8,7 @@ use crate::{tt_iter::TtIter, ExpandError, MetaTemplate}; | |||
8 | 8 | ||
9 | #[derive(Clone, Debug, PartialEq, Eq)] | 9 | #[derive(Clone, Debug, PartialEq, Eq)] |
10 | pub(crate) enum Op { | 10 | pub(crate) enum Op { |
11 | Var { name: SmolStr, kind: Option<SmolStr> }, | 11 | Var { name: SmolStr, kind: Option<SmolStr>, id: tt::TokenId }, |
12 | Repeat { subtree: MetaTemplate, kind: RepeatKind, separator: Option<Separator> }, | 12 | Repeat { subtree: MetaTemplate, kind: RepeatKind, separator: Option<Separator> }, |
13 | Leaf(tt::Leaf), | 13 | Leaf(tt::Leaf), |
14 | Subtree(MetaTemplate), | 14 | Subtree(MetaTemplate), |
@@ -106,18 +106,25 @@ fn next_op<'a>(first: &tt::TokenTree, src: &mut TtIter<'a>, mode: Mode) -> Resul | |||
106 | } | 106 | } |
107 | let name = UNDERSCORE.clone(); | 107 | let name = UNDERSCORE.clone(); |
108 | let kind = eat_fragment_kind(src, mode)?; | 108 | let kind = eat_fragment_kind(src, mode)?; |
109 | Op::Var { name, kind } | 109 | let id = punct.id; |
110 | Op::Var { name, kind, id } | ||
111 | } | ||
112 | tt::Leaf::Ident(ident) if ident.text == "crate" => { | ||
113 | // We simply produce identifier `$crate` here. And it will be resolved when lowering ast to Path. | ||
114 | Op::Leaf(tt::Leaf::from(tt::Ident { text: "$crate".into(), id: ident.id })) | ||
110 | } | 115 | } |
111 | tt::Leaf::Ident(ident) => { | 116 | tt::Leaf::Ident(ident) => { |
112 | let name = ident.text.clone(); | 117 | let name = ident.text.clone(); |
113 | let kind = eat_fragment_kind(src, mode)?; | 118 | let kind = eat_fragment_kind(src, mode)?; |
114 | Op::Var { name, kind } | 119 | let id = ident.id; |
120 | Op::Var { name, kind, id } | ||
115 | } | 121 | } |
116 | tt::Leaf::Literal(lit) => { | 122 | tt::Leaf::Literal(lit) => { |
117 | if is_boolean_literal(&lit) { | 123 | if is_boolean_literal(&lit) { |
118 | let name = lit.text.clone(); | 124 | let name = lit.text.clone(); |
119 | let kind = eat_fragment_kind(src, mode)?; | 125 | let kind = eat_fragment_kind(src, mode)?; |
120 | Op::Var { name, kind } | 126 | let id = lit.id; |
127 | Op::Var { name, kind, id } | ||
121 | } else { | 128 | } else { |
122 | bail!("bad var 2"); | 129 | bail!("bad var 2"); |
123 | } | 130 | } |
diff --git a/crates/mbe/src/tests.rs b/crates/mbe/src/tests.rs index 1d9afb4fb..d854985c5 100644 --- a/crates/mbe/src/tests.rs +++ b/crates/mbe/src/tests.rs | |||
@@ -1080,6 +1080,19 @@ fn test_vertical_bar_with_pat() { | |||
1080 | } | 1080 | } |
1081 | 1081 | ||
1082 | #[test] | 1082 | #[test] |
1083 | fn test_dollar_crate_lhs_is_not_meta() { | ||
1084 | parse_macro( | ||
1085 | r#" | ||
1086 | macro_rules! foo { | ||
1087 | ($crate) => {}; | ||
1088 | () => {0}; | ||
1089 | } | ||
1090 | "#, | ||
1091 | ) | ||
1092 | .assert_expand_items(r#"foo!{}"#, r#"0"#); | ||
1093 | } | ||
1094 | |||
1095 | #[test] | ||
1083 | fn test_lifetime() { | 1096 | fn test_lifetime() { |
1084 | parse_macro( | 1097 | parse_macro( |
1085 | r#" | 1098 | r#" |