aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/release.yaml4
-rw-r--r--Cargo.lock6
-rw-r--r--crates/assists/src/ast_transform.rs3
-rw-r--r--crates/assists/src/handlers/extract_assignment.rs400
-rw-r--r--crates/assists/src/handlers/fill_match_arms.rs2
-rw-r--r--crates/assists/src/handlers/fix_visibility.rs27
-rw-r--r--crates/assists/src/lib.rs2
-rw-r--r--crates/assists/src/tests/generated.rs29
-rw-r--r--crates/assists/src/utils.rs14
-rw-r--r--crates/base_db/src/input.rs7
-rw-r--r--crates/cfg/Cargo.toml2
-rw-r--r--crates/completion/Cargo.toml2
-rw-r--r--crates/completion/src/completions.rs5
-rw-r--r--crates/completion/src/completions/record.rs68
-rw-r--r--crates/completion/src/completions/trait_impl.rs47
-rw-r--r--crates/completion/src/render.rs3
-rw-r--r--crates/completion/src/render/const_.rs8
-rw-r--r--crates/completion/src/render/function.rs10
-rw-r--r--crates/completion/src/render/macro_.rs15
-rw-r--r--crates/completion/src/render/type_alias.rs8
-rw-r--r--crates/hir/src/attrs.rs28
-rw-r--r--crates/hir/src/code_model.rs63
-rw-r--r--crates/hir/src/from_id.rs29
-rw-r--r--crates/hir/src/has_source.rs77
-rw-r--r--crates/hir/src/lib.rs7
-rw-r--r--crates/hir/src/semantics.rs12
-rw-r--r--crates/hir/src/semantics/source_to_def.rs18
-rw-r--r--crates/hir/src/source_analyzer.rs1
-rw-r--r--crates/hir_def/Cargo.toml2
-rw-r--r--crates/hir_def/src/attr.rs36
-rw-r--r--crates/hir_def/src/generics.rs44
-rw-r--r--crates/hir_def/src/item_tree.rs9
-rw-r--r--crates/hir_def/src/keys.rs5
-rw-r--r--crates/hir_def/src/lib.rs29
-rw-r--r--crates/hir_def/src/resolver.rs17
-rw-r--r--crates/hir_expand/src/db.rs2
-rw-r--r--crates/hir_expand/src/eager.rs6
-rw-r--r--crates/hir_ty/Cargo.toml2
-rw-r--r--crates/hir_ty/src/db.rs7
-rw-r--r--crates/hir_ty/src/diagnostics/expr.rs26
-rw-r--r--crates/hir_ty/src/infer/path.rs1
-rw-r--r--crates/hir_ty/src/lib.rs4
-rw-r--r--crates/hir_ty/src/lower.rs15
-rw-r--r--crates/hir_ty/src/tests/macros.rs46
-rw-r--r--crates/hir_ty/src/tests/regression.rs18
-rw-r--r--crates/hir_ty/src/tests/simple.rs16
-rw-r--r--crates/ide/Cargo.toml2
-rw-r--r--crates/ide/src/call_hierarchy.rs8
-rw-r--r--crates/ide/src/diagnostics/fixes.rs9
-rw-r--r--crates/ide/src/display/navigation_target.rs137
-rw-r--r--crates/ide/src/doc_links.rs1
-rw-r--r--crates/ide/src/goto_implementation.rs6
-rw-r--r--crates/ide/src/goto_type_definition.rs4
-rw-r--r--crates/ide/src/hover.rs74
-rw-r--r--crates/ide/src/lib.rs5
-rw-r--r--crates/ide/src/references.rs16
-rw-r--r--crates/ide/src/runnables.rs2
-rw-r--r--crates/ide/src/syntax_highlighting.rs1
-rw-r--r--crates/ide/src/syntax_highlighting/injection.rs13
-rw-r--r--crates/ide/src/syntax_highlighting/tags.rs1
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlighting.html4
-rw-r--r--crates/ide/src/syntax_highlighting/tests.rs4
-rw-r--r--crates/ide/src/view_hir.rs25
-rw-r--r--crates/ide_db/Cargo.toml2
-rw-r--r--crates/ide_db/src/defs.rs13
-rw-r--r--crates/ide_db/src/search.rs53
-rw-r--r--crates/proc_macro_api/src/msg.rs8
-rw-r--r--crates/proc_macro_api/src/process.rs7
-rw-r--r--crates/project_model/src/project_json.rs3
-rw-r--r--crates/rust-analyzer/Cargo.toml2
-rw-r--r--crates/rust-analyzer/src/cli/analysis_stats.rs11
-rw-r--r--crates/rust-analyzer/src/config.rs2
-rw-r--r--crates/rust-analyzer/src/handlers.rs10
-rw-r--r--crates/rust-analyzer/src/lsp_ext.rs8
-rw-r--r--crates/rust-analyzer/src/main_loop.rs1
-rw-r--r--crates/rust-analyzer/src/markdown.rs13
-rw-r--r--crates/rust-analyzer/src/semantic_tokens.rs1
-rw-r--r--crates/rust-analyzer/src/to_proto.rs2
-rw-r--r--crates/ssr/Cargo.toml2
-rw-r--r--crates/syntax/Cargo.toml2
-rw-r--r--crates/syntax/src/ast/edit.rs4
-rw-r--r--docs/dev/README.md2
-rw-r--r--docs/dev/lsp-extensions.md13
-rw-r--r--docs/user/manual.adoc2
-rw-r--r--editors/code/package.json9
-rw-r--r--editors/code/src/commands.ts55
-rw-r--r--editors/code/src/lsp_ext.ts1
-rw-r--r--editors/code/src/main.ts1
-rw-r--r--xtask/src/dist.rs10
-rw-r--r--xtask/tests/tidy.rs2
90 files changed, 1402 insertions, 331 deletions
diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml
index 7b9fbe6b8..889ada401 100644
--- a/.github/workflows/release.yaml
+++ b/.github/workflows/release.yaml
@@ -124,10 +124,10 @@ jobs:
124 - name: Checkout repository 124 - name: Checkout repository
125 uses: actions/checkout@v2 125 uses: actions/checkout@v2
126 126
127 - name: Install Rust toolchain (beta) 127 - name: Install Rust toolchain
128 uses: actions-rs/toolchain@v1 128 uses: actions-rs/toolchain@v1
129 with: 129 with:
130 toolchain: beta 130 toolchain: stable
131 target: aarch64-apple-darwin 131 target: aarch64-apple-darwin
132 profile: minimal 132 profile: minimal
133 override: true 133 override: true
diff --git a/Cargo.lock b/Cargo.lock
index 1aa0c072d..4aaee3ab1 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -386,11 +386,11 @@ dependencies = [
386 386
387[[package]] 387[[package]]
388name = "expect-test" 388name = "expect-test"
389version = "1.0.2" 389version = "1.1.0"
390source = "registry+https://github.com/rust-lang/crates.io-index" 390source = "registry+https://github.com/rust-lang/crates.io-index"
391checksum = "eab78d4c727fa879de40951ffc01ed781a231b28786b19d6bfbdc0d1ec92eb3e" 391checksum = "2300477aab3a378f2ca00a4fbd4dc713654ab7ed790e4017493cb33656280633"
392dependencies = [ 392dependencies = [
393 "difference", 393 "dissimilar",
394 "once_cell", 394 "once_cell",
395] 395]
396 396
diff --git a/crates/assists/src/ast_transform.rs b/crates/assists/src/ast_transform.rs
index da94e9987..4a3ed7783 100644
--- a/crates/assists/src/ast_transform.rs
+++ b/crates/assists/src/ast_transform.rs
@@ -204,7 +204,8 @@ impl<'a> AstTransform<'a> for QualifyPaths<'a> {
204 } 204 }
205 PathResolution::Local(_) 205 PathResolution::Local(_)
206 | PathResolution::TypeParam(_) 206 | PathResolution::TypeParam(_)
207 | PathResolution::SelfType(_) => None, 207 | PathResolution::SelfType(_)
208 | PathResolution::ConstParam(_) => None,
208 PathResolution::Macro(_) => None, 209 PathResolution::Macro(_) => None,
209 PathResolution::AssocItem(_) => None, 210 PathResolution::AssocItem(_) => None,
210 } 211 }
diff --git a/crates/assists/src/handlers/extract_assignment.rs b/crates/assists/src/handlers/extract_assignment.rs
new file mode 100644
index 000000000..ae99598c0
--- /dev/null
+++ b/crates/assists/src/handlers/extract_assignment.rs
@@ -0,0 +1,400 @@
1use syntax::{
2 ast::{self, edit::AstNodeEdit, make},
3 AstNode,
4};
5use test_utils::mark;
6
7use crate::{
8 assist_context::{AssistContext, Assists},
9 AssistId, AssistKind,
10};
11
12// Assist: extract_assignment
13//
14// Extracts variable assigment to outside an if or match statement.
15//
16// ```
17// fn main() {
18// let mut foo = 6;
19//
20// if true {
21// <|>foo = 5;
22// } else {
23// foo = 4;
24// }
25// }
26// ```
27// ->
28// ```
29// fn main() {
30// let mut foo = 6;
31//
32// foo = if true {
33// 5
34// } else {
35// 4
36// };
37// }
38// ```
39pub(crate) fn extract_assigment(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
40 let assign_expr = ctx.find_node_at_offset::<ast::BinExpr>()?;
41 let name_expr = if assign_expr.op_kind()? == ast::BinOp::Assignment {
42 assign_expr.lhs()?
43 } else {
44 return None;
45 };
46
47 let (old_stmt, new_stmt) = if let Some(if_expr) = ctx.find_node_at_offset::<ast::IfExpr>() {
48 (
49 ast::Expr::cast(if_expr.syntax().to_owned())?,
50 exprify_if(&if_expr, &ctx.sema, &name_expr)?.indent(if_expr.indent_level()),
51 )
52 } else if let Some(match_expr) = ctx.find_node_at_offset::<ast::MatchExpr>() {
53 (
54 ast::Expr::cast(match_expr.syntax().to_owned())?,
55 exprify_match(&match_expr, &ctx.sema, &name_expr)?,
56 )
57 } else {
58 return None;
59 };
60
61 let expr_stmt = make::expr_stmt(new_stmt);
62
63 acc.add(
64 AssistId("extract_assignment", AssistKind::RefactorExtract),
65 "Extract assignment",
66 old_stmt.syntax().text_range(),
67 move |edit| {
68 edit.replace(old_stmt.syntax().text_range(), format!("{} = {};", name_expr, expr_stmt));
69 },
70 )
71}
72
73fn exprify_match(
74 match_expr: &ast::MatchExpr,
75 sema: &hir::Semantics<ide_db::RootDatabase>,
76 name: &ast::Expr,
77) -> Option<ast::Expr> {
78 let new_arm_list = match_expr
79 .match_arm_list()?
80 .arms()
81 .map(|arm| {
82 if let ast::Expr::BlockExpr(block) = arm.expr()? {
83 let new_block = exprify_block(&block, sema, name)?.indent(block.indent_level());
84 Some(arm.replace_descendant(block, new_block))
85 } else {
86 None
87 }
88 })
89 .collect::<Option<Vec<_>>>()?;
90 let new_arm_list = match_expr
91 .match_arm_list()?
92 .replace_descendants(match_expr.match_arm_list()?.arms().zip(new_arm_list));
93 Some(make::expr_match(match_expr.expr()?, new_arm_list))
94}
95
96fn exprify_if(
97 statement: &ast::IfExpr,
98 sema: &hir::Semantics<ide_db::RootDatabase>,
99 name: &ast::Expr,
100) -> Option<ast::Expr> {
101 let then_branch = exprify_block(&statement.then_branch()?, sema, name)?;
102 let else_branch = match statement.else_branch()? {
103 ast::ElseBranch::Block(ref block) => {
104 ast::ElseBranch::Block(exprify_block(block, sema, name)?)
105 }
106 ast::ElseBranch::IfExpr(expr) => {
107 mark::hit!(test_extract_assigment_chained_if);
108 ast::ElseBranch::IfExpr(ast::IfExpr::cast(
109 exprify_if(&expr, sema, name)?.syntax().to_owned(),
110 )?)
111 }
112 };
113 Some(make::expr_if(statement.condition()?, then_branch, Some(else_branch)))
114}
115
116fn exprify_block(
117 block: &ast::BlockExpr,
118 sema: &hir::Semantics<ide_db::RootDatabase>,
119 name: &ast::Expr,
120) -> Option<ast::BlockExpr> {
121 if block.expr().is_some() {
122 return None;
123 }
124
125 let mut stmts: Vec<_> = block.statements().collect();
126 let stmt = stmts.pop()?;
127
128 if let ast::Stmt::ExprStmt(stmt) = stmt {
129 if let ast::Expr::BinExpr(expr) = stmt.expr()? {
130 if expr.op_kind()? == ast::BinOp::Assignment && is_equivalent(sema, &expr.lhs()?, name)
131 {
132 // The last statement in the block is an assignment to the name we want
133 return Some(make::block_expr(stmts, Some(expr.rhs()?)));
134 }
135 }
136 }
137 None
138}
139
140fn is_equivalent(
141 sema: &hir::Semantics<ide_db::RootDatabase>,
142 expr0: &ast::Expr,
143 expr1: &ast::Expr,
144) -> bool {
145 match (expr0, expr1) {
146 (ast::Expr::FieldExpr(field_expr0), ast::Expr::FieldExpr(field_expr1)) => {
147 mark::hit!(test_extract_assignment_field_assignment);
148 sema.resolve_field(field_expr0) == sema.resolve_field(field_expr1)
149 }
150 (ast::Expr::PathExpr(path0), ast::Expr::PathExpr(path1)) => {
151 let path0 = path0.path();
152 let path1 = path1.path();
153 if let (Some(path0), Some(path1)) = (path0, path1) {
154 sema.resolve_path(&path0) == sema.resolve_path(&path1)
155 } else {
156 false
157 }
158 }
159 _ => false,
160 }
161}
162
163#[cfg(test)]
164mod tests {
165 use super::*;
166
167 use crate::tests::{check_assist, check_assist_not_applicable};
168
169 #[test]
170 fn test_extract_assignment_if() {
171 check_assist(
172 extract_assigment,
173 r#"
174fn foo() {
175 let mut a = 1;
176
177 if true {
178 <|>a = 2;
179 } else {
180 a = 3;
181 }
182}"#,
183 r#"
184fn foo() {
185 let mut a = 1;
186
187 a = if true {
188 2
189 } else {
190 3
191 };
192}"#,
193 );
194 }
195
196 #[test]
197 fn test_extract_assignment_match() {
198 check_assist(
199 extract_assigment,
200 r#"
201fn foo() {
202 let mut a = 1;
203
204 match 1 {
205 1 => {
206 <|>a = 2;
207 },
208 2 => {
209 a = 3;
210 },
211 3 => {
212 a = 4;
213 }
214 }
215}"#,
216 r#"
217fn foo() {
218 let mut a = 1;
219
220 a = match 1 {
221 1 => {
222 2
223 },
224 2 => {
225 3
226 },
227 3 => {
228 4
229 }
230 };
231}"#,
232 );
233 }
234
235 #[test]
236 fn test_extract_assignment_not_last_not_applicable() {
237 check_assist_not_applicable(
238 extract_assigment,
239 r#"
240fn foo() {
241 let mut a = 1;
242
243 if true {
244 <|>a = 2;
245 b = a;
246 } else {
247 a = 3;
248 }
249}"#,
250 )
251 }
252
253 #[test]
254 fn test_extract_assignment_chained_if() {
255 mark::check!(test_extract_assigment_chained_if);
256 check_assist(
257 extract_assigment,
258 r#"
259fn foo() {
260 let mut a = 1;
261
262 if true {
263 <|>a = 2;
264 } else if false {
265 a = 3;
266 } else {
267 a = 4;
268 }
269}"#,
270 r#"
271fn foo() {
272 let mut a = 1;
273
274 a = if true {
275 2
276 } else if false {
277 3
278 } else {
279 4
280 };
281}"#,
282 );
283 }
284
285 #[test]
286 fn test_extract_assigment_retains_stmts() {
287 check_assist(
288 extract_assigment,
289 r#"
290fn foo() {
291 let mut a = 1;
292
293 if true {
294 let b = 2;
295 <|>a = 2;
296 } else {
297 let b = 3;
298 a = 3;
299 }
300}"#,
301 r#"
302fn foo() {
303 let mut a = 1;
304
305 a = if true {
306 let b = 2;
307 2
308 } else {
309 let b = 3;
310 3
311 };
312}"#,
313 )
314 }
315
316 #[test]
317 fn extract_assignment_let_stmt_not_applicable() {
318 check_assist_not_applicable(
319 extract_assigment,
320 r#"
321fn foo() {
322 let mut a = 1;
323
324 let b = if true {
325 <|>a = 2
326 } else {
327 a = 3
328 };
329}"#,
330 )
331 }
332
333 #[test]
334 fn extract_assignment_if_missing_assigment_not_applicable() {
335 check_assist_not_applicable(
336 extract_assigment,
337 r#"
338fn foo() {
339 let mut a = 1;
340
341 if true {
342 <|>a = 2;
343 } else {}
344}"#,
345 )
346 }
347
348 #[test]
349 fn extract_assignment_match_missing_assigment_not_applicable() {
350 check_assist_not_applicable(
351 extract_assigment,
352 r#"
353fn foo() {
354 let mut a = 1;
355
356 match 1 {
357 1 => {
358 <|>a = 2;
359 },
360 2 => {
361 a = 3;
362 },
363 3 => {},
364 }
365}"#,
366 )
367 }
368
369 #[test]
370 fn test_extract_assignment_field_assignment() {
371 mark::check!(test_extract_assignment_field_assignment);
372 check_assist(
373 extract_assigment,
374 r#"
375struct A(usize);
376
377fn foo() {
378 let mut a = A(1);
379
380 if true {
381 <|>a.0 = 2;
382 } else {
383 a.0 = 3;
384 }
385}"#,
386 r#"
387struct A(usize);
388
389fn foo() {
390 let mut a = A(1);
391
392 a.0 = if true {
393 2
394 } else {
395 3
396 };
397}"#,
398 )
399 }
400}
diff --git a/crates/assists/src/handlers/fill_match_arms.rs b/crates/assists/src/handlers/fill_match_arms.rs
index cb60a3128..f9a62b9fa 100644
--- a/crates/assists/src/handlers/fill_match_arms.rs
+++ b/crates/assists/src/handlers/fill_match_arms.rs
@@ -196,7 +196,7 @@ fn build_pat(db: &RootDatabase, module: hir::Module, var: hir::Variant) -> Optio
196 let path = mod_path_to_ast(&module.find_use_path(db, ModuleDef::from(var))?); 196 let path = mod_path_to_ast(&module.find_use_path(db, ModuleDef::from(var))?);
197 197
198 // FIXME: use HIR for this; it doesn't currently expose struct vs. tuple vs. unit variants though 198 // FIXME: use HIR for this; it doesn't currently expose struct vs. tuple vs. unit variants though
199 let pat: ast::Pat = match var.source(db).value.kind() { 199 let pat: ast::Pat = match var.source(db)?.value.kind() {
200 ast::StructKind::Tuple(field_list) => { 200 ast::StructKind::Tuple(field_list) => {
201 let pats = iter::repeat(make::wildcard_pat().into()).take(field_list.fields().count()); 201 let pats = iter::repeat(make::wildcard_pat().into()).take(field_list.fields().count());
202 make::tuple_struct_pat(path, pats).into() 202 make::tuple_struct_pat(path, pats).into()
diff --git a/crates/assists/src/handlers/fix_visibility.rs b/crates/assists/src/handlers/fix_visibility.rs
index 8558a8ff0..de1e8f0bf 100644
--- a/crates/assists/src/handlers/fix_visibility.rs
+++ b/crates/assists/src/handlers/fix_visibility.rs
@@ -97,7 +97,8 @@ fn add_vis_to_referenced_record_field(acc: &mut Assists, ctx: &AssistContext) ->
97 let parent_name = parent.name(ctx.db()); 97 let parent_name = parent.name(ctx.db());
98 let target_module = parent.module(ctx.db()); 98 let target_module = parent.module(ctx.db());
99 99
100 let in_file_source = record_field_def.source(ctx.db()); 100 #[allow(deprecated)]
101 let in_file_source = record_field_def.source(ctx.db())?;
101 let (offset, current_visibility, target) = match in_file_source.value { 102 let (offset, current_visibility, target) = match in_file_source.value {
102 hir::FieldSource::Named(it) => { 103 hir::FieldSource::Named(it) => {
103 let s = it.syntax(); 104 let s = it.syntax();
@@ -145,53 +146,53 @@ fn target_data_for_def(
145 fn offset_target_and_file_id<S, Ast>( 146 fn offset_target_and_file_id<S, Ast>(
146 db: &dyn HirDatabase, 147 db: &dyn HirDatabase,
147 x: S, 148 x: S,
148 ) -> (TextSize, Option<ast::Visibility>, TextRange, FileId) 149 ) -> Option<(TextSize, Option<ast::Visibility>, TextRange, FileId)>
149 where 150 where
150 S: HasSource<Ast = Ast>, 151 S: HasSource<Ast = Ast>,
151 Ast: AstNode + ast::VisibilityOwner, 152 Ast: AstNode + ast::VisibilityOwner,
152 { 153 {
153 let source = x.source(db); 154 let source = x.source(db)?;
154 let in_file_syntax = source.syntax(); 155 let in_file_syntax = source.syntax();
155 let file_id = in_file_syntax.file_id; 156 let file_id = in_file_syntax.file_id;
156 let syntax = in_file_syntax.value; 157 let syntax = in_file_syntax.value;
157 let current_visibility = source.value.visibility(); 158 let current_visibility = source.value.visibility();
158 ( 159 Some((
159 vis_offset(syntax), 160 vis_offset(syntax),
160 current_visibility, 161 current_visibility,
161 syntax.text_range(), 162 syntax.text_range(),
162 file_id.original_file(db.upcast()), 163 file_id.original_file(db.upcast()),
163 ) 164 ))
164 } 165 }
165 166
166 let target_name; 167 let target_name;
167 let (offset, current_visibility, target, target_file) = match def { 168 let (offset, current_visibility, target, target_file) = match def {
168 hir::ModuleDef::Function(f) => { 169 hir::ModuleDef::Function(f) => {
169 target_name = Some(f.name(db)); 170 target_name = Some(f.name(db));
170 offset_target_and_file_id(db, f) 171 offset_target_and_file_id(db, f)?
171 } 172 }
172 hir::ModuleDef::Adt(adt) => { 173 hir::ModuleDef::Adt(adt) => {
173 target_name = Some(adt.name(db)); 174 target_name = Some(adt.name(db));
174 match adt { 175 match adt {
175 hir::Adt::Struct(s) => offset_target_and_file_id(db, s), 176 hir::Adt::Struct(s) => offset_target_and_file_id(db, s)?,
176 hir::Adt::Union(u) => offset_target_and_file_id(db, u), 177 hir::Adt::Union(u) => offset_target_and_file_id(db, u)?,
177 hir::Adt::Enum(e) => offset_target_and_file_id(db, e), 178 hir::Adt::Enum(e) => offset_target_and_file_id(db, e)?,
178 } 179 }
179 } 180 }
180 hir::ModuleDef::Const(c) => { 181 hir::ModuleDef::Const(c) => {
181 target_name = c.name(db); 182 target_name = c.name(db);
182 offset_target_and_file_id(db, c) 183 offset_target_and_file_id(db, c)?
183 } 184 }
184 hir::ModuleDef::Static(s) => { 185 hir::ModuleDef::Static(s) => {
185 target_name = s.name(db); 186 target_name = s.name(db);
186 offset_target_and_file_id(db, s) 187 offset_target_and_file_id(db, s)?
187 } 188 }
188 hir::ModuleDef::Trait(t) => { 189 hir::ModuleDef::Trait(t) => {
189 target_name = Some(t.name(db)); 190 target_name = Some(t.name(db));
190 offset_target_and_file_id(db, t) 191 offset_target_and_file_id(db, t)?
191 } 192 }
192 hir::ModuleDef::TypeAlias(t) => { 193 hir::ModuleDef::TypeAlias(t) => {
193 target_name = Some(t.name(db)); 194 target_name = Some(t.name(db));
194 offset_target_and_file_id(db, t) 195 offset_target_and_file_id(db, t)?
195 } 196 }
196 hir::ModuleDef::Module(m) => { 197 hir::ModuleDef::Module(m) => {
197 target_name = m.name(db); 198 target_name = m.name(db);
diff --git a/crates/assists/src/lib.rs b/crates/assists/src/lib.rs
index fdec886e9..212464f85 100644
--- a/crates/assists/src/lib.rs
+++ b/crates/assists/src/lib.rs
@@ -116,6 +116,7 @@ mod handlers {
116 mod convert_integer_literal; 116 mod convert_integer_literal;
117 mod early_return; 117 mod early_return;
118 mod expand_glob_import; 118 mod expand_glob_import;
119 mod extract_assignment;
119 mod extract_module_to_file; 120 mod extract_module_to_file;
120 mod extract_struct_from_enum_variant; 121 mod extract_struct_from_enum_variant;
121 mod extract_variable; 122 mod extract_variable;
@@ -167,6 +168,7 @@ mod handlers {
167 convert_integer_literal::convert_integer_literal, 168 convert_integer_literal::convert_integer_literal,
168 early_return::convert_to_guarded_return, 169 early_return::convert_to_guarded_return,
169 expand_glob_import::expand_glob_import, 170 expand_glob_import::expand_glob_import,
171 extract_assignment::extract_assigment,
170 extract_module_to_file::extract_module_to_file, 172 extract_module_to_file::extract_module_to_file,
171 extract_struct_from_enum_variant::extract_struct_from_enum_variant, 173 extract_struct_from_enum_variant::extract_struct_from_enum_variant,
172 extract_variable::extract_variable, 174 extract_variable::extract_variable,
diff --git a/crates/assists/src/tests/generated.rs b/crates/assists/src/tests/generated.rs
index d3dfe24e7..b91a816e8 100644
--- a/crates/assists/src/tests/generated.rs
+++ b/crates/assists/src/tests/generated.rs
@@ -238,6 +238,35 @@ fn qux(bar: Bar, baz: Baz) {}
238} 238}
239 239
240#[test] 240#[test]
241fn doctest_extract_assignment() {
242 check_doc_test(
243 "extract_assignment",
244 r#####"
245fn main() {
246 let mut foo = 6;
247
248 if true {
249 <|>foo = 5;
250 } else {
251 foo = 4;
252 }
253}
254"#####,
255 r#####"
256fn main() {
257 let mut foo = 6;
258
259 foo = if true {
260 5
261 } else {
262 4
263 };
264}
265"#####,
266 )
267}
268
269#[test]
241fn doctest_extract_module_to_file() { 270fn doctest_extract_module_to_file() {
242 check_doc_test( 271 check_doc_test(
243 "extract_module_to_file", 272 "extract_module_to_file",
diff --git a/crates/assists/src/utils.rs b/crates/assists/src/utils.rs
index d41084b59..b05596446 100644
--- a/crates/assists/src/utils.rs
+++ b/crates/assists/src/utils.rs
@@ -94,14 +94,18 @@ pub fn filter_assoc_items(
94 ast::AssocItem::MacroCall(_) => None, 94 ast::AssocItem::MacroCall(_) => None,
95 } 95 }
96 .is_some() 96 .is_some()
97 }; 97 }
98 98
99 items 99 items
100 .iter() 100 .iter()
101 .map(|i| match i { 101 // Note: This throws away items with no source.
102 hir::AssocItem::Function(i) => ast::AssocItem::Fn(i.source(db).value), 102 .filter_map(|i| {
103 hir::AssocItem::TypeAlias(i) => ast::AssocItem::TypeAlias(i.source(db).value), 103 let item = match i {
104 hir::AssocItem::Const(i) => ast::AssocItem::Const(i.source(db).value), 104 hir::AssocItem::Function(i) => ast::AssocItem::Fn(i.source(db)?.value),
105 hir::AssocItem::TypeAlias(i) => ast::AssocItem::TypeAlias(i.source(db)?.value),
106 hir::AssocItem::Const(i) => ast::AssocItem::Const(i.source(db)?.value),
107 };
108 Some(item)
105 }) 109 })
106 .filter(has_def_name) 110 .filter(has_def_name)
107 .filter(|it| match it { 111 .filter(|it| match it {
diff --git a/crates/base_db/src/input.rs b/crates/base_db/src/input.rs
index 9567bcc42..2dd8fbe67 100644
--- a/crates/base_db/src/input.rs
+++ b/crates/base_db/src/input.rs
@@ -190,10 +190,11 @@ pub struct CrateData {
190 pub proc_macro: Vec<ProcMacro>, 190 pub proc_macro: Vec<ProcMacro>,
191} 191}
192 192
193#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 193#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
194pub enum Edition { 194pub enum Edition {
195 Edition2018,
196 Edition2015, 195 Edition2015,
196 Edition2018,
197 Edition2021,
197} 198}
198 199
199#[derive(Default, Debug, Clone, PartialEq, Eq)] 200#[derive(Default, Debug, Clone, PartialEq, Eq)]
@@ -393,6 +394,7 @@ impl FromStr for Edition {
393 let res = match s { 394 let res = match s {
394 "2015" => Edition::Edition2015, 395 "2015" => Edition::Edition2015,
395 "2018" => Edition::Edition2018, 396 "2018" => Edition::Edition2018,
397 "2021" => Edition::Edition2021,
396 _ => return Err(ParseEditionError { invalid_input: s.to_string() }), 398 _ => return Err(ParseEditionError { invalid_input: s.to_string() }),
397 }; 399 };
398 Ok(res) 400 Ok(res)
@@ -404,6 +406,7 @@ impl fmt::Display for Edition {
404 f.write_str(match self { 406 f.write_str(match self {
405 Edition::Edition2015 => "2015", 407 Edition::Edition2015 => "2015",
406 Edition::Edition2018 => "2018", 408 Edition::Edition2018 => "2018",
409 Edition::Edition2021 => "2021",
407 }) 410 })
408 } 411 }
409} 412}
diff --git a/crates/cfg/Cargo.toml b/crates/cfg/Cargo.toml
index c68e391c1..73247d130 100644
--- a/crates/cfg/Cargo.toml
+++ b/crates/cfg/Cargo.toml
@@ -17,4 +17,4 @@ tt = { path = "../tt", version = "0.0.0" }
17[dev-dependencies] 17[dev-dependencies]
18mbe = { path = "../mbe" } 18mbe = { path = "../mbe" }
19syntax = { path = "../syntax" } 19syntax = { path = "../syntax" }
20expect-test = "1.0" 20expect-test = "1.1"
diff --git a/crates/completion/Cargo.toml b/crates/completion/Cargo.toml
index 78e93e78e..99a1bdd54 100644
--- a/crates/completion/Cargo.toml
+++ b/crates/completion/Cargo.toml
@@ -28,4 +28,4 @@ test_utils = { path = "../test_utils", version = "0.0.0" }
28hir = { path = "../hir", version = "0.0.0" } 28hir = { path = "../hir", version = "0.0.0" }
29 29
30[dev-dependencies] 30[dev-dependencies]
31expect-test = "1.0" 31expect-test = "1.1"
diff --git a/crates/completion/src/completions.rs b/crates/completion/src/completions.rs
index d9fe13485..00c9e76f0 100644
--- a/crates/completion/src/completions.rs
+++ b/crates/completion/src/completions.rs
@@ -106,8 +106,9 @@ impl Completions {
106 func: hir::Function, 106 func: hir::Function,
107 local_name: Option<String>, 107 local_name: Option<String>,
108 ) { 108 ) {
109 let item = render_fn(RenderContext::new(ctx), None, local_name, func); 109 if let Some(item) = render_fn(RenderContext::new(ctx), None, local_name, func) {
110 self.add(item) 110 self.add(item)
111 }
111 } 112 }
112 113
113 pub(crate) fn add_variant_pat( 114 pub(crate) fn add_variant_pat(
diff --git a/crates/completion/src/completions/record.rs b/crates/completion/src/completions/record.rs
index 91bf4a8ad..e58b9a274 100644
--- a/crates/completion/src/completions/record.rs
+++ b/crates/completion/src/completions/record.rs
@@ -20,13 +20,17 @@ pub(crate) fn complete_record(acc: &mut Completions, ctx: &CompletionContext) ->
20 20
21 let missing_fields = ctx.sema.record_literal_missing_fields(record_lit); 21 let missing_fields = ctx.sema.record_literal_missing_fields(record_lit);
22 if impl_default_trait && !missing_fields.is_empty() { 22 if impl_default_trait && !missing_fields.is_empty() {
23 let completion_text = "..Default::default()";
24 let completion_text = completion_text
25 .strip_prefix(ctx.token.to_string().as_str())
26 .unwrap_or(completion_text);
23 acc.add( 27 acc.add(
24 CompletionItem::new( 28 CompletionItem::new(
25 CompletionKind::Snippet, 29 CompletionKind::Snippet,
26 ctx.source_range(), 30 ctx.source_range(),
27 "..Default::default()", 31 "..Default::default()",
28 ) 32 )
29 .insert_text("..Default::default()") 33 .insert_text(completion_text)
30 .kind(CompletionItemKind::Field) 34 .kind(CompletionItemKind::Field)
31 .build(), 35 .build(),
32 ); 36 );
@@ -48,7 +52,10 @@ mod tests {
48 use expect_test::{expect, Expect}; 52 use expect_test::{expect, Expect};
49 use ide_db::helpers::FamousDefs; 53 use ide_db::helpers::FamousDefs;
50 54
51 use crate::{test_utils::completion_list, CompletionKind}; 55 use crate::{
56 test_utils::{self, completion_list},
57 CompletionKind,
58 };
52 59
53 fn check(ra_fixture: &str, expect: Expect) { 60 fn check(ra_fixture: &str, expect: Expect) {
54 let actual = completion_list(ra_fixture, CompletionKind::Reference); 61 let actual = completion_list(ra_fixture, CompletionKind::Reference);
@@ -63,6 +70,18 @@ mod tests {
63 expect.assert_eq(&actual); 70 expect.assert_eq(&actual);
64 } 71 }
65 72
73 fn check_edit(what: &str, ra_fixture_before: &str, ra_fixture_after: &str) {
74 test_utils::check_edit(
75 what,
76 &format!(
77 "//- /main.rs crate:main deps:core{}\n{}",
78 ra_fixture_before,
79 FamousDefs::FIXTURE,
80 ),
81 &(ra_fixture_after.to_owned() + "\n"),
82 );
83 }
84
66 #[test] 85 #[test]
67 fn test_record_literal_field_default() { 86 fn test_record_literal_field_default() {
68 let test_code = r#" 87 let test_code = r#"
@@ -102,6 +121,51 @@ fn process(f: S) {
102 } 121 }
103 122
104 #[test] 123 #[test]
124 fn test_record_literal_field_default_completion() {
125 check_edit(
126 "..Default::default()",
127 r#"
128struct S { foo: u32, bar: usize }
129
130impl core::default::Default for S {
131 fn default() -> Self {
132 S {
133 foo: 0,
134 bar: 0,
135 }
136 }
137}
138
139fn process(f: S) {
140 let other = S {
141 foo: 5,
142 .<|>
143 };
144}
145"#,
146 r#"
147struct S { foo: u32, bar: usize }
148
149impl core::default::Default for S {
150 fn default() -> Self {
151 S {
152 foo: 0,
153 bar: 0,
154 }
155 }
156}
157
158fn process(f: S) {
159 let other = S {
160 foo: 5,
161 ..Default::default()
162 };
163}
164"#,
165 );
166 }
167
168 #[test]
105 fn test_record_literal_field_without_default() { 169 fn test_record_literal_field_without_default() {
106 let test_code = r#" 170 let test_code = r#"
107struct S { foo: u32, bar: usize } 171struct S { foo: u32, bar: usize }
diff --git a/crates/completion/src/completions/trait_impl.rs b/crates/completion/src/completions/trait_impl.rs
index c4e0d0669..54bb897e9 100644
--- a/crates/completion/src/completions/trait_impl.rs
+++ b/crates/completion/src/completions/trait_impl.rs
@@ -156,19 +156,21 @@ fn add_function_impl(
156 }; 156 };
157 let range = TextRange::new(fn_def_node.text_range().start(), ctx.source_range().end()); 157 let range = TextRange::new(fn_def_node.text_range().start(), ctx.source_range().end());
158 158
159 let function_decl = function_declaration(&func.source(ctx.db).value); 159 if let Some(src) = func.source(ctx.db) {
160 match ctx.config.snippet_cap { 160 let function_decl = function_declaration(&src.value);
161 Some(cap) => { 161 match ctx.config.snippet_cap {
162 let snippet = format!("{} {{\n $0\n}}", function_decl); 162 Some(cap) => {
163 builder.snippet_edit(cap, TextEdit::replace(range, snippet)) 163 let snippet = format!("{} {{\n $0\n}}", function_decl);
164 } 164 builder.snippet_edit(cap, TextEdit::replace(range, snippet))
165 None => { 165 }
166 let header = format!("{} {{", function_decl); 166 None => {
167 builder.text_edit(TextEdit::replace(range, header)) 167 let header = format!("{} {{", function_decl);
168 builder.text_edit(TextEdit::replace(range, header))
169 }
168 } 170 }
171 .kind(completion_kind)
172 .add_to(acc);
169 } 173 }
170 .kind(completion_kind)
171 .add_to(acc);
172} 174}
173 175
174fn add_type_alias_impl( 176fn add_type_alias_impl(
@@ -200,16 +202,19 @@ fn add_const_impl(
200 let const_name = const_.name(ctx.db).map(|n| n.to_string()); 202 let const_name = const_.name(ctx.db).map(|n| n.to_string());
201 203
202 if let Some(const_name) = const_name { 204 if let Some(const_name) = const_name {
203 let snippet = make_const_compl_syntax(&const_.source(ctx.db).value); 205 if let Some(source) = const_.source(ctx.db) {
204 206 let snippet = make_const_compl_syntax(&source.value);
205 let range = TextRange::new(const_def_node.text_range().start(), ctx.source_range().end()); 207
206 208 let range =
207 CompletionItem::new(CompletionKind::Magic, ctx.source_range(), snippet.clone()) 209 TextRange::new(const_def_node.text_range().start(), ctx.source_range().end());
208 .text_edit(TextEdit::replace(range, snippet)) 210
209 .lookup_by(const_name) 211 CompletionItem::new(CompletionKind::Magic, ctx.source_range(), snippet.clone())
210 .kind(CompletionItemKind::Const) 212 .text_edit(TextEdit::replace(range, snippet))
211 .set_documentation(const_.docs(ctx.db)) 213 .lookup_by(const_name)
212 .add_to(acc); 214 .kind(CompletionItemKind::Const)
215 .set_documentation(const_.docs(ctx.db))
216 .add_to(acc);
217 }
213 } 218 }
214} 219}
215 220
diff --git a/crates/completion/src/render.rs b/crates/completion/src/render.rs
index 1ba7201a1..ac0b2a513 100644
--- a/crates/completion/src/render.rs
+++ b/crates/completion/src/render.rs
@@ -157,8 +157,7 @@ impl<'a> Render<'a> {
157 157
158 let kind = match resolution { 158 let kind = match resolution {
159 ScopeDef::ModuleDef(Function(func)) => { 159 ScopeDef::ModuleDef(Function(func)) => {
160 let item = render_fn(self.ctx, import_to_add, Some(local_name), *func); 160 return render_fn(self.ctx, import_to_add, Some(local_name), *func);
161 return Some(item);
162 } 161 }
163 ScopeDef::ModuleDef(Variant(_)) 162 ScopeDef::ModuleDef(Variant(_))
164 if self.ctx.completion.is_pat_binding_or_const 163 if self.ctx.completion.is_pat_binding_or_const
diff --git a/crates/completion/src/render/const_.rs b/crates/completion/src/render/const_.rs
index 039bdabc0..ce924f309 100644
--- a/crates/completion/src/render/const_.rs
+++ b/crates/completion/src/render/const_.rs
@@ -15,7 +15,7 @@ pub(crate) fn render_const<'a>(
15 ctx: RenderContext<'a>, 15 ctx: RenderContext<'a>,
16 const_: hir::Const, 16 const_: hir::Const,
17) -> Option<CompletionItem> { 17) -> Option<CompletionItem> {
18 ConstRender::new(ctx, const_).render() 18 ConstRender::new(ctx, const_)?.render()
19} 19}
20 20
21#[derive(Debug)] 21#[derive(Debug)]
@@ -26,9 +26,9 @@ struct ConstRender<'a> {
26} 26}
27 27
28impl<'a> ConstRender<'a> { 28impl<'a> ConstRender<'a> {
29 fn new(ctx: RenderContext<'a>, const_: hir::Const) -> ConstRender<'a> { 29 fn new(ctx: RenderContext<'a>, const_: hir::Const) -> Option<ConstRender<'a>> {
30 let ast_node = const_.source(ctx.db()).value; 30 let ast_node = const_.source(ctx.db())?.value;
31 ConstRender { ctx, const_, ast_node } 31 Some(ConstRender { ctx, const_, ast_node })
32 } 32 }
33 33
34 fn render(self) -> Option<CompletionItem> { 34 fn render(self) -> Option<CompletionItem> {
diff --git a/crates/completion/src/render/function.rs b/crates/completion/src/render/function.rs
index 316e05b52..081be14f4 100644
--- a/crates/completion/src/render/function.rs
+++ b/crates/completion/src/render/function.rs
@@ -14,9 +14,9 @@ pub(crate) fn render_fn<'a>(
14 import_to_add: Option<ImportEdit>, 14 import_to_add: Option<ImportEdit>,
15 local_name: Option<String>, 15 local_name: Option<String>,
16 fn_: hir::Function, 16 fn_: hir::Function,
17) -> CompletionItem { 17) -> Option<CompletionItem> {
18 let _p = profile::span("render_fn"); 18 let _p = profile::span("render_fn");
19 FunctionRender::new(ctx, local_name, fn_).render(import_to_add) 19 Some(FunctionRender::new(ctx, local_name, fn_)?.render(import_to_add))
20} 20}
21 21
22#[derive(Debug)] 22#[derive(Debug)]
@@ -32,11 +32,11 @@ impl<'a> FunctionRender<'a> {
32 ctx: RenderContext<'a>, 32 ctx: RenderContext<'a>,
33 local_name: Option<String>, 33 local_name: Option<String>,
34 fn_: hir::Function, 34 fn_: hir::Function,
35 ) -> FunctionRender<'a> { 35 ) -> Option<FunctionRender<'a>> {
36 let name = local_name.unwrap_or_else(|| fn_.name(ctx.db()).to_string()); 36 let name = local_name.unwrap_or_else(|| fn_.name(ctx.db()).to_string());
37 let ast_node = fn_.source(ctx.db()).value; 37 let ast_node = fn_.source(ctx.db())?.value;
38 38
39 FunctionRender { ctx, name, func: fn_, ast_node } 39 Some(FunctionRender { ctx, name, func: fn_, ast_node })
40 } 40 }
41 41
42 fn render(self, import_to_add: Option<ImportEdit>) -> CompletionItem { 42 fn render(self, import_to_add: Option<ImportEdit>) -> CompletionItem {
diff --git a/crates/completion/src/render/macro_.rs b/crates/completion/src/render/macro_.rs
index dac79592f..6f4f9945c 100644
--- a/crates/completion/src/render/macro_.rs
+++ b/crates/completion/src/render/macro_.rs
@@ -39,20 +39,13 @@ impl<'a> MacroRender<'a> {
39 } 39 }
40 40
41 fn render(&self, import_to_add: Option<ImportEdit>) -> Option<CompletionItem> { 41 fn render(&self, import_to_add: Option<ImportEdit>) -> Option<CompletionItem> {
42 // FIXME: Currently proc-macro do not have ast-node,
43 // such that it does not have source
44 // more discussion: https://github.com/rust-analyzer/rust-analyzer/issues/6913
45 if self.macro_.is_proc_macro() {
46 return None;
47 }
48
49 let mut builder = 42 let mut builder =
50 CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), &self.label()) 43 CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), &self.label())
51 .kind(CompletionItemKind::Macro) 44 .kind(CompletionItemKind::Macro)
52 .set_documentation(self.docs.clone()) 45 .set_documentation(self.docs.clone())
53 .set_deprecated(self.ctx.is_deprecated(self.macro_)) 46 .set_deprecated(self.ctx.is_deprecated(self.macro_))
54 .add_import(import_to_add) 47 .add_import(import_to_add)
55 .detail(self.detail()); 48 .set_detail(self.detail());
56 49
57 let needs_bang = self.needs_bang(); 50 let needs_bang = self.needs_bang();
58 builder = match self.ctx.snippet_cap() { 51 builder = match self.ctx.snippet_cap() {
@@ -95,9 +88,9 @@ impl<'a> MacroRender<'a> {
95 format!("{}!", self.name) 88 format!("{}!", self.name)
96 } 89 }
97 90
98 fn detail(&self) -> String { 91 fn detail(&self) -> Option<String> {
99 let ast_node = self.macro_.source(self.ctx.db()).value; 92 let ast_node = self.macro_.source(self.ctx.db())?.value;
100 macro_label(&ast_node) 93 Some(macro_label(&ast_node))
101 } 94 }
102} 95}
103 96
diff --git a/crates/completion/src/render/type_alias.rs b/crates/completion/src/render/type_alias.rs
index 9605c7fa9..69b445b9c 100644
--- a/crates/completion/src/render/type_alias.rs
+++ b/crates/completion/src/render/type_alias.rs
@@ -15,7 +15,7 @@ pub(crate) fn render_type_alias<'a>(
15 ctx: RenderContext<'a>, 15 ctx: RenderContext<'a>,
16 type_alias: hir::TypeAlias, 16 type_alias: hir::TypeAlias,
17) -> Option<CompletionItem> { 17) -> Option<CompletionItem> {
18 TypeAliasRender::new(ctx, type_alias).render() 18 TypeAliasRender::new(ctx, type_alias)?.render()
19} 19}
20 20
21#[derive(Debug)] 21#[derive(Debug)]
@@ -26,9 +26,9 @@ struct TypeAliasRender<'a> {
26} 26}
27 27
28impl<'a> TypeAliasRender<'a> { 28impl<'a> TypeAliasRender<'a> {
29 fn new(ctx: RenderContext<'a>, type_alias: hir::TypeAlias) -> TypeAliasRender<'a> { 29 fn new(ctx: RenderContext<'a>, type_alias: hir::TypeAlias) -> Option<TypeAliasRender<'a>> {
30 let ast_node = type_alias.source(ctx.db()).value; 30 let ast_node = type_alias.source(ctx.db())?.value;
31 TypeAliasRender { ctx, type_alias, ast_node } 31 Some(TypeAliasRender { ctx, type_alias, ast_node })
32 } 32 }
33 33
34 fn render(self) -> Option<CompletionItem> { 34 fn render(self) -> Option<CompletionItem> {
diff --git a/crates/hir/src/attrs.rs b/crates/hir/src/attrs.rs
index d32ce37ed..99fb65bac 100644
--- a/crates/hir/src/attrs.rs
+++ b/crates/hir/src/attrs.rs
@@ -3,15 +3,15 @@ use hir_def::{
3 attr::{Attrs, Documentation}, 3 attr::{Attrs, Documentation},
4 path::ModPath, 4 path::ModPath,
5 resolver::HasResolver, 5 resolver::HasResolver,
6 AttrDefId, ModuleDefId, 6 AttrDefId, GenericParamId, ModuleDefId,
7}; 7};
8use hir_expand::hygiene::Hygiene; 8use hir_expand::hygiene::Hygiene;
9use hir_ty::db::HirDatabase; 9use hir_ty::db::HirDatabase;
10use syntax::ast; 10use syntax::ast;
11 11
12use crate::{ 12use crate::{
13 Adt, Const, Enum, Field, Function, MacroDef, Module, ModuleDef, Static, Struct, Trait, 13 Adt, Const, ConstParam, Enum, Field, Function, GenericParam, LifetimeParam, MacroDef, Module,
14 TypeAlias, Union, Variant, 14 ModuleDef, Static, Struct, Trait, TypeAlias, TypeParam, Union, Variant,
15}; 15};
16 16
17pub trait HasAttrs { 17pub trait HasAttrs {
@@ -62,25 +62,27 @@ impl_has_attrs![
62 (Function, FunctionId), 62 (Function, FunctionId),
63 (Adt, AdtId), 63 (Adt, AdtId),
64 (Module, ModuleId), 64 (Module, ModuleId),
65 (GenericParam, GenericParamId),
65]; 66];
66 67
67macro_rules! impl_has_attrs_adt { 68macro_rules! impl_has_attrs_enum {
68 ($($adt:ident),*) => {$( 69 ($($variant:ident),* for $enum:ident) => {$(
69 impl HasAttrs for $adt { 70 impl HasAttrs for $variant {
70 fn attrs(self, db: &dyn HirDatabase) -> Attrs { 71 fn attrs(self, db: &dyn HirDatabase) -> Attrs {
71 Adt::$adt(self).attrs(db) 72 $enum::$variant(self).attrs(db)
72 } 73 }
73 fn docs(self, db: &dyn HirDatabase) -> Option<Documentation> { 74 fn docs(self, db: &dyn HirDatabase) -> Option<Documentation> {
74 Adt::$adt(self).docs(db) 75 $enum::$variant(self).docs(db)
75 } 76 }
76 fn resolve_doc_path(self, db: &dyn HirDatabase, link: &str, ns: Option<Namespace>) -> Option<ModuleDef> { 77 fn resolve_doc_path(self, db: &dyn HirDatabase, link: &str, ns: Option<Namespace>) -> Option<ModuleDef> {
77 Adt::$adt(self).resolve_doc_path(db, link, ns) 78 $enum::$variant(self).resolve_doc_path(db, link, ns)
78 } 79 }
79 } 80 }
80 )*}; 81 )*};
81} 82}
82 83
83impl_has_attrs_adt![Struct, Union, Enum]; 84impl_has_attrs_enum![Struct, Union, Enum for Adt];
85impl_has_attrs_enum![TypeParam, ConstParam, LifetimeParam for GenericParam];
84 86
85fn resolve_doc_path( 87fn resolve_doc_path(
86 db: &dyn HirDatabase, 88 db: &dyn HirDatabase,
@@ -99,6 +101,12 @@ fn resolve_doc_path(
99 AttrDefId::TraitId(it) => it.resolver(db.upcast()), 101 AttrDefId::TraitId(it) => it.resolver(db.upcast()),
100 AttrDefId::TypeAliasId(it) => it.resolver(db.upcast()), 102 AttrDefId::TypeAliasId(it) => it.resolver(db.upcast()),
101 AttrDefId::ImplId(it) => it.resolver(db.upcast()), 103 AttrDefId::ImplId(it) => it.resolver(db.upcast()),
104 AttrDefId::GenericParamId(it) => match it {
105 GenericParamId::TypeParamId(it) => it.parent,
106 GenericParamId::LifetimeParamId(it) => it.parent,
107 GenericParamId::ConstParamId(it) => it.parent,
108 }
109 .resolver(db.upcast()),
102 AttrDefId::MacroDefId(_) => return None, 110 AttrDefId::MacroDefId(_) => return None,
103 }; 111 };
104 let path = ast::Path::parse(link).ok()?; 112 let path = ast::Path::parse(link).ok()?;
diff --git a/crates/hir/src/code_model.rs b/crates/hir/src/code_model.rs
index b7ded3478..62eccf475 100644
--- a/crates/hir/src/code_model.rs
+++ b/crates/hir/src/code_model.rs
@@ -18,10 +18,10 @@ use hir_def::{
18 resolver::{HasResolver, Resolver}, 18 resolver::{HasResolver, Resolver},
19 src::HasSource as _, 19 src::HasSource as _,
20 type_ref::{Mutability, TypeRef}, 20 type_ref::{Mutability, TypeRef},
21 AdtId, AssocContainerId, AssocItemId, AssocItemLoc, AttrDefId, ConstId, DefWithBodyId, EnumId, 21 AdtId, AssocContainerId, AssocItemId, AssocItemLoc, AttrDefId, ConstId, ConstParamId,
22 FunctionId, GenericDefId, HasModule, ImplId, LifetimeParamId, LocalEnumVariantId, LocalFieldId, 22 DefWithBodyId, EnumId, FunctionId, GenericDefId, HasModule, ImplId, LifetimeParamId,
23 LocalModuleId, Lookup, ModuleId, StaticId, StructId, TraitId, TypeAliasId, TypeParamId, 23 LocalEnumVariantId, LocalFieldId, LocalModuleId, Lookup, ModuleId, StaticId, StructId, TraitId,
24 UnionId, 24 TypeAliasId, TypeParamId, UnionId,
25}; 25};
26use hir_def::{find_path::PrefixKind, item_scope::ItemInNs, visibility::Visibility}; 26use hir_def::{find_path::PrefixKind, item_scope::ItemInNs, visibility::Visibility};
27use hir_expand::{ 27use hir_expand::{
@@ -39,7 +39,7 @@ use hir_ty::{
39 TyDefId, TyKind, TypeCtor, 39 TyDefId, TyKind, TypeCtor,
40}; 40};
41use rustc_hash::FxHashSet; 41use rustc_hash::FxHashSet;
42use stdx::impl_from; 42use stdx::{format_to, impl_from};
43use syntax::{ 43use syntax::{
44 ast::{self, AttrsOwner, NameOwner}, 44 ast::{self, AttrsOwner, NameOwner},
45 AstNode, SmolStr, 45 AstNode, SmolStr,
@@ -797,6 +797,19 @@ impl Function {
797 pub fn has_body(self, db: &dyn HirDatabase) -> bool { 797 pub fn has_body(self, db: &dyn HirDatabase) -> bool {
798 db.function_data(self.id).has_body 798 db.function_data(self.id).has_body
799 } 799 }
800
801 /// A textual representation of the HIR of this function for debugging purposes.
802 pub fn debug_hir(self, db: &dyn HirDatabase) -> String {
803 let body = db.body(self.id.into());
804
805 let mut result = String::new();
806 format_to!(result, "HIR expressions in the body of `{}`:\n", self.name(db));
807 for (id, expr) in body.exprs.iter() {
808 format_to!(result, "{:?}: {:?}\n", id, expr);
809 }
810
811 result
812 }
800} 813}
801 814
802// Note: logically, this belongs to `hir_ty`, but we are not using it there yet. 815// Note: logically, this belongs to `hir_ty`, but we are not using it there yet.
@@ -983,13 +996,7 @@ impl MacroDef {
983 996
984 /// XXX: this parses the file 997 /// XXX: this parses the file
985 pub fn name(self, db: &dyn HirDatabase) -> Option<Name> { 998 pub fn name(self, db: &dyn HirDatabase) -> Option<Name> {
986 // FIXME: Currently proc-macro do not have ast-node, 999 self.source(db)?.value.name().map(|it| it.as_name())
987 // such that it does not have source
988 // more discussion: https://github.com/rust-analyzer/rust-analyzer/issues/6913
989 if self.is_proc_macro() {
990 return None;
991 }
992 self.source(db).value.name().map(|it| it.as_name())
993 } 1000 }
994 1001
995 /// Indicate it is a proc-macro 1002 /// Indicate it is a proc-macro
@@ -1125,7 +1132,12 @@ impl GenericDef {
1125 id: LifetimeParamId { parent: self.into(), local_id }, 1132 id: LifetimeParamId { parent: self.into(), local_id },
1126 }) 1133 })
1127 .map(GenericParam::LifetimeParam); 1134 .map(GenericParam::LifetimeParam);
1128 ty_params.chain(lt_params).collect() 1135 let const_params = generics
1136 .consts
1137 .iter()
1138 .map(|(local_id, _)| ConstParam { id: ConstParamId { parent: self.into(), local_id } })
1139 .map(GenericParam::ConstParam);
1140 ty_params.chain(lt_params).chain(const_params).collect()
1129 } 1141 }
1130 1142
1131 pub fn type_params(self, db: &dyn HirDatabase) -> Vec<TypeParam> { 1143 pub fn type_params(self, db: &dyn HirDatabase) -> Vec<TypeParam> {
@@ -1237,8 +1249,9 @@ impl Label {
1237pub enum GenericParam { 1249pub enum GenericParam {
1238 TypeParam(TypeParam), 1250 TypeParam(TypeParam),
1239 LifetimeParam(LifetimeParam), 1251 LifetimeParam(LifetimeParam),
1252 ConstParam(ConstParam),
1240} 1253}
1241impl_from!(TypeParam, LifetimeParam for GenericParam); 1254impl_from!(TypeParam, LifetimeParam, ConstParam for GenericParam);
1242 1255
1243#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] 1256#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1244pub struct TypeParam { 1257pub struct TypeParam {
@@ -1300,6 +1313,26 @@ impl LifetimeParam {
1300 } 1313 }
1301} 1314}
1302 1315
1316#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1317pub struct ConstParam {
1318 pub(crate) id: ConstParamId,
1319}
1320
1321impl ConstParam {
1322 pub fn name(self, db: &dyn HirDatabase) -> Name {
1323 let params = db.generic_params(self.id.parent);
1324 params.consts[self.id.local_id].name.clone()
1325 }
1326
1327 pub fn module(self, db: &dyn HirDatabase) -> Module {
1328 self.id.parent.module(db.upcast()).into()
1329 }
1330
1331 pub fn parent(self, _db: &dyn HirDatabase) -> GenericDef {
1332 self.id.parent.into()
1333 }
1334}
1335
1303#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 1336#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1304pub struct Impl { 1337pub struct Impl {
1305 pub(crate) id: ImplId, 1338 pub(crate) id: ImplId,
@@ -1352,7 +1385,7 @@ impl Impl {
1352 } 1385 }
1353 1386
1354 pub fn is_builtin_derive(self, db: &dyn HirDatabase) -> Option<InFile<ast::Attr>> { 1387 pub fn is_builtin_derive(self, db: &dyn HirDatabase) -> Option<InFile<ast::Attr>> {
1355 let src = self.source(db); 1388 let src = self.source(db)?;
1356 let item = src.file_id.is_builtin_derive(db.upcast())?; 1389 let item = src.file_id.is_builtin_derive(db.upcast())?;
1357 let hygenic = hir_expand::hygiene::Hygiene::new(db.upcast(), item.file_id); 1390 let hygenic = hir_expand::hygiene::Hygiene::new(db.upcast(), item.file_id);
1358 1391
diff --git a/crates/hir/src/from_id.rs b/crates/hir/src/from_id.rs
index a0792b9a6..3e47a5e9d 100644
--- a/crates/hir/src/from_id.rs
+++ b/crates/hir/src/from_id.rs
@@ -6,13 +6,13 @@
6use hir_def::{ 6use hir_def::{
7 expr::{LabelId, PatId}, 7 expr::{LabelId, PatId},
8 item_scope::ItemInNs, 8 item_scope::ItemInNs,
9 AdtId, AssocItemId, DefWithBodyId, EnumVariantId, FieldId, GenericDefId, ModuleDefId, 9 AdtId, AssocItemId, DefWithBodyId, EnumVariantId, FieldId, GenericDefId, GenericParamId,
10 VariantId, 10 ModuleDefId, VariantId,
11}; 11};
12 12
13use crate::{ 13use crate::{
14 Adt, AssocItem, DefWithBody, Field, GenericDef, Label, Local, MacroDef, ModuleDef, Variant, 14 code_model::GenericParam, Adt, AssocItem, DefWithBody, Field, GenericDef, Label, Local,
15 VariantDef, 15 MacroDef, ModuleDef, Variant, VariantDef,
16}; 16};
17 17
18macro_rules! from_id { 18macro_rules! from_id {
@@ -44,6 +44,7 @@ from_id![
44 (hir_def::ImplId, crate::Impl), 44 (hir_def::ImplId, crate::Impl),
45 (hir_def::TypeParamId, crate::TypeParam), 45 (hir_def::TypeParamId, crate::TypeParam),
46 (hir_def::LifetimeParamId, crate::LifetimeParam), 46 (hir_def::LifetimeParamId, crate::LifetimeParam),
47 (hir_def::ConstParamId, crate::ConstParam),
47 (hir_expand::MacroDefId, crate::MacroDef) 48 (hir_expand::MacroDefId, crate::MacroDef)
48]; 49];
49 50
@@ -67,6 +68,26 @@ impl From<Adt> for AdtId {
67 } 68 }
68} 69}
69 70
71impl From<GenericParamId> for GenericParam {
72 fn from(id: GenericParamId) -> Self {
73 match id {
74 GenericParamId::TypeParamId(it) => GenericParam::TypeParam(it.into()),
75 GenericParamId::LifetimeParamId(it) => GenericParam::LifetimeParam(it.into()),
76 GenericParamId::ConstParamId(it) => GenericParam::ConstParam(it.into()),
77 }
78 }
79}
80
81impl From<GenericParam> for GenericParamId {
82 fn from(id: GenericParam) -> Self {
83 match id {
84 GenericParam::TypeParam(it) => GenericParamId::TypeParamId(it.id),
85 GenericParam::LifetimeParam(it) => GenericParamId::LifetimeParamId(it.id),
86 GenericParam::ConstParam(it) => GenericParamId::ConstParamId(it.id),
87 }
88 }
89}
90
70impl From<EnumVariantId> for Variant { 91impl From<EnumVariantId> for Variant {
71 fn from(id: EnumVariantId) -> Self { 92 fn from(id: EnumVariantId) -> Self {
72 Variant { parent: id.parent.into(), id: id.local_id } 93 Variant { parent: id.parent.into(), id: id.local_id }
diff --git a/crates/hir/src/has_source.rs b/crates/hir/src/has_source.rs
index 0dc07c33e..7c57d8378 100644
--- a/crates/hir/src/has_source.rs
+++ b/crates/hir/src/has_source.rs
@@ -10,13 +10,13 @@ use hir_expand::InFile;
10use syntax::ast; 10use syntax::ast;
11 11
12use crate::{ 12use crate::{
13 db::HirDatabase, Const, Enum, Field, FieldSource, Function, Impl, LifetimeParam, MacroDef, 13 db::HirDatabase, Const, ConstParam, Enum, Field, FieldSource, Function, Impl, LifetimeParam,
14 Module, Static, Struct, Trait, TypeAlias, TypeParam, Union, Variant, 14 MacroDef, Module, Static, Struct, Trait, TypeAlias, TypeParam, Union, Variant,
15}; 15};
16 16
17pub trait HasSource { 17pub trait HasSource {
18 type Ast; 18 type Ast;
19 fn source(self, db: &dyn HirDatabase) -> InFile<Self::Ast>; 19 fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>>;
20} 20}
21 21
22/// NB: Module is !HasSource, because it has two source nodes at the same time: 22/// NB: Module is !HasSource, because it has two source nodes at the same time:
@@ -46,97 +46,104 @@ impl Module {
46 46
47impl HasSource for Field { 47impl HasSource for Field {
48 type Ast = FieldSource; 48 type Ast = FieldSource;
49 fn source(self, db: &dyn HirDatabase) -> InFile<FieldSource> { 49 fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
50 let var = VariantId::from(self.parent); 50 let var = VariantId::from(self.parent);
51 let src = var.child_source(db.upcast()); 51 let src = var.child_source(db.upcast());
52 src.map(|it| match it[self.id].clone() { 52 let field_source = src.map(|it| match it[self.id].clone() {
53 Either::Left(it) => FieldSource::Pos(it), 53 Either::Left(it) => FieldSource::Pos(it),
54 Either::Right(it) => FieldSource::Named(it), 54 Either::Right(it) => FieldSource::Named(it),
55 }) 55 });
56 Some(field_source)
56 } 57 }
57} 58}
58impl HasSource for Struct { 59impl HasSource for Struct {
59 type Ast = ast::Struct; 60 type Ast = ast::Struct;
60 fn source(self, db: &dyn HirDatabase) -> InFile<ast::Struct> { 61 fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
61 self.id.lookup(db.upcast()).source(db.upcast()) 62 Some(self.id.lookup(db.upcast()).source(db.upcast()))
62 } 63 }
63} 64}
64impl HasSource for Union { 65impl HasSource for Union {
65 type Ast = ast::Union; 66 type Ast = ast::Union;
66 fn source(self, db: &dyn HirDatabase) -> InFile<ast::Union> { 67 fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
67 self.id.lookup(db.upcast()).source(db.upcast()) 68 Some(self.id.lookup(db.upcast()).source(db.upcast()))
68 } 69 }
69} 70}
70impl HasSource for Enum { 71impl HasSource for Enum {
71 type Ast = ast::Enum; 72 type Ast = ast::Enum;
72 fn source(self, db: &dyn HirDatabase) -> InFile<ast::Enum> { 73 fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
73 self.id.lookup(db.upcast()).source(db.upcast()) 74 Some(self.id.lookup(db.upcast()).source(db.upcast()))
74 } 75 }
75} 76}
76impl HasSource for Variant { 77impl HasSource for Variant {
77 type Ast = ast::Variant; 78 type Ast = ast::Variant;
78 fn source(self, db: &dyn HirDatabase) -> InFile<ast::Variant> { 79 fn source(self, db: &dyn HirDatabase) -> Option<InFile<ast::Variant>> {
79 self.parent.id.child_source(db.upcast()).map(|map| map[self.id].clone()) 80 Some(self.parent.id.child_source(db.upcast()).map(|map| map[self.id].clone()))
80 } 81 }
81} 82}
82impl HasSource for Function { 83impl HasSource for Function {
83 type Ast = ast::Fn; 84 type Ast = ast::Fn;
84 fn source(self, db: &dyn HirDatabase) -> InFile<ast::Fn> { 85 fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
85 self.id.lookup(db.upcast()).source(db.upcast()) 86 Some(self.id.lookup(db.upcast()).source(db.upcast()))
86 } 87 }
87} 88}
88impl HasSource for Const { 89impl HasSource for Const {
89 type Ast = ast::Const; 90 type Ast = ast::Const;
90 fn source(self, db: &dyn HirDatabase) -> InFile<ast::Const> { 91 fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
91 self.id.lookup(db.upcast()).source(db.upcast()) 92 Some(self.id.lookup(db.upcast()).source(db.upcast()))
92 } 93 }
93} 94}
94impl HasSource for Static { 95impl HasSource for Static {
95 type Ast = ast::Static; 96 type Ast = ast::Static;
96 fn source(self, db: &dyn HirDatabase) -> InFile<ast::Static> { 97 fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
97 self.id.lookup(db.upcast()).source(db.upcast()) 98 Some(self.id.lookup(db.upcast()).source(db.upcast()))
98 } 99 }
99} 100}
100impl HasSource for Trait { 101impl HasSource for Trait {
101 type Ast = ast::Trait; 102 type Ast = ast::Trait;
102 fn source(self, db: &dyn HirDatabase) -> InFile<ast::Trait> { 103 fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
103 self.id.lookup(db.upcast()).source(db.upcast()) 104 Some(self.id.lookup(db.upcast()).source(db.upcast()))
104 } 105 }
105} 106}
106impl HasSource for TypeAlias { 107impl HasSource for TypeAlias {
107 type Ast = ast::TypeAlias; 108 type Ast = ast::TypeAlias;
108 fn source(self, db: &dyn HirDatabase) -> InFile<ast::TypeAlias> { 109 fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
109 self.id.lookup(db.upcast()).source(db.upcast()) 110 Some(self.id.lookup(db.upcast()).source(db.upcast()))
110 } 111 }
111} 112}
112impl HasSource for MacroDef { 113impl HasSource for MacroDef {
113 type Ast = ast::Macro; 114 type Ast = ast::Macro;
114 fn source(self, db: &dyn HirDatabase) -> InFile<ast::Macro> { 115 fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
115 InFile { 116 let ast_id = self.id.ast_id?;
116 file_id: self.id.ast_id.expect("MacroDef without ast_id").file_id, 117 Some(InFile { file_id: ast_id.file_id, value: ast_id.to_node(db.upcast()) })
117 value: self.id.ast_id.expect("MacroDef without ast_id").to_node(db.upcast()),
118 }
119 } 118 }
120} 119}
121impl HasSource for Impl { 120impl HasSource for Impl {
122 type Ast = ast::Impl; 121 type Ast = ast::Impl;
123 fn source(self, db: &dyn HirDatabase) -> InFile<ast::Impl> { 122 fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
124 self.id.lookup(db.upcast()).source(db.upcast()) 123 Some(self.id.lookup(db.upcast()).source(db.upcast()))
125 } 124 }
126} 125}
127 126
128impl HasSource for TypeParam { 127impl HasSource for TypeParam {
129 type Ast = Either<ast::Trait, ast::TypeParam>; 128 type Ast = Either<ast::Trait, ast::TypeParam>;
130 fn source(self, db: &dyn HirDatabase) -> InFile<Self::Ast> { 129 fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
131 let child_source = self.id.parent.child_source(db.upcast()); 130 let child_source = self.id.parent.child_source(db.upcast());
132 child_source.map(|it| it[self.id.local_id].clone()) 131 Some(child_source.map(|it| it[self.id.local_id].clone()))
133 } 132 }
134} 133}
135 134
136impl HasSource for LifetimeParam { 135impl HasSource for LifetimeParam {
137 type Ast = ast::LifetimeParam; 136 type Ast = ast::LifetimeParam;
138 fn source(self, db: &dyn HirDatabase) -> InFile<Self::Ast> { 137 fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
138 let child_source = self.id.parent.child_source(db.upcast());
139 Some(child_source.map(|it| it[self.id.local_id].clone()))
140 }
141}
142
143impl HasSource for ConstParam {
144 type Ast = ast::ConstParam;
145 fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
139 let child_source = self.id.parent.child_source(db.upcast()); 146 let child_source = self.id.parent.child_source(db.upcast());
140 child_source.map(|it| it[self.id.local_id].clone()) 147 Some(child_source.map(|it| it[self.id.local_id].clone()))
141 } 148 }
142} 149}
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index 7ac9fd507..769945c47 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -34,9 +34,10 @@ pub use crate::{
34 attrs::{HasAttrs, Namespace}, 34 attrs::{HasAttrs, Namespace},
35 code_model::{ 35 code_model::{
36 Access, Adt, AsAssocItem, AssocItem, AssocItemContainer, Callable, CallableKind, Const, 36 Access, Adt, AsAssocItem, AssocItem, AssocItemContainer, Callable, CallableKind, Const,
37 Crate, CrateDependency, DefWithBody, Enum, Field, FieldSource, Function, GenericDef, 37 ConstParam, Crate, CrateDependency, DefWithBody, Enum, Field, FieldSource, Function,
38 HasVisibility, Impl, Label, LifetimeParam, Local, MacroDef, Module, ModuleDef, ScopeDef, 38 GenericDef, GenericParam, HasVisibility, Impl, Label, LifetimeParam, Local, MacroDef,
39 Static, Struct, Trait, Type, TypeAlias, TypeParam, Union, Variant, VariantDef, 39 Module, ModuleDef, ScopeDef, Static, Struct, Trait, Type, TypeAlias, TypeParam, Union,
40 Variant, VariantDef,
40 }, 41 },
41 has_source::HasSource, 42 has_source::HasSource,
42 semantics::{PathResolution, Semantics, SemanticsScope}, 43 semantics::{PathResolution, Semantics, SemanticsScope},
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs
index 67cd16e31..cd689c869 100644
--- a/crates/hir/src/semantics.rs
+++ b/crates/hir/src/semantics.rs
@@ -25,9 +25,9 @@ use crate::{
25 diagnostics::Diagnostic, 25 diagnostics::Diagnostic,
26 semantics::source_to_def::{ChildContainer, SourceToDefCache, SourceToDefCtx}, 26 semantics::source_to_def::{ChildContainer, SourceToDefCache, SourceToDefCtx},
27 source_analyzer::{resolve_hir_path, SourceAnalyzer}, 27 source_analyzer::{resolve_hir_path, SourceAnalyzer},
28 AssocItem, Callable, Crate, Field, Function, HirFileId, Impl, InFile, Label, LifetimeParam, 28 AssocItem, Callable, ConstParam, Crate, Field, Function, HirFileId, Impl, InFile, Label,
29 Local, MacroDef, Module, ModuleDef, Name, Path, ScopeDef, Trait, Type, TypeAlias, TypeParam, 29 LifetimeParam, Local, MacroDef, Module, ModuleDef, Name, Path, ScopeDef, Trait, Type,
30 VariantDef, 30 TypeAlias, TypeParam, VariantDef,
31}; 31};
32 32
33#[derive(Debug, Clone, PartialEq, Eq)] 33#[derive(Debug, Clone, PartialEq, Eq)]
@@ -38,6 +38,7 @@ pub enum PathResolution {
38 Local(Local), 38 Local(Local),
39 /// A generic parameter 39 /// A generic parameter
40 TypeParam(TypeParam), 40 TypeParam(TypeParam),
41 ConstParam(ConstParam),
41 SelfType(Impl), 42 SelfType(Impl),
42 Macro(MacroDef), 43 Macro(MacroDef),
43 AssocItem(AssocItem), 44 AssocItem(AssocItem),
@@ -59,7 +60,9 @@ impl PathResolution {
59 PathResolution::Def(ModuleDef::TypeAlias(alias)) => { 60 PathResolution::Def(ModuleDef::TypeAlias(alias)) => {
60 Some(TypeNs::TypeAliasId((*alias).into())) 61 Some(TypeNs::TypeAliasId((*alias).into()))
61 } 62 }
62 PathResolution::Local(_) | PathResolution::Macro(_) => None, 63 PathResolution::Local(_) | PathResolution::Macro(_) | PathResolution::ConstParam(_) => {
64 None
65 }
63 PathResolution::TypeParam(param) => Some(TypeNs::GenericParam((*param).into())), 66 PathResolution::TypeParam(param) => Some(TypeNs::GenericParam((*param).into())),
64 PathResolution::SelfType(impl_def) => Some(TypeNs::SelfType((*impl_def).into())), 67 PathResolution::SelfType(impl_def) => Some(TypeNs::SelfType((*impl_def).into())),
65 PathResolution::AssocItem(AssocItem::Const(_)) 68 PathResolution::AssocItem(AssocItem::Const(_))
@@ -744,6 +747,7 @@ to_def_impls![
744 (crate::Variant, ast::Variant, enum_variant_to_def), 747 (crate::Variant, ast::Variant, enum_variant_to_def),
745 (crate::TypeParam, ast::TypeParam, type_param_to_def), 748 (crate::TypeParam, ast::TypeParam, type_param_to_def),
746 (crate::LifetimeParam, ast::LifetimeParam, lifetime_param_to_def), 749 (crate::LifetimeParam, ast::LifetimeParam, lifetime_param_to_def),
750 (crate::ConstParam, ast::ConstParam, const_param_to_def),
747 (crate::MacroDef, ast::MacroRules, macro_rules_to_def), 751 (crate::MacroDef, ast::MacroRules, macro_rules_to_def),
748 (crate::Local, ast::IdentPat, bind_pat_to_def), 752 (crate::Local, ast::IdentPat, bind_pat_to_def),
749 (crate::Label, ast::Label, label_to_def), 753 (crate::Label, ast::Label, label_to_def),
diff --git a/crates/hir/src/semantics/source_to_def.rs b/crates/hir/src/semantics/source_to_def.rs
index 424e6e8a9..4b9ebff72 100644
--- a/crates/hir/src/semantics/source_to_def.rs
+++ b/crates/hir/src/semantics/source_to_def.rs
@@ -6,9 +6,9 @@ use hir_def::{
6 dyn_map::DynMap, 6 dyn_map::DynMap,
7 expr::{LabelId, PatId}, 7 expr::{LabelId, PatId},
8 keys::{self, Key}, 8 keys::{self, Key},
9 ConstId, DefWithBodyId, EnumId, EnumVariantId, FieldId, FunctionId, GenericDefId, ImplId, 9 ConstId, ConstParamId, DefWithBodyId, EnumId, EnumVariantId, FieldId, FunctionId, GenericDefId,
10 LifetimeParamId, ModuleId, StaticId, StructId, TraitId, TypeAliasId, TypeParamId, UnionId, 10 ImplId, LifetimeParamId, ModuleId, StaticId, StructId, TraitId, TypeAliasId, TypeParamId,
11 VariantId, 11 UnionId, VariantId,
12}; 12};
13use hir_expand::{name::AsName, AstId, MacroDefKind}; 13use hir_expand::{name::AsName, AstId, MacroDefKind};
14use rustc_hash::FxHashMap; 14use rustc_hash::FxHashMap;
@@ -157,6 +157,18 @@ impl SourceToDefCtx<'_, '_> {
157 dyn_map[keys::LIFETIME_PARAM].get(&src).copied() 157 dyn_map[keys::LIFETIME_PARAM].get(&src).copied()
158 } 158 }
159 159
160 pub(super) fn const_param_to_def(
161 &mut self,
162 src: InFile<ast::ConstParam>,
163 ) -> Option<ConstParamId> {
164 let container: ChildContainer =
165 self.find_generic_param_container(src.as_ref().map(|it| it.syntax()))?.into();
166 let db = self.db;
167 let dyn_map =
168 &*self.cache.entry(container).or_insert_with(|| container.child_by_source(db));
169 dyn_map[keys::CONST_PARAM].get(&src).copied()
170 }
171
160 // FIXME: use DynMap as well? 172 // FIXME: use DynMap as well?
161 pub(super) fn macro_rules_to_def( 173 pub(super) fn macro_rules_to_def(
162 &mut self, 174 &mut self,
diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs
index bddc49c05..30a8e513d 100644
--- a/crates/hir/src/source_analyzer.rs
+++ b/crates/hir/src/source_analyzer.rs
@@ -479,6 +479,7 @@ pub(crate) fn resolve_hir_path(
479 ValueNs::StructId(it) => PathResolution::Def(Struct::from(it).into()), 479 ValueNs::StructId(it) => PathResolution::Def(Struct::from(it).into()),
480 ValueNs::EnumVariantId(it) => PathResolution::Def(Variant::from(it).into()), 480 ValueNs::EnumVariantId(it) => PathResolution::Def(Variant::from(it).into()),
481 ValueNs::ImplSelf(impl_id) => PathResolution::SelfType(impl_id.into()), 481 ValueNs::ImplSelf(impl_id) => PathResolution::SelfType(impl_id.into()),
482 ValueNs::GenericParam(it) => PathResolution::ConstParam(it.into()),
482 }; 483 };
483 Some(res) 484 Some(res)
484 }); 485 });
diff --git a/crates/hir_def/Cargo.toml b/crates/hir_def/Cargo.toml
index e8b581e2f..7ef966cd2 100644
--- a/crates/hir_def/Cargo.toml
+++ b/crates/hir_def/Cargo.toml
@@ -33,4 +33,4 @@ cfg = { path = "../cfg", version = "0.0.0" }
33tt = { path = "../tt", version = "0.0.0" } 33tt = { path = "../tt", version = "0.0.0" }
34 34
35[dev-dependencies] 35[dev-dependencies]
36expect-test = "1.0" 36expect-test = "1.1"
diff --git a/crates/hir_def/src/attr.rs b/crates/hir_def/src/attr.rs
index 042e119b1..6b79e7bad 100644
--- a/crates/hir_def/src/attr.rs
+++ b/crates/hir_def/src/attr.rs
@@ -21,7 +21,7 @@ use crate::{
21 nameres::ModuleSource, 21 nameres::ModuleSource,
22 path::{ModPath, PathKind}, 22 path::{ModPath, PathKind},
23 src::HasChildSource, 23 src::HasChildSource,
24 AdtId, AttrDefId, Lookup, 24 AdtId, AttrDefId, GenericParamId, Lookup,
25}; 25};
26 26
27/// Holds documentation 27/// Holds documentation
@@ -235,6 +235,25 @@ impl Attrs {
235 AttrDefId::StaticId(it) => attrs_from_item_tree(it.lookup(db).id, db), 235 AttrDefId::StaticId(it) => attrs_from_item_tree(it.lookup(db).id, db),
236 AttrDefId::FunctionId(it) => attrs_from_item_tree(it.lookup(db).id, db), 236 AttrDefId::FunctionId(it) => attrs_from_item_tree(it.lookup(db).id, db),
237 AttrDefId::TypeAliasId(it) => attrs_from_item_tree(it.lookup(db).id, db), 237 AttrDefId::TypeAliasId(it) => attrs_from_item_tree(it.lookup(db).id, db),
238 AttrDefId::GenericParamId(it) => match it {
239 GenericParamId::TypeParamId(it) => {
240 let src = it.parent.child_source(db);
241 RawAttrs::from_attrs_owner(
242 db,
243 src.with_value(
244 src.value[it.local_id].as_ref().either(|it| it as _, |it| it as _),
245 ),
246 )
247 }
248 GenericParamId::LifetimeParamId(it) => {
249 let src = it.parent.child_source(db);
250 RawAttrs::from_attrs_owner(db, src.with_value(&src.value[it.local_id]))
251 }
252 GenericParamId::ConstParamId(it) => {
253 let src = it.parent.child_source(db);
254 RawAttrs::from_attrs_owner(db, src.with_value(&src.value[it.local_id]))
255 }
256 },
238 }; 257 };
239 258
240 raw_attrs.filter(db, def.krate(db)) 259 raw_attrs.filter(db, def.krate(db))
@@ -260,14 +279,13 @@ impl Attrs {
260 } 279 }
261 280
262 pub fn docs(&self) -> Option<Documentation> { 281 pub fn docs(&self) -> Option<Documentation> {
263 let docs = self 282 let docs = self.by_key("doc").attrs().flat_map(|attr| match attr.input.as_ref()? {
264 .by_key("doc") 283 AttrInput::Literal(s) => Some(s),
265 .attrs() 284 AttrInput::TokenTree(_) => None,
266 .flat_map(|attr| match attr.input.as_ref()? { 285 });
267 AttrInput::Literal(s) => Some(s), 286 // FIXME: Replace `Itertools::intersperse` with `Iterator::intersperse[_with]` until the
268 AttrInput::TokenTree(_) => None, 287 // libstd api gets stabilized (https://github.com/rust-lang/rust/issues/79524).
269 }) 288 let docs = Itertools::intersperse(docs, &SmolStr::new_inline("\n"))
270 .intersperse(&SmolStr::new_inline("\n"))
271 .map(|it| it.as_str()) 289 .map(|it| it.as_str())
272 .collect::<String>(); 290 .collect::<String>();
273 if docs.is_empty() { 291 if docs.is_empty() {
diff --git a/crates/hir_def/src/generics.rs b/crates/hir_def/src/generics.rs
index bb8fca009..9b5b886c2 100644
--- a/crates/hir_def/src/generics.rs
+++ b/crates/hir_def/src/generics.rs
@@ -21,11 +21,11 @@ use crate::{
21 keys, 21 keys,
22 src::{HasChildSource, HasSource}, 22 src::{HasChildSource, HasSource},
23 type_ref::{LifetimeRef, TypeBound, TypeRef}, 23 type_ref::{LifetimeRef, TypeBound, TypeRef},
24 AdtId, GenericDefId, LifetimeParamId, LocalLifetimeParamId, LocalTypeParamId, Lookup, 24 AdtId, ConstParamId, GenericDefId, LifetimeParamId, LocalConstParamId, LocalLifetimeParamId,
25 TypeParamId, 25 LocalTypeParamId, Lookup, TypeParamId,
26}; 26};
27 27
28/// Data about a generic parameter (to a function, struct, impl, ...). 28/// Data about a generic type parameter (to a function, struct, impl, ...).
29#[derive(Clone, PartialEq, Eq, Debug)] 29#[derive(Clone, PartialEq, Eq, Debug)]
30pub struct TypeParamData { 30pub struct TypeParamData {
31 pub name: Option<Name>, 31 pub name: Option<Name>,
@@ -33,12 +33,19 @@ pub struct TypeParamData {
33 pub provenance: TypeParamProvenance, 33 pub provenance: TypeParamProvenance,
34} 34}
35 35
36/// Data about a generic parameter (to a function, struct, impl, ...). 36/// Data about a generic lifetime parameter (to a function, struct, impl, ...).
37#[derive(Clone, PartialEq, Eq, Debug)] 37#[derive(Clone, PartialEq, Eq, Debug)]
38pub struct LifetimeParamData { 38pub struct LifetimeParamData {
39 pub name: Name, 39 pub name: Name,
40} 40}
41 41
42/// Data about a generic const parameter (to a function, struct, impl, ...).
43#[derive(Clone, PartialEq, Eq, Debug)]
44pub struct ConstParamData {
45 pub name: Name,
46 pub ty: TypeRef,
47}
48
42#[derive(Copy, Clone, PartialEq, Eq, Debug)] 49#[derive(Copy, Clone, PartialEq, Eq, Debug)]
43pub enum TypeParamProvenance { 50pub enum TypeParamProvenance {
44 TypeParamList, 51 TypeParamList,
@@ -51,6 +58,7 @@ pub enum TypeParamProvenance {
51pub struct GenericParams { 58pub struct GenericParams {
52 pub types: Arena<TypeParamData>, 59 pub types: Arena<TypeParamData>,
53 pub lifetimes: Arena<LifetimeParamData>, 60 pub lifetimes: Arena<LifetimeParamData>,
61 pub consts: Arena<ConstParamData>,
54 pub where_predicates: Vec<WherePredicate>, 62 pub where_predicates: Vec<WherePredicate>,
55} 63}
56 64
@@ -76,6 +84,7 @@ pub enum WherePredicateTypeTarget {
76pub(crate) struct SourceMap { 84pub(crate) struct SourceMap {
77 pub(crate) type_params: ArenaMap<LocalTypeParamId, Either<ast::Trait, ast::TypeParam>>, 85 pub(crate) type_params: ArenaMap<LocalTypeParamId, Either<ast::Trait, ast::TypeParam>>,
78 lifetime_params: ArenaMap<LocalLifetimeParamId, ast::LifetimeParam>, 86 lifetime_params: ArenaMap<LocalLifetimeParamId, ast::LifetimeParam>,
87 const_params: ArenaMap<LocalConstParamId, ast::ConstParam>,
79} 88}
80 89
81impl GenericParams { 90impl GenericParams {
@@ -268,6 +277,13 @@ impl GenericParams {
268 let lifetime_ref = LifetimeRef::new_name(name); 277 let lifetime_ref = LifetimeRef::new_name(name);
269 self.fill_bounds(&lower_ctx, &lifetime_param, Either::Right(lifetime_ref)); 278 self.fill_bounds(&lower_ctx, &lifetime_param, Either::Right(lifetime_ref));
270 } 279 }
280 for const_param in params.const_params() {
281 let name = const_param.name().map_or_else(Name::missing, |it| it.as_name());
282 let ty = const_param.ty().map_or(TypeRef::Error, |it| TypeRef::from_ast(lower_ctx, it));
283 let param = ConstParamData { name, ty };
284 let param_id = self.consts.alloc(param);
285 sm.const_params.insert(param_id, const_param.clone());
286 }
271 } 287 }
272 288
273 fn fill_where_predicates(&mut self, lower_ctx: &LowerCtx, where_clause: ast::WhereClause) { 289 fn fill_where_predicates(&mut self, lower_ctx: &LowerCtx, where_clause: ast::WhereClause) {
@@ -353,12 +369,16 @@ impl GenericParams {
353 }); 369 });
354 } 370 }
355 371
356 pub fn find_by_name(&self, name: &Name) -> Option<LocalTypeParamId> { 372 pub fn find_type_by_name(&self, name: &Name) -> Option<LocalTypeParamId> {
357 self.types 373 self.types
358 .iter() 374 .iter()
359 .find_map(|(id, p)| if p.name.as_ref() == Some(name) { Some(id) } else { None }) 375 .find_map(|(id, p)| if p.name.as_ref() == Some(name) { Some(id) } else { None })
360 } 376 }
361 377
378 pub fn find_const_by_name(&self, name: &Name) -> Option<LocalConstParamId> {
379 self.consts.iter().find_map(|(id, p)| if p.name == *name { Some(id) } else { None })
380 }
381
362 pub fn find_trait_self_param(&self) -> Option<LocalTypeParamId> { 382 pub fn find_trait_self_param(&self) -> Option<LocalTypeParamId> {
363 self.types.iter().find_map(|(id, p)| { 383 self.types.iter().find_map(|(id, p)| {
364 if p.provenance == TypeParamProvenance::TraitSelf { 384 if p.provenance == TypeParamProvenance::TraitSelf {
@@ -390,6 +410,16 @@ impl HasChildSource<LocalLifetimeParamId> for GenericDefId {
390 } 410 }
391} 411}
392 412
413impl HasChildSource<LocalConstParamId> for GenericDefId {
414 type Value = ast::ConstParam;
415 fn child_source(
416 &self,
417 db: &dyn DefDatabase,
418 ) -> InFile<ArenaMap<LocalConstParamId, Self::Value>> {
419 GenericParams::new(db, *self).1.map(|source_maps| source_maps.const_params)
420 }
421}
422
393impl ChildBySource for GenericDefId { 423impl ChildBySource for GenericDefId {
394 fn child_by_source(&self, db: &dyn DefDatabase) -> DynMap { 424 fn child_by_source(&self, db: &dyn DefDatabase) -> DynMap {
395 let mut res = DynMap::default(); 425 let mut res = DynMap::default();
@@ -406,6 +436,10 @@ impl ChildBySource for GenericDefId {
406 let id = LifetimeParamId { parent: *self, local_id }; 436 let id = LifetimeParamId { parent: *self, local_id };
407 res[keys::LIFETIME_PARAM].insert(sm.with_value(src.clone()), id); 437 res[keys::LIFETIME_PARAM].insert(sm.with_value(src.clone()), id);
408 } 438 }
439 for (local_id, src) in sm.value.const_params.iter() {
440 let id = ConstParamId { parent: *self, local_id };
441 res[keys::CONST_PARAM].insert(sm.with_value(src.clone()), id);
442 }
409 res 443 res
410 } 444 }
411} 445}
diff --git a/crates/hir_def/src/item_tree.rs b/crates/hir_def/src/item_tree.rs
index 100dbf5d6..b6f510731 100644
--- a/crates/hir_def/src/item_tree.rs
+++ b/crates/hir_def/src/item_tree.rs
@@ -260,6 +260,7 @@ impl GenericParamsStorage {
260 fn alloc(&mut self, params: GenericParams) -> GenericParamsId { 260 fn alloc(&mut self, params: GenericParams) -> GenericParamsId {
261 if params.types.is_empty() 261 if params.types.is_empty()
262 && params.lifetimes.is_empty() 262 && params.lifetimes.is_empty()
263 && params.consts.is_empty()
263 && params.where_predicates.is_empty() 264 && params.where_predicates.is_empty()
264 { 265 {
265 return GenericParamsId::EMPTY; 266 return GenericParamsId::EMPTY;
@@ -269,8 +270,12 @@ impl GenericParamsStorage {
269 } 270 }
270} 271}
271 272
272static EMPTY_GENERICS: GenericParams = 273static EMPTY_GENERICS: GenericParams = GenericParams {
273 GenericParams { types: Arena::new(), lifetimes: Arena::new(), where_predicates: Vec::new() }; 274 types: Arena::new(),
275 lifetimes: Arena::new(),
276 consts: Arena::new(),
277 where_predicates: Vec::new(),
278};
274 279
275#[derive(Default, Debug, Eq, PartialEq)] 280#[derive(Default, Debug, Eq, PartialEq)]
276struct ItemTreeData { 281struct ItemTreeData {
diff --git a/crates/hir_def/src/keys.rs b/crates/hir_def/src/keys.rs
index 9c585de2c..89b3ed868 100644
--- a/crates/hir_def/src/keys.rs
+++ b/crates/hir_def/src/keys.rs
@@ -8,8 +8,8 @@ use syntax::{ast, AstNode, AstPtr};
8 8
9use crate::{ 9use crate::{
10 dyn_map::{DynMap, Policy}, 10 dyn_map::{DynMap, Policy},
11 ConstId, EnumId, EnumVariantId, FieldId, FunctionId, ImplId, LifetimeParamId, StaticId, 11 ConstId, ConstParamId, EnumId, EnumVariantId, FieldId, FunctionId, ImplId, LifetimeParamId,
12 StructId, TraitId, TypeAliasId, TypeParamId, UnionId, 12 StaticId, StructId, TraitId, TypeAliasId, TypeParamId, UnionId,
13}; 13};
14 14
15pub type Key<K, V> = crate::dyn_map::Key<InFile<K>, V, AstPtrPolicy<K, V>>; 15pub type Key<K, V> = crate::dyn_map::Key<InFile<K>, V, AstPtrPolicy<K, V>>;
@@ -29,6 +29,7 @@ pub const TUPLE_FIELD: Key<ast::TupleField, FieldId> = Key::new();
29pub const RECORD_FIELD: Key<ast::RecordField, FieldId> = Key::new(); 29pub const RECORD_FIELD: Key<ast::RecordField, FieldId> = Key::new();
30pub const TYPE_PARAM: Key<ast::TypeParam, TypeParamId> = Key::new(); 30pub const TYPE_PARAM: Key<ast::TypeParam, TypeParamId> = Key::new();
31pub const LIFETIME_PARAM: Key<ast::LifetimeParam, LifetimeParamId> = Key::new(); 31pub const LIFETIME_PARAM: Key<ast::LifetimeParam, LifetimeParamId> = Key::new();
32pub const CONST_PARAM: Key<ast::ConstParam, ConstParamId> = Key::new();
32 33
33pub const MACRO: Key<ast::MacroCall, MacroDefId> = Key::new(); 34pub const MACRO: Key<ast::MacroCall, MacroDefId> = Key::new();
34 35
diff --git a/crates/hir_def/src/lib.rs b/crates/hir_def/src/lib.rs
index ba09a9126..211cb2faf 100644
--- a/crates/hir_def/src/lib.rs
+++ b/crates/hir_def/src/lib.rs
@@ -232,6 +232,13 @@ pub struct LifetimeParamId {
232pub type LocalLifetimeParamId = Idx<generics::LifetimeParamData>; 232pub type LocalLifetimeParamId = Idx<generics::LifetimeParamData>;
233 233
234#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 234#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
235pub struct ConstParamId {
236 pub parent: GenericDefId,
237 pub local_id: LocalConstParamId,
238}
239pub type LocalConstParamId = Idx<generics::ConstParamData>;
240
241#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
235pub enum ContainerId { 242pub enum ContainerId {
236 ModuleId(ModuleId), 243 ModuleId(ModuleId),
237 DefWithBodyId(DefWithBodyId), 244 DefWithBodyId(DefWithBodyId),
@@ -254,6 +261,15 @@ pub enum AdtId {
254} 261}
255impl_from!(StructId, UnionId, EnumId for AdtId); 262impl_from!(StructId, UnionId, EnumId for AdtId);
256 263
264/// A generic param
265#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
266pub enum GenericParamId {
267 TypeParamId(TypeParamId),
268 LifetimeParamId(LifetimeParamId),
269 ConstParamId(ConstParamId),
270}
271impl_from!(TypeParamId, LifetimeParamId, ConstParamId for GenericParamId);
272
257/// The defs which can be visible in the module. 273/// The defs which can be visible in the module.
258#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 274#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
259pub enum ModuleDefId { 275pub enum ModuleDefId {
@@ -350,6 +366,7 @@ pub enum AttrDefId {
350 TypeAliasId(TypeAliasId), 366 TypeAliasId(TypeAliasId),
351 MacroDefId(MacroDefId), 367 MacroDefId(MacroDefId),
352 ImplId(ImplId), 368 ImplId(ImplId),
369 GenericParamId(GenericParamId),
353} 370}
354 371
355impl_from!( 372impl_from!(
@@ -363,7 +380,8 @@ impl_from!(
363 TraitId, 380 TraitId,
364 TypeAliasId, 381 TypeAliasId,
365 MacroDefId, 382 MacroDefId,
366 ImplId 383 ImplId,
384 GenericParamId
367 for AttrDefId 385 for AttrDefId
368); 386);
369 387
@@ -488,6 +506,15 @@ impl AttrDefId {
488 AttrDefId::TraitId(it) => it.lookup(db).container.module(db).krate, 506 AttrDefId::TraitId(it) => it.lookup(db).container.module(db).krate,
489 AttrDefId::TypeAliasId(it) => it.lookup(db).module(db).krate, 507 AttrDefId::TypeAliasId(it) => it.lookup(db).module(db).krate,
490 AttrDefId::ImplId(it) => it.lookup(db).container.module(db).krate, 508 AttrDefId::ImplId(it) => it.lookup(db).container.module(db).krate,
509 AttrDefId::GenericParamId(it) => {
510 match it {
511 GenericParamId::TypeParamId(it) => it.parent,
512 GenericParamId::LifetimeParamId(it) => it.parent,
513 GenericParamId::ConstParamId(it) => it.parent,
514 }
515 .module(db)
516 .krate
517 }
491 // FIXME: `MacroDefId` should store the defining module, then this can implement 518 // FIXME: `MacroDefId` should store the defining module, then this can implement
492 // `HasModule` 519 // `HasModule`
493 AttrDefId::MacroDefId(it) => it.krate, 520 AttrDefId::MacroDefId(it) => it.krate,
diff --git a/crates/hir_def/src/resolver.rs b/crates/hir_def/src/resolver.rs
index f8cc5e075..129f1dbac 100644
--- a/crates/hir_def/src/resolver.rs
+++ b/crates/hir_def/src/resolver.rs
@@ -20,9 +20,9 @@ use crate::{
20 path::{ModPath, PathKind}, 20 path::{ModPath, PathKind},
21 per_ns::PerNs, 21 per_ns::PerNs,
22 visibility::{RawVisibility, Visibility}, 22 visibility::{RawVisibility, Visibility},
23 AdtId, AssocContainerId, ConstId, ContainerId, DefWithBodyId, EnumId, EnumVariantId, 23 AdtId, AssocContainerId, ConstId, ConstParamId, ContainerId, DefWithBodyId, EnumId,
24 FunctionId, GenericDefId, HasModule, ImplId, LocalModuleId, Lookup, ModuleDefId, ModuleId, 24 EnumVariantId, FunctionId, GenericDefId, HasModule, ImplId, LocalModuleId, Lookup, ModuleDefId,
25 StaticId, StructId, TraitId, TypeAliasId, TypeParamId, VariantId, 25 ModuleId, StaticId, StructId, TraitId, TypeAliasId, TypeParamId, VariantId,
26}; 26};
27 27
28#[derive(Debug, Clone, Default)] 28#[derive(Debug, Clone, Default)]
@@ -93,6 +93,7 @@ pub enum ValueNs {
93 StaticId(StaticId), 93 StaticId(StaticId),
94 StructId(StructId), 94 StructId(StructId),
95 EnumVariantId(EnumVariantId), 95 EnumVariantId(EnumVariantId),
96 GenericParam(ConstParamId),
96} 97}
97 98
98impl Resolver { 99impl Resolver {
@@ -163,7 +164,7 @@ impl Resolver {
163 } 164 }
164 165
165 Scope::GenericParams { params, def } => { 166 Scope::GenericParams { params, def } => {
166 if let Some(local_id) = params.find_by_name(first_name) { 167 if let Some(local_id) = params.find_type_by_name(first_name) {
167 let idx = if path.segments.len() == 1 { None } else { Some(1) }; 168 let idx = if path.segments.len() == 1 { None } else { Some(1) };
168 return Some(( 169 return Some((
169 TypeNs::GenericParam(TypeParamId { local_id, parent: *def }), 170 TypeNs::GenericParam(TypeParamId { local_id, parent: *def }),
@@ -285,11 +286,17 @@ impl Resolver {
285 Scope::ExprScope(_) => continue, 286 Scope::ExprScope(_) => continue,
286 287
287 Scope::GenericParams { params, def } if n_segments > 1 => { 288 Scope::GenericParams { params, def } if n_segments > 1 => {
288 if let Some(local_id) = params.find_by_name(first_name) { 289 if let Some(local_id) = params.find_type_by_name(first_name) {
289 let ty = TypeNs::GenericParam(TypeParamId { local_id, parent: *def }); 290 let ty = TypeNs::GenericParam(TypeParamId { local_id, parent: *def });
290 return Some(ResolveValueResult::Partial(ty, 1)); 291 return Some(ResolveValueResult::Partial(ty, 1));
291 } 292 }
292 } 293 }
294 Scope::GenericParams { params, def } if n_segments == 1 => {
295 if let Some(local_id) = params.find_const_by_name(first_name) {
296 let val = ValueNs::GenericParam(ConstParamId { local_id, parent: *def });
297 return Some(ResolveValueResult::ValueNs(val));
298 }
299 }
293 Scope::GenericParams { .. } => continue, 300 Scope::GenericParams { .. } => continue,
294 301
295 Scope::ImplDefScope(impl_) => { 302 Scope::ImplDefScope(impl_) => {
diff --git a/crates/hir_expand/src/db.rs b/crates/hir_expand/src/db.rs
index 06f0a3ed9..0a0d021e0 100644
--- a/crates/hir_expand/src/db.rs
+++ b/crates/hir_expand/src/db.rs
@@ -404,7 +404,7 @@ fn to_fragment_kind(db: &dyn AstDatabase, id: MacroCallId) -> FragmentKind {
404 TRY_EXPR => FragmentKind::Expr, 404 TRY_EXPR => FragmentKind::Expr,
405 TUPLE_EXPR => FragmentKind::Expr, 405 TUPLE_EXPR => FragmentKind::Expr,
406 PAREN_EXPR => FragmentKind::Expr, 406 PAREN_EXPR => FragmentKind::Expr,
407 407 ARRAY_EXPR => FragmentKind::Expr,
408 FOR_EXPR => FragmentKind::Expr, 408 FOR_EXPR => FragmentKind::Expr,
409 PATH_EXPR => FragmentKind::Expr, 409 PATH_EXPR => FragmentKind::Expr,
410 CLOSURE_EXPR => FragmentKind::Expr, 410 CLOSURE_EXPR => FragmentKind::Expr,
diff --git a/crates/hir_expand/src/eager.rs b/crates/hir_expand/src/eager.rs
index 6354b090d..ae7b51a08 100644
--- a/crates/hir_expand/src/eager.rs
+++ b/crates/hir_expand/src/eager.rs
@@ -218,6 +218,12 @@ fn eager_macro_recur(
218 } 218 }
219 }; 219 };
220 220
221 // check if the whole original sytnax is replaced
222 // Note that SyntaxRewriter cannot replace the root node itself
223 if child.syntax() == &original {
224 return Ok(insert);
225 }
226
221 rewriter.replace(child.syntax(), &insert); 227 rewriter.replace(child.syntax(), &insert);
222 } 228 }
223 229
diff --git a/crates/hir_ty/Cargo.toml b/crates/hir_ty/Cargo.toml
index 2dfccd191..3d1778590 100644
--- a/crates/hir_ty/Cargo.toml
+++ b/crates/hir_ty/Cargo.toml
@@ -31,7 +31,7 @@ syntax = { path = "../syntax", version = "0.0.0" }
31test_utils = { path = "../test_utils", version = "0.0.0" } 31test_utils = { path = "../test_utils", version = "0.0.0" }
32 32
33[dev-dependencies] 33[dev-dependencies]
34expect-test = "1.0" 34expect-test = "1.1"
35tracing = "0.1" 35tracing = "0.1"
36tracing-subscriber = { version = "0.2", default-features = false, features = ["env-filter", "registry"] } 36tracing-subscriber = { version = "0.2", default-features = false, features = ["env-filter", "registry"] }
37tracing-tree = { version = "0.1.4" } 37tracing-tree = { version = "0.1.4" }
diff --git a/crates/hir_ty/src/db.rs b/crates/hir_ty/src/db.rs
index 66bdb8e88..f3567c49e 100644
--- a/crates/hir_ty/src/db.rs
+++ b/crates/hir_ty/src/db.rs
@@ -5,8 +5,8 @@ use std::sync::Arc;
5use arena::map::ArenaMap; 5use arena::map::ArenaMap;
6use base_db::{impl_intern_key, salsa, CrateId, Upcast}; 6use base_db::{impl_intern_key, salsa, CrateId, Upcast};
7use hir_def::{ 7use hir_def::{
8 db::DefDatabase, expr::ExprId, DefWithBodyId, FunctionId, GenericDefId, ImplId, LocalFieldId, 8 db::DefDatabase, expr::ExprId, ConstParamId, DefWithBodyId, FunctionId, GenericDefId, ImplId,
9 TypeParamId, VariantId, 9 LocalFieldId, TypeParamId, VariantId,
10}; 10};
11 11
12use crate::{ 12use crate::{
@@ -37,6 +37,9 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
37 #[salsa::cycle(crate::lower::impl_self_ty_recover)] 37 #[salsa::cycle(crate::lower::impl_self_ty_recover)]
38 fn impl_self_ty(&self, def: ImplId) -> Binders<Ty>; 38 fn impl_self_ty(&self, def: ImplId) -> Binders<Ty>;
39 39
40 #[salsa::invoke(crate::lower::const_param_ty_query)]
41 fn const_param_ty(&self, def: ConstParamId) -> Ty;
42
40 #[salsa::invoke(crate::lower::impl_trait_query)] 43 #[salsa::invoke(crate::lower::impl_trait_query)]
41 fn impl_trait(&self, def: ImplId) -> Option<Binders<TraitRef>>; 44 fn impl_trait(&self, def: ImplId) -> Option<Binders<TraitRef>>;
42 45
diff --git a/crates/hir_ty/src/diagnostics/expr.rs b/crates/hir_ty/src/diagnostics/expr.rs
index 849415706..b4e453411 100644
--- a/crates/hir_ty/src/diagnostics/expr.rs
+++ b/crates/hir_ty/src/diagnostics/expr.rs
@@ -156,7 +156,7 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
156 // FIXME: Due to shortcomings in the current type system implementation, only emit this 156 // FIXME: Due to shortcomings in the current type system implementation, only emit this
157 // diagnostic if there are no type mismatches in the containing function. 157 // diagnostic if there are no type mismatches in the containing function.
158 if self.infer.type_mismatches.iter().next().is_some() { 158 if self.infer.type_mismatches.iter().next().is_some() {
159 return Some(()); 159 return None;
160 } 160 }
161 161
162 let is_method_call = matches!(expr, Expr::MethodCall { .. }); 162 let is_method_call = matches!(expr, Expr::MethodCall { .. });
@@ -170,6 +170,14 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
170 let mut args = args.clone(); 170 let mut args = args.clone();
171 args.insert(0, *receiver); 171 args.insert(0, *receiver);
172 172
173 let receiver = &self.infer.type_of_expr[*receiver];
174 if receiver.strip_references().is_unknown() {
175 // if the receiver is of unknown type, it's very likely we
176 // don't know enough to correctly resolve the method call.
177 // This is kind of a band-aid for #6975.
178 return None;
179 }
180
173 // FIXME: note that we erase information about substs here. This 181 // FIXME: note that we erase information about substs here. This
174 // is not right, but, luckily, doesn't matter as we care only 182 // is not right, but, luckily, doesn't matter as we care only
175 // about the number of params 183 // about the number of params
@@ -505,6 +513,22 @@ fn f() {
505 } 513 }
506 514
507 #[test] 515 #[test]
516 fn method_unknown_receiver() {
517 // note: this is incorrect code, so there might be errors on this in the
518 // future, but we shouldn't emit an argument count diagnostic here
519 check_diagnostics(
520 r#"
521trait Foo { fn method(&self, arg: usize) {} }
522
523fn f() {
524 let x;
525 x.method();
526}
527"#,
528 );
529 }
530
531 #[test]
508 fn tuple_struct() { 532 fn tuple_struct() {
509 check_diagnostics( 533 check_diagnostics(
510 r#" 534 r#"
diff --git a/crates/hir_ty/src/infer/path.rs b/crates/hir_ty/src/infer/path.rs
index 80d7ed10e..5d541104e 100644
--- a/crates/hir_ty/src/infer/path.rs
+++ b/crates/hir_ty/src/infer/path.rs
@@ -89,6 +89,7 @@ impl<'a> InferenceContext<'a> {
89 return None; 89 return None;
90 } 90 }
91 } 91 }
92 ValueNs::GenericParam(it) => return Some(self.db.const_param_ty(it)),
92 }; 93 };
93 94
94 let ty = self.db.value_ty(typable); 95 let ty = self.db.value_ty(typable);
diff --git a/crates/hir_ty/src/lib.rs b/crates/hir_ty/src/lib.rs
index 357bd92f9..e00c7e176 100644
--- a/crates/hir_ty/src/lib.rs
+++ b/crates/hir_ty/src/lib.rs
@@ -791,6 +791,10 @@ impl Ty {
791 matches!(self, Ty::Apply(ApplicationTy { ctor: TypeCtor::Never, .. })) 791 matches!(self, Ty::Apply(ApplicationTy { ctor: TypeCtor::Never, .. }))
792 } 792 }
793 793
794 pub fn is_unknown(&self) -> bool {
795 matches!(self, Ty::Unknown)
796 }
797
794 /// If this is a `dyn Trait` type, this returns the `Trait` part. 798 /// If this is a `dyn Trait` type, this returns the `Trait` part.
795 pub fn dyn_trait_ref(&self) -> Option<&TraitRef> { 799 pub fn dyn_trait_ref(&self) -> Option<&TraitRef> {
796 match self { 800 match self {
diff --git a/crates/hir_ty/src/lower.rs b/crates/hir_ty/src/lower.rs
index 8da56cd11..222f61a11 100644
--- a/crates/hir_ty/src/lower.rs
+++ b/crates/hir_ty/src/lower.rs
@@ -16,9 +16,9 @@ use hir_def::{
16 path::{GenericArg, Path, PathSegment, PathSegments}, 16 path::{GenericArg, Path, PathSegment, PathSegments},
17 resolver::{HasResolver, Resolver, TypeNs}, 17 resolver::{HasResolver, Resolver, TypeNs},
18 type_ref::{TypeBound, TypeRef}, 18 type_ref::{TypeBound, TypeRef},
19 AdtId, AssocContainerId, AssocItemId, ConstId, EnumId, EnumVariantId, FunctionId, GenericDefId, 19 AdtId, AssocContainerId, AssocItemId, ConstId, ConstParamId, EnumId, EnumVariantId, FunctionId,
20 HasModule, ImplId, LocalFieldId, Lookup, StaticId, StructId, TraitId, TypeAliasId, TypeParamId, 20 GenericDefId, HasModule, ImplId, LocalFieldId, Lookup, StaticId, StructId, TraitId,
21 UnionId, VariantId, 21 TypeAliasId, TypeParamId, UnionId, VariantId,
22}; 22};
23use hir_expand::name::Name; 23use hir_expand::name::Name;
24use smallvec::SmallVec; 24use smallvec::SmallVec;
@@ -1221,6 +1221,15 @@ pub(crate) fn impl_self_ty_query(db: &dyn HirDatabase, impl_id: ImplId) -> Binde
1221 Binders::new(generics.len(), Ty::from_hir(&ctx, &impl_data.target_type)) 1221 Binders::new(generics.len(), Ty::from_hir(&ctx, &impl_data.target_type))
1222} 1222}
1223 1223
1224pub(crate) fn const_param_ty_query(db: &dyn HirDatabase, def: ConstParamId) -> Ty {
1225 let parent_data = db.generic_params(def.parent);
1226 let data = &parent_data.consts[def.local_id];
1227 let resolver = def.parent.resolver(db.upcast());
1228 let ctx = TyLoweringContext::new(db, &resolver);
1229
1230 Ty::from_hir(&ctx, &data.ty)
1231}
1232
1224pub(crate) fn impl_self_ty_recover( 1233pub(crate) fn impl_self_ty_recover(
1225 db: &dyn HirDatabase, 1234 db: &dyn HirDatabase,
1226 _cycle: &[String], 1235 _cycle: &[String],
diff --git a/crates/hir_ty/src/tests/macros.rs b/crates/hir_ty/src/tests/macros.rs
index a7656b864..1953da7be 100644
--- a/crates/hir_ty/src/tests/macros.rs
+++ b/crates/hir_ty/src/tests/macros.rs
@@ -540,6 +540,52 @@ fn bar() -> u32 {0}
540} 540}
541 541
542#[test] 542#[test]
543fn infer_builtin_macros_include_str() {
544 check_types(
545 r#"
546//- /main.rs
547#[rustc_builtin_macro]
548macro_rules! include_str {() => {}}
549
550fn main() {
551 let a = include_str!("foo.rs");
552 a;
553} //^ &str
554
555//- /foo.rs
556hello
557"#,
558 );
559}
560
561#[test]
562fn infer_builtin_macros_include_str_with_lazy_nested() {
563 check_types(
564 r#"
565//- /main.rs
566#[rustc_builtin_macro]
567macro_rules! concat {() => {}}
568#[rustc_builtin_macro]
569macro_rules! include_str {() => {}}
570
571macro_rules! m {
572 ($x:expr) => {
573 concat!("foo", $x)
574 };
575}
576
577fn main() {
578 let a = include_str!(m!(".rs"));
579 a;
580} //^ &str
581
582//- /foo.rs
583hello
584"#,
585 );
586}
587
588#[test]
543#[ignore] 589#[ignore]
544fn include_accidentally_quadratic() { 590fn include_accidentally_quadratic() {
545 let file = project_dir().join("crates/syntax/test_data/accidentally_quadratic"); 591 let file = project_dir().join("crates/syntax/test_data/accidentally_quadratic");
diff --git a/crates/hir_ty/src/tests/regression.rs b/crates/hir_ty/src/tests/regression.rs
index 307a257b1..cffe8630b 100644
--- a/crates/hir_ty/src/tests/regression.rs
+++ b/crates/hir_ty/src/tests/regression.rs
@@ -326,6 +326,24 @@ fn infer_paren_macro_call() {
326} 326}
327 327
328#[test] 328#[test]
329fn infer_array_macro_call() {
330 check_infer(
331 r#"
332 macro_rules! bar { () => {0u32} }
333 fn test() {
334 let a = [bar!()];
335 }
336 "#,
337 expect![[r#"
338 !0..4 '0u32': u32
339 44..69 '{ ...()]; }': ()
340 54..55 'a': [u32; _]
341 58..66 '[bar!()]': [u32; _]
342 "#]],
343 );
344}
345
346#[test]
329fn bug_1030() { 347fn bug_1030() {
330 check_infer( 348 check_infer(
331 r#" 349 r#"
diff --git a/crates/hir_ty/src/tests/simple.rs b/crates/hir_ty/src/tests/simple.rs
index a61282d5a..8d431b920 100644
--- a/crates/hir_ty/src/tests/simple.rs
+++ b/crates/hir_ty/src/tests/simple.rs
@@ -2375,3 +2375,19 @@ fn infer_operator_overload() {
2375 "#]], 2375 "#]],
2376 ); 2376 );
2377} 2377}
2378
2379#[test]
2380fn infer_const_params() {
2381 check_infer(
2382 r#"
2383 fn foo<const FOO: usize>() {
2384 let bar = FOO;
2385 }
2386 "#,
2387 expect![[r#"
2388 27..49 '{ ...FOO; }': ()
2389 37..40 'bar': usize
2390 43..46 'FOO': usize
2391 "#]],
2392 );
2393}
diff --git a/crates/ide/Cargo.toml b/crates/ide/Cargo.toml
index f1544dbe0..bb28cca4d 100644
--- a/crates/ide/Cargo.toml
+++ b/crates/ide/Cargo.toml
@@ -36,4 +36,4 @@ completion = { path = "../completion", version = "0.0.0" }
36hir = { path = "../hir", version = "0.0.0" } 36hir = { path = "../hir", version = "0.0.0" }
37 37
38[dev-dependencies] 38[dev-dependencies]
39expect-test = "1.0" 39expect-test = "1.1"
diff --git a/crates/ide/src/call_hierarchy.rs b/crates/ide/src/call_hierarchy.rs
index 60e0cd4ad..3c2d39f5d 100644
--- a/crates/ide/src/call_hierarchy.rs
+++ b/crates/ide/src/call_hierarchy.rs
@@ -8,7 +8,7 @@ use ide_db::RootDatabase;
8use syntax::{ast, match_ast, AstNode, TextRange}; 8use syntax::{ast, match_ast, AstNode, TextRange};
9 9
10use crate::{ 10use crate::{
11 display::ToNav, goto_definition, references, FilePosition, NavigationTarget, RangeInfo, 11 display::TryToNav, goto_definition, references, FilePosition, NavigationTarget, RangeInfo,
12}; 12};
13 13
14#[derive(Debug, Clone)] 14#[derive(Debug, Clone)]
@@ -61,7 +61,7 @@ pub(crate) fn incoming_calls(db: &RootDatabase, position: FilePosition) -> Optio
61 match node { 61 match node {
62 ast::Fn(it) => { 62 ast::Fn(it) => {
63 let def = sema.to_def(&it)?; 63 let def = sema.to_def(&it)?;
64 Some(def.to_nav(sema.db)) 64 def.try_to_nav(sema.db)
65 }, 65 },
66 _ => None, 66 _ => None,
67 } 67 }
@@ -99,7 +99,7 @@ pub(crate) fn outgoing_calls(db: &RootDatabase, position: FilePosition) -> Optio
99 match callable.kind() { 99 match callable.kind() {
100 hir::CallableKind::Function(it) => { 100 hir::CallableKind::Function(it) => {
101 let fn_def: hir::Function = it.into(); 101 let fn_def: hir::Function = it.into();
102 let nav = fn_def.to_nav(db); 102 let nav = fn_def.try_to_nav(db)?;
103 Some(nav) 103 Some(nav)
104 } 104 }
105 _ => None, 105 _ => None,
@@ -107,7 +107,7 @@ pub(crate) fn outgoing_calls(db: &RootDatabase, position: FilePosition) -> Optio
107 } 107 }
108 FnCallNode::MethodCallExpr(expr) => { 108 FnCallNode::MethodCallExpr(expr) => {
109 let function = sema.resolve_method_call(&expr)?; 109 let function = sema.resolve_method_call(&expr)?;
110 Some(function.to_nav(db)) 110 function.try_to_nav(db)
111 } 111 }
112 } { 112 } {
113 Some((func_target, name_ref.syntax().text_range())) 113 Some((func_target, name_ref.syntax().text_range()))
diff --git a/crates/ide/src/diagnostics/fixes.rs b/crates/ide/src/diagnostics/fixes.rs
index d79f5c170..ec0f840e9 100644
--- a/crates/ide/src/diagnostics/fixes.rs
+++ b/crates/ide/src/diagnostics/fixes.rs
@@ -156,20 +156,23 @@ fn missing_record_expr_field_fix(
156 let record_fields = match VariantDef::from(def_id) { 156 let record_fields = match VariantDef::from(def_id) {
157 VariantDef::Struct(s) => { 157 VariantDef::Struct(s) => {
158 module = s.module(sema.db); 158 module = s.module(sema.db);
159 let source = s.source(sema.db); 159 #[allow(deprecated)]
160 let source = s.source(sema.db)?;
160 def_file_id = source.file_id; 161 def_file_id = source.file_id;
161 let fields = source.value.field_list()?; 162 let fields = source.value.field_list()?;
162 record_field_list(fields)? 163 record_field_list(fields)?
163 } 164 }
164 VariantDef::Union(u) => { 165 VariantDef::Union(u) => {
165 module = u.module(sema.db); 166 module = u.module(sema.db);
166 let source = u.source(sema.db); 167 #[allow(deprecated)]
168 let source = u.source(sema.db)?;
167 def_file_id = source.file_id; 169 def_file_id = source.file_id;
168 source.value.record_field_list()? 170 source.value.record_field_list()?
169 } 171 }
170 VariantDef::Variant(e) => { 172 VariantDef::Variant(e) => {
171 module = e.module(sema.db); 173 module = e.module(sema.db);
172 let source = e.source(sema.db); 174 #[allow(deprecated)]
175 let source = e.source(sema.db)?;
173 def_file_id = source.file_id; 176 def_file_id = source.file_id;
174 let fields = source.value.field_list()?; 177 let fields = source.value.field_list()?;
175 record_field_list(fields)? 178 record_field_list(fields)?
diff --git a/crates/ide/src/display/navigation_target.rs b/crates/ide/src/display/navigation_target.rs
index 6431e7d6d..e24c78301 100644
--- a/crates/ide/src/display/navigation_target.rs
+++ b/crates/ide/src/display/navigation_target.rs
@@ -24,6 +24,7 @@ pub enum SymbolKind {
24 Impl, 24 Impl,
25 Field, 25 Field,
26 TypeParam, 26 TypeParam,
27 ConstParam,
27 LifetimeParam, 28 LifetimeParam,
28 ValueParam, 29 ValueParam,
29 SelfParam, 30 SelfParam,
@@ -209,40 +210,32 @@ impl ToNav for FileSymbol {
209impl TryToNav for Definition { 210impl TryToNav for Definition {
210 fn try_to_nav(&self, db: &RootDatabase) -> Option<NavigationTarget> { 211 fn try_to_nav(&self, db: &RootDatabase) -> Option<NavigationTarget> {
211 match self { 212 match self {
212 Definition::Macro(it) => { 213 Definition::Macro(it) => it.try_to_nav(db),
213 // FIXME: Currently proc-macro do not have ast-node, 214 Definition::Field(it) => it.try_to_nav(db),
214 // such that it does not have source
215 // more discussion: https://github.com/rust-analyzer/rust-analyzer/issues/6913
216 if it.is_proc_macro() {
217 return None;
218 }
219 Some(it.to_nav(db))
220 }
221 Definition::Field(it) => Some(it.to_nav(db)),
222 Definition::ModuleDef(it) => it.try_to_nav(db), 215 Definition::ModuleDef(it) => it.try_to_nav(db),
223 Definition::SelfType(it) => Some(it.to_nav(db)), 216 Definition::SelfType(it) => it.try_to_nav(db),
224 Definition::Local(it) => Some(it.to_nav(db)), 217 Definition::Local(it) => Some(it.to_nav(db)),
225 Definition::TypeParam(it) => Some(it.to_nav(db)), 218 Definition::TypeParam(it) => it.try_to_nav(db),
226 Definition::LifetimeParam(it) => Some(it.to_nav(db)), 219 Definition::LifetimeParam(it) => it.try_to_nav(db),
227 Definition::Label(it) => Some(it.to_nav(db)), 220 Definition::Label(it) => Some(it.to_nav(db)),
221 Definition::ConstParam(it) => it.try_to_nav(db),
228 } 222 }
229 } 223 }
230} 224}
231 225
232impl TryToNav for hir::ModuleDef { 226impl TryToNav for hir::ModuleDef {
233 fn try_to_nav(&self, db: &RootDatabase) -> Option<NavigationTarget> { 227 fn try_to_nav(&self, db: &RootDatabase) -> Option<NavigationTarget> {
234 let res = match self { 228 match self {
235 hir::ModuleDef::Module(it) => it.to_nav(db), 229 hir::ModuleDef::Module(it) => Some(it.to_nav(db)),
236 hir::ModuleDef::Function(it) => it.to_nav(db), 230 hir::ModuleDef::Function(it) => it.try_to_nav(db),
237 hir::ModuleDef::Adt(it) => it.to_nav(db), 231 hir::ModuleDef::Adt(it) => it.try_to_nav(db),
238 hir::ModuleDef::Variant(it) => it.to_nav(db), 232 hir::ModuleDef::Variant(it) => it.try_to_nav(db),
239 hir::ModuleDef::Const(it) => it.to_nav(db), 233 hir::ModuleDef::Const(it) => it.try_to_nav(db),
240 hir::ModuleDef::Static(it) => it.to_nav(db), 234 hir::ModuleDef::Static(it) => it.try_to_nav(db),
241 hir::ModuleDef::Trait(it) => it.to_nav(db), 235 hir::ModuleDef::Trait(it) => it.try_to_nav(db),
242 hir::ModuleDef::TypeAlias(it) => it.to_nav(db), 236 hir::ModuleDef::TypeAlias(it) => it.try_to_nav(db),
243 hir::ModuleDef::BuiltinType(_) => return None, 237 hir::ModuleDef::BuiltinType(_) => None,
244 }; 238 }
245 Some(res)
246 } 239 }
247} 240}
248 241
@@ -277,13 +270,13 @@ impl ToNavFromAst for hir::Trait {
277 const KIND: SymbolKind = SymbolKind::Trait; 270 const KIND: SymbolKind = SymbolKind::Trait;
278} 271}
279 272
280impl<D> ToNav for D 273impl<D> TryToNav for D
281where 274where
282 D: HasSource + ToNavFromAst + Copy + HasAttrs, 275 D: HasSource + ToNavFromAst + Copy + HasAttrs,
283 D::Ast: ast::NameOwner + ShortLabel, 276 D::Ast: ast::NameOwner + ShortLabel,
284{ 277{
285 fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { 278 fn try_to_nav(&self, db: &RootDatabase) -> Option<NavigationTarget> {
286 let src = self.source(db); 279 let src = self.source(db)?;
287 let mut res = NavigationTarget::from_named( 280 let mut res = NavigationTarget::from_named(
288 db, 281 db,
289 src.as_ref().map(|it| it as &dyn ast::NameOwner), 282 src.as_ref().map(|it| it as &dyn ast::NameOwner),
@@ -291,7 +284,7 @@ where
291 ); 284 );
292 res.docs = self.docs(db); 285 res.docs = self.docs(db);
293 res.description = src.value.short_label(); 286 res.description = src.value.short_label();
294 res 287 Some(res)
295 } 288 }
296} 289}
297 290
@@ -310,9 +303,9 @@ impl ToNav for hir::Module {
310 } 303 }
311} 304}
312 305
313impl ToNav for hir::Impl { 306impl TryToNav for hir::Impl {
314 fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { 307 fn try_to_nav(&self, db: &RootDatabase) -> Option<NavigationTarget> {
315 let src = self.source(db); 308 let src = self.source(db)?;
316 let derive_attr = self.is_builtin_derive(db); 309 let derive_attr = self.is_builtin_derive(db);
317 let frange = if let Some(item) = &derive_attr { 310 let frange = if let Some(item) = &derive_attr {
318 item.syntax().original_file_range(db) 311 item.syntax().original_file_range(db)
@@ -325,21 +318,21 @@ impl ToNav for hir::Impl {
325 src.value.self_ty().map(|ty| src.with_value(ty.syntax()).original_file_range(db).range) 318 src.value.self_ty().map(|ty| src.with_value(ty.syntax()).original_file_range(db).range)
326 }; 319 };
327 320
328 NavigationTarget::from_syntax( 321 Some(NavigationTarget::from_syntax(
329 frange.file_id, 322 frange.file_id,
330 "impl".into(), 323 "impl".into(),
331 focus_range, 324 focus_range,
332 frange.range, 325 frange.range,
333 SymbolKind::Impl, 326 SymbolKind::Impl,
334 ) 327 ))
335 } 328 }
336} 329}
337 330
338impl ToNav for hir::Field { 331impl TryToNav for hir::Field {
339 fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { 332 fn try_to_nav(&self, db: &RootDatabase) -> Option<NavigationTarget> {
340 let src = self.source(db); 333 let src = self.source(db)?;
341 334
342 match &src.value { 335 let field_source = match &src.value {
343 FieldSource::Named(it) => { 336 FieldSource::Named(it) => {
344 let mut res = 337 let mut res =
345 NavigationTarget::from_named(db, src.with_value(it), SymbolKind::Field); 338 NavigationTarget::from_named(db, src.with_value(it), SymbolKind::Field);
@@ -357,13 +350,14 @@ impl ToNav for hir::Field {
357 SymbolKind::Field, 350 SymbolKind::Field,
358 ) 351 )
359 } 352 }
360 } 353 };
354 Some(field_source)
361 } 355 }
362} 356}
363 357
364impl ToNav for hir::MacroDef { 358impl TryToNav for hir::MacroDef {
365 fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { 359 fn try_to_nav(&self, db: &RootDatabase) -> Option<NavigationTarget> {
366 let src = self.source(db); 360 let src = self.source(db)?;
367 log::debug!("nav target {:#?}", src.value.syntax()); 361 log::debug!("nav target {:#?}", src.value.syntax());
368 let mut res = NavigationTarget::from_named( 362 let mut res = NavigationTarget::from_named(
369 db, 363 db,
@@ -371,26 +365,26 @@ impl ToNav for hir::MacroDef {
371 SymbolKind::Macro, 365 SymbolKind::Macro,
372 ); 366 );
373 res.docs = self.docs(db); 367 res.docs = self.docs(db);
374 res 368 Some(res)
375 } 369 }
376} 370}
377 371
378impl ToNav for hir::Adt { 372impl TryToNav for hir::Adt {
379 fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { 373 fn try_to_nav(&self, db: &RootDatabase) -> Option<NavigationTarget> {
380 match self { 374 match self {
381 hir::Adt::Struct(it) => it.to_nav(db), 375 hir::Adt::Struct(it) => it.try_to_nav(db),
382 hir::Adt::Union(it) => it.to_nav(db), 376 hir::Adt::Union(it) => it.try_to_nav(db),
383 hir::Adt::Enum(it) => it.to_nav(db), 377 hir::Adt::Enum(it) => it.try_to_nav(db),
384 } 378 }
385 } 379 }
386} 380}
387 381
388impl ToNav for hir::AssocItem { 382impl TryToNav for hir::AssocItem {
389 fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { 383 fn try_to_nav(&self, db: &RootDatabase) -> Option<NavigationTarget> {
390 match self { 384 match self {
391 AssocItem::Function(it) => it.to_nav(db), 385 AssocItem::Function(it) => it.try_to_nav(db),
392 AssocItem::Const(it) => it.to_nav(db), 386 AssocItem::Const(it) => it.try_to_nav(db),
393 AssocItem::TypeAlias(it) => it.to_nav(db), 387 AssocItem::TypeAlias(it) => it.try_to_nav(db),
394 } 388 }
395 } 389 }
396} 390}
@@ -444,9 +438,9 @@ impl ToNav for hir::Label {
444 } 438 }
445} 439}
446 440
447impl ToNav for hir::TypeParam { 441impl TryToNav for hir::TypeParam {
448 fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { 442 fn try_to_nav(&self, db: &RootDatabase) -> Option<NavigationTarget> {
449 let src = self.source(db); 443 let src = self.source(db)?;
450 let full_range = match &src.value { 444 let full_range = match &src.value {
451 Either::Left(it) => it.syntax().text_range(), 445 Either::Left(it) => it.syntax().text_range(),
452 Either::Right(it) => it.syntax().text_range(), 446 Either::Right(it) => it.syntax().text_range(),
@@ -455,7 +449,7 @@ impl ToNav for hir::TypeParam {
455 Either::Left(_) => None, 449 Either::Left(_) => None,
456 Either::Right(it) => it.name().map(|it| it.syntax().text_range()), 450 Either::Right(it) => it.name().map(|it| it.syntax().text_range()),
457 }; 451 };
458 NavigationTarget { 452 Some(NavigationTarget {
459 file_id: src.file_id.original_file(db), 453 file_id: src.file_id.original_file(db),
460 name: self.name(db).to_string().into(), 454 name: self.name(db).to_string().into(),
461 kind: Some(SymbolKind::TypeParam), 455 kind: Some(SymbolKind::TypeParam),
@@ -464,15 +458,15 @@ impl ToNav for hir::TypeParam {
464 container_name: None, 458 container_name: None,
465 description: None, 459 description: None,
466 docs: None, 460 docs: None,
467 } 461 })
468 } 462 }
469} 463}
470 464
471impl ToNav for hir::LifetimeParam { 465impl TryToNav for hir::LifetimeParam {
472 fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { 466 fn try_to_nav(&self, db: &RootDatabase) -> Option<NavigationTarget> {
473 let src = self.source(db); 467 let src = self.source(db)?;
474 let full_range = src.value.syntax().text_range(); 468 let full_range = src.value.syntax().text_range();
475 NavigationTarget { 469 Some(NavigationTarget {
476 file_id: src.file_id.original_file(db), 470 file_id: src.file_id.original_file(db),
477 name: self.name(db).to_string().into(), 471 name: self.name(db).to_string().into(),
478 kind: Some(SymbolKind::LifetimeParam), 472 kind: Some(SymbolKind::LifetimeParam),
@@ -481,7 +475,24 @@ impl ToNav for hir::LifetimeParam {
481 container_name: None, 475 container_name: None,
482 description: None, 476 description: None,
483 docs: None, 477 docs: None,
484 } 478 })
479 }
480}
481
482impl TryToNav for hir::ConstParam {
483 fn try_to_nav(&self, db: &RootDatabase) -> Option<NavigationTarget> {
484 let src = self.source(db)?;
485 let full_range = src.value.syntax().text_range();
486 Some(NavigationTarget {
487 file_id: src.file_id.original_file(db),
488 name: self.name(db).to_string().into(),
489 kind: Some(SymbolKind::ConstParam),
490 full_range,
491 focus_range: src.value.name().map(|n| n.syntax().text_range()),
492 container_name: None,
493 description: None,
494 docs: None,
495 })
485 } 496 }
486} 497}
487 498
diff --git a/crates/ide/src/doc_links.rs b/crates/ide/src/doc_links.rs
index e10516f43..367fac05e 100644
--- a/crates/ide/src/doc_links.rs
+++ b/crates/ide/src/doc_links.rs
@@ -193,6 +193,7 @@ fn rewrite_intra_doc_link(
193 Definition::SelfType(_) 193 Definition::SelfType(_)
194 | Definition::Local(_) 194 | Definition::Local(_)
195 | Definition::TypeParam(_) 195 | Definition::TypeParam(_)
196 | Definition::ConstParam(_)
196 | Definition::LifetimeParam(_) 197 | Definition::LifetimeParam(_)
197 | Definition::Label(_) => return None, 198 | Definition::Label(_) => return None,
198 }?; 199 }?;
diff --git a/crates/ide/src/goto_implementation.rs b/crates/ide/src/goto_implementation.rs
index 6eac39639..da9378a97 100644
--- a/crates/ide/src/goto_implementation.rs
+++ b/crates/ide/src/goto_implementation.rs
@@ -2,7 +2,7 @@ use hir::{Crate, Impl, Semantics};
2use ide_db::RootDatabase; 2use ide_db::RootDatabase;
3use syntax::{algo::find_node_at_offset, ast, AstNode}; 3use syntax::{algo::find_node_at_offset, ast, AstNode};
4 4
5use crate::{display::ToNav, FilePosition, NavigationTarget, RangeInfo}; 5use crate::{display::TryToNav, FilePosition, NavigationTarget, RangeInfo};
6 6
7// Feature: Go to Implementation 7// Feature: Go to Implementation
8// 8//
@@ -55,7 +55,7 @@ fn impls_for_def(
55 impls 55 impls
56 .into_iter() 56 .into_iter()
57 .filter(|impl_def| ty.is_equal_for_find_impls(&impl_def.target_ty(sema.db))) 57 .filter(|impl_def| ty.is_equal_for_find_impls(&impl_def.target_ty(sema.db)))
58 .map(|imp| imp.to_nav(sema.db)) 58 .filter_map(|imp| imp.try_to_nav(sema.db))
59 .collect(), 59 .collect(),
60 ) 60 )
61} 61}
@@ -69,7 +69,7 @@ fn impls_for_trait(
69 69
70 let impls = Impl::for_trait(sema.db, krate, tr); 70 let impls = Impl::for_trait(sema.db, krate, tr);
71 71
72 Some(impls.into_iter().map(|imp| imp.to_nav(sema.db)).collect()) 72 Some(impls.into_iter().filter_map(|imp| imp.try_to_nav(sema.db)).collect())
73} 73}
74 74
75#[cfg(test)] 75#[cfg(test)]
diff --git a/crates/ide/src/goto_type_definition.rs b/crates/ide/src/goto_type_definition.rs
index aba6bf5dc..7e84e06be 100644
--- a/crates/ide/src/goto_type_definition.rs
+++ b/crates/ide/src/goto_type_definition.rs
@@ -1,7 +1,7 @@
1use ide_db::RootDatabase; 1use ide_db::RootDatabase;
2use syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset, T}; 2use syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset, T};
3 3
4use crate::{display::ToNav, FilePosition, NavigationTarget, RangeInfo}; 4use crate::{display::TryToNav, FilePosition, NavigationTarget, RangeInfo};
5 5
6// Feature: Go to Type Definition 6// Feature: Go to Type Definition
7// 7//
@@ -37,7 +37,7 @@ pub(crate) fn goto_type_definition(
37 37
38 let adt_def = ty.autoderef(db).filter_map(|ty| ty.as_adt()).last()?; 38 let adt_def = ty.autoderef(db).filter_map(|ty| ty.as_adt()).last()?;
39 39
40 let nav = adt_def.to_nav(db); 40 let nav = adt_def.try_to_nav(db)?;
41 Some(RangeInfo::new(node.text_range(), vec![nav])) 41 Some(RangeInfo::new(node.text_range(), vec![nav]))
42} 42}
43 43
diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs
index 73245fbe7..2737c900f 100644
--- a/crates/ide/src/hover.rs
+++ b/crates/ide/src/hover.rs
@@ -13,7 +13,7 @@ use syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset,
13use test_utils::mark; 13use test_utils::mark;
14 14
15use crate::{ 15use crate::{
16 display::{macro_label, ShortLabel, ToNav, TryToNav}, 16 display::{macro_label, ShortLabel, TryToNav},
17 doc_links::{remove_links, rewrite_links}, 17 doc_links::{remove_links, rewrite_links},
18 markdown_remove::remove_markdown, 18 markdown_remove::remove_markdown,
19 markup::Markup, 19 markup::Markup,
@@ -109,6 +109,8 @@ pub(crate) fn hover(
109 match node { 109 match node {
110 ast::Name(name) => NameClass::classify(&sema, &name).and_then(|d| d.defined(sema.db)), 110 ast::Name(name) => NameClass::classify(&sema, &name).and_then(|d| d.defined(sema.db)),
111 ast::NameRef(name_ref) => NameRefClass::classify(&sema, &name_ref).map(|d| d.referenced(sema.db)), 111 ast::NameRef(name_ref) => NameRefClass::classify(&sema, &name_ref).map(|d| d.referenced(sema.db)),
112 ast::Lifetime(lifetime) => NameClass::classify_lifetime(&sema, &lifetime)
113 .map_or_else(|| NameRefClass::classify_lifetime(&sema, &lifetime).map(|d| d.referenced(sema.db)), |d| d.defined(sema.db)),
112 _ => None, 114 _ => None,
113 } 115 }
114 }; 116 };
@@ -181,10 +183,10 @@ fn show_implementations_action(db: &RootDatabase, def: Definition) -> Option<Hov
181 183
182 match def { 184 match def {
183 Definition::ModuleDef(it) => match it { 185 Definition::ModuleDef(it) => match it {
184 ModuleDef::Adt(Adt::Struct(it)) => Some(to_action(it.to_nav(db))), 186 ModuleDef::Adt(Adt::Struct(it)) => Some(to_action(it.try_to_nav(db)?)),
185 ModuleDef::Adt(Adt::Union(it)) => Some(to_action(it.to_nav(db))), 187 ModuleDef::Adt(Adt::Union(it)) => Some(to_action(it.try_to_nav(db)?)),
186 ModuleDef::Adt(Adt::Enum(it)) => Some(to_action(it.to_nav(db))), 188 ModuleDef::Adt(Adt::Enum(it)) => Some(to_action(it.try_to_nav(db)?)),
187 ModuleDef::Trait(it) => Some(to_action(it.to_nav(db))), 189 ModuleDef::Trait(it) => Some(to_action(it.try_to_nav(db)?)),
188 _ => None, 190 _ => None,
189 }, 191 },
190 _ => None, 192 _ => None,
@@ -204,7 +206,8 @@ fn runnable_action(
204 _ => None, 206 _ => None,
205 }, 207 },
206 ModuleDef::Function(it) => { 208 ModuleDef::Function(it) => {
207 let src = it.source(sema.db); 209 #[allow(deprecated)]
210 let src = it.source(sema.db)?;
208 if src.file_id != file_id.into() { 211 if src.file_id != file_id.into() {
209 mark::hit!(hover_macro_generated_struct_fn_doc_comment); 212 mark::hit!(hover_macro_generated_struct_fn_doc_comment);
210 mark::hit!(hover_macro_generated_struct_fn_doc_attr); 213 mark::hit!(hover_macro_generated_struct_fn_doc_attr);
@@ -324,17 +327,12 @@ fn hover_for_definition(db: &RootDatabase, def: Definition) -> Option<Markup> {
324 let mod_path = definition_mod_path(db, &def); 327 let mod_path = definition_mod_path(db, &def);
325 return match def { 328 return match def {
326 Definition::Macro(it) => { 329 Definition::Macro(it) => {
327 // FIXME: Currently proc-macro do not have ast-node, 330 let label = macro_label(&it.source(db)?.value);
328 // such that it does not have source
329 // more discussion: https://github.com/rust-analyzer/rust-analyzer/issues/6913
330 if it.is_proc_macro() {
331 return None;
332 }
333 let label = macro_label(&it.source(db).value);
334 from_def_source_labeled(db, it, Some(label), mod_path) 331 from_def_source_labeled(db, it, Some(label), mod_path)
335 } 332 }
336 Definition::Field(def) => { 333 Definition::Field(def) => {
337 let src = def.source(db).value; 334 #[allow(deprecated)]
335 let src = def.source(db)?.value;
338 if let FieldSource::Named(it) = src { 336 if let FieldSource::Named(it) = src {
339 from_def_source_labeled(db, def, it.short_label(), mod_path) 337 from_def_source_labeled(db, def, it.short_label(), mod_path)
340 } else { 338 } else {
@@ -360,9 +358,9 @@ fn hover_for_definition(db: &RootDatabase, def: Definition) -> Option<Markup> {
360 ModuleDef::Static(it) => from_def_source(db, it, mod_path), 358 ModuleDef::Static(it) => from_def_source(db, it, mod_path),
361 ModuleDef::Trait(it) => from_def_source(db, it, mod_path), 359 ModuleDef::Trait(it) => from_def_source(db, it, mod_path),
362 ModuleDef::TypeAlias(it) => from_def_source(db, it, mod_path), 360 ModuleDef::TypeAlias(it) => from_def_source(db, it, mod_path),
363 ModuleDef::BuiltinType(it) => return Some(it.to_string().into()), 361 ModuleDef::BuiltinType(it) => Some(Markup::fenced_block(&it)),
364 }, 362 },
365 Definition::Local(it) => return Some(Markup::fenced_block(&it.ty(db).display(db))), 363 Definition::Local(it) => Some(Markup::fenced_block(&it.ty(db).display(db))),
366 Definition::SelfType(impl_def) => { 364 Definition::SelfType(impl_def) => {
367 impl_def.target_ty(db).as_adt().and_then(|adt| match adt { 365 impl_def.target_ty(db).as_adt().and_then(|adt| match adt {
368 Adt::Struct(it) => from_def_source(db, it, mod_path), 366 Adt::Struct(it) => from_def_source(db, it, mod_path),
@@ -370,7 +368,9 @@ fn hover_for_definition(db: &RootDatabase, def: Definition) -> Option<Markup> {
370 Adt::Enum(it) => from_def_source(db, it, mod_path), 368 Adt::Enum(it) => from_def_source(db, it, mod_path),
371 }) 369 })
372 } 370 }
373 Definition::TypeParam(_) | Definition::LifetimeParam(_) | Definition::Label(_) => { 371 Definition::Label(it) => Some(Markup::fenced_block(&it.name(db))),
372 Definition::LifetimeParam(it) => Some(Markup::fenced_block(&it.name(db))),
373 Definition::TypeParam(_) | Definition::ConstParam(_) => {
374 // FIXME: Hover for generic param 374 // FIXME: Hover for generic param
375 None 375 None
376 } 376 }
@@ -381,7 +381,8 @@ fn hover_for_definition(db: &RootDatabase, def: Definition) -> Option<Markup> {
381 D: HasSource<Ast = A> + HasAttrs + Copy, 381 D: HasSource<Ast = A> + HasAttrs + Copy,
382 A: ShortLabel, 382 A: ShortLabel,
383 { 383 {
384 let short_label = def.source(db).value.short_label(); 384 #[allow(deprecated)]
385 let short_label = def.source(db)?.value.short_label();
385 from_def_source_labeled(db, def, short_label, mod_path) 386 from_def_source_labeled(db, def, short_label, mod_path)
386 } 387 }
387 388
@@ -403,7 +404,7 @@ fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> {
403 return tokens.max_by_key(priority); 404 return tokens.max_by_key(priority);
404 fn priority(n: &SyntaxToken) -> usize { 405 fn priority(n: &SyntaxToken) -> usize {
405 match n.kind() { 406 match n.kind() {
406 IDENT | INT_NUMBER => 3, 407 IDENT | INT_NUMBER | LIFETIME_IDENT => 3,
407 T!['('] | T![')'] => 2, 408 T!['('] | T![')'] => 2,
408 kind if kind.is_trivia() => 0, 409 kind if kind.is_trivia() => 0,
409 _ => 1, 410 _ => 1,
@@ -1169,7 +1170,10 @@ fn f() { fo<|>o!(); }
1169 r#"struct TS(String, i32<|>);"#, 1170 r#"struct TS(String, i32<|>);"#,
1170 expect![[r#" 1171 expect![[r#"
1171 *i32* 1172 *i32*
1173
1174 ```rust
1172 i32 1175 i32
1176 ```
1173 "#]], 1177 "#]],
1174 ) 1178 )
1175 } 1179 }
@@ -3221,4 +3225,36 @@ fn no_hover() {
3221"#, 3225"#,
3222 ); 3226 );
3223 } 3227 }
3228
3229 #[test]
3230 fn hover_label() {
3231 check(
3232 r#"
3233fn foo() {
3234 'label<|>: loop {}
3235}
3236"#,
3237 expect![[r#"
3238 *'label*
3239
3240 ```rust
3241 'label
3242 ```
3243 "#]],
3244 );
3245 }
3246
3247 #[test]
3248 fn hover_lifetime() {
3249 check(
3250 r#"fn foo<'lifetime>(_: &'lifetime<|> ()) {}"#,
3251 expect![[r#"
3252 *'lifetime*
3253
3254 ```rust
3255 'lifetime
3256 ```
3257 "#]],
3258 );
3259 }
3224} 3260}
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs
index b3331f03f..a450794f3 100644
--- a/crates/ide/src/lib.rs
+++ b/crates/ide/src/lib.rs
@@ -31,6 +31,7 @@ mod folding_ranges;
31mod goto_definition; 31mod goto_definition;
32mod goto_implementation; 32mod goto_implementation;
33mod goto_type_definition; 33mod goto_type_definition;
34mod view_hir;
34mod hover; 35mod hover;
35mod inlay_hints; 36mod inlay_hints;
36mod join_lines; 37mod join_lines;
@@ -271,6 +272,10 @@ impl Analysis {
271 self.with_db(|db| syntax_tree::syntax_tree(&db, file_id, text_range)) 272 self.with_db(|db| syntax_tree::syntax_tree(&db, file_id, text_range))
272 } 273 }
273 274
275 pub fn view_hir(&self, position: FilePosition) -> Cancelable<String> {
276 self.with_db(|db| view_hir::view_hir(&db, position))
277 }
278
274 pub fn expand_macro(&self, position: FilePosition) -> Cancelable<Option<ExpandedMacro>> { 279 pub fn expand_macro(&self, position: FilePosition) -> Cancelable<Option<ExpandedMacro>> {
275 self.with_db(|db| expand_macro::expand_macro(db, position)) 280 self.with_db(|db| expand_macro::expand_macro(db, position))
276 } 281 }
diff --git a/crates/ide/src/references.rs b/crates/ide/src/references.rs
index 21b2d7ca1..fa58fc319 100644
--- a/crates/ide/src/references.rs
+++ b/crates/ide/src/references.rs
@@ -1144,4 +1144,20 @@ fn foo<'a>() -> &'a () {
1144 "#]], 1144 "#]],
1145 ); 1145 );
1146 } 1146 }
1147
1148 #[test]
1149 fn test_find_const_param() {
1150 check(
1151 r#"
1152fn foo<const FOO<|>: usize>() -> usize {
1153 FOO
1154}
1155"#,
1156 expect![[r#"
1157 FOO ConstParam FileId(0) 7..23 13..16 Other
1158
1159 FileId(0) 42..45 Other
1160 "#]],
1161 );
1162 }
1147} 1163}
diff --git a/crates/ide/src/runnables.rs b/crates/ide/src/runnables.rs
index 891183266..c893afc7c 100644
--- a/crates/ide/src/runnables.rs
+++ b/crates/ide/src/runnables.rs
@@ -230,7 +230,7 @@ impl TestAttr {
230 230
231const RUSTDOC_FENCE: &str = "```"; 231const RUSTDOC_FENCE: &str = "```";
232const RUSTDOC_CODE_BLOCK_ATTRIBUTES_RUNNABLE: &[&str] = 232const RUSTDOC_CODE_BLOCK_ATTRIBUTES_RUNNABLE: &[&str] =
233 &["", "rust", "should_panic", "edition2015", "edition2018"]; 233 &["", "rust", "should_panic", "edition2015", "edition2018", "edition2021"];
234 234
235fn has_runnable_doc_test(attrs: &hir::Attrs) -> bool { 235fn has_runnable_doc_test(attrs: &hir::Attrs) -> bool {
236 attrs.docs().map_or(false, |doc| { 236 attrs.docs().map_or(false, |doc| {
diff --git a/crates/ide/src/syntax_highlighting.rs b/crates/ide/src/syntax_highlighting.rs
index 5ad96581b..ba0085244 100644
--- a/crates/ide/src/syntax_highlighting.rs
+++ b/crates/ide/src/syntax_highlighting.rs
@@ -819,6 +819,7 @@ fn highlight_def(db: &RootDatabase, def: Definition) -> Highlight {
819 }, 819 },
820 Definition::SelfType(_) => HighlightTag::Symbol(SymbolKind::Impl), 820 Definition::SelfType(_) => HighlightTag::Symbol(SymbolKind::Impl),
821 Definition::TypeParam(_) => HighlightTag::Symbol(SymbolKind::TypeParam), 821 Definition::TypeParam(_) => HighlightTag::Symbol(SymbolKind::TypeParam),
822 Definition::ConstParam(_) => HighlightTag::Symbol(SymbolKind::ConstParam),
822 Definition::Local(local) => { 823 Definition::Local(local) => {
823 let tag = if local.is_param(db) { 824 let tag = if local.is_param(db) {
824 HighlightTag::Symbol(SymbolKind::ValueParam) 825 HighlightTag::Symbol(SymbolKind::ValueParam)
diff --git a/crates/ide/src/syntax_highlighting/injection.rs b/crates/ide/src/syntax_highlighting/injection.rs
index 9eb184c74..6cbd683c6 100644
--- a/crates/ide/src/syntax_highlighting/injection.rs
+++ b/crates/ide/src/syntax_highlighting/injection.rs
@@ -54,8 +54,17 @@ pub(super) fn highlight_injection(
54type RangesMap = BTreeMap<TextSize, TextSize>; 54type RangesMap = BTreeMap<TextSize, TextSize>;
55 55
56const RUSTDOC_FENCE: &'static str = "```"; 56const RUSTDOC_FENCE: &'static str = "```";
57const RUSTDOC_FENCE_TOKENS: &[&'static str] = 57const RUSTDOC_FENCE_TOKENS: &[&'static str] = &[
58 &["", "rust", "should_panic", "ignore", "no_run", "compile_fail", "edition2015", "edition2018"]; 58 "",
59 "rust",
60 "should_panic",
61 "ignore",
62 "no_run",
63 "compile_fail",
64 "edition2015",
65 "edition2018",
66 "edition2021",
67];
59 68
60/// Extracts Rust code from documentation comments as well as a mapping from 69/// Extracts Rust code from documentation comments as well as a mapping from
61/// the extracted source code back to the original source ranges. 70/// the extracted source code back to the original source ranges.
diff --git a/crates/ide/src/syntax_highlighting/tags.rs b/crates/ide/src/syntax_highlighting/tags.rs
index 2a6cc0cab..8b8867079 100644
--- a/crates/ide/src/syntax_highlighting/tags.rs
+++ b/crates/ide/src/syntax_highlighting/tags.rs
@@ -77,6 +77,7 @@ impl HighlightTag {
77 SymbolKind::Function => "function", 77 SymbolKind::Function => "function",
78 SymbolKind::TypeAlias => "type_alias", 78 SymbolKind::TypeAlias => "type_alias",
79 SymbolKind::TypeParam => "type_param", 79 SymbolKind::TypeParam => "type_param",
80 SymbolKind::ConstParam => "const_param",
80 SymbolKind::LifetimeParam => "lifetime", 81 SymbolKind::LifetimeParam => "lifetime",
81 SymbolKind::Macro => "macro", 82 SymbolKind::Macro => "macro",
82 SymbolKind::Local => "variable", 83 SymbolKind::Local => "variable",
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlighting.html b/crates/ide/src/syntax_highlighting/test_data/highlighting.html
index 72ff9dd40..02270b077 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlighting.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlighting.html
@@ -118,6 +118,10 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
118 <span class="keyword control">loop</span> <span class="punctuation">{</span><span class="punctuation">}</span> 118 <span class="keyword control">loop</span> <span class="punctuation">{</span><span class="punctuation">}</span>
119<span class="punctuation">}</span> 119<span class="punctuation">}</span>
120 120
121<span class="keyword">fn</span> <span class="function declaration">const_param</span><span class="punctuation">&lt;</span><span class="keyword">const</span> <span class="const_param declaration">FOO</span><span class="punctuation">:</span> <span class="builtin_type">usize</span><span class="punctuation">&gt;</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">usize</span> <span class="punctuation">{</span>
122 <span class="const_param">FOO</span>
123<span class="punctuation">}</span>
124
121<span class="keyword">use</span> <span class="module">ops</span><span class="operator">::</span><span class="trait">Fn</span><span class="punctuation">;</span> 125<span class="keyword">use</span> <span class="module">ops</span><span class="operator">::</span><span class="trait">Fn</span><span class="punctuation">;</span>
122<span class="keyword">fn</span> <span class="function declaration">baz</span><span class="punctuation">&lt;</span><span class="type_param declaration">F</span><span class="punctuation">:</span> <span class="trait">Fn</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="operator">-&gt;</span> <span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">&gt;</span><span class="punctuation">(</span><span class="value_param declaration callable">f</span><span class="punctuation">:</span> <span class="type_param">F</span><span class="punctuation">)</span> <span class="punctuation">{</span> 126<span class="keyword">fn</span> <span class="function declaration">baz</span><span class="punctuation">&lt;</span><span class="type_param declaration">F</span><span class="punctuation">:</span> <span class="trait">Fn</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="operator">-&gt;</span> <span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">&gt;</span><span class="punctuation">(</span><span class="value_param declaration callable">f</span><span class="punctuation">:</span> <span class="type_param">F</span><span class="punctuation">)</span> <span class="punctuation">{</span>
123 <span class="value_param callable">f</span><span class="punctuation">(</span><span class="punctuation">)</span> 127 <span class="value_param callable">f</span><span class="punctuation">(</span><span class="punctuation">)</span>
diff --git a/crates/ide/src/syntax_highlighting/tests.rs b/crates/ide/src/syntax_highlighting/tests.rs
index e0df0d2b5..30b5b648e 100644
--- a/crates/ide/src/syntax_highlighting/tests.rs
+++ b/crates/ide/src/syntax_highlighting/tests.rs
@@ -91,6 +91,10 @@ fn never() -> ! {
91 loop {} 91 loop {}
92} 92}
93 93
94fn const_param<const FOO: usize>() -> usize {
95 FOO
96}
97
94use ops::Fn; 98use ops::Fn;
95fn baz<F: Fn() -> ()>(f: F) { 99fn baz<F: Fn() -> ()>(f: F) {
96 f() 100 f()
diff --git a/crates/ide/src/view_hir.rs b/crates/ide/src/view_hir.rs
new file mode 100644
index 000000000..cfcfb7cfb
--- /dev/null
+++ b/crates/ide/src/view_hir.rs
@@ -0,0 +1,25 @@
1use hir::{Function, Semantics};
2use ide_db::base_db::FilePosition;
3use ide_db::RootDatabase;
4use syntax::{algo::find_node_at_offset, ast, AstNode};
5
6// Feature: View Hir
7//
8// |===
9// | Editor | Action Name
10//
11// | VS Code | **Rust Analyzer: View Hir**
12// |===
13pub(crate) fn view_hir(db: &RootDatabase, position: FilePosition) -> String {
14 body_hir(db, position).unwrap_or("Not inside a function body".to_string())
15}
16
17fn body_hir(db: &RootDatabase, position: FilePosition) -> Option<String> {
18 let sema = Semantics::new(db);
19 let source_file = sema.parse(position.file_id);
20
21 let function = find_node_at_offset::<ast::Fn>(source_file.syntax(), position.offset)?;
22
23 let function: Function = sema.to_def(&function)?;
24 Some(function.debug_hir(db))
25}
diff --git a/crates/ide_db/Cargo.toml b/crates/ide_db/Cargo.toml
index ebe53c8ee..d3d3dc688 100644
--- a/crates/ide_db/Cargo.toml
+++ b/crates/ide_db/Cargo.toml
@@ -32,4 +32,4 @@ test_utils = { path = "../test_utils", version = "0.0.0" }
32hir = { path = "../hir", version = "0.0.0" } 32hir = { path = "../hir", version = "0.0.0" }
33 33
34[dev-dependencies] 34[dev-dependencies]
35expect-test = "1.0" 35expect-test = "1.1"
diff --git a/crates/ide_db/src/defs.rs b/crates/ide_db/src/defs.rs
index d33a6cb86..cc5078bf0 100644
--- a/crates/ide_db/src/defs.rs
+++ b/crates/ide_db/src/defs.rs
@@ -6,8 +6,8 @@
6// FIXME: this badly needs rename/rewrite (matklad, 2020-02-06). 6// FIXME: this badly needs rename/rewrite (matklad, 2020-02-06).
7 7
8use hir::{ 8use hir::{
9 db::HirDatabase, Crate, Field, HasVisibility, Impl, Label, LifetimeParam, Local, MacroDef, 9 db::HirDatabase, ConstParam, Crate, Field, HasVisibility, Impl, Label, LifetimeParam, Local,
10 Module, ModuleDef, Name, PathResolution, Semantics, TypeParam, Visibility, 10 MacroDef, Module, ModuleDef, Name, PathResolution, Semantics, TypeParam, Visibility,
11}; 11};
12use syntax::{ 12use syntax::{
13 ast::{self, AstNode}, 13 ast::{self, AstNode},
@@ -26,6 +26,7 @@ pub enum Definition {
26 Local(Local), 26 Local(Local),
27 TypeParam(TypeParam), 27 TypeParam(TypeParam),
28 LifetimeParam(LifetimeParam), 28 LifetimeParam(LifetimeParam),
29 ConstParam(ConstParam),
29 Label(Label), 30 Label(Label),
30} 31}
31 32
@@ -39,6 +40,7 @@ impl Definition {
39 Definition::Local(it) => Some(it.module(db)), 40 Definition::Local(it) => Some(it.module(db)),
40 Definition::TypeParam(it) => Some(it.module(db)), 41 Definition::TypeParam(it) => Some(it.module(db)),
41 Definition::LifetimeParam(it) => Some(it.module(db)), 42 Definition::LifetimeParam(it) => Some(it.module(db)),
43 Definition::ConstParam(it) => Some(it.module(db)),
42 Definition::Label(it) => Some(it.module(db)), 44 Definition::Label(it) => Some(it.module(db)),
43 } 45 }
44 } 46 }
@@ -52,6 +54,7 @@ impl Definition {
52 Definition::Local(_) => None, 54 Definition::Local(_) => None,
53 Definition::TypeParam(_) => None, 55 Definition::TypeParam(_) => None,
54 Definition::LifetimeParam(_) => None, 56 Definition::LifetimeParam(_) => None,
57 Definition::ConstParam(_) => None,
55 Definition::Label(_) => None, 58 Definition::Label(_) => None,
56 } 59 }
57 } 60 }
@@ -79,6 +82,7 @@ impl Definition {
79 Definition::Local(it) => it.name(db)?, 82 Definition::Local(it) => it.name(db)?,
80 Definition::TypeParam(it) => it.name(db), 83 Definition::TypeParam(it) => it.name(db),
81 Definition::LifetimeParam(it) => it.name(db), 84 Definition::LifetimeParam(it) => it.name(db),
85 Definition::ConstParam(it) => it.name(db),
82 Definition::Label(it) => it.name(db), 86 Definition::Label(it) => it.name(db),
83 }; 87 };
84 Some(name) 88 Some(name)
@@ -233,6 +237,10 @@ impl NameClass {
233 let def = sema.to_def(&it)?; 237 let def = sema.to_def(&it)?;
234 Some(NameClass::Definition(Definition::TypeParam(def))) 238 Some(NameClass::Definition(Definition::TypeParam(def)))
235 }, 239 },
240 ast::ConstParam(it) => {
241 let def = sema.to_def(&it)?;
242 Some(NameClass::Definition(Definition::ConstParam(def)))
243 },
236 _ => None, 244 _ => None,
237 } 245 }
238 } 246 }
@@ -417,6 +425,7 @@ impl From<PathResolution> for Definition {
417 PathResolution::TypeParam(par) => Definition::TypeParam(par), 425 PathResolution::TypeParam(par) => Definition::TypeParam(par),
418 PathResolution::Macro(def) => Definition::Macro(def), 426 PathResolution::Macro(def) => Definition::Macro(def),
419 PathResolution::SelfType(impl_def) => Definition::SelfType(impl_def), 427 PathResolution::SelfType(impl_def) => Definition::SelfType(impl_def),
428 PathResolution::ConstParam(par) => Definition::ConstParam(par),
420 } 429 }
421 } 430 }
422} 431}
diff --git a/crates/ide_db/src/search.rs b/crates/ide_db/src/search.rs
index ff10f71c3..436c59d2c 100644
--- a/crates/ide_db/src/search.rs
+++ b/crates/ide_db/src/search.rs
@@ -121,31 +121,56 @@ impl Definition {
121 121
122 if let Definition::Local(var) = self { 122 if let Definition::Local(var) = self {
123 let range = match var.parent(db) { 123 let range = match var.parent(db) {
124 DefWithBody::Function(f) => f.source(db).value.syntax().text_range(), 124 DefWithBody::Function(f) => {
125 DefWithBody::Const(c) => c.source(db).value.syntax().text_range(), 125 f.source(db).and_then(|src| Some(src.value.syntax().text_range()))
126 DefWithBody::Static(s) => s.source(db).value.syntax().text_range(), 126 }
127 DefWithBody::Const(c) => {
128 c.source(db).and_then(|src| Some(src.value.syntax().text_range()))
129 }
130 DefWithBody::Static(s) => {
131 s.source(db).and_then(|src| Some(src.value.syntax().text_range()))
132 }
127 }; 133 };
128 let mut res = FxHashMap::default(); 134 let mut res = FxHashMap::default();
129 res.insert(file_id, Some(range)); 135 res.insert(file_id, range);
130 return SearchScope::new(res); 136 return SearchScope::new(res);
131 } 137 }
132 138
133 if let Definition::LifetimeParam(param) = self { 139 if let Definition::LifetimeParam(param) = self {
140 #[allow(deprecated)]
134 let range = match param.parent(db) { 141 let range = match param.parent(db) {
135 hir::GenericDef::Function(it) => it.source(db).value.syntax().text_range(), 142 hir::GenericDef::Function(it) => {
143 it.source(db).and_then(|src| Some(src.value.syntax().text_range()))
144 }
136 hir::GenericDef::Adt(it) => match it { 145 hir::GenericDef::Adt(it) => match it {
137 hir::Adt::Struct(it) => it.source(db).value.syntax().text_range(), 146 hir::Adt::Struct(it) => {
138 hir::Adt::Union(it) => it.source(db).value.syntax().text_range(), 147 it.source(db).and_then(|src| Some(src.value.syntax().text_range()))
139 hir::Adt::Enum(it) => it.source(db).value.syntax().text_range(), 148 }
149 hir::Adt::Union(it) => {
150 it.source(db).and_then(|src| Some(src.value.syntax().text_range()))
151 }
152 hir::Adt::Enum(it) => {
153 it.source(db).and_then(|src| Some(src.value.syntax().text_range()))
154 }
140 }, 155 },
141 hir::GenericDef::Trait(it) => it.source(db).value.syntax().text_range(), 156 hir::GenericDef::Trait(it) => {
142 hir::GenericDef::TypeAlias(it) => it.source(db).value.syntax().text_range(), 157 it.source(db).and_then(|src| Some(src.value.syntax().text_range()))
143 hir::GenericDef::Impl(it) => it.source(db).value.syntax().text_range(), 158 }
144 hir::GenericDef::Variant(it) => it.source(db).value.syntax().text_range(), 159 hir::GenericDef::TypeAlias(it) => {
145 hir::GenericDef::Const(it) => it.source(db).value.syntax().text_range(), 160 it.source(db).and_then(|src| Some(src.value.syntax().text_range()))
161 }
162 hir::GenericDef::Impl(it) => {
163 it.source(db).and_then(|src| Some(src.value.syntax().text_range()))
164 }
165 hir::GenericDef::Variant(it) => {
166 it.source(db).and_then(|src| Some(src.value.syntax().text_range()))
167 }
168 hir::GenericDef::Const(it) => {
169 it.source(db).and_then(|src| Some(src.value.syntax().text_range()))
170 }
146 }; 171 };
147 let mut res = FxHashMap::default(); 172 let mut res = FxHashMap::default();
148 res.insert(file_id, Some(range)); 173 res.insert(file_id, range);
149 return SearchScope::new(res); 174 return SearchScope::new(res);
150 } 175 }
151 176
diff --git a/crates/proc_macro_api/src/msg.rs b/crates/proc_macro_api/src/msg.rs
index f84ebdbc5..4cd572101 100644
--- a/crates/proc_macro_api/src/msg.rs
+++ b/crates/proc_macro_api/src/msg.rs
@@ -58,7 +58,13 @@ pub trait Message: Serialize + DeserializeOwned {
58 fn read(inp: &mut impl BufRead) -> io::Result<Option<Self>> { 58 fn read(inp: &mut impl BufRead) -> io::Result<Option<Self>> {
59 Ok(match read_json(inp)? { 59 Ok(match read_json(inp)? {
60 None => None, 60 None => None,
61 Some(text) => Some(serde_json::from_str(&text)?), 61 Some(text) => {
62 let mut deserializer = serde_json::Deserializer::from_str(&text);
63 // Note that some proc-macro generate very deep syntax tree
64 // We have to disable the current limit of serde here
65 deserializer.disable_recursion_limit();
66 Some(Self::deserialize(&mut deserializer)?)
67 }
62 }) 68 })
63 } 69 }
64 fn write(self, out: &mut impl Write) -> io::Result<()> { 70 fn write(self, out: &mut impl Write) -> io::Result<()> {
diff --git a/crates/proc_macro_api/src/process.rs b/crates/proc_macro_api/src/process.rs
index d68723ada..6d6ab8888 100644
--- a/crates/proc_macro_api/src/process.rs
+++ b/crates/proc_macro_api/src/process.rs
@@ -92,10 +92,11 @@ fn client_loop(task_rx: Receiver<Task>, mut process: Process) {
92 for Task { req, result_tx } in task_rx { 92 for Task { req, result_tx } in task_rx {
93 match send_request(&mut stdin, &mut stdout, req) { 93 match send_request(&mut stdin, &mut stdout, req) {
94 Ok(res) => result_tx.send(res).unwrap(), 94 Ok(res) => result_tx.send(res).unwrap(),
95 Err(_err) => { 95 Err(err) => {
96 log::error!( 96 log::error!(
97 "proc macro server crashed, server process state: {:?}", 97 "proc macro server crashed, server process state: {:?}, server request error: {:?}",
98 process.child.try_wait() 98 process.child.try_wait(),
99 err
99 ); 100 );
100 let res = Response::Error(ResponseError { 101 let res = Response::Error(ResponseError {
101 code: ErrorCode::ServerErrorEnd, 102 code: ErrorCode::ServerErrorEnd,
diff --git a/crates/project_model/src/project_json.rs b/crates/project_model/src/project_json.rs
index aab279223..af884eb84 100644
--- a/crates/project_model/src/project_json.rs
+++ b/crates/project_model/src/project_json.rs
@@ -139,6 +139,8 @@ enum EditionData {
139 Edition2015, 139 Edition2015,
140 #[serde(rename = "2018")] 140 #[serde(rename = "2018")]
141 Edition2018, 141 Edition2018,
142 #[serde(rename = "2021")]
143 Edition2021,
142} 144}
143 145
144impl From<EditionData> for Edition { 146impl From<EditionData> for Edition {
@@ -146,6 +148,7 @@ impl From<EditionData> for Edition {
146 match data { 148 match data {
147 EditionData::Edition2015 => Edition::Edition2015, 149 EditionData::Edition2015 => Edition::Edition2015,
148 EditionData::Edition2018 => Edition::Edition2018, 150 EditionData::Edition2018 => Edition::Edition2018,
151 EditionData::Edition2021 => Edition::Edition2021,
149 } 152 }
150 } 153 }
151} 154}
diff --git a/crates/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml
index 0a63593fb..af7b86ead 100644
--- a/crates/rust-analyzer/Cargo.toml
+++ b/crates/rust-analyzer/Cargo.toml
@@ -62,7 +62,7 @@ proc_macro_srv = { path = "../proc_macro_srv", version = "0.0.0" }
62winapi = "0.3.8" 62winapi = "0.3.8"
63 63
64[dev-dependencies] 64[dev-dependencies]
65expect-test = "1.0" 65expect-test = "1.1"
66test_utils = { path = "../test_utils" } 66test_utils = { path = "../test_utils" }
67mbe = { path = "../mbe" } 67mbe = { path = "../mbe" }
68tt = { path = "../tt" } 68tt = { path = "../tt" }
diff --git a/crates/rust-analyzer/src/cli/analysis_stats.rs b/crates/rust-analyzer/src/cli/analysis_stats.rs
index a23fb7a33..9445aec07 100644
--- a/crates/rust-analyzer/src/cli/analysis_stats.rs
+++ b/crates/rust-analyzer/src/cli/analysis_stats.rs
@@ -161,11 +161,12 @@ impl AnalysisStatsCmd {
161 } 161 }
162 let mut msg = format!("processing: {}", full_name); 162 let mut msg = format!("processing: {}", full_name);
163 if verbosity.is_verbose() { 163 if verbosity.is_verbose() {
164 let src = f.source(db); 164 if let Some(src) = f.source(db) {
165 let original_file = src.file_id.original_file(db); 165 let original_file = src.file_id.original_file(db);
166 let path = vfs.file_path(original_file); 166 let path = vfs.file_path(original_file);
167 let syntax_range = src.value.syntax().text_range(); 167 let syntax_range = src.value.syntax().text_range();
168 format_to!(msg, " ({} {:?})", path, syntax_range); 168 format_to!(msg, " ({} {:?})", path, syntax_range);
169 }
169 } 170 }
170 if verbosity.is_spammy() { 171 if verbosity.is_spammy() {
171 bar.println(msg.to_string()); 172 bar.println(msg.to_string());
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index 1db5b4e7d..685a9fdf0 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -679,7 +679,7 @@ fn schema(fields: &[(&'static str, &'static str, &[&str], &str)]) -> serde_json:
679 for ((f1, ..), (f2, ..)) in fields.iter().zip(&fields[1..]) { 679 for ((f1, ..), (f2, ..)) in fields.iter().zip(&fields[1..]) {
680 fn key(f: &str) -> &str { 680 fn key(f: &str) -> &str {
681 f.splitn(2, "_").next().unwrap() 681 f.splitn(2, "_").next().unwrap()
682 }; 682 }
683 assert!(key(f1) <= key(f2), "wrong field order: {:?} {:?}", f1, f2); 683 assert!(key(f1) <= key(f2), "wrong field order: {:?} {:?}", f1, f2);
684 } 684 }
685 685
diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs
index 948cfc17c..dd486070b 100644
--- a/crates/rust-analyzer/src/handlers.rs
+++ b/crates/rust-analyzer/src/handlers.rs
@@ -105,6 +105,16 @@ pub(crate) fn handle_syntax_tree(
105 Ok(res) 105 Ok(res)
106} 106}
107 107
108pub(crate) fn handle_view_hir(
109 snap: GlobalStateSnapshot,
110 params: lsp_types::TextDocumentPositionParams,
111) -> Result<String> {
112 let _p = profile::span("handle_view_hir");
113 let position = from_proto::file_position(&snap, params)?;
114 let res = snap.analysis.view_hir(position)?;
115 Ok(res)
116}
117
108pub(crate) fn handle_expand_macro( 118pub(crate) fn handle_expand_macro(
109 snap: GlobalStateSnapshot, 119 snap: GlobalStateSnapshot,
110 params: lsp_ext::ExpandMacroParams, 120 params: lsp_ext::ExpandMacroParams,
diff --git a/crates/rust-analyzer/src/lsp_ext.rs b/crates/rust-analyzer/src/lsp_ext.rs
index 93ac45415..a85978737 100644
--- a/crates/rust-analyzer/src/lsp_ext.rs
+++ b/crates/rust-analyzer/src/lsp_ext.rs
@@ -53,6 +53,14 @@ pub struct SyntaxTreeParams {
53 pub range: Option<Range>, 53 pub range: Option<Range>,
54} 54}
55 55
56pub enum ViewHir {}
57
58impl Request for ViewHir {
59 type Params = lsp_types::TextDocumentPositionParams;
60 type Result = String;
61 const METHOD: &'static str = "rust-analyzer/viewHir";
62}
63
56pub enum ExpandMacro {} 64pub enum ExpandMacro {}
57 65
58impl Request for ExpandMacro { 66impl Request for ExpandMacro {
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index 5d55dc96e..8eca79f7e 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -443,6 +443,7 @@ impl GlobalState {
443 .on_sync::<lsp_ext::MemoryUsage>(|s, p| handlers::handle_memory_usage(s, p))? 443 .on_sync::<lsp_ext::MemoryUsage>(|s, p| handlers::handle_memory_usage(s, p))?
444 .on::<lsp_ext::AnalyzerStatus>(handlers::handle_analyzer_status) 444 .on::<lsp_ext::AnalyzerStatus>(handlers::handle_analyzer_status)
445 .on::<lsp_ext::SyntaxTree>(handlers::handle_syntax_tree) 445 .on::<lsp_ext::SyntaxTree>(handlers::handle_syntax_tree)
446 .on::<lsp_ext::ViewHir>(handlers::handle_view_hir)
446 .on::<lsp_ext::ExpandMacro>(handlers::handle_expand_macro) 447 .on::<lsp_ext::ExpandMacro>(handlers::handle_expand_macro)
447 .on::<lsp_ext::ParentModule>(handlers::handle_parent_module) 448 .on::<lsp_ext::ParentModule>(handlers::handle_parent_module)
448 .on::<lsp_ext::Runnables>(handlers::handle_runnables) 449 .on::<lsp_ext::Runnables>(handlers::handle_runnables)
diff --git a/crates/rust-analyzer/src/markdown.rs b/crates/rust-analyzer/src/markdown.rs
index 968ea55f0..a49a58c00 100644
--- a/crates/rust-analyzer/src/markdown.rs
+++ b/crates/rust-analyzer/src/markdown.rs
@@ -1,8 +1,17 @@
1//! Transforms markdown 1//! Transforms markdown
2 2
3const RUSTDOC_FENCE: &str = "```"; 3const RUSTDOC_FENCE: &str = "```";
4const RUSTDOC_CODE_BLOCK_ATTRIBUTES_RUST_SPECIFIC: &[&str] = 4const RUSTDOC_CODE_BLOCK_ATTRIBUTES_RUST_SPECIFIC: &[&str] = &[
5 &["", "rust", "should_panic", "ignore", "no_run", "compile_fail", "edition2015", "edition2018"]; 5 "",
6 "rust",
7 "should_panic",
8 "ignore",
9 "no_run",
10 "compile_fail",
11 "edition2015",
12 "edition2018",
13 "edition2021",
14];
6 15
7pub(crate) fn format_docs(src: &str) -> String { 16pub(crate) fn format_docs(src: &str) -> String {
8 let mut processed_lines = Vec::new(); 17 let mut processed_lines = Vec::new();
diff --git a/crates/rust-analyzer/src/semantic_tokens.rs b/crates/rust-analyzer/src/semantic_tokens.rs
index c2f6a655d..5c4366f16 100644
--- a/crates/rust-analyzer/src/semantic_tokens.rs
+++ b/crates/rust-analyzer/src/semantic_tokens.rs
@@ -44,6 +44,7 @@ define_semantic_token_types![
44 (ESCAPE_SEQUENCE, "escapeSequence"), 44 (ESCAPE_SEQUENCE, "escapeSequence"),
45 (FORMAT_SPECIFIER, "formatSpecifier"), 45 (FORMAT_SPECIFIER, "formatSpecifier"),
46 (GENERIC, "generic"), 46 (GENERIC, "generic"),
47 (CONST_PARAMETER, "constParameter"),
47 (LIFETIME, "lifetime"), 48 (LIFETIME, "lifetime"),
48 (LABEL, "label"), 49 (LABEL, "label"),
49 (PUNCTUATION, "punctuation"), 50 (PUNCTUATION, "punctuation"),
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs
index 1a38e79f0..999b18351 100644
--- a/crates/rust-analyzer/src/to_proto.rs
+++ b/crates/rust-analyzer/src/to_proto.rs
@@ -42,6 +42,7 @@ pub(crate) fn symbol_kind(symbol_kind: SymbolKind) -> lsp_types::SymbolKind {
42 SymbolKind::Field => lsp_types::SymbolKind::Field, 42 SymbolKind::Field => lsp_types::SymbolKind::Field,
43 SymbolKind::Static => lsp_types::SymbolKind::Constant, 43 SymbolKind::Static => lsp_types::SymbolKind::Constant,
44 SymbolKind::Const => lsp_types::SymbolKind::Constant, 44 SymbolKind::Const => lsp_types::SymbolKind::Constant,
45 SymbolKind::ConstParam => lsp_types::SymbolKind::Constant,
45 SymbolKind::Impl => lsp_types::SymbolKind::Object, 46 SymbolKind::Impl => lsp_types::SymbolKind::Object,
46 SymbolKind::Local 47 SymbolKind::Local
47 | SymbolKind::SelfParam 48 | SymbolKind::SelfParam
@@ -378,6 +379,7 @@ fn semantic_token_type_and_modifiers(
378 SymbolKind::Impl => lsp_types::SemanticTokenType::TYPE, 379 SymbolKind::Impl => lsp_types::SemanticTokenType::TYPE,
379 SymbolKind::Field => lsp_types::SemanticTokenType::PROPERTY, 380 SymbolKind::Field => lsp_types::SemanticTokenType::PROPERTY,
380 SymbolKind::TypeParam => lsp_types::SemanticTokenType::TYPE_PARAMETER, 381 SymbolKind::TypeParam => lsp_types::SemanticTokenType::TYPE_PARAMETER,
382 SymbolKind::ConstParam => semantic_tokens::CONST_PARAMETER,
381 SymbolKind::LifetimeParam => semantic_tokens::LIFETIME, 383 SymbolKind::LifetimeParam => semantic_tokens::LIFETIME,
382 SymbolKind::Label => semantic_tokens::LABEL, 384 SymbolKind::Label => semantic_tokens::LABEL,
383 SymbolKind::ValueParam => lsp_types::SemanticTokenType::PARAMETER, 385 SymbolKind::ValueParam => lsp_types::SemanticTokenType::PARAMETER,
diff --git a/crates/ssr/Cargo.toml b/crates/ssr/Cargo.toml
index 339eda86a..cc8136d22 100644
--- a/crates/ssr/Cargo.toml
+++ b/crates/ssr/Cargo.toml
@@ -21,4 +21,4 @@ hir = { path = "../hir", version = "0.0.0" }
21test_utils = { path = "../test_utils", version = "0.0.0" } 21test_utils = { path = "../test_utils", version = "0.0.0" }
22 22
23[dev-dependencies] 23[dev-dependencies]
24expect-test = "1.0" 24expect-test = "1.1"
diff --git a/crates/syntax/Cargo.toml b/crates/syntax/Cargo.toml
index 5d8389ade..181077944 100644
--- a/crates/syntax/Cargo.toml
+++ b/crates/syntax/Cargo.toml
@@ -33,4 +33,4 @@ profile = { path = "../profile", version = "0.0.0" }
33[dev-dependencies] 33[dev-dependencies]
34walkdir = "2.3.1" 34walkdir = "2.3.1"
35rayon = "1" 35rayon = "1"
36expect-test = "1.0" 36expect-test = "1.1"
diff --git a/crates/syntax/src/ast/edit.rs b/crates/syntax/src/ast/edit.rs
index 77233ab31..824ebf41c 100644
--- a/crates/syntax/src/ast/edit.rs
+++ b/crates/syntax/src/ast/edit.rs
@@ -220,7 +220,7 @@ impl ast::RecordExprFieldList {
220 InsertPosition::After($anchor.syntax().clone().into()) 220 InsertPosition::After($anchor.syntax().clone().into())
221 } 221 }
222 }; 222 };
223 }; 223 }
224 224
225 let position = match position { 225 let position = match position {
226 InsertPosition::First => after_l_curly!(), 226 InsertPosition::First => after_l_curly!(),
@@ -533,7 +533,7 @@ impl ast::GenericParamList {
533 InsertPosition::After($anchor.syntax().clone().into()) 533 InsertPosition::After($anchor.syntax().clone().into())
534 } 534 }
535 }; 535 };
536 }; 536 }
537 537
538 let position = match self.generic_params().last() { 538 let position = match self.generic_params().last() {
539 Some(it) => after_field!(it), 539 Some(it) => after_field!(it),
diff --git a/docs/dev/README.md b/docs/dev/README.md
index 4a2f9feb3..55527bab0 100644
--- a/docs/dev/README.md
+++ b/docs/dev/README.md
@@ -227,6 +227,8 @@ There are also two VS Code commands which might be of interest:
227 227
228* `Rust Analyzer: Syntax Tree` shows syntax tree of the current file/selection. 228* `Rust Analyzer: Syntax Tree` shows syntax tree of the current file/selection.
229 229
230* `Rust Analyzer: View Hir` shows the HIR expressions within the function containing the cursor.
231
230 You can hover over syntax nodes in the opened text file to see the appropriate 232 You can hover over syntax nodes in the opened text file to see the appropriate
231 rust code that it refers to and the rust editor will also highlight the proper 233 rust code that it refers to and the rust editor will also highlight the proper
232 text range. 234 text range.
diff --git a/docs/dev/lsp-extensions.md b/docs/dev/lsp-extensions.md
index 8c01db07c..78d86f060 100644
--- a/docs/dev/lsp-extensions.md
+++ b/docs/dev/lsp-extensions.md
@@ -1,5 +1,5 @@
1<!--- 1<!---
2lsp_ext.rs hash: 203fdf79b21b5987 2lsp_ext.rs hash: 91f2c62457e0a20f
3 3
4If you need to change the above hash to make the test pass, please check if you 4If you need to change the above hash to make the test pass, please check if you
5need to adjust this doc as well and ping this issue: 5need to adjust this doc as well and ping this issue:
@@ -449,6 +449,17 @@ interface SyntaxTeeParams {
449Returns textual representation of a parse tree for the file/selected region. 449Returns textual representation of a parse tree for the file/selected region.
450Primarily for debugging, but very useful for all people working on rust-analyzer itself. 450Primarily for debugging, but very useful for all people working on rust-analyzer itself.
451 451
452## View Hir
453
454**Method:** `rust-analyzer/viewHir`
455
456**Request:** `TextDocumentPositionParams`
457
458**Response:** `string`
459
460Returns a textual representation of the HIR of the function containing the cursor.
461For debugging or when working on rust-analyzer itself.
462
452## Expand Macro 463## Expand Macro
453 464
454**Method:** `rust-analyzer/expandMacro` 465**Method:** `rust-analyzer/expandMacro`
diff --git a/docs/user/manual.adoc b/docs/user/manual.adoc
index 8ed7b041f..266baffa1 100644
--- a/docs/user/manual.adoc
+++ b/docs/user/manual.adoc
@@ -342,7 +342,7 @@ interface Crate {
342 /// Path to the root module of the crate. 342 /// Path to the root module of the crate.
343 root_module: string; 343 root_module: string;
344 /// Edition of the crate. 344 /// Edition of the crate.
345 edition: "2015" | "2018"; 345 edition: "2015" | "2018" | "2021";
346 /// Dependencies 346 /// Dependencies
347 deps: Dep[]; 347 deps: Dep[];
348 /// Should this crate be treated as a member of current "workspace". 348 /// Should this crate be treated as a member of current "workspace".
diff --git a/editors/code/package.json b/editors/code/package.json
index 587f11b90..3e55a3523 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -104,6 +104,11 @@
104 "category": "Rust Analyzer" 104 "category": "Rust Analyzer"
105 }, 105 },
106 { 106 {
107 "command": "rust-analyzer.viewHir",
108 "title": "View Hir",
109 "category": "Rust Analyzer"
110 },
111 {
107 "command": "rust-analyzer.expandMacro", 112 "command": "rust-analyzer.expandMacro",
108 "title": "Expand macro recursively", 113 "title": "Expand macro recursively",
109 "category": "Rust Analyzer" 114 "category": "Rust Analyzer"
@@ -1007,6 +1012,10 @@
1007 "when": "inRustProject" 1012 "when": "inRustProject"
1008 }, 1013 },
1009 { 1014 {
1015 "command": "rust-analyzer.viewHir",
1016 "when": "inRustProject"
1017 },
1018 {
1010 "command": "rust-analyzer.expandMacro", 1019 "command": "rust-analyzer.expandMacro",
1011 "when": "inRustProject" 1020 "when": "inRustProject"
1012 }, 1021 },
diff --git a/editors/code/src/commands.ts b/editors/code/src/commands.ts
index b12e134ca..c1c9f9754 100644
--- a/editors/code/src/commands.ts
+++ b/editors/code/src/commands.ts
@@ -340,6 +340,61 @@ export function syntaxTree(ctx: Ctx): Cmd {
340 }; 340 };
341} 341}
342 342
343// Opens the virtual file that will show the HIR of the function containing the cursor position
344//
345// The contents of the file come from the `TextDocumentContentProvider`
346export function viewHir(ctx: Ctx): Cmd {
347 const tdcp = new class implements vscode.TextDocumentContentProvider {
348 readonly uri = vscode.Uri.parse('rust-analyzer://viewHir/hir.txt');
349 readonly eventEmitter = new vscode.EventEmitter<vscode.Uri>();
350 constructor() {
351 vscode.workspace.onDidChangeTextDocument(this.onDidChangeTextDocument, this, ctx.subscriptions);
352 vscode.window.onDidChangeActiveTextEditor(this.onDidChangeActiveTextEditor, this, ctx.subscriptions);
353 }
354
355 private onDidChangeTextDocument(event: vscode.TextDocumentChangeEvent) {
356 if (isRustDocument(event.document)) {
357 // We need to order this after language server updates, but there's no API for that.
358 // Hence, good old sleep().
359 void sleep(10).then(() => this.eventEmitter.fire(this.uri));
360 }
361 }
362 private onDidChangeActiveTextEditor(editor: vscode.TextEditor | undefined) {
363 if (editor && isRustEditor(editor)) {
364 this.eventEmitter.fire(this.uri);
365 }
366 }
367
368 provideTextDocumentContent(_uri: vscode.Uri, ct: vscode.CancellationToken): vscode.ProviderResult<string> {
369 const rustEditor = ctx.activeRustEditor;
370 const client = ctx.client;
371 if (!rustEditor || !client) return '';
372
373 const params = {
374 textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(rustEditor.document),
375 position: client.code2ProtocolConverter.asPosition(
376 rustEditor.selection.active,
377 ),
378 };
379 return client.sendRequest(ra.viewHir, params, ct);
380 }
381
382 get onDidChange(): vscode.Event<vscode.Uri> {
383 return this.eventEmitter.event;
384 }
385 };
386
387 ctx.pushCleanup(vscode.workspace.registerTextDocumentContentProvider('rust-analyzer', tdcp));
388
389 return async () => {
390 const document = await vscode.workspace.openTextDocument(tdcp.uri);
391 tdcp.eventEmitter.fire(tdcp.uri);
392 void await vscode.window.showTextDocument(document, {
393 viewColumn: vscode.ViewColumn.Two,
394 preserveFocus: true
395 });
396 };
397}
343 398
344// Opens the virtual file that will show the syntax tree 399// Opens the virtual file that will show the syntax tree
345// 400//
diff --git a/editors/code/src/lsp_ext.ts b/editors/code/src/lsp_ext.ts
index 5e877ce65..d21a3db86 100644
--- a/editors/code/src/lsp_ext.ts
+++ b/editors/code/src/lsp_ext.ts
@@ -24,6 +24,7 @@ export interface SyntaxTreeParams {
24} 24}
25export const syntaxTree = new lc.RequestType<SyntaxTreeParams, string, void>("rust-analyzer/syntaxTree"); 25export const syntaxTree = new lc.RequestType<SyntaxTreeParams, string, void>("rust-analyzer/syntaxTree");
26 26
27export const viewHir = new lc.RequestType<lc.TextDocumentPositionParams, string, void>("rust-analyzer/viewHir");
27 28
28export interface ExpandMacroParams { 29export interface ExpandMacroParams {
29 textDocument: lc.TextDocumentIdentifier; 30 textDocument: lc.TextDocumentIdentifier;
diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts
index 282240d84..60907dfd4 100644
--- a/editors/code/src/main.ts
+++ b/editors/code/src/main.ts
@@ -105,6 +105,7 @@ async function tryActivate(context: vscode.ExtensionContext) {
105 ctx.registerCommand('joinLines', commands.joinLines); 105 ctx.registerCommand('joinLines', commands.joinLines);
106 ctx.registerCommand('parentModule', commands.parentModule); 106 ctx.registerCommand('parentModule', commands.parentModule);
107 ctx.registerCommand('syntaxTree', commands.syntaxTree); 107 ctx.registerCommand('syntaxTree', commands.syntaxTree);
108 ctx.registerCommand('viewHir', commands.viewHir);
108 ctx.registerCommand('expandMacro', commands.expandMacro); 109 ctx.registerCommand('expandMacro', commands.expandMacro);
109 ctx.registerCommand('run', commands.run); 110 ctx.registerCommand('run', commands.run);
110 ctx.registerCommand('debug', commands.debug); 111 ctx.registerCommand('debug', commands.debug);
diff --git a/xtask/src/dist.rs b/xtask/src/dist.rs
index d59b88131..6bc34106b 100644
--- a/xtask/src/dist.rs
+++ b/xtask/src/dist.rs
@@ -63,8 +63,7 @@ fn dist_server() -> Result<()> {
63 env::set_var("CC", "clang"); 63 env::set_var("CC", "clang");
64 } 64 }
65 65
66 let toolchain = toolchain(&target); 66 cmd!("cargo build --manifest-path ./crates/rust-analyzer/Cargo.toml --bin rust-analyzer --target {target} --release").run()?;
67 cmd!("cargo +{toolchain} build --manifest-path ./crates/rust-analyzer/Cargo.toml --bin rust-analyzer --target {target} --release").run()?;
68 67
69 let suffix = exe_suffix(&target); 68 let suffix = exe_suffix(&target);
70 let src = 69 let src =
@@ -118,13 +117,6 @@ fn exe_suffix(target: &str) -> String {
118 } 117 }
119} 118}
120 119
121fn toolchain(target: &str) -> String {
122 match target {
123 "aarch64-apple-darwin" => "beta".to_string(),
124 _ => "stable".to_string(),
125 }
126}
127
128fn gzip(src_path: &Path, dest_path: &Path) -> Result<()> { 120fn gzip(src_path: &Path, dest_path: &Path) -> Result<()> {
129 let mut encoder = GzEncoder::new(File::create(dest_path)?, Compression::best()); 121 let mut encoder = GzEncoder::new(File::create(dest_path)?, Compression::best());
130 let mut input = io::BufReader::new(File::open(src_path)?); 122 let mut input = io::BufReader::new(File::open(src_path)?);
diff --git a/xtask/tests/tidy.rs b/xtask/tests/tidy.rs
index 6bfa922e6..d1ffb70ad 100644
--- a/xtask/tests/tidy.rs
+++ b/xtask/tests/tidy.rs
@@ -139,7 +139,7 @@ fn deny_clippy(path: &PathBuf, text: &String) {
139 return; 139 return;
140 } 140 }
141 141
142 if text.contains("[\u{61}llow(clippy") { 142 if text.contains("\u{61}llow(clippy") {
143 panic!( 143 panic!(
144 "\n\nallowing lints is forbidden: {}. 144 "\n\nallowing lints is forbidden: {}.
145rust-analyzer intentionally doesn't check clippy on CI. 145rust-analyzer intentionally doesn't check clippy on CI.