diff options
Diffstat (limited to 'crates')
35 files changed, 360 insertions, 142 deletions
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 05a60e158..4ee08ef21 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs | |||
@@ -51,7 +51,7 @@ use hir_def::{ | |||
51 | }; | 51 | }; |
52 | use hir_expand::{diagnostics::DiagnosticSink, name::name, MacroDefKind}; | 52 | use hir_expand::{diagnostics::DiagnosticSink, name::name, MacroDefKind}; |
53 | use hir_ty::{ | 53 | use hir_ty::{ |
54 | autoderef, | 54 | autoderef, could_unify, |
55 | method_resolution::{self, TyFingerprint}, | 55 | method_resolution::{self, TyFingerprint}, |
56 | primitive::UintTy, | 56 | primitive::UintTy, |
57 | to_assoc_type_id, | 57 | to_assoc_type_id, |
@@ -2154,6 +2154,10 @@ impl Type { | |||
2154 | 2154 | ||
2155 | walk_type(db, self, &mut cb); | 2155 | walk_type(db, self, &mut cb); |
2156 | } | 2156 | } |
2157 | |||
2158 | pub fn could_unify_with(&self, other: &Type) -> bool { | ||
2159 | could_unify(&self.ty, &other.ty) | ||
2160 | } | ||
2157 | } | 2161 | } |
2158 | 2162 | ||
2159 | // FIXME: closures | 2163 | // FIXME: closures |
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs index 1198e3f0b..3ff135f41 100644 --- a/crates/hir/src/semantics.rs +++ b/crates/hir/src/semantics.rs | |||
@@ -445,7 +445,7 @@ impl<'db> SemanticsImpl<'db> { | |||
445 | } | 445 | } |
446 | }; | 446 | }; |
447 | gpl.lifetime_params() | 447 | gpl.lifetime_params() |
448 | .find(|tp| tp.lifetime().as_ref().map(|lt| lt.text()) == Some(text)) | 448 | .find(|tp| tp.lifetime().as_ref().map(|lt| lt.text()).as_ref() == Some(&text)) |
449 | })?; | 449 | })?; |
450 | let src = self.find_file(lifetime_param.syntax().clone()).with_value(lifetime_param); | 450 | let src = self.find_file(lifetime_param.syntax().clone()).with_value(lifetime_param); |
451 | ToDef::to_def(self, src) | 451 | ToDef::to_def(self, src) |
diff --git a/crates/hir_def/src/body/lower.rs b/crates/hir_def/src/body/lower.rs index 19f5065d1..229e81dd4 100644 --- a/crates/hir_def/src/body/lower.rs +++ b/crates/hir_def/src/body/lower.rs | |||
@@ -74,6 +74,7 @@ pub(super) fn lower( | |||
74 | _c: Count::new(), | 74 | _c: Count::new(), |
75 | }, | 75 | }, |
76 | expander, | 76 | expander, |
77 | statements_in_scope: Vec::new(), | ||
77 | } | 78 | } |
78 | .collect(params, body) | 79 | .collect(params, body) |
79 | } | 80 | } |
@@ -83,6 +84,7 @@ struct ExprCollector<'a> { | |||
83 | expander: Expander, | 84 | expander: Expander, |
84 | body: Body, | 85 | body: Body, |
85 | source_map: BodySourceMap, | 86 | source_map: BodySourceMap, |
87 | statements_in_scope: Vec<Statement>, | ||
86 | } | 88 | } |
87 | 89 | ||
88 | impl ExprCollector<'_> { | 90 | impl ExprCollector<'_> { |
@@ -533,15 +535,13 @@ impl ExprCollector<'_> { | |||
533 | ids[0] | 535 | ids[0] |
534 | } | 536 | } |
535 | ast::Expr::MacroStmts(e) => { | 537 | ast::Expr::MacroStmts(e) => { |
536 | // FIXME: these statements should be held by some hir containter | 538 | e.statements().for_each(|s| self.collect_stmt(s)); |
537 | for stmt in e.statements() { | 539 | let tail = e |
538 | self.collect_stmt(stmt); | 540 | .expr() |
539 | } | 541 | .map(|e| self.collect_expr(e)) |
540 | if let Some(expr) = e.expr() { | 542 | .unwrap_or_else(|| self.alloc_expr(Expr::Missing, syntax_ptr.clone())); |
541 | self.collect_expr(expr) | 543 | |
542 | } else { | 544 | self.alloc_expr(Expr::MacroStmts { tail }, syntax_ptr) |
543 | self.alloc_expr(Expr::Missing, syntax_ptr) | ||
544 | } | ||
545 | } | 545 | } |
546 | }) | 546 | }) |
547 | } | 547 | } |
@@ -618,58 +618,54 @@ impl ExprCollector<'_> { | |||
618 | } | 618 | } |
619 | } | 619 | } |
620 | 620 | ||
621 | fn collect_stmt(&mut self, s: ast::Stmt) -> Option<Vec<Statement>> { | 621 | fn collect_stmt(&mut self, s: ast::Stmt) { |
622 | let stmt = match s { | 622 | match s { |
623 | ast::Stmt::LetStmt(stmt) => { | 623 | ast::Stmt::LetStmt(stmt) => { |
624 | self.check_cfg(&stmt)?; | 624 | if self.check_cfg(&stmt).is_none() { |
625 | 625 | return; | |
626 | } | ||
626 | let pat = self.collect_pat_opt(stmt.pat()); | 627 | let pat = self.collect_pat_opt(stmt.pat()); |
627 | let type_ref = stmt.ty().map(|it| TypeRef::from_ast(&self.ctx(), it)); | 628 | let type_ref = stmt.ty().map(|it| TypeRef::from_ast(&self.ctx(), it)); |
628 | let initializer = stmt.initializer().map(|e| self.collect_expr(e)); | 629 | let initializer = stmt.initializer().map(|e| self.collect_expr(e)); |
629 | vec![Statement::Let { pat, type_ref, initializer }] | 630 | self.statements_in_scope.push(Statement::Let { pat, type_ref, initializer }); |
630 | } | 631 | } |
631 | ast::Stmt::ExprStmt(stmt) => { | 632 | ast::Stmt::ExprStmt(stmt) => { |
632 | self.check_cfg(&stmt)?; | 633 | if self.check_cfg(&stmt).is_none() { |
634 | return; | ||
635 | } | ||
633 | 636 | ||
634 | // Note that macro could be expended to multiple statements | 637 | // Note that macro could be expended to multiple statements |
635 | if let Some(ast::Expr::MacroCall(m)) = stmt.expr() { | 638 | if let Some(ast::Expr::MacroCall(m)) = stmt.expr() { |
636 | let syntax_ptr = AstPtr::new(&stmt.expr().unwrap()); | 639 | let syntax_ptr = AstPtr::new(&stmt.expr().unwrap()); |
637 | let mut stmts = vec![]; | ||
638 | 640 | ||
639 | self.collect_macro_call(m, syntax_ptr.clone(), false, |this, expansion| { | 641 | self.collect_macro_call(m, syntax_ptr.clone(), false, |this, expansion| { |
640 | match expansion { | 642 | match expansion { |
641 | Some(expansion) => { | 643 | Some(expansion) => { |
642 | let statements: ast::MacroStmts = expansion; | 644 | let statements: ast::MacroStmts = expansion; |
643 | 645 | ||
644 | statements.statements().for_each(|stmt| { | 646 | statements.statements().for_each(|stmt| this.collect_stmt(stmt)); |
645 | if let Some(mut r) = this.collect_stmt(stmt) { | ||
646 | stmts.append(&mut r); | ||
647 | } | ||
648 | }); | ||
649 | if let Some(expr) = statements.expr() { | 647 | if let Some(expr) = statements.expr() { |
650 | stmts.push(Statement::Expr(this.collect_expr(expr))); | 648 | let expr = this.collect_expr(expr); |
649 | this.statements_in_scope.push(Statement::Expr(expr)); | ||
651 | } | 650 | } |
652 | } | 651 | } |
653 | None => { | 652 | None => { |
654 | stmts.push(Statement::Expr( | 653 | let expr = this.alloc_expr(Expr::Missing, syntax_ptr.clone()); |
655 | this.alloc_expr(Expr::Missing, syntax_ptr.clone()), | 654 | this.statements_in_scope.push(Statement::Expr(expr)); |
656 | )); | ||
657 | } | 655 | } |
658 | } | 656 | } |
659 | }); | 657 | }); |
660 | stmts | ||
661 | } else { | 658 | } else { |
662 | vec![Statement::Expr(self.collect_expr_opt(stmt.expr()))] | 659 | let expr = self.collect_expr_opt(stmt.expr()); |
660 | self.statements_in_scope.push(Statement::Expr(expr)); | ||
663 | } | 661 | } |
664 | } | 662 | } |
665 | ast::Stmt::Item(item) => { | 663 | ast::Stmt::Item(item) => { |
666 | self.check_cfg(&item)?; | 664 | if self.check_cfg(&item).is_none() { |
667 | 665 | return; | |
668 | return None; | 666 | } |
669 | } | 667 | } |
670 | }; | 668 | } |
671 | |||
672 | Some(stmt) | ||
673 | } | 669 | } |
674 | 670 | ||
675 | fn collect_block(&mut self, block: ast::BlockExpr) -> ExprId { | 671 | fn collect_block(&mut self, block: ast::BlockExpr) -> ExprId { |
@@ -685,10 +681,12 @@ impl ExprCollector<'_> { | |||
685 | let module = if has_def_map { def_map.root() } else { self.expander.module }; | 681 | let module = if has_def_map { def_map.root() } else { self.expander.module }; |
686 | let prev_def_map = mem::replace(&mut self.expander.def_map, def_map); | 682 | let prev_def_map = mem::replace(&mut self.expander.def_map, def_map); |
687 | let prev_local_module = mem::replace(&mut self.expander.module, module); | 683 | let prev_local_module = mem::replace(&mut self.expander.module, module); |
684 | let prev_statements = std::mem::take(&mut self.statements_in_scope); | ||
685 | |||
686 | block.statements().for_each(|s| self.collect_stmt(s)); | ||
688 | 687 | ||
689 | let statements = | ||
690 | block.statements().filter_map(|s| self.collect_stmt(s)).flatten().collect(); | ||
691 | let tail = block.tail_expr().map(|e| self.collect_expr(e)); | 688 | let tail = block.tail_expr().map(|e| self.collect_expr(e)); |
689 | let statements = std::mem::replace(&mut self.statements_in_scope, prev_statements); | ||
692 | let syntax_node_ptr = AstPtr::new(&block.into()); | 690 | let syntax_node_ptr = AstPtr::new(&block.into()); |
693 | let expr_id = self.alloc_expr( | 691 | let expr_id = self.alloc_expr( |
694 | Expr::Block { id: block_id, statements, tail, label: None }, | 692 | Expr::Block { id: block_id, statements, tail, label: None }, |
diff --git a/crates/hir_def/src/expr.rs b/crates/hir_def/src/expr.rs index 24be93773..6c7376fad 100644 --- a/crates/hir_def/src/expr.rs +++ b/crates/hir_def/src/expr.rs | |||
@@ -171,6 +171,9 @@ pub enum Expr { | |||
171 | Unsafe { | 171 | Unsafe { |
172 | body: ExprId, | 172 | body: ExprId, |
173 | }, | 173 | }, |
174 | MacroStmts { | ||
175 | tail: ExprId, | ||
176 | }, | ||
174 | Array(Array), | 177 | Array(Array), |
175 | Literal(Literal), | 178 | Literal(Literal), |
176 | } | 179 | } |
@@ -357,6 +360,7 @@ impl Expr { | |||
357 | f(*repeat) | 360 | f(*repeat) |
358 | } | 361 | } |
359 | }, | 362 | }, |
363 | Expr::MacroStmts { tail } => f(*tail), | ||
360 | Expr::Literal(_) => {} | 364 | Expr::Literal(_) => {} |
361 | } | 365 | } |
362 | } | 366 | } |
diff --git a/crates/hir_def/src/item_tree.rs b/crates/hir_def/src/item_tree.rs index ae2475b4e..ca0048b16 100644 --- a/crates/hir_def/src/item_tree.rs +++ b/crates/hir_def/src/item_tree.rs | |||
@@ -110,15 +110,6 @@ impl ItemTree { | |||
110 | // still need to collect inner items. | 110 | // still need to collect inner items. |
111 | ctx.lower_inner_items(e.syntax()) | 111 | ctx.lower_inner_items(e.syntax()) |
112 | }, | 112 | }, |
113 | ast::ExprStmt(stmt) => { | ||
114 | // Macros can expand to stmt. We return an empty item tree in this case, but | ||
115 | // still need to collect inner items. | ||
116 | ctx.lower_inner_items(stmt.syntax()) | ||
117 | }, | ||
118 | ast::Item(item) => { | ||
119 | // Macros can expand to stmt and other item, and we add it as top level item | ||
120 | ctx.lower_single_item(item) | ||
121 | }, | ||
122 | _ => { | 113 | _ => { |
123 | panic!("cannot create item tree from {:?} {}", syntax, syntax); | 114 | panic!("cannot create item tree from {:?} {}", syntax, syntax); |
124 | }, | 115 | }, |
diff --git a/crates/hir_def/src/item_tree/lower.rs b/crates/hir_def/src/item_tree/lower.rs index d3fe1ce1e..3f558edd8 100644 --- a/crates/hir_def/src/item_tree/lower.rs +++ b/crates/hir_def/src/item_tree/lower.rs | |||
@@ -87,14 +87,6 @@ impl Ctx { | |||
87 | self.tree | 87 | self.tree |
88 | } | 88 | } |
89 | 89 | ||
90 | pub(super) fn lower_single_item(mut self, item: ast::Item) -> ItemTree { | ||
91 | self.tree.top_level = self | ||
92 | .lower_mod_item(&item, false) | ||
93 | .map(|item| item.0) | ||
94 | .unwrap_or_else(|| Default::default()); | ||
95 | self.tree | ||
96 | } | ||
97 | |||
98 | pub(super) fn lower_inner_items(mut self, within: &SyntaxNode) -> ItemTree { | 90 | pub(super) fn lower_inner_items(mut self, within: &SyntaxNode) -> ItemTree { |
99 | self.collect_inner_items(within); | 91 | self.collect_inner_items(within); |
100 | self.tree | 92 | self.tree |
diff --git a/crates/hir_expand/src/db.rs b/crates/hir_expand/src/db.rs index fc73e435b..d672f6723 100644 --- a/crates/hir_expand/src/db.rs +++ b/crates/hir_expand/src/db.rs | |||
@@ -5,7 +5,13 @@ use std::sync::Arc; | |||
5 | use base_db::{salsa, SourceDatabase}; | 5 | use base_db::{salsa, SourceDatabase}; |
6 | use mbe::{ExpandError, ExpandResult, MacroRules}; | 6 | use mbe::{ExpandError, ExpandResult, MacroRules}; |
7 | use parser::FragmentKind; | 7 | use parser::FragmentKind; |
8 | use syntax::{algo::diff, ast::NameOwner, AstNode, GreenNode, Parse, SyntaxKind::*, SyntaxNode}; | 8 | use syntax::{ |
9 | algo::diff, | ||
10 | ast::{MacroStmts, NameOwner}, | ||
11 | AstNode, GreenNode, Parse, | ||
12 | SyntaxKind::*, | ||
13 | SyntaxNode, | ||
14 | }; | ||
9 | 15 | ||
10 | use crate::{ | 16 | use crate::{ |
11 | ast_id_map::AstIdMap, hygiene::HygieneFrame, BuiltinDeriveExpander, BuiltinFnLikeExpander, | 17 | ast_id_map::AstIdMap, hygiene::HygieneFrame, BuiltinDeriveExpander, BuiltinFnLikeExpander, |
@@ -340,13 +346,19 @@ fn parse_macro_with_arg( | |||
340 | None => return ExpandResult { value: None, err: result.err }, | 346 | None => return ExpandResult { value: None, err: result.err }, |
341 | }; | 347 | }; |
342 | 348 | ||
343 | log::debug!("expanded = {}", tt.as_debug_string()); | ||
344 | |||
345 | let fragment_kind = to_fragment_kind(db, macro_call_id); | 349 | let fragment_kind = to_fragment_kind(db, macro_call_id); |
346 | 350 | ||
351 | log::debug!("expanded = {}", tt.as_debug_string()); | ||
352 | log::debug!("kind = {:?}", fragment_kind); | ||
353 | |||
347 | let (parse, rev_token_map) = match mbe::token_tree_to_syntax_node(&tt, fragment_kind) { | 354 | let (parse, rev_token_map) = match mbe::token_tree_to_syntax_node(&tt, fragment_kind) { |
348 | Ok(it) => it, | 355 | Ok(it) => it, |
349 | Err(err) => { | 356 | Err(err) => { |
357 | log::debug!( | ||
358 | "failed to parse expanstion to {:?} = {}", | ||
359 | fragment_kind, | ||
360 | tt.as_debug_string() | ||
361 | ); | ||
350 | return ExpandResult::only_err(err); | 362 | return ExpandResult::only_err(err); |
351 | } | 363 | } |
352 | }; | 364 | }; |
@@ -362,15 +374,34 @@ fn parse_macro_with_arg( | |||
362 | return ExpandResult::only_err(err); | 374 | return ExpandResult::only_err(err); |
363 | } | 375 | } |
364 | }; | 376 | }; |
365 | 377 | if is_self_replicating(&node, &call_node.value) { | |
366 | if !diff(&node, &call_node.value).is_empty() { | ||
367 | ExpandResult { value: Some((parse, Arc::new(rev_token_map))), err: Some(err) } | ||
368 | } else { | ||
369 | return ExpandResult::only_err(err); | 378 | return ExpandResult::only_err(err); |
379 | } else { | ||
380 | ExpandResult { value: Some((parse, Arc::new(rev_token_map))), err: Some(err) } | ||
381 | } | ||
382 | } | ||
383 | None => { | ||
384 | log::debug!("parse = {:?}", parse.syntax_node().kind()); | ||
385 | ExpandResult { value: Some((parse, Arc::new(rev_token_map))), err: None } | ||
386 | } | ||
387 | } | ||
388 | } | ||
389 | |||
390 | fn is_self_replicating(from: &SyntaxNode, to: &SyntaxNode) -> bool { | ||
391 | if diff(from, to).is_empty() { | ||
392 | return true; | ||
393 | } | ||
394 | if let Some(stmts) = MacroStmts::cast(from.clone()) { | ||
395 | if stmts.statements().any(|stmt| diff(stmt.syntax(), to).is_empty()) { | ||
396 | return true; | ||
397 | } | ||
398 | if let Some(expr) = stmts.expr() { | ||
399 | if diff(expr.syntax(), to).is_empty() { | ||
400 | return true; | ||
370 | } | 401 | } |
371 | } | 402 | } |
372 | None => ExpandResult { value: Some((parse, Arc::new(rev_token_map))), err: None }, | ||
373 | } | 403 | } |
404 | false | ||
374 | } | 405 | } |
375 | 406 | ||
376 | fn hygiene_frame(db: &dyn AstDatabase, file_id: HirFileId) -> Arc<HygieneFrame> { | 407 | fn hygiene_frame(db: &dyn AstDatabase, file_id: HirFileId) -> Arc<HygieneFrame> { |
@@ -390,21 +421,15 @@ fn to_fragment_kind(db: &dyn AstDatabase, id: MacroCallId) -> FragmentKind { | |||
390 | 421 | ||
391 | let parent = match syn.parent() { | 422 | let parent = match syn.parent() { |
392 | Some(it) => it, | 423 | Some(it) => it, |
393 | None => { | 424 | None => return FragmentKind::Statements, |
394 | // FIXME: | ||
395 | // If it is root, which means the parent HirFile | ||
396 | // MacroKindFile must be non-items | ||
397 | // return expr now. | ||
398 | return FragmentKind::Expr; | ||
399 | } | ||
400 | }; | 425 | }; |
401 | 426 | ||
402 | match parent.kind() { | 427 | match parent.kind() { |
403 | MACRO_ITEMS | SOURCE_FILE => FragmentKind::Items, | 428 | MACRO_ITEMS | SOURCE_FILE => FragmentKind::Items, |
404 | MACRO_STMTS => FragmentKind::Statement, | 429 | MACRO_STMTS => FragmentKind::Statements, |
405 | ITEM_LIST => FragmentKind::Items, | 430 | ITEM_LIST => FragmentKind::Items, |
406 | LET_STMT => { | 431 | LET_STMT => { |
407 | // FIXME: Handle Pattern | 432 | // FIXME: Handle LHS Pattern |
408 | FragmentKind::Expr | 433 | FragmentKind::Expr |
409 | } | 434 | } |
410 | EXPR_STMT => FragmentKind::Statements, | 435 | EXPR_STMT => FragmentKind::Statements, |
diff --git a/crates/hir_expand/src/name.rs b/crates/hir_expand/src/name.rs index 0aeea48d5..cd691b1d2 100644 --- a/crates/hir_expand/src/name.rs +++ b/crates/hir_expand/src/name.rs | |||
@@ -75,14 +75,14 @@ impl AsName for ast::NameRef { | |||
75 | fn as_name(&self) -> Name { | 75 | fn as_name(&self) -> Name { |
76 | match self.as_tuple_field() { | 76 | match self.as_tuple_field() { |
77 | Some(idx) => Name::new_tuple_field(idx), | 77 | Some(idx) => Name::new_tuple_field(idx), |
78 | None => Name::resolve(self.text()), | 78 | None => Name::resolve(&self.text()), |
79 | } | 79 | } |
80 | } | 80 | } |
81 | } | 81 | } |
82 | 82 | ||
83 | impl AsName for ast::Name { | 83 | impl AsName for ast::Name { |
84 | fn as_name(&self) -> Name { | 84 | fn as_name(&self) -> Name { |
85 | Name::resolve(self.text()) | 85 | Name::resolve(&self.text()) |
86 | } | 86 | } |
87 | } | 87 | } |
88 | 88 | ||
diff --git a/crates/hir_ty/src/infer.rs b/crates/hir_ty/src/infer.rs index 8f9cf7480..e4407ff50 100644 --- a/crates/hir_ty/src/infer.rs +++ b/crates/hir_ty/src/infer.rs | |||
@@ -45,6 +45,11 @@ use crate::{ | |||
45 | to_assoc_type_id, to_chalk_trait_id, AliasEq, AliasTy, Interner, TyKind, | 45 | to_assoc_type_id, to_chalk_trait_id, AliasEq, AliasTy, Interner, TyKind, |
46 | }; | 46 | }; |
47 | 47 | ||
48 | // This lint has a false positive here. See the link below for details. | ||
49 | // | ||
50 | // https://github.com/rust-lang/rust/issues/57411 | ||
51 | #[allow(unreachable_pub)] | ||
52 | pub use unify::could_unify; | ||
48 | pub(crate) use unify::unify; | 53 | pub(crate) use unify::unify; |
49 | 54 | ||
50 | mod unify; | 55 | mod unify; |
diff --git a/crates/hir_ty/src/infer/expr.rs b/crates/hir_ty/src/infer/expr.rs index 3f3187ea2..e6ede05ca 100644 --- a/crates/hir_ty/src/infer/expr.rs +++ b/crates/hir_ty/src/infer/expr.rs | |||
@@ -767,6 +767,7 @@ impl<'a> InferenceContext<'a> { | |||
767 | None => self.table.new_float_var(), | 767 | None => self.table.new_float_var(), |
768 | }, | 768 | }, |
769 | }, | 769 | }, |
770 | Expr::MacroStmts { tail } => self.infer_expr(*tail, expected), | ||
770 | }; | 771 | }; |
771 | // use a new type variable if we got unknown here | 772 | // use a new type variable if we got unknown here |
772 | let ty = self.insert_type_vars_shallow(ty); | 773 | let ty = self.insert_type_vars_shallow(ty); |
diff --git a/crates/hir_ty/src/infer/unify.rs b/crates/hir_ty/src/infer/unify.rs index 75250a369..6e7b0f5a6 100644 --- a/crates/hir_ty/src/infer/unify.rs +++ b/crates/hir_ty/src/infer/unify.rs | |||
@@ -157,6 +157,10 @@ impl<T> Canonicalized<T> { | |||
157 | } | 157 | } |
158 | } | 158 | } |
159 | 159 | ||
160 | pub fn could_unify(t1: &Ty, t2: &Ty) -> bool { | ||
161 | InferenceTable::new().unify(t1, t2) | ||
162 | } | ||
163 | |||
160 | pub(crate) fn unify(tys: &Canonical<(Ty, Ty)>) -> Option<Substitution> { | 164 | pub(crate) fn unify(tys: &Canonical<(Ty, Ty)>) -> Option<Substitution> { |
161 | let mut table = InferenceTable::new(); | 165 | let mut table = InferenceTable::new(); |
162 | let vars = Substitution( | 166 | let vars = Substitution( |
diff --git a/crates/hir_ty/src/lib.rs b/crates/hir_ty/src/lib.rs index 69265286f..6f9c698e6 100644 --- a/crates/hir_ty/src/lib.rs +++ b/crates/hir_ty/src/lib.rs | |||
@@ -41,7 +41,7 @@ use crate::{ | |||
41 | }; | 41 | }; |
42 | 42 | ||
43 | pub use autoderef::autoderef; | 43 | pub use autoderef::autoderef; |
44 | pub use infer::{InferenceResult, InferenceVar}; | 44 | pub use infer::{could_unify, InferenceResult, InferenceVar}; |
45 | pub use lower::{ | 45 | pub use lower::{ |
46 | associated_type_shorthand_candidates, callable_item_sig, CallableDefId, ImplTraitLoweringMode, | 46 | associated_type_shorthand_candidates, callable_item_sig, CallableDefId, ImplTraitLoweringMode, |
47 | TyDefId, TyLoweringContext, ValueTyDefId, | 47 | TyDefId, TyLoweringContext, ValueTyDefId, |
diff --git a/crates/hir_ty/src/tests/macros.rs b/crates/hir_ty/src/tests/macros.rs index 7eda51866..01935ec99 100644 --- a/crates/hir_ty/src/tests/macros.rs +++ b/crates/hir_ty/src/tests/macros.rs | |||
@@ -226,12 +226,49 @@ fn expr_macro_expanded_in_stmts() { | |||
226 | "#, | 226 | "#, |
227 | expect![[r#" | 227 | expect![[r#" |
228 | !0..8 'leta=();': () | 228 | !0..8 'leta=();': () |
229 | !0..8 'leta=();': () | ||
230 | !3..4 'a': () | ||
231 | !5..7 '()': () | ||
229 | 57..84 '{ ...); } }': () | 232 | 57..84 '{ ...); } }': () |
230 | "#]], | 233 | "#]], |
231 | ); | 234 | ); |
232 | } | 235 | } |
233 | 236 | ||
234 | #[test] | 237 | #[test] |
238 | fn recurisve_macro_expanded_in_stmts() { | ||
239 | check_infer( | ||
240 | r#" | ||
241 | macro_rules! ng { | ||
242 | ([$($tts:tt)*]) => { | ||
243 | $($tts)*; | ||
244 | }; | ||
245 | ([$($tts:tt)*] $head:tt $($rest:tt)*) => { | ||
246 | ng! { | ||
247 | [$($tts)* $head] $($rest)* | ||
248 | } | ||
249 | }; | ||
250 | } | ||
251 | fn foo() { | ||
252 | ng!([] let a = 3); | ||
253 | let b = a; | ||
254 | } | ||
255 | "#, | ||
256 | expect![[r#" | ||
257 | !0..7 'leta=3;': {unknown} | ||
258 | !0..7 'leta=3;': {unknown} | ||
259 | !0..13 'ng!{[leta=3]}': {unknown} | ||
260 | !0..13 'ng!{[leta=]3}': {unknown} | ||
261 | !0..13 'ng!{[leta]=3}': {unknown} | ||
262 | !3..4 'a': i32 | ||
263 | !5..6 '3': i32 | ||
264 | 196..237 '{ ...= a; }': () | ||
265 | 229..230 'b': i32 | ||
266 | 233..234 'a': i32 | ||
267 | "#]], | ||
268 | ); | ||
269 | } | ||
270 | |||
271 | #[test] | ||
235 | fn recursive_inner_item_macro_rules() { | 272 | fn recursive_inner_item_macro_rules() { |
236 | check_infer( | 273 | check_infer( |
237 | r#" | 274 | r#" |
@@ -246,7 +283,8 @@ fn recursive_inner_item_macro_rules() { | |||
246 | "#, | 283 | "#, |
247 | expect![[r#" | 284 | expect![[r#" |
248 | !0..1 '1': i32 | 285 | !0..1 '1': i32 |
249 | !0..7 'mac!($)': {unknown} | 286 | !0..26 'macro_...>{1};}': {unknown} |
287 | !0..26 'macro_...>{1};}': {unknown} | ||
250 | 107..143 '{ ...!(); }': () | 288 | 107..143 '{ ...!(); }': () |
251 | 129..130 'a': i32 | 289 | 129..130 'a': i32 |
252 | "#]], | 290 | "#]], |
diff --git a/crates/ide/src/diagnostics/fixes.rs b/crates/ide/src/diagnostics/fixes.rs index 2f840909c..5fb3e2d91 100644 --- a/crates/ide/src/diagnostics/fixes.rs +++ b/crates/ide/src/diagnostics/fixes.rs | |||
@@ -210,7 +210,7 @@ fn missing_record_expr_field_fix( | |||
210 | } | 210 | } |
211 | let new_field = make::record_field( | 211 | let new_field = make::record_field( |
212 | None, | 212 | None, |
213 | make::name(record_expr_field.field_name()?.text()), | 213 | make::name(&record_expr_field.field_name()?.text()), |
214 | make::ty(&new_field_type.display_source_code(sema.db, module.into()).ok()?), | 214 | make::ty(&new_field_type.display_source_code(sema.db, module.into()).ok()?), |
215 | ); | 215 | ); |
216 | 216 | ||
diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs index 16c04eeee..25f96222c 100644 --- a/crates/ide/src/inlay_hints.rs +++ b/crates/ide/src/inlay_hints.rs | |||
@@ -416,7 +416,7 @@ fn get_string_representation(expr: &ast::Expr) -> Option<String> { | |||
416 | match expr { | 416 | match expr { |
417 | ast::Expr::MethodCallExpr(method_call_expr) => { | 417 | ast::Expr::MethodCallExpr(method_call_expr) => { |
418 | let name_ref = method_call_expr.name_ref()?; | 418 | let name_ref = method_call_expr.name_ref()?; |
419 | match name_ref.text() { | 419 | match name_ref.text().as_str() { |
420 | "clone" => method_call_expr.receiver().map(|rec| rec.to_string()), | 420 | "clone" => method_call_expr.receiver().map(|rec| rec.to_string()), |
421 | name_ref => Some(name_ref.to_owned()), | 421 | name_ref => Some(name_ref.to_owned()), |
422 | } | 422 | } |
diff --git a/crates/ide/src/runnables.rs b/crates/ide/src/runnables.rs index 5b488e2c5..7e4c5a078 100644 --- a/crates/ide/src/runnables.rs +++ b/crates/ide/src/runnables.rs | |||
@@ -226,7 +226,9 @@ pub(crate) fn runnable_fn(sema: &Semantics<RootDatabase>, def: hir::Function) -> | |||
226 | let func = def.source(sema.db)?; | 226 | let func = def.source(sema.db)?; |
227 | let name_string = def.name(sema.db).to_string(); | 227 | let name_string = def.name(sema.db).to_string(); |
228 | 228 | ||
229 | let kind = if name_string == "main" { | 229 | let root = def.krate(sema.db)?.root_module(sema.db); |
230 | |||
231 | let kind = if name_string == "main" && def.module(sema.db) == root { | ||
230 | RunnableKind::Bin | 232 | RunnableKind::Bin |
231 | } else { | 233 | } else { |
232 | let canonical_path = { | 234 | let canonical_path = { |
@@ -444,6 +446,10 @@ fn test_foo() {} | |||
444 | 446 | ||
445 | #[bench] | 447 | #[bench] |
446 | fn bench() {} | 448 | fn bench() {} |
449 | |||
450 | mod not_a_root { | ||
451 | fn main() {} | ||
452 | } | ||
447 | "#, | 453 | "#, |
448 | &[&BIN, &TEST, &TEST, &BENCH], | 454 | &[&BIN, &TEST, &TEST, &BENCH], |
449 | expect![[r#" | 455 | expect![[r#" |
diff --git a/crates/ide/src/syntax_highlighting/format.rs b/crates/ide/src/syntax_highlighting/format.rs index e503abc93..5bbadb0f4 100644 --- a/crates/ide/src/syntax_highlighting/format.rs +++ b/crates/ide/src/syntax_highlighting/format.rs | |||
@@ -31,7 +31,7 @@ fn is_format_string(string: &ast::String) -> Option<()> { | |||
31 | let parent = string.syntax().parent()?; | 31 | let parent = string.syntax().parent()?; |
32 | 32 | ||
33 | let name = parent.parent().and_then(ast::MacroCall::cast)?.path()?.segment()?.name_ref()?; | 33 | let name = parent.parent().and_then(ast::MacroCall::cast)?.path()?.segment()?.name_ref()?; |
34 | if !matches!(name.text(), "format_args" | "format_args_nl") { | 34 | if !matches!(name.text().as_str(), "format_args" | "format_args_nl") { |
35 | return None; | 35 | return None; |
36 | } | 36 | } |
37 | 37 | ||
diff --git a/crates/ide_assists/src/handlers/extract_struct_from_enum_variant.rs b/crates/ide_assists/src/handlers/extract_struct_from_enum_variant.rs index 596c536a7..a8d6355bd 100644 --- a/crates/ide_assists/src/handlers/extract_struct_from_enum_variant.rs +++ b/crates/ide_assists/src/handlers/extract_struct_from_enum_variant.rs | |||
@@ -195,7 +195,7 @@ fn extract_struct_def( | |||
195 | 195 | ||
196 | fn update_variant(rewriter: &mut SyntaxRewriter, variant: &ast::Variant) -> Option<()> { | 196 | fn update_variant(rewriter: &mut SyntaxRewriter, variant: &ast::Variant) -> Option<()> { |
197 | let name = variant.name()?; | 197 | let name = variant.name()?; |
198 | let tuple_field = make::tuple_field(None, make::ty(name.text())); | 198 | let tuple_field = make::tuple_field(None, make::ty(&name.text())); |
199 | let replacement = make::variant( | 199 | let replacement = make::variant( |
200 | name, | 200 | name, |
201 | Some(ast::FieldList::TupleFieldList(make::tuple_field_list(iter::once(tuple_field)))), | 201 | Some(ast::FieldList::TupleFieldList(make::tuple_field_list(iter::once(tuple_field)))), |
diff --git a/crates/ide_assists/src/handlers/generate_enum_is_method.rs b/crates/ide_assists/src/handlers/generate_enum_is_method.rs index 7e181a480..a9f71a703 100644 --- a/crates/ide_assists/src/handlers/generate_enum_is_method.rs +++ b/crates/ide_assists/src/handlers/generate_enum_is_method.rs | |||
@@ -44,7 +44,7 @@ pub(crate) fn generate_enum_is_method(acc: &mut Assists, ctx: &AssistContext) -> | |||
44 | }; | 44 | }; |
45 | 45 | ||
46 | let enum_lowercase_name = to_lower_snake_case(&parent_enum.name()?.to_string()); | 46 | let enum_lowercase_name = to_lower_snake_case(&parent_enum.name()?.to_string()); |
47 | let fn_name = format!("is_{}", &to_lower_snake_case(variant_name.text())); | 47 | let fn_name = format!("is_{}", &to_lower_snake_case(&variant_name.text())); |
48 | 48 | ||
49 | // Return early if we've found an existing new fn | 49 | // Return early if we've found an existing new fn |
50 | let impl_def = find_struct_impl(&ctx, &parent_enum, &fn_name)?; | 50 | let impl_def = find_struct_impl(&ctx, &parent_enum, &fn_name)?; |
diff --git a/crates/ide_assists/src/handlers/generate_enum_projection_method.rs b/crates/ide_assists/src/handlers/generate_enum_projection_method.rs index 871bcab50..e2f572ba3 100644 --- a/crates/ide_assists/src/handlers/generate_enum_projection_method.rs +++ b/crates/ide_assists/src/handlers/generate_enum_projection_method.rs | |||
@@ -132,7 +132,8 @@ fn generate_enum_projection_method( | |||
132 | ast::StructKind::Unit => return None, | 132 | ast::StructKind::Unit => return None, |
133 | }; | 133 | }; |
134 | 134 | ||
135 | let fn_name = format!("{}_{}", props.fn_name_prefix, &to_lower_snake_case(variant_name.text())); | 135 | let fn_name = |
136 | format!("{}_{}", props.fn_name_prefix, &to_lower_snake_case(&variant_name.text())); | ||
136 | 137 | ||
137 | // Return early if we've found an existing new fn | 138 | // Return early if we've found an existing new fn |
138 | let impl_def = find_struct_impl(&ctx, &parent_enum, &fn_name)?; | 139 | let impl_def = find_struct_impl(&ctx, &parent_enum, &fn_name)?; |
diff --git a/crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs b/crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs index 4f0ef52ca..f872d20c8 100644 --- a/crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs +++ b/crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs | |||
@@ -165,7 +165,7 @@ fn impl_def_from_trait( | |||
165 | } | 165 | } |
166 | let impl_def = make::impl_trait( | 166 | let impl_def = make::impl_trait( |
167 | trait_path.clone(), | 167 | trait_path.clone(), |
168 | make::path_unqualified(make::path_segment(make::name_ref(annotated_name.text()))), | 168 | make::path_unqualified(make::path_segment(make::name_ref(&annotated_name.text()))), |
169 | ); | 169 | ); |
170 | let (impl_def, first_assoc_item) = | 170 | let (impl_def, first_assoc_item) = |
171 | add_trait_assoc_items_to_impl(sema, trait_items, trait_, impl_def, target_scope); | 171 | add_trait_assoc_items_to_impl(sema, trait_items, trait_, impl_def, target_scope); |
@@ -178,12 +178,13 @@ fn update_attribute( | |||
178 | trait_name: &ast::NameRef, | 178 | trait_name: &ast::NameRef, |
179 | attr: &ast::Attr, | 179 | attr: &ast::Attr, |
180 | ) { | 180 | ) { |
181 | let trait_name = trait_name.text(); | ||
181 | let new_attr_input = input | 182 | let new_attr_input = input |
182 | .syntax() | 183 | .syntax() |
183 | .descendants_with_tokens() | 184 | .descendants_with_tokens() |
184 | .filter(|t| t.kind() == IDENT) | 185 | .filter(|t| t.kind() == IDENT) |
185 | .filter_map(|t| t.into_token().map(|t| t.text().to_string())) | 186 | .filter_map(|t| t.into_token().map(|t| t.text().to_string())) |
186 | .filter(|t| t != trait_name.text()) | 187 | .filter(|t| t != &trait_name) |
187 | .collect::<Vec<_>>(); | 188 | .collect::<Vec<_>>(); |
188 | let has_more_derives = !new_attr_input.is_empty(); | 189 | let has_more_derives = !new_attr_input.is_empty(); |
189 | 190 | ||
diff --git a/crates/ide_assists/src/utils.rs b/crates/ide_assists/src/utils.rs index 728d174db..5f630ec75 100644 --- a/crates/ide_assists/src/utils.rs +++ b/crates/ide_assists/src/utils.rs | |||
@@ -246,7 +246,7 @@ fn invert_special_case(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Opti | |||
246 | let method = mce.name_ref()?; | 246 | let method = mce.name_ref()?; |
247 | let arg_list = mce.arg_list()?; | 247 | let arg_list = mce.arg_list()?; |
248 | 248 | ||
249 | let method = match method.text() { | 249 | let method = match method.text().as_str() { |
250 | "is_some" => "is_none", | 250 | "is_some" => "is_none", |
251 | "is_none" => "is_some", | 251 | "is_none" => "is_some", |
252 | "is_ok" => "is_err", | 252 | "is_ok" => "is_err", |
@@ -443,7 +443,7 @@ fn generate_impl_text_inner(adt: &ast::Adt, trait_text: Option<&str>, code: &str | |||
443 | buf.push_str(trait_text); | 443 | buf.push_str(trait_text); |
444 | buf.push_str(" for "); | 444 | buf.push_str(" for "); |
445 | } | 445 | } |
446 | buf.push_str(adt.name().unwrap().text()); | 446 | buf.push_str(&adt.name().unwrap().text()); |
447 | if let Some(generic_params) = generic_params { | 447 | if let Some(generic_params) = generic_params { |
448 | let lifetime_params = generic_params | 448 | let lifetime_params = generic_params |
449 | .lifetime_params() | 449 | .lifetime_params() |
diff --git a/crates/ide_completion/src/item.rs b/crates/ide_completion/src/item.rs index 9a4b5217a..cc4ac9ea2 100644 --- a/crates/ide_completion/src/item.rs +++ b/crates/ide_completion/src/item.rs | |||
@@ -122,7 +122,7 @@ impl fmt::Debug for CompletionItem { | |||
122 | } | 122 | } |
123 | } | 123 | } |
124 | 124 | ||
125 | #[derive(Debug, Clone, Copy, Ord, PartialOrd, Eq, PartialEq, Default)] | 125 | #[derive(Debug, Clone, Copy, Eq, PartialEq, Default)] |
126 | pub struct CompletionRelevance { | 126 | pub struct CompletionRelevance { |
127 | /// This is set in cases like these: | 127 | /// This is set in cases like these: |
128 | /// | 128 | /// |
@@ -134,31 +134,41 @@ pub struct CompletionRelevance { | |||
134 | /// } | 134 | /// } |
135 | /// ``` | 135 | /// ``` |
136 | pub exact_name_match: bool, | 136 | pub exact_name_match: bool, |
137 | /// See CompletionRelevanceTypeMatch doc comments for cases where this is set. | ||
138 | pub type_match: Option<CompletionRelevanceTypeMatch>, | ||
137 | /// This is set in cases like these: | 139 | /// This is set in cases like these: |
138 | /// | 140 | /// |
139 | /// ``` | 141 | /// ``` |
140 | /// fn f(spam: String) {} | 142 | /// fn foo(a: u32) { |
141 | /// fn main { | 143 | /// let b = 0; |
142 | /// let foo = String::new(); | 144 | /// $0 // `a` and `b` are local |
143 | /// f($0) // type of local matches the type of param | ||
144 | /// } | 145 | /// } |
145 | /// ``` | 146 | /// ``` |
146 | pub exact_type_match: bool, | 147 | pub is_local: bool, |
148 | } | ||
149 | |||
150 | #[derive(Debug, Clone, Copy, Eq, PartialEq)] | ||
151 | pub enum CompletionRelevanceTypeMatch { | ||
147 | /// This is set in cases like these: | 152 | /// This is set in cases like these: |
148 | /// | 153 | /// |
149 | /// ``` | 154 | /// ``` |
150 | /// fn foo(bar: u32) { | 155 | /// enum Option<T> { Some(T), None } |
151 | /// $0 // `bar` is local | 156 | /// fn f(a: Option<u32>) {} |
157 | /// fn main { | ||
158 | /// f(Option::N$0) // type `Option<T>` could unify with `Option<u32>` | ||
152 | /// } | 159 | /// } |
153 | /// ``` | 160 | /// ``` |
161 | CouldUnify, | ||
162 | /// This is set in cases like these: | ||
154 | /// | 163 | /// |
155 | /// ``` | 164 | /// ``` |
156 | /// fn foo() { | 165 | /// fn f(spam: String) {} |
157 | /// let bar = 0; | 166 | /// fn main { |
158 | /// $0 // `bar` is local | 167 | /// let foo = String::new(); |
168 | /// f($0) // type of local matches the type of param | ||
159 | /// } | 169 | /// } |
160 | /// ``` | 170 | /// ``` |
161 | pub is_local: bool, | 171 | Exact, |
162 | } | 172 | } |
163 | 173 | ||
164 | impl CompletionRelevance { | 174 | impl CompletionRelevance { |
@@ -177,9 +187,11 @@ impl CompletionRelevance { | |||
177 | if self.exact_name_match { | 187 | if self.exact_name_match { |
178 | score += 1; | 188 | score += 1; |
179 | } | 189 | } |
180 | if self.exact_type_match { | 190 | score += match self.type_match { |
181 | score += 3; | 191 | Some(CompletionRelevanceTypeMatch::Exact) => 4, |
182 | } | 192 | Some(CompletionRelevanceTypeMatch::CouldUnify) => 3, |
193 | None => 0, | ||
194 | }; | ||
183 | if self.is_local { | 195 | if self.is_local { |
184 | score += 1; | 196 | score += 1; |
185 | } | 197 | } |
@@ -342,7 +354,7 @@ impl CompletionItem { | |||
342 | // match, but with exact type match set because self.ref_match | 354 | // match, but with exact type match set because self.ref_match |
343 | // is only set if there is an exact type match. | 355 | // is only set if there is an exact type match. |
344 | let mut relevance = self.relevance; | 356 | let mut relevance = self.relevance; |
345 | relevance.exact_type_match = true; | 357 | relevance.type_match = Some(CompletionRelevanceTypeMatch::Exact); |
346 | 358 | ||
347 | self.ref_match.map(|mutability| (mutability, relevance)) | 359 | self.ref_match.map(|mutability| (mutability, relevance)) |
348 | } | 360 | } |
@@ -523,7 +535,7 @@ mod tests { | |||
523 | use itertools::Itertools; | 535 | use itertools::Itertools; |
524 | use test_utils::assert_eq_text; | 536 | use test_utils::assert_eq_text; |
525 | 537 | ||
526 | use super::CompletionRelevance; | 538 | use super::{CompletionRelevance, CompletionRelevanceTypeMatch}; |
527 | 539 | ||
528 | /// Check that these are CompletionRelevance are sorted in ascending order | 540 | /// Check that these are CompletionRelevance are sorted in ascending order |
529 | /// by their relevance score. | 541 | /// by their relevance score. |
@@ -576,15 +588,22 @@ mod tests { | |||
576 | is_local: true, | 588 | is_local: true, |
577 | ..CompletionRelevance::default() | 589 | ..CompletionRelevance::default() |
578 | }], | 590 | }], |
579 | vec![CompletionRelevance { exact_type_match: true, ..CompletionRelevance::default() }], | 591 | vec![CompletionRelevance { |
592 | type_match: Some(CompletionRelevanceTypeMatch::CouldUnify), | ||
593 | ..CompletionRelevance::default() | ||
594 | }], | ||
595 | vec![CompletionRelevance { | ||
596 | type_match: Some(CompletionRelevanceTypeMatch::Exact), | ||
597 | ..CompletionRelevance::default() | ||
598 | }], | ||
580 | vec![CompletionRelevance { | 599 | vec![CompletionRelevance { |
581 | exact_name_match: true, | 600 | exact_name_match: true, |
582 | exact_type_match: true, | 601 | type_match: Some(CompletionRelevanceTypeMatch::Exact), |
583 | ..CompletionRelevance::default() | 602 | ..CompletionRelevance::default() |
584 | }], | 603 | }], |
585 | vec![CompletionRelevance { | 604 | vec![CompletionRelevance { |
586 | exact_name_match: true, | 605 | exact_name_match: true, |
587 | exact_type_match: true, | 606 | type_match: Some(CompletionRelevanceTypeMatch::Exact), |
588 | is_local: true, | 607 | is_local: true, |
589 | }], | 608 | }], |
590 | ]; | 609 | ]; |
diff --git a/crates/ide_completion/src/render.rs b/crates/ide_completion/src/render.rs index 9ce49074f..1a762d3dc 100644 --- a/crates/ide_completion/src/render.rs +++ b/crates/ide_completion/src/render.rs | |||
@@ -20,8 +20,8 @@ use ide_db::{ | |||
20 | use syntax::TextRange; | 20 | use syntax::TextRange; |
21 | 21 | ||
22 | use crate::{ | 22 | use crate::{ |
23 | item::ImportEdit, CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, | 23 | item::{CompletionRelevanceTypeMatch, ImportEdit}, |
24 | CompletionRelevance, | 24 | CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, CompletionRelevance, |
25 | }; | 25 | }; |
26 | 26 | ||
27 | use crate::render::{enum_variant::render_variant, function::render_fn, macro_::render_macro}; | 27 | use crate::render::{enum_variant::render_variant, function::render_fn, macro_::render_macro}; |
@@ -143,7 +143,7 @@ impl<'a> Render<'a> { | |||
143 | .set_deprecated(is_deprecated); | 143 | .set_deprecated(is_deprecated); |
144 | 144 | ||
145 | item.set_relevance(CompletionRelevance { | 145 | item.set_relevance(CompletionRelevance { |
146 | exact_type_match: compute_exact_type_match(self.ctx.completion, ty), | 146 | type_match: compute_type_match(self.ctx.completion, ty), |
147 | exact_name_match: compute_exact_name_match(self.ctx.completion, name.to_string()), | 147 | exact_name_match: compute_exact_name_match(self.ctx.completion, name.to_string()), |
148 | ..CompletionRelevance::default() | 148 | ..CompletionRelevance::default() |
149 | }); | 149 | }); |
@@ -245,7 +245,7 @@ impl<'a> Render<'a> { | |||
245 | } | 245 | } |
246 | 246 | ||
247 | item.set_relevance(CompletionRelevance { | 247 | item.set_relevance(CompletionRelevance { |
248 | exact_type_match: compute_exact_type_match(self.ctx.completion, &ty), | 248 | type_match: compute_type_match(self.ctx.completion, &ty), |
249 | exact_name_match: compute_exact_name_match(self.ctx.completion, &local_name), | 249 | exact_name_match: compute_exact_name_match(self.ctx.completion, &local_name), |
250 | is_local: true, | 250 | is_local: true, |
251 | ..CompletionRelevance::default() | 251 | ..CompletionRelevance::default() |
@@ -309,14 +309,24 @@ impl<'a> Render<'a> { | |||
309 | } | 309 | } |
310 | } | 310 | } |
311 | 311 | ||
312 | fn compute_exact_type_match(ctx: &CompletionContext, completion_ty: &hir::Type) -> bool { | 312 | fn compute_type_match( |
313 | match ctx.expected_type.as_ref() { | 313 | ctx: &CompletionContext, |
314 | Some(expected_type) => { | 314 | completion_ty: &hir::Type, |
315 | // We don't ever consider unit type to be an exact type match, since | 315 | ) -> Option<CompletionRelevanceTypeMatch> { |
316 | // nearly always this is not meaningful to the user. | 316 | let expected_type = ctx.expected_type.as_ref()?; |
317 | completion_ty == expected_type && !expected_type.is_unit() | 317 | |
318 | } | 318 | // We don't ever consider unit type to be an exact type match, since |
319 | None => false, | 319 | // nearly always this is not meaningful to the user. |
320 | if expected_type.is_unit() { | ||
321 | return None; | ||
322 | } | ||
323 | |||
324 | if completion_ty == expected_type { | ||
325 | Some(CompletionRelevanceTypeMatch::Exact) | ||
326 | } else if expected_type.could_unify_with(completion_ty) { | ||
327 | Some(CompletionRelevanceTypeMatch::CouldUnify) | ||
328 | } else { | ||
329 | None | ||
320 | } | 330 | } |
321 | } | 331 | } |
322 | 332 | ||
@@ -348,6 +358,7 @@ mod tests { | |||
348 | use itertools::Itertools; | 358 | use itertools::Itertools; |
349 | 359 | ||
350 | use crate::{ | 360 | use crate::{ |
361 | item::CompletionRelevanceTypeMatch, | ||
351 | test_utils::{check_edit, do_completion, get_all_items, TEST_CONFIG}, | 362 | test_utils::{check_edit, do_completion, get_all_items, TEST_CONFIG}, |
352 | CompletionKind, CompletionRelevance, | 363 | CompletionKind, CompletionRelevance, |
353 | }; | 364 | }; |
@@ -360,7 +371,11 @@ mod tests { | |||
360 | fn check_relevance(ra_fixture: &str, expect: Expect) { | 371 | fn check_relevance(ra_fixture: &str, expect: Expect) { |
361 | fn display_relevance(relevance: CompletionRelevance) -> String { | 372 | fn display_relevance(relevance: CompletionRelevance) -> String { |
362 | let relevance_factors = vec![ | 373 | let relevance_factors = vec![ |
363 | (relevance.exact_type_match, "type"), | 374 | (relevance.type_match == Some(CompletionRelevanceTypeMatch::Exact), "type"), |
375 | ( | ||
376 | relevance.type_match == Some(CompletionRelevanceTypeMatch::CouldUnify), | ||
377 | "type_could_unify", | ||
378 | ), | ||
364 | (relevance.exact_name_match, "name"), | 379 | (relevance.exact_name_match, "name"), |
365 | (relevance.is_local, "local"), | 380 | (relevance.is_local, "local"), |
366 | ] | 381 | ] |
@@ -533,7 +548,9 @@ fn main() { let _: m::Spam = S$0 } | |||
533 | detail: "(i32)", | 548 | detail: "(i32)", |
534 | relevance: CompletionRelevance { | 549 | relevance: CompletionRelevance { |
535 | exact_name_match: false, | 550 | exact_name_match: false, |
536 | exact_type_match: true, | 551 | type_match: Some( |
552 | Exact, | ||
553 | ), | ||
537 | is_local: false, | 554 | is_local: false, |
538 | }, | 555 | }, |
539 | trigger_call_info: true, | 556 | trigger_call_info: true, |
@@ -559,7 +576,9 @@ fn main() { let _: m::Spam = S$0 } | |||
559 | detail: "()", | 576 | detail: "()", |
560 | relevance: CompletionRelevance { | 577 | relevance: CompletionRelevance { |
561 | exact_name_match: false, | 578 | exact_name_match: false, |
562 | exact_type_match: true, | 579 | type_match: Some( |
580 | Exact, | ||
581 | ), | ||
563 | is_local: false, | 582 | is_local: false, |
564 | }, | 583 | }, |
565 | }, | 584 | }, |
@@ -1108,7 +1127,7 @@ fn main() { | |||
1108 | detail: "S", | 1127 | detail: "S", |
1109 | relevance: CompletionRelevance { | 1128 | relevance: CompletionRelevance { |
1110 | exact_name_match: true, | 1129 | exact_name_match: true, |
1111 | exact_type_match: false, | 1130 | type_match: None, |
1112 | is_local: true, | 1131 | is_local: true, |
1113 | }, | 1132 | }, |
1114 | ref_match: "&mut ", | 1133 | ref_match: "&mut ", |
@@ -1353,4 +1372,34 @@ fn foo(f: Foo) { let _: &u32 = f.b$0 } | |||
1353 | "#]], | 1372 | "#]], |
1354 | ); | 1373 | ); |
1355 | } | 1374 | } |
1375 | |||
1376 | #[test] | ||
1377 | fn generic_enum() { | ||
1378 | check_relevance( | ||
1379 | r#" | ||
1380 | enum Foo<T> { A(T), B } | ||
1381 | // bar() should not be an exact type match | ||
1382 | // because the generic parameters are different | ||
1383 | fn bar() -> Foo<u8> { Foo::B } | ||
1384 | // FIXME baz() should be an exact type match | ||
1385 | // because the types could unify, but it currently | ||
1386 | // is not. This is due to the T here being | ||
1387 | // TyKind::Placeholder rather than TyKind::Missing. | ||
1388 | fn baz<T>() -> Foo<T> { Foo::B } | ||
1389 | fn foo() { | ||
1390 | let foo: Foo<u32> = Foo::B; | ||
1391 | let _: Foo<u32> = f$0; | ||
1392 | } | ||
1393 | "#, | ||
1394 | expect![[r#" | ||
1395 | ev Foo::A(…) [type_could_unify] | ||
1396 | ev Foo::B [type_could_unify] | ||
1397 | lc foo [type+local] | ||
1398 | en Foo [] | ||
1399 | fn baz() [] | ||
1400 | fn bar() [] | ||
1401 | fn foo() [] | ||
1402 | "#]], | ||
1403 | ); | ||
1404 | } | ||
1356 | } | 1405 | } |
diff --git a/crates/ide_completion/src/render/enum_variant.rs b/crates/ide_completion/src/render/enum_variant.rs index 374247b05..832f5ced1 100644 --- a/crates/ide_completion/src/render/enum_variant.rs +++ b/crates/ide_completion/src/render/enum_variant.rs | |||
@@ -6,7 +6,7 @@ use itertools::Itertools; | |||
6 | 6 | ||
7 | use crate::{ | 7 | use crate::{ |
8 | item::{CompletionItem, CompletionKind, ImportEdit}, | 8 | item::{CompletionItem, CompletionKind, ImportEdit}, |
9 | render::{builder_ext::Params, compute_exact_type_match, compute_ref_match, RenderContext}, | 9 | render::{builder_ext::Params, compute_ref_match, compute_type_match, RenderContext}, |
10 | CompletionRelevance, | 10 | CompletionRelevance, |
11 | }; | 11 | }; |
12 | 12 | ||
@@ -77,7 +77,7 @@ impl<'a> EnumRender<'a> { | |||
77 | 77 | ||
78 | let ty = self.variant.parent_enum(self.ctx.completion.db).ty(self.ctx.completion.db); | 78 | let ty = self.variant.parent_enum(self.ctx.completion.db).ty(self.ctx.completion.db); |
79 | item.set_relevance(CompletionRelevance { | 79 | item.set_relevance(CompletionRelevance { |
80 | exact_type_match: compute_exact_type_match(self.ctx.completion, &ty), | 80 | type_match: compute_type_match(self.ctx.completion, &ty), |
81 | ..CompletionRelevance::default() | 81 | ..CompletionRelevance::default() |
82 | }); | 82 | }); |
83 | 83 | ||
diff --git a/crates/ide_completion/src/render/function.rs b/crates/ide_completion/src/render/function.rs index b1eba20e8..d681e2c91 100644 --- a/crates/ide_completion/src/render/function.rs +++ b/crates/ide_completion/src/render/function.rs | |||
@@ -8,7 +8,7 @@ use syntax::ast::Fn; | |||
8 | use crate::{ | 8 | use crate::{ |
9 | item::{CompletionItem, CompletionItemKind, CompletionKind, CompletionRelevance, ImportEdit}, | 9 | item::{CompletionItem, CompletionItemKind, CompletionKind, CompletionRelevance, ImportEdit}, |
10 | render::{ | 10 | render::{ |
11 | builder_ext::Params, compute_exact_name_match, compute_exact_type_match, compute_ref_match, | 11 | builder_ext::Params, compute_exact_name_match, compute_ref_match, compute_type_match, |
12 | RenderContext, | 12 | RenderContext, |
13 | }, | 13 | }, |
14 | }; | 14 | }; |
@@ -73,7 +73,7 @@ impl<'a> FunctionRender<'a> { | |||
73 | 73 | ||
74 | let ret_type = self.func.ret_type(self.ctx.db()); | 74 | let ret_type = self.func.ret_type(self.ctx.db()); |
75 | item.set_relevance(CompletionRelevance { | 75 | item.set_relevance(CompletionRelevance { |
76 | exact_type_match: compute_exact_type_match(self.ctx.completion, &ret_type), | 76 | type_match: compute_type_match(self.ctx.completion, &ret_type), |
77 | exact_name_match: compute_exact_name_match(self.ctx.completion, self.name.clone()), | 77 | exact_name_match: compute_exact_name_match(self.ctx.completion, self.name.clone()), |
78 | ..CompletionRelevance::default() | 78 | ..CompletionRelevance::default() |
79 | }); | 79 | }); |
diff --git a/crates/ide_db/src/defs.rs b/crates/ide_db/src/defs.rs index 75167ff39..ab23dd7ac 100644 --- a/crates/ide_db/src/defs.rs +++ b/crates/ide_db/src/defs.rs | |||
@@ -343,7 +343,7 @@ impl NameRefClass { | |||
343 | hir::AssocItem::TypeAlias(it) => Some(*it), | 343 | hir::AssocItem::TypeAlias(it) => Some(*it), |
344 | _ => None, | 344 | _ => None, |
345 | }) | 345 | }) |
346 | .find(|alias| &alias.name(sema.db).to_string() == name_ref.text()) | 346 | .find(|alias| &alias.name(sema.db).to_string() == &name_ref.text()) |
347 | { | 347 | { |
348 | return Some(NameRefClass::Definition(Definition::ModuleDef( | 348 | return Some(NameRefClass::Definition(Definition::ModuleDef( |
349 | ModuleDef::TypeAlias(ty), | 349 | ModuleDef::TypeAlias(ty), |
diff --git a/crates/ide_db/src/helpers/import_assets.rs b/crates/ide_db/src/helpers/import_assets.rs index 1881c746f..3deb0d159 100644 --- a/crates/ide_db/src/helpers/import_assets.rs +++ b/crates/ide_db/src/helpers/import_assets.rs | |||
@@ -288,7 +288,7 @@ fn path_applicable_imports( | |||
288 | import_for_item( | 288 | import_for_item( |
289 | sema.db, | 289 | sema.db, |
290 | mod_path, | 290 | mod_path, |
291 | unresolved_first_segment, | 291 | &unresolved_first_segment, |
292 | &unresolved_qualifier, | 292 | &unresolved_qualifier, |
293 | item, | 293 | item, |
294 | ) | 294 | ) |
diff --git a/crates/ide_db/src/helpers/insert_use.rs b/crates/ide_db/src/helpers/insert_use.rs index 20c195f82..e681ced80 100644 --- a/crates/ide_db/src/helpers/insert_use.rs +++ b/crates/ide_db/src/helpers/insert_use.rs | |||
@@ -509,7 +509,7 @@ impl ImportGroup { | |||
509 | PathSegmentKind::SelfKw => ImportGroup::ThisModule, | 509 | PathSegmentKind::SelfKw => ImportGroup::ThisModule, |
510 | PathSegmentKind::SuperKw => ImportGroup::SuperModule, | 510 | PathSegmentKind::SuperKw => ImportGroup::SuperModule, |
511 | PathSegmentKind::CrateKw => ImportGroup::ThisCrate, | 511 | PathSegmentKind::CrateKw => ImportGroup::ThisCrate, |
512 | PathSegmentKind::Name(name) => match name.text() { | 512 | PathSegmentKind::Name(name) => match name.text().as_str() { |
513 | "std" => ImportGroup::Std, | 513 | "std" => ImportGroup::Std, |
514 | "core" => ImportGroup::Std, | 514 | "core" => ImportGroup::Std, |
515 | _ => ImportGroup::ExternCrate, | 515 | _ => ImportGroup::ExternCrate, |
diff --git a/crates/ide_ssr/src/resolving.rs b/crates/ide_ssr/src/resolving.rs index dc7835473..541da4122 100644 --- a/crates/ide_ssr/src/resolving.rs +++ b/crates/ide_ssr/src/resolving.rs | |||
@@ -150,7 +150,7 @@ impl Resolver<'_, '_> { | |||
150 | fn path_contains_placeholder(&self, path: &ast::Path) -> bool { | 150 | fn path_contains_placeholder(&self, path: &ast::Path) -> bool { |
151 | if let Some(segment) = path.segment() { | 151 | if let Some(segment) = path.segment() { |
152 | if let Some(name_ref) = segment.name_ref() { | 152 | if let Some(name_ref) = segment.name_ref() { |
153 | if self.placeholders_by_stand_in.contains_key(name_ref.text()) { | 153 | if self.placeholders_by_stand_in.contains_key(name_ref.text().as_str()) { |
154 | return true; | 154 | return true; |
155 | } | 155 | } |
156 | } | 156 | } |
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs index 25169005f..530c8a5a4 100644 --- a/crates/rust-analyzer/src/to_proto.rs +++ b/crates/rust-analyzer/src/to_proto.rs | |||
@@ -1138,7 +1138,7 @@ mod tests { | |||
1138 | ( | 1138 | ( |
1139 | "&arg", | 1139 | "&arg", |
1140 | Some( | 1140 | Some( |
1141 | "fffffffa", | 1141 | "fffffff9", |
1142 | ), | 1142 | ), |
1143 | ), | 1143 | ), |
1144 | ( | 1144 | ( |
diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs index c08f2c14f..c6a7b99b7 100644 --- a/crates/syntax/src/ast/make.rs +++ b/crates/syntax/src/ast/make.rs | |||
@@ -268,14 +268,14 @@ pub fn arg_list(args: impl IntoIterator<Item = ast::Expr>) -> ast::ArgList { | |||
268 | } | 268 | } |
269 | 269 | ||
270 | pub fn ident_pat(name: ast::Name) -> ast::IdentPat { | 270 | pub fn ident_pat(name: ast::Name) -> ast::IdentPat { |
271 | return from_text(name.text()); | 271 | return from_text(&name.text()); |
272 | 272 | ||
273 | fn from_text(text: &str) -> ast::IdentPat { | 273 | fn from_text(text: &str) -> ast::IdentPat { |
274 | ast_from_text(&format!("fn f({}: ())", text)) | 274 | ast_from_text(&format!("fn f({}: ())", text)) |
275 | } | 275 | } |
276 | } | 276 | } |
277 | pub fn ident_mut_pat(name: ast::Name) -> ast::IdentPat { | 277 | pub fn ident_mut_pat(name: ast::Name) -> ast::IdentPat { |
278 | return from_text(name.text()); | 278 | return from_text(&name.text()); |
279 | 279 | ||
280 | fn from_text(text: &str) -> ast::IdentPat { | 280 | fn from_text(text: &str) -> ast::IdentPat { |
281 | ast_from_text(&format!("fn f(mut {}: ())", text)) | 281 | ast_from_text(&format!("fn f(mut {}: ())", text)) |
diff --git a/crates/syntax/src/ast/node_ext.rs b/crates/syntax/src/ast/node_ext.rs index bdf907a21..2772d7364 100644 --- a/crates/syntax/src/ast/node_ext.rs +++ b/crates/syntax/src/ast/node_ext.rs | |||
@@ -8,23 +8,23 @@ use parser::SyntaxKind; | |||
8 | 8 | ||
9 | use crate::{ | 9 | use crate::{ |
10 | ast::{self, support, AstNode, AstToken, AttrsOwner, NameOwner, SyntaxNode}, | 10 | ast::{self, support, AstNode, AstToken, AttrsOwner, NameOwner, SyntaxNode}, |
11 | SmolStr, SyntaxElement, SyntaxToken, T, | 11 | SmolStr, SyntaxElement, SyntaxToken, TokenText, T, |
12 | }; | 12 | }; |
13 | 13 | ||
14 | impl ast::Lifetime { | 14 | impl ast::Lifetime { |
15 | pub fn text(&self) -> &str { | 15 | pub fn text(&self) -> TokenText { |
16 | text_of_first_token(self.syntax()) | 16 | text_of_first_token(self.syntax()) |
17 | } | 17 | } |
18 | } | 18 | } |
19 | 19 | ||
20 | impl ast::Name { | 20 | impl ast::Name { |
21 | pub fn text(&self) -> &str { | 21 | pub fn text(&self) -> TokenText { |
22 | text_of_first_token(self.syntax()) | 22 | text_of_first_token(self.syntax()) |
23 | } | 23 | } |
24 | } | 24 | } |
25 | 25 | ||
26 | impl ast::NameRef { | 26 | impl ast::NameRef { |
27 | pub fn text(&self) -> &str { | 27 | pub fn text(&self) -> TokenText { |
28 | text_of_first_token(self.syntax()) | 28 | text_of_first_token(self.syntax()) |
29 | } | 29 | } |
30 | 30 | ||
@@ -33,10 +33,11 @@ impl ast::NameRef { | |||
33 | } | 33 | } |
34 | } | 34 | } |
35 | 35 | ||
36 | fn text_of_first_token(node: &SyntaxNode) -> &str { | 36 | fn text_of_first_token(node: &SyntaxNode) -> TokenText { |
37 | let t = | 37 | let first_token = |
38 | node.green().children().next().and_then(|it| it.into_token()).unwrap().text().to_string(); | 38 | node.green().children().next().and_then(|it| it.into_token()).unwrap().to_owned(); |
39 | Box::leak(Box::new(t)) | 39 | |
40 | TokenText(first_token) | ||
40 | } | 41 | } |
41 | 42 | ||
42 | pub enum Macro { | 43 | pub enum Macro { |
@@ -378,7 +379,7 @@ impl fmt::Display for NameOrNameRef { | |||
378 | } | 379 | } |
379 | 380 | ||
380 | impl NameOrNameRef { | 381 | impl NameOrNameRef { |
381 | pub fn text(&self) -> &str { | 382 | pub fn text(&self) -> TokenText { |
382 | match self { | 383 | match self { |
383 | NameOrNameRef::Name(name) => name.text(), | 384 | NameOrNameRef::Name(name) => name.text(), |
384 | NameOrNameRef::NameRef(name_ref) => name_ref.text(), | 385 | NameOrNameRef::NameRef(name_ref) => name_ref.text(), |
diff --git a/crates/syntax/src/lib.rs b/crates/syntax/src/lib.rs index 2a5c61171..90de6bef6 100644 --- a/crates/syntax/src/lib.rs +++ b/crates/syntax/src/lib.rs | |||
@@ -29,6 +29,7 @@ mod syntax_error; | |||
29 | mod parsing; | 29 | mod parsing; |
30 | mod validation; | 30 | mod validation; |
31 | mod ptr; | 31 | mod ptr; |
32 | mod token_text; | ||
32 | #[cfg(test)] | 33 | #[cfg(test)] |
33 | mod tests; | 34 | mod tests; |
34 | 35 | ||
@@ -55,6 +56,7 @@ pub use crate::{ | |||
55 | SyntaxElement, SyntaxElementChildren, SyntaxNode, SyntaxNodeChildren, SyntaxToken, | 56 | SyntaxElement, SyntaxElementChildren, SyntaxNode, SyntaxNodeChildren, SyntaxToken, |
56 | SyntaxTreeBuilder, | 57 | SyntaxTreeBuilder, |
57 | }, | 58 | }, |
59 | token_text::TokenText, | ||
58 | }; | 60 | }; |
59 | pub use parser::{SyntaxKind, T}; | 61 | pub use parser::{SyntaxKind, T}; |
60 | pub use rowan::{ | 62 | pub use rowan::{ |
diff --git a/crates/syntax/src/token_text.rs b/crates/syntax/src/token_text.rs new file mode 100644 index 000000000..d2ed0a12a --- /dev/null +++ b/crates/syntax/src/token_text.rs | |||
@@ -0,0 +1,77 @@ | |||
1 | //! Yet another version of owned string, backed by a syntax tree token. | ||
2 | |||
3 | use std::{cmp::Ordering, fmt, ops}; | ||
4 | |||
5 | pub struct TokenText(pub(crate) rowan::GreenToken); | ||
6 | |||
7 | impl TokenText { | ||
8 | pub fn as_str(&self) -> &str { | ||
9 | self.0.text() | ||
10 | } | ||
11 | } | ||
12 | |||
13 | impl ops::Deref for TokenText { | ||
14 | type Target = str; | ||
15 | |||
16 | fn deref(&self) -> &str { | ||
17 | self.as_str() | ||
18 | } | ||
19 | } | ||
20 | impl AsRef<str> for TokenText { | ||
21 | fn as_ref(&self) -> &str { | ||
22 | self.as_str() | ||
23 | } | ||
24 | } | ||
25 | |||
26 | impl From<TokenText> for String { | ||
27 | fn from(token_text: TokenText) -> Self { | ||
28 | token_text.as_str().into() | ||
29 | } | ||
30 | } | ||
31 | |||
32 | impl PartialEq<&'_ str> for TokenText { | ||
33 | fn eq(&self, other: &&str) -> bool { | ||
34 | self.as_str() == *other | ||
35 | } | ||
36 | } | ||
37 | impl PartialEq<TokenText> for &'_ str { | ||
38 | fn eq(&self, other: &TokenText) -> bool { | ||
39 | other == self | ||
40 | } | ||
41 | } | ||
42 | impl PartialEq<String> for TokenText { | ||
43 | fn eq(&self, other: &String) -> bool { | ||
44 | self.as_str() == other.as_str() | ||
45 | } | ||
46 | } | ||
47 | impl PartialEq<TokenText> for String { | ||
48 | fn eq(&self, other: &TokenText) -> bool { | ||
49 | other == self | ||
50 | } | ||
51 | } | ||
52 | impl PartialEq for TokenText { | ||
53 | fn eq(&self, other: &TokenText) -> bool { | ||
54 | self.as_str() == other.as_str() | ||
55 | } | ||
56 | } | ||
57 | impl Eq for TokenText {} | ||
58 | impl Ord for TokenText { | ||
59 | fn cmp(&self, other: &Self) -> Ordering { | ||
60 | self.as_str().cmp(other.as_str()) | ||
61 | } | ||
62 | } | ||
63 | impl PartialOrd for TokenText { | ||
64 | fn partial_cmp(&self, other: &Self) -> Option<Ordering> { | ||
65 | Some(self.cmp(other)) | ||
66 | } | ||
67 | } | ||
68 | impl fmt::Display for TokenText { | ||
69 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
70 | fmt::Display::fmt(self.as_str(), f) | ||
71 | } | ||
72 | } | ||
73 | impl fmt::Debug for TokenText { | ||
74 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
75 | fmt::Debug::fmt(self.as_str(), f) | ||
76 | } | ||
77 | } | ||