diff options
-rw-r--r-- | crates/ra_hir_expand/src/builtin_derive.rs | 28 | ||||
-rw-r--r-- | crates/ra_hir_expand/src/lib.rs | 4 | ||||
-rw-r--r-- | crates/ra_hir_expand/src/quote.rs | 27 | ||||
-rw-r--r-- | crates/ra_mbe/src/lib.rs | 18 | ||||
-rw-r--r-- | crates/ra_mbe/src/mbe_expander/matcher.rs | 2 | ||||
-rw-r--r-- | crates/ra_mbe/src/mbe_expander/transcriber.rs | 7 | ||||
-rw-r--r-- | crates/ra_mbe/src/subtree_source.rs | 12 | ||||
-rw-r--r-- | crates/ra_mbe/src/syntax_bridge.rs | 176 | ||||
-rw-r--r-- | crates/ra_mbe/src/tests.rs | 57 | ||||
-rw-r--r-- | crates/ra_tt/src/lib.rs | 22 | ||||
-rw-r--r-- | editors/code/src/test/fixtures/rust-diagnostics/error/E0277.json | 261 | ||||
-rw-r--r-- | editors/code/src/test/utils/diagnotics/rust.test.ts | 34 | ||||
-rw-r--r-- | editors/code/src/utils/diagnostics/rust.ts | 38 |
13 files changed, 605 insertions, 81 deletions
diff --git a/crates/ra_hir_expand/src/builtin_derive.rs b/crates/ra_hir_expand/src/builtin_derive.rs index b26441253..62c60e336 100644 --- a/crates/ra_hir_expand/src/builtin_derive.rs +++ b/crates/ra_hir_expand/src/builtin_derive.rs | |||
@@ -97,11 +97,24 @@ fn parse_adt(tt: &tt::Subtree) -> Result<BasicAdtInfo, mbe::ExpandError> { | |||
97 | 97 | ||
98 | fn make_type_args(n: usize, bound: Vec<tt::TokenTree>) -> Vec<tt::TokenTree> { | 98 | fn make_type_args(n: usize, bound: Vec<tt::TokenTree>) -> Vec<tt::TokenTree> { |
99 | let mut result = Vec::<tt::TokenTree>::new(); | 99 | let mut result = Vec::<tt::TokenTree>::new(); |
100 | result.push(tt::Leaf::Punct(tt::Punct { char: '<', spacing: tt::Spacing::Alone }).into()); | 100 | result.push( |
101 | tt::Leaf::Punct(tt::Punct { | ||
102 | char: '<', | ||
103 | spacing: tt::Spacing::Alone, | ||
104 | id: tt::TokenId::unspecified(), | ||
105 | }) | ||
106 | .into(), | ||
107 | ); | ||
101 | for i in 0..n { | 108 | for i in 0..n { |
102 | if i > 0 { | 109 | if i > 0 { |
103 | result | 110 | result.push( |
104 | .push(tt::Leaf::Punct(tt::Punct { char: ',', spacing: tt::Spacing::Alone }).into()); | 111 | tt::Leaf::Punct(tt::Punct { |
112 | char: ',', | ||
113 | spacing: tt::Spacing::Alone, | ||
114 | id: tt::TokenId::unspecified(), | ||
115 | }) | ||
116 | .into(), | ||
117 | ); | ||
105 | } | 118 | } |
106 | result.push( | 119 | result.push( |
107 | tt::Leaf::Ident(tt::Ident { | 120 | tt::Leaf::Ident(tt::Ident { |
@@ -112,7 +125,14 @@ fn make_type_args(n: usize, bound: Vec<tt::TokenTree>) -> Vec<tt::TokenTree> { | |||
112 | ); | 125 | ); |
113 | result.extend(bound.iter().cloned()); | 126 | result.extend(bound.iter().cloned()); |
114 | } | 127 | } |
115 | result.push(tt::Leaf::Punct(tt::Punct { char: '>', spacing: tt::Spacing::Alone }).into()); | 128 | result.push( |
129 | tt::Leaf::Punct(tt::Punct { | ||
130 | char: '>', | ||
131 | spacing: tt::Spacing::Alone, | ||
132 | id: tt::TokenId::unspecified(), | ||
133 | }) | ||
134 | .into(), | ||
135 | ); | ||
116 | result | 136 | result |
117 | } | 137 | } |
118 | 138 | ||
diff --git a/crates/ra_hir_expand/src/lib.rs b/crates/ra_hir_expand/src/lib.rs index cb4e1950b..2fa5d5140 100644 --- a/crates/ra_hir_expand/src/lib.rs +++ b/crates/ra_hir_expand/src/lib.rs | |||
@@ -227,7 +227,7 @@ impl ExpansionInfo { | |||
227 | let token_id = self.macro_arg.1.token_by_range(range)?; | 227 | let token_id = self.macro_arg.1.token_by_range(range)?; |
228 | let token_id = self.macro_def.0.map_id_down(token_id); | 228 | let token_id = self.macro_def.0.map_id_down(token_id); |
229 | 229 | ||
230 | let range = self.exp_map.range_by_token(token_id)?; | 230 | let range = self.exp_map.range_by_token(token_id)?.by_kind(token.value.kind())?; |
231 | 231 | ||
232 | let token = algo::find_covering_element(&self.expanded.value, range).into_token()?; | 232 | let token = algo::find_covering_element(&self.expanded.value, range).into_token()?; |
233 | 233 | ||
@@ -248,7 +248,7 @@ impl ExpansionInfo { | |||
248 | } | 248 | } |
249 | }; | 249 | }; |
250 | 250 | ||
251 | let range = token_map.range_by_token(token_id)?; | 251 | let range = token_map.range_by_token(token_id)?.by_kind(token.value.kind())?; |
252 | let token = algo::find_covering_element(&tt.value, range + tt.value.text_range().start()) | 252 | let token = algo::find_covering_element(&tt.value, range + tt.value.text_range().start()) |
253 | .into_token()?; | 253 | .into_token()?; |
254 | Some((tt.with_value(token), origin)) | 254 | Some((tt.with_value(token), origin)) |
diff --git a/crates/ra_hir_expand/src/quote.rs b/crates/ra_hir_expand/src/quote.rs index aa8a5f23f..49155fe62 100644 --- a/crates/ra_hir_expand/src/quote.rs +++ b/crates/ra_hir_expand/src/quote.rs | |||
@@ -16,7 +16,10 @@ macro_rules! __quote { | |||
16 | { | 16 | { |
17 | let children = $crate::__quote!($($tt)*); | 17 | let children = $crate::__quote!($($tt)*); |
18 | let subtree = tt::Subtree { | 18 | let subtree = tt::Subtree { |
19 | delimiter: Some(tt::Delimiter::$delim), | 19 | delimiter: Some(tt::Delimiter { |
20 | kind: tt::DelimiterKind::$delim, | ||
21 | id: tt::TokenId::unspecified(), | ||
22 | }), | ||
20 | token_trees: $crate::quote::IntoTt::to_tokens(children), | 23 | token_trees: $crate::quote::IntoTt::to_tokens(children), |
21 | }; | 24 | }; |
22 | subtree | 25 | subtree |
@@ -29,6 +32,7 @@ macro_rules! __quote { | |||
29 | tt::Leaf::Punct(tt::Punct { | 32 | tt::Leaf::Punct(tt::Punct { |
30 | char: $first, | 33 | char: $first, |
31 | spacing: tt::Spacing::Alone, | 34 | spacing: tt::Spacing::Alone, |
35 | id: tt::TokenId::unspecified(), | ||
32 | }).into() | 36 | }).into() |
33 | ] | 37 | ] |
34 | } | 38 | } |
@@ -40,10 +44,12 @@ macro_rules! __quote { | |||
40 | tt::Leaf::Punct(tt::Punct { | 44 | tt::Leaf::Punct(tt::Punct { |
41 | char: $first, | 45 | char: $first, |
42 | spacing: tt::Spacing::Joint, | 46 | spacing: tt::Spacing::Joint, |
47 | id: tt::TokenId::unspecified(), | ||
43 | }).into(), | 48 | }).into(), |
44 | tt::Leaf::Punct(tt::Punct { | 49 | tt::Leaf::Punct(tt::Punct { |
45 | char: $sec, | 50 | char: $sec, |
46 | spacing: tt::Spacing::Alone, | 51 | spacing: tt::Spacing::Alone, |
52 | id: tt::TokenId::unspecified(), | ||
47 | }).into() | 53 | }).into() |
48 | ] | 54 | ] |
49 | } | 55 | } |
@@ -179,15 +185,15 @@ macro_rules! impl_to_to_tokentrees { | |||
179 | } | 185 | } |
180 | 186 | ||
181 | impl_to_to_tokentrees! { | 187 | impl_to_to_tokentrees! { |
182 | u32 => self { tt::Literal{text: self.to_string().into()} }; | 188 | u32 => self { tt::Literal{text: self.to_string().into(), id: tt::TokenId::unspecified()} }; |
183 | usize => self { tt::Literal{text: self.to_string().into()}}; | 189 | usize => self { tt::Literal{text: self.to_string().into(), id: tt::TokenId::unspecified()}}; |
184 | i32 => self { tt::Literal{text: self.to_string().into()}}; | 190 | i32 => self { tt::Literal{text: self.to_string().into(), id: tt::TokenId::unspecified()}}; |
185 | tt::Leaf => self { self }; | 191 | tt::Leaf => self { self }; |
186 | tt::Literal => self { self }; | 192 | tt::Literal => self { self }; |
187 | tt::Ident => self { self }; | 193 | tt::Ident => self { self }; |
188 | tt::Punct => self { self }; | 194 | tt::Punct => self { self }; |
189 | &str => self { tt::Literal{text: format!("{:?}", self.escape_default().to_string()).into()}}; | 195 | &str => self { tt::Literal{text: format!("{:?}", self.escape_default().to_string()).into(), id: tt::TokenId::unspecified()}}; |
190 | String => self { tt::Literal{text: format!("{:?}", self.escape_default().to_string()).into()}} | 196 | String => self { tt::Literal{text: format!("{:?}", self.escape_default().to_string()).into(), id: tt::TokenId::unspecified()}} |
191 | } | 197 | } |
192 | 198 | ||
193 | #[cfg(test)] | 199 | #[cfg(test)] |
@@ -254,8 +260,13 @@ mod tests { | |||
254 | let fields = | 260 | let fields = |
255 | fields.iter().map(|it| quote!(#it: self.#it.clone(), ).token_trees.clone()).flatten(); | 261 | fields.iter().map(|it| quote!(#it: self.#it.clone(), ).token_trees.clone()).flatten(); |
256 | 262 | ||
257 | let list = | 263 | let list = tt::Subtree { |
258 | tt::Subtree { delimiter: Some(tt::Delimiter::Brace), token_trees: fields.collect() }; | 264 | delimiter: Some(tt::Delimiter { |
265 | kind: tt::DelimiterKind::Brace, | ||
266 | id: tt::TokenId::unspecified(), | ||
267 | }), | ||
268 | token_trees: fields.collect(), | ||
269 | }; | ||
259 | 270 | ||
260 | let quoted = quote! { | 271 | let quoted = quote! { |
261 | impl Clone for #struct_name { | 272 | impl Clone for #struct_name { |
diff --git a/crates/ra_mbe/src/lib.rs b/crates/ra_mbe/src/lib.rs index ce2deadf6..45dad2d10 100644 --- a/crates/ra_mbe/src/lib.rs +++ b/crates/ra_mbe/src/lib.rs | |||
@@ -67,7 +67,15 @@ impl Shift { | |||
67 | .token_trees | 67 | .token_trees |
68 | .iter() | 68 | .iter() |
69 | .filter_map(|tt| match tt { | 69 | .filter_map(|tt| match tt { |
70 | tt::TokenTree::Subtree(subtree) => max_id(subtree), | 70 | tt::TokenTree::Subtree(subtree) => { |
71 | let tree_id = max_id(subtree); | ||
72 | match subtree.delimiter { | ||
73 | Some(it) if it.id != tt::TokenId::unspecified() => { | ||
74 | Some(tree_id.map_or(it.id.0, |t| t.max(it.id.0))) | ||
75 | } | ||
76 | _ => tree_id, | ||
77 | } | ||
78 | } | ||
71 | tt::TokenTree::Leaf(tt::Leaf::Ident(ident)) | 79 | tt::TokenTree::Leaf(tt::Leaf::Ident(ident)) |
72 | if ident.id != tt::TokenId::unspecified() => | 80 | if ident.id != tt::TokenId::unspecified() => |
73 | { | 81 | { |
@@ -85,9 +93,13 @@ impl Shift { | |||
85 | match t { | 93 | match t { |
86 | tt::TokenTree::Leaf(leaf) => match leaf { | 94 | tt::TokenTree::Leaf(leaf) => match leaf { |
87 | tt::Leaf::Ident(ident) => ident.id = self.shift(ident.id), | 95 | tt::Leaf::Ident(ident) => ident.id = self.shift(ident.id), |
88 | _ => (), | 96 | tt::Leaf::Punct(punct) => punct.id = self.shift(punct.id), |
97 | tt::Leaf::Literal(lit) => lit.id = self.shift(lit.id), | ||
89 | }, | 98 | }, |
90 | tt::TokenTree::Subtree(tt) => self.shift_all(tt), | 99 | tt::TokenTree::Subtree(tt) => { |
100 | tt.delimiter.as_mut().map(|it: &mut Delimiter| it.id = self.shift(it.id)); | ||
101 | self.shift_all(tt) | ||
102 | } | ||
91 | } | 103 | } |
92 | } | 104 | } |
93 | } | 105 | } |
diff --git a/crates/ra_mbe/src/mbe_expander/matcher.rs b/crates/ra_mbe/src/mbe_expander/matcher.rs index 3f5136478..e36b5a412 100644 --- a/crates/ra_mbe/src/mbe_expander/matcher.rs +++ b/crates/ra_mbe/src/mbe_expander/matcher.rs | |||
@@ -106,7 +106,7 @@ fn match_subtree( | |||
106 | } | 106 | } |
107 | Op::TokenTree(tt::TokenTree::Subtree(lhs)) => { | 107 | Op::TokenTree(tt::TokenTree::Subtree(lhs)) => { |
108 | let rhs = src.expect_subtree().map_err(|()| err!("expected subtree"))?; | 108 | let rhs = src.expect_subtree().map_err(|()| err!("expected subtree"))?; |
109 | if lhs.delimiter != rhs.delimiter { | 109 | if lhs.delimiter_kind() != rhs.delimiter_kind() { |
110 | bail!("mismatched delimiter") | 110 | bail!("mismatched delimiter") |
111 | } | 111 | } |
112 | let mut src = TtIter::new(rhs); | 112 | let mut src = TtIter::new(rhs); |
diff --git a/crates/ra_mbe/src/mbe_expander/transcriber.rs b/crates/ra_mbe/src/mbe_expander/transcriber.rs index f7636db11..eda66cd50 100644 --- a/crates/ra_mbe/src/mbe_expander/transcriber.rs +++ b/crates/ra_mbe/src/mbe_expander/transcriber.rs | |||
@@ -108,7 +108,12 @@ fn expand_var(ctx: &mut ExpandCtx, v: &SmolStr) -> Result<Fragment, ExpandError> | |||
108 | let tt = tt::Subtree { | 108 | let tt = tt::Subtree { |
109 | delimiter: None, | 109 | delimiter: None, |
110 | token_trees: vec![ | 110 | token_trees: vec![ |
111 | tt::Leaf::from(tt::Punct { char: '$', spacing: tt::Spacing::Alone }).into(), | 111 | tt::Leaf::from(tt::Punct { |
112 | char: '$', | ||
113 | spacing: tt::Spacing::Alone, | ||
114 | id: tt::TokenId::unspecified(), | ||
115 | }) | ||
116 | .into(), | ||
112 | tt::Leaf::from(tt::Ident { text: v.clone(), id: tt::TokenId::unspecified() }) | 117 | tt::Leaf::from(tt::Ident { text: v.clone(), id: tt::TokenId::unspecified() }) |
113 | .into(), | 118 | .into(), |
114 | ], | 119 | ], |
diff --git a/crates/ra_mbe/src/subtree_source.rs b/crates/ra_mbe/src/subtree_source.rs index 061e9f20b..b841c39d3 100644 --- a/crates/ra_mbe/src/subtree_source.rs +++ b/crates/ra_mbe/src/subtree_source.rs | |||
@@ -70,11 +70,11 @@ impl<'a> SubtreeTokenSource<'a> { | |||
70 | } | 70 | } |
71 | Some(tt::TokenTree::Subtree(subtree)) => { | 71 | Some(tt::TokenTree::Subtree(subtree)) => { |
72 | self.cached_cursor.set(cursor.subtree().unwrap()); | 72 | self.cached_cursor.set(cursor.subtree().unwrap()); |
73 | cached.push(Some(convert_delim(subtree.delimiter, false))); | 73 | cached.push(Some(convert_delim(subtree.delimiter_kind(), false))); |
74 | } | 74 | } |
75 | None => { | 75 | None => { |
76 | if let Some(subtree) = cursor.end() { | 76 | if let Some(subtree) = cursor.end() { |
77 | cached.push(Some(convert_delim(subtree.delimiter, true))); | 77 | cached.push(Some(convert_delim(subtree.delimiter_kind(), true))); |
78 | self.cached_cursor.set(cursor.bump()); | 78 | self.cached_cursor.set(cursor.bump()); |
79 | } | 79 | } |
80 | } | 80 | } |
@@ -114,11 +114,11 @@ impl<'a> TokenSource for SubtreeTokenSource<'a> { | |||
114 | } | 114 | } |
115 | } | 115 | } |
116 | 116 | ||
117 | fn convert_delim(d: Option<tt::Delimiter>, closing: bool) -> TtToken { | 117 | fn convert_delim(d: Option<tt::DelimiterKind>, closing: bool) -> TtToken { |
118 | let (kinds, texts) = match d { | 118 | let (kinds, texts) = match d { |
119 | Some(tt::Delimiter::Parenthesis) => ([T!['('], T![')']], "()"), | 119 | Some(tt::DelimiterKind::Parenthesis) => ([T!['('], T![')']], "()"), |
120 | Some(tt::Delimiter::Brace) => ([T!['{'], T!['}']], "{}"), | 120 | Some(tt::DelimiterKind::Brace) => ([T!['{'], T!['}']], "{}"), |
121 | Some(tt::Delimiter::Bracket) => ([T!['['], T![']']], "[]"), | 121 | Some(tt::DelimiterKind::Bracket) => ([T!['['], T![']']], "[]"), |
122 | None => ([L_DOLLAR, R_DOLLAR], ""), | 122 | None => ([L_DOLLAR, R_DOLLAR], ""), |
123 | }; | 123 | }; |
124 | 124 | ||
diff --git a/crates/ra_mbe/src/syntax_bridge.rs b/crates/ra_mbe/src/syntax_bridge.rs index b8e2cfc1d..2c60430d1 100644 --- a/crates/ra_mbe/src/syntax_bridge.rs +++ b/crates/ra_mbe/src/syntax_bridge.rs | |||
@@ -5,17 +5,37 @@ use ra_syntax::{ | |||
5 | ast, AstToken, NodeOrToken, Parse, SmolStr, SyntaxKind, SyntaxKind::*, SyntaxNode, | 5 | ast, AstToken, NodeOrToken, Parse, SmolStr, SyntaxKind, SyntaxKind::*, SyntaxNode, |
6 | SyntaxTreeBuilder, TextRange, TextUnit, T, | 6 | SyntaxTreeBuilder, TextRange, TextUnit, T, |
7 | }; | 7 | }; |
8 | use rustc_hash::FxHashMap; | ||
8 | use std::iter::successors; | 9 | use std::iter::successors; |
9 | use tt::buffer::{Cursor, TokenBuffer}; | 10 | use tt::buffer::{Cursor, TokenBuffer}; |
10 | 11 | ||
11 | use crate::subtree_source::SubtreeTokenSource; | 12 | use crate::subtree_source::SubtreeTokenSource; |
12 | use crate::ExpandError; | 13 | use crate::ExpandError; |
13 | 14 | ||
15 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] | ||
16 | pub enum TokenTextRange { | ||
17 | Token(TextRange), | ||
18 | Delimiter(TextRange, TextRange), | ||
19 | } | ||
20 | |||
21 | impl TokenTextRange { | ||
22 | pub fn by_kind(self, kind: SyntaxKind) -> Option<TextRange> { | ||
23 | match self { | ||
24 | TokenTextRange::Token(it) => Some(it), | ||
25 | TokenTextRange::Delimiter(open, close) => match kind { | ||
26 | T!['{'] | T!['('] | T!['['] => Some(open), | ||
27 | T!['}'] | T![')'] | T![']'] => Some(close), | ||
28 | _ => None, | ||
29 | }, | ||
30 | } | ||
31 | } | ||
32 | } | ||
33 | |||
14 | /// Maps `tt::TokenId` to the relative range of the original token. | 34 | /// Maps `tt::TokenId` to the relative range of the original token. |
15 | #[derive(Debug, PartialEq, Eq, Default)] | 35 | #[derive(Debug, PartialEq, Eq, Default)] |
16 | pub struct TokenMap { | 36 | pub struct TokenMap { |
17 | /// Maps `tt::TokenId` to the *relative* source range. | 37 | /// Maps `tt::TokenId` to the *relative* source range. |
18 | entries: Vec<(tt::TokenId, TextRange)>, | 38 | entries: Vec<(tt::TokenId, TokenTextRange)>, |
19 | } | 39 | } |
20 | 40 | ||
21 | /// Convert the syntax tree (what user has written) to a `TokenTree` (what macro | 41 | /// Convert the syntax tree (what user has written) to a `TokenTree` (what macro |
@@ -71,17 +91,32 @@ pub fn token_tree_to_syntax_node( | |||
71 | 91 | ||
72 | impl TokenMap { | 92 | impl TokenMap { |
73 | pub fn token_by_range(&self, relative_range: TextRange) -> Option<tt::TokenId> { | 93 | pub fn token_by_range(&self, relative_range: TextRange) -> Option<tt::TokenId> { |
74 | let &(token_id, _) = self.entries.iter().find(|(_, range)| *range == relative_range)?; | 94 | let &(token_id, _) = self.entries.iter().find(|(_, range)| match range { |
95 | TokenTextRange::Token(it) => *it == relative_range, | ||
96 | TokenTextRange::Delimiter(open, close) => { | ||
97 | *open == relative_range || *close == relative_range | ||
98 | } | ||
99 | })?; | ||
75 | Some(token_id) | 100 | Some(token_id) |
76 | } | 101 | } |
77 | 102 | ||
78 | pub fn range_by_token(&self, token_id: tt::TokenId) -> Option<TextRange> { | 103 | pub fn range_by_token(&self, token_id: tt::TokenId) -> Option<TokenTextRange> { |
79 | let &(_, range) = self.entries.iter().find(|(tid, _)| *tid == token_id)?; | 104 | let &(_, range) = self.entries.iter().find(|(tid, _)| *tid == token_id)?; |
80 | Some(range) | 105 | Some(range) |
81 | } | 106 | } |
82 | 107 | ||
83 | fn insert(&mut self, token_id: tt::TokenId, relative_range: TextRange) { | 108 | fn insert(&mut self, token_id: tt::TokenId, relative_range: TextRange) { |
84 | self.entries.push((token_id, relative_range)); | 109 | self.entries.push((token_id, TokenTextRange::Token(relative_range))); |
110 | } | ||
111 | |||
112 | fn insert_delim( | ||
113 | &mut self, | ||
114 | token_id: tt::TokenId, | ||
115 | open_relative_range: TextRange, | ||
116 | close_relative_range: TextRange, | ||
117 | ) { | ||
118 | self.entries | ||
119 | .push((token_id, TokenTextRange::Delimiter(open_relative_range, close_relative_range))); | ||
85 | } | 120 | } |
86 | } | 121 | } |
87 | 122 | ||
@@ -121,7 +156,10 @@ fn convert_doc_comment(token: &ra_syntax::SyntaxToken) -> Option<Vec<tt::TokenTr | |||
121 | token_trees.push(mk_punct('!')); | 156 | token_trees.push(mk_punct('!')); |
122 | } | 157 | } |
123 | token_trees.push(tt::TokenTree::from(tt::Subtree { | 158 | token_trees.push(tt::TokenTree::from(tt::Subtree { |
124 | delimiter: Some(tt::Delimiter::Bracket), | 159 | delimiter: Some(tt::Delimiter { |
160 | kind: tt::DelimiterKind::Bracket, | ||
161 | id: tt::TokenId::unspecified(), | ||
162 | }), | ||
125 | token_trees: meta_tkns, | 163 | token_trees: meta_tkns, |
126 | })); | 164 | })); |
127 | 165 | ||
@@ -136,11 +174,15 @@ fn convert_doc_comment(token: &ra_syntax::SyntaxToken) -> Option<Vec<tt::TokenTr | |||
136 | } | 174 | } |
137 | 175 | ||
138 | fn mk_punct(c: char) -> tt::TokenTree { | 176 | fn mk_punct(c: char) -> tt::TokenTree { |
139 | tt::TokenTree::from(tt::Leaf::from(tt::Punct { char: c, spacing: tt::Spacing::Alone })) | 177 | tt::TokenTree::from(tt::Leaf::from(tt::Punct { |
178 | char: c, | ||
179 | spacing: tt::Spacing::Alone, | ||
180 | id: tt::TokenId::unspecified(), | ||
181 | })) | ||
140 | } | 182 | } |
141 | 183 | ||
142 | fn mk_doc_literal(comment: &ast::Comment) -> tt::TokenTree { | 184 | fn mk_doc_literal(comment: &ast::Comment) -> tt::TokenTree { |
143 | let lit = tt::Literal { text: doc_comment_text(comment) }; | 185 | let lit = tt::Literal { text: doc_comment_text(comment), id: tt::TokenId::unspecified() }; |
144 | 186 | ||
145 | tt::TokenTree::from(tt::Leaf::from(lit)) | 187 | tt::TokenTree::from(tt::Leaf::from(lit)) |
146 | } | 188 | } |
@@ -186,12 +228,16 @@ impl Convertor { | |||
186 | .last() | 228 | .last() |
187 | .unwrap(); | 229 | .unwrap(); |
188 | 230 | ||
189 | let (delimiter, skip_first) = match (first_child.kind(), last_child.kind()) { | 231 | let (delimiter_kind, skip_first) = match (first_child.kind(), last_child.kind()) { |
190 | (T!['('], T![')']) => (Some(tt::Delimiter::Parenthesis), true), | 232 | (T!['('], T![')']) => (Some(tt::DelimiterKind::Parenthesis), true), |
191 | (T!['{'], T!['}']) => (Some(tt::Delimiter::Brace), true), | 233 | (T!['{'], T!['}']) => (Some(tt::DelimiterKind::Brace), true), |
192 | (T!['['], T![']']) => (Some(tt::Delimiter::Bracket), true), | 234 | (T!['['], T![']']) => (Some(tt::DelimiterKind::Bracket), true), |
193 | _ => (None, false), | 235 | _ => (None, false), |
194 | }; | 236 | }; |
237 | let delimiter = delimiter_kind.map(|kind| tt::Delimiter { | ||
238 | kind, | ||
239 | id: self.alloc_delim(first_child.text_range(), last_child.text_range()), | ||
240 | }); | ||
195 | 241 | ||
196 | let mut token_trees = Vec::new(); | 242 | let mut token_trees = Vec::new(); |
197 | let mut child_iter = tt.children_with_tokens().skip(skip_first as usize).peekable(); | 243 | let mut child_iter = tt.children_with_tokens().skip(skip_first as usize).peekable(); |
@@ -223,25 +269,34 @@ impl Convertor { | |||
223 | .take(token.text().len() - 1) | 269 | .take(token.text().len() - 1) |
224 | .chain(std::iter::once(last_spacing)); | 270 | .chain(std::iter::once(last_spacing)); |
225 | for (char, spacing) in token.text().chars().zip(spacing_iter) { | 271 | for (char, spacing) in token.text().chars().zip(spacing_iter) { |
226 | token_trees.push(tt::Leaf::from(tt::Punct { char, spacing }).into()); | 272 | token_trees.push( |
273 | tt::Leaf::from(tt::Punct { | ||
274 | char, | ||
275 | spacing, | ||
276 | id: self.alloc(token.text_range()), | ||
277 | }) | ||
278 | .into(), | ||
279 | ); | ||
227 | } | 280 | } |
228 | } else { | 281 | } else { |
229 | let child: tt::TokenTree = | 282 | macro_rules! make_leaf { |
230 | if token.kind() == T![true] || token.kind() == T![false] { | 283 | ($i:ident) => { |
231 | tt::Leaf::from(tt::Literal { text: token.text().clone() }).into() | 284 | tt::$i { |
232 | } else if token.kind().is_keyword() | 285 | id: self.alloc(token.text_range()), |
233 | || token.kind() == IDENT | 286 | text: token.text().clone(), |
234 | || token.kind() == LIFETIME | 287 | } |
235 | { | 288 | .into() |
236 | let id = self.alloc(token.text_range()); | ||
237 | let text = token.text().clone(); | ||
238 | tt::Leaf::from(tt::Ident { text, id }).into() | ||
239 | } else if token.kind().is_literal() { | ||
240 | tt::Leaf::from(tt::Literal { text: token.text().clone() }).into() | ||
241 | } else { | ||
242 | return None; | ||
243 | }; | 289 | }; |
244 | token_trees.push(child); | 290 | } |
291 | |||
292 | let child: tt::Leaf = match token.kind() { | ||
293 | T![true] | T![false] => make_leaf!(Literal), | ||
294 | IDENT | LIFETIME => make_leaf!(Ident), | ||
295 | k if k.is_keyword() => make_leaf!(Ident), | ||
296 | k if k.is_literal() => make_leaf!(Literal), | ||
297 | _ => return None, | ||
298 | }; | ||
299 | token_trees.push(child.into()); | ||
245 | } | 300 | } |
246 | } | 301 | } |
247 | NodeOrToken::Node(node) => { | 302 | NodeOrToken::Node(node) => { |
@@ -266,11 +321,26 @@ impl Convertor { | |||
266 | self.map.insert(token_id, relative_range); | 321 | self.map.insert(token_id, relative_range); |
267 | token_id | 322 | token_id |
268 | } | 323 | } |
324 | |||
325 | fn alloc_delim( | ||
326 | &mut self, | ||
327 | open_abs_range: TextRange, | ||
328 | close_abs_range: TextRange, | ||
329 | ) -> tt::TokenId { | ||
330 | let open_relative_range = open_abs_range - self.global_offset; | ||
331 | let close_relative_range = close_abs_range - self.global_offset; | ||
332 | let token_id = tt::TokenId(self.next_id); | ||
333 | self.next_id += 1; | ||
334 | |||
335 | self.map.insert_delim(token_id, open_relative_range, close_relative_range); | ||
336 | token_id | ||
337 | } | ||
269 | } | 338 | } |
270 | 339 | ||
271 | struct TtTreeSink<'a> { | 340 | struct TtTreeSink<'a> { |
272 | buf: String, | 341 | buf: String, |
273 | cursor: Cursor<'a>, | 342 | cursor: Cursor<'a>, |
343 | open_delims: FxHashMap<tt::TokenId, TextUnit>, | ||
274 | text_pos: TextUnit, | 344 | text_pos: TextUnit, |
275 | inner: SyntaxTreeBuilder, | 345 | inner: SyntaxTreeBuilder, |
276 | token_map: TokenMap, | 346 | token_map: TokenMap, |
@@ -285,6 +355,7 @@ impl<'a> TtTreeSink<'a> { | |||
285 | TtTreeSink { | 355 | TtTreeSink { |
286 | buf: String::new(), | 356 | buf: String::new(), |
287 | cursor, | 357 | cursor, |
358 | open_delims: FxHashMap::default(), | ||
288 | text_pos: 0.into(), | 359 | text_pos: 0.into(), |
289 | inner: SyntaxTreeBuilder::default(), | 360 | inner: SyntaxTreeBuilder::default(), |
290 | roots: smallvec::SmallVec::new(), | 361 | roots: smallvec::SmallVec::new(), |
@@ -297,11 +368,11 @@ impl<'a> TtTreeSink<'a> { | |||
297 | } | 368 | } |
298 | } | 369 | } |
299 | 370 | ||
300 | fn delim_to_str(d: Option<tt::Delimiter>, closing: bool) -> SmolStr { | 371 | fn delim_to_str(d: Option<tt::DelimiterKind>, closing: bool) -> SmolStr { |
301 | let texts = match d { | 372 | let texts = match d { |
302 | Some(tt::Delimiter::Parenthesis) => "()", | 373 | Some(tt::DelimiterKind::Parenthesis) => "()", |
303 | Some(tt::Delimiter::Brace) => "{}", | 374 | Some(tt::DelimiterKind::Brace) => "{}", |
304 | Some(tt::Delimiter::Bracket) => "[]", | 375 | Some(tt::DelimiterKind::Bracket) => "[]", |
305 | None => return "".into(), | 376 | None => return "".into(), |
306 | }; | 377 | }; |
307 | 378 | ||
@@ -322,34 +393,49 @@ impl<'a> TreeSink for TtTreeSink<'a> { | |||
322 | break; | 393 | break; |
323 | } | 394 | } |
324 | 395 | ||
325 | match self.cursor.token_tree() { | 396 | let text: SmolStr = match self.cursor.token_tree() { |
326 | Some(tt::TokenTree::Leaf(leaf)) => { | 397 | Some(tt::TokenTree::Leaf(leaf)) => { |
327 | // Mark the range if needed | 398 | // Mark the range if needed |
328 | if let tt::Leaf::Ident(ident) = leaf { | 399 | let id = match leaf { |
329 | if kind == IDENT { | 400 | tt::Leaf::Ident(ident) => ident.id, |
330 | let range = | 401 | tt::Leaf::Punct(punct) => punct.id, |
331 | TextRange::offset_len(self.text_pos, TextUnit::of_str(&ident.text)); | 402 | tt::Leaf::Literal(lit) => lit.id, |
332 | self.token_map.insert(ident.id, range); | 403 | }; |
333 | } | 404 | let text = SmolStr::new(format!("{}", leaf)); |
334 | } | 405 | let range = TextRange::offset_len(self.text_pos, TextUnit::of_str(&text)); |
335 | 406 | self.token_map.insert(id, range); | |
336 | self.cursor = self.cursor.bump(); | 407 | self.cursor = self.cursor.bump(); |
337 | self.buf += &format!("{}", leaf); | 408 | text |
338 | } | 409 | } |
339 | Some(tt::TokenTree::Subtree(subtree)) => { | 410 | Some(tt::TokenTree::Subtree(subtree)) => { |
340 | self.cursor = self.cursor.subtree().unwrap(); | 411 | self.cursor = self.cursor.subtree().unwrap(); |
341 | self.buf += &delim_to_str(subtree.delimiter, false); | 412 | if let Some(id) = subtree.delimiter.map(|it| it.id) { |
413 | self.open_delims.insert(id, self.text_pos); | ||
414 | } | ||
415 | delim_to_str(subtree.delimiter_kind(), false) | ||
342 | } | 416 | } |
343 | None => { | 417 | None => { |
344 | if let Some(parent) = self.cursor.end() { | 418 | if let Some(parent) = self.cursor.end() { |
345 | self.cursor = self.cursor.bump(); | 419 | self.cursor = self.cursor.bump(); |
346 | self.buf += &delim_to_str(parent.delimiter, true); | 420 | if let Some(id) = parent.delimiter.map(|it| it.id) { |
421 | if let Some(open_delim) = self.open_delims.get(&id) { | ||
422 | let open_range = | ||
423 | TextRange::offset_len(*open_delim, TextUnit::from_usize(1)); | ||
424 | let close_range = | ||
425 | TextRange::offset_len(self.text_pos, TextUnit::from_usize(1)); | ||
426 | self.token_map.insert_delim(id, open_range, close_range); | ||
427 | } | ||
428 | } | ||
429 | delim_to_str(parent.delimiter_kind(), true) | ||
430 | } else { | ||
431 | continue; | ||
347 | } | 432 | } |
348 | } | 433 | } |
349 | }; | 434 | }; |
435 | self.buf += &text; | ||
436 | self.text_pos += TextUnit::of_str(&text); | ||
350 | } | 437 | } |
351 | 438 | ||
352 | self.text_pos += TextUnit::of_str(&self.buf); | ||
353 | let text = SmolStr::new(self.buf.as_str()); | 439 | let text = SmolStr::new(self.buf.as_str()); |
354 | self.buf.clear(); | 440 | self.buf.clear(); |
355 | self.inner.token(kind, text); | 441 | self.inner.token(kind, text); |
@@ -495,7 +581,7 @@ mod tests { | |||
495 | let token_tree = ast::TokenTree::cast(token_tree).unwrap(); | 581 | let token_tree = ast::TokenTree::cast(token_tree).unwrap(); |
496 | let tt = ast_to_token_tree(&token_tree).unwrap().0; | 582 | let tt = ast_to_token_tree(&token_tree).unwrap().0; |
497 | 583 | ||
498 | assert_eq!(tt.delimiter, Some(tt::Delimiter::Brace)); | 584 | assert_eq!(tt.delimiter_kind(), Some(tt::DelimiterKind::Brace)); |
499 | } | 585 | } |
500 | 586 | ||
501 | #[test] | 587 | #[test] |
diff --git a/crates/ra_mbe/src/tests.rs b/crates/ra_mbe/src/tests.rs index 148cc2625..ff225f0db 100644 --- a/crates/ra_mbe/src/tests.rs +++ b/crates/ra_mbe/src/tests.rs | |||
@@ -77,13 +77,41 @@ macro_rules! foobar { | |||
77 | } | 77 | } |
78 | 78 | ||
79 | assert_eq!(expansion.token_trees.len(), 3); | 79 | assert_eq!(expansion.token_trees.len(), 3); |
80 | // ($e:ident) => { foo bar $e } | 80 | // {($e:ident) => { foo bar $e }} |
81 | // 0 1 2 3 4 | 81 | // 012345 67 8 9 T 12 |
82 | assert_eq!(get_id(&expansion.token_trees[0]), Some(2)); | 82 | assert_eq!(get_id(&expansion.token_trees[0]), Some(9)); |
83 | assert_eq!(get_id(&expansion.token_trees[1]), Some(3)); | 83 | assert_eq!(get_id(&expansion.token_trees[1]), Some(10)); |
84 | 84 | ||
85 | // So baz should be 5 | 85 | // The input args of macro call include parentheses: |
86 | assert_eq!(get_id(&expansion.token_trees[2]), Some(5)); | 86 | // (baz) |
87 | // So baz should be 12+1+1 | ||
88 | assert_eq!(get_id(&expansion.token_trees[2]), Some(14)); | ||
89 | } | ||
90 | |||
91 | #[test] | ||
92 | fn test_token_map() { | ||
93 | use ra_parser::SyntaxKind::*; | ||
94 | use ra_syntax::T; | ||
95 | |||
96 | let macro_definition = r#" | ||
97 | macro_rules! foobar { | ||
98 | ($e:ident) => { fn $e() {} } | ||
99 | } | ||
100 | "#; | ||
101 | let rules = create_rules(macro_definition); | ||
102 | let (expansion, (token_map, content)) = expand_and_map(&rules, "foobar!(baz);"); | ||
103 | |||
104 | let get_text = |id, kind| -> String { | ||
105 | content[token_map.range_by_token(id).unwrap().by_kind(kind).unwrap()].to_string() | ||
106 | }; | ||
107 | |||
108 | assert_eq!(expansion.token_trees.len(), 4); | ||
109 | // {($e:ident) => { fn $e() {} }} | ||
110 | // 012345 67 8 9 T12 3 | ||
111 | |||
112 | assert_eq!(get_text(tt::TokenId(9), IDENT), "fn"); | ||
113 | assert_eq!(get_text(tt::TokenId(12), T!['(']), "("); | ||
114 | assert_eq!(get_text(tt::TokenId(13), T!['{']), "{"); | ||
87 | } | 115 | } |
88 | 116 | ||
89 | #[test] | 117 | #[test] |
@@ -1441,6 +1469,23 @@ pub(crate) fn expand(rules: &MacroRules, invocation: &str) -> tt::Subtree { | |||
1441 | rules.expand(&invocation_tt).unwrap() | 1469 | rules.expand(&invocation_tt).unwrap() |
1442 | } | 1470 | } |
1443 | 1471 | ||
1472 | pub(crate) fn expand_and_map( | ||
1473 | rules: &MacroRules, | ||
1474 | invocation: &str, | ||
1475 | ) -> (tt::Subtree, (TokenMap, String)) { | ||
1476 | let source_file = ast::SourceFile::parse(invocation).ok().unwrap(); | ||
1477 | let macro_invocation = | ||
1478 | source_file.syntax().descendants().find_map(ast::MacroCall::cast).unwrap(); | ||
1479 | |||
1480 | let (invocation_tt, _) = ast_to_token_tree(¯o_invocation.token_tree().unwrap()).unwrap(); | ||
1481 | let expanded = rules.expand(&invocation_tt).unwrap(); | ||
1482 | |||
1483 | let (node, expanded_token_tree) = | ||
1484 | token_tree_to_syntax_node(&expanded, FragmentKind::Items).unwrap(); | ||
1485 | |||
1486 | (expanded, (expanded_token_tree, node.syntax_node().to_string())) | ||
1487 | } | ||
1488 | |||
1444 | pub(crate) enum MacroKind { | 1489 | pub(crate) enum MacroKind { |
1445 | Items, | 1490 | Items, |
1446 | Stmts, | 1491 | Stmts, |
diff --git a/crates/ra_tt/src/lib.rs b/crates/ra_tt/src/lib.rs index e7bfd5fd2..10f424aae 100644 --- a/crates/ra_tt/src/lib.rs +++ b/crates/ra_tt/src/lib.rs | |||
@@ -55,7 +55,13 @@ pub struct Subtree { | |||
55 | } | 55 | } |
56 | 56 | ||
57 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] | 57 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] |
58 | pub enum Delimiter { | 58 | pub struct Delimiter { |
59 | pub id: TokenId, | ||
60 | pub kind: DelimiterKind, | ||
61 | } | ||
62 | |||
63 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] | ||
64 | pub enum DelimiterKind { | ||
59 | Parenthesis, | 65 | Parenthesis, |
60 | Brace, | 66 | Brace, |
61 | Bracket, | 67 | Bracket, |
@@ -64,12 +70,14 @@ pub enum Delimiter { | |||
64 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | 70 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] |
65 | pub struct Literal { | 71 | pub struct Literal { |
66 | pub text: SmolStr, | 72 | pub text: SmolStr, |
73 | pub id: TokenId, | ||
67 | } | 74 | } |
68 | 75 | ||
69 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | 76 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
70 | pub struct Punct { | 77 | pub struct Punct { |
71 | pub char: char, | 78 | pub char: char, |
72 | pub spacing: Spacing, | 79 | pub spacing: Spacing, |
80 | pub id: TokenId, | ||
73 | } | 81 | } |
74 | 82 | ||
75 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | 83 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
@@ -95,10 +103,10 @@ impl fmt::Display for TokenTree { | |||
95 | 103 | ||
96 | impl fmt::Display for Subtree { | 104 | impl fmt::Display for Subtree { |
97 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | 105 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
98 | let (l, r) = match self.delimiter { | 106 | let (l, r) = match self.delimiter_kind() { |
99 | Some(Delimiter::Parenthesis) => ("(", ")"), | 107 | Some(DelimiterKind::Parenthesis) => ("(", ")"), |
100 | Some(Delimiter::Brace) => ("{", "}"), | 108 | Some(DelimiterKind::Brace) => ("{", "}"), |
101 | Some(Delimiter::Bracket) => ("[", "]"), | 109 | Some(DelimiterKind::Bracket) => ("[", "]"), |
102 | None => ("", ""), | 110 | None => ("", ""), |
103 | }; | 111 | }; |
104 | f.write_str(l)?; | 112 | f.write_str(l)?; |
@@ -163,6 +171,10 @@ impl Subtree { | |||
163 | 171 | ||
164 | self.token_trees.len() + children_count | 172 | self.token_trees.len() + children_count |
165 | } | 173 | } |
174 | |||
175 | pub fn delimiter_kind(&self) -> Option<DelimiterKind> { | ||
176 | self.delimiter.map(|it| it.kind) | ||
177 | } | ||
166 | } | 178 | } |
167 | 179 | ||
168 | pub mod buffer; | 180 | pub mod buffer; |
diff --git a/editors/code/src/test/fixtures/rust-diagnostics/error/E0277.json b/editors/code/src/test/fixtures/rust-diagnostics/error/E0277.json new file mode 100644 index 000000000..bfef33c7d --- /dev/null +++ b/editors/code/src/test/fixtures/rust-diagnostics/error/E0277.json | |||
@@ -0,0 +1,261 @@ | |||
1 | { | ||
2 | "rendered": "error[E0277]: can't compare `{integer}` with `&str`\n --> src/main.rs:2:5\n |\n2 | assert_eq!(1, \"love\");\n | ^^^^^^^^^^^^^^^^^^^^^^ no implementation for `{integer} == &str`\n |\n = help: the trait `std::cmp::PartialEq<&str>` is not implemented for `{integer}`\n = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)\n\n", | ||
3 | "children": [ | ||
4 | { | ||
5 | "children": [], | ||
6 | "code": null, | ||
7 | "level": "help", | ||
8 | "message": "the trait `std::cmp::PartialEq<&str>` is not implemented for `{integer}`", | ||
9 | "rendered": null, | ||
10 | "spans": [] | ||
11 | } | ||
12 | ], | ||
13 | "code": { | ||
14 | "code": "E0277", | ||
15 | "explanation": "\nYou tried to use a type which doesn't implement some trait in a place which\nexpected that trait. Erroneous code example:\n\n```compile_fail,E0277\n// here we declare the Foo trait with a bar method\ntrait Foo {\n fn bar(&self);\n}\n\n// we now declare a function which takes an object implementing the Foo trait\nfn some_func<T: Foo>(foo: T) {\n foo.bar();\n}\n\nfn main() {\n // we now call the method with the i32 type, which doesn't implement\n // the Foo trait\n some_func(5i32); // error: the trait bound `i32 : Foo` is not satisfied\n}\n```\n\nIn order to fix this error, verify that the type you're using does implement\nthe trait. Example:\n\n```\ntrait Foo {\n fn bar(&self);\n}\n\nfn some_func<T: Foo>(foo: T) {\n foo.bar(); // we can now use this method since i32 implements the\n // Foo trait\n}\n\n// we implement the trait on the i32 type\nimpl Foo for i32 {\n fn bar(&self) {}\n}\n\nfn main() {\n some_func(5i32); // ok!\n}\n```\n\nOr in a generic context, an erroneous code example would look like:\n\n```compile_fail,E0277\nfn some_func<T>(foo: T) {\n println!(\"{:?}\", foo); // error: the trait `core::fmt::Debug` is not\n // implemented for the type `T`\n}\n\nfn main() {\n // We now call the method with the i32 type,\n // which *does* implement the Debug trait.\n some_func(5i32);\n}\n```\n\nNote that the error here is in the definition of the generic function: Although\nwe only call it with a parameter that does implement `Debug`, the compiler\nstill rejects the function: It must work with all possible input types. In\norder to make this example compile, we need to restrict the generic type we're\naccepting:\n\n```\nuse std::fmt;\n\n// Restrict the input type to types that implement Debug.\nfn some_func<T: fmt::Debug>(foo: T) {\n println!(\"{:?}\", foo);\n}\n\nfn main() {\n // Calling the method is still fine, as i32 implements Debug.\n some_func(5i32);\n\n // This would fail to compile now:\n // struct WithoutDebug;\n // some_func(WithoutDebug);\n}\n```\n\nRust only looks at the signature of the called function, as such it must\nalready specify all requirements that will be used for every type parameter.\n" | ||
16 | }, | ||
17 | "level": "error", | ||
18 | "message": "can't compare `{integer}` with `&str`", | ||
19 | "spans": [ | ||
20 | { | ||
21 | "byte_end": 155, | ||
22 | "byte_start": 153, | ||
23 | "column_end": 33, | ||
24 | "column_start": 31, | ||
25 | "expansion": { | ||
26 | "def_site_span": { | ||
27 | "byte_end": 940, | ||
28 | "byte_start": 0, | ||
29 | "column_end": 6, | ||
30 | "column_start": 1, | ||
31 | "expansion": null, | ||
32 | "file_name": "<::core::macros::assert_eq macros>", | ||
33 | "is_primary": false, | ||
34 | "label": null, | ||
35 | "line_end": 36, | ||
36 | "line_start": 1, | ||
37 | "suggested_replacement": null, | ||
38 | "suggestion_applicability": null, | ||
39 | "text": [ | ||
40 | { | ||
41 | "highlight_end": 35, | ||
42 | "highlight_start": 1, | ||
43 | "text": "($ left : expr, $ right : expr) =>" | ||
44 | }, | ||
45 | { | ||
46 | "highlight_end": 3, | ||
47 | "highlight_start": 1, | ||
48 | "text": "({" | ||
49 | }, | ||
50 | { | ||
51 | "highlight_end": 33, | ||
52 | "highlight_start": 1, | ||
53 | "text": " match (& $ left, & $ right)" | ||
54 | }, | ||
55 | { | ||
56 | "highlight_end": 7, | ||
57 | "highlight_start": 1, | ||
58 | "text": " {" | ||
59 | }, | ||
60 | { | ||
61 | "highlight_end": 34, | ||
62 | "highlight_start": 1, | ||
63 | "text": " (left_val, right_val) =>" | ||
64 | }, | ||
65 | { | ||
66 | "highlight_end": 11, | ||
67 | "highlight_start": 1, | ||
68 | "text": " {" | ||
69 | }, | ||
70 | { | ||
71 | "highlight_end": 46, | ||
72 | "highlight_start": 1, | ||
73 | "text": " if ! (* left_val == * right_val)" | ||
74 | }, | ||
75 | { | ||
76 | "highlight_end": 15, | ||
77 | "highlight_start": 1, | ||
78 | "text": " {" | ||
79 | }, | ||
80 | { | ||
81 | "highlight_end": 25, | ||
82 | "highlight_start": 1, | ||
83 | "text": " panic !" | ||
84 | }, | ||
85 | { | ||
86 | "highlight_end": 57, | ||
87 | "highlight_start": 1, | ||
88 | "text": " (r#\"assertion failed: `(left == right)`" | ||
89 | }, | ||
90 | { | ||
91 | "highlight_end": 16, | ||
92 | "highlight_start": 1, | ||
93 | "text": " left: `{:?}`," | ||
94 | }, | ||
95 | { | ||
96 | "highlight_end": 18, | ||
97 | "highlight_start": 1, | ||
98 | "text": " right: `{:?}`\"#," | ||
99 | }, | ||
100 | { | ||
101 | "highlight_end": 47, | ||
102 | "highlight_start": 1, | ||
103 | "text": " & * left_val, & * right_val)" | ||
104 | }, | ||
105 | { | ||
106 | "highlight_end": 15, | ||
107 | "highlight_start": 1, | ||
108 | "text": " }" | ||
109 | }, | ||
110 | { | ||
111 | "highlight_end": 11, | ||
112 | "highlight_start": 1, | ||
113 | "text": " }" | ||
114 | }, | ||
115 | { | ||
116 | "highlight_end": 7, | ||
117 | "highlight_start": 1, | ||
118 | "text": " }" | ||
119 | }, | ||
120 | { | ||
121 | "highlight_end": 42, | ||
122 | "highlight_start": 1, | ||
123 | "text": " }) ; ($ left : expr, $ right : expr,) =>" | ||
124 | }, | ||
125 | { | ||
126 | "highlight_end": 49, | ||
127 | "highlight_start": 1, | ||
128 | "text": "({ $ crate :: assert_eq ! ($ left, $ right) }) ;" | ||
129 | }, | ||
130 | { | ||
131 | "highlight_end": 53, | ||
132 | "highlight_start": 1, | ||
133 | "text": "($ left : expr, $ right : expr, $ ($ arg : tt) +) =>" | ||
134 | }, | ||
135 | { | ||
136 | "highlight_end": 3, | ||
137 | "highlight_start": 1, | ||
138 | "text": "({" | ||
139 | }, | ||
140 | { | ||
141 | "highlight_end": 37, | ||
142 | "highlight_start": 1, | ||
143 | "text": " match (& ($ left), & ($ right))" | ||
144 | }, | ||
145 | { | ||
146 | "highlight_end": 7, | ||
147 | "highlight_start": 1, | ||
148 | "text": " {" | ||
149 | }, | ||
150 | { | ||
151 | "highlight_end": 34, | ||
152 | "highlight_start": 1, | ||
153 | "text": " (left_val, right_val) =>" | ||
154 | }, | ||
155 | { | ||
156 | "highlight_end": 11, | ||
157 | "highlight_start": 1, | ||
158 | "text": " {" | ||
159 | }, | ||
160 | { | ||
161 | "highlight_end": 46, | ||
162 | "highlight_start": 1, | ||
163 | "text": " if ! (* left_val == * right_val)" | ||
164 | }, | ||
165 | { | ||
166 | "highlight_end": 15, | ||
167 | "highlight_start": 1, | ||
168 | "text": " {" | ||
169 | }, | ||
170 | { | ||
171 | "highlight_end": 25, | ||
172 | "highlight_start": 1, | ||
173 | "text": " panic !" | ||
174 | }, | ||
175 | { | ||
176 | "highlight_end": 57, | ||
177 | "highlight_start": 1, | ||
178 | "text": " (r#\"assertion failed: `(left == right)`" | ||
179 | }, | ||
180 | { | ||
181 | "highlight_end": 16, | ||
182 | "highlight_start": 1, | ||
183 | "text": " left: `{:?}`," | ||
184 | }, | ||
185 | { | ||
186 | "highlight_end": 22, | ||
187 | "highlight_start": 1, | ||
188 | "text": " right: `{:?}`: {}\"#," | ||
189 | }, | ||
190 | { | ||
191 | "highlight_end": 72, | ||
192 | "highlight_start": 1, | ||
193 | "text": " & * left_val, & * right_val, $ crate :: format_args !" | ||
194 | }, | ||
195 | { | ||
196 | "highlight_end": 33, | ||
197 | "highlight_start": 1, | ||
198 | "text": " ($ ($ arg) +))" | ||
199 | }, | ||
200 | { | ||
201 | "highlight_end": 15, | ||
202 | "highlight_start": 1, | ||
203 | "text": " }" | ||
204 | }, | ||
205 | { | ||
206 | "highlight_end": 11, | ||
207 | "highlight_start": 1, | ||
208 | "text": " }" | ||
209 | }, | ||
210 | { | ||
211 | "highlight_end": 7, | ||
212 | "highlight_start": 1, | ||
213 | "text": " }" | ||
214 | }, | ||
215 | { | ||
216 | "highlight_end": 6, | ||
217 | "highlight_start": 1, | ||
218 | "text": " }) ;" | ||
219 | } | ||
220 | ] | ||
221 | }, | ||
222 | "macro_decl_name": "assert_eq!", | ||
223 | "span": { | ||
224 | "byte_end": 38, | ||
225 | "byte_start": 16, | ||
226 | "column_end": 27, | ||
227 | "column_start": 5, | ||
228 | "expansion": null, | ||
229 | "file_name": "src/main.rs", | ||
230 | "is_primary": false, | ||
231 | "label": null, | ||
232 | "line_end": 2, | ||
233 | "line_start": 2, | ||
234 | "suggested_replacement": null, | ||
235 | "suggestion_applicability": null, | ||
236 | "text": [ | ||
237 | { | ||
238 | "highlight_end": 27, | ||
239 | "highlight_start": 5, | ||
240 | "text": " assert_eq!(1, \"love\");" | ||
241 | } | ||
242 | ] | ||
243 | } | ||
244 | }, | ||
245 | "file_name": "<::core::macros::assert_eq macros>", | ||
246 | "is_primary": true, | ||
247 | "label": "no implementation for `{integer} == &str`", | ||
248 | "line_end": 7, | ||
249 | "line_start": 7, | ||
250 | "suggested_replacement": null, | ||
251 | "suggestion_applicability": null, | ||
252 | "text": [ | ||
253 | { | ||
254 | "highlight_end": 33, | ||
255 | "highlight_start": 31, | ||
256 | "text": " if ! (* left_val == * right_val)" | ||
257 | } | ||
258 | ] | ||
259 | } | ||
260 | ] | ||
261 | } | ||
diff --git a/editors/code/src/test/utils/diagnotics/rust.test.ts b/editors/code/src/test/utils/diagnotics/rust.test.ts index 0222dbbaa..358325cc8 100644 --- a/editors/code/src/test/utils/diagnotics/rust.test.ts +++ b/editors/code/src/test/utils/diagnotics/rust.test.ts | |||
@@ -199,4 +199,38 @@ describe('mapRustDiagnosticToVsCode', () => { | |||
199 | // There are no suggested fixes | 199 | // There are no suggested fixes |
200 | assert.strictEqual(suggestedFixes.length, 0); | 200 | assert.strictEqual(suggestedFixes.length, 0); |
201 | }); | 201 | }); |
202 | |||
203 | it('should map a macro invocation location to normal file path', () => { | ||
204 | const { location, diagnostic, suggestedFixes } = mapFixtureToVsCode( | ||
205 | 'error/E0277', | ||
206 | ); | ||
207 | |||
208 | assert.strictEqual( | ||
209 | diagnostic.severity, | ||
210 | vscode.DiagnosticSeverity.Error, | ||
211 | ); | ||
212 | assert.strictEqual( | ||
213 | diagnostic.message, | ||
214 | [ | ||
215 | "can't compare `{integer}` with `&str`", | ||
216 | 'the trait `std::cmp::PartialEq<&str>` is not implemented for `{integer}`', | ||
217 | ].join('\n'), | ||
218 | ); | ||
219 | assert.strictEqual(diagnostic.code, 'E0277'); | ||
220 | assert.strictEqual(diagnostic.source, 'rustc'); | ||
221 | assert.deepStrictEqual(diagnostic.tags, []); | ||
222 | |||
223 | // No related information | ||
224 | assert.deepStrictEqual(diagnostic.relatedInformation, []); | ||
225 | |||
226 | // There are no suggested fixes | ||
227 | assert.strictEqual(suggestedFixes.length, 0); | ||
228 | |||
229 | // The file url should be normal file | ||
230 | // Ignore the first part because it depends on vs workspace location | ||
231 | assert.strictEqual( | ||
232 | location.uri.path.substr(-'src/main.rs'.length), | ||
233 | 'src/main.rs', | ||
234 | ); | ||
235 | }); | ||
202 | }); | 236 | }); |
diff --git a/editors/code/src/utils/diagnostics/rust.ts b/editors/code/src/utils/diagnostics/rust.ts index b6efc0f56..1f0c0d3e4 100644 --- a/editors/code/src/utils/diagnostics/rust.ts +++ b/editors/code/src/utils/diagnostics/rust.ts | |||
@@ -10,6 +10,12 @@ export enum SuggestionApplicability { | |||
10 | Unspecified = 'Unspecified', | 10 | Unspecified = 'Unspecified', |
11 | } | 11 | } |
12 | 12 | ||
13 | export interface RustDiagnosticSpanMacroExpansion { | ||
14 | span: RustDiagnosticSpan; | ||
15 | macro_decl_name: string; | ||
16 | def_site_span?: RustDiagnosticSpan; | ||
17 | } | ||
18 | |||
13 | // Reference: | 19 | // Reference: |
14 | // https://github.com/rust-lang/rust/blob/master/src/libsyntax/json.rs | 20 | // https://github.com/rust-lang/rust/blob/master/src/libsyntax/json.rs |
15 | export interface RustDiagnosticSpan { | 21 | export interface RustDiagnosticSpan { |
@@ -20,6 +26,7 @@ export interface RustDiagnosticSpan { | |||
20 | is_primary: boolean; | 26 | is_primary: boolean; |
21 | file_name: string; | 27 | file_name: string; |
22 | label?: string; | 28 | label?: string; |
29 | expansion?: RustDiagnosticSpanMacroExpansion; | ||
23 | suggested_replacement?: string; | 30 | suggested_replacement?: string; |
24 | suggestion_applicability?: SuggestionApplicability; | 31 | suggestion_applicability?: SuggestionApplicability; |
25 | } | 32 | } |
@@ -61,9 +68,40 @@ function mapLevelToSeverity(s: string): vscode.DiagnosticSeverity { | |||
61 | } | 68 | } |
62 | 69 | ||
63 | /** | 70 | /** |
71 | * Check whether a file name is from macro invocation | ||
72 | */ | ||
73 | function isFromMacro(fileName: string): boolean { | ||
74 | return fileName.startsWith('<') && fileName.endsWith('>'); | ||
75 | } | ||
76 | |||
77 | /** | ||
78 | * Converts a Rust macro span to a VsCode location recursively | ||
79 | */ | ||
80 | function mapMacroSpanToLocation( | ||
81 | spanMacro: RustDiagnosticSpanMacroExpansion, | ||
82 | ): vscode.Location | undefined { | ||
83 | if (!isFromMacro(spanMacro.span.file_name)) { | ||
84 | return mapSpanToLocation(spanMacro.span); | ||
85 | } | ||
86 | |||
87 | if (spanMacro.span.expansion) { | ||
88 | return mapMacroSpanToLocation(spanMacro.span.expansion); | ||
89 | } | ||
90 | |||
91 | return; | ||
92 | } | ||
93 | |||
94 | /** | ||
64 | * Converts a Rust span to a VsCode location | 95 | * Converts a Rust span to a VsCode location |
65 | */ | 96 | */ |
66 | function mapSpanToLocation(span: RustDiagnosticSpan): vscode.Location { | 97 | function mapSpanToLocation(span: RustDiagnosticSpan): vscode.Location { |
98 | if (isFromMacro(span.file_name) && span.expansion) { | ||
99 | const macroLoc = mapMacroSpanToLocation(span.expansion); | ||
100 | if (macroLoc) { | ||
101 | return macroLoc; | ||
102 | } | ||
103 | } | ||
104 | |||
67 | const fileName = path.join(vscode.workspace.rootPath || '', span.file_name); | 105 | const fileName = path.join(vscode.workspace.rootPath || '', span.file_name); |
68 | const fileUri = vscode.Uri.file(fileName); | 106 | const fileUri = vscode.Uri.file(fileName); |
69 | 107 | ||