Skip to main content

yog_script_analyze/ast/
accessor.rs

1//! What an accessor has in scope besides its own: `value` (a `set`'s or an
2//! `init`'s new value) and, a property's, `field` (its stored value, as
3//! C# 13's). One rule, for everything that walks accessors.
4
5use super::{Accessor, Name, Param, Type};
6use crate::words::{field, init, set, value};
7
8/// Accessor `a` of a member of type `ty` (`property`: a property's, not an
9/// indexer's): the names it has beyond the member's parameters.
10pub fn accessor_scope(ty: &Type, a: &Accessor, property: bool) -> Vec<Param> {
11    let writes = [set::WORD.text, init::WORD.text].contains(&a.name.text.as_str());
12    let mut names = Vec::new();
13    if writes {
14        names.push(named(value::WORD.text, ty, &a.name));
15    }
16    if property {
17        names.push(named(field::WORD.text, ty, &a.name));
18    }
19    names
20}
21
22/// A `set`'s `value` parameter of type `ty` (an indexer's setter takes it last).
23pub fn value_param(ty: &Type, at: &Name) -> Param {
24    named(value::WORD.text, ty, at)
25}
26
27fn named(text: &str, ty: &Type, at: &Name) -> Param {
28    Param {
29        params: false,
30        extension: false,
31        pass: None,
32        lifetime: None,
33        ty: ty.clone(),
34        name: Name {
35            text: text.to_string(),
36            span: at.span,
37        },
38        default: None,
39    }
40}
41
42/// Whether property `d` is a stored value (an auto-property, `T X { get;
43/// set; }`): its accessors without bodies, and it not `abstract` (an
44/// abstract one's accessors are its overrides').
45pub fn stores_value(d: &super::Decl) -> bool {
46    match &d.kind {
47        super::DeclKind::Property { accessors, .. } => {
48            accessors.iter().all(|a| a.body.is_none()) && !d.has(super::Modifier::Abstract)
49        }
50        _ => false,
51    }
52}
53
54/// Whether a property of `accessors` keeps a value of its own (C#'s
55/// `field`): one with a body reaches `field`, or one has no body beside one
56/// that has. A property of none with bodies is all its own value.
57pub fn keeps_value(accessors: &[Accessor]) -> bool {
58    let bodies = accessors.iter().filter(|a| a.body.is_some()).count();
59    if bodies == 0 {
60        return false;
61    }
62    bodies < accessors.len()
63        || accessors
64            .iter()
65            .filter_map(|a| a.body.as_ref())
66            .any(reaches_field)
67}
68
69fn reaches_field(body: &super::MethodBody) -> bool {
70    let mut found = false;
71    match body {
72        super::MethodBody::Expr(e) => found = names_field(e),
73        super::MethodBody::Block(b) => b
74            .stmts
75            .iter()
76            .for_each(|s| s.each_expr(&mut |e| found |= names_field(e))),
77    }
78    found
79}
80
81fn names_field(e: &super::Expr) -> bool {
82    if matches!(&e.kind, super::ExprKind::Ident(n) if n == field::WORD.text) {
83        return true;
84    }
85    let mut found = false;
86    e.each_child(|c| found |= names_field(c));
87    found
88}