diff options
Diffstat (limited to 'crates')
-rw-r--r-- | crates/hir_def/src/find_path.rs | 2 | ||||
-rw-r--r-- | crates/ide/src/hover.rs | 51 | ||||
-rw-r--r-- | crates/ide/src/lib.rs | 4 | ||||
-rw-r--r-- | crates/ide/src/markdown_remove.rs | 23 | ||||
-rw-r--r-- | crates/ide/src/typing/on_enter.rs | 29 | ||||
-rw-r--r-- | crates/mbe/src/subtree_source.rs | 30 | ||||
-rw-r--r-- | crates/rust-analyzer/src/config.rs | 6 | ||||
-rw-r--r-- | crates/rust-analyzer/src/handlers.rs | 6 | ||||
-rw-r--r-- | crates/rust-analyzer/src/lsp_utils.rs | 11 | ||||
-rw-r--r-- | crates/rust-analyzer/src/main_loop.rs | 2 | ||||
-rw-r--r-- | crates/syntax/src/parsing/lexer.rs | 23 | ||||
-rw-r--r-- | crates/syntax/src/ptr.rs | 2 | ||||
-rw-r--r-- | crates/syntax/src/validation.rs | 45 | ||||
-rw-r--r-- | crates/syntax/test_data/parser/err/0046_ambiguous_trait_object.rast | 192 | ||||
-rw-r--r-- | crates/syntax/test_data/parser/err/0046_ambiguous_trait_object.rs | 6 | ||||
-rw-r--r-- | crates/syntax/test_data/parser/ok/0069_multi_trait_object.rast | 200 | ||||
-rw-r--r-- | crates/syntax/test_data/parser/ok/0069_multi_trait_object.rs | 6 |
17 files changed, 608 insertions, 30 deletions
diff --git a/crates/hir_def/src/find_path.rs b/crates/hir_def/src/find_path.rs index 9106ed45f..02613c4c4 100644 --- a/crates/hir_def/src/find_path.rs +++ b/crates/hir_def/src/find_path.rs | |||
@@ -222,6 +222,7 @@ fn find_path_inner( | |||
222 | best_path_len - 1, | 222 | best_path_len - 1, |
223 | prefixed, | 223 | prefixed, |
224 | )?; | 224 | )?; |
225 | mark::hit!(partially_imported); | ||
225 | path.segments.push(info.path.segments.last().unwrap().clone()); | 226 | path.segments.push(info.path.segments.last().unwrap().clone()); |
226 | Some(path) | 227 | Some(path) |
227 | }) | 228 | }) |
@@ -515,6 +516,7 @@ mod tests { | |||
515 | 516 | ||
516 | #[test] | 517 | #[test] |
517 | fn partially_imported() { | 518 | fn partially_imported() { |
519 | mark::check!(partially_imported); | ||
518 | // Tests that short paths are used even for external items, when parts of the path are | 520 | // Tests that short paths are used even for external items, when parts of the path are |
519 | // already in scope. | 521 | // already in scope. |
520 | let code = r#" | 522 | let code = r#" |
diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs index 4521d72cc..53265488e 100644 --- a/crates/ide/src/hover.rs +++ b/crates/ide/src/hover.rs | |||
@@ -15,6 +15,7 @@ use test_utils::mark; | |||
15 | use crate::{ | 15 | use crate::{ |
16 | display::{macro_label, ShortLabel, ToNav, TryToNav}, | 16 | display::{macro_label, ShortLabel, ToNav, TryToNav}, |
17 | link_rewrite::{remove_links, rewrite_links}, | 17 | link_rewrite::{remove_links, rewrite_links}, |
18 | markdown_remove::remove_markdown, | ||
18 | markup::Markup, | 19 | markup::Markup, |
19 | runnables::runnable, | 20 | runnables::runnable, |
20 | FileId, FilePosition, NavigationTarget, RangeInfo, Runnable, | 21 | FileId, FilePosition, NavigationTarget, RangeInfo, Runnable, |
@@ -27,6 +28,7 @@ pub struct HoverConfig { | |||
27 | pub debug: bool, | 28 | pub debug: bool, |
28 | pub goto_type_def: bool, | 29 | pub goto_type_def: bool, |
29 | pub links_in_hover: bool, | 30 | pub links_in_hover: bool, |
31 | pub markdown: bool, | ||
30 | } | 32 | } |
31 | 33 | ||
32 | impl Default for HoverConfig { | 34 | impl Default for HoverConfig { |
@@ -37,6 +39,7 @@ impl Default for HoverConfig { | |||
37 | debug: true, | 39 | debug: true, |
38 | goto_type_def: true, | 40 | goto_type_def: true, |
39 | links_in_hover: true, | 41 | links_in_hover: true, |
42 | markdown: true, | ||
40 | } | 43 | } |
41 | } | 44 | } |
42 | } | 45 | } |
@@ -48,6 +51,7 @@ impl HoverConfig { | |||
48 | debug: false, | 51 | debug: false, |
49 | goto_type_def: false, | 52 | goto_type_def: false, |
50 | links_in_hover: true, | 53 | links_in_hover: true, |
54 | markdown: true, | ||
51 | }; | 55 | }; |
52 | 56 | ||
53 | pub fn any(&self) -> bool { | 57 | pub fn any(&self) -> bool { |
@@ -91,6 +95,7 @@ pub(crate) fn hover( | |||
91 | db: &RootDatabase, | 95 | db: &RootDatabase, |
92 | position: FilePosition, | 96 | position: FilePosition, |
93 | links_in_hover: bool, | 97 | links_in_hover: bool, |
98 | markdown: bool, | ||
94 | ) -> Option<RangeInfo<HoverResult>> { | 99 | ) -> Option<RangeInfo<HoverResult>> { |
95 | let sema = Semantics::new(db); | 100 | let sema = Semantics::new(db); |
96 | let file = sema.parse(position.file_id).syntax().clone(); | 101 | let file = sema.parse(position.file_id).syntax().clone(); |
@@ -109,7 +114,9 @@ pub(crate) fn hover( | |||
109 | }; | 114 | }; |
110 | if let Some(definition) = definition { | 115 | if let Some(definition) = definition { |
111 | if let Some(markup) = hover_for_definition(db, definition) { | 116 | if let Some(markup) = hover_for_definition(db, definition) { |
112 | let markup = if links_in_hover { | 117 | let markup = if !markdown { |
118 | remove_markdown(&markup.as_str()) | ||
119 | } else if links_in_hover { | ||
113 | rewrite_links(db, &markup.as_str(), &definition) | 120 | rewrite_links(db, &markup.as_str(), &definition) |
114 | } else { | 121 | } else { |
115 | remove_links(&markup.as_str()) | 122 | remove_links(&markup.as_str()) |
@@ -147,7 +154,11 @@ pub(crate) fn hover( | |||
147 | } | 154 | } |
148 | }; | 155 | }; |
149 | 156 | ||
150 | res.markup = Markup::fenced_block(&ty.display(db)); | 157 | res.markup = if markdown { |
158 | Markup::fenced_block(&ty.display(db)) | ||
159 | } else { | ||
160 | ty.display(db).to_string().into() | ||
161 | }; | ||
151 | let range = sema.original_range(&node).range; | 162 | let range = sema.original_range(&node).range; |
152 | Some(RangeInfo::new(range, res)) | 163 | Some(RangeInfo::new(range, res)) |
153 | } | 164 | } |
@@ -383,12 +394,12 @@ mod tests { | |||
383 | 394 | ||
384 | fn check_hover_no_result(ra_fixture: &str) { | 395 | fn check_hover_no_result(ra_fixture: &str) { |
385 | let (analysis, position) = fixture::position(ra_fixture); | 396 | let (analysis, position) = fixture::position(ra_fixture); |
386 | assert!(analysis.hover(position, true).unwrap().is_none()); | 397 | assert!(analysis.hover(position, true, true).unwrap().is_none()); |
387 | } | 398 | } |
388 | 399 | ||
389 | fn check(ra_fixture: &str, expect: Expect) { | 400 | fn check(ra_fixture: &str, expect: Expect) { |
390 | let (analysis, position) = fixture::position(ra_fixture); | 401 | let (analysis, position) = fixture::position(ra_fixture); |
391 | let hover = analysis.hover(position, true).unwrap().unwrap(); | 402 | let hover = analysis.hover(position, true, true).unwrap().unwrap(); |
392 | 403 | ||
393 | let content = analysis.db.file_text(position.file_id); | 404 | let content = analysis.db.file_text(position.file_id); |
394 | let hovered_element = &content[hover.range]; | 405 | let hovered_element = &content[hover.range]; |
@@ -399,7 +410,18 @@ mod tests { | |||
399 | 410 | ||
400 | fn check_hover_no_links(ra_fixture: &str, expect: Expect) { | 411 | fn check_hover_no_links(ra_fixture: &str, expect: Expect) { |
401 | let (analysis, position) = fixture::position(ra_fixture); | 412 | let (analysis, position) = fixture::position(ra_fixture); |
402 | let hover = analysis.hover(position, false).unwrap().unwrap(); | 413 | let hover = analysis.hover(position, false, true).unwrap().unwrap(); |
414 | |||
415 | let content = analysis.db.file_text(position.file_id); | ||
416 | let hovered_element = &content[hover.range]; | ||
417 | |||
418 | let actual = format!("*{}*\n{}\n", hovered_element, hover.info.markup); | ||
419 | expect.assert_eq(&actual) | ||
420 | } | ||
421 | |||
422 | fn check_hover_no_markdown(ra_fixture: &str, expect: Expect) { | ||
423 | let (analysis, position) = fixture::position(ra_fixture); | ||
424 | let hover = analysis.hover(position, true, false).unwrap().unwrap(); | ||
403 | 425 | ||
404 | let content = analysis.db.file_text(position.file_id); | 426 | let content = analysis.db.file_text(position.file_id); |
405 | let hovered_element = &content[hover.range]; | 427 | let hovered_element = &content[hover.range]; |
@@ -410,7 +432,7 @@ mod tests { | |||
410 | 432 | ||
411 | fn check_actions(ra_fixture: &str, expect: Expect) { | 433 | fn check_actions(ra_fixture: &str, expect: Expect) { |
412 | let (analysis, position) = fixture::position(ra_fixture); | 434 | let (analysis, position) = fixture::position(ra_fixture); |
413 | let hover = analysis.hover(position, true).unwrap().unwrap(); | 435 | let hover = analysis.hover(position, true, true).unwrap().unwrap(); |
414 | expect.assert_debug_eq(&hover.info.actions) | 436 | expect.assert_debug_eq(&hover.info.actions) |
415 | } | 437 | } |
416 | 438 | ||
@@ -434,6 +456,23 @@ fn main() { | |||
434 | } | 456 | } |
435 | 457 | ||
436 | #[test] | 458 | #[test] |
459 | fn hover_remove_markdown_if_configured() { | ||
460 | check_hover_no_markdown( | ||
461 | r#" | ||
462 | pub fn foo() -> u32 { 1 } | ||
463 | |||
464 | fn main() { | ||
465 | let foo_test = foo()<|>; | ||
466 | } | ||
467 | "#, | ||
468 | expect![[r#" | ||
469 | *foo()* | ||
470 | u32 | ||
471 | "#]], | ||
472 | ); | ||
473 | } | ||
474 | |||
475 | #[test] | ||
437 | fn hover_shows_long_type_of_an_expression() { | 476 | fn hover_shows_long_type_of_an_expression() { |
438 | check( | 477 | check( |
439 | r#" | 478 | r#" |
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs index 1aa673cf8..57f3581b6 100644 --- a/crates/ide/src/lib.rs +++ b/crates/ide/src/lib.rs | |||
@@ -46,6 +46,7 @@ mod syntax_highlighting; | |||
46 | mod syntax_tree; | 46 | mod syntax_tree; |
47 | mod typing; | 47 | mod typing; |
48 | mod link_rewrite; | 48 | mod link_rewrite; |
49 | mod markdown_remove; | ||
49 | 50 | ||
50 | use std::sync::Arc; | 51 | use std::sync::Arc; |
51 | 52 | ||
@@ -376,8 +377,9 @@ impl Analysis { | |||
376 | &self, | 377 | &self, |
377 | position: FilePosition, | 378 | position: FilePosition, |
378 | links_in_hover: bool, | 379 | links_in_hover: bool, |
380 | markdown: bool, | ||
379 | ) -> Cancelable<Option<RangeInfo<HoverResult>>> { | 381 | ) -> Cancelable<Option<RangeInfo<HoverResult>>> { |
380 | self.with_db(|db| hover::hover(db, position, links_in_hover)) | 382 | self.with_db(|db| hover::hover(db, position, links_in_hover, markdown)) |
381 | } | 383 | } |
382 | 384 | ||
383 | /// Computes parameter information for the given call expression. | 385 | /// Computes parameter information for the given call expression. |
diff --git a/crates/ide/src/markdown_remove.rs b/crates/ide/src/markdown_remove.rs new file mode 100644 index 000000000..02ad39dfb --- /dev/null +++ b/crates/ide/src/markdown_remove.rs | |||
@@ -0,0 +1,23 @@ | |||
1 | //! Removes markdown from strings. | ||
2 | |||
3 | use pulldown_cmark::{Event, Parser, Tag}; | ||
4 | |||
5 | /// Removes all markdown, keeping the text and code blocks | ||
6 | /// | ||
7 | /// Currently limited in styling, i.e. no ascii tables or lists | ||
8 | pub fn remove_markdown(markdown: &str) -> String { | ||
9 | let mut out = String::new(); | ||
10 | let parser = Parser::new(markdown); | ||
11 | |||
12 | for event in parser { | ||
13 | match event { | ||
14 | Event::Text(text) | Event::Code(text) => out.push_str(&text), | ||
15 | Event::SoftBreak | Event::HardBreak | Event::Rule | Event::End(Tag::CodeBlock(_)) => { | ||
16 | out.push('\n') | ||
17 | } | ||
18 | _ => {} | ||
19 | } | ||
20 | } | ||
21 | |||
22 | out | ||
23 | } | ||
diff --git a/crates/ide/src/typing/on_enter.rs b/crates/ide/src/typing/on_enter.rs index a0dc4b9df..98adef1d6 100644 --- a/crates/ide/src/typing/on_enter.rs +++ b/crates/ide/src/typing/on_enter.rs | |||
@@ -51,12 +51,12 @@ pub(crate) fn on_enter(db: &RootDatabase, position: FilePosition) -> Option<Text | |||
51 | return None; | 51 | return None; |
52 | } | 52 | } |
53 | 53 | ||
54 | let mut remove_last_space = false; | 54 | let mut remove_trailing_whitespace = false; |
55 | // Continuing single-line non-doc comments (like this one :) ) is annoying | 55 | // Continuing single-line non-doc comments (like this one :) ) is annoying |
56 | if prefix == "//" && comment_range.end() == position.offset { | 56 | if prefix == "//" && comment_range.end() == position.offset { |
57 | if comment.text().ends_with(' ') { | 57 | if comment.text().ends_with(' ') { |
58 | mark::hit!(continues_end_of_line_comment_with_space); | 58 | mark::hit!(continues_end_of_line_comment_with_space); |
59 | remove_last_space = true; | 59 | remove_trailing_whitespace = true; |
60 | } else if !followed_by_comment(&comment) { | 60 | } else if !followed_by_comment(&comment) { |
61 | return None; | 61 | return None; |
62 | } | 62 | } |
@@ -64,8 +64,10 @@ pub(crate) fn on_enter(db: &RootDatabase, position: FilePosition) -> Option<Text | |||
64 | 64 | ||
65 | let indent = node_indent(&file, comment.syntax())?; | 65 | let indent = node_indent(&file, comment.syntax())?; |
66 | let inserted = format!("\n{}{} $0", indent, prefix); | 66 | let inserted = format!("\n{}{} $0", indent, prefix); |
67 | let delete = if remove_last_space { | 67 | let delete = if remove_trailing_whitespace { |
68 | TextRange::new(position.offset - TextSize::of(' '), position.offset) | 68 | let trimmed_len = comment.text().trim_end().len() as u32; |
69 | let trailing_whitespace_len = comment.text().len() as u32 - trimmed_len; | ||
70 | TextRange::new(position.offset - TextSize::from(trailing_whitespace_len), position.offset) | ||
69 | } else { | 71 | } else { |
70 | TextRange::empty(position.offset) | 72 | TextRange::empty(position.offset) |
71 | }; | 73 | }; |
@@ -253,4 +255,23 @@ fn main() { | |||
253 | "#, | 255 | "#, |
254 | ); | 256 | ); |
255 | } | 257 | } |
258 | |||
259 | #[test] | ||
260 | fn trims_all_trailing_whitespace() { | ||
261 | do_check( | ||
262 | " | ||
263 | fn main() { | ||
264 | // Fix me \t\t <|> | ||
265 | let x = 1 + 1; | ||
266 | } | ||
267 | ", | ||
268 | " | ||
269 | fn main() { | ||
270 | // Fix me | ||
271 | // $0 | ||
272 | let x = 1 + 1; | ||
273 | } | ||
274 | ", | ||
275 | ); | ||
276 | } | ||
256 | } | 277 | } |
diff --git a/crates/mbe/src/subtree_source.rs b/crates/mbe/src/subtree_source.rs index 41461b315..396ce8b16 100644 --- a/crates/mbe/src/subtree_source.rs +++ b/crates/mbe/src/subtree_source.rs | |||
@@ -155,9 +155,14 @@ fn convert_delim(d: Option<tt::DelimiterKind>, closing: bool) -> TtToken { | |||
155 | } | 155 | } |
156 | 156 | ||
157 | fn convert_literal(l: &tt::Literal) -> TtToken { | 157 | fn convert_literal(l: &tt::Literal) -> TtToken { |
158 | let kind = lex_single_syntax_kind(&l.text) | 158 | let is_negated = l.text.starts_with('-'); |
159 | let inner_text = &l.text[if is_negated { 1 } else { 0 }..]; | ||
160 | |||
161 | let kind = lex_single_syntax_kind(inner_text) | ||
159 | .map(|(kind, _error)| kind) | 162 | .map(|(kind, _error)| kind) |
160 | .filter(|kind| kind.is_literal()) | 163 | .filter(|kind| { |
164 | kind.is_literal() && (!is_negated || matches!(kind, FLOAT_NUMBER | INT_NUMBER)) | ||
165 | }) | ||
161 | .unwrap_or_else(|| panic!("Fail to convert given literal {:#?}", &l)); | 166 | .unwrap_or_else(|| panic!("Fail to convert given literal {:#?}", &l)); |
162 | 167 | ||
163 | TtToken { kind, is_joint_to_next: false, text: l.text.clone() } | 168 | TtToken { kind, is_joint_to_next: false, text: l.text.clone() } |
@@ -195,3 +200,24 @@ fn convert_leaf(leaf: &tt::Leaf) -> TtToken { | |||
195 | tt::Leaf::Punct(punct) => convert_punct(*punct), | 200 | tt::Leaf::Punct(punct) => convert_punct(*punct), |
196 | } | 201 | } |
197 | } | 202 | } |
203 | |||
204 | #[cfg(test)] | ||
205 | mod tests { | ||
206 | use super::{convert_literal, TtToken}; | ||
207 | use syntax::{SmolStr, SyntaxKind}; | ||
208 | |||
209 | #[test] | ||
210 | fn test_negative_literal() { | ||
211 | assert_eq!( | ||
212 | convert_literal(&tt::Literal { | ||
213 | id: tt::TokenId::unspecified(), | ||
214 | text: SmolStr::new("-42.0") | ||
215 | }), | ||
216 | TtToken { | ||
217 | kind: SyntaxKind::FLOAT_NUMBER, | ||
218 | is_joint_to_next: false, | ||
219 | text: SmolStr::new("-42.0") | ||
220 | } | ||
221 | ); | ||
222 | } | ||
223 | } | ||
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index dcbc11c14..1b9b24698 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs | |||
@@ -15,7 +15,7 @@ use ide::{ | |||
15 | AssistConfig, CompletionConfig, DiagnosticsConfig, HoverConfig, InlayHintsConfig, | 15 | AssistConfig, CompletionConfig, DiagnosticsConfig, HoverConfig, InlayHintsConfig, |
16 | MergeBehaviour, | 16 | MergeBehaviour, |
17 | }; | 17 | }; |
18 | use lsp_types::ClientCapabilities; | 18 | use lsp_types::{ClientCapabilities, MarkupKind}; |
19 | use project_model::{CargoConfig, ProjectJson, ProjectJsonData, ProjectManifest}; | 19 | use project_model::{CargoConfig, ProjectJson, ProjectJsonData, ProjectManifest}; |
20 | use rustc_hash::FxHashSet; | 20 | use rustc_hash::FxHashSet; |
21 | use serde::Deserialize; | 21 | use serde::Deserialize; |
@@ -333,6 +333,7 @@ impl Config { | |||
333 | debug: data.hoverActions_enable && data.hoverActions_debug, | 333 | debug: data.hoverActions_enable && data.hoverActions_debug, |
334 | goto_type_def: data.hoverActions_enable && data.hoverActions_gotoTypeDef, | 334 | goto_type_def: data.hoverActions_enable && data.hoverActions_gotoTypeDef, |
335 | links_in_hover: data.hoverActions_linksInHover, | 335 | links_in_hover: data.hoverActions_linksInHover, |
336 | markdown: true, | ||
336 | }; | 337 | }; |
337 | 338 | ||
338 | log::info!("Config::update() = {:#?}", self); | 339 | log::info!("Config::update() = {:#?}", self); |
@@ -340,6 +341,9 @@ impl Config { | |||
340 | 341 | ||
341 | pub fn update_caps(&mut self, caps: &ClientCapabilities) { | 342 | pub fn update_caps(&mut self, caps: &ClientCapabilities) { |
342 | if let Some(doc_caps) = caps.text_document.as_ref() { | 343 | if let Some(doc_caps) = caps.text_document.as_ref() { |
344 | if let Some(value) = doc_caps.hover.as_ref().and_then(|it| it.content_format.as_ref()) { | ||
345 | self.hover.markdown = value.contains(&MarkupKind::Markdown) | ||
346 | } | ||
343 | if let Some(value) = doc_caps.definition.as_ref().and_then(|it| it.link_support) { | 347 | if let Some(value) = doc_caps.definition.as_ref().and_then(|it| it.link_support) { |
344 | self.client_caps.location_link = value; | 348 | self.client_caps.location_link = value; |
345 | } | 349 | } |
diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs index e970abb7c..468655f9c 100644 --- a/crates/rust-analyzer/src/handlers.rs +++ b/crates/rust-analyzer/src/handlers.rs | |||
@@ -618,7 +618,11 @@ pub(crate) fn handle_hover( | |||
618 | ) -> Result<Option<lsp_ext::Hover>> { | 618 | ) -> Result<Option<lsp_ext::Hover>> { |
619 | let _p = profile::span("handle_hover"); | 619 | let _p = profile::span("handle_hover"); |
620 | let position = from_proto::file_position(&snap, params.text_document_position_params)?; | 620 | let position = from_proto::file_position(&snap, params.text_document_position_params)?; |
621 | let info = match snap.analysis.hover(position, snap.config.hover.links_in_hover)? { | 621 | let info = match snap.analysis.hover( |
622 | position, | ||
623 | snap.config.hover.links_in_hover, | ||
624 | snap.config.hover.markdown, | ||
625 | )? { | ||
622 | None => return Ok(None), | 626 | None => return Ok(None), |
623 | Some(info) => info, | 627 | Some(info) => info, |
624 | }; | 628 | }; |
diff --git a/crates/rust-analyzer/src/lsp_utils.rs b/crates/rust-analyzer/src/lsp_utils.rs index 85c661571..bd888f634 100644 --- a/crates/rust-analyzer/src/lsp_utils.rs +++ b/crates/rust-analyzer/src/lsp_utils.rs | |||
@@ -25,8 +25,9 @@ pub(crate) enum Progress { | |||
25 | } | 25 | } |
26 | 26 | ||
27 | impl Progress { | 27 | impl Progress { |
28 | pub(crate) fn percentage(done: usize, total: usize) -> f64 { | 28 | pub(crate) fn fraction(done: usize, total: usize) -> f64 { |
29 | (done as f64 / total.max(1) as f64) * 100.0 | 29 | assert!(done <= total); |
30 | done as f64 / total.max(1) as f64 | ||
30 | } | 31 | } |
31 | } | 32 | } |
32 | 33 | ||
@@ -43,11 +44,15 @@ impl GlobalState { | |||
43 | title: &str, | 44 | title: &str, |
44 | state: Progress, | 45 | state: Progress, |
45 | message: Option<String>, | 46 | message: Option<String>, |
46 | percentage: Option<f64>, | 47 | fraction: Option<f64>, |
47 | ) { | 48 | ) { |
48 | if !self.config.client_caps.work_done_progress { | 49 | if !self.config.client_caps.work_done_progress { |
49 | return; | 50 | return; |
50 | } | 51 | } |
52 | let percentage = fraction.map(|f| { | ||
53 | assert!(0.0 <= f && f <= 1.0); | ||
54 | f * 100.0 | ||
55 | }); | ||
51 | let token = lsp_types::ProgressToken::String(format!("rustAnalyzer/{}", title)); | 56 | let token = lsp_types::ProgressToken::String(format!("rustAnalyzer/{}", title)); |
52 | let work_done_progress = match state { | 57 | let work_done_progress = match state { |
53 | Progress::Begin => { | 58 | Progress::Begin => { |
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index c2d0ac791..4b7ac8224 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs | |||
@@ -230,7 +230,7 @@ impl GlobalState { | |||
230 | "roots scanned", | 230 | "roots scanned", |
231 | state, | 231 | state, |
232 | Some(format!("{}/{}", n_done, n_total)), | 232 | Some(format!("{}/{}", n_done, n_total)), |
233 | Some(Progress::percentage(n_done, n_total)), | 233 | Some(Progress::fraction(n_done, n_total)), |
234 | ) | 234 | ) |
235 | } | 235 | } |
236 | } | 236 | } |
diff --git a/crates/syntax/src/parsing/lexer.rs b/crates/syntax/src/parsing/lexer.rs index f1202113b..7e38c32cc 100644 --- a/crates/syntax/src/parsing/lexer.rs +++ b/crates/syntax/src/parsing/lexer.rs | |||
@@ -1,10 +1,10 @@ | |||
1 | //! Lexer analyzes raw input string and produces lexemes (tokens). | 1 | //! Lexer analyzes raw input string and produces lexemes (tokens). |
2 | //! It is just a bridge to `rustc_lexer`. | 2 | //! It is just a bridge to `rustc_lexer`. |
3 | 3 | ||
4 | use rustc_lexer::{LiteralKind as LK, RawStrError}; | ||
5 | |||
6 | use std::convert::TryInto; | 4 | use std::convert::TryInto; |
7 | 5 | ||
6 | use rustc_lexer::{LiteralKind as LK, RawStrError}; | ||
7 | |||
8 | use crate::{ | 8 | use crate::{ |
9 | SyntaxError, | 9 | SyntaxError, |
10 | SyntaxKind::{self, *}, | 10 | SyntaxKind::{self, *}, |
@@ -61,17 +61,18 @@ pub fn tokenize(text: &str) -> (Vec<Token>, Vec<SyntaxError>) { | |||
61 | (tokens, errors) | 61 | (tokens, errors) |
62 | } | 62 | } |
63 | 63 | ||
64 | /// Returns `SyntaxKind` and `Option<SyntaxError>` of the first token | 64 | /// Returns `SyntaxKind` and `Option<SyntaxError>` if `text` parses as a single token. |
65 | /// encountered at the beginning of the string. | ||
66 | /// | 65 | /// |
67 | /// Returns `None` if the string contains zero *or two or more* tokens. | 66 | /// Returns `None` if the string contains zero *or two or more* tokens. |
68 | /// The token is malformed if the returned error is not `None`. | 67 | /// The token is malformed if the returned error is not `None`. |
69 | /// | 68 | /// |
70 | /// Beware that unescape errors are not checked at tokenization time. | 69 | /// Beware that unescape errors are not checked at tokenization time. |
71 | pub fn lex_single_syntax_kind(text: &str) -> Option<(SyntaxKind, Option<SyntaxError>)> { | 70 | pub fn lex_single_syntax_kind(text: &str) -> Option<(SyntaxKind, Option<SyntaxError>)> { |
72 | lex_first_token(text) | 71 | let (first_token, err) = lex_first_token(text)?; |
73 | .filter(|(token, _)| token.len == TextSize::of(text)) | 72 | if first_token.len != TextSize::of(text) { |
74 | .map(|(token, error)| (token.kind, error)) | 73 | return None; |
74 | } | ||
75 | Some((first_token.kind, err)) | ||
75 | } | 76 | } |
76 | 77 | ||
77 | /// The same as `lex_single_syntax_kind()` but returns only `SyntaxKind` and | 78 | /// The same as `lex_single_syntax_kind()` but returns only `SyntaxKind` and |
@@ -79,9 +80,11 @@ pub fn lex_single_syntax_kind(text: &str) -> Option<(SyntaxKind, Option<SyntaxEr | |||
79 | /// | 80 | /// |
80 | /// Beware that unescape errors are not checked at tokenization time. | 81 | /// Beware that unescape errors are not checked at tokenization time. |
81 | pub fn lex_single_valid_syntax_kind(text: &str) -> Option<SyntaxKind> { | 82 | pub fn lex_single_valid_syntax_kind(text: &str) -> Option<SyntaxKind> { |
82 | lex_first_token(text) | 83 | let (single_token, err) = lex_single_syntax_kind(text)?; |
83 | .filter(|(token, error)| !error.is_some() && token.len == TextSize::of(text)) | 84 | if err.is_some() { |
84 | .map(|(token, _error)| token.kind) | 85 | return None; |
86 | } | ||
87 | Some(single_token) | ||
85 | } | 88 | } |
86 | 89 | ||
87 | /// Returns `SyntaxKind` and `Option<SyntaxError>` of the first token | 90 | /// Returns `SyntaxKind` and `Option<SyntaxError>` of the first token |
diff --git a/crates/syntax/src/ptr.rs b/crates/syntax/src/ptr.rs index ca7957747..d3fb7a5d9 100644 --- a/crates/syntax/src/ptr.rs +++ b/crates/syntax/src/ptr.rs | |||
@@ -12,6 +12,8 @@ use crate::{AstNode, SyntaxKind, SyntaxNode, TextRange}; | |||
12 | /// specific node across reparses of the same file. | 12 | /// specific node across reparses of the same file. |
13 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | 13 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] |
14 | pub struct SyntaxNodePtr { | 14 | pub struct SyntaxNodePtr { |
15 | // Don't expose this field further. At some point, we might want to replace | ||
16 | // range with node id. | ||
15 | pub(crate) range: TextRange, | 17 | pub(crate) range: TextRange, |
16 | kind: SyntaxKind, | 18 | kind: SyntaxKind, |
17 | } | 19 | } |
diff --git a/crates/syntax/src/validation.rs b/crates/syntax/src/validation.rs index 2dddaf09a..0f9a5e8ae 100644 --- a/crates/syntax/src/validation.rs +++ b/crates/syntax/src/validation.rs | |||
@@ -3,10 +3,11 @@ | |||
3 | mod block; | 3 | mod block; |
4 | 4 | ||
5 | use crate::{ | 5 | use crate::{ |
6 | ast, match_ast, AstNode, SyntaxError, | 6 | algo, ast, match_ast, AstNode, SyntaxError, |
7 | SyntaxKind::{BYTE, BYTE_STRING, CHAR, CONST, FN, INT_NUMBER, STRING, TYPE_ALIAS}, | 7 | SyntaxKind::{BYTE, BYTE_STRING, CHAR, CONST, FN, INT_NUMBER, STRING, TYPE_ALIAS}, |
8 | SyntaxNode, SyntaxToken, TextSize, T, | 8 | SyntaxNode, SyntaxToken, TextSize, T, |
9 | }; | 9 | }; |
10 | use rowan::Direction; | ||
10 | use rustc_lexer::unescape::{ | 11 | use rustc_lexer::unescape::{ |
11 | self, unescape_byte, unescape_byte_literal, unescape_char, unescape_literal, Mode, | 12 | self, unescape_byte, unescape_byte_literal, unescape_char, unescape_literal, Mode, |
12 | }; | 13 | }; |
@@ -95,6 +96,9 @@ pub(crate) fn validate(root: &SyntaxNode) -> Vec<SyntaxError> { | |||
95 | ast::Visibility(it) => validate_visibility(it, &mut errors), | 96 | ast::Visibility(it) => validate_visibility(it, &mut errors), |
96 | ast::RangeExpr(it) => validate_range_expr(it, &mut errors), | 97 | ast::RangeExpr(it) => validate_range_expr(it, &mut errors), |
97 | ast::PathSegment(it) => validate_path_keywords(it, &mut errors), | 98 | ast::PathSegment(it) => validate_path_keywords(it, &mut errors), |
99 | ast::RefType(it) => validate_trait_object_ref_ty(it, &mut errors), | ||
100 | ast::PtrType(it) => validate_trait_object_ptr_ty(it, &mut errors), | ||
101 | ast::FnPtrType(it) => validate_trait_object_fn_ptr_ret_ty(it, &mut errors), | ||
98 | _ => (), | 102 | _ => (), |
99 | } | 103 | } |
100 | } | 104 | } |
@@ -301,3 +305,42 @@ fn validate_path_keywords(segment: ast::PathSegment, errors: &mut Vec<SyntaxErro | |||
301 | return true; | 305 | return true; |
302 | } | 306 | } |
303 | } | 307 | } |
308 | |||
309 | fn validate_trait_object_ref_ty(ty: ast::RefType, errors: &mut Vec<SyntaxError>) { | ||
310 | if let Some(ast::Type::DynTraitType(ty)) = ty.ty() { | ||
311 | if let Some(err) = validate_trait_object_ty(ty) { | ||
312 | errors.push(err); | ||
313 | } | ||
314 | } | ||
315 | } | ||
316 | |||
317 | fn validate_trait_object_ptr_ty(ty: ast::PtrType, errors: &mut Vec<SyntaxError>) { | ||
318 | if let Some(ast::Type::DynTraitType(ty)) = ty.ty() { | ||
319 | if let Some(err) = validate_trait_object_ty(ty) { | ||
320 | errors.push(err); | ||
321 | } | ||
322 | } | ||
323 | } | ||
324 | |||
325 | fn validate_trait_object_fn_ptr_ret_ty(ty: ast::FnPtrType, errors: &mut Vec<SyntaxError>) { | ||
326 | if let Some(ast::Type::DynTraitType(ty)) = ty.ret_type().and_then(|ty| ty.ty()) { | ||
327 | if let Some(err) = validate_trait_object_ty(ty) { | ||
328 | errors.push(err); | ||
329 | } | ||
330 | } | ||
331 | } | ||
332 | |||
333 | fn validate_trait_object_ty(ty: ast::DynTraitType) -> Option<SyntaxError> { | ||
334 | let tbl = ty.type_bound_list()?; | ||
335 | |||
336 | if tbl.bounds().count() > 1 { | ||
337 | let dyn_token = ty.dyn_token()?; | ||
338 | let potential_parentheses = | ||
339 | algo::skip_trivia_token(dyn_token.prev_token()?, Direction::Prev)?; | ||
340 | let kind = potential_parentheses.kind(); | ||
341 | if !matches!(kind, T!['('] | T![<] | T![=]) { | ||
342 | return Some(SyntaxError::new("ambiguous `+` in a type", ty.syntax().text_range())); | ||
343 | } | ||
344 | } | ||
345 | None | ||
346 | } | ||
diff --git a/crates/syntax/test_data/parser/err/0046_ambiguous_trait_object.rast b/crates/syntax/test_data/parser/err/0046_ambiguous_trait_object.rast new file mode 100644 index 000000000..592741cdb --- /dev/null +++ b/crates/syntax/test_data/parser/err/0046_ambiguous_trait_object.rast | |||
@@ -0,0 +1,192 @@ | |||
1 | [email protected] | ||
2 | [email protected] | ||
3 | [email protected] "type" | ||
4 | [email protected] " " | ||
5 | [email protected] | ||
6 | [email protected] "Foo" | ||
7 | [email protected] | ||
8 | [email protected] "<" | ||
9 | [email protected] | ||
10 | [email protected] "\'a" | ||
11 | [email protected] ">" | ||
12 | [email protected] " " | ||
13 | [email protected] "=" | ||
14 | [email protected] " " | ||
15 | [email protected] | ||
16 | [email protected] "&" | ||
17 | [email protected] "\'a" | ||
18 | [email protected] " " | ||
19 | [email protected] | ||
20 | [email protected] "dyn" | ||
21 | [email protected] " " | ||
22 | [email protected] | ||
23 | [email protected] | ||
24 | [email protected] | ||
25 | [email protected] | ||
26 | [email protected] | ||
27 | [email protected] | ||
28 | [email protected] "Send" | ||
29 | [email protected] " " | ||
30 | [email protected] "+" | ||
31 | [email protected] " " | ||
32 | [email protected] | ||
33 | [email protected] | ||
34 | [email protected] | ||
35 | [email protected] | ||
36 | [email protected] | ||
37 | [email protected] "Sync" | ||
38 | [email protected] ";" | ||
39 | [email protected] "\n" | ||
40 | [email protected] | ||
41 | [email protected] "type" | ||
42 | [email protected] " " | ||
43 | [email protected] | ||
44 | [email protected] "Foo" | ||
45 | [email protected] " " | ||
46 | [email protected] "=" | ||
47 | [email protected] " " | ||
48 | [email protected] | ||
49 | [email protected] "*" | ||
50 | [email protected] "const" | ||
51 | [email protected] " " | ||
52 | [email protected] | ||
53 | [email protected] "dyn" | ||
54 | [email protected] " " | ||
55 | [email protected] | ||
56 | [email protected] | ||
57 | [email protected] | ||
58 | [email protected] | ||
59 | [email protected] | ||
60 | [email protected] | ||
61 | [email protected] "Send" | ||
62 | [email protected] " " | ||
63 | [email protected] "+" | ||
64 | [email protected] " " | ||
65 | [email protected] | ||
66 | [email protected] | ||
67 | [email protected] | ||
68 | [email protected] | ||
69 | [email protected] | ||
70 | [email protected] "Sync" | ||
71 | [email protected] ";" | ||
72 | [email protected] "\n" | ||
73 | [email protected] | ||
74 | [email protected] "type" | ||
75 | [email protected] " " | ||
76 | [email protected] | ||
77 | [email protected] "Foo" | ||
78 | [email protected] " " | ||
79 | [email protected] "=" | ||
80 | [email protected] " " | ||
81 | [email protected] | ||
82 | [email protected] "fn" | ||
83 | [email protected] | ||
84 | [email protected] "(" | ||
85 | [email protected] ")" | ||
86 | [email protected] " " | ||
87 | [email protected] | ||
88 | [email protected] "->" | ||
89 | [email protected] " " | ||
90 | [email protected] | ||
91 | [email protected] "dyn" | ||
92 | [email protected] " " | ||
93 | [email protected] | ||
94 | [email protected] | ||
95 | [email protected] | ||
96 | [email protected] | ||
97 | [email protected] | ||
98 | [email protected] | ||
99 | [email protected] "Send" | ||
100 | [email protected] " " | ||
101 | [email protected] "+" | ||
102 | [email protected] " " | ||
103 | [email protected] | ||
104 | [email protected] "\'static" | ||
105 | [email protected] ";" | ||
106 | [email protected] "\n" | ||
107 | [email protected] | ||
108 | [email protected] "fn" | ||
109 | [email protected] " " | ||
110 | [email protected] | ||
111 | [email protected] "main" | ||
112 | [email protected] | ||
113 | [email protected] "(" | ||
114 | [email protected] ")" | ||
115 | [email protected] " " | ||
116 | [email protected] | ||
117 | [email protected] "{" | ||
118 | [email protected] "\n " | ||
119 | [email protected] | ||
120 | [email protected] "let" | ||
121 | [email protected] " " | ||
122 | [email protected] | ||
123 | [email protected] | ||
124 | [email protected] "b" | ||
125 | [email protected] " " | ||
126 | [email protected] "=" | ||
127 | [email protected] " " | ||
128 | [email protected] | ||
129 | [email protected] | ||
130 | [email protected] "(" | ||
131 | [email protected] | ||
132 | [email protected] "&" | ||
133 | [email protected] | ||
134 | [email protected] | ||
135 | [email protected] | ||
136 | [email protected] | ||
137 | [email protected] "a" | ||
138 | [email protected] ")" | ||
139 | [email protected] " " | ||
140 | [email protected] "as" | ||
141 | [email protected] " " | ||
142 | [email protected] | ||
143 | [email protected] "&" | ||
144 | [email protected] | ||
145 | [email protected] "dyn" | ||
146 | [email protected] " " | ||
147 | [email protected] | ||
148 | [email protected] | ||
149 | [email protected] | ||
150 | [email protected] | ||
151 | [email protected] | ||
152 | [email protected] | ||
153 | [email protected] "Add" | ||
154 | [email protected] | ||
155 | [email protected] "<" | ||
156 | [email protected] | ||
157 | [email protected] | ||
158 | [email protected] | ||
159 | [email protected] | ||
160 | [email protected] | ||
161 | [email protected] "Other" | ||
162 | [email protected] "," | ||
163 | [email protected] " " | ||
164 | [email protected] | ||
165 | [email protected] | ||
166 | [email protected] "Output" | ||
167 | [email protected] " " | ||
168 | [email protected] "=" | ||
169 | [email protected] " " | ||
170 | [email protected] | ||
171 | [email protected] | ||
172 | [email protected] | ||
173 | [email protected] | ||
174 | [email protected] "Addable" | ||
175 | [email protected] ">" | ||
176 | [email protected] " " | ||
177 | [email protected] "+" | ||
178 | [email protected] " " | ||
179 | [email protected] | ||
180 | [email protected] | ||
181 | [email protected] | ||
182 | [email protected] | ||
183 | [email protected] | ||
184 | [email protected] "Other" | ||
185 | [email protected] ";" | ||
186 | [email protected] "\n" | ||
187 | [email protected] "}" | ||
188 | [email protected] "\n" | ||
189 | error 19..34: ambiguous `+` in a type | ||
190 | error 54..69: ambiguous `+` in a type | ||
191 | error 90..108: ambiguous `+` in a type | ||
192 | error 143..183: ambiguous `+` in a type | ||
diff --git a/crates/syntax/test_data/parser/err/0046_ambiguous_trait_object.rs b/crates/syntax/test_data/parser/err/0046_ambiguous_trait_object.rs new file mode 100644 index 000000000..3a73d81bb --- /dev/null +++ b/crates/syntax/test_data/parser/err/0046_ambiguous_trait_object.rs | |||
@@ -0,0 +1,6 @@ | |||
1 | type Foo<'a> = &'a dyn Send + Sync; | ||
2 | type Foo = *const dyn Send + Sync; | ||
3 | type Foo = fn() -> dyn Send + 'static; | ||
4 | fn main() { | ||
5 | let b = (&a) as &dyn Add<Other, Output = Addable> + Other; | ||
6 | } | ||
diff --git a/crates/syntax/test_data/parser/ok/0069_multi_trait_object.rast b/crates/syntax/test_data/parser/ok/0069_multi_trait_object.rast new file mode 100644 index 000000000..0cd868a83 --- /dev/null +++ b/crates/syntax/test_data/parser/ok/0069_multi_trait_object.rast | |||
@@ -0,0 +1,200 @@ | |||
1 | [email protected] | ||
2 | [email protected] | ||
3 | [email protected] "type" | ||
4 | [email protected] " " | ||
5 | [email protected] | ||
6 | [email protected] "Foo" | ||
7 | [email protected] | ||
8 | [email protected] "<" | ||
9 | [email protected] | ||
10 | [email protected] "\'a" | ||
11 | [email protected] ">" | ||
12 | [email protected] " " | ||
13 | [email protected] "=" | ||
14 | [email protected] " " | ||
15 | [email protected] | ||
16 | [email protected] "&" | ||
17 | [email protected] "\'a" | ||
18 | [email protected] " " | ||
19 | [email protected] | ||
20 | [email protected] "(" | ||
21 | [email protected] | ||
22 | [email protected] "dyn" | ||
23 | [email protected] " " | ||
24 | [email protected] | ||
25 | [email protected] | ||
26 | [email protected] | ||
27 | [email protected] | ||
28 | [email protected] | ||
29 | [email protected] | ||
30 | [email protected] "Send" | ||
31 | [email protected] " " | ||
32 | [email protected] "+" | ||
33 | [email protected] " " | ||
34 | [email protected] | ||
35 | [email protected] | ||
36 | [email protected] | ||
37 | [email protected] | ||
38 | [email protected] | ||
39 | [email protected] "Sync" | ||
40 | [email protected] ")" | ||
41 | [email protected] ";" | ||
42 | [email protected] "\n" | ||
43 | [email protected] | ||
44 | [email protected] "type" | ||
45 | [email protected] " " | ||
46 | [email protected] | ||
47 | [email protected] "Foo" | ||
48 | [email protected] " " | ||
49 | [email protected] "=" | ||
50 | [email protected] " " | ||
51 | [email protected] | ||
52 | [email protected] "*" | ||
53 | [email protected] "const" | ||
54 | [email protected] " " | ||
55 | [email protected] | ||
56 | [email protected] "(" | ||
57 | [email protected] | ||
58 | [email protected] "dyn" | ||
59 | [email protected] " " | ||
60 | [email protected] | ||
61 | [email protected] | ||
62 | [email protected] | ||
63 | [email protected] | ||
64 | [email protected] | ||
65 | [email protected] | ||
66 | [email protected] "Send" | ||
67 | [email protected] " " | ||
68 | [email protected] "+" | ||
69 | [email protected] " " | ||
70 | [email protected] | ||
71 | [email protected] | ||
72 | [email protected] | ||
73 | [email protected] | ||
74 | [email protected] | ||
75 | [email protected] "Sync" | ||
76 | [email protected] ")" | ||
77 | [email protected] ";" | ||
78 | [email protected] "\n" | ||
79 | [email protected] | ||
80 | [email protected] "type" | ||
81 | [email protected] " " | ||
82 | [email protected] | ||
83 | [email protected] "Foo" | ||
84 | [email protected] " " | ||
85 | [email protected] "=" | ||
86 | [email protected] " " | ||
87 | [email protected] | ||
88 | [email protected] "fn" | ||
89 | [email protected] | ||
90 | [email protected] "(" | ||
91 | [email protected] ")" | ||
92 | [email protected] " " | ||
93 | [email protected] | ||
94 | [email protected] "->" | ||
95 | [email protected] " " | ||
96 | [email protected] | ||
97 | [email protected] "(" | ||
98 | [email protected] | ||
99 | [email protected] "dyn" | ||
100 | [email protected] " " | ||
101 | [email protected] | ||
102 | [email protected] | ||
103 | [email protected] | ||
104 | [email protected] | ||
105 | [email protected] | ||
106 | [email protected] | ||
107 | [email protected] "Send" | ||
108 | [email protected] " " | ||
109 | [email protected] "+" | ||
110 | [email protected] " " | ||
111 | [email protected] | ||
112 | [email protected] "\'static" | ||
113 | [email protected] ")" | ||
114 | [email protected] ";" | ||
115 | [email protected] "\n" | ||
116 | [email protected] | ||
117 | [email protected] "fn" | ||
118 | [email protected] " " | ||
119 | [email protected] | ||
120 | [email protected] "main" | ||
121 | [email protected] | ||
122 | [email protected] "(" | ||
123 | [email protected] ")" | ||
124 | [email protected] " " | ||
125 | [email protected] | ||
126 | [email protected] "{" | ||
127 | [email protected] "\n " | ||
128 | [email protected] | ||
129 | [email protected] "let" | ||
130 | [email protected] " " | ||
131 | [email protected] | ||
132 | [email protected] | ||
133 | [email protected] "b" | ||
134 | [email protected] " " | ||
135 | [email protected] "=" | ||
136 | [email protected] " " | ||
137 | [email protected] | ||
138 | [email protected] | ||
139 | [email protected] "(" | ||
140 | [email protected] | ||
141 | [email protected] "&" | ||
142 | [email protected] | ||
143 | [email protected] | ||
144 | [email protected] | ||
145 | [email protected] | ||
146 | [email protected] "a" | ||
147 | [email protected] ")" | ||
148 | [email protected] " " | ||
149 | [email protected] "as" | ||
150 | [email protected] " " | ||
151 | [email protected] | ||
152 | [email protected] "&" | ||
153 | [email protected] | ||
154 | [email protected] "(" | ||
155 | [email protected] | ||
156 | [email protected] "dyn" | ||
157 | [email protected] " " | ||
158 | [email protected] | ||
159 | [email protected] | ||
160 | [email protected] | ||
161 | [email protected] | ||
162 | [email protected] | ||
163 | [email protected] | ||
164 | [email protected] "Add" | ||
165 | [email protected] | ||
166 | [email protected] "<" | ||
167 | [email protected] | ||
168 | [email protected] | ||
169 | [email protected] | ||
170 | [email protected] | ||
171 | [email protected] | ||
172 | [email protected] "Other" | ||
173 | [email protected] "," | ||
174 | [email protected] " " | ||
175 | [email protected] | ||
176 | [email protected] | ||
177 | [email protected] "Output" | ||
178 | [email protected] " " | ||
179 | [email protected] "=" | ||
180 | [email protected] " " | ||
181 | [email protected] | ||
182 | [email protected] | ||
183 | [email protected] | ||
184 | [email protected] | ||
185 | [email protected] "Addable" | ||
186 | [email protected] ">" | ||
187 | [email protected] " " | ||
188 | [email protected] "+" | ||
189 | [email protected] " " | ||
190 | [email protected] | ||
191 | [email protected] | ||
192 | [email protected] | ||
193 | [email protected] | ||
194 | [email protected] | ||
195 | [email protected] "Other" | ||
196 | [email protected] ")" | ||
197 | [email protected] ";" | ||
198 | [email protected] "\n" | ||
199 | [email protected] "}" | ||
200 | [email protected] "\n" | ||
diff --git a/crates/syntax/test_data/parser/ok/0069_multi_trait_object.rs b/crates/syntax/test_data/parser/ok/0069_multi_trait_object.rs new file mode 100644 index 000000000..97eb79c48 --- /dev/null +++ b/crates/syntax/test_data/parser/ok/0069_multi_trait_object.rs | |||
@@ -0,0 +1,6 @@ | |||
1 | type Foo<'a> = &'a (dyn Send + Sync); | ||
2 | type Foo = *const (dyn Send + Sync); | ||
3 | type Foo = fn() -> (dyn Send + 'static); | ||
4 | fn main() { | ||
5 | let b = (&a) as &(dyn Add<Other, Output = Addable> + Other); | ||
6 | } | ||