Skip to content

Commit b07c83f

Browse files
bors[bot]ivan770
andauthored
Merge #9810
9810: Add reference here r=matklad a=ivan770 Superseds #6853 Co-authored-by: ivan770 <ivan@ivan770.me>
2 parents 6ea07fc + be3e70c commit b07c83f

File tree

5 files changed

+231
-5
lines changed

5 files changed

+231
-5
lines changed

crates/hir/src/diagnostics.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
//! be expressed in terms of hir types themselves.
66
use cfg::{CfgExpr, CfgOptions};
77
use either::Either;
8-
use hir_def::path::ModPath;
8+
use hir_def::{path::ModPath, type_ref::Mutability};
99
use hir_expand::{name::Name, HirFileId, InFile};
1010
use syntax::{ast, AstPtr, SyntaxNodePtr, TextRange};
1111

@@ -28,6 +28,7 @@ macro_rules! diagnostics {
2828
}
2929

3030
diagnostics![
31+
AddReferenceHere,
3132
BreakOutsideOfLoop,
3233
InactiveCode,
3334
IncorrectCase,
@@ -154,4 +155,10 @@ pub struct MissingMatchArms {
154155
pub arms: AstPtr<ast::MatchArmList>,
155156
}
156157

158+
#[derive(Debug)]
159+
pub struct AddReferenceHere {
160+
pub expr: InFile<AstPtr<ast::Expr>>,
161+
pub mutability: Mutability,
162+
}
163+
157164
pub use hir_ty::diagnostics::IncorrectCase;

crates/hir/src/lib.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,8 +82,8 @@ use crate::db::{DefDatabase, HirDatabase};
8282
pub use crate::{
8383
attrs::{HasAttrs, Namespace},
8484
diagnostics::{
85-
AnyDiagnostic, BreakOutsideOfLoop, InactiveCode, IncorrectCase, MacroError,
86-
MismatchedArgCount, MissingFields, MissingMatchArms, MissingOkOrSomeInTailExpr,
85+
AddReferenceHere, AnyDiagnostic, BreakOutsideOfLoop, InactiveCode, IncorrectCase,
86+
MacroError, MismatchedArgCount, MissingFields, MissingMatchArms, MissingOkOrSomeInTailExpr,
8787
MissingUnsafe, NoSuchField, RemoveThisSemicolon, ReplaceFilterMapNextWithFindMap,
8888
UnimplementedBuiltinMacro, UnresolvedExternCrate, UnresolvedImport, UnresolvedMacroCall,
8989
UnresolvedModule, UnresolvedProcMacro,
@@ -1251,6 +1251,12 @@ impl Function {
12511251
Err(SyntheticSyntax) => (),
12521252
}
12531253
}
1254+
BodyValidationDiagnostic::AddReferenceHere { arg_expr, mutability } => {
1255+
match source_map.expr_syntax(arg_expr) {
1256+
Ok(expr) => acc.push(AddReferenceHere { expr, mutability }.into()),
1257+
Err(SyntheticSyntax) => (),
1258+
}
1259+
}
12541260
}
12551261
}
12561262

crates/hir_ty/src/diagnostics/expr.rs

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55
use std::{cell::RefCell, sync::Arc};
66

77
use hir_def::{
8-
expr::Statement, path::path, resolver::HasResolver, AssocItemId, DefWithBodyId, HasModule,
8+
expr::Statement, path::path, resolver::HasResolver, type_ref::Mutability, AssocItemId,
9+
DefWithBodyId, HasModule,
910
};
1011
use hir_expand::name;
1112
use itertools::Either;
@@ -17,7 +18,7 @@ use crate::{
1718
self,
1819
usefulness::{compute_match_usefulness, expand_pattern, MatchCheckCtx, PatternArena},
1920
},
20-
AdtId, InferenceResult, Interner, TyExt, TyKind,
21+
AdtId, InferenceResult, Interner, Ty, TyExt, TyKind,
2122
};
2223

