aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--PRIVACY.md17
-rw-r--r--crates/assists/src/assist_config.rs5
-rw-r--r--crates/assists/src/ast_transform.rs74
-rw-r--r--crates/assists/src/handlers/auto_import.rs16
-rw-r--r--crates/assists/src/handlers/convert_integer_literal.rs701
-rw-r--r--crates/assists/src/handlers/remove_dbg.rs9
-rw-r--r--crates/assists/src/lib.rs2
-rw-r--r--crates/assists/src/tests.rs27
-rw-r--r--crates/assists/src/tests/generated.rs13
-rw-r--r--crates/hir/src/code_model.rs4
-rw-r--r--crates/hir/src/lib.rs1
-rw-r--r--crates/hir_def/src/find_path.rs247
-rw-r--r--crates/hir_def/src/nameres/collector.rs3
-rw-r--r--crates/hir_def/src/nameres/tests/macros.rs7
-rw-r--r--crates/ide/src/completion/complete_postfix/format_like.rs2
-rw-r--r--crates/ide/src/completion/complete_unqualified_path.rs20
-rw-r--r--crates/ide/src/completion/completion_context.rs5
-rw-r--r--crates/ide/src/inlay_hints.rs132
-rw-r--r--crates/rust-analyzer/src/config.rs17
-rw-r--r--editors/code/package.json17
-rw-r--r--xtask/src/codegen/gen_feature_docs.rs12
21 files changed, 1141 insertions, 190 deletions
diff --git a/PRIVACY.md b/PRIVACY.md
new file mode 100644
index 000000000..dd165c0e2
--- /dev/null
+++ b/PRIVACY.md
@@ -0,0 +1,17 @@
1# Privacy Notes
2
3## LSP server binary
4
5The LSP server performs no network access in itself, but runs `cargo metadata` which will update or download the crate registry and the source code of the project dependencies.
6
7## Visual Studio Code extension
8
9The Code extension connects to GitHub to download updated LSP binaries and, if the nightly channel is selected, to perform update checks.
10
11## Other editor plugins
12
13Any other editor plugins that integrate with `rust-analyzer` are not under the control of the `rust-analyzer` developers. For any privacy concerns, you should check with their respective developers.
14
15## Others
16
17If `cargo check` is enabled (the default), any build scripts or procedural macros used by the project or its dependencies will be executed. This is also the case when `cargo check` is disabled, but build script or procedural macro support is enabled in `rust-analyzer` (off by default).
diff --git a/crates/assists/src/assist_config.rs b/crates/assists/src/assist_config.rs
index adf02edab..b24527ec4 100644
--- a/crates/assists/src/assist_config.rs
+++ b/crates/assists/src/assist_config.rs
@@ -4,6 +4,8 @@
4//! module, and we use to statically check that we only produce snippet 4//! module, and we use to statically check that we only produce snippet
5//! assists if we are allowed to. 5//! assists if we are allowed to.
6 6
7use hir::PrefixKind;
8
7use crate::{utils::MergeBehaviour, AssistKind}; 9use crate::{utils::MergeBehaviour, AssistKind};
8 10
9#[derive(Clone, Debug, PartialEq, Eq)] 11#[derive(Clone, Debug, PartialEq, Eq)]
@@ -37,10 +39,11 @@ impl Default for AssistConfig {
37#[derive(Clone, Copy, Debug, PartialEq, Eq)] 39#[derive(Clone, Copy, Debug, PartialEq, Eq)]
38pub struct InsertUseConfig { 40pub struct InsertUseConfig {
39 pub merge: Option<MergeBehaviour>, 41 pub merge: Option<MergeBehaviour>,
42 pub prefix_kind: PrefixKind,
40} 43}
41 44
42impl Default for InsertUseConfig { 45impl Default for InsertUseConfig {
43 fn default() -> Self { 46 fn default() -> Self {
44 InsertUseConfig { merge: Some(MergeBehaviour::Full) } 47 InsertUseConfig { merge: Some(MergeBehaviour::Full), prefix_kind: PrefixKind::Plain }
45 } 48 }
46} 49}
diff --git a/crates/assists/src/ast_transform.rs b/crates/assists/src/ast_transform.rs
index 835da3bb2..4307e0191 100644
--- a/crates/assists/src/ast_transform.rs
+++ b/crates/assists/src/ast_transform.rs
@@ -5,12 +5,13 @@ use hir::{HirDisplay, PathResolution, SemanticsScope};
5use syntax::{ 5use syntax::{
6 algo::SyntaxRewriter, 6 algo::SyntaxRewriter,
7 ast::{self, AstNode}, 7 ast::{self, AstNode},
8 SyntaxNode,
8}; 9};
9 10
10pub fn apply<'a, N: AstNode>(transformer: &dyn AstTransform<'a>, node: N) -> N { 11pub fn apply<'a, N: AstNode>(transformer: &dyn AstTransform<'a>, node: N) -> N {
11 SyntaxRewriter::from_fn(|element| match element { 12 SyntaxRewriter::from_fn(|element| match element {
12 syntax::SyntaxElement::Node(n) => { 13 syntax::SyntaxElement::Node(n) => {
13 let replacement = transformer.get_substitution(&n)?; 14 let replacement = transformer.get_substitution(&n, transformer)?;
14 Some(replacement.into()) 15 Some(replacement.into())
15 } 16 }
16 _ => None, 17 _ => None,
@@ -47,32 +48,35 @@ pub fn apply<'a, N: AstNode>(transformer: &dyn AstTransform<'a>, node: N) -> N {
47/// We'd want to somehow express this concept simpler, but so far nobody got to 48/// We'd want to somehow express this concept simpler, but so far nobody got to
48/// simplifying this! 49/// simplifying this!
49pub trait AstTransform<'a> { 50pub trait AstTransform<'a> {
50 fn get_substitution(&self, node: &syntax::SyntaxNode) -> Option<syntax::SyntaxNode>; 51 fn get_substitution(
52 &self,
53 node: &SyntaxNode,
54 recur: &dyn AstTransform<'a>,
55 ) -> Option<SyntaxNode>;
51 56
52 fn chain_before(self, other: Box<dyn AstTransform<'a> + 'a>) -> Box<dyn AstTransform<'a> + 'a>;
53 fn or<T: AstTransform<'a> + 'a>(self, other: T) -> Box<dyn AstTransform<'a> + 'a> 57 fn or<T: AstTransform<'a> + 'a>(self, other: T) -> Box<dyn AstTransform<'a> + 'a>
54 where 58 where
55 Self: Sized + 'a, 59 Self: Sized + 'a,
56 { 60 {
57 self.chain_before(Box::new(other)) 61 Box::new(Or(Box::new(self), Box::new(other)))
58 } 62 }
59} 63}
60 64
61struct NullTransformer; 65struct Or<'a>(Box<dyn AstTransform<'a> + 'a>, Box<dyn AstTransform<'a> + 'a>);
62 66
63impl<'a> AstTransform<'a> for NullTransformer { 67impl<'a> AstTransform<'a> for Or<'a> {
64 fn get_substitution(&self, _node: &syntax::SyntaxNode) -> Option<syntax::SyntaxNode> { 68 fn get_substitution(
65 None 69 &self,
66 } 70 node: &SyntaxNode,
67 fn chain_before(self, other: Box<dyn AstTransform<'a> + 'a>) -> Box<dyn AstTransform<'a> + 'a> { 71 recur: &dyn AstTransform<'a>,
68 other 72 ) -> Option<SyntaxNode> {
73 self.0.get_substitution(node, recur).or_else(|| self.1.get_substitution(node, recur))
69 } 74 }
70} 75}
71 76
72pub struct SubstituteTypeParams<'a> { 77pub struct SubstituteTypeParams<'a> {
73 source_scope: &'a SemanticsScope<'a>, 78 source_scope: &'a SemanticsScope<'a>,
74 substs: FxHashMap<hir::TypeParam, ast::Type>, 79 substs: FxHashMap<hir::TypeParam, ast::Type>,
75 previous: Box<dyn AstTransform<'a> + 'a>,
76} 80}
77 81
78impl<'a> SubstituteTypeParams<'a> { 82impl<'a> SubstituteTypeParams<'a> {
@@ -111,11 +115,7 @@ impl<'a> SubstituteTypeParams<'a> {
111 } 115 }
112 }) 116 })
113 .collect(); 117 .collect();
114 return SubstituteTypeParams { 118 return SubstituteTypeParams { source_scope, substs: substs_by_param };
115 source_scope,
116 substs: substs_by_param,
117 previous: Box::new(NullTransformer),
118 };
119 119
120 // FIXME: It would probably be nicer if we could get this via HIR (i.e. get the 120 // FIXME: It would probably be nicer if we could get this via HIR (i.e. get the
121 // trait ref, and then go from the types in the substs back to the syntax). 121 // trait ref, and then go from the types in the substs back to the syntax).
@@ -140,7 +140,14 @@ impl<'a> SubstituteTypeParams<'a> {
140 Some(result) 140 Some(result)
141 } 141 }
142 } 142 }
143 fn get_substitution_inner(&self, node: &syntax::SyntaxNode) -> Option<syntax::SyntaxNode> { 143}
144
145impl<'a> AstTransform<'a> for SubstituteTypeParams<'a> {
146 fn get_substitution(
147 &self,
148 node: &SyntaxNode,
149 _recur: &dyn AstTransform<'a>,
150 ) -> Option<SyntaxNode> {
144 let type_ref = ast::Type::cast(node.clone())?; 151 let type_ref = ast::Type::cast(node.clone())?;
145 let path = match &type_ref { 152 let path = match &type_ref {
146 ast::Type::PathType(path_type) => path_type.path()?, 153 ast::Type::PathType(path_type) => path_type.path()?,
@@ -154,27 +161,23 @@ impl<'a> SubstituteTypeParams<'a> {
154 } 161 }
155} 162}
156 163
157impl<'a> AstTransform<'a> for SubstituteTypeParams<'a> {
158 fn get_substitution(&self, node: &syntax::SyntaxNode) -> Option<syntax::SyntaxNode> {
159 self.get_substitution_inner(node).or_else(|| self.previous.get_substitution(node))
160 }
161 fn chain_before(self, other: Box<dyn AstTransform<'a> + 'a>) -> Box<dyn AstTransform<'a> + 'a> {
162 Box::new(SubstituteTypeParams { previous: other, ..self })
163 }
164}
165
166pub struct QualifyPaths<'a> { 164pub struct QualifyPaths<'a> {
167 target_scope: &'a SemanticsScope<'a>, 165 target_scope: &'a SemanticsScope<'a>,
168 source_scope: &'a SemanticsScope<'a>, 166 source_scope: &'a SemanticsScope<'a>,
169 previous: Box<dyn AstTransform<'a> + 'a>,
170} 167}
171 168
172impl<'a> QualifyPaths<'a> { 169impl<'a> QualifyPaths<'a> {
173 pub fn new(target_scope: &'a SemanticsScope<'a>, source_scope: &'a SemanticsScope<'a>) -> Self { 170 pub fn new(target_scope: &'a SemanticsScope<'a>, source_scope: &'a SemanticsScope<'a>) -> Self {
174 Self { target_scope, source_scope, previous: Box::new(NullTransformer) } 171 Self { target_scope, source_scope }
175 } 172 }
173}
176 174
177 fn get_substitution_inner(&self, node: &syntax::SyntaxNode) -> Option<syntax::SyntaxNode> { 175impl<'a> AstTransform<'a> for QualifyPaths<'a> {
176 fn get_substitution(
177 &self,
178 node: &SyntaxNode,
179 recur: &dyn AstTransform<'a>,
180 ) -> Option<SyntaxNode> {
178 // FIXME handle value ns? 181 // FIXME handle value ns?
179 let from = self.target_scope.module()?; 182 let from = self.target_scope.module()?;
180 let p = ast::Path::cast(node.clone())?; 183 let p = ast::Path::cast(node.clone())?;
@@ -191,7 +194,7 @@ impl<'a> QualifyPaths<'a> {
191 let type_args = p 194 let type_args = p
192 .segment() 195 .segment()
193 .and_then(|s| s.generic_arg_list()) 196 .and_then(|s| s.generic_arg_list())
194 .map(|arg_list| apply(self, arg_list)); 197 .map(|arg_list| apply(recur, arg_list));
195 if let Some(type_args) = type_args { 198 if let Some(type_args) = type_args {
196 let last_segment = path.segment().unwrap(); 199 let last_segment = path.segment().unwrap();
197 path = path.with_segment(last_segment.with_generic_args(type_args)) 200 path = path.with_segment(last_segment.with_generic_args(type_args))
@@ -208,15 +211,6 @@ impl<'a> QualifyPaths<'a> {
208 } 211 }
209} 212}
210 213
211impl<'a> AstTransform<'a> for QualifyPaths<'a> {
212 fn get_substitution(&self, node: &syntax::SyntaxNode) -> Option<syntax::SyntaxNode> {
213 self.get_substitution_inner(node).or_else(|| self.previous.get_substitution(node))
214 }
215 fn chain_before(self, other: Box<dyn AstTransform<'a> + 'a>) -> Box<dyn AstTransform<'a> + 'a> {
216 Box::new(QualifyPaths { previous: other, ..self })
217 }
218}
219
220pub(crate) fn path_to_ast(path: hir::ModPath) -> ast::Path { 214pub(crate) fn path_to_ast(path: hir::ModPath) -> ast::Path {
221 let parse = ast::SourceFile::parse(&path.to_string()); 215 let parse = ast::SourceFile::parse(&path.to_string());
222 parse 216 parse
diff --git a/crates/assists/src/handlers/auto_import.rs b/crates/assists/src/handlers/auto_import.rs
index fa524ffd9..357ff6392 100644
--- a/crates/assists/src/handlers/auto_import.rs
+++ b/crates/assists/src/handlers/auto_import.rs
@@ -191,12 +191,16 @@ impl AutoImportAssets {
191 _ => Some(candidate), 191 _ => Some(candidate),
192 }) 192 })
193 .filter_map(|candidate| match candidate { 193 .filter_map(|candidate| match candidate {
194 Either::Left(module_def) => { 194 Either::Left(module_def) => self.module_with_name_to_import.find_use_path_prefixed(
195 self.module_with_name_to_import.find_use_path_prefixed(db, module_def) 195 db,
196 } 196 module_def,
197 Either::Right(macro_def) => { 197 ctx.config.insert_use.prefix_kind,
198 self.module_with_name_to_import.find_use_path_prefixed(db, macro_def) 198 ),
199 } 199 Either::Right(macro_def) => self.module_with_name_to_import.find_use_path_prefixed(
200 db,
201 macro_def,
202 ctx.config.insert_use.prefix_kind,
203 ),
200 }) 204 })
201 .filter(|use_path| !use_path.segments.is_empty()) 205 .filter(|use_path| !use_path.segments.is_empty())
202 .take(20) 206 .take(20)
diff --git a/crates/assists/src/handlers/convert_integer_literal.rs b/crates/assists/src/handlers/convert_integer_literal.rs
new file mode 100644
index 000000000..ea35e833a
--- /dev/null
+++ b/crates/assists/src/handlers/convert_integer_literal.rs
@@ -0,0 +1,701 @@
1use syntax::{ast, AstNode, SmolStr};
2
3use crate::{AssistContext, AssistId, AssistKind, Assists, GroupLabel};
4
5// Assist: convert_integer_literal
6//
7// Converts the base of integer literals to other bases.
8//
9// ```
10// const _: i32 = 10<|>;
11// ```
12// ->
13// ```
14// const _: i32 = 0b1010;
15// ```
16pub(crate) fn convert_integer_literal(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
17 let literal = ctx.find_node_at_offset::<ast::Literal>()?;
18 let range = literal.syntax().text_range();
19 let group_id = GroupLabel("Convert integer base".into());
20
21 let suffix = match literal.kind() {
22 ast::LiteralKind::IntNumber { suffix } => suffix,
23 _ => return None,
24 };
25 let suffix_len = suffix.as_ref().map(|s| s.len()).unwrap_or(0);
26 let raw_literal_text = literal.syntax().to_string();
27
28 // Gets the literal's text without the type suffix and without underscores.
29 let literal_text = raw_literal_text
30 .chars()
31 .take(raw_literal_text.len() - suffix_len)
32 .filter(|c| *c != '_')
33 .collect::<SmolStr>();
34 let literal_base = IntegerLiteralBase::identify(&literal_text)?;
35
36 for base in IntegerLiteralBase::bases() {
37 if *base == literal_base {
38 continue;
39 }
40
41 let mut converted = literal_base.convert(&literal_text, base);
42
43 let label = if let Some(suffix) = &suffix {
44 format!("Convert {} ({}) to {}", &literal_text, suffix, &converted)
45 } else {
46 format!("Convert {} to {}", &literal_text, &converted)
47 };
48
49 // Appends the type suffix back into the new literal if it exists.
50 if let Some(suffix) = &suffix {
51 converted.push_str(&suffix);
52 }
53
54 acc.add_group(
55 &group_id,
56 AssistId("convert_integer_literal", AssistKind::RefactorInline),
57 label,
58 range,
59 |builder| builder.replace(range, converted),
60 );
61 }
62
63 Some(())
64}
65
66#[derive(Debug, PartialEq, Eq)]
67enum IntegerLiteralBase {
68 Binary,
69 Octal,
70 Decimal,
71 Hexadecimal,
72}
73
74impl IntegerLiteralBase {
75 fn identify(literal_text: &str) -> Option<Self> {
76 // We cannot express a literal in anything other than decimal in under 3 characters, so we return here if possible.
77 if literal_text.len() < 3 && literal_text.chars().all(|c| c.is_digit(10)) {
78 return Some(Self::Decimal);
79 }
80
81 let base = match &literal_text[..2] {
82 "0b" => Self::Binary,
83 "0o" => Self::Octal,
84 "0x" => Self::Hexadecimal,
85 _ => Self::Decimal,
86 };
87
88 // Checks that all characters after the base prefix are all valid digits for that base.
89 if literal_text[base.prefix_len()..].chars().all(|c| c.is_digit(base.base())) {
90 Some(base)
91 } else {
92 None
93 }
94 }
95
96 fn convert(&self, literal_text: &str, to: &IntegerLiteralBase) -> String {
97 let digits = &literal_text[self.prefix_len()..];
98 let value = u128::from_str_radix(digits, self.base()).unwrap();
99
100 match to {
101 Self::Binary => format!("0b{:b}", value),
102 Self::Octal => format!("0o{:o}", value),
103 Self::Decimal => value.to_string(),
104 Self::Hexadecimal => format!("0x{:X}", value),
105 }
106 }
107
108 const fn base(&self) -> u32 {
109 match self {
110 Self::Binary => 2,
111 Self::Octal => 8,
112 Self::Decimal => 10,
113 Self::Hexadecimal => 16,
114 }
115 }
116
117 const fn prefix_len(&self) -> usize {
118 match self {
119 Self::Decimal => 0,
120 _ => 2,
121 }
122 }
123
124 const fn bases() -> &'static [IntegerLiteralBase] {
125 &[
126 IntegerLiteralBase::Binary,
127 IntegerLiteralBase::Octal,
128 IntegerLiteralBase::Decimal,
129 IntegerLiteralBase::Hexadecimal,
130 ]
131 }
132}
133
134#[cfg(test)]
135mod tests {
136
137 use super::*;
138 use crate::tests::{check_assist_by_label, check_assist_target};
139
140 #[test]
141 fn binary_target() {
142 check_assist_target(convert_integer_literal, "const _: i32 = 0b1010<|>;", "0b1010");
143 }
144
145 #[test]
146 fn octal_target() {
147 check_assist_target(convert_integer_literal, "const _: i32 = 0o12<|>;", "0o12");
148 }
149
150 #[test]
151 fn decimal_target() {
152 check_assist_target(convert_integer_literal, "const _: i32 = 10<|>;", "10");
153 }
154
155 #[test]
156 fn hexadecimal_target() {
157 check_assist_target(convert_integer_literal, "const _: i32 = 0xA<|>;", "0xA");
158 }
159
160 #[test]
161 fn binary_target_with_underscores() {
162 check_assist_target(convert_integer_literal, "const _: i32 = 0b10_10<|>;", "0b10_10");
163 }
164
165 #[test]
166 fn octal_target_with_underscores() {
167 check_assist_target(convert_integer_literal, "const _: i32 = 0o1_2<|>;", "0o1_2");
168 }
169
170 #[test]
171 fn decimal_target_with_underscores() {
172 check_assist_target(convert_integer_literal, "const _: i32 = 1_0<|>;", "1_0");
173 }
174
175 #[test]
176 fn hexadecimal_target_with_underscores() {
177 check_assist_target(convert_integer_literal, "const _: i32 = 0x_A<|>;", "0x_A");
178 }
179
180 #[test]
181 fn convert_decimal_integer() {
182 let before = "const _: i32 = 1000<|>;";
183
184 check_assist_by_label(
185 convert_integer_literal,
186 before,
187 "const _: i32 = 0b1111101000;",
188 "Convert 1000 to 0b1111101000",
189 );
190
191 check_assist_by_label(
192 convert_integer_literal,
193 before,
194 "const _: i32 = 0o1750;",
195 "Convert 1000 to 0o1750",
196 );
197
198 check_assist_by_label(
199 convert_integer_literal,
200 before,
201 "const _: i32 = 0x3E8;",
202 "Convert 1000 to 0x3E8",
203 );
204 }
205
206 // Decimal numbers under 3 digits have a special case where they return early because we can't fit a
207 // other base's prefix, so we have a separate test for that.
208 #[test]
209 fn convert_small_decimal_integer() {
210 let before = "const _: i32 = 10<|>;";
211
212 check_assist_by_label(
213 convert_integer_literal,
214 before,
215 "const _: i32 = 0b1010;",
216 "Convert 10 to 0b1010",
217 );
218
219 check_assist_by_label(
220 convert_integer_literal,
221 before,
222 "const _: i32 = 0o12;",
223 "Convert 10 to 0o12",
224 );
225
226 check_assist_by_label(
227 convert_integer_literal,
228 before,
229 "const _: i32 = 0xA;",
230 "Convert 10 to 0xA",
231 );
232 }
233
234 #[test]
235 fn convert_hexadecimal_integer() {
236 let before = "const _: i32 = 0xFF<|>;";
237
238 check_assist_by_label(
239 convert_integer_literal,
240 before,
241 "const _: i32 = 0b11111111;",
242 "Convert 0xFF to 0b11111111",
243 );
244
245 check_assist_by_label(
246 convert_integer_literal,
247 before,
248 "const _: i32 = 0o377;",
249 "Convert 0xFF to 0o377",
250 );
251
252 check_assist_by_label(
253 convert_integer_literal,
254 before,
255 "const _: i32 = 255;",
256 "Convert 0xFF to 255",
257 );
258 }
259
260 #[test]
261 fn convert_binary_integer() {
262 let before = "const _: i32 = 0b11111111<|>;";
263
264 check_assist_by_label(
265 convert_integer_literal,
266 before,
267 "const _: i32 = 0o377;",
268 "Convert 0b11111111 to 0o377",
269 );
270
271 check_assist_by_label(
272 convert_integer_literal,
273 before,
274 "const _: i32 = 255;",
275 "Convert 0b11111111 to 255",
276 );
277
278 check_assist_by_label(
279 convert_integer_literal,
280 before,
281 "const _: i32 = 0xFF;",
282 "Convert 0b11111111 to 0xFF",
283 );
284 }
285
286 #[test]
287 fn convert_octal_integer() {
288 let before = "const _: i32 = 0o377<|>;";
289
290 check_assist_by_label(
291 convert_integer_literal,
292 before,
293 "const _: i32 = 0b11111111;",
294 "Convert 0o377 to 0b11111111",
295 );
296
297 check_assist_by_label(
298 convert_integer_literal,
299 before,
300 "const _: i32 = 255;",
301 "Convert 0o377 to 255",
302 );
303
304 check_assist_by_label(
305 convert_integer_literal,
306 before,
307 "const _: i32 = 0xFF;",
308 "Convert 0o377 to 0xFF",
309 );
310 }
311
312 #[test]
313 fn convert_decimal_integer_with_underscores() {
314 let before = "const _: i32 = 1_00_0<|>;";
315
316 check_assist_by_label(
317 convert_integer_literal,
318 before,
319 "const _: i32 = 0b1111101000;",
320 "Convert 1000 to 0b1111101000",
321 );
322
323 check_assist_by_label(
324 convert_integer_literal,
325 before,
326 "const _: i32 = 0o1750;",
327 "Convert 1000 to 0o1750",
328 );
329
330 check_assist_by_label(
331 convert_integer_literal,
332 before,
333 "const _: i32 = 0x3E8;",
334 "Convert 1000 to 0x3E8",
335 );
336 }
337
338 #[test]
339 fn convert_small_decimal_integer_with_underscores() {
340 let before = "const _: i32 = 1_0<|>;";
341
342 check_assist_by_label(
343 convert_integer_literal,
344 before,
345 "const _: i32 = 0b1010;",
346 "Convert 10 to 0b1010",
347 );
348
349 check_assist_by_label(
350 convert_integer_literal,
351 before,
352 "const _: i32 = 0o12;",
353 "Convert 10 to 0o12",
354 );
355
356 check_assist_by_label(
357 convert_integer_literal,
358 before,
359 "const _: i32 = 0xA;",
360 "Convert 10 to 0xA",
361 );
362 }
363
364 #[test]
365 fn convert_hexadecimal_integer_with_underscores() {
366 let before = "const _: i32 = 0x_F_F<|>;";
367
368 check_assist_by_label(
369 convert_integer_literal,
370 before,
371 "const _: i32 = 0b11111111;",
372 "Convert 0xFF to 0b11111111",
373 );
374
375 check_assist_by_label(
376 convert_integer_literal,
377 before,
378 "const _: i32 = 0o377;",
379 "Convert 0xFF to 0o377",
380 );
381
382 check_assist_by_label(
383 convert_integer_literal,
384 before,
385 "const _: i32 = 255;",
386 "Convert 0xFF to 255",
387 );
388 }
389
390 #[test]
391 fn convert_binary_integer_with_underscores() {
392 let before = "const _: i32 = 0b1111_1111<|>;";
393
394 check_assist_by_label(
395 convert_integer_literal,
396 before,
397 "const _: i32 = 0o377;",
398 "Convert 0b11111111 to 0o377",
399 );
400
401 check_assist_by_label(
402 convert_integer_literal,
403 before,
404 "const _: i32 = 255;",
405 "Convert 0b11111111 to 255",
406 );
407
408 check_assist_by_label(
409 convert_integer_literal,
410 before,
411 "const _: i32 = 0xFF;",
412 "Convert 0b11111111 to 0xFF",
413 );
414 }
415
416 #[test]
417 fn convert_octal_integer_with_underscores() {
418 let before = "const _: i32 = 0o3_77<|>;";
419
420 check_assist_by_label(
421 convert_integer_literal,
422 before,
423 "const _: i32 = 0b11111111;",
424 "Convert 0o377 to 0b11111111",
425 );
426
427 check_assist_by_label(
428 convert_integer_literal,
429 before,
430 "const _: i32 = 255;",
431 "Convert 0o377 to 255",
432 );
433
434 check_assist_by_label(
435 convert_integer_literal,
436 before,
437 "const _: i32 = 0xFF;",
438 "Convert 0o377 to 0xFF",
439 );
440 }
441
442 #[test]
443 fn convert_decimal_integer_with_suffix() {
444 let before = "const _: i32 = 1000i32<|>;";
445
446 check_assist_by_label(
447 convert_integer_literal,
448 before,
449 "const _: i32 = 0b1111101000i32;",
450 "Convert 1000 (i32) to 0b1111101000",
451 );
452
453 check_assist_by_label(
454 convert_integer_literal,
455 before,
456 "const _: i32 = 0o1750i32;",
457 "Convert 1000 (i32) to 0o1750",
458 );
459
460 check_assist_by_label(
461 convert_integer_literal,
462 before,
463 "const _: i32 = 0x3E8i32;",
464 "Convert 1000 (i32) to 0x3E8",
465 );
466 }
467
468 #[test]
469 fn convert_small_decimal_integer_with_suffix() {
470 let before = "const _: i32 = 10i32<|>;";
471
472 check_assist_by_label(
473 convert_integer_literal,
474 before,
475 "const _: i32 = 0b1010i32;",
476 "Convert 10 (i32) to 0b1010",
477 );
478
479 check_assist_by_label(
480 convert_integer_literal,
481 before,
482 "const _: i32 = 0o12i32;",
483 "Convert 10 (i32) to 0o12",
484 );
485
486 check_assist_by_label(
487 convert_integer_literal,
488 before,
489 "const _: i32 = 0xAi32;",
490 "Convert 10 (i32) to 0xA",
491 );
492 }
493
494 #[test]
495 fn convert_hexadecimal_integer_with_suffix() {
496 let before = "const _: i32 = 0xFFi32<|>;";
497
498 check_assist_by_label(
499 convert_integer_literal,
500 before,
501 "const _: i32 = 0b11111111i32;",
502 "Convert 0xFF (i32) to 0b11111111",
503 );
504
505 check_assist_by_label(
506 convert_integer_literal,
507 before,
508 "const _: i32 = 0o377i32;",
509 "Convert 0xFF (i32) to 0o377",
510 );
511
512 check_assist_by_label(
513 convert_integer_literal,
514 before,
515 "const _: i32 = 255i32;",
516 "Convert 0xFF (i32) to 255",
517 );
518 }
519
520 #[test]
521 fn convert_binary_integer_with_suffix() {
522 let before = "const _: i32 = 0b11111111i32<|>;";
523
524 check_assist_by_label(
525 convert_integer_literal,
526 before,
527 "const _: i32 = 0o377i32;",
528 "Convert 0b11111111 (i32) to 0o377",
529 );
530
531 check_assist_by_label(
532 convert_integer_literal,
533 before,
534 "const _: i32 = 255i32;",
535 "Convert 0b11111111 (i32) to 255",
536 );
537
538 check_assist_by_label(
539 convert_integer_literal,
540 before,
541 "const _: i32 = 0xFFi32;",
542 "Convert 0b11111111 (i32) to 0xFF",
543 );
544 }
545
546 #[test]
547 fn convert_octal_integer_with_suffix() {
548 let before = "const _: i32 = 0o377i32<|>;";
549
550 check_assist_by_label(
551 convert_integer_literal,
552 before,
553 "const _: i32 = 0b11111111i32;",
554 "Convert 0o377 (i32) to 0b11111111",
555 );
556
557 check_assist_by_label(
558 convert_integer_literal,
559 before,
560 "const _: i32 = 255i32;",
561 "Convert 0o377 (i32) to 255",
562 );
563
564 check_assist_by_label(
565 convert_integer_literal,
566 before,
567 "const _: i32 = 0xFFi32;",
568 "Convert 0o377 (i32) to 0xFF",
569 );
570 }
571
572 #[test]
573 fn convert_decimal_integer_with_underscores_and_suffix() {
574 let before = "const _: i32 = 1_00_0i32<|>;";
575
576 check_assist_by_label(
577 convert_integer_literal,
578 before,
579 "const _: i32 = 0b1111101000i32;",
580 "Convert 1000 (i32) to 0b1111101000",
581 );
582
583 check_assist_by_label(
584 convert_integer_literal,
585 before,
586 "const _: i32 = 0o1750i32;",
587 "Convert 1000 (i32) to 0o1750",
588 );
589
590 check_assist_by_label(
591 convert_integer_literal,
592 before,
593 "const _: i32 = 0x3E8i32;",
594 "Convert 1000 (i32) to 0x3E8",
595 );
596 }
597
598 #[test]
599 fn convert_small_decimal_integer_with_underscores_and_suffix() {
600 let before = "const _: i32 = 1_0i32<|>;";
601
602 check_assist_by_label(
603 convert_integer_literal,
604 before,
605 "const _: i32 = 0b1010i32;",
606 "Convert 10 (i32) to 0b1010",
607 );
608
609 check_assist_by_label(
610 convert_integer_literal,
611 before,
612 "const _: i32 = 0o12i32;",
613 "Convert 10 (i32) to 0o12",
614 );
615
616 check_assist_by_label(
617 convert_integer_literal,
618 before,
619 "const _: i32 = 0xAi32;",
620 "Convert 10 (i32) to 0xA",
621 );
622 }
623
624 #[test]
625 fn convert_hexadecimal_integer_with_underscores_and_suffix() {
626 let before = "const _: i32 = 0x_F_Fi32<|>;";
627
628 check_assist_by_label(
629 convert_integer_literal,
630 before,
631 "const _: i32 = 0b11111111i32;",
632 "Convert 0xFF (i32) to 0b11111111",
633 );
634
635 check_assist_by_label(
636 convert_integer_literal,
637 before,
638 "const _: i32 = 0o377i32;",
639 "Convert 0xFF (i32) to 0o377",
640 );
641
642 check_assist_by_label(
643 convert_integer_literal,
644 before,
645 "const _: i32 = 255i32;",
646 "Convert 0xFF (i32) to 255",
647 );
648 }
649
650 #[test]
651 fn convert_binary_integer_with_underscores_and_suffix() {
652 let before = "const _: i32 = 0b1111_1111i32<|>;";
653
654 check_assist_by_label(
655 convert_integer_literal,
656 before,
657 "const _: i32 = 0o377i32;",
658 "Convert 0b11111111 (i32) to 0o377",
659 );
660
661 check_assist_by_label(
662 convert_integer_literal,
663 before,
664 "const _: i32 = 255i32;",
665 "Convert 0b11111111 (i32) to 255",
666 );
667
668 check_assist_by_label(
669 convert_integer_literal,
670 before,
671 "const _: i32 = 0xFFi32;",
672 "Convert 0b11111111 (i32) to 0xFF",
673 );
674 }
675
676 #[test]
677 fn convert_octal_integer_with_underscores_and_suffix() {
678 let before = "const _: i32 = 0o3_77i32<|>;";
679
680 check_assist_by_label(
681 convert_integer_literal,
682 before,
683 "const _: i32 = 0b11111111i32;",
684 "Convert 0o377 (i32) to 0b11111111",
685 );
686
687 check_assist_by_label(
688 convert_integer_literal,
689 before,
690 "const _: i32 = 255i32;",
691 "Convert 0o377 (i32) to 255",
692 );
693
694 check_assist_by_label(
695 convert_integer_literal,
696 before,
697 "const _: i32 = 0xFFi32;",
698 "Convert 0o377 (i32) to 0xFF",
699 );
700 }
701}
diff --git a/crates/assists/src/handlers/remove_dbg.rs b/crates/assists/src/handlers/remove_dbg.rs
index a8ab2aecc..e10616779 100644
--- a/crates/assists/src/handlers/remove_dbg.rs
+++ b/crates/assists/src/handlers/remove_dbg.rs
@@ -1,6 +1,6 @@
1use syntax::{ 1use syntax::{
2 ast::{self, AstNode}, 2 ast::{self, AstNode},
3 SyntaxElement, TextRange, TextSize, T, 3 SyntaxElement, SyntaxKind, TextRange, TextSize, T,
4}; 4};
5 5
6use crate::{AssistContext, AssistId, AssistKind, Assists}; 6use crate::{AssistContext, AssistId, AssistKind, Assists};
@@ -117,7 +117,10 @@ fn needs_parentheses_around_macro_contents(macro_contents: Vec<SyntaxElement>) -
117 } 117 }
118 symbol_kind => { 118 symbol_kind => {
119 let symbol_not_in_bracket = unpaired_brackets_in_contents.is_empty(); 119 let symbol_not_in_bracket = unpaired_brackets_in_contents.is_empty();
120 if symbol_not_in_bracket && symbol_kind.is_punct() { 120 if symbol_not_in_bracket
121 && symbol_kind != SyntaxKind::COLON
122 && symbol_kind.is_punct()
123 {
121 return true; 124 return true;
122 } 125 }
123 } 126 }
@@ -159,6 +162,8 @@ fn foo(n: usize) {
159} 162}
160", 163",
161 ); 164 );
165
166 check_assist(remove_dbg, "<|>dbg!(Foo::foo_test()).bar()", "Foo::foo_test().bar()");
162 } 167 }
163 168
164 #[test] 169 #[test]
diff --git a/crates/assists/src/lib.rs b/crates/assists/src/lib.rs
index cbac53e71..a2bec818c 100644
--- a/crates/assists/src/lib.rs
+++ b/crates/assists/src/lib.rs
@@ -128,6 +128,7 @@ mod handlers {
128 mod auto_import; 128 mod auto_import;
129 mod change_return_type_to_result; 129 mod change_return_type_to_result;
130 mod change_visibility; 130 mod change_visibility;
131 mod convert_integer_literal;
131 mod early_return; 132 mod early_return;
132 mod expand_glob_import; 133 mod expand_glob_import;
133 mod extract_struct_from_enum_variant; 134 mod extract_struct_from_enum_variant;
@@ -172,6 +173,7 @@ mod handlers {
172 auto_import::auto_import, 173 auto_import::auto_import,
173 change_return_type_to_result::change_return_type_to_result, 174 change_return_type_to_result::change_return_type_to_result,
174 change_visibility::change_visibility, 175 change_visibility::change_visibility,
176 convert_integer_literal::convert_integer_literal,
175 early_return::convert_to_guarded_return, 177 early_return::convert_to_guarded_return,
176 expand_glob_import::expand_glob_import, 178 expand_glob_import::expand_glob_import,
177 extract_struct_from_enum_variant::extract_struct_from_enum_variant, 179 extract_struct_from_enum_variant::extract_struct_from_enum_variant,
diff --git a/crates/assists/src/tests.rs b/crates/assists/src/tests.rs
index ba1fb543b..2b687decf 100644
--- a/crates/assists/src/tests.rs
+++ b/crates/assists/src/tests.rs
@@ -15,18 +15,30 @@ pub(crate) fn with_single_file(text: &str) -> (RootDatabase, FileId) {
15 15
16pub(crate) fn check_assist(assist: Handler, ra_fixture_before: &str, ra_fixture_after: &str) { 16pub(crate) fn check_assist(assist: Handler, ra_fixture_before: &str, ra_fixture_after: &str) {
17 let ra_fixture_after = trim_indent(ra_fixture_after); 17 let ra_fixture_after = trim_indent(ra_fixture_after);
18 check(assist, ra_fixture_before, ExpectedResult::After(&ra_fixture_after)); 18 check(assist, ra_fixture_before, ExpectedResult::After(&ra_fixture_after), None);
19}
20
21// There is no way to choose what assist within a group you want to test against,
22// so this is here to allow you choose.
23pub(crate) fn check_assist_by_label(
24 assist: Handler,
25 ra_fixture_before: &str,
26 ra_fixture_after: &str,
27 label: &str,
28) {
29 let ra_fixture_after = trim_indent(ra_fixture_after);
30 check(assist, ra_fixture_before, ExpectedResult::After(&ra_fixture_after), Some(label));
19} 31}
20 32
21// FIXME: instead of having a separate function here, maybe use 33// FIXME: instead of having a separate function here, maybe use
22// `extract_ranges` and mark the target as `<target> </target>` in the 34// `extract_ranges` and mark the target as `<target> </target>` in the
23// fixture? 35// fixture?
24pub(crate) fn check_assist_target(assist: Handler, ra_fixture: &str, target: &str) { 36pub(crate) fn check_assist_target(assist: Handler, ra_fixture: &str, target: &str) {
25 check(assist, ra_fixture, ExpectedResult::Target(target)); 37 check(assist, ra_fixture, ExpectedResult::Target(target), None);
26} 38}
27 39
28pub(crate) fn check_assist_not_applicable(assist: Handler, ra_fixture: &str) { 40pub(crate) fn check_assist_not_applicable(assist: Handler, ra_fixture: &str) {
29 check(assist, ra_fixture, ExpectedResult::NotApplicable); 41 check(assist, ra_fixture, ExpectedResult::NotApplicable, None);
30} 42}
31 43
32fn check_doc_test(assist_id: &str, before: &str, after: &str) { 44fn check_doc_test(assist_id: &str, before: &str, after: &str) {
@@ -65,7 +77,7 @@ enum ExpectedResult<'a> {
65 Target(&'a str), 77 Target(&'a str),
66} 78}
67 79
68fn check(handler: Handler, before: &str, expected: ExpectedResult) { 80fn check(handler: Handler, before: &str, expected: ExpectedResult, assist_label: Option<&str>) {
69 let (db, file_with_caret_id, range_or_offset) = RootDatabase::with_range_or_offset(before); 81 let (db, file_with_caret_id, range_or_offset) = RootDatabase::with_range_or_offset(before);
70 let text_without_caret = db.file_text(file_with_caret_id).to_string(); 82 let text_without_caret = db.file_text(file_with_caret_id).to_string();
71 83
@@ -77,7 +89,12 @@ fn check(handler: Handler, before: &str, expected: ExpectedResult) {
77 let mut acc = Assists::new_resolved(&ctx); 89 let mut acc = Assists::new_resolved(&ctx);
78 handler(&mut acc, &ctx); 90 handler(&mut acc, &ctx);
79 let mut res = acc.finish_resolved(); 91 let mut res = acc.finish_resolved();
80 let assist = res.pop(); 92
93 let assist = match assist_label {
94 Some(label) => res.into_iter().find(|resolved| resolved.assist.label == label),
95 None => res.pop(),
96 };
97
81 match (assist, expected) { 98 match (assist, expected) {
82 (Some(assist), ExpectedResult::After(after)) => { 99 (Some(assist), ExpectedResult::After(after)) => {
83 let mut source_change = assist.source_change; 100 let mut source_change = assist.source_change;
diff --git a/crates/assists/src/tests/generated.rs b/crates/assists/src/tests/generated.rs
index 27d15adb0..7f6e98a54 100644
--- a/crates/assists/src/tests/generated.rs
+++ b/crates/assists/src/tests/generated.rs
@@ -204,6 +204,19 @@ pub(crate) fn frobnicate() {}
204} 204}
205 205
206#[test] 206#[test]
207fn doctest_convert_integer_literal() {
208 check_doc_test(
209 "convert_integer_literal",
210 r#####"
211const _: i32 = 10<|>;
212"#####,
213 r#####"
214const _: i32 = 0b1010;
215"#####,
216 )
217}
218
219#[test]
207fn doctest_convert_to_guarded_return() { 220fn doctest_convert_to_guarded_return() {
208 check_doc_test( 221 check_doc_test(
209 "convert_to_guarded_return", 222 "convert_to_guarded_return",
diff --git a/crates/hir/src/code_model.rs b/crates/hir/src/code_model.rs
index a19b0fad7..c75d46bff 100644
--- a/crates/hir/src/code_model.rs
+++ b/crates/hir/src/code_model.rs
@@ -4,6 +4,7 @@ use std::{iter, sync::Arc};
4use arrayvec::ArrayVec; 4use arrayvec::ArrayVec;
5use base_db::{CrateId, CrateName, Edition, FileId}; 5use base_db::{CrateId, CrateName, Edition, FileId};
6use either::Either; 6use either::Either;
7use hir_def::find_path::PrefixKind;
7use hir_def::{ 8use hir_def::{
8 adt::ReprKind, 9 adt::ReprKind,
9 adt::StructKind, 10 adt::StructKind,
@@ -390,8 +391,9 @@ impl Module {
390 self, 391 self,
391 db: &dyn DefDatabase, 392 db: &dyn DefDatabase,
392 item: impl Into<ItemInNs>, 393 item: impl Into<ItemInNs>,
394 prefix_kind: PrefixKind,
393 ) -> Option<ModPath> { 395 ) -> Option<ModPath> {
394 hir_def::find_path::find_path_prefixed(db, item.into(), self.into()) 396 hir_def::find_path::find_path_prefixed(db, item.into(), self.into(), prefix_kind)
395 } 397 }
396} 398}
397 399
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index b9d9c7e25..87084fa13 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -48,6 +48,7 @@ pub use hir_def::{
48 body::scope::ExprScopes, 48 body::scope::ExprScopes,
49 builtin_type::BuiltinType, 49 builtin_type::BuiltinType,
50 docs::Documentation, 50 docs::Documentation,
51 find_path::PrefixKind,
51 item_scope::ItemInNs, 52 item_scope::ItemInNs,
52 nameres::ModuleSource, 53 nameres::ModuleSource,
53 path::ModPath, 54 path::ModPath,
diff --git a/crates/hir_def/src/find_path.rs b/crates/hir_def/src/find_path.rs
index baf374144..9106ed45f 100644
--- a/crates/hir_def/src/find_path.rs
+++ b/crates/hir_def/src/find_path.rs
@@ -19,12 +19,17 @@ use crate::{
19/// *from where* you're referring to the item, hence the `from` parameter. 19/// *from where* you're referring to the item, hence the `from` parameter.
20pub fn find_path(db: &dyn DefDatabase, item: ItemInNs, from: ModuleId) -> Option<ModPath> { 20pub fn find_path(db: &dyn DefDatabase, item: ItemInNs, from: ModuleId) -> Option<ModPath> {
21 let _p = profile::span("find_path"); 21 let _p = profile::span("find_path");
22 find_path_inner(db, item, from, MAX_PATH_LEN, Prefixed::Not) 22 find_path_inner(db, item, from, MAX_PATH_LEN, None)
23} 23}
24 24
25pub fn find_path_prefixed(db: &dyn DefDatabase, item: ItemInNs, from: ModuleId) -> Option<ModPath> { 25pub fn find_path_prefixed(
26 let _p = profile::span("find_path_absolute"); 26 db: &dyn DefDatabase,
27 find_path_inner(db, item, from, MAX_PATH_LEN, Prefixed::Plain) 27 item: ItemInNs,
28 from: ModuleId,
29 prefix_kind: PrefixKind,
30) -> Option<ModPath> {
31 let _p = profile::span("find_path_prefixed");
32 find_path_inner(db, item, from, MAX_PATH_LEN, Some(prefix_kind))
28} 33}
29 34
30const MAX_PATH_LEN: usize = 15; 35const MAX_PATH_LEN: usize = 15;
@@ -42,58 +47,52 @@ impl ModPath {
42 } 47 }
43} 48}
44 49
45fn check_crate_self_super( 50fn check_self_super(def_map: &CrateDefMap, item: ItemInNs, from: ModuleId) -> Option<ModPath> {
46 def_map: &CrateDefMap, 51 if item == ItemInNs::Types(from.into()) {
47 item: ItemInNs,
48 from: ModuleId,
49) -> Option<ModPath> {
50 // - if the item is the crate root, return `crate`
51 if item
52 == ItemInNs::Types(ModuleDefId::ModuleId(ModuleId {
53 krate: from.krate,
54 local_id: def_map.root,
55 }))
56 {
57 Some(ModPath::from_segments(PathKind::Crate, Vec::new()))
58 } else if item == ItemInNs::Types(from.into()) {
59 // - if the item is the module we're in, use `self` 52 // - if the item is the module we're in, use `self`
60 Some(ModPath::from_segments(PathKind::Super(0), Vec::new())) 53 Some(ModPath::from_segments(PathKind::Super(0), Vec::new()))
61 } else { 54 } else if let Some(parent_id) = def_map.modules[from.local_id].parent {
62 if let Some(parent_id) = def_map.modules[from.local_id].parent { 55 // - if the item is the parent module, use `super` (this is not used recursively, since `super::super` is ugly)
63 // - if the item is the parent module, use `super` (this is not used recursively, since `super::super` is ugly) 56 if item
64 if item 57 == ItemInNs::Types(ModuleDefId::ModuleId(ModuleId {
65 == ItemInNs::Types(ModuleDefId::ModuleId(ModuleId { 58 krate: from.krate,
66 krate: from.krate, 59 local_id: parent_id,
67 local_id: parent_id, 60 }))
68 })) 61 {
69 { 62 Some(ModPath::from_segments(PathKind::Super(1), Vec::new()))
70 return Some(ModPath::from_segments(PathKind::Super(1), Vec::new())); 63 } else {
71 } 64 None
72 } 65 }
66 } else {
73 None 67 None
74 } 68 }
75} 69}
76 70
77#[derive(Copy, Clone, PartialEq, Eq)] 71#[derive(Copy, Clone, Debug, PartialEq, Eq)]
78pub enum Prefixed { 72pub enum PrefixKind {
79 Not, 73 /// Causes paths to always start with either `self`, `super`, `crate` or a crate-name.
74 /// This is the same as plain, just that paths will start with `self` iprepended f the path
75 /// starts with an identifier that is not a crate.
80 BySelf, 76 BySelf,
77 /// Causes paths to ignore imports in the local module.
81 Plain, 78 Plain,
79 /// Causes paths to start with `crate` where applicable, effectively forcing paths to be absolute.
80 ByCrate,
82} 81}
83 82
84impl Prefixed { 83impl PrefixKind {
85 #[inline] 84 #[inline]
86 fn prefix(self) -> Option<PathKind> { 85 fn prefix(self) -> PathKind {
87 match self { 86 match self {
88 Prefixed::Not => None, 87 PrefixKind::BySelf => PathKind::Super(0),
89 Prefixed::BySelf => Some(PathKind::Super(0)), 88 PrefixKind::Plain => PathKind::Plain,
90 Prefixed::Plain => Some(PathKind::Plain), 89 PrefixKind::ByCrate => PathKind::Crate,
91 } 90 }
92 } 91 }
93 92
94 #[inline] 93 #[inline]
95 fn prefixed(self) -> bool { 94 fn is_absolute(&self) -> bool {
96 self != Prefixed::Not 95 self == &PrefixKind::ByCrate
97 } 96 }
98} 97}
99 98
@@ -102,7 +101,7 @@ fn find_path_inner(
102 item: ItemInNs, 101 item: ItemInNs,
103 from: ModuleId, 102 from: ModuleId,
104 max_len: usize, 103 max_len: usize,
105 prefixed: Prefixed, 104 prefixed: Option<PrefixKind>,
106) -> Option<ModPath> { 105) -> Option<ModPath> {
107 if max_len == 0 { 106 if max_len == 0 {
108 return None; 107 return None;
@@ -115,13 +114,25 @@ fn find_path_inner(
115 let from_scope: &crate::item_scope::ItemScope = &def_map.modules[from.local_id].scope; 114 let from_scope: &crate::item_scope::ItemScope = &def_map.modules[from.local_id].scope;
116 let scope_name = 115 let scope_name =
117 if let Some((name, _)) = from_scope.name_of(item) { Some(name.clone()) } else { None }; 116 if let Some((name, _)) = from_scope.name_of(item) { Some(name.clone()) } else { None };
118 if !prefixed.prefixed() && scope_name.is_some() { 117 if prefixed.is_none() && scope_name.is_some() {
119 return scope_name 118 return scope_name
120 .map(|scope_name| ModPath::from_segments(PathKind::Plain, vec![scope_name])); 119 .map(|scope_name| ModPath::from_segments(PathKind::Plain, vec![scope_name]));
121 } 120 }
122 121
123 if let modpath @ Some(_) = check_crate_self_super(&def_map, item, from) { 122 // - if the item is the crate root, return `crate`
124 return modpath; 123 if item
124 == ItemInNs::Types(ModuleDefId::ModuleId(ModuleId {
125 krate: from.krate,
126 local_id: def_map.root,
127 }))
128 {
129 return Some(ModPath::from_segments(PathKind::Crate, Vec::new()));
130 }
131
132 if prefixed.filter(PrefixKind::is_absolute).is_none() {
133 if let modpath @ Some(_) = check_self_super(&def_map, item, from) {
134 return modpath;
135 }
125 } 136 }
126 137
127 // - if the item is the crate root of a dependency crate, return the name from the extern prelude 138 // - if the item is the crate root of a dependency crate, return the name from the extern prelude
@@ -226,7 +237,7 @@ fn find_path_inner(
226 } 237 }
227 } 238 }
228 239
229 if let Some(prefix) = prefixed.prefix() { 240 if let Some(prefix) = prefixed.map(PrefixKind::prefix) {
230 best_path.or_else(|| { 241 best_path.or_else(|| {
231 scope_name.map(|scope_name| ModPath::from_segments(prefix, vec![scope_name])) 242 scope_name.map(|scope_name| ModPath::from_segments(prefix, vec![scope_name]))
232 }) 243 })
@@ -355,7 +366,7 @@ mod tests {
355 /// `code` needs to contain a cursor marker; checks that `find_path` for the 366 /// `code` needs to contain a cursor marker; checks that `find_path` for the
356 /// item the `path` refers to returns that same path when called from the 367 /// item the `path` refers to returns that same path when called from the
357 /// module the cursor is in. 368 /// module the cursor is in.
358 fn check_found_path_(ra_fixture: &str, path: &str, absolute: bool) { 369 fn check_found_path_(ra_fixture: &str, path: &str, prefix_kind: Option<PrefixKind>) {
359 let (db, pos) = TestDB::with_position(ra_fixture); 370 let (db, pos) = TestDB::with_position(ra_fixture);
360 let module = db.module_for_file(pos.file_id); 371 let module = db.module_for_file(pos.file_id);
361 let parsed_path_file = syntax::SourceFile::parse(&format!("use {};", path)); 372 let parsed_path_file = syntax::SourceFile::parse(&format!("use {};", path));
@@ -375,20 +386,22 @@ mod tests {
375 .take_types() 386 .take_types()
376 .unwrap(); 387 .unwrap();
377 388
378 let found_path = if absolute { find_path_prefixed } else { find_path }( 389 let found_path =
379 &db, 390 find_path_inner(&db, ItemInNs::Types(resolved), module, MAX_PATH_LEN, prefix_kind);
380 ItemInNs::Types(resolved), 391 assert_eq!(found_path, Some(mod_path), "{:?}", prefix_kind);
381 module,
382 );
383 assert_eq!(found_path, Some(mod_path), "absolute {}", absolute);
384 } 392 }
385 393
386 fn check_found_path(ra_fixture: &str, path: &str) { 394 fn check_found_path(
387 check_found_path_(ra_fixture, path, false); 395 ra_fixture: &str,
388 } 396 unprefixed: &str,
389 397 prefixed: &str,
390 fn check_found_path_abs(ra_fixture: &str, path: &str) { 398 absolute: &str,
391 check_found_path_(ra_fixture, path, true); 399 self_prefixed: &str,
400 ) {
401 check_found_path_(ra_fixture, unprefixed, None);
402 check_found_path_(ra_fixture, prefixed, Some(PrefixKind::Plain));
403 check_found_path_(ra_fixture, absolute, Some(PrefixKind::ByCrate));
404 check_found_path_(ra_fixture, self_prefixed, Some(PrefixKind::BySelf));
392 } 405 }
393 406
394 #[test] 407 #[test]
@@ -398,8 +411,7 @@ mod tests {
398 struct S; 411 struct S;
399 <|> 412 <|>
400 "#; 413 "#;
401 check_found_path(code, "S"); 414 check_found_path(code, "S", "S", "crate::S", "self::S");
402 check_found_path_abs(code, "S");
403 } 415 }
404 416
405 #[test] 417 #[test]
@@ -409,8 +421,7 @@ mod tests {
409 enum E { A } 421 enum E { A }
410 <|> 422 <|>
411 "#; 423 "#;
412 check_found_path(code, "E::A"); 424 check_found_path(code, "E::A", "E::A", "E::A", "E::A");
413 check_found_path_abs(code, "E::A");
414 } 425 }
415 426
416 #[test] 427 #[test]
@@ -422,8 +433,7 @@ mod tests {
422 } 433 }
423 <|> 434 <|>
424 "#; 435 "#;
425 check_found_path(code, "foo::S"); 436 check_found_path(code, "foo::S", "foo::S", "crate::foo::S", "self::foo::S");
426 check_found_path_abs(code, "foo::S");
427 } 437 }
428 438
429 #[test] 439 #[test]
@@ -437,8 +447,7 @@ mod tests {
437 //- /foo/bar.rs 447 //- /foo/bar.rs
438 <|> 448 <|>
439 "#; 449 "#;
440 check_found_path(code, "super::S"); 450 check_found_path(code, "super::S", "super::S", "crate::foo::S", "super::S");
441 check_found_path_abs(code, "super::S");
442 } 451 }
443 452
444 #[test] 453 #[test]
@@ -449,8 +458,7 @@ mod tests {
449 //- /foo.rs 458 //- /foo.rs
450 <|> 459 <|>
451 "#; 460 "#;
452 check_found_path(code, "self"); 461 check_found_path(code, "self", "self", "crate::foo", "self");
453 check_found_path_abs(code, "self");
454 } 462 }
455 463
456 #[test] 464 #[test]
@@ -461,8 +469,7 @@ mod tests {
461 //- /foo.rs 469 //- /foo.rs
462 <|> 470 <|>
463 "#; 471 "#;
464 check_found_path(code, "crate"); 472 check_found_path(code, "crate", "crate", "crate", "crate");
465 check_found_path_abs(code, "crate");
466 } 473 }
467 474
468 #[test] 475 #[test]
@@ -474,8 +481,7 @@ mod tests {
474 //- /foo.rs 481 //- /foo.rs
475 <|> 482 <|>
476 "#; 483 "#;
477 check_found_path(code, "crate::S"); 484 check_found_path(code, "crate::S", "crate::S", "crate::S", "crate::S");
478 check_found_path_abs(code, "crate::S");
479 } 485 }
480 486
481 #[test] 487 #[test]
@@ -486,8 +492,7 @@ mod tests {
486 //- /std.rs crate:std 492 //- /std.rs crate:std
487 pub struct S; 493 pub struct S;
488 "#; 494 "#;
489 check_found_path(code, "std::S"); 495 check_found_path(code, "std::S", "std::S", "std::S", "std::S");
490 check_found_path_abs(code, "std::S");
491 } 496 }
492 497
493 #[test] 498 #[test]
@@ -499,8 +504,13 @@ mod tests {
499 //- /std.rs crate:std 504 //- /std.rs crate:std
500 pub struct S; 505 pub struct S;
501 "#; 506 "#;
502 check_found_path(code, "std_renamed::S"); 507 check_found_path(
503 check_found_path_abs(code, "std_renamed::S"); 508 code,
509 "std_renamed::S",
510 "std_renamed::S",
511 "std_renamed::S",
512 "std_renamed::S",
513 );
504 } 514 }
505 515
506 #[test] 516 #[test]
@@ -520,8 +530,13 @@ mod tests {
520 } 530 }
521 } 531 }
522 "#; 532 "#;
523 check_found_path(code, "ast::ModuleItem"); 533 check_found_path(
524 check_found_path_abs(code, "syntax::ast::ModuleItem"); 534 code,
535 "ast::ModuleItem",
536 "syntax::ast::ModuleItem",
537 "syntax::ast::ModuleItem",
538 "syntax::ast::ModuleItem",
539 );
525 540
526 let code = r#" 541 let code = r#"
527 //- /main.rs crate:main deps:syntax 542 //- /main.rs crate:main deps:syntax
@@ -535,8 +550,13 @@ mod tests {
535 } 550 }
536 } 551 }
537 "#; 552 "#;
538 check_found_path(code, "syntax::ast::ModuleItem"); 553 check_found_path(
539 check_found_path_abs(code, "syntax::ast::ModuleItem"); 554 code,
555 "syntax::ast::ModuleItem",
556 "syntax::ast::ModuleItem",
557 "syntax::ast::ModuleItem",
558 "syntax::ast::ModuleItem",
559 );
540 } 560 }
541 561
542 #[test] 562 #[test]
@@ -549,8 +569,7 @@ mod tests {
549 } 569 }
550 <|> 570 <|>
551 "#; 571 "#;
552 check_found_path(code, "bar::S"); 572 check_found_path(code, "bar::S", "bar::S", "crate::bar::S", "self::bar::S");
553 check_found_path_abs(code, "bar::S");
554 } 573 }
555 574
556 #[test] 575 #[test]
@@ -563,8 +582,7 @@ mod tests {
563 } 582 }
564 <|> 583 <|>
565 "#; 584 "#;
566 check_found_path(code, "bar::U"); 585 check_found_path(code, "bar::U", "bar::U", "crate::bar::U", "self::bar::U");
567 check_found_path_abs(code, "bar::U");
568 } 586 }
569 587
570 #[test] 588 #[test]
@@ -577,8 +595,7 @@ mod tests {
577 //- /core.rs crate:core 595 //- /core.rs crate:core
578 pub struct S; 596 pub struct S;
579 "#; 597 "#;
580 check_found_path(code, "std::S"); 598 check_found_path(code, "std::S", "std::S", "std::S", "std::S");
581 check_found_path_abs(code, "std::S");
582 } 599 }
583 600
584 #[test] 601 #[test]
@@ -591,8 +608,7 @@ mod tests {
591 #[prelude_import] 608 #[prelude_import]
592 pub use prelude::*; 609 pub use prelude::*;
593 "#; 610 "#;
594 check_found_path(code, "S"); 611 check_found_path(code, "S", "S", "S", "S");
595 check_found_path_abs(code, "S");
596 } 612 }
597 613
598 #[test] 614 #[test]
@@ -608,10 +624,8 @@ mod tests {
608 #[prelude_import] 624 #[prelude_import]
609 pub use prelude::*; 625 pub use prelude::*;
610 "#; 626 "#;
611 check_found_path(code, "None"); 627 check_found_path(code, "None", "None", "None", "None");
612 check_found_path(code, "Some"); 628 check_found_path(code, "Some", "Some", "Some", "Some");
613 check_found_path_abs(code, "None");
614 check_found_path_abs(code, "Some");
615 } 629 }
616 630
617 #[test] 631 #[test]
@@ -627,8 +641,7 @@ mod tests {
627 //- /baz.rs 641 //- /baz.rs
628 pub use crate::foo::bar::S; 642 pub use crate::foo::bar::S;
629 "#; 643 "#;
630 check_found_path(code, "baz::S"); 644 check_found_path(code, "baz::S", "baz::S", "crate::baz::S", "self::baz::S");
631 check_found_path_abs(code, "baz::S");
632 } 645 }
633 646
634 #[test] 647 #[test]
@@ -642,8 +655,7 @@ mod tests {
642 <|> 655 <|>
643 "#; 656 "#;
644 // crate::S would be shorter, but using private imports seems wrong 657 // crate::S would be shorter, but using private imports seems wrong
645 check_found_path(code, "crate::bar::S"); 658 check_found_path(code, "crate::bar::S", "crate::bar::S", "crate::bar::S", "crate::bar::S");
646 check_found_path_abs(code, "crate::bar::S");
647 } 659 }
648 660
649 #[test] 661 #[test]
@@ -661,8 +673,7 @@ mod tests {
661 //- /baz.rs 673 //- /baz.rs
662 pub use super::foo; 674 pub use super::foo;
663 "#; 675 "#;
664 check_found_path(code, "crate::foo::S"); 676 check_found_path(code, "crate::foo::S", "crate::foo::S", "crate::foo::S", "crate::foo::S");
665 check_found_path_abs(code, "crate::foo::S");
666 } 677 }
667 678
668 #[test] 679 #[test]
@@ -682,8 +693,13 @@ mod tests {
682 pub struct Arc; 693 pub struct Arc;
683 } 694 }
684 "#; 695 "#;
685 check_found_path(code, "std::sync::Arc"); 696 check_found_path(
686 check_found_path_abs(code, "std::sync::Arc"); 697 code,
698 "std::sync::Arc",
699 "std::sync::Arc",
700 "std::sync::Arc",
701 "std::sync::Arc",
702 );
687 } 703 }
688 704
689 #[test] 705 #[test]
@@ -707,8 +723,13 @@ mod tests {
707 pub struct Error; 723 pub struct Error;
708 } 724 }
709 "#; 725 "#;
710 check_found_path(code, "core::fmt::Error"); 726 check_found_path(
711 check_found_path_abs(code, "core::fmt::Error"); 727 code,
728 "core::fmt::Error",
729 "core::fmt::Error",
730 "core::fmt::Error",
731 "core::fmt::Error",
732 );
712 } 733 }
713 734
714 #[test] 735 #[test]
@@ -731,8 +752,13 @@ mod tests {
731 pub struct Arc; 752 pub struct Arc;
732 } 753 }
733 "#; 754 "#;
734 check_found_path(code, "alloc::sync::Arc"); 755 check_found_path(
735 check_found_path_abs(code, "alloc::sync::Arc"); 756 code,
757 "alloc::sync::Arc",
758 "alloc::sync::Arc",
759 "alloc::sync::Arc",
760 "alloc::sync::Arc",
761 );
736 } 762 }
737 763
738 #[test] 764 #[test]
@@ -749,8 +775,13 @@ mod tests {
749 //- /zzz.rs crate:megaalloc 775 //- /zzz.rs crate:megaalloc
750 pub struct Arc; 776 pub struct Arc;
751 "#; 777 "#;
752 check_found_path(code, "megaalloc::Arc"); 778 check_found_path(
753 check_found_path_abs(code, "megaalloc::Arc"); 779 code,
780 "megaalloc::Arc",
781 "megaalloc::Arc",
782 "megaalloc::Arc",
783 "megaalloc::Arc",
784 );
754 } 785 }
755 786
756 #[test] 787 #[test]
@@ -763,9 +794,7 @@ mod tests {
763 pub use u8; 794 pub use u8;
764 } 795 }
765 "#; 796 "#;
766 check_found_path(code, "u8"); 797 check_found_path(code, "u8", "u8", "u8", "u8");
767 check_found_path(code, "u16"); 798 check_found_path(code, "u16", "u16", "u16", "u16");
768 check_found_path_abs(code, "u8");
769 check_found_path_abs(code, "u16");
770 } 799 }
771} 800}
diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs
index 100e25ffc..c8cd04264 100644
--- a/crates/hir_def/src/nameres/collector.rs
+++ b/crates/hir_def/src/nameres/collector.rs
@@ -1229,9 +1229,10 @@ impl ModCollector<'_, '_> {
1229 } else { 1229 } else {
1230 let derive = attrs.by_key("proc_macro_derive"); 1230 let derive = attrs.by_key("proc_macro_derive");
1231 if let Some(arg) = derive.tt_values().next() { 1231 if let Some(arg) = derive.tt_values().next() {
1232 if let [TokenTree::Leaf(Leaf::Ident(trait_name))] = &*arg.token_trees { 1232 if let [TokenTree::Leaf(Leaf::Ident(trait_name)), ..] = &*arg.token_trees {
1233 trait_name.as_name() 1233 trait_name.as_name()
1234 } else { 1234 } else {
1235 log::trace!("malformed `#[proc_macro_derive]`: {}", arg);
1235 return; 1236 return;
1236 } 1237 }
1237 } else { 1238 } else {
diff --git a/crates/hir_def/src/nameres/tests/macros.rs b/crates/hir_def/src/nameres/tests/macros.rs
index 0851c3b7d..305fca0f9 100644
--- a/crates/hir_def/src/nameres/tests/macros.rs
+++ b/crates/hir_def/src/nameres/tests/macros.rs
@@ -688,13 +688,20 @@ fn resolves_proc_macros() {
688 pub fn derive_macro(_item: TokenStream) -> TokenStream { 688 pub fn derive_macro(_item: TokenStream) -> TokenStream {
689 TokenStream 689 TokenStream
690 } 690 }
691
692 #[proc_macro_derive(AnotherTrait, attributes(helper_attr))]
693 pub fn derive_macro_2(_item: TokenStream) -> TokenStream {
694 TokenStream
695 }
691 ", 696 ",
692 expect![[r#" 697 expect![[r#"
693 crate 698 crate
699 AnotherTrait: m
694 DummyTrait: m 700 DummyTrait: m
695 TokenStream: t v 701 TokenStream: t v
696 attribute_macro: v m 702 attribute_macro: v m
697 derive_macro: v 703 derive_macro: v
704 derive_macro_2: v
698 function_like_macro: v m 705 function_like_macro: v m
699 "#]], 706 "#]],
700 ); 707 );
diff --git a/crates/ide/src/completion/complete_postfix/format_like.rs b/crates/ide/src/completion/complete_postfix/format_like.rs
index 0287fc803..81c33bf3a 100644
--- a/crates/ide/src/completion/complete_postfix/format_like.rs
+++ b/crates/ide/src/completion/complete_postfix/format_like.rs
@@ -1,4 +1,4 @@
1// Feature: Postfix completion for `format`-like strings. 1// Feature: Format String Completion.
2// 2//
3// `"Result {result} is {2 + 2}"` is expanded to the `"Result {} is {}", result, 2 + 2`. 3// `"Result {result} is {2 + 2}"` is expanded to the `"Result {} is {}", result, 2 + 2`.
4// 4//
diff --git a/crates/ide/src/completion/complete_unqualified_path.rs b/crates/ide/src/completion/complete_unqualified_path.rs
index 2010d9a2f..8b6757195 100644
--- a/crates/ide/src/completion/complete_unqualified_path.rs
+++ b/crates/ide/src/completion/complete_unqualified_path.rs
@@ -267,6 +267,26 @@ fn quux() { <|> }
267 ); 267 );
268 } 268 }
269 269
270 /// Regression test for issue #6091.
271 #[test]
272 fn correctly_completes_module_items_prefixed_with_underscore() {
273 check_edit(
274 "_alpha",
275 r#"
276fn main() {
277 _<|>
278}
279fn _alpha() {}
280"#,
281 r#"
282fn main() {
283 _alpha()$0
284}
285fn _alpha() {}
286"#,
287 )
288 }
289
270 #[test] 290 #[test]
271 fn completes_extern_prelude() { 291 fn completes_extern_prelude() {
272 check( 292 check(
diff --git a/crates/ide/src/completion/completion_context.rs b/crates/ide/src/completion/completion_context.rs
index 101be8eb5..8dea8a4bf 100644
--- a/crates/ide/src/completion/completion_context.rs
+++ b/crates/ide/src/completion/completion_context.rs
@@ -221,10 +221,11 @@ impl<'a> CompletionContext<'a> {
221 Some(ctx) 221 Some(ctx)
222 } 222 }
223 223
224 // The range of the identifier that is being completed. 224 /// The range of the identifier that is being completed.
225 pub(crate) fn source_range(&self) -> TextRange { 225 pub(crate) fn source_range(&self) -> TextRange {
226 // check kind of macro-expanded token, but use range of original token 226 // check kind of macro-expanded token, but use range of original token
227 if self.token.kind() == IDENT || self.token.kind().is_keyword() { 227 let kind = self.token.kind();
228 if kind == IDENT || kind == UNDERSCORE || kind.is_keyword() {
228 mark::hit!(completes_if_prefix_is_keyword); 229 mark::hit!(completes_if_prefix_is_keyword);
229 self.original_token.text_range() 230 self.original_token.text_range()
230 } else { 231 } else {
diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs
index 0afe5f8fd..1d7e8de56 100644
--- a/crates/ide/src/inlay_hints.rs
+++ b/crates/ide/src/inlay_hints.rs
@@ -189,7 +189,7 @@ fn get_bind_pat_hints(
189 189
190 let ty = sema.type_of_pat(&pat.clone().into())?; 190 let ty = sema.type_of_pat(&pat.clone().into())?;
191 191
192 if should_not_display_type_hint(sema.db, &pat, &ty) { 192 if should_not_display_type_hint(sema, &pat, &ty) {
193 return None; 193 return None;
194 } 194 }
195 195
@@ -215,10 +215,12 @@ fn pat_is_enum_variant(db: &RootDatabase, bind_pat: &ast::IdentPat, pat_ty: &Typ
215} 215}
216 216
217fn should_not_display_type_hint( 217fn should_not_display_type_hint(
218 db: &RootDatabase, 218 sema: &Semantics<RootDatabase>,
219 bind_pat: &ast::IdentPat, 219 bind_pat: &ast::IdentPat,
220 pat_ty: &Type, 220 pat_ty: &Type,
221) -> bool { 221) -> bool {
222 let db = sema.db;
223
222 if pat_ty.is_unknown() { 224 if pat_ty.is_unknown() {
223 return true; 225 return true;
224 } 226 }
@@ -249,6 +251,15 @@ fn should_not_display_type_hint(
249 return it.condition().and_then(|condition| condition.pat()).is_some() 251 return it.condition().and_then(|condition| condition.pat()).is_some()
250 && pat_is_enum_variant(db, bind_pat, pat_ty); 252 && pat_is_enum_variant(db, bind_pat, pat_ty);
251 }, 253 },
254 ast::ForExpr(it) => {
255 // We *should* display hint only if user provided "in {expr}" and we know the type of expr (and it's not unit).
256 // Type of expr should be iterable.
257 return it.in_token().is_none() ||
258 it.iterable()
259 .and_then(|iterable_expr|sema.type_of_expr(&iterable_expr))
260 .map(|iterable_ty| iterable_ty.is_unknown() || iterable_ty.is_unit())
261 .unwrap_or(true)
262 },
252 _ => (), 263 _ => (),
253 } 264 }
254 } 265 }
@@ -496,19 +507,6 @@ fn main() {
496 } 507 }
497 508
498 #[test] 509 #[test]
499 fn for_expression() {
500 check(
501 r#"
502fn main() {
503 let mut start = 0;
504 //^^^^^^^^^ i32
505 for increment in 0..2 { start += increment; }
506 //^^^^^^^^^ i32
507}"#,
508 );
509 }
510
511 #[test]
512 fn if_expr() { 510 fn if_expr() {
513 check( 511 check(
514 r#" 512 r#"
@@ -924,4 +922,108 @@ fn main() {
924 "#]], 922 "#]],
925 ); 923 );
926 } 924 }
925
926 #[test]
927 fn incomplete_for_no_hint() {
928 check(
929 r#"
930fn main() {
931 let data = &[1i32, 2, 3];
932 //^^^^ &[i32; _]
933 for i
934}"#,
935 );
936 check(
937 r#"
938//- /main.rs crate:main deps:core
939pub struct Vec<T> {}
940
941impl<T> Vec<T> {
942 pub fn new() -> Self { Vec {} }
943 pub fn push(&mut self, t: T) {}
944}
945
946impl<T> IntoIterator for Vec<T> {
947 type Item=T;
948}
949
950fn main() {
951 let mut data = Vec::new();
952 //^^^^^^^^ Vec<&str>
953 data.push("foo");
954 for i in
955
956 println!("Unit expr");
957}
958
959//- /core.rs crate:core
960#[prelude_import] use iter::*;
961mod iter {
962 trait IntoIterator {
963 type Item;
964 }
965}
966//- /alloc.rs crate:alloc deps:core
967mod collections {
968 struct Vec<T> {}
969 impl<T> Vec<T> {
970 fn new() -> Self { Vec {} }
971 fn push(&mut self, t: T) { }
972 }
973 impl<T> IntoIterator for Vec<T> {
974 type Item=T;
975 }
976}
977"#,
978 );
979 }
980
981 #[test]
982 fn complete_for_hint() {
983 check(
984 r#"
985//- /main.rs crate:main deps:core
986pub struct Vec<T> {}
987
988impl<T> Vec<T> {
989 pub fn new() -> Self { Vec {} }
990 pub fn push(&mut self, t: T) {}
991}
992
993impl<T> IntoIterator for Vec<T> {
994 type Item=T;
995}
996
997fn main() {
998 let mut data = Vec::new();
999 //^^^^^^^^ Vec<&str>
1000 data.push("foo");
1001 for i in data {
1002 //^ &str
1003 let z = i;
1004 //^ &str
1005 }
1006}
1007
1008//- /core.rs crate:core
1009#[prelude_import] use iter::*;
1010mod iter {
1011 trait IntoIterator {
1012 type Item;
1013 }
1014}
1015//- /alloc.rs crate:alloc deps:core
1016mod collections {
1017 struct Vec<T> {}
1018 impl<T> Vec<T> {
1019 fn new() -> Self { Vec {} }
1020 fn push(&mut self, t: T) { }
1021 }
1022 impl<T> IntoIterator for Vec<T> {
1023 type Item=T;
1024 }
1025}
1026"#,
1027 );
1028 }
927} 1029}
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index 0ab4c37bf..dcbc11c14 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -10,6 +10,7 @@
10use std::{ffi::OsString, path::PathBuf}; 10use std::{ffi::OsString, path::PathBuf};
11 11
12use flycheck::FlycheckConfig; 12use flycheck::FlycheckConfig;
13use hir::PrefixKind;
13use ide::{ 14use ide::{
14 AssistConfig, CompletionConfig, DiagnosticsConfig, HoverConfig, InlayHintsConfig, 15 AssistConfig, CompletionConfig, DiagnosticsConfig, HoverConfig, InlayHintsConfig,
15 MergeBehaviour, 16 MergeBehaviour,
@@ -289,6 +290,11 @@ impl Config {
289 MergeBehaviourDef::Full => Some(MergeBehaviour::Full), 290 MergeBehaviourDef::Full => Some(MergeBehaviour::Full),
290 MergeBehaviourDef::Last => Some(MergeBehaviour::Last), 291 MergeBehaviourDef::Last => Some(MergeBehaviour::Last),
291 }; 292 };
293 self.assist.insert_use.prefix_kind = match data.assist_importPrefix {
294 ImportPrefixDef::Plain => PrefixKind::Plain,
295 ImportPrefixDef::ByCrate => PrefixKind::ByCrate,
296 ImportPrefixDef::BySelf => PrefixKind::BySelf,
297 };
292 298
293 self.call_info_full = data.callInfo_full; 299 self.call_info_full = data.callInfo_full;
294 300
@@ -403,13 +409,21 @@ enum ManifestOrProjectJson {
403} 409}
404 410
405#[derive(Deserialize)] 411#[derive(Deserialize)]
406#[serde(rename_all = "lowercase")] 412#[serde(rename_all = "snake_case")]
407enum MergeBehaviourDef { 413enum MergeBehaviourDef {
408 None, 414 None,
409 Full, 415 Full,
410 Last, 416 Last,
411} 417}
412 418
419#[derive(Deserialize)]
420#[serde(rename_all = "snake_case")]
421enum ImportPrefixDef {
422 Plain,
423 BySelf,
424 ByCrate,
425}
426
413macro_rules! config_data { 427macro_rules! config_data {
414 (struct $name:ident { $($field:ident: $ty:ty = $default:expr,)*}) => { 428 (struct $name:ident { $($field:ident: $ty:ty = $default:expr,)*}) => {
415 #[allow(non_snake_case)] 429 #[allow(non_snake_case)]
@@ -434,6 +448,7 @@ macro_rules! config_data {
434config_data! { 448config_data! {
435 struct ConfigData { 449 struct ConfigData {
436 assist_importMergeBehaviour: MergeBehaviourDef = MergeBehaviourDef::None, 450 assist_importMergeBehaviour: MergeBehaviourDef = MergeBehaviourDef::None,
451 assist_importPrefix: ImportPrefixDef = ImportPrefixDef::Plain,
437 452
438 callInfo_full: bool = true, 453 callInfo_full: bool = true,
439 454
diff --git a/editors/code/package.json b/editors/code/package.json
index cc2ac3bd2..1f0e7550b 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -652,6 +652,21 @@
652 "default": "full", 652 "default": "full",
653 "description": "The strategy to use when inserting new imports or merging imports." 653 "description": "The strategy to use when inserting new imports or merging imports."
654 }, 654 },
655 "rust-analyzer.assist.importPrefix": {
656 "type": "string",
657 "enum": [
658 "plain",
659 "by_self",
660 "by_crate"
661 ],
662 "enumDescriptions": [
663 "Insert import paths relative to the current module, using up to one `super` prefix if the parent module contains the requested item.",
664 "Prefix all import paths with `self` if they don't begin with `self`, `super`, `crate` or a crate name",
665 "Force import paths to be absolute by always starting them with `crate` or the crate name they refer to."
666 ],
667 "default": "plain",
668 "description": "The path structure for newly inserted paths to use."
669 },
655 "rust-analyzer.runnables.overrideCargo": { 670 "rust-analyzer.runnables.overrideCargo": {
656 "type": [ 671 "type": [
657 "null", 672 "null",
@@ -1033,4 +1048,4 @@
1033 ] 1048 ]
1034 } 1049 }
1035 } 1050 }
1036} 1051} \ No newline at end of file
diff --git a/xtask/src/codegen/gen_feature_docs.rs b/xtask/src/codegen/gen_feature_docs.rs
index 3f0013e82..341e67c73 100644
--- a/xtask/src/codegen/gen_feature_docs.rs
+++ b/xtask/src/codegen/gen_feature_docs.rs
@@ -38,7 +38,9 @@ impl Feature {
38 38
39 for block in comment_blocks { 39 for block in comment_blocks {
40 let id = block.id; 40 let id = block.id;
41 assert!(is_valid_feature_name(&id), "invalid feature name: {:?}", id); 41 if let Err(msg) = is_valid_feature_name(&id) {
42 panic!("invalid feature name: {:?}:\n {}", id, msg)
43 }
42 let doc = block.contents.join("\n"); 44 let doc = block.contents.join("\n");
43 let location = Location::new(path.clone(), block.line); 45 let location = Location::new(path.clone(), block.line);
44 acc.push(Feature { id, location, doc }) 46 acc.push(Feature { id, location, doc })
@@ -49,7 +51,7 @@ impl Feature {
49 } 51 }
50} 52}
51 53
52fn is_valid_feature_name(feature: &str) -> bool { 54fn is_valid_feature_name(feature: &str) -> Result<(), String> {
53 'word: for word in feature.split_whitespace() { 55 'word: for word in feature.split_whitespace() {
54 for &short in ["to", "and"].iter() { 56 for &short in ["to", "and"].iter() {
55 if word == short { 57 if word == short {
@@ -58,14 +60,14 @@ fn is_valid_feature_name(feature: &str) -> bool {
58 } 60 }
59 for &short in ["To", "And"].iter() { 61 for &short in ["To", "And"].iter() {
60 if word == short { 62 if word == short {
61 return false; 63 return Err(format!("Don't capitalize {:?}", word));
62 } 64 }
63 } 65 }
64 if !word.starts_with(char::is_uppercase) { 66 if !word.starts_with(char::is_uppercase) {
65 return false; 67 return Err(format!("Capitalize {:?}", word));
66 } 68 }
67 } 69 }
68 true 70 Ok(())
69} 71}
70 72
71impl fmt::Display for Feature { 73impl fmt::Display for Feature {