mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-27 05:23:24 +00:00
Merge #9810
9810: Add reference here r=matklad a=ivan770 Superseds #6853 Co-authored-by: ivan770 <ivan@ivan770.me>
This commit is contained in:
commit
b07c83f8b0
5 changed files with 231 additions and 5 deletions
|
@ -5,7 +5,7 @@
|
||||||
//! be expressed in terms of hir types themselves.
|
//! be expressed in terms of hir types themselves.
|
||||||
use cfg::{CfgExpr, CfgOptions};
|
use cfg::{CfgExpr, CfgOptions};
|
||||||
use either::Either;
|
use either::Either;
|
||||||
use hir_def::path::ModPath;
|
use hir_def::{path::ModPath, type_ref::Mutability};
|
||||||
use hir_expand::{name::Name, HirFileId, InFile};
|
use hir_expand::{name::Name, HirFileId, InFile};
|
||||||
use syntax::{ast, AstPtr, SyntaxNodePtr, TextRange};
|
use syntax::{ast, AstPtr, SyntaxNodePtr, TextRange};
|
||||||
|
|
||||||
|
@ -28,6 +28,7 @@ macro_rules! diagnostics {
|
||||||
}
|
}
|
||||||
|
|
||||||
diagnostics![
|
diagnostics![
|
||||||
|
AddReferenceHere,
|
||||||
BreakOutsideOfLoop,
|
BreakOutsideOfLoop,
|
||||||
InactiveCode,
|
InactiveCode,
|
||||||
IncorrectCase,
|
IncorrectCase,
|
||||||
|
@ -154,4 +155,10 @@ pub struct MissingMatchArms {
|
||||||
pub arms: AstPtr<ast::MatchArmList>,
|
pub arms: AstPtr<ast::MatchArmList>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct AddReferenceHere {
|
||||||
|
pub expr: InFile<AstPtr<ast::Expr>>,
|
||||||
|
pub mutability: Mutability,
|
||||||
|
}
|
||||||
|
|
||||||
pub use hir_ty::diagnostics::IncorrectCase;
|
pub use hir_ty::diagnostics::IncorrectCase;
|
||||||
|
|
|
@ -82,8 +82,8 @@ use crate::db::{DefDatabase, HirDatabase};
|
||||||
pub use crate::{
|
pub use crate::{
|
||||||
attrs::{HasAttrs, Namespace},
|
attrs::{HasAttrs, Namespace},
|
||||||
diagnostics::{
|
diagnostics::{
|
||||||
AnyDiagnostic, BreakOutsideOfLoop, InactiveCode, IncorrectCase, MacroError,
|
AddReferenceHere, AnyDiagnostic, BreakOutsideOfLoop, InactiveCode, IncorrectCase,
|
||||||
MismatchedArgCount, MissingFields, MissingMatchArms, MissingOkOrSomeInTailExpr,
|
MacroError, MismatchedArgCount, MissingFields, MissingMatchArms, MissingOkOrSomeInTailExpr,
|
||||||
MissingUnsafe, NoSuchField, RemoveThisSemicolon, ReplaceFilterMapNextWithFindMap,
|
MissingUnsafe, NoSuchField, RemoveThisSemicolon, ReplaceFilterMapNextWithFindMap,
|
||||||
UnimplementedBuiltinMacro, UnresolvedExternCrate, UnresolvedImport, UnresolvedMacroCall,
|
UnimplementedBuiltinMacro, UnresolvedExternCrate, UnresolvedImport, UnresolvedMacroCall,
|
||||||
UnresolvedModule, UnresolvedProcMacro,
|
UnresolvedModule, UnresolvedProcMacro,
|
||||||
|
@ -1251,6 +1251,12 @@ impl Function {
|
||||||
Err(SyntheticSyntax) => (),
|
Err(SyntheticSyntax) => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
BodyValidationDiagnostic::AddReferenceHere { arg_expr, mutability } => {
|
||||||
|
match source_map.expr_syntax(arg_expr) {
|
||||||
|
Ok(expr) => acc.push(AddReferenceHere { expr, mutability }.into()),
|
||||||
|
Err(SyntheticSyntax) => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,8 @@
|
||||||
use std::{cell::RefCell, sync::Arc};
|
use std::{cell::RefCell, sync::Arc};
|
||||||
|
|
||||||
use hir_def::{
|
use hir_def::{
|
||||||
expr::Statement, path::path, resolver::HasResolver, AssocItemId, DefWithBodyId, HasModule,
|
expr::Statement, path::path, resolver::HasResolver, type_ref::Mutability, AssocItemId,
|
||||||
|
DefWithBodyId, HasModule,
|
||||||
};
|
};
|
||||||
use hir_expand::name;
|
use hir_expand::name;
|
||||||
use itertools::Either;
|
use itertools::Either;
|
||||||
|
@ -17,7 +18,7 @@ use crate::{
|
||||||
self,
|
self,
|
||||||
usefulness::{compute_match_usefulness, expand_pattern, MatchCheckCtx, PatternArena},
|
usefulness::{compute_match_usefulness, expand_pattern, MatchCheckCtx, PatternArena},
|
||||||
},
|
},
|
||||||
AdtId, InferenceResult, Interner, TyExt, TyKind,
|
AdtId, InferenceResult, Interner, Ty, TyExt, TyKind,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub(crate) use hir_def::{
|
pub(crate) use hir_def::{
|
||||||
|
@ -50,6 +51,10 @@ pub enum BodyValidationDiagnostic {
|
||||||
MissingMatchArms {
|
MissingMatchArms {
|
||||||
match_expr: ExprId,
|
match_expr: ExprId,
|
||||||
},
|
},
|
||||||
|
AddReferenceHere {
|
||||||
|
arg_expr: ExprId,
|
||||||
|
mutability: Mutability,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BodyValidationDiagnostic {
|
impl BodyValidationDiagnostic {
|
||||||
|
@ -118,6 +123,22 @@ impl ExprValidator {
|
||||||
self.validate_missing_tail_expr(body.body_expr, *id);
|
self.validate_missing_tail_expr(body.body_expr, *id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let infer = &self.infer;
|
||||||
|
let diagnostics = &mut self.diagnostics;
|
||||||
|
|
||||||
|
infer
|
||||||
|
.expr_type_mismatches()
|
||||||
|
.filter_map(|(expr, mismatch)| {
|
||||||
|
let (expr_without_ref, mutability) =
|
||||||
|
check_missing_refs(infer, expr, &mismatch.expected)?;
|
||||||
|
|
||||||
|
Some((expr_without_ref, mutability))
|
||||||
|
})
|
||||||
|
.for_each(|(arg_expr, mutability)| {
|
||||||
|
diagnostics
|
||||||
|
.push(BodyValidationDiagnostic::AddReferenceHere { arg_expr, mutability });
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_for_filter_map_next(&mut self, db: &dyn HirDatabase) {
|
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
|
||||||
walk(pat, body, infer, &mut has_type_mismatches);
|
walk(pat, body, infer, &mut has_type_mismatches);
|
||||||
!has_type_mismatches
|
!has_type_mismatches
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn check_missing_refs(
|
||||||
|
infer: &InferenceResult,
|
||||||
|
arg: ExprId,
|
||||||
|
param: &Ty,
|
||||||
|
) -> Option<(ExprId, Mutability)> {
|
||||||
|
let arg_ty = infer.type_of_expr.get(arg)?;
|
||||||
|
|
||||||
|
let reference_one = arg_ty.as_reference();
|
||||||
|
let reference_two = param.as_reference();
|
||||||
|
|
||||||
|
match (reference_one, reference_two) {
|
||||||
|
(None, Some((referenced_ty, _, mutability))) if referenced_ty == arg_ty => {
|
||||||
|
Some((arg, Mutability::from_mutable(matches!(mutability, chalk_ir::Mutability::Mut))))
|
||||||
|
}
|
||||||
|
(None, Some((referenced_ty, _, mutability))) => match referenced_ty.kind(&Interner) {
|
||||||
|
TyKind::Slice(subst) if matches!(arg_ty.kind(&Interner), TyKind::Array(arr_subst, _) if arr_subst == subst) => {
|
||||||
|
Some((
|
||||||
|
arg,
|
||||||
|
Mutability::from_mutable(matches!(mutability, chalk_ir::Mutability::Mut)),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
},
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
163
crates/ide_diagnostics/src/handlers/add_reference_here.rs
Normal file
163
crates/ide_diagnostics/src/handlers/add_reference_here.rs
Normal file
|
@ -0,0 +1,163 @@
|
||||||
|
use hir::db::AstDatabase;
|
||||||
|
use ide_db::source_change::SourceChange;
|
||||||
|
use syntax::AstNode;
|
||||||
|
use text_edit::TextEdit;
|
||||||
|
|
||||||
|
use crate::{fix, Assist, Diagnostic, DiagnosticsContext};
|
||||||
|
|
||||||
|
// Diagnostic: add-reference-here
|
||||||
|
//
|
||||||
|
// This diagnostic is triggered when there's a missing referencing of expression.
|
||||||
|
pub(crate) fn add_reference_here(
|
||||||
|
ctx: &DiagnosticsContext<'_>,
|
||||||
|
d: &hir::AddReferenceHere,
|
||||||
|
) -> Diagnostic {
|
||||||
|
Diagnostic::new(
|
||||||
|
"add-reference-here",
|
||||||
|
"add reference here",
|
||||||
|
ctx.sema.diagnostics_display_range(d.expr.clone().map(|it| it.into())).range,
|
||||||
|
)
|
||||||
|
.with_fixes(fixes(ctx, d))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::AddReferenceHere) -> Option<Vec<Assist>> {
|
||||||
|
let root = ctx.sema.db.parse_or_expand(d.expr.file_id)?;
|
||||||
|
let arg_expr = d.expr.value.to_node(&root);
|
||||||
|
|
||||||
|
let arg_with_ref = format!("&{}{}", d.mutability.as_keyword_for_ref(), arg_expr.syntax());
|
||||||
|
|
||||||
|
let arg_range = arg_expr.syntax().text_range();
|
||||||
|
let edit = TextEdit::replace(arg_range, arg_with_ref);
|
||||||
|
let source_change =
|
||||||
|
SourceChange::from_text_edit(d.expr.file_id.original_file(ctx.sema.db), edit);
|
||||||
|
|
||||||
|
Some(vec![fix("add_reference_here", "Add reference here", source_change, arg_range)])
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::tests::{check_diagnostics, check_fix};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn missing_reference() {
|
||||||
|
check_diagnostics(
|
||||||
|
r#"
|
||||||
|
fn main() {
|
||||||
|
test(123);
|
||||||
|
//^^^ 💡 error: add reference here
|
||||||
|
}
|
||||||
|
fn test(arg: &i32) {}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_add_reference_to_int() {
|
||||||
|
check_fix(
|
||||||
|
r#"
|
||||||
|
fn main() {
|
||||||
|
test(123$0);
|
||||||
|
}
|
||||||
|
fn test(arg: &i32) {}
|
||||||
|
"#,
|
||||||
|
r#"
|
||||||
|
fn main() {
|
||||||
|
test(&123);
|
||||||
|
}
|
||||||
|
fn test(arg: &i32) {}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_add_mutable_reference_to_int() {
|
||||||
|
check_fix(
|
||||||
|
r#"
|
||||||
|
fn main() {
|
||||||
|
test($0123);
|
||||||
|
}
|
||||||
|
fn test(arg: &mut i32) {}
|
||||||
|
"#,
|
||||||
|
r#"
|
||||||
|
fn main() {
|
||||||
|
test(&mut 123);
|
||||||
|
}
|
||||||
|
fn test(arg: &mut i32) {}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_add_reference_to_array() {
|
||||||
|
check_fix(
|
||||||
|
r#"
|
||||||
|
fn main() {
|
||||||
|
test($0[1, 2, 3]);
|
||||||
|
}
|
||||||
|
fn test(arg: &[i32]) {}
|
||||||
|
"#,
|
||||||
|
r#"
|
||||||
|
fn main() {
|
||||||
|
test(&[1, 2, 3]);
|
||||||
|
}
|
||||||
|
fn test(arg: &[i32]) {}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_add_reference_to_method_call() {
|
||||||
|
check_fix(
|
||||||
|
r#"
|
||||||
|
fn main() {
|
||||||
|
Test.call_by_ref($0123);
|
||||||
|
}
|
||||||
|
struct Test;
|
||||||
|
impl Test {
|
||||||
|
fn call_by_ref(&self, arg: &i32) {}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
r#"
|
||||||
|
fn main() {
|
||||||
|
Test.call_by_ref(&123);
|
||||||
|
}
|
||||||
|
struct Test;
|
||||||
|
impl Test {
|
||||||
|
fn call_by_ref(&self, arg: &i32) {}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_add_reference_to_let_stmt() {
|
||||||
|
check_fix(
|
||||||
|
r#"
|
||||||
|
fn main() {
|
||||||
|
let test: &i32 = $0123;
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
r#"
|
||||||
|
fn main() {
|
||||||
|
let test: &i32 = &123;
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_add_mutable_reference_to_let_stmt() {
|
||||||
|
check_fix(
|
||||||
|
r#"
|
||||||
|
fn main() {
|
||||||
|
let test: &mut i32 = $0123;
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
r#"
|
||||||
|
fn main() {
|
||||||
|
let test: &mut i32 = &mut 123;
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -24,6 +24,7 @@
|
||||||
//! don't yet have a great pattern for how to do them properly.
|
//! don't yet have a great pattern for how to do them properly.
|
||||||
|
|
||||||
mod handlers {
|
mod handlers {
|
||||||
|
pub(crate) mod add_reference_here;
|
||||||
pub(crate) mod break_outside_of_loop;
|
pub(crate) mod break_outside_of_loop;
|
||||||
pub(crate) mod inactive_code;
|
pub(crate) mod inactive_code;
|
||||||
pub(crate) mod incorrect_case;
|
pub(crate) mod incorrect_case;
|
||||||
|
@ -176,6 +177,7 @@ pub fn diagnostics(
|
||||||
for diag in diags {
|
for diag in diags {
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
let d = match diag {
|
let d = match diag {
|
||||||
|
AnyDiagnostic::AddReferenceHere(d) => handlers::add_reference_here::add_reference_here(&ctx, &d),
|
||||||
AnyDiagnostic::BreakOutsideOfLoop(d) => handlers::break_outside_of_loop::break_outside_of_loop(&ctx, &d),
|
AnyDiagnostic::BreakOutsideOfLoop(d) => handlers::break_outside_of_loop::break_outside_of_loop(&ctx, &d),
|
||||||
AnyDiagnostic::IncorrectCase(d) => handlers::incorrect_case::incorrect_case(&ctx, &d),
|
AnyDiagnostic::IncorrectCase(d) => handlers::incorrect_case::incorrect_case(&ctx, &d),
|
||||||
AnyDiagnostic::MacroError(d) => handlers::macro_error::macro_error(&ctx, &d),
|
AnyDiagnostic::MacroError(d) => handlers::macro_error::macro_error(&ctx, &d),
|
||||||
|
|
Loading…
Reference in a new issue