Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions crates/ra_hir/src/semantics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,10 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
self.analyze(field.syntax()).resolve_record_field(self.db, field)
}

pub fn resolve_record_field_pat(&self, field: &ast::RecordFieldPat) -> Option<StructField> {
self.analyze(field.syntax()).resolve_record_field_pat(self.db, field)
}

pub fn resolve_macro_call(&self, macro_call: &ast::MacroCall) -> Option<MacroDef> {
let sa = self.analyze(macro_call.syntax());
let macro_call = self.find_file(macro_call.syntax().clone()).with_value(macro_call);
Expand Down
11 changes: 11 additions & 0 deletions crates/ra_hir/src/source_analyzer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ impl SourceAnalyzer {
}

fn pat_id(&self, pat: &ast::Pat) -> Option<PatId> {
// FIXME: macros, see `expr_id`
let src = InFile { file_id: self.file_id, value: pat };
self.body_source_map.as_ref()?.node_pat(src)
}
Expand Down Expand Up @@ -167,6 +168,16 @@ impl SourceAnalyzer {
Some((struct_field.into(), local))
}

pub(crate) fn resolve_record_field_pat(
&self,
_db: &dyn HirDatabase,
field: &ast::RecordFieldPat,
) -> Option<StructField> {
let pat_id = self.pat_id(&field.pat()?)?;
let struct_field = self.infer.as_ref()?.record_field_pat_resolution(pat_id)?;
Some(struct_field.into())
}

pub(crate) fn resolve_macro_call(
&self,
db: &dyn HirDatabase,
Expand Down
4 changes: 4 additions & 0 deletions crates/ra_hir_def/src/body/scope.rs
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,10 @@ fn compute_expr_scopes(expr: ExprId, body: &Body, scopes: &mut ExprScopes, scope
for arm in arms {
let scope = scopes.new_scope(scope);
scopes.add_bindings(body, scope, arm.pat);
if let Some(guard) = arm.guard {
scopes.set_scope(guard, scope);
compute_expr_scopes(guard, body, scopes, scope);
}
scopes.set_scope(arm.expr, scope);
compute_expr_scopes(arm.expr, body, scopes, scope);
}
Expand Down
4 changes: 4 additions & 0 deletions crates/ra_hir_ty/src/infer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ pub struct InferenceResult {
field_resolutions: FxHashMap<ExprId, StructFieldId>,
/// For each field in record literal, records the field it resolves to.
record_field_resolutions: FxHashMap<ExprId, StructFieldId>,
record_field_pat_resolutions: FxHashMap<PatId, StructFieldId>,
/// For each struct literal, records the variant it resolves to.
variant_resolutions: FxHashMap<ExprOrPatId, VariantId>,
/// For each associated item record what it resolves to
Expand All @@ -147,6 +148,9 @@ impl InferenceResult {
pub fn record_field_resolution(&self, expr: ExprId) -> Option<StructFieldId> {
self.record_field_resolutions.get(&expr).copied()
}
pub fn record_field_pat_resolution(&self, pat: PatId) -> Option<StructFieldId> {
self.record_field_pat_resolutions.get(&pat).copied()
}
pub fn variant_resolution_for_expr(&self, id: ExprId) -> Option<VariantId> {
self.variant_resolutions.get(&id.into()).copied()
}
Expand Down
6 changes: 6 additions & 0 deletions crates/ra_hir_ty/src/infer/pat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use hir_def::{
expr::{BindingAnnotation, Pat, PatId, RecordFieldPat},
path::Path,
type_ref::Mutability,
StructFieldId,
};
use hir_expand::name::Name;
use test_utils::tested_by;
Expand Down Expand Up @@ -67,6 +68,11 @@ impl<'a> InferenceContext<'a> {
let field_tys = def.map(|it| self.db.field_types(it)).unwrap_or_default();
for subpat in subpats {
let matching_field = var_data.as_ref().and_then(|it| it.field(&subpat.name));
if let Some(local_id) = matching_field {
let field_def = StructFieldId { parent: def.unwrap(), local_id };
self.result.record_field_pat_resolutions.insert(subpat.pat, field_def);
}

let expected_ty =
matching_field.map_or(Ty::Unknown, |field| field_tys[field].clone().subst(&substs));
let expected_ty = self.normalize_associated_types_in(expected_ty);
Expand Down
26 changes: 26 additions & 0 deletions crates/ra_hir_ty/src/tests/patterns.rs
Original file line number Diff line number Diff line change
Expand Up @@ -455,3 +455,29 @@ fn test() {
"###
);
}

#[test]
fn infer_guard() {
assert_snapshot!(
infer(r#"
struct S;
impl S { fn foo(&self) -> bool { false } }

fn main() {
match S {
s if s.foo() => (),
}
}
"#), @"
[28; 32) 'self': &S
[42; 51) '{ false }': bool
[44; 49) 'false': bool
[65; 116) '{ ... } }': ()
[71; 114) 'match ... }': ()
[77; 78) 'S': S
[89; 90) 's': S
[94; 95) 's': S
[94; 101) 's.foo()': bool
[105; 107) '()': ()
")
}
28 changes: 22 additions & 6 deletions crates/ra_ide/src/goto_definition.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,9 @@ pub(crate) enum ReferenceResult {

impl ReferenceResult {
fn to_vec(self) -> Vec<NavigationTarget> {
use self::ReferenceResult::*;
match self {
Exact(target) => vec![target],
Approximate(vec) => vec,
ReferenceResult::Exact(target) => vec![target],
ReferenceResult::Approximate(vec) => vec,
}
}
}
Expand All @@ -74,8 +73,6 @@ pub(crate) fn reference_definition(
sema: &Semantics<RootDatabase>,
name_ref: &ast::NameRef,
) -> ReferenceResult {
use self::ReferenceResult::*;

let name_kind = classify_name_ref(sema, name_ref);
if let Some(def) = name_kind {
let def = def.definition();
Expand All @@ -91,7 +88,7 @@ pub(crate) fn reference_definition(
.into_iter()
.map(|s| s.to_nav(sema.db))
.collect();
Approximate(navs)
ReferenceResult::Approximate(navs)
}

#[cfg(test)]
Expand Down Expand Up @@ -398,6 +395,25 @@ mod tests {
);
}

#[test]
fn goto_def_for_record_pat_fields() {
covers!(ra_ide_db::goto_def_for_record_field_pats);
check_goto(
r"
//- /lib.rs
struct Foo {
spam: u32,
}

fn bar(foo: Foo) -> Foo {
let Foo { spam<|>: _, } = foo
}
",
"spam RECORD_FIELD_DEF FileId(1) [17; 26) [17; 21)",
"spam: u32|spam",
);
}

#[test]
fn goto_def_for_record_fields_macros() {
check_goto(
Expand Down
9 changes: 9 additions & 0 deletions crates/ra_ide_db/src/defs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,7 @@ fn classify_name_inner(sema: &Semantics<RootDatabase>, name: &ast::Name) -> Opti
}
}

#[derive(Debug)]
pub enum NameRefClass {
Definition(Definition),
FieldShorthand { local: Local, field: Definition },
Expand Down Expand Up @@ -229,6 +230,14 @@ pub fn classify_name_ref(
}
}

if let Some(record_field_pat) = ast::RecordFieldPat::cast(parent.clone()) {
tested_by!(goto_def_for_record_field_pats; force);
if let Some(field) = sema.resolve_record_field_pat(&record_field_pat) {
let field = Definition::StructField(field);
return Some(NameRefClass::Definition(field));
}
}

if let Some(macro_call) = parent.ancestors().find_map(ast::MacroCall::cast) {
tested_by!(goto_def_for_macros; force);
if let Some(macro_def) = sema.resolve_macro_call(&macro_call) {
Expand Down
1 change: 1 addition & 0 deletions crates/ra_ide_db/src/marks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@ test_utils::marks![
goto_def_for_fields
goto_def_for_record_fields
goto_def_for_field_init_shorthand
goto_def_for_record_field_pats
search_filters_by_range
];