diff options
Diffstat (limited to 'crates')
47 files changed, 1402 insertions, 451 deletions
diff --git a/crates/ra_assists/src/handlers/add_from_impl_for_enum.rs b/crates/ra_assists/src/handlers/add_from_impl_for_enum.rs index 03806724a..49deb6701 100644 --- a/crates/ra_assists/src/handlers/add_from_impl_for_enum.rs +++ b/crates/ra_assists/src/handlers/add_from_impl_for_enum.rs | |||
@@ -1,11 +1,12 @@ | |||
1 | use ra_ide_db::RootDatabase; | ||
1 | use ra_syntax::{ | 2 | use ra_syntax::{ |
2 | ast::{self, AstNode, NameOwner}, | 3 | ast::{self, AstNode, NameOwner}, |
3 | TextSize, | 4 | TextSize, |
4 | }; | 5 | }; |
5 | use stdx::format_to; | 6 | use stdx::format_to; |
6 | 7 | ||
7 | use crate::{Assist, AssistCtx, AssistId}; | 8 | use crate::{utils::FamousDefs, Assist, AssistCtx, AssistId}; |
8 | use ra_ide_db::RootDatabase; | 9 | use test_utils::tested_by; |
9 | 10 | ||
10 | // Assist add_from_impl_for_enum | 11 | // Assist add_from_impl_for_enum |
11 | // | 12 | // |
@@ -41,7 +42,8 @@ pub(crate) fn add_from_impl_for_enum(ctx: AssistCtx) -> Option<Assist> { | |||
41 | _ => return None, | 42 | _ => return None, |
42 | }; | 43 | }; |
43 | 44 | ||
44 | if already_has_from_impl(ctx.sema, &variant) { | 45 | if existing_from_impl(ctx.sema, &variant).is_some() { |
46 | tested_by!(test_add_from_impl_already_exists); | ||
45 | return None; | 47 | return None; |
46 | } | 48 | } |
47 | 49 | ||
@@ -70,41 +72,33 @@ impl From<{0}> for {1} {{ | |||
70 | ) | 72 | ) |
71 | } | 73 | } |
72 | 74 | ||
73 | fn already_has_from_impl( | 75 | fn existing_from_impl( |
74 | sema: &'_ hir::Semantics<'_, RootDatabase>, | 76 | sema: &'_ hir::Semantics<'_, RootDatabase>, |
75 | variant: &ast::EnumVariant, | 77 | variant: &ast::EnumVariant, |
76 | ) -> bool { | 78 | ) -> Option<()> { |
77 | let scope = sema.scope(&variant.syntax()); | 79 | let variant = sema.to_def(variant)?; |
80 | let enum_ = variant.parent_enum(sema.db); | ||
81 | let krate = enum_.module(sema.db).krate(); | ||
78 | 82 | ||
79 | let from_path = ast::make::path_from_text("From"); | 83 | let from_trait = FamousDefs(sema, krate).core_convert_From()?; |
80 | let from_hir_path = match hir::Path::from_ast(from_path) { | ||
81 | Some(p) => p, | ||
82 | None => return false, | ||
83 | }; | ||
84 | let from_trait = match scope.resolve_hir_path(&from_hir_path) { | ||
85 | Some(hir::PathResolution::Def(hir::ModuleDef::Trait(t))) => t, | ||
86 | _ => return false, | ||
87 | }; | ||
88 | 84 | ||
89 | let e: hir::Enum = match sema.to_def(&variant.parent_enum()) { | 85 | let enum_type = enum_.ty(sema.db); |
90 | Some(e) => e, | ||
91 | None => return false, | ||
92 | }; | ||
93 | let e_ty = e.ty(sema.db); | ||
94 | 86 | ||
95 | let hir_enum_var: hir::EnumVariant = match sema.to_def(variant) { | 87 | let wrapped_type = variant.fields(sema.db).get(0)?.signature_ty(sema.db); |
96 | Some(ev) => ev, | ||
97 | None => return false, | ||
98 | }; | ||
99 | let var_ty = hir_enum_var.fields(sema.db)[0].signature_ty(sema.db); | ||
100 | 88 | ||
101 | e_ty.impls_trait(sema.db, from_trait, &[var_ty]) | 89 | if enum_type.impls_trait(sema.db, from_trait, &[wrapped_type]) { |
90 | Some(()) | ||
91 | } else { | ||
92 | None | ||
93 | } | ||
102 | } | 94 | } |
103 | 95 | ||
104 | #[cfg(test)] | 96 | #[cfg(test)] |
105 | mod tests { | 97 | mod tests { |
106 | use super::*; | 98 | use super::*; |
99 | |||
107 | use crate::helpers::{check_assist, check_assist_not_applicable}; | 100 | use crate::helpers::{check_assist, check_assist_not_applicable}; |
101 | use test_utils::covers; | ||
108 | 102 | ||
109 | #[test] | 103 | #[test] |
110 | fn test_add_from_impl_for_enum() { | 104 | fn test_add_from_impl_for_enum() { |
@@ -136,36 +130,40 @@ mod tests { | |||
136 | ); | 130 | ); |
137 | } | 131 | } |
138 | 132 | ||
133 | fn check_not_applicable(ra_fixture: &str) { | ||
134 | let fixture = | ||
135 | format!("//- main.rs crate:main deps:core\n{}\n{}", ra_fixture, FamousDefs::FIXTURE); | ||
136 | check_assist_not_applicable(add_from_impl_for_enum, &fixture) | ||
137 | } | ||
138 | |||
139 | #[test] | 139 | #[test] |
140 | fn test_add_from_impl_no_element() { | 140 | fn test_add_from_impl_no_element() { |
141 | check_assist_not_applicable(add_from_impl_for_enum, "enum A { <|>One }"); | 141 | check_not_applicable("enum A { <|>One }"); |
142 | } | 142 | } |
143 | 143 | ||
144 | #[test] | 144 | #[test] |
145 | fn test_add_from_impl_more_than_one_element_in_tuple() { | 145 | fn test_add_from_impl_more_than_one_element_in_tuple() { |
146 | check_assist_not_applicable(add_from_impl_for_enum, "enum A { <|>One(u32, String) }"); | 146 | check_not_applicable("enum A { <|>One(u32, String) }"); |
147 | } | 147 | } |
148 | 148 | ||
149 | #[test] | 149 | #[test] |
150 | fn test_add_from_impl_struct_variant() { | 150 | fn test_add_from_impl_struct_variant() { |
151 | check_assist_not_applicable(add_from_impl_for_enum, "enum A { <|>One { x: u32 } }"); | 151 | check_not_applicable("enum A { <|>One { x: u32 } }"); |
152 | } | 152 | } |
153 | 153 | ||
154 | #[test] | 154 | #[test] |
155 | fn test_add_from_impl_already_exists() { | 155 | fn test_add_from_impl_already_exists() { |
156 | check_assist_not_applicable( | 156 | covers!(test_add_from_impl_already_exists); |
157 | add_from_impl_for_enum, | 157 | check_not_applicable( |
158 | r#"enum A { <|>One(u32), } | 158 | r#" |
159 | enum A { <|>One(u32), } | ||
159 | 160 | ||
160 | impl From<u32> for A { | 161 | impl From<u32> for A { |
161 | fn from(v: u32) -> Self { | 162 | fn from(v: u32) -> Self { |
162 | A::One(v) | 163 | A::One(v) |
163 | } | 164 | } |
164 | } | 165 | } |
165 | 166 | "#, | |
166 | pub trait From<T> { | ||
167 | fn from(T) -> Self; | ||
168 | }"#, | ||
169 | ); | 167 | ); |
170 | } | 168 | } |
171 | 169 | ||
diff --git a/crates/ra_assists/src/handlers/replace_if_let_with_match.rs b/crates/ra_assists/src/handlers/replace_if_let_with_match.rs index 0a0a88f3d..9841f6980 100644 --- a/crates/ra_assists/src/handlers/replace_if_let_with_match.rs +++ b/crates/ra_assists/src/handlers/replace_if_let_with_match.rs | |||
@@ -1,11 +1,10 @@ | |||
1 | use ra_fmt::unwrap_trivial_block; | 1 | use ra_fmt::unwrap_trivial_block; |
2 | use ra_syntax::{ | 2 | use ra_syntax::{ |
3 | ast::{self, make}, | 3 | ast::{self, edit::IndentLevel, make}, |
4 | AstNode, | 4 | AstNode, |
5 | }; | 5 | }; |
6 | 6 | ||
7 | use crate::{Assist, AssistCtx, AssistId}; | 7 | use crate::{utils::TryEnum, Assist, AssistCtx, AssistId}; |
8 | use ast::edit::IndentLevel; | ||
9 | 8 | ||
10 | // Assist: replace_if_let_with_match | 9 | // Assist: replace_if_let_with_match |
11 | // | 10 | // |
@@ -44,15 +43,21 @@ pub(crate) fn replace_if_let_with_match(ctx: AssistCtx) -> Option<Assist> { | |||
44 | ast::ElseBranch::IfExpr(_) => return None, | 43 | ast::ElseBranch::IfExpr(_) => return None, |
45 | }; | 44 | }; |
46 | 45 | ||
47 | ctx.add_assist(AssistId("replace_if_let_with_match"), "Replace with match", |edit| { | 46 | let sema = ctx.sema; |
47 | ctx.add_assist(AssistId("replace_if_let_with_match"), "Replace with match", move |edit| { | ||
48 | let match_expr = { | 48 | let match_expr = { |
49 | let then_arm = { | 49 | let then_arm = { |
50 | let then_expr = unwrap_trivial_block(then_block); | 50 | let then_expr = unwrap_trivial_block(then_block); |
51 | make::match_arm(vec![pat], then_expr) | 51 | make::match_arm(vec![pat.clone()], then_expr) |
52 | }; | 52 | }; |
53 | let else_arm = { | 53 | let else_arm = { |
54 | let pattern = sema | ||
55 | .type_of_pat(&pat) | ||
56 | .and_then(|ty| TryEnum::from_ty(sema, &ty)) | ||
57 | .map(|it| it.sad_pattern()) | ||
58 | .unwrap_or_else(|| make::placeholder_pat().into()); | ||
54 | let else_expr = unwrap_trivial_block(else_block); | 59 | let else_expr = unwrap_trivial_block(else_block); |
55 | make::match_arm(vec![make::placeholder_pat().into()], else_expr) | 60 | make::match_arm(vec![pattern], else_expr) |
56 | }; | 61 | }; |
57 | make::expr_match(expr, make::match_arm_list(vec![then_arm, else_arm])) | 62 | make::expr_match(expr, make::match_arm_list(vec![then_arm, else_arm])) |
58 | }; | 63 | }; |
@@ -68,6 +73,7 @@ pub(crate) fn replace_if_let_with_match(ctx: AssistCtx) -> Option<Assist> { | |||
68 | #[cfg(test)] | 73 | #[cfg(test)] |
69 | mod tests { | 74 | mod tests { |
70 | use super::*; | 75 | use super::*; |
76 | |||
71 | use crate::helpers::{check_assist, check_assist_target}; | 77 | use crate::helpers::{check_assist, check_assist_target}; |
72 | 78 | ||
73 | #[test] | 79 | #[test] |
@@ -145,4 +151,64 @@ impl VariantData { | |||
145 | }", | 151 | }", |
146 | ); | 152 | ); |
147 | } | 153 | } |
154 | |||
155 | #[test] | ||
156 | fn special_case_option() { | ||
157 | check_assist( | ||
158 | replace_if_let_with_match, | ||
159 | r#" | ||
160 | enum Option<T> { Some(T), None } | ||
161 | use Option::*; | ||
162 | |||
163 | fn foo(x: Option<i32>) { | ||
164 | <|>if let Some(x) = x { | ||
165 | println!("{}", x) | ||
166 | } else { | ||
167 | println!("none") | ||
168 | } | ||
169 | } | ||
170 | "#, | ||
171 | r#" | ||
172 | enum Option<T> { Some(T), None } | ||
173 | use Option::*; | ||
174 | |||
175 | fn foo(x: Option<i32>) { | ||
176 | <|>match x { | ||
177 | Some(x) => println!("{}", x), | ||
178 | None => println!("none"), | ||
179 | } | ||
180 | } | ||
181 | "#, | ||
182 | ); | ||
183 | } | ||
184 | |||
185 | #[test] | ||
186 | fn special_case_result() { | ||
187 | check_assist( | ||
188 | replace_if_let_with_match, | ||
189 | r#" | ||
190 | enum Result<T, E> { Ok(T), Err(E) } | ||
191 | use Result::*; | ||
192 | |||
193 | fn foo(x: Result<i32, ()>) { | ||
194 | <|>if let Ok(x) = x { | ||
195 | println!("{}", x) | ||
196 | } else { | ||
197 | println!("none") | ||
198 | } | ||
199 | } | ||
200 | "#, | ||
201 | r#" | ||
202 | enum Result<T, E> { Ok(T), Err(E) } | ||
203 | use Result::*; | ||
204 | |||
205 | fn foo(x: Result<i32, ()>) { | ||
206 | <|>match x { | ||
207 | Ok(x) => println!("{}", x), | ||
208 | Err(_) => println!("none"), | ||
209 | } | ||
210 | } | ||
211 | "#, | ||
212 | ); | ||
213 | } | ||
148 | } | 214 | } |
diff --git a/crates/ra_assists/src/handlers/replace_let_with_if_let.rs b/crates/ra_assists/src/handlers/replace_let_with_if_let.rs index bdbaae389..0cf23b754 100644 --- a/crates/ra_assists/src/handlers/replace_let_with_if_let.rs +++ b/crates/ra_assists/src/handlers/replace_let_with_if_let.rs | |||
@@ -1,6 +1,5 @@ | |||
1 | use std::iter::once; | 1 | use std::iter::once; |
2 | 2 | ||
3 | use hir::Adt; | ||
4 | use ra_syntax::{ | 3 | use ra_syntax::{ |
5 | ast::{ | 4 | ast::{ |
6 | self, | 5 | self, |
@@ -12,6 +11,7 @@ use ra_syntax::{ | |||
12 | 11 | ||
13 | use crate::{ | 12 | use crate::{ |
14 | assist_ctx::{Assist, AssistCtx}, | 13 | assist_ctx::{Assist, AssistCtx}, |
14 | utils::TryEnum, | ||
15 | AssistId, | 15 | AssistId, |
16 | }; | 16 | }; |
17 | 17 | ||
@@ -45,20 +45,10 @@ pub(crate) fn replace_let_with_if_let(ctx: AssistCtx) -> Option<Assist> { | |||
45 | let init = let_stmt.initializer()?; | 45 | let init = let_stmt.initializer()?; |
46 | let original_pat = let_stmt.pat()?; | 46 | let original_pat = let_stmt.pat()?; |
47 | let ty = ctx.sema.type_of_expr(&init)?; | 47 | let ty = ctx.sema.type_of_expr(&init)?; |
48 | let enum_ = match ty.as_adt() { | 48 | let happy_variant = TryEnum::from_ty(ctx.sema, &ty).map(|it| it.happy_case()); |
49 | Some(Adt::Enum(it)) => it, | ||
50 | _ => return None, | ||
51 | }; | ||
52 | let happy_case = | ||
53 | [("Result", "Ok"), ("Option", "Some")].iter().find_map(|(known_type, happy_case)| { | ||
54 | if &enum_.name(ctx.db).to_string() == known_type { | ||
55 | return Some(happy_case); | ||
56 | } | ||
57 | None | ||
58 | }); | ||
59 | 49 | ||
60 | ctx.add_assist(AssistId("replace_let_with_if_let"), "Replace with if-let", |edit| { | 50 | ctx.add_assist(AssistId("replace_let_with_if_let"), "Replace with if-let", |edit| { |
61 | let with_placeholder: ast::Pat = match happy_case { | 51 | let with_placeholder: ast::Pat = match happy_variant { |
62 | None => make::placeholder_pat().into(), | 52 | None => make::placeholder_pat().into(), |
63 | Some(var_name) => make::tuple_struct_pat( | 53 | Some(var_name) => make::tuple_struct_pat( |
64 | make::path_unqualified(make::path_segment(make::name_ref(var_name))), | 54 | make::path_unqualified(make::path_segment(make::name_ref(var_name))), |
diff --git a/crates/ra_assists/src/handlers/replace_unwrap_with_match.rs b/crates/ra_assists/src/handlers/replace_unwrap_with_match.rs index 62cb7a763..62d4ea522 100644 --- a/crates/ra_assists/src/handlers/replace_unwrap_with_match.rs +++ b/crates/ra_assists/src/handlers/replace_unwrap_with_match.rs | |||
@@ -1,12 +1,11 @@ | |||
1 | use std::iter; | 1 | use std::iter; |
2 | 2 | ||
3 | use ra_syntax::{ | 3 | use ra_syntax::{ |
4 | ast::{self, make}, | 4 | ast::{self, edit::IndentLevel, make}, |
5 | AstNode, | 5 | AstNode, |
6 | }; | 6 | }; |
7 | 7 | ||
8 | use crate::{Assist, AssistCtx, AssistId}; | 8 | use crate::{utils::TryEnum, Assist, AssistCtx, AssistId}; |
9 | use ast::edit::IndentLevel; | ||
10 | 9 | ||
11 | // Assist: replace_unwrap_with_match | 10 | // Assist: replace_unwrap_with_match |
12 | // | 11 | // |
@@ -38,42 +37,27 @@ pub(crate) fn replace_unwrap_with_match(ctx: AssistCtx) -> Option<Assist> { | |||
38 | } | 37 | } |
39 | let caller = method_call.expr()?; | 38 | let caller = method_call.expr()?; |
40 | let ty = ctx.sema.type_of_expr(&caller)?; | 39 | let ty = ctx.sema.type_of_expr(&caller)?; |
40 | let happy_variant = TryEnum::from_ty(ctx.sema, &ty)?.happy_case(); | ||
41 | 41 | ||
42 | let type_name = ty.as_adt()?.name(ctx.sema.db).to_string(); | 42 | ctx.add_assist(AssistId("replace_unwrap_with_match"), "Replace unwrap with match", |edit| { |
43 | let ok_path = make::path_unqualified(make::path_segment(make::name_ref(happy_variant))); | ||
44 | let it = make::bind_pat(make::name("a")).into(); | ||
45 | let ok_tuple = make::tuple_struct_pat(ok_path, iter::once(it)).into(); | ||
43 | 46 | ||
44 | for (unwrap_type, variant_name) in [("Result", "Ok"), ("Option", "Some")].iter() { | 47 | let bind_path = make::path_unqualified(make::path_segment(make::name_ref("a"))); |
45 | if &type_name == unwrap_type { | 48 | let ok_arm = make::match_arm(iter::once(ok_tuple), make::expr_path(bind_path)); |
46 | return ctx.add_assist( | ||
47 | AssistId("replace_unwrap_with_match"), | ||
48 | "Replace unwrap with match", | ||
49 | |edit| { | ||
50 | let ok_path = | ||
51 | make::path_unqualified(make::path_segment(make::name_ref(variant_name))); | ||
52 | let it = make::bind_pat(make::name("a")).into(); | ||
53 | let ok_tuple = make::tuple_struct_pat(ok_path, iter::once(it)).into(); | ||
54 | 49 | ||
55 | let bind_path = make::path_unqualified(make::path_segment(make::name_ref("a"))); | 50 | let unreachable_call = make::unreachable_macro_call().into(); |
56 | let ok_arm = make::match_arm(iter::once(ok_tuple), make::expr_path(bind_path)); | 51 | let err_arm = make::match_arm(iter::once(make::placeholder_pat().into()), unreachable_call); |
57 | 52 | ||
58 | let unreachable_call = make::unreachable_macro_call().into(); | 53 | let match_arm_list = make::match_arm_list(vec![ok_arm, err_arm]); |
59 | let err_arm = make::match_arm( | 54 | let match_expr = make::expr_match(caller.clone(), match_arm_list); |
60 | iter::once(make::placeholder_pat().into()), | 55 | let match_expr = IndentLevel::from_node(method_call.syntax()).increase_indent(match_expr); |
61 | unreachable_call, | ||
62 | ); | ||
63 | 56 | ||
64 | let match_arm_list = make::match_arm_list(vec![ok_arm, err_arm]); | 57 | edit.target(method_call.syntax().text_range()); |
65 | let match_expr = make::expr_match(caller.clone(), match_arm_list); | 58 | edit.set_cursor(caller.syntax().text_range().start()); |
66 | let match_expr = | 59 | edit.replace_ast::<ast::Expr>(method_call.into(), match_expr); |
67 | IndentLevel::from_node(method_call.syntax()).increase_indent(match_expr); | 60 | }) |
68 | |||
69 | edit.target(method_call.syntax().text_range()); | ||
70 | edit.set_cursor(caller.syntax().text_range().start()); | ||
71 | edit.replace_ast::<ast::Expr>(method_call.into(), match_expr); | ||
72 | }, | ||
73 | ); | ||
74 | } | ||
75 | } | ||
76 | None | ||
77 | } | 61 | } |
78 | 62 | ||
79 | #[cfg(test)] | 63 | #[cfg(test)] |
diff --git a/crates/ra_assists/src/marks.rs b/crates/ra_assists/src/marks.rs index 6c2a2b8b6..8d910205f 100644 --- a/crates/ra_assists/src/marks.rs +++ b/crates/ra_assists/src/marks.rs | |||
@@ -8,4 +8,5 @@ test_utils::marks![ | |||
8 | test_not_inline_mut_variable | 8 | test_not_inline_mut_variable |
9 | test_not_applicable_if_variable_unused | 9 | test_not_applicable_if_variable_unused |
10 | change_visibility_field_false_positive | 10 | change_visibility_field_false_positive |
11 | test_add_from_impl_already_exists | ||
11 | ]; | 12 | ]; |
diff --git a/crates/ra_assists/src/utils.rs b/crates/ra_assists/src/utils.rs index 3d6c59bda..efd988697 100644 --- a/crates/ra_assists/src/utils.rs +++ b/crates/ra_assists/src/utils.rs | |||
@@ -1,7 +1,9 @@ | |||
1 | //! Assorted functions shared by several assists. | 1 | //! Assorted functions shared by several assists. |
2 | pub(crate) mod insert_use; | 2 | pub(crate) mod insert_use; |
3 | 3 | ||
4 | use hir::Semantics; | 4 | use std::iter; |
5 | |||
6 | use hir::{Adt, Crate, Semantics, Trait, Type}; | ||
5 | use ra_ide_db::RootDatabase; | 7 | use ra_ide_db::RootDatabase; |
6 | use ra_syntax::{ | 8 | use ra_syntax::{ |
7 | ast::{self, make, NameOwner}, | 9 | ast::{self, make, NameOwner}, |
@@ -99,3 +101,109 @@ fn invert_special_case(expr: &ast::Expr) -> Option<ast::Expr> { | |||
99 | _ => None, | 101 | _ => None, |
100 | } | 102 | } |
101 | } | 103 | } |
104 | |||
105 | #[derive(Clone, Copy)] | ||
106 | pub(crate) enum TryEnum { | ||
107 | Result, | ||
108 | Option, | ||
109 | } | ||
110 | |||
111 | impl TryEnum { | ||
112 | const ALL: [TryEnum; 2] = [TryEnum::Option, TryEnum::Result]; | ||
113 | |||
114 | pub(crate) fn from_ty(sema: &Semantics<RootDatabase>, ty: &Type) -> Option<TryEnum> { | ||
115 | let enum_ = match ty.as_adt() { | ||
116 | Some(Adt::Enum(it)) => it, | ||
117 | _ => return None, | ||
118 | }; | ||
119 | TryEnum::ALL.iter().find_map(|&var| { | ||
120 | if &enum_.name(sema.db).to_string() == var.type_name() { | ||
121 | return Some(var); | ||
122 | } | ||
123 | None | ||
124 | }) | ||
125 | } | ||
126 | |||
127 | pub(crate) fn happy_case(self) -> &'static str { | ||
128 | match self { | ||
129 | TryEnum::Result => "Ok", | ||
130 | TryEnum::Option => "Some", | ||
131 | } | ||
132 | } | ||
133 | |||
134 | pub(crate) fn sad_pattern(self) -> ast::Pat { | ||
135 | match self { | ||
136 | TryEnum::Result => make::tuple_struct_pat( | ||
137 | make::path_unqualified(make::path_segment(make::name_ref("Err"))), | ||
138 | iter::once(make::placeholder_pat().into()), | ||
139 | ) | ||
140 | .into(), | ||
141 | TryEnum::Option => make::bind_pat(make::name("None")).into(), | ||
142 | } | ||
143 | } | ||
144 | |||
145 | fn type_name(self) -> &'static str { | ||
146 | match self { | ||
147 | TryEnum::Result => "Result", | ||
148 | TryEnum::Option => "Option", | ||
149 | } | ||
150 | } | ||
151 | } | ||
152 | |||
153 | /// Helps with finding well-know things inside the standard library. This is | ||
154 | /// somewhat similar to the known paths infra inside hir, but it different; We | ||
155 | /// want to make sure that IDE specific paths don't become interesting inside | ||
156 | /// the compiler itself as well. | ||
157 | pub(crate) struct FamousDefs<'a, 'b>(pub(crate) &'a Semantics<'b, RootDatabase>, pub(crate) Crate); | ||
158 | |||
159 | #[allow(non_snake_case)] | ||
160 | impl FamousDefs<'_, '_> { | ||
161 | #[cfg(test)] | ||
162 | pub(crate) const FIXTURE: &'static str = r#" | ||
163 | //- /libcore.rs crate:core | ||
164 | pub mod convert{ | ||
165 | pub trait From<T> { | ||
166 | fn from(T) -> Self; | ||
167 | } | ||
168 | } | ||
169 | |||
170 | pub mod prelude { pub use crate::convert::From } | ||
171 | #[prelude_import] | ||
172 | pub use prelude::*; | ||
173 | "#; | ||
174 | |||
175 | pub(crate) fn core_convert_From(&self) -> Option<Trait> { | ||
176 | self.find_trait("core:convert:From") | ||
177 | } | ||
178 | |||
179 | fn find_trait(&self, path: &str) -> Option<Trait> { | ||
180 | let db = self.0.db; | ||
181 | let mut path = path.split(':'); | ||
182 | let trait_ = path.next_back()?; | ||
183 | let std_crate = path.next()?; | ||
184 | let std_crate = self | ||
185 | .1 | ||
186 | .dependencies(db) | ||
187 | .into_iter() | ||
188 | .find(|dep| &dep.name.to_string() == std_crate)? | ||
189 | .krate; | ||
190 | |||
191 | let mut module = std_crate.root_module(db)?; | ||
192 | for segment in path { | ||
193 | module = module.children(db).find_map(|child| { | ||
194 | let name = child.name(db)?; | ||
195 | if &name.to_string() == segment { | ||
196 | Some(child) | ||
197 | } else { | ||
198 | None | ||
199 | } | ||
200 | })?; | ||
201 | } | ||
202 | let def = | ||
203 | module.scope(db, None).into_iter().find(|(name, _def)| &name.to_string() == trait_)?.1; | ||
204 | match def { | ||
205 | hir::ScopeDef::ModuleDef(hir::ModuleDef::Trait(it)) => Some(it), | ||
206 | _ => None, | ||
207 | } | ||
208 | } | ||
209 | } | ||
diff --git a/crates/ra_flycheck/Cargo.toml b/crates/ra_flycheck/Cargo.toml index 76e5cada4..324c33d9d 100644 --- a/crates/ra_flycheck/Cargo.toml +++ b/crates/ra_flycheck/Cargo.toml | |||
@@ -6,7 +6,7 @@ authors = ["rust-analyzer developers"] | |||
6 | 6 | ||
7 | [dependencies] | 7 | [dependencies] |
8 | crossbeam-channel = "0.4.0" | 8 | crossbeam-channel = "0.4.0" |
9 | lsp-types = { version = "0.73.0", features = ["proposed"] } | 9 | lsp-types = { version = "0.74.0", features = ["proposed"] } |
10 | log = "0.4.8" | 10 | log = "0.4.8" |
11 | cargo_metadata = "0.9.1" | 11 | cargo_metadata = "0.9.1" |
12 | serde_json = "1.0.48" | 12 | serde_json = "1.0.48" |
diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs index fb788736d..af59aa1b6 100644 --- a/crates/ra_hir/src/code_model.rs +++ b/crates/ra_hir/src/code_model.rs | |||
@@ -953,6 +953,16 @@ impl TypeParam { | |||
953 | pub fn module(self, db: &dyn HirDatabase) -> Module { | 953 | pub fn module(self, db: &dyn HirDatabase) -> Module { |
954 | self.id.parent.module(db.upcast()).into() | 954 | self.id.parent.module(db.upcast()).into() |
955 | } | 955 | } |
956 | |||
957 | pub fn ty(self, db: &dyn HirDatabase) -> Type { | ||
958 | let resolver = self.id.parent.resolver(db.upcast()); | ||
959 | let environment = TraitEnvironment::lower(db, &resolver); | ||
960 | let ty = Ty::Placeholder(self.id); | ||
961 | Type { | ||
962 | krate: self.id.parent.module(db.upcast()).krate, | ||
963 | ty: InEnvironment { value: ty, environment }, | ||
964 | } | ||
965 | } | ||
956 | } | 966 | } |
957 | 967 | ||
958 | // FIXME: rename from `ImplDef` to `Impl` | 968 | // FIXME: rename from `ImplDef` to `Impl` |
@@ -1157,18 +1167,21 @@ impl Type { | |||
1157 | 1167 | ||
1158 | pub fn fields(&self, db: &dyn HirDatabase) -> Vec<(Field, Type)> { | 1168 | pub fn fields(&self, db: &dyn HirDatabase) -> Vec<(Field, Type)> { |
1159 | if let Ty::Apply(a_ty) = &self.ty.value { | 1169 | if let Ty::Apply(a_ty) = &self.ty.value { |
1160 | if let TypeCtor::Adt(AdtId::StructId(s)) = a_ty.ctor { | 1170 | let variant_id = match a_ty.ctor { |
1161 | let var_def = s.into(); | 1171 | TypeCtor::Adt(AdtId::StructId(s)) => s.into(), |
1162 | return db | 1172 | TypeCtor::Adt(AdtId::UnionId(u)) => u.into(), |
1163 | .field_types(var_def) | 1173 | _ => return Vec::new(), |
1164 | .iter() | 1174 | }; |
1165 | .map(|(local_id, ty)| { | 1175 | |
1166 | let def = Field { parent: var_def.into(), id: local_id }; | 1176 | return db |
1167 | let ty = ty.clone().subst(&a_ty.parameters); | 1177 | .field_types(variant_id) |
1168 | (def, self.derived(ty)) | 1178 | .iter() |
1169 | }) | 1179 | .map(|(local_id, ty)| { |
1170 | .collect(); | 1180 | let def = Field { parent: variant_id.into(), id: local_id }; |
1171 | } | 1181 | let ty = ty.clone().subst(&a_ty.parameters); |
1182 | (def, self.derived(ty)) | ||
1183 | }) | ||
1184 | .collect(); | ||
1172 | }; | 1185 | }; |
1173 | Vec::new() | 1186 | Vec::new() |
1174 | } | 1187 | } |
diff --git a/crates/ra_hir/src/semantics.rs b/crates/ra_hir/src/semantics.rs index 86bfb416c..a0a0f234b 100644 --- a/crates/ra_hir/src/semantics.rs +++ b/crates/ra_hir/src/semantics.rs | |||
@@ -9,6 +9,7 @@ use hir_def::{ | |||
9 | AsMacroCall, TraitId, | 9 | AsMacroCall, TraitId, |
10 | }; | 10 | }; |
11 | use hir_expand::ExpansionInfo; | 11 | use hir_expand::ExpansionInfo; |
12 | use hir_ty::associated_type_shorthand_candidates; | ||
12 | use itertools::Itertools; | 13 | use itertools::Itertools; |
13 | use ra_db::{FileId, FileRange}; | 14 | use ra_db::{FileId, FileRange}; |
14 | use ra_prof::profile; | 15 | use ra_prof::profile; |
@@ -24,8 +25,9 @@ use crate::{ | |||
24 | semantics::source_to_def::{ChildContainer, SourceToDefCache, SourceToDefCtx}, | 25 | semantics::source_to_def::{ChildContainer, SourceToDefCache, SourceToDefCtx}, |
25 | source_analyzer::{resolve_hir_path, SourceAnalyzer}, | 26 | source_analyzer::{resolve_hir_path, SourceAnalyzer}, |
26 | AssocItem, Field, Function, HirFileId, ImplDef, InFile, Local, MacroDef, Module, ModuleDef, | 27 | AssocItem, Field, Function, HirFileId, ImplDef, InFile, Local, MacroDef, Module, ModuleDef, |
27 | Name, Origin, Path, ScopeDef, Trait, Type, TypeParam, | 28 | Name, Origin, Path, ScopeDef, Trait, Type, TypeAlias, TypeParam, |
28 | }; | 29 | }; |
30 | use resolver::TypeNs; | ||
29 | 31 | ||
30 | #[derive(Debug, Clone, PartialEq, Eq)] | 32 | #[derive(Debug, Clone, PartialEq, Eq)] |
31 | pub enum PathResolution { | 33 | pub enum PathResolution { |
@@ -40,6 +42,44 @@ pub enum PathResolution { | |||
40 | AssocItem(AssocItem), | 42 | AssocItem(AssocItem), |
41 | } | 43 | } |
42 | 44 | ||
45 | impl PathResolution { | ||
46 | fn in_type_ns(&self) -> Option<TypeNs> { | ||
47 | match self { | ||
48 | PathResolution::Def(ModuleDef::Adt(adt)) => Some(TypeNs::AdtId((*adt).into())), | ||
49 | PathResolution::Def(ModuleDef::BuiltinType(builtin)) => { | ||
50 | Some(TypeNs::BuiltinType(*builtin)) | ||
51 | } | ||
52 | PathResolution::Def(ModuleDef::Const(_)) | ||
53 | | PathResolution::Def(ModuleDef::EnumVariant(_)) | ||
54 | | PathResolution::Def(ModuleDef::Function(_)) | ||
55 | | PathResolution::Def(ModuleDef::Module(_)) | ||
56 | | PathResolution::Def(ModuleDef::Static(_)) | ||
57 | | PathResolution::Def(ModuleDef::Trait(_)) => None, | ||
58 | PathResolution::Def(ModuleDef::TypeAlias(alias)) => { | ||
59 | Some(TypeNs::TypeAliasId((*alias).into())) | ||
60 | } | ||
61 | PathResolution::Local(_) | PathResolution::Macro(_) => None, | ||
62 | PathResolution::TypeParam(param) => Some(TypeNs::GenericParam((*param).into())), | ||
63 | PathResolution::SelfType(impl_def) => Some(TypeNs::SelfType((*impl_def).into())), | ||
64 | PathResolution::AssocItem(AssocItem::Const(_)) | ||
65 | | PathResolution::AssocItem(AssocItem::Function(_)) => None, | ||
66 | PathResolution::AssocItem(AssocItem::TypeAlias(alias)) => { | ||
67 | Some(TypeNs::TypeAliasId((*alias).into())) | ||
68 | } | ||
69 | } | ||
70 | } | ||
71 | |||
72 | /// Returns an iterator over associated types that may be specified after this path (using | ||
73 | /// `Ty::Assoc` syntax). | ||
74 | pub fn assoc_type_shorthand_candidates<R>( | ||
75 | &self, | ||
76 | db: &dyn HirDatabase, | ||
77 | mut cb: impl FnMut(TypeAlias) -> Option<R>, | ||
78 | ) -> Option<R> { | ||
79 | associated_type_shorthand_candidates(db, self.in_type_ns()?, |_, _, id| cb(id.into())) | ||
80 | } | ||
81 | } | ||
82 | |||
43 | /// Primary API to get semantic information, like types, from syntax trees. | 83 | /// Primary API to get semantic information, like types, from syntax trees. |
44 | pub struct Semantics<'db, DB> { | 84 | pub struct Semantics<'db, DB> { |
45 | pub db: &'db DB, | 85 | pub db: &'db DB, |
diff --git a/crates/ra_hir_expand/src/builtin_derive.rs b/crates/ra_hir_expand/src/builtin_derive.rs index bb45b0f1d..e60f879a3 100644 --- a/crates/ra_hir_expand/src/builtin_derive.rs +++ b/crates/ra_hir_expand/src/builtin_derive.rs | |||
@@ -9,7 +9,7 @@ use ra_syntax::{ | |||
9 | }; | 9 | }; |
10 | 10 | ||
11 | use crate::db::AstDatabase; | 11 | use crate::db::AstDatabase; |
12 | use crate::{name, quote, LazyMacroId, MacroDefId, MacroDefKind}; | 12 | use crate::{name, quote, LazyMacroId, MacroCallId, MacroDefId, MacroDefKind}; |
13 | 13 | ||
14 | macro_rules! register_builtin { | 14 | macro_rules! register_builtin { |
15 | ( $($trait:ident => $expand:ident),* ) => { | 15 | ( $($trait:ident => $expand:ident),* ) => { |
@@ -153,76 +153,113 @@ fn expand_simple_derive( | |||
153 | Ok(expanded) | 153 | Ok(expanded) |
154 | } | 154 | } |
155 | 155 | ||
156 | fn find_builtin_crate(db: &dyn AstDatabase, id: LazyMacroId) -> tt::TokenTree { | ||
157 | // FIXME: make hygiene works for builtin derive macro | ||
158 | // such that $crate can be used here. | ||
159 | |||
160 | let m: MacroCallId = id.into(); | ||
161 | let file_id = m.as_file().original_file(db); | ||
162 | let cg = db.crate_graph(); | ||
163 | let krates = db.relevant_crates(file_id); | ||
164 | let krate = match krates.get(0) { | ||
165 | Some(krate) => krate, | ||
166 | None => { | ||
167 | let tt = quote! { core }; | ||
168 | return tt.token_trees[0].clone(); | ||
169 | } | ||
170 | }; | ||
171 | |||
172 | // XXX | ||
173 | // All crates except core itself should have a dependency on core, | ||
174 | // We detect `core` by seeing whether it doesn't have such a dependency. | ||
175 | let tt = if cg[*krate].dependencies.iter().any(|dep| dep.name == "core") { | ||
176 | quote! { core } | ||
177 | } else { | ||
178 | quote! { crate } | ||
179 | }; | ||
180 | |||
181 | tt.token_trees[0].clone() | ||
182 | } | ||
183 | |||
156 | fn copy_expand( | 184 | fn copy_expand( |
157 | _db: &dyn AstDatabase, | 185 | db: &dyn AstDatabase, |
158 | _id: LazyMacroId, | 186 | id: LazyMacroId, |
159 | tt: &tt::Subtree, | 187 | tt: &tt::Subtree, |
160 | ) -> Result<tt::Subtree, mbe::ExpandError> { | 188 | ) -> Result<tt::Subtree, mbe::ExpandError> { |
161 | expand_simple_derive(tt, quote! { std::marker::Copy }) | 189 | let krate = find_builtin_crate(db, id); |
190 | expand_simple_derive(tt, quote! { #krate::marker::Copy }) | ||
162 | } | 191 | } |
163 | 192 | ||
164 | fn clone_expand( | 193 | fn clone_expand( |
165 | _db: &dyn AstDatabase, | 194 | db: &dyn AstDatabase, |
166 | _id: LazyMacroId, | 195 | id: LazyMacroId, |
167 | tt: &tt::Subtree, | 196 | tt: &tt::Subtree, |
168 | ) -> Result<tt::Subtree, mbe::ExpandError> { | 197 | ) -> Result<tt::Subtree, mbe::ExpandError> { |
169 | expand_simple_derive(tt, quote! { std::clone::Clone }) | 198 | let krate = find_builtin_crate(db, id); |
199 | expand_simple_derive(tt, quote! { #krate::clone::Clone }) | ||
170 | } | 200 | } |
171 | 201 | ||
172 | fn default_expand( | 202 | fn default_expand( |
173 | _db: &dyn AstDatabase, | 203 | db: &dyn AstDatabase, |
174 | _id: LazyMacroId, | 204 | id: LazyMacroId, |
175 | tt: &tt::Subtree, | 205 | tt: &tt::Subtree, |
176 | ) -> Result<tt::Subtree, mbe::ExpandError> { | 206 | ) -> Result<tt::Subtree, mbe::ExpandError> { |
177 | expand_simple_derive(tt, quote! { std::default::Default }) | 207 | let krate = find_builtin_crate(db, id); |
208 | expand_simple_derive(tt, quote! { #krate::default::Default }) | ||
178 | } | 209 | } |
179 | 210 | ||
180 | fn debug_expand( | 211 | fn debug_expand( |
181 | _db: &dyn AstDatabase, | 212 | db: &dyn AstDatabase, |
182 | _id: LazyMacroId, | 213 | id: LazyMacroId, |
183 | tt: &tt::Subtree, | 214 | tt: &tt::Subtree, |
184 | ) -> Result<tt::Subtree, mbe::ExpandError> { | 215 | ) -> Result<tt::Subtree, mbe::ExpandError> { |
185 | expand_simple_derive(tt, quote! { std::fmt::Debug }) | 216 | let krate = find_builtin_crate(db, id); |
217 | expand_simple_derive(tt, quote! { #krate::fmt::Debug }) | ||
186 | } | 218 | } |
187 | 219 | ||
188 | fn hash_expand( | 220 | fn hash_expand( |
189 | _db: &dyn AstDatabase, | 221 | db: &dyn AstDatabase, |
190 | _id: LazyMacroId, | 222 | id: LazyMacroId, |
191 | tt: &tt::Subtree, | 223 | tt: &tt::Subtree, |
192 | ) -> Result<tt::Subtree, mbe::ExpandError> { | 224 | ) -> Result<tt::Subtree, mbe::ExpandError> { |
193 | expand_simple_derive(tt, quote! { std::hash::Hash }) | 225 | let krate = find_builtin_crate(db, id); |
226 | expand_simple_derive(tt, quote! { #krate::hash::Hash }) | ||
194 | } | 227 | } |
195 | 228 | ||
196 | fn eq_expand( | 229 | fn eq_expand( |
197 | _db: &dyn AstDatabase, | 230 | db: &dyn AstDatabase, |
198 | _id: LazyMacroId, | 231 | id: LazyMacroId, |
199 | tt: &tt::Subtree, | 232 | tt: &tt::Subtree, |
200 | ) -> Result<tt::Subtree, mbe::ExpandError> { | 233 | ) -> Result<tt::Subtree, mbe::ExpandError> { |
201 | expand_simple_derive(tt, quote! { std::cmp::Eq }) | 234 | let krate = find_builtin_crate(db, id); |
235 | expand_simple_derive(tt, quote! { #krate::cmp::Eq }) | ||
202 | } | 236 | } |
203 | 237 | ||
204 | fn partial_eq_expand( | 238 | fn partial_eq_expand( |
205 | _db: &dyn AstDatabase, | 239 | db: &dyn AstDatabase, |
206 | _id: LazyMacroId, | 240 | id: LazyMacroId, |
207 | tt: &tt::Subtree, | 241 | tt: &tt::Subtree, |
208 | ) -> Result<tt::Subtree, mbe::ExpandError> { | 242 | ) -> Result<tt::Subtree, mbe::ExpandError> { |
209 | expand_simple_derive(tt, quote! { std::cmp::PartialEq }) | 243 | let krate = find_builtin_crate(db, id); |
244 | expand_simple_derive(tt, quote! { #krate::cmp::PartialEq }) | ||
210 | } | 245 | } |
211 | 246 | ||
212 | fn ord_expand( | 247 | fn ord_expand( |
213 | _db: &dyn AstDatabase, | 248 | db: &dyn AstDatabase, |
214 | _id: LazyMacroId, | 249 | id: LazyMacroId, |
215 | tt: &tt::Subtree, | 250 | tt: &tt::Subtree, |
216 | ) -> Result<tt::Subtree, mbe::ExpandError> { | 251 | ) -> Result<tt::Subtree, mbe::ExpandError> { |
217 | expand_simple_derive(tt, quote! { std::cmp::Ord }) | 252 | let krate = find_builtin_crate(db, id); |
253 | expand_simple_derive(tt, quote! { #krate::cmp::Ord }) | ||
218 | } | 254 | } |
219 | 255 | ||
220 | fn partial_ord_expand( | 256 | fn partial_ord_expand( |
221 | _db: &dyn AstDatabase, | 257 | db: &dyn AstDatabase, |
222 | _id: LazyMacroId, | 258 | id: LazyMacroId, |
223 | tt: &tt::Subtree, | 259 | tt: &tt::Subtree, |
224 | ) -> Result<tt::Subtree, mbe::ExpandError> { | 260 | ) -> Result<tt::Subtree, mbe::ExpandError> { |
225 | expand_simple_derive(tt, quote! { std::cmp::PartialOrd }) | 261 | let krate = find_builtin_crate(db, id); |
262 | expand_simple_derive(tt, quote! { #krate::cmp::PartialOrd }) | ||
226 | } | 263 | } |
227 | 264 | ||
228 | #[cfg(test)] | 265 | #[cfg(test)] |
@@ -234,8 +271,18 @@ mod tests { | |||
234 | 271 | ||
235 | fn expand_builtin_derive(s: &str, name: Name) -> String { | 272 | fn expand_builtin_derive(s: &str, name: Name) -> String { |
236 | let def = find_builtin_derive(&name).unwrap(); | 273 | let def = find_builtin_derive(&name).unwrap(); |
274 | let fixture = format!( | ||
275 | r#"//- /main.rs crate:main deps:core | ||
276 | <|> | ||
277 | {} | ||
278 | //- /lib.rs crate:core | ||
279 | // empty | ||
280 | "#, | ||
281 | s | ||
282 | ); | ||
237 | 283 | ||
238 | let (db, file_id) = TestDB::with_single_file(&s); | 284 | let (db, file_pos) = TestDB::with_position(&fixture); |
285 | let file_id = file_pos.file_id; | ||
239 | let parsed = db.parse(file_id); | 286 | let parsed = db.parse(file_id); |
240 | let items: Vec<_> = | 287 | let items: Vec<_> = |
241 | parsed.syntax_node().descendants().filter_map(ast::ModuleItem::cast).collect(); | 288 | parsed.syntax_node().descendants().filter_map(ast::ModuleItem::cast).collect(); |
@@ -264,7 +311,7 @@ mod tests { | |||
264 | known::Copy, | 311 | known::Copy, |
265 | ); | 312 | ); |
266 | 313 | ||
267 | assert_eq!(expanded, "impl< >std::marker::CopyforFoo< >{}"); | 314 | assert_eq!(expanded, "impl< >core::marker::CopyforFoo< >{}"); |
268 | } | 315 | } |
269 | 316 | ||
270 | #[test] | 317 | #[test] |
@@ -279,7 +326,7 @@ mod tests { | |||
279 | 326 | ||
280 | assert_eq!( | 327 | assert_eq!( |
281 | expanded, | 328 | expanded, |
282 | "impl<T0:std::marker::Copy,T1:std::marker::Copy>std::marker::CopyforFoo<T0,T1>{}" | 329 | "impl<T0:core::marker::Copy,T1:core::marker::Copy>core::marker::CopyforFoo<T0,T1>{}" |
283 | ); | 330 | ); |
284 | } | 331 | } |
285 | 332 | ||
@@ -297,7 +344,7 @@ mod tests { | |||
297 | 344 | ||
298 | assert_eq!( | 345 | assert_eq!( |
299 | expanded, | 346 | expanded, |
300 | "impl<T0:std::marker::Copy,T1:std::marker::Copy>std::marker::CopyforFoo<T0,T1>{}" | 347 | "impl<T0:core::marker::Copy,T1:core::marker::Copy>core::marker::CopyforFoo<T0,T1>{}" |
301 | ); | 348 | ); |
302 | } | 349 | } |
303 | 350 | ||
@@ -313,7 +360,7 @@ mod tests { | |||
313 | 360 | ||
314 | assert_eq!( | 361 | assert_eq!( |
315 | expanded, | 362 | expanded, |
316 | "impl<T0:std::clone::Clone,T1:std::clone::Clone>std::clone::CloneforFoo<T0,T1>{}" | 363 | "impl<T0:core::clone::Clone,T1:core::clone::Clone>core::clone::CloneforFoo<T0,T1>{}" |
317 | ); | 364 | ); |
318 | } | 365 | } |
319 | } | 366 | } |
diff --git a/crates/ra_hir_ty/src/lib.rs b/crates/ra_hir_ty/src/lib.rs index a8ef32ec5..a6f56c661 100644 --- a/crates/ra_hir_ty/src/lib.rs +++ b/crates/ra_hir_ty/src/lib.rs | |||
@@ -66,7 +66,8 @@ pub use autoderef::autoderef; | |||
66 | pub use infer::{InferTy, InferenceResult}; | 66 | pub use infer::{InferTy, InferenceResult}; |
67 | pub use lower::CallableDef; | 67 | pub use lower::CallableDef; |
68 | pub use lower::{ | 68 | pub use lower::{ |
69 | callable_item_sig, ImplTraitLoweringMode, TyDefId, TyLoweringContext, ValueTyDefId, | 69 | associated_type_shorthand_candidates, callable_item_sig, ImplTraitLoweringMode, TyDefId, |
70 | TyLoweringContext, ValueTyDefId, | ||
70 | }; | 71 | }; |
71 | pub use traits::{InEnvironment, Obligation, ProjectionPredicate, TraitEnvironment}; | 72 | pub use traits::{InEnvironment, Obligation, ProjectionPredicate, TraitEnvironment}; |
72 | 73 | ||
diff --git a/crates/ra_hir_ty/src/lower.rs b/crates/ra_hir_ty/src/lower.rs index a6f893037..9ad6dbe07 100644 --- a/crates/ra_hir_ty/src/lower.rs +++ b/crates/ra_hir_ty/src/lower.rs | |||
@@ -17,9 +17,9 @@ use hir_def::{ | |||
17 | path::{GenericArg, Path, PathSegment, PathSegments}, | 17 | path::{GenericArg, Path, PathSegment, PathSegments}, |
18 | resolver::{HasResolver, Resolver, TypeNs}, | 18 | resolver::{HasResolver, Resolver, TypeNs}, |
19 | type_ref::{TypeBound, TypeRef}, | 19 | type_ref::{TypeBound, TypeRef}, |
20 | AdtId, AssocContainerId, ConstId, EnumId, EnumVariantId, FunctionId, GenericDefId, HasModule, | 20 | AdtId, AssocContainerId, AssocItemId, ConstId, EnumId, EnumVariantId, FunctionId, GenericDefId, |
21 | ImplId, LocalFieldId, Lookup, StaticId, StructId, TraitId, TypeAliasId, TypeParamId, UnionId, | 21 | HasModule, ImplId, LocalFieldId, Lookup, StaticId, StructId, TraitId, TypeAliasId, TypeParamId, |
22 | VariantId, | 22 | UnionId, VariantId, |
23 | }; | 23 | }; |
24 | use ra_arena::map::ArenaMap; | 24 | use ra_arena::map::ArenaMap; |
25 | use ra_db::CrateId; | 25 | use ra_db::CrateId; |
@@ -34,6 +34,7 @@ use crate::{ | |||
34 | Binders, BoundVar, DebruijnIndex, FnSig, GenericPredicate, PolyFnSig, ProjectionPredicate, | 34 | Binders, BoundVar, DebruijnIndex, FnSig, GenericPredicate, PolyFnSig, ProjectionPredicate, |
35 | ProjectionTy, Substs, TraitEnvironment, TraitRef, Ty, TypeCtor, TypeWalk, | 35 | ProjectionTy, Substs, TraitEnvironment, TraitRef, Ty, TypeCtor, TypeWalk, |
36 | }; | 36 | }; |
37 | use hir_expand::name::Name; | ||
37 | 38 | ||
38 | #[derive(Debug)] | 39 | #[derive(Debug)] |
39 | pub struct TyLoweringContext<'a> { | 40 | pub struct TyLoweringContext<'a> { |
@@ -383,61 +384,38 @@ impl Ty { | |||
383 | res: Option<TypeNs>, | 384 | res: Option<TypeNs>, |
384 | segment: PathSegment<'_>, | 385 | segment: PathSegment<'_>, |
385 | ) -> Ty { | 386 | ) -> Ty { |
386 | let traits_from_env: Vec<_> = match res { | 387 | if let Some(res) = res { |
387 | Some(TypeNs::SelfType(impl_id)) => match ctx.db.impl_trait(impl_id) { | 388 | let ty = |
388 | None => return Ty::Unknown, | 389 | associated_type_shorthand_candidates(ctx.db, res, move |name, t, associated_ty| { |
389 | Some(trait_ref) => vec![trait_ref.value], | 390 | if name == segment.name { |
390 | }, | 391 | let substs = match ctx.type_param_mode { |
391 | Some(TypeNs::GenericParam(param_id)) => { | 392 | TypeParamLoweringMode::Placeholder => { |
392 | let predicates = ctx.db.generic_predicates_for_param(param_id); | 393 | // if we're lowering to placeholders, we have to put |
393 | let mut traits_: Vec<_> = predicates | 394 | // them in now |
394 | .iter() | 395 | let s = Substs::type_params( |
395 | .filter_map(|pred| match &pred.value { | 396 | ctx.db, |
396 | GenericPredicate::Implemented(tr) => Some(tr.clone()), | 397 | ctx.resolver.generic_def().expect( |
397 | _ => None, | 398 | "there should be generics if there's a generic param", |
398 | }) | 399 | ), |
399 | .collect(); | 400 | ); |
400 | // Handle `Self::Type` referring to own associated type in trait definitions | 401 | t.substs.clone().subst_bound_vars(&s) |
401 | if let GenericDefId::TraitId(trait_id) = param_id.parent { | 402 | } |
402 | let generics = generics(ctx.db.upcast(), trait_id.into()); | 403 | TypeParamLoweringMode::Variable => t.substs.clone(), |
403 | if generics.params.types[param_id.local_id].provenance | ||
404 | == TypeParamProvenance::TraitSelf | ||
405 | { | ||
406 | let trait_ref = TraitRef { | ||
407 | trait_: trait_id, | ||
408 | substs: Substs::bound_vars(&generics, DebruijnIndex::INNERMOST), | ||
409 | }; | 404 | }; |
410 | traits_.push(trait_ref); | 405 | // FIXME handle type parameters on the segment |
406 | return Some(Ty::Projection(ProjectionTy { | ||
407 | associated_ty, | ||
408 | parameters: substs, | ||
409 | })); | ||
411 | } | 410 | } |
412 | } | 411 | |
413 | traits_ | 412 | None |
414 | } | 413 | }); |
415 | _ => return Ty::Unknown, | 414 | |
416 | }; | 415 | ty.unwrap_or(Ty::Unknown) |
417 | let traits = traits_from_env.into_iter().flat_map(|t| all_super_trait_refs(ctx.db, t)); | 416 | } else { |
418 | for t in traits { | 417 | Ty::Unknown |
419 | if let Some(associated_ty) = | ||
420 | ctx.db.trait_data(t.trait_).associated_type_by_name(&segment.name) | ||
421 | { | ||
422 | let substs = match ctx.type_param_mode { | ||
423 | TypeParamLoweringMode::Placeholder => { | ||
424 | // if we're lowering to placeholders, we have to put | ||
425 | // them in now | ||
426 | let s = Substs::type_params( | ||
427 | ctx.db, | ||
428 | ctx.resolver | ||
429 | .generic_def() | ||
430 | .expect("there should be generics if there's a generic param"), | ||
431 | ); | ||
432 | t.substs.subst_bound_vars(&s) | ||
433 | } | ||
434 | TypeParamLoweringMode::Variable => t.substs, | ||
435 | }; | ||
436 | // FIXME handle (forbid) type parameters on the segment | ||
437 | return Ty::Projection(ProjectionTy { associated_ty, parameters: substs }); | ||
438 | } | ||
439 | } | 418 | } |
440 | Ty::Unknown | ||
441 | } | 419 | } |
442 | 420 | ||
443 | fn from_hir_path_inner( | 421 | fn from_hir_path_inner( |
@@ -694,6 +672,61 @@ pub fn callable_item_sig(db: &dyn HirDatabase, def: CallableDef) -> PolyFnSig { | |||
694 | } | 672 | } |
695 | } | 673 | } |
696 | 674 | ||
675 | pub fn associated_type_shorthand_candidates<R>( | ||
676 | db: &dyn HirDatabase, | ||
677 | res: TypeNs, | ||
678 | mut cb: impl FnMut(&Name, &TraitRef, TypeAliasId) -> Option<R>, | ||
679 | ) -> Option<R> { | ||
680 | let traits_from_env: Vec<_> = match res { | ||
681 | TypeNs::SelfType(impl_id) => match db.impl_trait(impl_id) { | ||
682 | None => vec![], | ||
683 | Some(trait_ref) => vec![trait_ref.value], | ||
684 | }, | ||
685 | TypeNs::GenericParam(param_id) => { | ||
686 | let predicates = db.generic_predicates_for_param(param_id); | ||
687 | let mut traits_: Vec<_> = predicates | ||
688 | .iter() | ||
689 | .filter_map(|pred| match &pred.value { | ||
690 | GenericPredicate::Implemented(tr) => Some(tr.clone()), | ||
691 | _ => None, | ||
692 | }) | ||
693 | .collect(); | ||
694 | // Handle `Self::Type` referring to own associated type in trait definitions | ||
695 | if let GenericDefId::TraitId(trait_id) = param_id.parent { | ||
696 | let generics = generics(db.upcast(), trait_id.into()); | ||
697 | if generics.params.types[param_id.local_id].provenance | ||
698 | == TypeParamProvenance::TraitSelf | ||
699 | { | ||
700 | let trait_ref = TraitRef { | ||
701 | trait_: trait_id, | ||
702 | substs: Substs::bound_vars(&generics, DebruijnIndex::INNERMOST), | ||
703 | }; | ||
704 | traits_.push(trait_ref); | ||
705 | } | ||
706 | } | ||
707 | traits_ | ||
708 | } | ||
709 | _ => vec![], | ||
710 | }; | ||
711 | |||
712 | for t in traits_from_env.into_iter().flat_map(move |t| all_super_trait_refs(db, t)) { | ||
713 | let data = db.trait_data(t.trait_); | ||
714 | |||
715 | for (name, assoc_id) in &data.items { | ||
716 | match assoc_id { | ||
717 | AssocItemId::TypeAliasId(alias) => { | ||
718 | if let Some(result) = cb(name, &t, *alias) { | ||
719 | return Some(result); | ||
720 | } | ||
721 | } | ||
722 | AssocItemId::FunctionId(_) | AssocItemId::ConstId(_) => {} | ||
723 | } | ||
724 | } | ||
725 | } | ||
726 | |||
727 | None | ||
728 | } | ||
729 | |||
697 | /// Build the type of all specific fields of a struct or enum variant. | 730 | /// Build the type of all specific fields of a struct or enum variant. |
698 | pub(crate) fn field_types_query( | 731 | pub(crate) fn field_types_query( |
699 | db: &dyn HirDatabase, | 732 | db: &dyn HirDatabase, |
diff --git a/crates/ra_hir_ty/src/tests/macros.rs b/crates/ra_hir_ty/src/tests/macros.rs index 6b5267232..5ddecbdc6 100644 --- a/crates/ra_hir_ty/src/tests/macros.rs +++ b/crates/ra_hir_ty/src/tests/macros.rs | |||
@@ -622,14 +622,14 @@ fn main() { | |||
622 | fn infer_derive_clone_simple() { | 622 | fn infer_derive_clone_simple() { |
623 | let (db, pos) = TestDB::with_position( | 623 | let (db, pos) = TestDB::with_position( |
624 | r#" | 624 | r#" |
625 | //- /main.rs crate:main deps:std | 625 | //- /main.rs crate:main deps:core |
626 | #[derive(Clone)] | 626 | #[derive(Clone)] |
627 | struct S; | 627 | struct S; |
628 | fn test() { | 628 | fn test() { |
629 | S.clone()<|>; | 629 | S.clone()<|>; |
630 | } | 630 | } |
631 | 631 | ||
632 | //- /lib.rs crate:std | 632 | //- /lib.rs crate:core |
633 | #[prelude_import] | 633 | #[prelude_import] |
634 | use clone::*; | 634 | use clone::*; |
635 | mod clone { | 635 | mod clone { |
@@ -643,10 +643,35 @@ mod clone { | |||
643 | } | 643 | } |
644 | 644 | ||
645 | #[test] | 645 | #[test] |
646 | fn infer_derive_clone_in_core() { | ||
647 | let (db, pos) = TestDB::with_position( | ||
648 | r#" | ||
649 | //- /lib.rs crate:core | ||
650 | #[prelude_import] | ||
651 | use clone::*; | ||
652 | mod clone { | ||
653 | trait Clone { | ||
654 | fn clone(&self) -> Self; | ||
655 | } | ||
656 | } | ||
657 | #[derive(Clone)] | ||
658 | pub struct S; | ||
659 | |||
660 | //- /main.rs crate:main deps:core | ||
661 | use core::S; | ||
662 | fn test() { | ||
663 | S.clone()<|>; | ||
664 | } | ||
665 | "#, | ||
666 | ); | ||
667 | assert_eq!("S", type_at_pos(&db, pos)); | ||
668 | } | ||
669 | |||
670 | #[test] | ||
646 | fn infer_derive_clone_with_params() { | 671 | fn infer_derive_clone_with_params() { |
647 | let (db, pos) = TestDB::with_position( | 672 | let (db, pos) = TestDB::with_position( |
648 | r#" | 673 | r#" |
649 | //- /main.rs crate:main deps:std | 674 | //- /main.rs crate:main deps:core |
650 | #[derive(Clone)] | 675 | #[derive(Clone)] |
651 | struct S; | 676 | struct S; |
652 | #[derive(Clone)] | 677 | #[derive(Clone)] |
@@ -656,7 +681,7 @@ fn test() { | |||
656 | (Wrapper(S).clone(), Wrapper(NonClone).clone())<|>; | 681 | (Wrapper(S).clone(), Wrapper(NonClone).clone())<|>; |
657 | } | 682 | } |
658 | 683 | ||
659 | //- /lib.rs crate:std | 684 | //- /lib.rs crate:core |
660 | #[prelude_import] | 685 | #[prelude_import] |
661 | use clone::*; | 686 | use clone::*; |
662 | mod clone { | 687 | mod clone { |
diff --git a/crates/ra_ide/src/completion/complete_dot.rs b/crates/ra_ide/src/completion/complete_dot.rs index 814354ffa..05f825c6f 100644 --- a/crates/ra_ide/src/completion/complete_dot.rs +++ b/crates/ra_ide/src/completion/complete_dot.rs | |||
@@ -250,6 +250,44 @@ mod tests { | |||
250 | } | 250 | } |
251 | 251 | ||
252 | #[test] | 252 | #[test] |
253 | fn test_union_field_completion() { | ||
254 | assert_debug_snapshot!( | ||
255 | do_ref_completion( | ||
256 | r" | ||
257 | union Un { | ||
258 | field: u8, | ||
259 | other: u16, | ||
260 | } | ||
261 | |||
262 | fn foo(u: Un) { | ||
263 | u.<|> | ||
264 | } | ||
265 | ", | ||
266 | ), | ||
267 | @r###" | ||
268 | [ | ||
269 | CompletionItem { | ||
270 | label: "field", | ||
271 | source_range: 140..140, | ||
272 | delete: 140..140, | ||
273 | insert: "field", | ||
274 | kind: Field, | ||
275 | detail: "u8", | ||
276 | }, | ||
277 | CompletionItem { | ||
278 | label: "other", | ||
279 | source_range: 140..140, | ||
280 | delete: 140..140, | ||
281 | insert: "other", | ||
282 | kind: Field, | ||
283 | detail: "u16", | ||
284 | }, | ||
285 | ] | ||
286 | "### | ||
287 | ); | ||
288 | } | ||
289 | |||
290 | #[test] | ||
253 | fn test_method_completion() { | 291 | fn test_method_completion() { |
254 | assert_debug_snapshot!( | 292 | assert_debug_snapshot!( |
255 | do_ref_completion( | 293 | do_ref_completion( |
diff --git a/crates/ra_ide/src/completion/complete_qualified_path.rs b/crates/ra_ide/src/completion/complete_qualified_path.rs index dd10f74e6..aa56a5cd8 100644 --- a/crates/ra_ide/src/completion/complete_qualified_path.rs +++ b/crates/ra_ide/src/completion/complete_qualified_path.rs | |||
@@ -5,19 +5,29 @@ use ra_syntax::AstNode; | |||
5 | use test_utils::tested_by; | 5 | use test_utils::tested_by; |
6 | 6 | ||
7 | use crate::completion::{CompletionContext, Completions}; | 7 | use crate::completion::{CompletionContext, Completions}; |
8 | use rustc_hash::FxHashSet; | ||
8 | 9 | ||
9 | pub(super) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionContext) { | 10 | pub(super) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionContext) { |
10 | let path = match &ctx.path_prefix { | 11 | let path = match &ctx.path_prefix { |
11 | Some(path) => path.clone(), | 12 | Some(path) => path.clone(), |
12 | _ => return, | 13 | _ => return, |
13 | }; | 14 | }; |
14 | let def = match ctx.scope().resolve_hir_path(&path) { | 15 | let scope = ctx.scope(); |
15 | Some(PathResolution::Def(def)) => def, | 16 | let context_module = scope.module(); |
16 | _ => return, | 17 | |
18 | let res = match scope.resolve_hir_path(&path) { | ||
19 | Some(res) => res, | ||
20 | None => return, | ||
17 | }; | 21 | }; |
18 | let context_module = ctx.scope().module(); | 22 | |
19 | match def { | 23 | // Add associated types on type parameters and `Self`. |
20 | hir::ModuleDef::Module(module) => { | 24 | res.assoc_type_shorthand_candidates(ctx.db, |alias| { |
25 | acc.add_type_alias(ctx, alias); | ||
26 | None::<()> | ||
27 | }); | ||
28 | |||
29 | match res { | ||
30 | PathResolution::Def(hir::ModuleDef::Module(module)) => { | ||
21 | let module_scope = module.scope(ctx.db, context_module); | 31 | let module_scope = module.scope(ctx.db, context_module); |
22 | for (name, def) in module_scope { | 32 | for (name, def) in module_scope { |
23 | if ctx.use_item_syntax.is_some() { | 33 | if ctx.use_item_syntax.is_some() { |
@@ -35,7 +45,8 @@ pub(super) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon | |||
35 | acc.add_resolution(ctx, name.to_string(), &def); | 45 | acc.add_resolution(ctx, name.to_string(), &def); |
36 | } | 46 | } |
37 | } | 47 | } |
38 | hir::ModuleDef::Adt(_) | hir::ModuleDef::TypeAlias(_) => { | 48 | PathResolution::Def(def @ hir::ModuleDef::Adt(_)) |
49 | | PathResolution::Def(def @ hir::ModuleDef::TypeAlias(_)) => { | ||
39 | if let hir::ModuleDef::Adt(Adt::Enum(e)) = def { | 50 | if let hir::ModuleDef::Adt(Adt::Enum(e)) = def { |
40 | for variant in e.variants(ctx.db) { | 51 | for variant in e.variants(ctx.db) { |
41 | acc.add_enum_variant(ctx, variant, None); | 52 | acc.add_enum_variant(ctx, variant, None); |
@@ -46,8 +57,10 @@ pub(super) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon | |||
46 | hir::ModuleDef::TypeAlias(a) => a.ty(ctx.db), | 57 | hir::ModuleDef::TypeAlias(a) => a.ty(ctx.db), |
47 | _ => unreachable!(), | 58 | _ => unreachable!(), |
48 | }; | 59 | }; |
49 | // Iterate assoc types separately | 60 | |
50 | // FIXME: complete T::AssocType | 61 | // XXX: For parity with Rust bug #22519, this does not complete Ty::AssocType. |
62 | // (where AssocType is defined on a trait, not an inherent impl) | ||
63 | |||
51 | let krate = ctx.krate; | 64 | let krate = ctx.krate; |
52 | if let Some(krate) = krate { | 65 | if let Some(krate) = krate { |
53 | let traits_in_scope = ctx.scope().traits_in_scope(); | 66 | let traits_in_scope = ctx.scope().traits_in_scope(); |
@@ -65,6 +78,7 @@ pub(super) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon | |||
65 | None::<()> | 78 | None::<()> |
66 | }); | 79 | }); |
67 | 80 | ||
81 | // Iterate assoc types separately | ||
68 | ty.iterate_impl_items(ctx.db, krate, |item| { | 82 | ty.iterate_impl_items(ctx.db, krate, |item| { |
69 | if context_module.map_or(false, |m| !item.is_visible_from(ctx.db, m)) { | 83 | if context_module.map_or(false, |m| !item.is_visible_from(ctx.db, m)) { |
70 | return None; | 84 | return None; |
@@ -77,7 +91,8 @@ pub(super) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon | |||
77 | }); | 91 | }); |
78 | } | 92 | } |
79 | } | 93 | } |
80 | hir::ModuleDef::Trait(t) => { | 94 | PathResolution::Def(hir::ModuleDef::Trait(t)) => { |
95 | // Handles `Trait::assoc` as well as `<Ty as Trait>::assoc`. | ||
81 | for item in t.items(ctx.db) { | 96 | for item in t.items(ctx.db) { |
82 | if context_module.map_or(false, |m| !item.is_visible_from(ctx.db, m)) { | 97 | if context_module.map_or(false, |m| !item.is_visible_from(ctx.db, m)) { |
83 | continue; | 98 | continue; |
@@ -91,8 +106,38 @@ pub(super) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon | |||
91 | } | 106 | } |
92 | } | 107 | } |
93 | } | 108 | } |
109 | PathResolution::TypeParam(_) | PathResolution::SelfType(_) => { | ||
110 | if let Some(krate) = ctx.krate { | ||
111 | let ty = match res { | ||
112 | PathResolution::TypeParam(param) => param.ty(ctx.db), | ||
113 | PathResolution::SelfType(impl_def) => impl_def.target_ty(ctx.db), | ||
114 | _ => return, | ||
115 | }; | ||
116 | |||
117 | let traits_in_scope = ctx.scope().traits_in_scope(); | ||
118 | let mut seen = FxHashSet::default(); | ||
119 | ty.iterate_path_candidates(ctx.db, krate, &traits_in_scope, None, |_ty, item| { | ||
120 | if context_module.map_or(false, |m| !item.is_visible_from(ctx.db, m)) { | ||
121 | return None; | ||
122 | } | ||
123 | |||
124 | // We might iterate candidates of a trait multiple times here, so deduplicate | ||
125 | // them. | ||
126 | if seen.insert(item) { | ||
127 | match item { | ||
128 | hir::AssocItem::Function(func) => { | ||
129 | acc.add_function(ctx, func, None); | ||
130 | } | ||
131 | hir::AssocItem::Const(ct) => acc.add_const(ctx, ct), | ||
132 | hir::AssocItem::TypeAlias(ty) => acc.add_type_alias(ctx, ty), | ||
133 | } | ||
134 | } | ||
135 | None::<()> | ||
136 | }); | ||
137 | } | ||
138 | } | ||
94 | _ => {} | 139 | _ => {} |
95 | }; | 140 | } |
96 | } | 141 | } |
97 | 142 | ||
98 | #[cfg(test)] | 143 | #[cfg(test)] |
@@ -844,6 +889,211 @@ mod tests { | |||
844 | } | 889 | } |
845 | 890 | ||
846 | #[test] | 891 | #[test] |
892 | fn completes_ty_param_assoc_ty() { | ||
893 | assert_debug_snapshot!( | ||
894 | do_reference_completion( | ||
895 | " | ||
896 | //- /lib.rs | ||
897 | trait Super { | ||
898 | type Ty; | ||
899 | const CONST: u8; | ||
900 | fn func() {} | ||
901 | fn method(&self) {} | ||
902 | } | ||
903 | |||
904 | trait Sub: Super { | ||
905 | type SubTy; | ||
906 | const C2: (); | ||
907 | fn subfunc() {} | ||
908 | fn submethod(&self) {} | ||
909 | } | ||
910 | |||
911 | fn foo<T: Sub>() { | ||
912 | T::<|> | ||
913 | } | ||
914 | " | ||
915 | ), | ||
916 | @r###" | ||
917 | [ | ||
918 | CompletionItem { | ||
919 | label: "C2", | ||
920 | source_range: 219..219, | ||
921 | delete: 219..219, | ||
922 | insert: "C2", | ||
923 | kind: Const, | ||
924 | detail: "const C2: ();", | ||
925 | }, | ||
926 | CompletionItem { | ||
927 | label: "CONST", | ||
928 | source_range: 219..219, | ||
929 | delete: 219..219, | ||
930 | insert: "CONST", | ||
931 | kind: Const, | ||
932 | detail: "const CONST: u8;", | ||
933 | }, | ||
934 | CompletionItem { | ||
935 | label: "SubTy", | ||
936 | source_range: 219..219, | ||
937 | delete: 219..219, | ||
938 | insert: "SubTy", | ||
939 | kind: TypeAlias, | ||
940 | detail: "type SubTy;", | ||
941 | }, | ||
942 | CompletionItem { | ||
943 | label: "Ty", | ||
944 | source_range: 219..219, | ||
945 | delete: 219..219, | ||
946 | insert: "Ty", | ||
947 | kind: TypeAlias, | ||
948 | detail: "type Ty;", | ||
949 | }, | ||
950 | CompletionItem { | ||
951 | label: "func()", | ||
952 | source_range: 219..219, | ||
953 | delete: 219..219, | ||
954 | insert: "func()$0", | ||
955 | kind: Function, | ||
956 | lookup: "func", | ||
957 | detail: "fn func()", | ||
958 | }, | ||
959 | CompletionItem { | ||
960 | label: "method()", | ||
961 | source_range: 219..219, | ||
962 | delete: 219..219, | ||
963 | insert: "method()$0", | ||
964 | kind: Method, | ||
965 | lookup: "method", | ||
966 | detail: "fn method(&self)", | ||
967 | }, | ||
968 | CompletionItem { | ||
969 | label: "subfunc()", | ||
970 | source_range: 219..219, | ||
971 | delete: 219..219, | ||
972 | insert: "subfunc()$0", | ||
973 | kind: Function, | ||
974 | lookup: "subfunc", | ||
975 | detail: "fn subfunc()", | ||
976 | }, | ||
977 | CompletionItem { | ||
978 | label: "submethod()", | ||
979 | source_range: 219..219, | ||
980 | delete: 219..219, | ||
981 | insert: "submethod()$0", | ||
982 | kind: Method, | ||
983 | lookup: "submethod", | ||
984 | detail: "fn submethod(&self)", | ||
985 | }, | ||
986 | ] | ||
987 | "### | ||
988 | ); | ||
989 | } | ||
990 | |||
991 | #[test] | ||
992 | fn completes_self_param_assoc_ty() { | ||
993 | assert_debug_snapshot!( | ||
994 | do_reference_completion( | ||
995 | " | ||
996 | //- /lib.rs | ||
997 | trait Super { | ||
998 | type Ty; | ||
999 | const CONST: u8 = 0; | ||
1000 | fn func() {} | ||
1001 | fn method(&self) {} | ||
1002 | } | ||
1003 | |||
1004 | trait Sub: Super { | ||
1005 | type SubTy; | ||
1006 | const C2: () = (); | ||
1007 | fn subfunc() {} | ||
1008 | fn submethod(&self) {} | ||
1009 | } | ||
1010 | |||
1011 | struct Wrap<T>(T); | ||
1012 | impl<T> Super for Wrap<T> {} | ||
1013 | impl<T> Sub for Wrap<T> { | ||
1014 | fn subfunc() { | ||
1015 | // Should be able to assume `Self: Sub + Super` | ||
1016 | Self::<|> | ||
1017 | } | ||
1018 | } | ||
1019 | " | ||
1020 | ), | ||
1021 | @r###" | ||
1022 | [ | ||
1023 | CompletionItem { | ||
1024 | label: "C2", | ||
1025 | source_range: 365..365, | ||
1026 | delete: 365..365, | ||
1027 | insert: "C2", | ||
1028 | kind: Const, | ||
1029 | detail: "const C2: () = ();", | ||
1030 | }, | ||
1031 | CompletionItem { | ||
1032 | label: "CONST", | ||
1033 | source_range: 365..365, | ||
1034 | delete: 365..365, | ||
1035 | insert: "CONST", | ||
1036 | kind: Const, | ||
1037 | detail: "const CONST: u8 = 0;", | ||
1038 | }, | ||
1039 | CompletionItem { | ||
1040 | label: "SubTy", | ||
1041 | source_range: 365..365, | ||
1042 | delete: 365..365, | ||
1043 | insert: "SubTy", | ||
1044 | kind: TypeAlias, | ||
1045 | detail: "type SubTy;", | ||
1046 | }, | ||
1047 | CompletionItem { | ||
1048 | label: "Ty", | ||
1049 | source_range: 365..365, | ||
1050 | delete: 365..365, | ||
1051 | insert: "Ty", | ||
1052 | kind: TypeAlias, | ||
1053 | detail: "type Ty;", | ||
1054 | }, | ||
1055 | CompletionItem { | ||
1056 | label: "func()", | ||
1057 | source_range: 365..365, | ||
1058 | delete: 365..365, | ||
1059 | insert: "func()$0", | ||
1060 | kind: Function, | ||
1061 | lookup: "func", | ||
1062 | detail: "fn func()", | ||
1063 | }, | ||
1064 | CompletionItem { | ||
1065 | label: "method()", | ||
1066 | source_range: 365..365, | ||
1067 | delete: 365..365, | ||
1068 | insert: "method()$0", | ||
1069 | kind: Method, | ||
1070 | lookup: "method", | ||
1071 | detail: "fn method(&self)", | ||
1072 | }, | ||
1073 | CompletionItem { | ||
1074 | label: "subfunc()", | ||
1075 | source_range: 365..365, | ||
1076 | delete: 365..365, | ||
1077 | insert: "subfunc()$0", | ||
1078 | kind: Function, | ||
1079 | lookup: "subfunc", | ||
1080 | detail: "fn subfunc()", | ||
1081 | }, | ||
1082 | CompletionItem { | ||
1083 | label: "submethod()", | ||
1084 | source_range: 365..365, | ||
1085 | delete: 365..365, | ||
1086 | insert: "submethod()$0", | ||
1087 | kind: Method, | ||
1088 | lookup: "submethod", | ||
1089 | detail: "fn submethod(&self)", | ||
1090 | }, | ||
1091 | ] | ||
1092 | "### | ||
1093 | ); | ||
1094 | } | ||
1095 | |||
1096 | #[test] | ||
847 | fn completes_type_alias() { | 1097 | fn completes_type_alias() { |
848 | assert_debug_snapshot!( | 1098 | assert_debug_snapshot!( |
849 | do_reference_completion( | 1099 | do_reference_completion( |
diff --git a/crates/ra_ide/src/completion/complete_unqualified_path.rs b/crates/ra_ide/src/completion/complete_unqualified_path.rs index f559f2b97..a6a5568de 100644 --- a/crates/ra_ide/src/completion/complete_unqualified_path.rs +++ b/crates/ra_ide/src/completion/complete_unqualified_path.rs | |||
@@ -53,7 +53,7 @@ fn complete_enum_variants(acc: &mut Completions, ctx: &CompletionContext, ty: &T | |||
53 | // Variants with trivial paths are already added by the existing completion logic, | 53 | // Variants with trivial paths are already added by the existing completion logic, |
54 | // so we should avoid adding these twice | 54 | // so we should avoid adding these twice |
55 | if path.segments.len() > 1 { | 55 | if path.segments.len() > 1 { |
56 | acc.add_enum_variant(ctx, variant, Some(path.to_string())); | 56 | acc.add_qualified_enum_variant(ctx, variant, path); |
57 | } | 57 | } |
58 | } | 58 | } |
59 | } | 59 | } |
@@ -1173,6 +1173,7 @@ mod tests { | |||
1173 | delete: 248..250, | 1173 | delete: 248..250, |
1174 | insert: "Foo::Bar", | 1174 | insert: "Foo::Bar", |
1175 | kind: EnumVariant, | 1175 | kind: EnumVariant, |
1176 | lookup: "Bar", | ||
1176 | detail: "()", | 1177 | detail: "()", |
1177 | }, | 1178 | }, |
1178 | CompletionItem { | 1179 | CompletionItem { |
@@ -1181,6 +1182,7 @@ mod tests { | |||
1181 | delete: 248..250, | 1182 | delete: 248..250, |
1182 | insert: "Foo::Baz", | 1183 | insert: "Foo::Baz", |
1183 | kind: EnumVariant, | 1184 | kind: EnumVariant, |
1185 | lookup: "Baz", | ||
1184 | detail: "()", | 1186 | detail: "()", |
1185 | }, | 1187 | }, |
1186 | CompletionItem { | 1188 | CompletionItem { |
@@ -1189,6 +1191,7 @@ mod tests { | |||
1189 | delete: 248..250, | 1191 | delete: 248..250, |
1190 | insert: "Foo::Quux", | 1192 | insert: "Foo::Quux", |
1191 | kind: EnumVariant, | 1193 | kind: EnumVariant, |
1194 | lookup: "Quux", | ||
1192 | detail: "()", | 1195 | detail: "()", |
1193 | }, | 1196 | }, |
1194 | ] | 1197 | ] |
@@ -1231,6 +1234,7 @@ mod tests { | |||
1231 | delete: 219..221, | 1234 | delete: 219..221, |
1232 | insert: "Foo::Bar", | 1235 | insert: "Foo::Bar", |
1233 | kind: EnumVariant, | 1236 | kind: EnumVariant, |
1237 | lookup: "Bar", | ||
1234 | detail: "()", | 1238 | detail: "()", |
1235 | }, | 1239 | }, |
1236 | CompletionItem { | 1240 | CompletionItem { |
@@ -1239,6 +1243,7 @@ mod tests { | |||
1239 | delete: 219..221, | 1243 | delete: 219..221, |
1240 | insert: "Foo::Baz", | 1244 | insert: "Foo::Baz", |
1241 | kind: EnumVariant, | 1245 | kind: EnumVariant, |
1246 | lookup: "Baz", | ||
1242 | detail: "()", | 1247 | detail: "()", |
1243 | }, | 1248 | }, |
1244 | CompletionItem { | 1249 | CompletionItem { |
@@ -1247,6 +1252,7 @@ mod tests { | |||
1247 | delete: 219..221, | 1252 | delete: 219..221, |
1248 | insert: "Foo::Quux", | 1253 | insert: "Foo::Quux", |
1249 | kind: EnumVariant, | 1254 | kind: EnumVariant, |
1255 | lookup: "Quux", | ||
1250 | detail: "()", | 1256 | detail: "()", |
1251 | }, | 1257 | }, |
1252 | ] | 1258 | ] |
@@ -1285,6 +1291,7 @@ mod tests { | |||
1285 | delete: 185..186, | 1291 | delete: 185..186, |
1286 | insert: "Foo::Bar", | 1292 | insert: "Foo::Bar", |
1287 | kind: EnumVariant, | 1293 | kind: EnumVariant, |
1294 | lookup: "Bar", | ||
1288 | detail: "()", | 1295 | detail: "()", |
1289 | }, | 1296 | }, |
1290 | CompletionItem { | 1297 | CompletionItem { |
@@ -1293,6 +1300,7 @@ mod tests { | |||
1293 | delete: 185..186, | 1300 | delete: 185..186, |
1294 | insert: "Foo::Baz", | 1301 | insert: "Foo::Baz", |
1295 | kind: EnumVariant, | 1302 | kind: EnumVariant, |
1303 | lookup: "Baz", | ||
1296 | detail: "()", | 1304 | detail: "()", |
1297 | }, | 1305 | }, |
1298 | CompletionItem { | 1306 | CompletionItem { |
@@ -1301,6 +1309,7 @@ mod tests { | |||
1301 | delete: 185..186, | 1309 | delete: 185..186, |
1302 | insert: "Foo::Quux", | 1310 | insert: "Foo::Quux", |
1303 | kind: EnumVariant, | 1311 | kind: EnumVariant, |
1312 | lookup: "Quux", | ||
1304 | detail: "()", | 1313 | detail: "()", |
1305 | }, | 1314 | }, |
1306 | CompletionItem { | 1315 | CompletionItem { |
@@ -1353,6 +1362,7 @@ mod tests { | |||
1353 | delete: 98..99, | 1362 | delete: 98..99, |
1354 | insert: "m::E::V", | 1363 | insert: "m::E::V", |
1355 | kind: EnumVariant, | 1364 | kind: EnumVariant, |
1365 | lookup: "V", | ||
1356 | detail: "()", | 1366 | detail: "()", |
1357 | }, | 1367 | }, |
1358 | ] | 1368 | ] |
diff --git a/crates/ra_ide/src/completion/presentation.rs b/crates/ra_ide/src/completion/presentation.rs index 77d354376..2edb130cf 100644 --- a/crates/ra_ide/src/completion/presentation.rs +++ b/crates/ra_ide/src/completion/presentation.rs | |||
@@ -1,6 +1,6 @@ | |||
1 | //! This modules takes care of rendering various definitions as completion items. | 1 | //! This modules takes care of rendering various definitions as completion items. |
2 | 2 | ||
3 | use hir::{Docs, HasAttrs, HasSource, HirDisplay, ScopeDef, StructKind, Type}; | 3 | use hir::{Docs, HasAttrs, HasSource, HirDisplay, ModPath, ScopeDef, StructKind, Type}; |
4 | use ra_syntax::ast::NameOwner; | 4 | use ra_syntax::ast::NameOwner; |
5 | use stdx::SepBy; | 5 | use stdx::SepBy; |
6 | use test_utils::tested_by; | 6 | use test_utils::tested_by; |
@@ -246,14 +246,37 @@ impl Completions { | |||
246 | .add_to(self); | 246 | .add_to(self); |
247 | } | 247 | } |
248 | 248 | ||
249 | pub(crate) fn add_qualified_enum_variant( | ||
250 | &mut self, | ||
251 | ctx: &CompletionContext, | ||
252 | variant: hir::EnumVariant, | ||
253 | path: ModPath, | ||
254 | ) { | ||
255 | self.add_enum_variant_impl(ctx, variant, None, Some(path)) | ||
256 | } | ||
257 | |||
249 | pub(crate) fn add_enum_variant( | 258 | pub(crate) fn add_enum_variant( |
250 | &mut self, | 259 | &mut self, |
251 | ctx: &CompletionContext, | 260 | ctx: &CompletionContext, |
252 | variant: hir::EnumVariant, | 261 | variant: hir::EnumVariant, |
253 | local_name: Option<String>, | 262 | local_name: Option<String>, |
254 | ) { | 263 | ) { |
264 | self.add_enum_variant_impl(ctx, variant, local_name, None) | ||
265 | } | ||
266 | |||
267 | fn add_enum_variant_impl( | ||
268 | &mut self, | ||
269 | ctx: &CompletionContext, | ||
270 | variant: hir::EnumVariant, | ||
271 | local_name: Option<String>, | ||
272 | path: Option<ModPath>, | ||
273 | ) { | ||
255 | let is_deprecated = is_deprecated(variant, ctx.db); | 274 | let is_deprecated = is_deprecated(variant, ctx.db); |
256 | let name = local_name.unwrap_or_else(|| variant.name(ctx.db).to_string()); | 275 | let name = local_name.unwrap_or_else(|| variant.name(ctx.db).to_string()); |
276 | let qualified_name = match &path { | ||
277 | Some(it) => it.to_string(), | ||
278 | None => name.to_string(), | ||
279 | }; | ||
257 | let detail_types = variant | 280 | let detail_types = variant |
258 | .fields(ctx.db) | 281 | .fields(ctx.db) |
259 | .into_iter() | 282 | .into_iter() |
@@ -271,16 +294,23 @@ impl Completions { | |||
271 | .surround_with("{ ", " }") | 294 | .surround_with("{ ", " }") |
272 | .to_string(), | 295 | .to_string(), |
273 | }; | 296 | }; |
274 | let mut res = | 297 | let mut res = CompletionItem::new( |
275 | CompletionItem::new(CompletionKind::Reference, ctx.source_range(), name.clone()) | 298 | CompletionKind::Reference, |
276 | .kind(CompletionItemKind::EnumVariant) | 299 | ctx.source_range(), |
277 | .set_documentation(variant.docs(ctx.db)) | 300 | qualified_name.clone(), |
278 | .set_deprecated(is_deprecated) | 301 | ) |
279 | .detail(detail); | 302 | .kind(CompletionItemKind::EnumVariant) |
303 | .set_documentation(variant.docs(ctx.db)) | ||
304 | .set_deprecated(is_deprecated) | ||
305 | .detail(detail); | ||
306 | |||
307 | if path.is_some() { | ||
308 | res = res.lookup_by(name); | ||
309 | } | ||
280 | 310 | ||
281 | if variant_kind == StructKind::Tuple { | 311 | if variant_kind == StructKind::Tuple { |
282 | let params = Params::Anonymous(variant.fields(ctx.db).len()); | 312 | let params = Params::Anonymous(variant.fields(ctx.db).len()); |
283 | res = res.add_call_parens(ctx, name, params) | 313 | res = res.add_call_parens(ctx, qualified_name, params) |
284 | } | 314 | } |
285 | 315 | ||
286 | res.add_to(self); | 316 | res.add_to(self); |
diff --git a/crates/ra_ide/src/display/function_signature.rs b/crates/ra_ide/src/display/function_signature.rs index b5e2785fe..db3907fe6 100644 --- a/crates/ra_ide/src/display/function_signature.rs +++ b/crates/ra_ide/src/display/function_signature.rs | |||
@@ -26,6 +26,8 @@ pub struct FunctionSignature { | |||
26 | pub kind: CallableKind, | 26 | pub kind: CallableKind, |
27 | /// Optional visibility | 27 | /// Optional visibility |
28 | pub visibility: Option<String>, | 28 | pub visibility: Option<String>, |
29 | /// Qualifiers like `async`, `unsafe`, ... | ||
30 | pub qualifier: FunctionQualifier, | ||
29 | /// Name of the function | 31 | /// Name of the function |
30 | pub name: Option<String>, | 32 | pub name: Option<String>, |
31 | /// Documentation for the function | 33 | /// Documentation for the function |
@@ -46,6 +48,16 @@ pub struct FunctionSignature { | |||
46 | pub has_self_param: bool, | 48 | pub has_self_param: bool, |
47 | } | 49 | } |
48 | 50 | ||
51 | #[derive(Debug, Default)] | ||
52 | pub struct FunctionQualifier { | ||
53 | // `async` and `const` are mutually exclusive. Do we need to enforcing it here? | ||
54 | pub is_async: bool, | ||
55 | pub is_const: bool, | ||
56 | pub is_unsafe: bool, | ||
57 | /// The string `extern ".."` | ||
58 | pub extern_abi: Option<String>, | ||
59 | } | ||
60 | |||
49 | impl FunctionSignature { | 61 | impl FunctionSignature { |
50 | pub(crate) fn with_doc_opt(mut self, doc: Option<Documentation>) -> Self { | 62 | pub(crate) fn with_doc_opt(mut self, doc: Option<Documentation>) -> Self { |
51 | self.doc = doc; | 63 | self.doc = doc; |
@@ -83,6 +95,8 @@ impl FunctionSignature { | |||
83 | FunctionSignature { | 95 | FunctionSignature { |
84 | kind: CallableKind::StructConstructor, | 96 | kind: CallableKind::StructConstructor, |
85 | visibility: node.visibility().map(|n| n.syntax().text().to_string()), | 97 | visibility: node.visibility().map(|n| n.syntax().text().to_string()), |
98 | // Do we need `const`? | ||
99 | qualifier: Default::default(), | ||
86 | name: node.name().map(|n| n.text().to_string()), | 100 | name: node.name().map(|n| n.text().to_string()), |
87 | ret_type: node.name().map(|n| n.text().to_string()), | 101 | ret_type: node.name().map(|n| n.text().to_string()), |
88 | parameters: params, | 102 | parameters: params, |
@@ -128,6 +142,8 @@ impl FunctionSignature { | |||
128 | FunctionSignature { | 142 | FunctionSignature { |
129 | kind: CallableKind::VariantConstructor, | 143 | kind: CallableKind::VariantConstructor, |
130 | visibility: None, | 144 | visibility: None, |
145 | // Do we need `const`? | ||
146 | qualifier: Default::default(), | ||
131 | name: Some(name), | 147 | name: Some(name), |
132 | ret_type: None, | 148 | ret_type: None, |
133 | parameters: params, | 149 | parameters: params, |
@@ -151,6 +167,7 @@ impl FunctionSignature { | |||
151 | FunctionSignature { | 167 | FunctionSignature { |
152 | kind: CallableKind::Macro, | 168 | kind: CallableKind::Macro, |
153 | visibility: None, | 169 | visibility: None, |
170 | qualifier: Default::default(), | ||
154 | name: node.name().map(|n| n.text().to_string()), | 171 | name: node.name().map(|n| n.text().to_string()), |
155 | ret_type: None, | 172 | ret_type: None, |
156 | parameters: params, | 173 | parameters: params, |
@@ -223,6 +240,12 @@ impl From<&'_ ast::FnDef> for FunctionSignature { | |||
223 | FunctionSignature { | 240 | FunctionSignature { |
224 | kind: CallableKind::Function, | 241 | kind: CallableKind::Function, |
225 | visibility: node.visibility().map(|n| n.syntax().text().to_string()), | 242 | visibility: node.visibility().map(|n| n.syntax().text().to_string()), |
243 | qualifier: FunctionQualifier { | ||
244 | is_async: node.async_token().is_some(), | ||
245 | is_const: node.const_token().is_some(), | ||
246 | is_unsafe: node.unsafe_token().is_some(), | ||
247 | extern_abi: node.abi().map(|n| n.to_string()), | ||
248 | }, | ||
226 | name: node.name().map(|n| n.text().to_string()), | 249 | name: node.name().map(|n| n.text().to_string()), |
227 | ret_type: node | 250 | ret_type: node |
228 | .ret_type() | 251 | .ret_type() |
@@ -246,6 +269,23 @@ impl Display for FunctionSignature { | |||
246 | write!(f, "{} ", t)?; | 269 | write!(f, "{} ", t)?; |
247 | } | 270 | } |
248 | 271 | ||
272 | if self.qualifier.is_async { | ||
273 | write!(f, "async ")?; | ||
274 | } | ||
275 | |||
276 | if self.qualifier.is_const { | ||
277 | write!(f, "const ")?; | ||
278 | } | ||
279 | |||
280 | if self.qualifier.is_unsafe { | ||
281 | write!(f, "unsafe ")?; | ||
282 | } | ||
283 | |||
284 | if let Some(extern_abi) = &self.qualifier.extern_abi { | ||
285 | // Keyword `extern` is included in the string. | ||
286 | write!(f, "{} ", extern_abi)?; | ||
287 | } | ||
288 | |||
249 | if let Some(name) = &self.name { | 289 | if let Some(name) = &self.name { |
250 | match self.kind { | 290 | match self.kind { |
251 | CallableKind::Function => write!(f, "fn {}", name)?, | 291 | CallableKind::Function => write!(f, "fn {}", name)?, |
diff --git a/crates/ra_ide/src/hover.rs b/crates/ra_ide/src/hover.rs index 58c799eca..a62f598f0 100644 --- a/crates/ra_ide/src/hover.rs +++ b/crates/ra_ide/src/hover.rs | |||
@@ -844,4 +844,29 @@ fn func(foo: i32) { if true { <|>foo; }; } | |||
844 | &["fn foo()\n```\n\n<- `\u{3000}` here"], | 844 | &["fn foo()\n```\n\n<- `\u{3000}` here"], |
845 | ); | 845 | ); |
846 | } | 846 | } |
847 | |||
848 | #[test] | ||
849 | fn test_hover_function_show_qualifiers() { | ||
850 | check_hover_result( | ||
851 | " | ||
852 | //- /lib.rs | ||
853 | async fn foo<|>() {} | ||
854 | ", | ||
855 | &["async fn foo()"], | ||
856 | ); | ||
857 | check_hover_result( | ||
858 | " | ||
859 | //- /lib.rs | ||
860 | pub const unsafe fn foo<|>() {} | ||
861 | ", | ||
862 | &["pub const unsafe fn foo()"], | ||
863 | ); | ||
864 | check_hover_result( | ||
865 | r#" | ||
866 | //- /lib.rs | ||
867 | pub(crate) async unsafe extern "C" fn foo<|>() {} | ||
868 | "#, | ||
869 | &[r#"pub(crate) async unsafe extern "C" fn foo()"#], | ||
870 | ); | ||
871 | } | ||
847 | } | 872 | } |
diff --git a/crates/ra_ide/src/snapshots/highlight_injection.html b/crates/ra_ide/src/snapshots/highlight_injection.html index 6ec13bd80..ea026d7a0 100644 --- a/crates/ra_ide/src/snapshots/highlight_injection.html +++ b/crates/ra_ide/src/snapshots/highlight_injection.html | |||
@@ -20,6 +20,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
20 | .macro { color: #94BFF3; } | 20 | .macro { color: #94BFF3; } |
21 | .module { color: #AFD8AF; } | 21 | .module { color: #AFD8AF; } |
22 | .variable { color: #DCDCCC; } | 22 | .variable { color: #DCDCCC; } |
23 | .format_specifier { color: #CC696B; } | ||
23 | .mutable { text-decoration: underline; } | 24 | .mutable { text-decoration: underline; } |
24 | 25 | ||
25 | .keyword { color: #F0DFAF; font-weight: bold; } | 26 | .keyword { color: #F0DFAF; font-weight: bold; } |
diff --git a/crates/ra_ide/src/snapshots/highlight_strings.html b/crates/ra_ide/src/snapshots/highlight_strings.html index 433f2e0c5..de06daf72 100644 --- a/crates/ra_ide/src/snapshots/highlight_strings.html +++ b/crates/ra_ide/src/snapshots/highlight_strings.html | |||
@@ -20,6 +20,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
20 | .macro { color: #94BFF3; } | 20 | .macro { color: #94BFF3; } |
21 | .module { color: #AFD8AF; } | 21 | .module { color: #AFD8AF; } |
22 | .variable { color: #DCDCCC; } | 22 | .variable { color: #DCDCCC; } |
23 | .format_specifier { color: #CC696B; } | ||
23 | .mutable { text-decoration: underline; } | 24 | .mutable { text-decoration: underline; } |
24 | 25 | ||
25 | .keyword { color: #F0DFAF; font-weight: bold; } | 26 | .keyword { color: #F0DFAF; font-weight: bold; } |
@@ -40,43 +41,43 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
40 | <span class="keyword">fn</span> <span class="function declaration">main</span>() { | 41 | <span class="keyword">fn</span> <span class="function declaration">main</span>() { |
41 | <span class="comment">// from https://doc.rust-lang.org/std/fmt/index.html</span> | 42 | <span class="comment">// from https://doc.rust-lang.org/std/fmt/index.html</span> |
42 | <span class="macro">println!</span>(<span class="string_literal">"Hello"</span>); <span class="comment">// => "Hello"</span> | 43 | <span class="macro">println!</span>(<span class="string_literal">"Hello"</span>); <span class="comment">// => "Hello"</span> |
43 | <span class="macro">println!</span>(<span class="string_literal">"Hello, </span><span class="attribute">{</span><span class="attribute">}</span><span class="string_literal">!"</span>, <span class="string_literal">"world"</span>); <span class="comment">// => "Hello, world!"</span> | 44 | <span class="macro">println!</span>(<span class="string_literal">"Hello, </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="string_literal">"world"</span>); <span class="comment">// => "Hello, world!"</span> |
44 | <span class="macro">println!</span>(<span class="string_literal">"The number is </span><span class="attribute">{</span><span class="attribute">}</span><span class="string_literal">"</span>, <span class="numeric_literal">1</span>); <span class="comment">// => "The number is 1"</span> | 45 | <span class="macro">println!</span>(<span class="string_literal">"The number is </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">"</span>, <span class="numeric_literal">1</span>); <span class="comment">// => "The number is 1"</span> |
45 | <span class="macro">println!</span>(<span class="string_literal">"</span><span class="attribute">{</span><span class="attribute">:</span><span class="attribute">?</span><span class="attribute">}</span><span class="string_literal">"</span>, (<span class="numeric_literal">3</span>, <span class="numeric_literal">4</span>)); <span class="comment">// => "(3, 4)"</span> | 46 | <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">?</span><span class="format_specifier">}</span><span class="string_literal">"</span>, (<span class="numeric_literal">3</span>, <span class="numeric_literal">4</span>)); <span class="comment">// => "(3, 4)"</span> |
46 | <span class="macro">println!</span>(<span class="string_literal">"</span><span class="attribute">{</span><span class="variable">value</span><span class="attribute">}</span><span class="string_literal">"</span>, value=<span class="numeric_literal">4</span>); <span class="comment">// => "4"</span> | 47 | <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">value</span><span class="format_specifier">}</span><span class="string_literal">"</span>, value=<span class="numeric_literal">4</span>); <span class="comment">// => "4"</span> |
47 | <span class="macro">println!</span>(<span class="string_literal">"</span><span class="attribute">{</span><span class="attribute">}</span><span class="string_literal"> </span><span class="attribute">{</span><span class="attribute">}</span><span class="string_literal">"</span>, <span class="numeric_literal">1</span>, <span class="numeric_literal">2</span>); <span class="comment">// => "1 2"</span> | 48 | <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">"</span>, <span class="numeric_literal">1</span>, <span class="numeric_literal">2</span>); <span class="comment">// => "1 2"</span> |
48 | <span class="macro">println!</span>(<span class="string_literal">"</span><span class="attribute">{</span><span class="attribute">:</span><span class="numeric_literal">0</span><span class="numeric_literal">4</span><span class="attribute">}</span><span class="string_literal">"</span>, <span class="numeric_literal">42</span>); <span class="comment">// => "0042" with leading zerosV</span> | 49 | <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="numeric_literal">4</span><span class="format_specifier">}</span><span class="string_literal">"</span>, <span class="numeric_literal">42</span>); <span class="comment">// => "0042" with leading zerosV</span> |
49 | <span class="macro">println!</span>(<span class="string_literal">"</span><span class="attribute">{</span><span class="numeric_literal">1</span><span class="attribute">}</span><span class="string_literal"> </span><span class="attribute">{</span><span class="attribute">}</span><span class="string_literal"> </span><span class="attribute">{</span><span class="numeric_literal">0</span><span class="attribute">}</span><span class="string_literal"> </span><span class="attribute">{</span><span class="attribute">}</span><span class="string_literal">"</span>, <span class="numeric_literal">1</span>, <span class="numeric_literal">2</span>); <span class="comment">// => "2 1 1 2"</span> | 50 | <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="numeric_literal">0</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">"</span>, <span class="numeric_literal">1</span>, <span class="numeric_literal">2</span>); <span class="comment">// => "2 1 1 2"</span> |
50 | <span class="macro">println!</span>(<span class="string_literal">"</span><span class="attribute">{</span><span class="variable">argument</span><span class="attribute">}</span><span class="string_literal">"</span>, argument = <span class="string_literal">"test"</span>); <span class="comment">// => "test"</span> | 51 | <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">argument</span><span class="format_specifier">}</span><span class="string_literal">"</span>, argument = <span class="string_literal">"test"</span>); <span class="comment">// => "test"</span> |
51 | <span class="macro">println!</span>(<span class="string_literal">"</span><span class="attribute">{</span><span class="variable">name</span><span class="attribute">}</span><span class="string_literal"> </span><span class="attribute">{</span><span class="attribute">}</span><span class="string_literal">"</span>, <span class="numeric_literal">1</span>, name = <span class="numeric_literal">2</span>); <span class="comment">// => "2 1"</span> | 52 | <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">"</span>, <span class="numeric_literal">1</span>, name = <span class="numeric_literal">2</span>); <span class="comment">// => "2 1"</span> |
52 | <span class="macro">println!</span>(<span class="string_literal">"</span><span class="attribute">{</span><span class="variable">a</span><span class="attribute">}</span><span class="string_literal"> </span><span class="attribute">{</span><span class="variable">c</span><span class="attribute">}</span><span class="string_literal"> </span><span class="attribute">{</span><span class="variable">b</span><span class="attribute">}</span><span class="string_literal">"</span>, a=<span class="string_literal">"a"</span>, b=<span class="char_literal">'b'</span>, c=<span class="numeric_literal">3</span>); <span class="comment">// => "a 3 b"</span> | 53 | <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">a</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="variable">c</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="variable">b</span><span class="format_specifier">}</span><span class="string_literal">"</span>, a=<span class="string_literal">"a"</span>, b=<span class="char_literal">'b'</span>, c=<span class="numeric_literal">3</span>); <span class="comment">// => "a 3 b"</span> |
53 | <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="attribute">{</span><span class="attribute">:</span><span class="numeric_literal">5</span><span class="attribute">}</span><span class="string_literal">!"</span>, <span class="string_literal">"x"</span>); | 54 | <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="string_literal">"x"</span>); |
54 | <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="attribute">{</span><span class="attribute">:</span><span class="numeric_literal">1</span><span class="attribute">$</span><span class="attribute">}</span><span class="string_literal">!"</span>, <span class="string_literal">"x"</span>, <span class="numeric_literal">5</span>); | 55 | <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">1</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="string_literal">"x"</span>, <span class="numeric_literal">5</span>); |
55 | <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="attribute">{</span><span class="numeric_literal">1</span><span class="attribute">:</span><span class="numeric_literal">0</span><span class="attribute">$</span><span class="attribute">}</span><span class="string_literal">!"</span>, <span class="numeric_literal">5</span>, <span class="string_literal">"x"</span>); | 56 | <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="numeric_literal">5</span>, <span class="string_literal">"x"</span>); |
56 | <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="attribute">{</span><span class="attribute">:</span><span class="variable">width</span><span class="attribute">$</span><span class="attribute">}</span><span class="string_literal">!"</span>, <span class="string_literal">"x"</span>, width = <span class="numeric_literal">5</span>); | 57 | <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="variable">width</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="string_literal">"x"</span>, width = <span class="numeric_literal">5</span>); |
57 | <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="attribute">{</span><span class="attribute">:</span><span class="attribute"><</span><span class="numeric_literal">5</span><span class="attribute">}</span><span class="string_literal">!"</span>, <span class="string_literal">"x"</span>); | 58 | <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier"><</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="string_literal">"x"</span>); |
58 | <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="attribute">{</span><span class="attribute">:</span><span class="attribute">-</span><span class="attribute"><</span><span class="numeric_literal">5</span><span class="attribute">}</span><span class="string_literal">!"</span>, <span class="string_literal">"x"</span>); | 59 | <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">-</span><span class="format_specifier"><</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="string_literal">"x"</span>); |
59 | <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="attribute">{</span><span class="attribute">:</span><span class="attribute">^</span><span class="numeric_literal">5</span><span class="attribute">}</span><span class="string_literal">!"</span>, <span class="string_literal">"x"</span>); | 60 | <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">^</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="string_literal">"x"</span>); |
60 | <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="attribute">{</span><span class="attribute">:</span><span class="attribute">></span><span class="numeric_literal">5</span><span class="attribute">}</span><span class="string_literal">!"</span>, <span class="string_literal">"x"</span>); | 61 | <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">></span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="string_literal">"x"</span>); |
61 | <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="attribute">{</span><span class="attribute">:</span><span class="attribute">+</span><span class="attribute">}</span><span class="string_literal">!"</span>, <span class="numeric_literal">5</span>); | 62 | <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">+</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="numeric_literal">5</span>); |
62 | <span class="macro">println!</span>(<span class="string_literal">"</span><span class="attribute">{</span><span class="attribute">:</span><span class="attribute">#</span><span class="variable">x</span><span class="string_literal">}!"</span>, <span class="numeric_literal">27</span>); | 63 | <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">#</span><span class="variable">x</span><span class="string_literal">}!"</span>, <span class="numeric_literal">27</span>); |
63 | <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="attribute">{</span><span class="attribute">:</span><span class="numeric_literal">0</span><span class="numeric_literal">5</span><span class="attribute">}</span><span class="string_literal">!"</span>, <span class="numeric_literal">5</span>); | 64 | <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="numeric_literal">5</span>); |
64 | <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="attribute">{</span><span class="attribute">:</span><span class="numeric_literal">0</span><span class="numeric_literal">5</span><span class="attribute">}</span><span class="string_literal">!"</span>, -<span class="numeric_literal">5</span>); | 65 | <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, -<span class="numeric_literal">5</span>); |
65 | <span class="macro">println!</span>(<span class="string_literal">"</span><span class="attribute">{</span><span class="attribute">:</span><span class="attribute">#</span><span class="numeric_literal">0</span><span class="numeric_literal">10</span><span class="variable">x</span><span class="attribute">}</span><span class="string_literal">!"</span>, <span class="numeric_literal">27</span>); | 66 | <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">#</span><span class="numeric_literal">0</span><span class="numeric_literal">10</span><span class="variable">x</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="numeric_literal">27</span>); |
66 | <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="attribute">{</span><span class="numeric_literal">0</span><span class="attribute">}</span><span class="string_literal"> is </span><span class="attribute">{</span><span class="numeric_literal">1</span><span class="attribute">:</span><span class="attribute">.</span><span class="numeric_literal">5</span><span class="attribute">}</span><span class="string_literal">"</span>, <span class="string_literal">"x"</span>, <span class="numeric_literal">0.01</span>); | 67 | <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">0</span><span class="format_specifier">}</span><span class="string_literal"> is </span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">"</span>, <span class="string_literal">"x"</span>, <span class="numeric_literal">0.01</span>); |
67 | <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="attribute">{</span><span class="numeric_literal">1</span><span class="attribute">}</span><span class="string_literal"> is </span><span class="attribute">{</span><span class="numeric_literal">2</span><span class="attribute">:</span><span class="attribute">.</span><span class="numeric_literal">0</span><span class="attribute">$</span><span class="attribute">}</span><span class="string_literal">"</span>, <span class="numeric_literal">5</span>, <span class="string_literal">"x"</span>, <span class="numeric_literal">0.01</span>); | 68 | <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">}</span><span class="string_literal"> is </span><span class="format_specifier">{</span><span class="numeric_literal">2</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="numeric_literal">0</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal">"</span>, <span class="numeric_literal">5</span>, <span class="string_literal">"x"</span>, <span class="numeric_literal">0.01</span>); |
68 | <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="attribute">{</span><span class="numeric_literal">0</span><span class="attribute">}</span><span class="string_literal"> is </span><span class="attribute">{</span><span class="numeric_literal">2</span><span class="attribute">:</span><span class="attribute">.</span><span class="numeric_literal">1</span><span class="attribute">$</span><span class="attribute">}</span><span class="string_literal">"</span>, <span class="string_literal">"x"</span>, <span class="numeric_literal">5</span>, <span class="numeric_literal">0.01</span>); | 69 | <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">0</span><span class="format_specifier">}</span><span class="string_literal"> is </span><span class="format_specifier">{</span><span class="numeric_literal">2</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="numeric_literal">1</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal">"</span>, <span class="string_literal">"x"</span>, <span class="numeric_literal">5</span>, <span class="numeric_literal">0.01</span>); |
69 | <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="attribute">{</span><span class="attribute">}</span><span class="string_literal"> is </span><span class="attribute">{</span><span class="attribute">:</span><span class="attribute">.</span><span class="attribute">*</span><span class="attribute">}</span><span class="string_literal">"</span>, <span class="string_literal">"x"</span>, <span class="numeric_literal">5</span>, <span class="numeric_literal">0.01</span>); | 70 | <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal"> is </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal">"</span>, <span class="string_literal">"x"</span>, <span class="numeric_literal">5</span>, <span class="numeric_literal">0.01</span>); |
70 | <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="attribute">{</span><span class="attribute">}</span><span class="string_literal"> is </span><span class="attribute">{</span><span class="numeric_literal">2</span><span class="attribute">:</span><span class="attribute">.</span><span class="attribute">*</span><span class="attribute">}</span><span class="string_literal">"</span>, <span class="string_literal">"x"</span>, <span class="numeric_literal">5</span>, <span class="numeric_literal">0.01</span>); | 71 | <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal"> is </span><span class="format_specifier">{</span><span class="numeric_literal">2</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal">"</span>, <span class="string_literal">"x"</span>, <span class="numeric_literal">5</span>, <span class="numeric_literal">0.01</span>); |
71 | <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="attribute">{</span><span class="attribute">}</span><span class="string_literal"> is </span><span class="attribute">{</span><span class="variable">number</span><span class="attribute">:</span><span class="attribute">.</span><span class="variable">prec</span><span class="attribute">$</span><span class="attribute">}</span><span class="string_literal">"</span>, <span class="string_literal">"x"</span>, prec = <span class="numeric_literal">5</span>, number = <span class="numeric_literal">0.01</span>); | 72 | <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal"> is </span><span class="format_specifier">{</span><span class="variable">number</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="variable">prec</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal">"</span>, <span class="string_literal">"x"</span>, prec = <span class="numeric_literal">5</span>, number = <span class="numeric_literal">0.01</span>); |
72 | <span class="macro">println!</span>(<span class="string_literal">"</span><span class="attribute">{</span><span class="attribute">}</span><span class="string_literal">, `</span><span class="attribute">{</span><span class="variable">name</span><span class="attribute">:</span><span class="attribute">.</span><span class="attribute">*</span><span class="attribute">}</span><span class="string_literal">` has 3 fractional digits"</span>, <span class="string_literal">"Hello"</span>, <span class="numeric_literal">3</span>, name=<span class="numeric_literal">1234.56</span>); | 73 | <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">, `</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal">` has 3 fractional digits"</span>, <span class="string_literal">"Hello"</span>, <span class="numeric_literal">3</span>, name=<span class="numeric_literal">1234.56</span>); |
73 | <span class="macro">println!</span>(<span class="string_literal">"</span><span class="attribute">{</span><span class="attribute">}</span><span class="string_literal">, `</span><span class="attribute">{</span><span class="variable">name</span><span class="attribute">:</span><span class="attribute">.</span><span class="attribute">*</span><span class="attribute">}</span><span class="string_literal">` has 3 characters"</span>, <span class="string_literal">"Hello"</span>, <span class="numeric_literal">3</span>, name=<span class="string_literal">"1234.56"</span>); | 74 | <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">, `</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal">` has 3 characters"</span>, <span class="string_literal">"Hello"</span>, <span class="numeric_literal">3</span>, name=<span class="string_literal">"1234.56"</span>); |
74 | <span class="macro">println!</span>(<span class="string_literal">"</span><span class="attribute">{</span><span class="attribute">}</span><span class="string_literal">, `</span><span class="attribute">{</span><span class="variable">name</span><span class="attribute">:</span><span class="attribute">></span><span class="numeric_literal">8</span><span class="attribute">.</span><span class="attribute">*</span><span class="attribute">}</span><span class="string_literal">` has 3 right-aligned characters"</span>, <span class="string_literal">"Hello"</span>, <span class="numeric_literal">3</span>, name=<span class="string_literal">"1234.56"</span>); | 75 | <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">, `</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">:</span><span class="format_specifier">></span><span class="numeric_literal">8</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal">` has 3 right-aligned characters"</span>, <span class="string_literal">"Hello"</span>, <span class="numeric_literal">3</span>, name=<span class="string_literal">"1234.56"</span>); |
75 | <span class="macro">println!</span>(<span class="string_literal">"Hello {{}}"</span>); | 76 | <span class="macro">println!</span>(<span class="string_literal">"Hello {{}}"</span>); |
76 | <span class="macro">println!</span>(<span class="string_literal">"{{ Hello"</span>); | 77 | <span class="macro">println!</span>(<span class="string_literal">"{{ Hello"</span>); |
77 | 78 | ||
78 | <span class="macro">println!</span>(<span class="string_literal">r"Hello, </span><span class="attribute">{</span><span class="attribute">}</span><span class="string_literal">!"</span>, <span class="string_literal">"world"</span>); | 79 | <span class="macro">println!</span>(<span class="string_literal">r"Hello, </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="string_literal">"world"</span>); |
79 | 80 | ||
80 | <span class="macro">println!</span>(<span class="string_literal">"</span><span class="attribute">{</span><span class="variable">\x41</span><span class="attribute">}</span><span class="string_literal">"</span>, A = <span class="numeric_literal">92</span>); | 81 | <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">\x41</span><span class="format_specifier">}</span><span class="string_literal">"</span>, A = <span class="numeric_literal">92</span>); |
81 | <span class="macro">println!</span>(<span class="string_literal">"</span><span class="attribute">{</span><span class="variable">ничоси</span><span class="attribute">}</span><span class="string_literal">"</span>, ничоси = <span class="numeric_literal">92</span>); | 82 | <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">ничоси</span><span class="format_specifier">}</span><span class="string_literal">"</span>, ничоси = <span class="numeric_literal">92</span>); |
82 | }</code></pre> \ No newline at end of file | 83 | }</code></pre> \ No newline at end of file |
diff --git a/crates/ra_ide/src/snapshots/highlighting.html b/crates/ra_ide/src/snapshots/highlighting.html index ccb1fc751..4b12fe823 100644 --- a/crates/ra_ide/src/snapshots/highlighting.html +++ b/crates/ra_ide/src/snapshots/highlighting.html | |||
@@ -20,6 +20,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
20 | .macro { color: #94BFF3; } | 20 | .macro { color: #94BFF3; } |
21 | .module { color: #AFD8AF; } | 21 | .module { color: #AFD8AF; } |
22 | .variable { color: #DCDCCC; } | 22 | .variable { color: #DCDCCC; } |
23 | .format_specifier { color: #CC696B; } | ||
23 | .mutable { text-decoration: underline; } | 24 | .mutable { text-decoration: underline; } |
24 | 25 | ||
25 | .keyword { color: #F0DFAF; font-weight: bold; } | 26 | .keyword { color: #F0DFAF; font-weight: bold; } |
diff --git a/crates/ra_ide/src/snapshots/rainbow_highlighting.html b/crates/ra_ide/src/snapshots/rainbow_highlighting.html index 3df82c45f..11e1f3e44 100644 --- a/crates/ra_ide/src/snapshots/rainbow_highlighting.html +++ b/crates/ra_ide/src/snapshots/rainbow_highlighting.html | |||
@@ -20,6 +20,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
20 | .macro { color: #94BFF3; } | 20 | .macro { color: #94BFF3; } |
21 | .module { color: #AFD8AF; } | 21 | .module { color: #AFD8AF; } |
22 | .variable { color: #DCDCCC; } | 22 | .variable { color: #DCDCCC; } |
23 | .format_specifier { color: #CC696B; } | ||
23 | .mutable { text-decoration: underline; } | 24 | .mutable { text-decoration: underline; } |
24 | 25 | ||
25 | .keyword { color: #F0DFAF; font-weight: bold; } | 26 | .keyword { color: #F0DFAF; font-weight: bold; } |
diff --git a/crates/ra_ide/src/syntax_highlighting.rs b/crates/ra_ide/src/syntax_highlighting.rs index be0f8c827..6658c7bb2 100644 --- a/crates/ra_ide/src/syntax_highlighting.rs +++ b/crates/ra_ide/src/syntax_highlighting.rs | |||
@@ -290,7 +290,7 @@ fn highlight_format_specifier(kind: FormatSpecifier) -> Option<HighlightTag> { | |||
290 | | FormatSpecifier::DollarSign | 290 | | FormatSpecifier::DollarSign |
291 | | FormatSpecifier::Dot | 291 | | FormatSpecifier::Dot |
292 | | FormatSpecifier::Asterisk | 292 | | FormatSpecifier::Asterisk |
293 | | FormatSpecifier::QuestionMark => HighlightTag::Attribute, | 293 | | FormatSpecifier::QuestionMark => HighlightTag::FormatSpecifier, |
294 | FormatSpecifier::Integer | FormatSpecifier::Zero => HighlightTag::NumericLiteral, | 294 | FormatSpecifier::Integer | FormatSpecifier::Zero => HighlightTag::NumericLiteral, |
295 | FormatSpecifier::Identifier => HighlightTag::Local, | 295 | FormatSpecifier::Identifier => HighlightTag::Local, |
296 | }) | 296 | }) |
diff --git a/crates/ra_ide/src/syntax_highlighting/html.rs b/crates/ra_ide/src/syntax_highlighting/html.rs index 010db4017..ff0eeeb52 100644 --- a/crates/ra_ide/src/syntax_highlighting/html.rs +++ b/crates/ra_ide/src/syntax_highlighting/html.rs | |||
@@ -79,6 +79,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
79 | .macro { color: #94BFF3; } | 79 | .macro { color: #94BFF3; } |
80 | .module { color: #AFD8AF; } | 80 | .module { color: #AFD8AF; } |
81 | .variable { color: #DCDCCC; } | 81 | .variable { color: #DCDCCC; } |
82 | .format_specifier { color: #CC696B; } | ||
82 | .mutable { text-decoration: underline; } | 83 | .mutable { text-decoration: underline; } |
83 | 84 | ||
84 | .keyword { color: #F0DFAF; font-weight: bold; } | 85 | .keyword { color: #F0DFAF; font-weight: bold; } |
diff --git a/crates/ra_ide/src/syntax_highlighting/tags.rs b/crates/ra_ide/src/syntax_highlighting/tags.rs index f2c421654..be1a0f12b 100644 --- a/crates/ra_ide/src/syntax_highlighting/tags.rs +++ b/crates/ra_ide/src/syntax_highlighting/tags.rs | |||
@@ -39,6 +39,7 @@ pub enum HighlightTag { | |||
39 | Union, | 39 | Union, |
40 | Local, | 40 | Local, |
41 | UnresolvedReference, | 41 | UnresolvedReference, |
42 | FormatSpecifier, | ||
42 | } | 43 | } |
43 | 44 | ||
44 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] | 45 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] |
@@ -81,6 +82,7 @@ impl HighlightTag { | |||
81 | HighlightTag::Union => "union", | 82 | HighlightTag::Union => "union", |
82 | HighlightTag::Local => "variable", | 83 | HighlightTag::Local => "variable", |
83 | HighlightTag::UnresolvedReference => "unresolved_reference", | 84 | HighlightTag::UnresolvedReference => "unresolved_reference", |
85 | HighlightTag::FormatSpecifier => "format_specifier", | ||
84 | } | 86 | } |
85 | } | 87 | } |
86 | } | 88 | } |
diff --git a/crates/ra_parser/src/grammar/expressions/atom.rs b/crates/ra_parser/src/grammar/expressions/atom.rs index 0d277a586..166dfc472 100644 --- a/crates/ra_parser/src/grammar/expressions/atom.rs +++ b/crates/ra_parser/src/grammar/expressions/atom.rs | |||
@@ -535,6 +535,22 @@ fn break_expr(p: &mut Parser, r: Restrictions) -> CompletedMarker { | |||
535 | fn try_block_expr(p: &mut Parser, m: Option<Marker>) -> CompletedMarker { | 535 | fn try_block_expr(p: &mut Parser, m: Option<Marker>) -> CompletedMarker { |
536 | assert!(p.at(T![try])); | 536 | assert!(p.at(T![try])); |
537 | let m = m.unwrap_or_else(|| p.start()); | 537 | let m = m.unwrap_or_else(|| p.start()); |
538 | // Special-case `try!` as macro. | ||
539 | // This is a hack until we do proper edition support | ||
540 | if p.nth_at(1, T![!]) { | ||
541 | // test try_macro_fallback | ||
542 | // fn foo() { try!(Ok(())); } | ||
543 | let path = p.start(); | ||
544 | let path_segment = p.start(); | ||
545 | let name_ref = p.start(); | ||
546 | p.bump_remap(IDENT); | ||
547 | name_ref.complete(p, NAME_REF); | ||
548 | path_segment.complete(p, PATH_SEGMENT); | ||
549 | path.complete(p, PATH); | ||
550 | let _block_like = items::macro_call_after_excl(p); | ||
551 | return m.complete(p, MACRO_CALL); | ||
552 | } | ||
553 | |||
538 | p.bump(T![try]); | 554 | p.bump(T![try]); |
539 | block(p); | 555 | block(p); |
540 | m.complete(p, TRY_EXPR) | 556 | m.complete(p, TRY_EXPR) |
diff --git a/crates/ra_proc_macro_srv/src/cli.rs b/crates/ra_proc_macro_srv/src/cli.rs index 7282e5b9b..1437794c9 100644 --- a/crates/ra_proc_macro_srv/src/cli.rs +++ b/crates/ra_proc_macro_srv/src/cli.rs | |||
@@ -1,15 +1,17 @@ | |||
1 | //! Driver for proc macro server | 1 | //! Driver for proc macro server |
2 | 2 | ||
3 | use crate::{expand_task, list_macros}; | 3 | use crate::ProcMacroSrv; |
4 | use ra_proc_macro::msg::{self, Message}; | 4 | use ra_proc_macro::msg::{self, Message}; |
5 | use std::io; | 5 | use std::io; |
6 | 6 | ||
7 | pub fn run() -> io::Result<()> { | 7 | pub fn run() -> io::Result<()> { |
8 | let mut srv = ProcMacroSrv::default(); | ||
9 | |||
8 | while let Some(req) = read_request()? { | 10 | while let Some(req) = read_request()? { |
9 | let res = match req { | 11 | let res = match req { |
10 | msg::Request::ListMacro(task) => Ok(msg::Response::ListMacro(list_macros(&task))), | 12 | msg::Request::ListMacro(task) => srv.list_macros(&task).map(msg::Response::ListMacro), |
11 | msg::Request::ExpansionMacro(task) => { | 13 | msg::Request::ExpansionMacro(task) => { |
12 | expand_task(&task).map(msg::Response::ExpansionMacro) | 14 | srv.expand(&task).map(msg::Response::ExpansionMacro) |
13 | } | 15 | } |
14 | }; | 16 | }; |
15 | 17 | ||
diff --git a/crates/ra_proc_macro_srv/src/dylib.rs b/crates/ra_proc_macro_srv/src/dylib.rs index d202eb0fd..aa84e951c 100644 --- a/crates/ra_proc_macro_srv/src/dylib.rs +++ b/crates/ra_proc_macro_srv/src/dylib.rs | |||
@@ -2,13 +2,12 @@ | |||
2 | 2 | ||
3 | use crate::{proc_macro::bridge, rustc_server::TokenStream}; | 3 | use crate::{proc_macro::bridge, rustc_server::TokenStream}; |
4 | use std::fs::File; | 4 | use std::fs::File; |
5 | use std::path::Path; | 5 | use std::path::{Path, PathBuf}; |
6 | 6 | ||
7 | use goblin::{mach::Mach, Object}; | 7 | use goblin::{mach::Mach, Object}; |
8 | use libloading::Library; | 8 | use libloading::Library; |
9 | use memmap::Mmap; | 9 | use memmap::Mmap; |
10 | use ra_proc_macro::ProcMacroKind; | 10 | use ra_proc_macro::ProcMacroKind; |
11 | |||
12 | use std::io; | 11 | use std::io; |
13 | 12 | ||
14 | const NEW_REGISTRAR_SYMBOL: &str = "_rustc_proc_macro_decls_"; | 13 | const NEW_REGISTRAR_SYMBOL: &str = "_rustc_proc_macro_decls_"; |
@@ -109,23 +108,21 @@ impl ProcMacroLibraryLibloading { | |||
109 | } | 108 | } |
110 | } | 109 | } |
111 | 110 | ||
112 | type ProcMacroLibraryImpl = ProcMacroLibraryLibloading; | ||
113 | |||
114 | pub struct Expander { | 111 | pub struct Expander { |
115 | libs: Vec<ProcMacroLibraryImpl>, | 112 | inner: ProcMacroLibraryLibloading, |
116 | } | 113 | } |
117 | 114 | ||
118 | impl Expander { | 115 | impl Expander { |
119 | pub fn new(lib: &Path) -> Result<Expander, String> { | 116 | pub fn new(lib: &Path) -> io::Result<Expander> { |
120 | // Some libraries for dynamic loading require canonicalized path even when it is | 117 | // Some libraries for dynamic loading require canonicalized path even when it is |
121 | // already absolute | 118 | // already absolute |
122 | let lib = lib | 119 | let lib = lib.canonicalize()?; |
123 | .canonicalize() | 120 | |
124 | .unwrap_or_else(|err| panic!("Cannot canonicalize {}: {:?}", lib.display(), err)); | 121 | let lib = ensure_file_with_lock_free_access(&lib)?; |
125 | 122 | ||
126 | let library = ProcMacroLibraryImpl::open(&lib).map_err(|e| e.to_string())?; | 123 | let library = ProcMacroLibraryLibloading::open(&lib)?; |
127 | 124 | ||
128 | Ok(Expander { libs: vec![library] }) | 125 | Ok(Expander { inner: library }) |
129 | } | 126 | } |
130 | 127 | ||
131 | pub fn expand( | 128 | pub fn expand( |
@@ -141,38 +138,36 @@ impl Expander { | |||
141 | TokenStream::with_subtree(attr.clone()) | 138 | TokenStream::with_subtree(attr.clone()) |
142 | }); | 139 | }); |
143 | 140 | ||
144 | for lib in &self.libs { | 141 | for proc_macro in &self.inner.exported_macros { |
145 | for proc_macro in &lib.exported_macros { | 142 | match proc_macro { |
146 | match proc_macro { | 143 | bridge::client::ProcMacro::CustomDerive { trait_name, client, .. } |
147 | bridge::client::ProcMacro::CustomDerive { trait_name, client, .. } | 144 | if *trait_name == macro_name => |
148 | if *trait_name == macro_name => | 145 | { |
149 | { | 146 | let res = client.run( |
150 | let res = client.run( | 147 | &crate::proc_macro::bridge::server::SameThread, |
151 | &crate::proc_macro::bridge::server::SameThread, | 148 | crate::rustc_server::Rustc::default(), |
152 | crate::rustc_server::Rustc::default(), | 149 | parsed_body, |
153 | parsed_body, | 150 | ); |
154 | ); | 151 | return res.map(|it| it.subtree); |
155 | return res.map(|it| it.subtree); | 152 | } |
156 | } | 153 | bridge::client::ProcMacro::Bang { name, client } if *name == macro_name => { |
157 | bridge::client::ProcMacro::Bang { name, client } if *name == macro_name => { | 154 | let res = client.run( |
158 | let res = client.run( | 155 | &crate::proc_macro::bridge::server::SameThread, |
159 | &crate::proc_macro::bridge::server::SameThread, | 156 | crate::rustc_server::Rustc::default(), |
160 | crate::rustc_server::Rustc::default(), | 157 | parsed_body, |
161 | parsed_body, | 158 | ); |
162 | ); | 159 | return res.map(|it| it.subtree); |
163 | return res.map(|it| it.subtree); | 160 | } |
164 | } | 161 | bridge::client::ProcMacro::Attr { name, client } if *name == macro_name => { |
165 | bridge::client::ProcMacro::Attr { name, client } if *name == macro_name => { | 162 | let res = client.run( |
166 | let res = client.run( | 163 | &crate::proc_macro::bridge::server::SameThread, |
167 | &crate::proc_macro::bridge::server::SameThread, | 164 | crate::rustc_server::Rustc::default(), |
168 | crate::rustc_server::Rustc::default(), | 165 | parsed_attributes, |
169 | parsed_attributes, | 166 | parsed_body, |
170 | parsed_body, | 167 | ); |
171 | ); | 168 | return res.map(|it| it.subtree); |
172 | return res.map(|it| it.subtree); | ||
173 | } | ||
174 | _ => continue, | ||
175 | } | 169 | } |
170 | _ => continue, | ||
176 | } | 171 | } |
177 | } | 172 | } |
178 | 173 | ||
@@ -180,9 +175,9 @@ impl Expander { | |||
180 | } | 175 | } |
181 | 176 | ||
182 | pub fn list_macros(&self) -> Vec<(String, ProcMacroKind)> { | 177 | pub fn list_macros(&self) -> Vec<(String, ProcMacroKind)> { |
183 | self.libs | 178 | self.inner |
179 | .exported_macros | ||
184 | .iter() | 180 | .iter() |
185 | .flat_map(|it| &it.exported_macros) | ||
186 | .map(|proc_macro| match proc_macro { | 181 | .map(|proc_macro| match proc_macro { |
187 | bridge::client::ProcMacro::CustomDerive { trait_name, .. } => { | 182 | bridge::client::ProcMacro::CustomDerive { trait_name, .. } => { |
188 | (trait_name.to_string(), ProcMacroKind::CustomDerive) | 183 | (trait_name.to_string(), ProcMacroKind::CustomDerive) |
@@ -197,3 +192,33 @@ impl Expander { | |||
197 | .collect() | 192 | .collect() |
198 | } | 193 | } |
199 | } | 194 | } |
195 | |||
196 | /// Copy the dylib to temp directory to prevent locking in Windows | ||
197 | #[cfg(windows)] | ||
198 | fn ensure_file_with_lock_free_access(path: &Path) -> io::Result<PathBuf> { | ||
199 | use std::{ffi::OsString, time::SystemTime}; | ||
200 | |||
201 | let mut to = std::env::temp_dir(); | ||
202 | |||
203 | let file_name = path.file_name().ok_or_else(|| { | ||
204 | io::Error::new( | ||
205 | io::ErrorKind::InvalidInput, | ||
206 | format!("File path is invalid: {}", path.display()), | ||
207 | ) | ||
208 | })?; | ||
209 | |||
210 | // generate a time deps unique number | ||
211 | let t = SystemTime::now().duration_since(std::time::UNIX_EPOCH).expect("Time went backwards"); | ||
212 | |||
213 | let mut unique_name = OsString::from(t.as_millis().to_string()); | ||
214 | unique_name.push(file_name); | ||
215 | |||
216 | to.push(unique_name); | ||
217 | std::fs::copy(path, &to).unwrap(); | ||
218 | Ok(to) | ||
219 | } | ||
220 | |||
221 | #[cfg(unix)] | ||
222 | fn ensure_file_with_lock_free_access(path: &Path) -> io::Result<PathBuf> { | ||
223 | Ok(path.to_path_buf()) | ||
224 | } | ||
diff --git a/crates/ra_proc_macro_srv/src/lib.rs b/crates/ra_proc_macro_srv/src/lib.rs index 3aca859db..922bb84bb 100644 --- a/crates/ra_proc_macro_srv/src/lib.rs +++ b/crates/ra_proc_macro_srv/src/lib.rs | |||
@@ -21,28 +21,46 @@ mod dylib; | |||
21 | 21 | ||
22 | use proc_macro::bridge::client::TokenStream; | 22 | use proc_macro::bridge::client::TokenStream; |
23 | use ra_proc_macro::{ExpansionResult, ExpansionTask, ListMacrosResult, ListMacrosTask}; | 23 | use ra_proc_macro::{ExpansionResult, ExpansionTask, ListMacrosResult, ListMacrosTask}; |
24 | use std::path::Path; | 24 | use std::{ |
25 | collections::{hash_map::Entry, HashMap}, | ||
26 | fs, | ||
27 | path::{Path, PathBuf}, | ||
28 | time::SystemTime, | ||
29 | }; | ||
25 | 30 | ||
26 | pub(crate) fn expand_task(task: &ExpansionTask) -> Result<ExpansionResult, String> { | 31 | #[derive(Default)] |
27 | let expander = create_expander(&task.lib); | 32 | pub(crate) struct ProcMacroSrv { |
33 | expanders: HashMap<(PathBuf, SystemTime), dylib::Expander>, | ||
34 | } | ||
28 | 35 | ||
29 | match expander.expand(&task.macro_name, &task.macro_body, task.attributes.as_ref()) { | 36 | impl ProcMacroSrv { |
30 | Ok(expansion) => Ok(ExpansionResult { expansion }), | 37 | pub fn expand(&mut self, task: &ExpansionTask) -> Result<ExpansionResult, String> { |
31 | Err(msg) => { | 38 | let expander = self.expander(&task.lib)?; |
32 | Err(format!("Cannot perform expansion for {}: error {:?}", &task.macro_name, msg)) | 39 | match expander.expand(&task.macro_name, &task.macro_body, task.attributes.as_ref()) { |
40 | Ok(expansion) => Ok(ExpansionResult { expansion }), | ||
41 | Err(msg) => { | ||
42 | Err(format!("Cannot perform expansion for {}: error {:?}", &task.macro_name, msg)) | ||
43 | } | ||
33 | } | 44 | } |
34 | } | 45 | } |
35 | } | ||
36 | 46 | ||
37 | pub(crate) fn list_macros(task: &ListMacrosTask) -> ListMacrosResult { | 47 | pub fn list_macros(&mut self, task: &ListMacrosTask) -> Result<ListMacrosResult, String> { |
38 | let expander = create_expander(&task.lib); | 48 | let expander = self.expander(&task.lib)?; |
49 | Ok(ListMacrosResult { macros: expander.list_macros() }) | ||
50 | } | ||
39 | 51 | ||
40 | ListMacrosResult { macros: expander.list_macros() } | 52 | fn expander(&mut self, path: &Path) -> Result<&dylib::Expander, String> { |
41 | } | 53 | let time = fs::metadata(path).and_then(|it| it.modified()).map_err(|err| { |
54 | format!("Failed to get file metadata for {}: {:?}", path.display(), err) | ||
55 | })?; | ||
42 | 56 | ||
43 | fn create_expander(lib: &Path) -> dylib::Expander { | 57 | Ok(match self.expanders.entry((path.to_path_buf(), time)) { |
44 | dylib::Expander::new(lib) | 58 | Entry::Vacant(v) => v.insert(dylib::Expander::new(path).map_err(|err| { |
45 | .unwrap_or_else(|err| panic!("Cannot create expander for {}: {:?}", lib.display(), err)) | 59 | format!("Cannot create expander for {}: {:?}", path.display(), err) |
60 | })?), | ||
61 | Entry::Occupied(e) => e.into_mut(), | ||
62 | }) | ||
63 | } | ||
46 | } | 64 | } |
47 | 65 | ||
48 | pub mod cli; | 66 | pub mod cli; |
diff --git a/crates/ra_proc_macro_srv/src/tests/utils.rs b/crates/ra_proc_macro_srv/src/tests/utils.rs index 2139ec7a4..646a427c5 100644 --- a/crates/ra_proc_macro_srv/src/tests/utils.rs +++ b/crates/ra_proc_macro_srv/src/tests/utils.rs | |||
@@ -1,7 +1,7 @@ | |||
1 | //! utils used in proc-macro tests | 1 | //! utils used in proc-macro tests |
2 | 2 | ||
3 | use crate::dylib; | 3 | use crate::dylib; |
4 | use crate::list_macros; | 4 | use crate::ProcMacroSrv; |
5 | pub use difference::Changeset as __Changeset; | 5 | pub use difference::Changeset as __Changeset; |
6 | use ra_proc_macro::ListMacrosTask; | 6 | use ra_proc_macro::ListMacrosTask; |
7 | use std::str::FromStr; | 7 | use std::str::FromStr; |
@@ -59,7 +59,7 @@ pub fn assert_expand( | |||
59 | pub fn list(crate_name: &str, version: &str) -> Vec<String> { | 59 | pub fn list(crate_name: &str, version: &str) -> Vec<String> { |
60 | let path = fixtures::dylib_path(crate_name, version); | 60 | let path = fixtures::dylib_path(crate_name, version); |
61 | let task = ListMacrosTask { lib: path }; | 61 | let task = ListMacrosTask { lib: path }; |
62 | 62 | let mut srv = ProcMacroSrv::default(); | |
63 | let res = list_macros(&task); | 63 | let res = srv.list_macros(&task).unwrap(); |
64 | res.macros.into_iter().map(|(name, kind)| format!("{} [{:?}]", name, kind)).collect() | 64 | res.macros.into_iter().map(|(name, kind)| format!("{} [{:?}]", name, kind)).collect() |
65 | } | 65 | } |
diff --git a/crates/ra_prof/src/hprof.rs b/crates/ra_prof/src/hprof.rs index 2b8a90363..a3f5321fb 100644 --- a/crates/ra_prof/src/hprof.rs +++ b/crates/ra_prof/src/hprof.rs | |||
@@ -30,8 +30,9 @@ pub fn init_from(spec: &str) { | |||
30 | pub type Label = &'static str; | 30 | pub type Label = &'static str; |
31 | 31 | ||
32 | /// This function starts a profiling scope in the current execution stack with a given description. | 32 | /// This function starts a profiling scope in the current execution stack with a given description. |
33 | /// It returns a Profile structure and measure elapsed time between this method invocation and Profile structure drop. | 33 | /// It returns a `Profile` struct that measures elapsed time between this method invocation and `Profile` struct drop. |
34 | /// It supports nested profiling scopes in case when this function invoked multiple times at the execution stack. In this case the profiling information will be nested at the output. | 34 | /// It supports nested profiling scopes in case when this function is invoked multiple times at the execution stack. |
35 | /// In this case the profiling information will be nested at the output. | ||
35 | /// Profiling information is being printed in the stderr. | 36 | /// Profiling information is being printed in the stderr. |
36 | /// | 37 | /// |
37 | /// # Example | 38 | /// # Example |
@@ -58,36 +59,35 @@ pub type Label = &'static str; | |||
58 | /// ``` | 59 | /// ``` |
59 | pub fn profile(label: Label) -> Profiler { | 60 | pub fn profile(label: Label) -> Profiler { |
60 | assert!(!label.is_empty()); | 61 | assert!(!label.is_empty()); |
61 | let enabled = PROFILING_ENABLED.load(Ordering::Relaxed) | 62 | |
62 | && PROFILE_STACK.with(|stack| stack.borrow_mut().push(label)); | 63 | if PROFILING_ENABLED.load(Ordering::Relaxed) |
63 | let label = if enabled { Some(label) } else { None }; | 64 | && PROFILE_STACK.with(|stack| stack.borrow_mut().push(label)) |
64 | Profiler { label, detail: None } | 65 | { |
66 | Profiler(Some(ProfilerImpl { label, detail: None })) | ||
67 | } else { | ||
68 | Profiler(None) | ||
69 | } | ||
65 | } | 70 | } |
66 | 71 | ||
67 | pub struct Profiler { | 72 | pub struct Profiler(Option<ProfilerImpl>); |
68 | label: Option<Label>, | 73 | |
74 | struct ProfilerImpl { | ||
75 | label: Label, | ||
69 | detail: Option<String>, | 76 | detail: Option<String>, |
70 | } | 77 | } |
71 | 78 | ||
72 | impl Profiler { | 79 | impl Profiler { |
73 | pub fn detail(mut self, detail: impl FnOnce() -> String) -> Profiler { | 80 | pub fn detail(mut self, detail: impl FnOnce() -> String) -> Profiler { |
74 | if self.label.is_some() { | 81 | if let Some(profiler) = &mut self.0 { |
75 | self.detail = Some(detail()) | 82 | profiler.detail = Some(detail()) |
76 | } | 83 | } |
77 | self | 84 | self |
78 | } | 85 | } |
79 | } | 86 | } |
80 | 87 | ||
81 | impl Drop for Profiler { | 88 | impl Drop for ProfilerImpl { |
82 | fn drop(&mut self) { | 89 | fn drop(&mut self) { |
83 | match self { | 90 | PROFILE_STACK.with(|it| it.borrow_mut().pop(self.label, self.detail.take())); |
84 | Profiler { label: Some(label), detail } => { | ||
85 | PROFILE_STACK.with(|stack| { | ||
86 | stack.borrow_mut().pop(label, detail.take()); | ||
87 | }); | ||
88 | } | ||
89 | Profiler { label: None, .. } => (), | ||
90 | } | ||
91 | } | 91 | } |
92 | } | 92 | } |
93 | 93 | ||
@@ -179,21 +179,18 @@ impl ProfileStack { | |||
179 | pub fn pop(&mut self, label: Label, detail: Option<String>) { | 179 | pub fn pop(&mut self, label: Label, detail: Option<String>) { |
180 | let start = self.starts.pop().unwrap(); | 180 | let start = self.starts.pop().unwrap(); |
181 | let duration = start.elapsed(); | 181 | let duration = start.elapsed(); |
182 | let level = self.starts.len(); | ||
183 | self.messages.finish(Message { duration, label, detail }); | 182 | self.messages.finish(Message { duration, label, detail }); |
184 | if level == 0 { | 183 | if self.starts.is_empty() { |
185 | let longer_than = self.filter.longer_than; | 184 | let longer_than = self.filter.longer_than; |
186 | // Convert to millis for comparison to avoid problems with rounding | 185 | // Convert to millis for comparison to avoid problems with rounding |
187 | // (otherwise we could print `0ms` despite user's `>0` filter when | 186 | // (otherwise we could print `0ms` despite user's `>0` filter when |
188 | // `duration` is just a few nanos). | 187 | // `duration` is just a few nanos). |
189 | if duration.as_millis() > longer_than.as_millis() { | 188 | if duration.as_millis() > longer_than.as_millis() { |
190 | let stderr = stderr(); | ||
191 | if let Some(root) = self.messages.root() { | 189 | if let Some(root) = self.messages.root() { |
192 | print(&self.messages, root, 0, longer_than, &mut stderr.lock()); | 190 | print(&self.messages, root, 0, longer_than, &mut stderr().lock()); |
193 | } | 191 | } |
194 | } | 192 | } |
195 | self.messages.clear(); | 193 | self.messages.clear(); |
196 | assert!(self.starts.is_empty()) | ||
197 | } | 194 | } |
198 | } | 195 | } |
199 | } | 196 | } |
diff --git a/crates/ra_syntax/src/ast.rs b/crates/ra_syntax/src/ast.rs index 7fca5661e..a716e525b 100644 --- a/crates/ra_syntax/src/ast.rs +++ b/crates/ra_syntax/src/ast.rs | |||
@@ -243,6 +243,21 @@ fn test_comments_preserve_trailing_whitespace() { | |||
243 | } | 243 | } |
244 | 244 | ||
245 | #[test] | 245 | #[test] |
246 | fn test_four_slash_line_comment() { | ||
247 | let file = SourceFile::parse( | ||
248 | r#" | ||
249 | //// too many slashes to be a doc comment | ||
250 | /// doc comment | ||
251 | mod foo {} | ||
252 | "#, | ||
253 | ) | ||
254 | .ok() | ||
255 | .unwrap(); | ||
256 | let module = file.syntax().descendants().find_map(Module::cast).unwrap(); | ||
257 | assert_eq!("doc comment", module.doc_comment_text().unwrap()); | ||
258 | } | ||
259 | |||
260 | #[test] | ||
246 | fn test_where_predicates() { | 261 | fn test_where_predicates() { |
247 | fn assert_bound(text: &str, bound: Option<TypeBound>) { | 262 | fn assert_bound(text: &str, bound: Option<TypeBound>) { |
248 | assert_eq!(text, bound.unwrap().syntax().text().to_string()); | 263 | assert_eq!(text, bound.unwrap().syntax().text().to_string()); |
diff --git a/crates/ra_syntax/src/ast/generated/nodes.rs b/crates/ra_syntax/src/ast/generated/nodes.rs index 2cb3ad011..3b5e05af9 100644 --- a/crates/ra_syntax/src/ast/generated/nodes.rs +++ b/crates/ra_syntax/src/ast/generated/nodes.rs | |||
@@ -1249,6 +1249,7 @@ pub struct PathSegment { | |||
1249 | } | 1249 | } |
1250 | impl PathSegment { | 1250 | impl PathSegment { |
1251 | pub fn coloncolon_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![::]) } | 1251 | pub fn coloncolon_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![::]) } |
1252 | pub fn crate_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![crate]) } | ||
1252 | pub fn l_angle_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![<]) } | 1253 | pub fn l_angle_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![<]) } |
1253 | pub fn name_ref(&self) -> Option<NameRef> { support::child(&self.syntax) } | 1254 | pub fn name_ref(&self) -> Option<NameRef> { support::child(&self.syntax) } |
1254 | pub fn type_arg_list(&self) -> Option<TypeArgList> { support::child(&self.syntax) } | 1255 | pub fn type_arg_list(&self) -> Option<TypeArgList> { support::child(&self.syntax) } |
diff --git a/crates/ra_syntax/src/ast/make.rs b/crates/ra_syntax/src/ast/make.rs index ee0f5cc40..492088353 100644 --- a/crates/ra_syntax/src/ast/make.rs +++ b/crates/ra_syntax/src/ast/make.rs | |||
@@ -22,8 +22,7 @@ pub fn path_unqualified(segment: ast::PathSegment) -> ast::Path { | |||
22 | pub fn path_qualified(qual: ast::Path, segment: ast::PathSegment) -> ast::Path { | 22 | pub fn path_qualified(qual: ast::Path, segment: ast::PathSegment) -> ast::Path { |
23 | path_from_text(&format!("{}::{}", qual, segment)) | 23 | path_from_text(&format!("{}::{}", qual, segment)) |
24 | } | 24 | } |
25 | 25 | fn path_from_text(text: &str) -> ast::Path { | |
26 | pub fn path_from_text(text: &str) -> ast::Path { | ||
27 | ast_from_text(text) | 26 | ast_from_text(text) |
28 | } | 27 | } |
29 | 28 | ||
diff --git a/crates/ra_syntax/src/ast/tokens.rs b/crates/ra_syntax/src/ast/tokens.rs index 3865729b8..74906d8a6 100644 --- a/crates/ra_syntax/src/ast/tokens.rs +++ b/crates/ra_syntax/src/ast/tokens.rs | |||
@@ -13,7 +13,12 @@ impl Comment { | |||
13 | } | 13 | } |
14 | 14 | ||
15 | pub fn prefix(&self) -> &'static str { | 15 | pub fn prefix(&self) -> &'static str { |
16 | prefix_by_kind(self.kind()) | 16 | for (prefix, k) in COMMENT_PREFIX_TO_KIND.iter() { |
17 | if *k == self.kind() && self.text().starts_with(prefix) { | ||
18 | return prefix; | ||
19 | } | ||
20 | } | ||
21 | unreachable!() | ||
17 | } | 22 | } |
18 | } | 23 | } |
19 | 24 | ||
@@ -48,6 +53,7 @@ pub enum CommentPlacement { | |||
48 | const COMMENT_PREFIX_TO_KIND: &[(&str, CommentKind)] = { | 53 | const COMMENT_PREFIX_TO_KIND: &[(&str, CommentKind)] = { |
49 | use {CommentPlacement::*, CommentShape::*}; | 54 | use {CommentPlacement::*, CommentShape::*}; |
50 | &[ | 55 | &[ |
56 | ("////", CommentKind { shape: Line, doc: None }), | ||
51 | ("///", CommentKind { shape: Line, doc: Some(Outer) }), | 57 | ("///", CommentKind { shape: Line, doc: Some(Outer) }), |
52 | ("//!", CommentKind { shape: Line, doc: Some(Inner) }), | 58 | ("//!", CommentKind { shape: Line, doc: Some(Inner) }), |
53 | ("/**", CommentKind { shape: Block, doc: Some(Outer) }), | 59 | ("/**", CommentKind { shape: Block, doc: Some(Outer) }), |
@@ -69,15 +75,6 @@ fn kind_by_prefix(text: &str) -> CommentKind { | |||
69 | panic!("bad comment text: {:?}", text) | 75 | panic!("bad comment text: {:?}", text) |
70 | } | 76 | } |
71 | 77 | ||
72 | fn prefix_by_kind(kind: CommentKind) -> &'static str { | ||
73 | for (prefix, k) in COMMENT_PREFIX_TO_KIND.iter() { | ||
74 | if *k == kind { | ||
75 | return prefix; | ||
76 | } | ||
77 | } | ||
78 | unreachable!() | ||
79 | } | ||
80 | |||
81 | impl Whitespace { | 78 | impl Whitespace { |
82 | pub fn spans_multiple_lines(&self) -> bool { | 79 | pub fn spans_multiple_lines(&self) -> bool { |
83 | let text = self.text(); | 80 | let text = self.text(); |
diff --git a/crates/ra_syntax/src/validation.rs b/crates/ra_syntax/src/validation.rs index 5e93895ec..a30bc97bb 100644 --- a/crates/ra_syntax/src/validation.rs +++ b/crates/ra_syntax/src/validation.rs | |||
@@ -96,6 +96,7 @@ pub(crate) fn validate(root: &SyntaxNode) -> Vec<SyntaxError> { | |||
96 | ast::RecordField(it) => validate_numeric_name(it.name_ref(), &mut errors), | 96 | ast::RecordField(it) => validate_numeric_name(it.name_ref(), &mut errors), |
97 | ast::Visibility(it) => validate_visibility(it, &mut errors), | 97 | ast::Visibility(it) => validate_visibility(it, &mut errors), |
98 | ast::RangeExpr(it) => validate_range_expr(it, &mut errors), | 98 | ast::RangeExpr(it) => validate_range_expr(it, &mut errors), |
99 | ast::PathSegment(it) => validate_crate_keyword_in_path_segment(it, &mut errors), | ||
99 | _ => (), | 100 | _ => (), |
100 | } | 101 | } |
101 | } | 102 | } |
@@ -222,3 +223,41 @@ fn validate_range_expr(expr: ast::RangeExpr, errors: &mut Vec<SyntaxError>) { | |||
222 | )); | 223 | )); |
223 | } | 224 | } |
224 | } | 225 | } |
226 | |||
227 | fn validate_crate_keyword_in_path_segment( | ||
228 | segment: ast::PathSegment, | ||
229 | errors: &mut Vec<SyntaxError>, | ||
230 | ) { | ||
231 | const ERR_MSG: &str = "The `crate` keyword is only allowed as the first segment of a path"; | ||
232 | |||
233 | let crate_token = match segment.crate_token() { | ||
234 | None => return, | ||
235 | Some(it) => it, | ||
236 | }; | ||
237 | |||
238 | // Disallow both ::crate and foo::crate | ||
239 | let path = segment.parent_path(); | ||
240 | if segment.coloncolon_token().is_some() || path.qualifier().is_some() { | ||
241 | errors.push(SyntaxError::new(ERR_MSG, crate_token.text_range())); | ||
242 | return; | ||
243 | } | ||
244 | |||
245 | // We now know that the path variable describes a complete path. | ||
246 | // For expressions and types, validation is complete, but we still have | ||
247 | // to handle UseItems like this: | ||
248 | // use foo:{crate}; | ||
249 | // so we crawl upwards looking for any preceding paths on `UseTree`s | ||
250 | for node in path.syntax().ancestors().skip(1) { | ||
251 | match_ast! { | ||
252 | match node { | ||
253 | ast::UseTree(it) => if let Some(tree_path) = it.path() { | ||
254 | if tree_path != path { | ||
255 | errors.push(SyntaxError::new(ERR_MSG, crate_token.text_range())); | ||
256 | } | ||
257 | }, | ||
258 | ast::UseTreeList(_it) => continue, | ||
259 | _ => return, | ||
260 | } | ||
261 | }; | ||
262 | } | ||
263 | } | ||
diff --git a/crates/ra_syntax/test_data/parser/err/0040_illegal_crate_kw_location.rast b/crates/ra_syntax/test_data/parser/err/0040_illegal_crate_kw_location.rast new file mode 100644 index 000000000..8306f7361 --- /dev/null +++ b/crates/ra_syntax/test_data/parser/err/0040_illegal_crate_kw_location.rast | |||
@@ -0,0 +1,76 @@ | |||
1 | [email protected] | ||
2 | [email protected] | ||
3 | [email protected] "use" | ||
4 | [email protected] " " | ||
5 | [email protected] | ||
6 | [email protected] | ||
7 | [email protected] | ||
8 | [email protected] "::" | ||
9 | [email protected] "crate" | ||
10 | [email protected] ";" | ||
11 | [email protected] "\n" | ||
12 | [email protected] | ||
13 | [email protected] "use" | ||
14 | [email protected] " " | ||
15 | [email protected] | ||
16 | [email protected] | ||
17 | [email protected] "{" | ||
18 | [email protected] | ||
19 | [email protected] | ||
20 | [email protected] | ||
21 | [email protected] "crate" | ||
22 | [email protected] "," | ||
23 | [email protected] " " | ||
24 | [email protected] | ||
25 | [email protected] | ||
26 | [email protected] | ||
27 | [email protected] | ||
28 | [email protected] "foo" | ||
29 | [email protected] "::" | ||
30 | [email protected] | ||
31 | [email protected] "{" | ||
32 | [email protected] | ||
33 | [email protected] | ||
34 | [email protected] | ||
35 | [email protected] "crate" | ||
36 | [email protected] "}" | ||
37 | [email protected] "}" | ||
38 | [email protected] ";" | ||
39 | [email protected] "\n" | ||
40 | [email protected] | ||
41 | [email protected] "use" | ||
42 | [email protected] " " | ||
43 | [email protected] | ||
44 | [email protected] | ||
45 | [email protected] | ||
46 | [email protected] | ||
47 | [email protected] | ||
48 | [email protected] "hello" | ||
49 | [email protected] "::" | ||
50 | [email protected] | ||
51 | [email protected] "crate" | ||
52 | [email protected] ";" | ||
53 | [email protected] "\n" | ||
54 | [email protected] | ||
55 | [email protected] "use" | ||
56 | [email protected] " " | ||
57 | [email protected] | ||
58 | [email protected] | ||
59 | [email protected] | ||
60 | [email protected] | ||
61 | [email protected] | ||
62 | [email protected] | ||
63 | [email protected] "hello" | ||
64 | [email protected] "::" | ||
65 | [email protected] | ||
66 | [email protected] "crate" | ||
67 | [email protected] "::" | ||
68 | [email protected] | ||
69 | [email protected] | ||
70 | [email protected] "there" | ||
71 | [email protected] ";" | ||
72 | [email protected] "\n" | ||
73 | error 6..11: The `crate` keyword is only allowed as the first segment of a path | ||
74 | error 31..36: The `crate` keyword is only allowed as the first segment of a path | ||
75 | error 51..56: The `crate` keyword is only allowed as the first segment of a path | ||
76 | error 69..74: The `crate` keyword is only allowed as the first segment of a path | ||
diff --git a/crates/ra_syntax/test_data/parser/err/0040_illegal_crate_kw_location.rs b/crates/ra_syntax/test_data/parser/err/0040_illegal_crate_kw_location.rs new file mode 100644 index 000000000..bead4c0b6 --- /dev/null +++ b/crates/ra_syntax/test_data/parser/err/0040_illegal_crate_kw_location.rs | |||
@@ -0,0 +1,4 @@ | |||
1 | use ::crate; | ||
2 | use {crate, foo::{crate}}; | ||
3 | use hello::crate; | ||
4 | use hello::crate::there; | ||
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0159_try_macro_fallback.rast b/crates/ra_syntax/test_data/parser/inline/ok/0159_try_macro_fallback.rast new file mode 100644 index 000000000..beb6d8010 --- /dev/null +++ b/crates/ra_syntax/test_data/parser/inline/ok/0159_try_macro_fallback.rast | |||
@@ -0,0 +1,35 @@ | |||
1 | [email protected] | ||
2 | [email protected] | ||
3 | [email protected] "fn" | ||
4 | [email protected] " " | ||
5 | [email protected] | ||
6 | [email protected] "foo" | ||
7 | [email protected] | ||
8 | [email protected] "(" | ||
9 | [email protected] ")" | ||
10 | [email protected] " " | ||
11 | [email protected] | ||
12 | [email protected] | ||
13 | [email protected] "{" | ||
14 | [email protected] " " | ||
15 | [email protected] | ||
16 | [email protected] | ||
17 | [email protected] | ||
18 | [email protected] | ||
19 | [email protected] | ||
20 | [email protected] "try" | ||
21 | [email protected] "!" | ||
22 | [email protected] | ||
23 | [email protected] "(" | ||
24 | [email protected] "Ok" | ||
25 | [email protected] | ||
26 | [email protected] "(" | ||
27 | [email protected] | ||
28 | [email protected] "(" | ||
29 | [email protected] ")" | ||
30 | [email protected] ")" | ||
31 | [email protected] ")" | ||
32 | [email protected] ";" | ||
33 | [email protected] " " | ||
34 | [email protected] "}" | ||
35 | [email protected] "\n" | ||
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0159_try_macro_fallback.rs b/crates/ra_syntax/test_data/parser/inline/ok/0159_try_macro_fallback.rs new file mode 100644 index 000000000..61a6b46a0 --- /dev/null +++ b/crates/ra_syntax/test_data/parser/inline/ok/0159_try_macro_fallback.rs | |||
@@ -0,0 +1 @@ | |||
fn foo() { try!(Ok(())); } | |||
diff --git a/crates/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml index cee0248b6..514d6d1a9 100644 --- a/crates/rust-analyzer/Cargo.toml +++ b/crates/rust-analyzer/Cargo.toml | |||
@@ -20,7 +20,7 @@ globset = "0.4.4" | |||
20 | itertools = "0.9.0" | 20 | itertools = "0.9.0" |
21 | jod-thread = "0.1.0" | 21 | jod-thread = "0.1.0" |
22 | log = "0.4.8" | 22 | log = "0.4.8" |
23 | lsp-types = { version = "0.73.0", features = ["proposed"] } | 23 | lsp-types = { version = "0.74.0", features = ["proposed"] } |
24 | parking_lot = "0.10.0" | 24 | parking_lot = "0.10.0" |
25 | pico-args = "0.3.1" | 25 | pico-args = "0.3.1" |
26 | rand = { version = "0.7.3", features = ["small_rng"] } | 26 | rand = { version = "0.7.3", features = ["small_rng"] } |
diff --git a/crates/rust-analyzer/src/conv.rs b/crates/rust-analyzer/src/conv.rs index b0f911f71..7be5ebcdb 100644 --- a/crates/rust-analyzer/src/conv.rs +++ b/crates/rust-analyzer/src/conv.rs | |||
@@ -25,7 +25,8 @@ use crate::{ | |||
25 | Result, | 25 | Result, |
26 | }; | 26 | }; |
27 | use semantic_tokens::{ | 27 | use semantic_tokens::{ |
28 | ATTRIBUTE, BUILTIN_TYPE, ENUM_MEMBER, LIFETIME, TYPE_ALIAS, UNION, UNRESOLVED_REFERENCE, | 28 | ATTRIBUTE, BUILTIN_TYPE, ENUM_MEMBER, FORMAT_SPECIFIER, LIFETIME, TYPE_ALIAS, UNION, |
29 | UNRESOLVED_REFERENCE, | ||
29 | }; | 30 | }; |
30 | 31 | ||
31 | pub trait Conv { | 32 | pub trait Conv { |
@@ -149,7 +150,7 @@ impl ConvWith<(&LineIndex, LineEndings)> for CompletionItem { | |||
149 | detail: self.detail().map(|it| it.to_string()), | 150 | detail: self.detail().map(|it| it.to_string()), |
150 | filter_text: Some(self.lookup().to_string()), | 151 | filter_text: Some(self.lookup().to_string()), |
151 | kind: self.kind().map(|it| it.conv()), | 152 | kind: self.kind().map(|it| it.conv()), |
152 | text_edit: Some(text_edit), | 153 | text_edit: Some(text_edit.into()), |
153 | additional_text_edits: Some(additional_text_edits), | 154 | additional_text_edits: Some(additional_text_edits), |
154 | documentation: self.documentation().map(|it| it.conv()), | 155 | documentation: self.documentation().map(|it| it.conv()), |
155 | deprecated: Some(self.deprecated()), | 156 | deprecated: Some(self.deprecated()), |
@@ -381,6 +382,7 @@ impl Conv for Highlight { | |||
381 | HighlightTag::Attribute => ATTRIBUTE, | 382 | HighlightTag::Attribute => ATTRIBUTE, |
382 | HighlightTag::Keyword => SemanticTokenType::KEYWORD, | 383 | HighlightTag::Keyword => SemanticTokenType::KEYWORD, |
383 | HighlightTag::UnresolvedReference => UNRESOLVED_REFERENCE, | 384 | HighlightTag::UnresolvedReference => UNRESOLVED_REFERENCE, |
385 | HighlightTag::FormatSpecifier => FORMAT_SPECIFIER, | ||
384 | }; | 386 | }; |
385 | 387 | ||
386 | for modifier in self.modifiers.iter() { | 388 | for modifier in self.modifiers.iter() { |
diff --git a/crates/rust-analyzer/src/main_loop/handlers.rs b/crates/rust-analyzer/src/main_loop/handlers.rs index 6caaf5f88..8db2dfa0c 100644 --- a/crates/rust-analyzer/src/main_loop/handlers.rs +++ b/crates/rust-analyzer/src/main_loop/handlers.rs | |||
@@ -326,10 +326,10 @@ pub fn handle_workspace_symbol( | |||
326 | 326 | ||
327 | pub fn handle_goto_definition( | 327 | pub fn handle_goto_definition( |
328 | world: WorldSnapshot, | 328 | world: WorldSnapshot, |
329 | params: req::TextDocumentPositionParams, | 329 | params: req::GotoDefinitionParams, |
330 | ) -> Result<Option<req::GotoDefinitionResponse>> { | 330 | ) -> Result<Option<req::GotoDefinitionResponse>> { |
331 | let _p = profile("handle_goto_definition"); | 331 | let _p = profile("handle_goto_definition"); |
332 | let position = params.try_conv_with(&world)?; | 332 | let position = params.text_document_position_params.try_conv_with(&world)?; |
333 | let nav_info = match world.analysis().goto_definition(position)? { | 333 | let nav_info = match world.analysis().goto_definition(position)? { |
334 | None => return Ok(None), | 334 | None => return Ok(None), |
335 | Some(it) => it, | 335 | Some(it) => it, |
@@ -340,10 +340,10 @@ pub fn handle_goto_definition( | |||
340 | 340 | ||
341 | pub fn handle_goto_implementation( | 341 | pub fn handle_goto_implementation( |
342 | world: WorldSnapshot, | 342 | world: WorldSnapshot, |
343 | params: req::TextDocumentPositionParams, | 343 | params: req::GotoImplementationParams, |
344 | ) -> Result<Option<req::GotoImplementationResponse>> { | 344 | ) -> Result<Option<req::GotoImplementationResponse>> { |
345 | let _p = profile("handle_goto_implementation"); | 345 | let _p = profile("handle_goto_implementation"); |
346 | let position = params.try_conv_with(&world)?; | 346 | let position = params.text_document_position_params.try_conv_with(&world)?; |
347 | let nav_info = match world.analysis().goto_implementation(position)? { | 347 | let nav_info = match world.analysis().goto_implementation(position)? { |
348 | None => return Ok(None), | 348 | None => return Ok(None), |
349 | Some(it) => it, | 349 | Some(it) => it, |
@@ -354,10 +354,10 @@ pub fn handle_goto_implementation( | |||
354 | 354 | ||
355 | pub fn handle_goto_type_definition( | 355 | pub fn handle_goto_type_definition( |
356 | world: WorldSnapshot, | 356 | world: WorldSnapshot, |
357 | params: req::TextDocumentPositionParams, | 357 | params: req::GotoTypeDefinitionParams, |
358 | ) -> Result<Option<req::GotoTypeDefinitionResponse>> { | 358 | ) -> Result<Option<req::GotoTypeDefinitionResponse>> { |
359 | let _p = profile("handle_goto_type_definition"); | 359 | let _p = profile("handle_goto_type_definition"); |
360 | let position = params.try_conv_with(&world)?; | 360 | let position = params.text_document_position_params.try_conv_with(&world)?; |
361 | let nav_info = match world.analysis().goto_type_definition(position)? { | 361 | let nav_info = match world.analysis().goto_type_definition(position)? { |
362 | None => return Ok(None), | 362 | None => return Ok(None), |
363 | Some(it) => it, | 363 | Some(it) => it, |
@@ -487,10 +487,10 @@ pub fn handle_folding_range( | |||
487 | 487 | ||
488 | pub fn handle_signature_help( | 488 | pub fn handle_signature_help( |
489 | world: WorldSnapshot, | 489 | world: WorldSnapshot, |
490 | params: req::TextDocumentPositionParams, | 490 | params: req::SignatureHelpParams, |
491 | ) -> Result<Option<req::SignatureHelp>> { | 491 | ) -> Result<Option<req::SignatureHelp>> { |
492 | let _p = profile("handle_signature_help"); | 492 | let _p = profile("handle_signature_help"); |
493 | let position = params.try_conv_with(&world)?; | 493 | let position = params.text_document_position_params.try_conv_with(&world)?; |
494 | if let Some(call_info) = world.analysis().call_info(position)? { | 494 | if let Some(call_info) = world.analysis().call_info(position)? { |
495 | let concise = !world.config.call_info_full; | 495 | let concise = !world.config.call_info_full; |
496 | let mut active_parameter = call_info.active_parameter.map(|it| it as i64); | 496 | let mut active_parameter = call_info.active_parameter.map(|it| it as i64); |
@@ -509,12 +509,9 @@ pub fn handle_signature_help( | |||
509 | } | 509 | } |
510 | } | 510 | } |
511 | 511 | ||
512 | pub fn handle_hover( | 512 | pub fn handle_hover(world: WorldSnapshot, params: req::HoverParams) -> Result<Option<Hover>> { |
513 | world: WorldSnapshot, | ||
514 | params: req::TextDocumentPositionParams, | ||
515 | ) -> Result<Option<Hover>> { | ||
516 | let _p = profile("handle_hover"); | 513 | let _p = profile("handle_hover"); |
517 | let position = params.try_conv_with(&world)?; | 514 | let position = params.text_document_position_params.try_conv_with(&world)?; |
518 | let info = match world.analysis().hover(position)? { | 515 | let info = match world.analysis().hover(position)? { |
519 | None => return Ok(None), | 516 | None => return Ok(None), |
520 | Some(info) => info, | 517 | Some(info) => info, |
@@ -878,8 +875,14 @@ pub fn handle_code_lens( | |||
878 | .map(|it| { | 875 | .map(|it| { |
879 | let range = it.node_range.conv_with(&line_index); | 876 | let range = it.node_range.conv_with(&line_index); |
880 | let pos = range.start; | 877 | let pos = range.start; |
881 | let lens_params = | 878 | let lens_params = req::GotoImplementationParams { |
882 | req::TextDocumentPositionParams::new(params.text_document.clone(), pos); | 879 | text_document_position_params: req::TextDocumentPositionParams::new( |
880 | params.text_document.clone(), | ||
881 | pos, | ||
882 | ), | ||
883 | work_done_progress_params: Default::default(), | ||
884 | partial_result_params: Default::default(), | ||
885 | }; | ||
883 | CodeLens { | 886 | CodeLens { |
884 | range, | 887 | range, |
885 | command: None, | 888 | command: None, |
@@ -894,7 +897,7 @@ pub fn handle_code_lens( | |||
894 | #[derive(Debug, Serialize, Deserialize)] | 897 | #[derive(Debug, Serialize, Deserialize)] |
895 | #[serde(rename_all = "camelCase")] | 898 | #[serde(rename_all = "camelCase")] |
896 | enum CodeLensResolveData { | 899 | enum CodeLensResolveData { |
897 | Impls(req::TextDocumentPositionParams), | 900 | Impls(req::GotoImplementationParams), |
898 | } | 901 | } |
899 | 902 | ||
900 | pub fn handle_code_lens_resolve(world: WorldSnapshot, code_lens: CodeLens) -> Result<CodeLens> { | 903 | pub fn handle_code_lens_resolve(world: WorldSnapshot, code_lens: CodeLens) -> Result<CodeLens> { |
@@ -927,7 +930,7 @@ pub fn handle_code_lens_resolve(world: WorldSnapshot, code_lens: CodeLens) -> Re | |||
927 | title, | 930 | title, |
928 | command: "rust-analyzer.showReferences".into(), | 931 | command: "rust-analyzer.showReferences".into(), |
929 | arguments: Some(vec![ | 932 | arguments: Some(vec![ |
930 | to_value(&lens_params.text_document.uri).unwrap(), | 933 | to_value(&lens_params.text_document_position_params.text_document.uri).unwrap(), |
931 | to_value(code_lens.range.start).unwrap(), | 934 | to_value(code_lens.range.start).unwrap(), |
932 | to_value(locations).unwrap(), | 935 | to_value(locations).unwrap(), |
933 | ]), | 936 | ]), |
@@ -944,16 +947,16 @@ pub fn handle_code_lens_resolve(world: WorldSnapshot, code_lens: CodeLens) -> Re | |||
944 | 947 | ||
945 | pub fn handle_document_highlight( | 948 | pub fn handle_document_highlight( |
946 | world: WorldSnapshot, | 949 | world: WorldSnapshot, |
947 | params: req::TextDocumentPositionParams, | 950 | params: req::DocumentHighlightParams, |
948 | ) -> Result<Option<Vec<DocumentHighlight>>> { | 951 | ) -> Result<Option<Vec<DocumentHighlight>>> { |
949 | let _p = profile("handle_document_highlight"); | 952 | let _p = profile("handle_document_highlight"); |
950 | let file_id = params.text_document.try_conv_with(&world)?; | 953 | let file_id = params.text_document_position_params.text_document.try_conv_with(&world)?; |
951 | let line_index = world.analysis().file_line_index(file_id)?; | 954 | let line_index = world.analysis().file_line_index(file_id)?; |
952 | 955 | ||
953 | let refs = match world | 956 | let refs = match world.analysis().find_all_refs( |
954 | .analysis() | 957 | params.text_document_position_params.try_conv_with(&world)?, |
955 | .find_all_refs(params.try_conv_with(&world)?, Some(SearchScope::single_file(file_id)))? | 958 | Some(SearchScope::single_file(file_id)), |
956 | { | 959 | )? { |
957 | None => return Ok(None), | 960 | None => return Ok(None), |
958 | Some(refs) => refs, | 961 | Some(refs) => refs, |
959 | }; | 962 | }; |
diff --git a/crates/rust-analyzer/src/req.rs b/crates/rust-analyzer/src/req.rs index ae3448892..0dae6bad4 100644 --- a/crates/rust-analyzer/src/req.rs +++ b/crates/rust-analyzer/src/req.rs | |||
@@ -8,14 +8,15 @@ pub use lsp_types::{ | |||
8 | notification::*, request::*, ApplyWorkspaceEditParams, CodeActionParams, CodeLens, | 8 | notification::*, request::*, ApplyWorkspaceEditParams, CodeActionParams, CodeLens, |
9 | CodeLensParams, CompletionParams, CompletionResponse, ConfigurationItem, ConfigurationParams, | 9 | CodeLensParams, CompletionParams, CompletionResponse, ConfigurationItem, ConfigurationParams, |
10 | DiagnosticTag, DidChangeConfigurationParams, DidChangeWatchedFilesParams, | 10 | DiagnosticTag, DidChangeConfigurationParams, DidChangeWatchedFilesParams, |
11 | DidChangeWatchedFilesRegistrationOptions, DocumentOnTypeFormattingParams, DocumentSymbolParams, | 11 | DidChangeWatchedFilesRegistrationOptions, DocumentHighlightParams, |
12 | DocumentSymbolResponse, FileSystemWatcher, Hover, InitializeResult, MessageType, | 12 | DocumentOnTypeFormattingParams, DocumentSymbolParams, DocumentSymbolResponse, |
13 | PartialResultParams, ProgressParams, ProgressParamsValue, ProgressToken, | 13 | FileSystemWatcher, GotoDefinitionParams, GotoDefinitionResponse, Hover, HoverParams, |
14 | PublishDiagnosticsParams, ReferenceParams, Registration, RegistrationParams, SelectionRange, | 14 | InitializeResult, MessageType, PartialResultParams, ProgressParams, ProgressParamsValue, |
15 | SelectionRangeParams, SemanticTokensParams, SemanticTokensRangeParams, | 15 | ProgressToken, PublishDiagnosticsParams, ReferenceParams, Registration, RegistrationParams, |
16 | SelectionRange, SelectionRangeParams, SemanticTokensParams, SemanticTokensRangeParams, | ||
16 | SemanticTokensRangeResult, SemanticTokensResult, ServerCapabilities, ShowMessageParams, | 17 | SemanticTokensRangeResult, SemanticTokensResult, ServerCapabilities, ShowMessageParams, |
17 | SignatureHelp, SymbolKind, TextDocumentEdit, TextDocumentPositionParams, TextEdit, | 18 | SignatureHelp, SignatureHelpParams, SymbolKind, TextDocumentEdit, TextDocumentPositionParams, |
18 | WorkDoneProgressParams, WorkspaceEdit, WorkspaceSymbolParams, | 19 | TextEdit, WorkDoneProgressParams, WorkspaceEdit, WorkspaceSymbolParams, |
19 | }; | 20 | }; |
20 | use std::path::PathBuf; | 21 | use std::path::PathBuf; |
21 | 22 | ||
diff --git a/crates/rust-analyzer/src/semantic_tokens.rs b/crates/rust-analyzer/src/semantic_tokens.rs index 10fe696f6..2dc5cb119 100644 --- a/crates/rust-analyzer/src/semantic_tokens.rs +++ b/crates/rust-analyzer/src/semantic_tokens.rs | |||
@@ -4,62 +4,69 @@ use std::ops; | |||
4 | 4 | ||
5 | use lsp_types::{Range, SemanticToken, SemanticTokenModifier, SemanticTokenType, SemanticTokens}; | 5 | use lsp_types::{Range, SemanticToken, SemanticTokenModifier, SemanticTokenType, SemanticTokens}; |
6 | 6 | ||
7 | pub(crate) const ATTRIBUTE: SemanticTokenType = SemanticTokenType::new("attribute"); | 7 | macro_rules! define_semantic_token_types { |
8 | pub(crate) const BUILTIN_TYPE: SemanticTokenType = SemanticTokenType::new("builtinType"); | 8 | ($(($ident:ident, $string:literal)),*$(,)?) => { |
9 | pub(crate) const ENUM_MEMBER: SemanticTokenType = SemanticTokenType::new("enumMember"); | 9 | $(pub(crate) const $ident: SemanticTokenType = SemanticTokenType::new($string);)* |
10 | pub(crate) const LIFETIME: SemanticTokenType = SemanticTokenType::new("lifetime"); | 10 | |
11 | pub(crate) const TYPE_ALIAS: SemanticTokenType = SemanticTokenType::new("typeAlias"); | 11 | pub(crate) const SUPPORTED_TYPES: &[SemanticTokenType] = &[ |
12 | pub(crate) const UNION: SemanticTokenType = SemanticTokenType::new("union"); | 12 | SemanticTokenType::COMMENT, |
13 | pub(crate) const UNRESOLVED_REFERENCE: SemanticTokenType = | 13 | SemanticTokenType::KEYWORD, |
14 | SemanticTokenType::new("unresolvedReference"); | 14 | SemanticTokenType::STRING, |
15 | 15 | SemanticTokenType::NUMBER, | |
16 | pub(crate) const CONSTANT: SemanticTokenModifier = SemanticTokenModifier::new("constant"); | 16 | SemanticTokenType::REGEXP, |
17 | pub(crate) const CONTROL_FLOW: SemanticTokenModifier = SemanticTokenModifier::new("controlFlow"); | 17 | SemanticTokenType::OPERATOR, |
18 | pub(crate) const MUTABLE: SemanticTokenModifier = SemanticTokenModifier::new("mutable"); | 18 | SemanticTokenType::NAMESPACE, |
19 | pub(crate) const UNSAFE: SemanticTokenModifier = SemanticTokenModifier::new("unsafe"); | 19 | SemanticTokenType::TYPE, |
20 | 20 | SemanticTokenType::STRUCT, | |
21 | pub(crate) const SUPPORTED_TYPES: &[SemanticTokenType] = &[ | 21 | SemanticTokenType::CLASS, |
22 | SemanticTokenType::COMMENT, | 22 | SemanticTokenType::INTERFACE, |
23 | SemanticTokenType::KEYWORD, | 23 | SemanticTokenType::ENUM, |
24 | SemanticTokenType::STRING, | 24 | SemanticTokenType::TYPE_PARAMETER, |
25 | SemanticTokenType::NUMBER, | 25 | SemanticTokenType::FUNCTION, |
26 | SemanticTokenType::REGEXP, | 26 | SemanticTokenType::MEMBER, |
27 | SemanticTokenType::OPERATOR, | 27 | SemanticTokenType::PROPERTY, |
28 | SemanticTokenType::NAMESPACE, | 28 | SemanticTokenType::MACRO, |
29 | SemanticTokenType::TYPE, | 29 | SemanticTokenType::VARIABLE, |
30 | SemanticTokenType::STRUCT, | 30 | SemanticTokenType::PARAMETER, |
31 | SemanticTokenType::CLASS, | 31 | SemanticTokenType::LABEL, |
32 | SemanticTokenType::INTERFACE, | 32 | $($ident),* |
33 | SemanticTokenType::ENUM, | 33 | ]; |
34 | SemanticTokenType::TYPE_PARAMETER, | 34 | }; |
35 | SemanticTokenType::FUNCTION, | 35 | } |
36 | SemanticTokenType::MEMBER, | 36 | |
37 | SemanticTokenType::PROPERTY, | 37 | define_semantic_token_types![ |
38 | SemanticTokenType::MACRO, | 38 | (ATTRIBUTE, "attribute"), |
39 | SemanticTokenType::VARIABLE, | 39 | (BUILTIN_TYPE, "builtinType"), |
40 | SemanticTokenType::PARAMETER, | 40 | (ENUM_MEMBER, "enumMember"), |
41 | SemanticTokenType::LABEL, | 41 | (LIFETIME, "lifetime"), |
42 | ATTRIBUTE, | 42 | (TYPE_ALIAS, "typeAlias"), |
43 | BUILTIN_TYPE, | 43 | (UNION, "union"), |
44 | ENUM_MEMBER, | 44 | (UNRESOLVED_REFERENCE, "unresolvedReference"), |
45 | LIFETIME, | 45 | (FORMAT_SPECIFIER, "formatSpecifier"), |
46 | TYPE_ALIAS, | ||
47 | UNION, | ||
48 | UNRESOLVED_REFERENCE, | ||
49 | ]; | 46 | ]; |
50 | 47 | ||
51 | pub(crate) const SUPPORTED_MODIFIERS: &[SemanticTokenModifier] = &[ | 48 | macro_rules! define_semantic_token_modifiers { |
52 | SemanticTokenModifier::DOCUMENTATION, | 49 | ($(($ident:ident, $string:literal)),*$(,)?) => { |
53 | SemanticTokenModifier::DECLARATION, | 50 | $(pub(crate) const $ident: SemanticTokenModifier = SemanticTokenModifier::new($string);)* |
54 | SemanticTokenModifier::DEFINITION, | 51 | |
55 | SemanticTokenModifier::STATIC, | 52 | pub(crate) const SUPPORTED_MODIFIERS: &[SemanticTokenModifier] = &[ |
56 | SemanticTokenModifier::ABSTRACT, | 53 | SemanticTokenModifier::DOCUMENTATION, |
57 | SemanticTokenModifier::DEPRECATED, | 54 | SemanticTokenModifier::DECLARATION, |
58 | SemanticTokenModifier::READONLY, | 55 | SemanticTokenModifier::DEFINITION, |
59 | CONSTANT, | 56 | SemanticTokenModifier::STATIC, |
60 | MUTABLE, | 57 | SemanticTokenModifier::ABSTRACT, |
61 | UNSAFE, | 58 | SemanticTokenModifier::DEPRECATED, |
62 | CONTROL_FLOW, | 59 | SemanticTokenModifier::READONLY, |
60 | $($ident),* | ||
61 | ]; | ||
62 | }; | ||
63 | } | ||
64 | |||
65 | define_semantic_token_modifiers![ | ||
66 | (CONSTANT, "constant"), | ||
67 | (CONTROL_FLOW, "controlFlow"), | ||
68 | (MUTABLE, "mutable"), | ||
69 | (UNSAFE, "unsafe"), | ||
63 | ]; | 70 | ]; |
64 | 71 | ||
65 | #[derive(Default)] | 72 | #[derive(Default)] |
diff --git a/crates/rust-analyzer/tests/heavy_tests/main.rs b/crates/rust-analyzer/tests/heavy_tests/main.rs index f6245ddd4..07b8114c6 100644 --- a/crates/rust-analyzer/tests/heavy_tests/main.rs +++ b/crates/rust-analyzer/tests/heavy_tests/main.rs | |||
@@ -4,8 +4,8 @@ use std::{collections::HashMap, path::PathBuf, time::Instant}; | |||
4 | 4 | ||
5 | use lsp_types::{ | 5 | use lsp_types::{ |
6 | CodeActionContext, DidOpenTextDocumentParams, DocumentFormattingParams, FormattingOptions, | 6 | CodeActionContext, DidOpenTextDocumentParams, DocumentFormattingParams, FormattingOptions, |
7 | PartialResultParams, Position, Range, TextDocumentItem, TextDocumentPositionParams, | 7 | GotoDefinitionParams, HoverParams, PartialResultParams, Position, Range, TextDocumentItem, |
8 | WorkDoneProgressParams, | 8 | TextDocumentPositionParams, WorkDoneProgressParams, |
9 | }; | 9 | }; |
10 | use rust_analyzer::req::{ | 10 | use rust_analyzer::req::{ |
11 | CodeActionParams, CodeActionRequest, Completion, CompletionParams, DidOpenTextDocument, | 11 | CodeActionParams, CodeActionRequest, Completion, CompletionParams, DidOpenTextDocument, |
@@ -610,10 +610,14 @@ fn main() { message(); } | |||
610 | }) | 610 | }) |
611 | .server(); | 611 | .server(); |
612 | server.wait_until_workspace_is_loaded(); | 612 | server.wait_until_workspace_is_loaded(); |
613 | let res = server.send_request::<GotoDefinition>(TextDocumentPositionParams::new( | 613 | let res = server.send_request::<GotoDefinition>(GotoDefinitionParams { |
614 | server.doc_id("src/main.rs"), | 614 | text_document_position_params: TextDocumentPositionParams::new( |
615 | Position::new(2, 15), | 615 | server.doc_id("src/main.rs"), |
616 | )); | 616 | Position::new(2, 15), |
617 | ), | ||
618 | work_done_progress_params: Default::default(), | ||
619 | partial_result_params: Default::default(), | ||
620 | }); | ||
617 | assert!(format!("{}", res).contains("hello.rs")); | 621 | assert!(format!("{}", res).contains("hello.rs")); |
618 | } | 622 | } |
619 | 623 | ||
@@ -692,10 +696,13 @@ pub fn foo(_input: TokenStream) -> TokenStream { | |||
692 | .root("bar") | 696 | .root("bar") |
693 | .server(); | 697 | .server(); |
694 | server.wait_until_workspace_is_loaded(); | 698 | server.wait_until_workspace_is_loaded(); |
695 | let res = server.send_request::<HoverRequest>(TextDocumentPositionParams::new( | 699 | let res = server.send_request::<HoverRequest>(HoverParams { |
696 | server.doc_id("foo/src/main.rs"), | 700 | text_document_position_params: TextDocumentPositionParams::new( |
697 | Position::new(7, 9), | 701 | server.doc_id("foo/src/main.rs"), |
698 | )); | 702 | Position::new(7, 9), |
703 | ), | ||
704 | work_done_progress_params: Default::default(), | ||
705 | }); | ||
699 | 706 | ||
700 | let value = res.get("contents").unwrap().get("value").unwrap().to_string(); | 707 | let value = res.get("contents").unwrap().get("value").unwrap().to_string(); |
701 | assert_eq!(value, r#""```rust\nfoo::Bar\nfn bar()\n```""#) | 708 | assert_eq!(value, r#""```rust\nfoo::Bar\nfn bar()\n```""#) |