plum

#treesitter#compiler#wasm

git clone https://git.pyrossh.dev/plum

A statically typed, imperative programming language inspired by rust, python


plum-core/src/builtin_usage.rs
29313cb 1
use std::collections::HashSet;
29313cb 2
29313cb 3
use crate::ast::{
29313cb 4
    Arg, AssignTarget, AttrKind, CasePattern, Expr, FnBody, Item, ParamType, Source, Stmt,
29313cb 5
    StringPart, Type,
29313cb 6
};
29313cb 7
29313cb 8
/// The builtin type names this walk tracks: primitive value types with no
29313cb 9
/// enum-variant/name-resolution step that naturally fails when unimported
29313cb 10
/// (unlike `Bool`, whose `True`/`False` variants simply don't resolve without
29313cb 11
/// `import std/Bool`) — so nothing else catches a missing `import std/Int` /
29313cb 12
/// `import std/Float` / `import std/Str`.
29313cb 13
const TRACKED_TYPES: [&str; 3] = ["Int", "Float", "Str"];
29313cb 14
29313cb 15
fn isTracked(name: &str) -> bool {
29313cb 16
    TRACKED_TYPES.contains(&name)
29313cb 17
}
29313cb 18
29313cb 19
/// Every builtin type name (`Int`, `Float`, `Str` — literal or type-annotation)
29313cb 20
/// mentioned anywhere in `source` — used by `loader::loadAndMerge` to enforce
29313cb 21
/// that a file using one of them explicitly `import`s it, exactly like every
29313cb 22
/// other stdlib type (`Bool`, ...) already must be.
29313cb 23
pub fn usedBuiltinTypeNames(source: &Source) -> HashSet<String> {
29313cb 24
    let mut names = HashSet::new();
29313cb 25
    for item in &source.items {
29313cb 26
        match item {
29313cb 27
            Item::Trait(t) => {
29313cb 28
                for m in &t.methods {
29313cb 29
                    for p in &m.params {
29313cb 30
                        walkParamType(&p.ty, &mut names);
29313cb 31
                    }
29313cb 32
                    if let Some(r) = &m.returns {
29313cb 33
                        walkType(r, &mut names);
29313cb 34
                    }
29313cb 35
                }
29313cb 36
            }
29313cb 37
            Item::Enum(e) => {
29313cb 38
                for p in &e.params {
29313cb 39
                    walkType(&p.ty, &mut names);
29313cb 40
                }
29313cb 41
                for v in &e.variants {
29313cb 42
                    for f in &v.fields {
29313cb 43
                        walkType(f, &mut names);
29313cb 44
                    }
29313cb 45
                    for val in &v.values {
29313cb 46
                        walkExpr(val, &mut names);
29313cb 47
                    }
29313cb 48
                }
29313cb 49
            }
29313cb 50
            Item::Fn(f) => {
29313cb 51
                for p in &f.params {
29313cb 52
                    walkParamType(&p.ty, &mut names);
29313cb 53
                    if let Some(d) = &p.default {
29313cb 54
                        walkExpr(d, &mut names);
29313cb 55
                    }
29313cb 56
                }
29313cb 57
                if let Some(r) = &f.returns {
29313cb 58
                    walkType(r, &mut names);
29313cb 59
                }
29313cb 60
                match &f.body {
29313cb 61
                    FnBody::Expr(e) => walkExpr(e, &mut names),
29313cb 62
                    FnBody::Block(b) => walkStmts(&b.stmts, &mut names),
29313cb 63
                    FnBody::Extern => {}
29313cb 64
                }
29313cb 65
            }
29313cb 66
            Item::Const(c) => walkExpr(&c.value, &mut names),
29313cb 67
            Item::Test(t) => walkStmts(&t.body.stmts, &mut names),
29313cb 68
        }
29313cb 69
    }
29313cb 70
    names
29313cb 71
}
29313cb 72
29313cb 73
fn walkType(ty: &Type, names: &mut HashSet<String>) {
29313cb 74
    if isTracked(&ty.name) {
29313cb 75
        names.insert(ty.name.clone());
29313cb 76
    }
29313cb 77
    for g in &ty.generics {
29313cb 78
        walkType(g, names);
29313cb 79
    }
29313cb 80
}
29313cb 81
29313cb 82
fn walkParamType(pt: &ParamType, names: &mut HashSet<String>) {
29313cb 83
    match pt {
29313cb 84
        ParamType::Type(t) => walkType(t, names),
29313cb 85
        ParamType::Variadic(t) => walkType(t, names),
29313cb 86
        ParamType::Fn(params, ret) => {
29313cb 87
            for p in params {
29313cb 88
                walkType(p, names);
29313cb 89
            }
29313cb 90
            if let Some(r) = ret {
29313cb 91
                walkType(r, names);
29313cb 92
            }
29313cb 93
        }
29313cb 94
    }
29313cb 95
}
29313cb 96
29313cb 97
fn walkStmts(stmts: &[Stmt], names: &mut HashSet<String>) {
29313cb 98
    for s in stmts {
29313cb 99
        walkStmt(s, names);
29313cb 100
    }
29313cb 101
}
29313cb 102
29313cb 103
fn walkStmt(stmt: &Stmt, names: &mut HashSet<String>) {
29313cb 104
    match stmt {
29313cb 105
        Stmt::Assign(a) => {
29313cb 106
            for t in &a.targets {
29313cb 107
                if let AssignTarget::Field(obj, _) = t {
29313cb 108
                    walkExpr(obj, names);
29313cb 109
                }
29313cb 110
            }
29313cb 111
            for v in &a.values {
29313cb 112
                walkExpr(v, names);
29313cb 113
            }
29313cb 114
        }
29313cb 115
        Stmt::Break | Stmt::Continue | Stmt::Todo => {}
29313cb 116
        Stmt::Assert(check) => walkExpr(&check.cond, names),
29313cb 117
        Stmt::For(f) => {
29313cb 118
            walkExpr(&f.iter, names);
29313cb 119
            walkStmts(&f.body.stmts, names);
29313cb 120
        }
29313cb 121
        Stmt::While(w) => {
29313cb 122
            walkExpr(&w.condition, names);
29313cb 123
            walkStmts(&w.body.stmts, names);
29313cb 124
        }
29313cb 125
        Stmt::If(i) => {
29313cb 126
            walkExpr(&i.condition, names);
29313cb 127
            walkStmts(&i.body.stmts, names);
29313cb 128
            for ei in &i.else_ifs {
29313cb 129
                walkExpr(&ei.condition, names);
29313cb 130
                walkStmts(&ei.body.stmts, names);
29313cb 131
            }
29313cb 132
            if let Some(e) = &i.else_ {
29313cb 133
                walkStmts(&e.stmts, names);
29313cb 134
            }
29313cb 135
        }
29313cb 136
        Stmt::Match(m) => {
29313cb 137
            for s in &m.subjects {
29313cb 138
                walkExpr(s, names);
29313cb 139
            }
29313cb 140
            for c in &m.cases {
29313cb 141
                for p in &c.patterns {
29313cb 142
                    walkCasePattern(p, names);
29313cb 143
                }
29313cb 144
                if let Some(g) = &c.guard {
29313cb 145
                    walkExpr(g, names);
29313cb 146
                }
29313cb 147
                walkStmts(&c.body.stmts, names);
29313cb 148
            }
29313cb 149
        }
29313cb 150
        Stmt::Return(e) => {
29313cb 151
            if let Some(e) = e {
29313cb 152
                walkExpr(e, names);
29313cb 153
            }
29313cb 154
        }
29313cb 155
        Stmt::Expr(e) => walkExpr(e, names),
29313cb 156
    }
29313cb 157
}
29313cb 158
29313cb 159
fn walkCasePattern(pat: &CasePattern, names: &mut HashSet<String>) {
29313cb 160
    match pat {
29313cb 161
        CasePattern::Class { fields, .. } => {
29313cb 162
            for f in fields {
29313cb 163
                walkCasePattern(f, names);
29313cb 164
            }
29313cb 165
        }
29313cb 166
        CasePattern::Int(_) => {
29313cb 167
            names.insert("Int".to_string());
29313cb 168
        }
29313cb 169
        CasePattern::Float(_) => {
29313cb 170
            names.insert("Float".to_string());
29313cb 171
        }
29313cb 172
        CasePattern::String(_) => {
29313cb 173
            names.insert("Str".to_string());
29313cb 174
        }
29313cb 175
        CasePattern::Name(_) | CasePattern::Wildcard => {}
29313cb 176
    }
29313cb 177
}
29313cb 178
29313cb 179
fn walkExpr(expr: &Expr, names: &mut HashSet<String>) {
29313cb 180
    match expr {
29313cb 181
        Expr::Binary(b) => {
29313cb 182
            walkExpr(&b.left, names);
29313cb 183
            walkExpr(&b.right, names);
29313cb 184
        }
29313cb 185
        Expr::Unary(u) => walkExpr(&u.operand, names),
29313cb 186
        Expr::Bool(b) => {
29313cb 187
            walkExpr(&b.left, names);
29313cb 188
            walkExpr(&b.right, names);
29313cb 189
        }
29313cb 190
        Expr::Not(e) => walkExpr(e, names),
af1776b 191
        Expr::Try(e) => walkExpr(e, names),
29313cb 192
        Expr::Compare(c) => {
29313cb 193
            walkExpr(&c.left, names);
29313cb 194
            walkExpr(&c.right, names);
29313cb 195
        }
29313cb 196
        Expr::Ternary(t) => {
29313cb 197
            walkExpr(&t.condition, names);
29313cb 198
            walkExpr(&t.then, names);
29313cb 199
            walkExpr(&t.else_, names);
29313cb 200
        }
ec5336f 201
        Expr::Elvis(e) => {
ec5336f 202
            walkExpr(&e.left, names);
ec5336f 203
            walkExpr(&e.right, names);
ec5336f 204
        }
29313cb 205
        Expr::FnCall(call) => {
29313cb 206
            if isTracked(&call.name) {
29313cb 207
                names.insert(call.name.clone());
29313cb 208
            }
29313cb 209
            for a in &call.args {
29313cb 210
                walkArg(a, names);
29313cb 211
            }
29313cb 212
        }
29313cb 213
        Expr::ClassCall(c) => {
29313cb 214
            if isTracked(&c.type_name) {
29313cb 215
                names.insert(c.type_name.clone());
29313cb 216
            }
29313cb 217
            for g in &c.generics {
29313cb 218
                walkType(g, names);
29313cb 219
            }
29313cb 220
            for f in &c.fields {
29313cb 221
                walkExpr(&f.value, names);
29313cb 222
            }
29313cb 223
        }
29313cb 224
        Expr::Attribute(a) => {
29313cb 225
            walkExpr(&a.object, names);
29313cb 226
            if let AttrKind::Method(call) = &a.attr {
29313cb 227
                for arg in &call.args {
29313cb 228
                    walkArg(arg, names);
29313cb 229
                }
29313cb 230
            }
29313cb 231
        }
29313cb 232
        Expr::Paren(e) => walkExpr(e, names),
29313cb 233
        Expr::String(s) => {
29313cb 234
            names.insert("Str".to_string());
29313cb 235
            for part in &s.parts {
29313cb 236
                if let StringPart::Interp(e) = part {
29313cb 237
                    walkExpr(e, names);
29313cb 238
                }
29313cb 239
            }
29313cb 240
        }
29313cb 241
        Expr::Int(_) => {
29313cb 242
            names.insert("Int".to_string());
29313cb 243
        }
29313cb 244
        Expr::Float(_) => {
29313cb 245
            names.insert("Float".to_string());
29313cb 246
        }
29313cb 247
        Expr::Self_ | Expr::Var(_) => {}
29313cb 248
        Expr::TypeName(name) => {
29313cb 249
            if isTracked(name) {
29313cb 250
                names.insert(name.clone());
29313cb 251
            }
29313cb 252
        }
29313cb 253
        Expr::Closure(c) => walkStmts(&c.body.stmts, names),
29313cb 254
    }
29313cb 255
}
29313cb 256
29313cb 257
fn walkArg(arg: &Arg, names: &mut HashSet<String>) {
29313cb 258
    match arg {
29313cb 259
        Arg::Positional(e) => walkExpr(e, names),
29313cb 260
        Arg::Keyword { value, .. } => walkExpr(value, names),
29313cb 261
        Arg::Pair { value, .. } => walkExpr(value, names),
29313cb 262
    }
29313cb 263
}