2324
pub(crate) use hir_def::{
@@ -50,6 +51,10 @@ pub enum BodyValidationDiagnostic {
5051
MissingMatchArms {
5152
match_expr: ExprId,
5253
},
54+
AddReferenceHere {
55+
arg_expr: ExprId,
56+
mutability: Mutability,
57+
},
5358
}
5459

5560
impl BodyValidationDiagnostic {
@@ -118,6 +123,22 @@ impl ExprValidator {
118123
self.validate_missing_tail_expr(body.body_expr, *id);
119124
}
120125
}
126+
127+
let infer = &self.infer;
128+
let diagnostics = &mut self.diagnostics;
129+
130+
infer
131+
.expr_type_mismatches()
132+
.filter_map(|(expr, mismatch)| {
133+
let (expr_without_ref, mutability) =
134+
check_missing_refs(infer, expr, &mismatch.expected)?;
135+
136+
Some((expr_without_ref, mutability))
137+
})
138+
.for_each(|(arg_expr, mutability)| {
139+
diagnostics
140+
.push(BodyValidationDiagnostic::AddReferenceHere { arg_expr, mutability });
141+
});
121142
}
122143

123144
fn check_for_filter_map_next(&mut self, db: &dyn HirDatabase) {
@@ -491,3 +512,30 @@ fn types_of_subpatterns_do_match(pat: PatId, body: &Body, infer: &InferenceResul
491512
walk(pat, body, infer, &mut has_type_mismatches);
492513
!has_type_mismatches
493514
}
515+
516+
fn check_missing_refs(
517+
infer: &InferenceResult,
518+
arg: ExprId,
519+
param: &Ty,
520+
) -> Option<(ExprId, Mutability)> {
521+
let arg_ty = infer.type_of_expr.get(arg)?;
522+
523+
let reference_one = arg_ty.as_reference();
524+
let reference_two = param.as_reference();
525+
526+
match (reference_one, reference_two) {
527+
(None, Some((referenced_ty, _, mutability))) if referenced_ty == arg_ty => {
528+
Some((arg, Mutability::from_mutable(matches!(mutability, chalk_ir::Mutability::Mut))))
529+
}
530+
(None, Some((referenced_ty, _, mutability))) => match referenced_ty.kind(&Interner) {
531+
TyKind::Slice(subst) if matches!(arg_ty.kind(&Interner), TyKind::Array(arr_subst, _) if arr_subst == subst) => {
532+
Some((
533+
arg,
534+
Mutability::from_mutable(matches!(mutability, chalk_ir::Mutability::Mut)),
535+
))
536+
}
537+
_ => None,
538+
},
539+
_ => None,
540+
}
541+
}
Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
use hir::db::AstDatabase;
2+
use ide_db::source_change::SourceChange;
3+
use syntax::AstNode;
4+
use text_edit::TextEdit;
5+
6+
use crate::{fix, Assist, Diagnostic, DiagnosticsContext};
7+
8+
// Diagnostic: add-reference-here
9+
//
10+
// This diagnostic is triggered when there's a missing referencing of expression.
11+
pub(crate) fn add_reference_here(
12+
ctx: &DiagnosticsContext<'_>,
13+
d: &hir::AddReferenceHere,
14+
) -> Diagnostic {
15+
Diagnostic::new(
16+
"add-reference-here",
17+
"add reference here",
18+
ctx.sema.diagnostics_display_range(d.expr.clone().map(|it| it.into())).range,
19+
)
20+
.with_fixes(fixes(ctx, d))
21+
}
22+
23+
fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::AddReferenceHere) -> Option<Vec<Assist>> {
24+
let root = ctx.sema.db.parse_or_expand(d.expr.file_id)?;
25+
let arg_expr = d.expr.value.to_node(&root);
26+
27+
let arg_with_ref = format!("&{}{}", d.mutability.as_keyword_for_ref(), arg_expr.syntax());
28+
29+
let arg_range = arg_expr.syntax().text_range();
30+
let edit = TextEdit::replace(arg_range, arg_with_ref);
31+
let source_change =
32+
SourceChange::from_text_edit(d.expr.file_id.original_file(ctx.sema.db), edit);
33+
34+
Some(vec![fix("add_reference_here", "Add reference here", source_change, arg_range)])
35+
}
36+
37+
#[cfg(test)]
38+
mod tests {
39+
use crate::tests::{check_diagnostics, check_fix};
40+
41+
#[test]
42+
fn missing_reference() {
43+
check_diagnostics(
44+
r#"
45+
fn main() {
46+
test(123);
47+
//^^^ 💡 error: add reference here
48+
}
49+
fn test(arg: &i32) {}
50+
"#,
51+
);
52+
}
53+
54+
#[test]
55+
fn test_add_reference_to_int() {
56+
check_fix(
57+
r#"
58+
fn main() {
59+
test(123$0);
60+
}
61+
fn test(arg: &i32) {}
62+
"#,
63+
r#"
64+
fn main() {
65+
test(&123);
66+
}
67+
fn test(arg: &i32) {}
68+
"#,
69+
);
70+
}
71+
72+
#[test]
73+
fn test_add_mutable_reference_to_int() {
74+
check_fix(
75+
r#"
76+
fn main() {
77+
test($0123);
78+
}
79+
fn test(arg: &mut i32) {}
80+
"#,
81+
r#"
82+
fn main() {
83+
test(&mut 123);
84+
}
85+
fn test(arg: &mut i32) {}
86+
"#,
87+
);
88+
}
89+
90+
#[test]
91+
fn test_add_reference_to_array() {
92+
check_fix(
93+
r#"
94+
fn main() {
95+
test($0[1, 2, 3]);
96+
}
97+
fn test(arg: &[i32]) {}
98+
"#,
99+
r#"
100+
fn main() {
101+
test(&[1, 2, 3]);
102+
}
103+
fn test(arg: &[i32]) {}
104+
"#,
105+
);
106+
}
107+
108+
#[test]
109+
fn test_add_reference_to_method_call() {
110+
check_fix(
111+
r#"
112+
fn main() {
113+
Test.call_by_ref($0123);
114+
}
115+
struct Test;
116+
impl Test {
117+
fn call_by_ref(&self, arg: &i32) {}
118+
}
119+
"#,
120+
r#"
121+
fn main() {
122+
Test.call_by_ref(&123);
123+
}
124+
struct Test;
125+
impl Test {
126+
fn call_by_ref(&self, arg: &i32) {}
127+
}
128+
"#,
129+
);
130+
}
131+
132+
#[test]
133+
fn test_add_reference_to_let_stmt() {
134+
check_fix(
135+
r#"
136+
fn main() {
137+
let test: &i32 = $0123;
138+
}
139+
"#,
140+
r#"
141+
fn main() {
142+
let test: &i32 = &123;
143+
}
144+
"#,
145+
);
146+
}
147+
148+
#[test]
149+
fn test_add_mutable_reference_to_let_stmt() {
150+
check_fix(
151+
r#"
152+
fn main() {
153+
let test: &mut i32 = $0123;
154+
}
155+
"#,
156+
r#"
157+
fn main() {
158+
let test: &mut i32 = &mut 123;
159+
}
160+
"#,
161+
);
162+
}
163+
}

crates/ide_diagnostics/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
//! don't yet have a great pattern for how to do them properly.
2525
2626
mod handlers {
27+
pub(crate) mod add_reference_here;
2728
pub(crate) mod break_outside_of_loop;
2829
pub(crate) mod inactive_code;
2930
pub(crate) mod incorrect_case;
@@ -176,6 +177,7 @@ pub fn diagnostics(
176177
for diag in diags {
177178
#[rustfmt::skip]
178179
let d = match diag {
180+
AnyDiagnostic::AddReferenceHere(d) => handlers::add_reference_here::add_reference_here(&ctx, &d),
179181
AnyDiagnostic::BreakOutsideOfLoop(d) => handlers::break_outside_of_loop::break_outside_of_loop(&ctx, &d),
180182
AnyDiagnostic::IncorrectCase(d) => handlers::incorrect_case::incorrect_case(&ctx, &d),
181183
AnyDiagnostic::MacroError(d) => handlers::macro_error::macro_error(&ctx, &d),

0 commit comments

Comments
 (0)