Skip to content

Commit da15d92

Browse files
committed
Auto merge of rust-lang#13870 - Veykril:private-field-diag, r=Veykril
Diagnose private field accesses
2 parents f5e7bf2 + e3d144d commit da15d92

File tree

6 files changed

+86
-11
lines changed

6 files changed

+86
-11
lines changed

crates/hir-ty/src/infer.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,7 @@ pub(crate) type InferResult<T> = Result<InferOk<T>, TypeError>;
208208
#[derive(Debug, PartialEq, Eq, Clone)]
209209
pub enum InferenceDiagnostic {
210210
NoSuchField { expr: ExprId },
211+
PrivateField { expr: ExprId, field: FieldId },
211212
BreakOutsideOfLoop { expr: ExprId, is_break: bool },
212213
MismatchedArgCount { call_expr: ExprId, expected: usize, found: usize },
213214
}

crates/hir-ty/src/infer/expr.rs

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
//! Type inference for expressions.
22
33
use std::{
4-
collections::hash_map::Entry,
54
iter::{repeat, repeat_with},
65
mem,
76
};
@@ -521,6 +520,7 @@ impl<'a> InferenceContext<'a> {
521520
let receiver_ty = self.infer_expr_inner(*expr, &Expectation::none());
522521

523522
let mut autoderef = Autoderef::new(&mut self.table, receiver_ty);
523+
let mut private_field = None;
524524
let ty = autoderef.by_ref().find_map(|(derefed_ty, _)| {
525525
let (field_id, parameters) = match derefed_ty.kind(Interner) {
526526
TyKind::Tuple(_, substs) => {
@@ -547,13 +547,8 @@ impl<'a> InferenceContext<'a> {
547547
let is_visible = self.db.field_visibilities(field_id.parent)[field_id.local_id]
548548
.is_visible_from(self.db.upcast(), self.resolver.module());
549549
if !is_visible {
550-
// Write down the first field resolution even if it is not visible
551-
// This aids IDE features for private fields like goto def and in
552-
// case of autoderef finding an applicable field, this will be
553-
// overwritten in a following cycle
554-
if let Entry::Vacant(entry) = self.result.field_resolutions.entry(tgt_expr)
555-
{
556-
entry.insert(field_id);
550+
if private_field.is_none() {
551+
private_field = Some(field_id);
557552
}
558553
return None;
559554
}
@@ -572,7 +567,17 @@ impl<'a> InferenceContext<'a> {
572567
let ty = self.normalize_associated_types_in(ty);
573568
ty
574569
}
575-
_ => self.err_ty(),
570+
_ => {
571+
// Write down the first private field resolution if we found no field
572+
// This aids IDE features for private fields like goto def
573+
if let Some(field) = private_field {
574+
self.result.field_resolutions.insert(tgt_expr, field);
575+
self.result
576+
.diagnostics
577+
.push(InferenceDiagnostic::PrivateField { expr: tgt_expr, field });
578+
}
579+
self.err_ty()
580+
}
576581
};
577582
ty
578583
}

crates/hir/src/diagnostics.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use hir_def::path::ModPath;
1010
use hir_expand::{name::Name, HirFileId, InFile};
1111
use syntax::{ast, AstPtr, SyntaxNodePtr, TextRange};
1212

13-
use crate::{MacroKind, Type};
13+
use crate::{Field, MacroKind, Type};
1414

1515
macro_rules! diagnostics {
1616
($($diag:ident,)*) => {
@@ -41,6 +41,7 @@ diagnostics![
4141
MissingMatchArms,
4242
MissingUnsafe,
4343
NoSuchField,
44+
PrivateField,
4445
ReplaceFilterMapNextWithFindMap,
4546
TypeMismatch,
4647
UnimplementedBuiltinMacro,
@@ -121,6 +122,12 @@ pub struct NoSuchField {
121122
pub field: InFile<AstPtr<ast::RecordExprField>>,
122123
}
123124

125+
#[derive(Debug)]
126+
pub struct PrivateField {
127+
pub expr: InFile<AstPtr<ast::Expr>>,
128+
pub field: Field,
129+
}
130+
124131
#[derive(Debug)]
125132
pub struct BreakOutsideOfLoop {
126133
pub expr: InFile<AstPtr<ast::Expr>>,

crates/hir/src/lib.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ pub use crate::{
8585
diagnostics::{
8686
AnyDiagnostic, BreakOutsideOfLoop, InactiveCode, IncorrectCase, InvalidDeriveTarget,
8787
MacroError, MalformedDerive, MismatchedArgCount, MissingFields, MissingMatchArms,
88-
MissingUnsafe, NoSuchField, ReplaceFilterMapNextWithFindMap, TypeMismatch,
88+
MissingUnsafe, NoSuchField, PrivateField, ReplaceFilterMapNextWithFindMap, TypeMismatch,
8989
UnimplementedBuiltinMacro, UnresolvedExternCrate, UnresolvedImport, UnresolvedMacroCall,
9090
UnresolvedModule, UnresolvedProcMacro,
9191
},
@@ -1353,6 +1353,11 @@ impl DefWithBody {
13531353
Err(SyntheticSyntax) => (),
13541354
}
13551355
}
1356+
&hir_ty::InferenceDiagnostic::PrivateField { expr, field } => {
1357+
let expr = source_map.expr_syntax(expr).expect("unexpected synthetic");
1358+
let field = field.into();
1359+
acc.push(PrivateField { expr, field }.into())
1360+
}
13561361
}
13571362
}
13581363
for (expr, mismatch) in infer.expr_type_mismatches() {
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
use crate::{Diagnostic, DiagnosticsContext};
2+
3+
// Diagnostic: private-field
4+
//
5+
// This diagnostic is triggered if created structure does not have field provided in record.
6+
pub(crate) fn private_field(ctx: &DiagnosticsContext<'_>, d: &hir::PrivateField) -> Diagnostic {
7+
// FIXME: add quickfix
8+
Diagnostic::new(
9+
"private-field",
10+
format!(
11+
"field `{}` of `{}` is private",
12+
d.field.name(ctx.sema.db),
13+
d.field.parent_def(ctx.sema.db).name(ctx.sema.db)
14+
),
15+
ctx.sema.diagnostics_display_range(d.expr.clone().map(|it| it.into())).range,
16+
)
17+
}
18+
19+
#[cfg(test)]
20+
mod tests {
21+
use crate::tests::check_diagnostics;
22+
23+
#[test]
24+
fn private_field() {
25+
check_diagnostics(
26+
r#"
27+
mod module { pub struct Struct { field: u32 } }
28+
fn main(s: module::Struct) {
29+
s.field;
30+
//^^^^^^^ error: field `field` of `Struct` is private
31+
}
32+
"#,
33+
);
34+
}
35+
36+
#[test]
37+
fn private_but_shadowed_in_deref() {
38+
check_diagnostics(
39+
r#"
40+
//- minicore: deref
41+
mod module {
42+
pub struct Struct { field: Inner }
43+
pub struct Inner { pub field: u32 }
44+
impl core::ops::Deref for Struct {
45+
type Target = Inner;
46+
fn deref(&self) -> &Inner { &self.field }
47+
}
48+
}
49+
fn main(s: module::Struct) {
50+
s.field;
51+
}
52+
"#,
53+
);
54+
}
55+
}

crates/ide-diagnostics/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ mod handlers {
3737
pub(crate) mod missing_match_arms;
3838
pub(crate) mod missing_unsafe;
3939
pub(crate) mod no_such_field;
40+
pub(crate) mod private_field;
4041
pub(crate) mod replace_filter_map_next_with_find_map;
4142
pub(crate) mod type_mismatch;
4243
pub(crate) mod unimplemented_builtin_macro;
@@ -254,6 +255,7 @@ pub fn diagnostics(
254255
AnyDiagnostic::MissingMatchArms(d) => handlers::missing_match_arms::missing_match_arms(&ctx, &d),
255256
AnyDiagnostic::MissingUnsafe(d) => handlers::missing_unsafe::missing_unsafe(&ctx, &d),
256257
AnyDiagnostic::NoSuchField(d) => handlers::no_such_field::no_such_field(&ctx, &d),
258+
AnyDiagnostic::PrivateField(d) => handlers::private_field::private_field(&ctx, &d),
257259
AnyDiagnostic::ReplaceFilterMapNextWithFindMap(d) => handlers::replace_filter_map_next_with_find_map::replace_filter_map_next_with_find_map(&ctx, &d),
258260
AnyDiagnostic::TypeMismatch(d) => handlers::type_mismatch::type_mismatch(&ctx, &d),
259261
AnyDiagnostic::UnimplementedBuiltinMacro(d) => handlers::unimplemented_builtin_macro::unimplemented_builtin_macro(&ctx, &d),

0 commit comments

Comments
 (0)