all: ast walker for marking unused fns
parent
119dfc0bb0
commit
1084b43ffb
|
@ -254,7 +254,7 @@ jobs:
|
||||||
## sudo apt-get install --quiet -y libsdl2-dev libsdl2-ttf-dev libsdl2-mixer-dev libsdl2-image-dev
|
## sudo apt-get install --quiet -y libsdl2-dev libsdl2-ttf-dev libsdl2-mixer-dev libsdl2-image-dev
|
||||||
- name: Build V
|
- name: Build V
|
||||||
run: make -j4 && ./v -cc gcc -cg -cflags -Werror -o v cmd/v
|
run: make -j4 && ./v -cc gcc -cg -cflags -Werror -o v cmd/v
|
||||||
- name: Valgrind
|
- name: Valgrind v.c
|
||||||
run: valgrind --error-exitcode=1 ./v -o v.c cmd/v
|
run: valgrind --error-exitcode=1 ./v -o v.c cmd/v
|
||||||
- name: Run sanitizers
|
- name: Run sanitizers
|
||||||
run: |
|
run: |
|
||||||
|
|
10
doc/docs.md
10
doc/docs.md
|
@ -1661,11 +1661,16 @@ fn sqr(n int) int {
|
||||||
return n * n
|
return n * n
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn cube(n int) int {
|
||||||
|
return n * n * n
|
||||||
|
}
|
||||||
|
|
||||||
fn run(value int, op fn (int) int) int {
|
fn run(value int, op fn (int) int) int {
|
||||||
return op(value)
|
return op(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
// Functions can be passed to other functions
|
||||||
println(run(5, sqr)) // "25"
|
println(run(5, sqr)) // "25"
|
||||||
// Anonymous functions can be declared inside other functions:
|
// Anonymous functions can be declared inside other functions:
|
||||||
double_fn := fn (n int) int {
|
double_fn := fn (n int) int {
|
||||||
|
@ -1676,6 +1681,11 @@ fn main() {
|
||||||
res := run(5, fn (n int) int {
|
res := run(5, fn (n int) int {
|
||||||
return n + n
|
return n + n
|
||||||
})
|
})
|
||||||
|
// You can even have an array/map of functions:
|
||||||
|
fns := [sqr, cube]
|
||||||
|
println(fns[0](10)) // "100"
|
||||||
|
fns_map := { 'sqr': sqr, 'cube': cube}
|
||||||
|
println(fns_map['cube'](2)) // "8"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -329,6 +329,7 @@ pub:
|
||||||
generic_params []GenericParam
|
generic_params []GenericParam
|
||||||
is_direct_arr bool // direct array access
|
is_direct_arr bool // direct array access
|
||||||
attrs []table.Attr
|
attrs []table.Attr
|
||||||
|
skip_gen bool // this function doesn't need to be generated (for example [if foo])
|
||||||
pub mut:
|
pub mut:
|
||||||
stmts []Stmt
|
stmts []Stmt
|
||||||
return_type table.Type
|
return_type table.Type
|
||||||
|
@ -1518,3 +1519,9 @@ pub fn ex2fe(x Expr) table.FExpr {
|
||||||
unsafe { C.memcpy(&res, &x, sizeof(table.FExpr)) }
|
unsafe { C.memcpy(&res, &x, sizeof(table.FExpr)) }
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// experimental ast.Table
|
||||||
|
pub struct Table {
|
||||||
|
// pub mut:
|
||||||
|
// main_fn_decl_node FnDecl
|
||||||
|
}
|
||||||
|
|
|
@ -27,6 +27,7 @@ pub mut:
|
||||||
parsed_files []ast.File
|
parsed_files []ast.File
|
||||||
cached_msvc MsvcResult
|
cached_msvc MsvcResult
|
||||||
table &table.Table
|
table &table.Table
|
||||||
|
table2 &ast.Table
|
||||||
timers &util.Timers = util.new_timers(false)
|
timers &util.Timers = util.new_timers(false)
|
||||||
ccoptions CcompilerOptions
|
ccoptions CcompilerOptions
|
||||||
}
|
}
|
||||||
|
@ -36,6 +37,7 @@ pub fn new_builder(pref &pref.Preferences) Builder {
|
||||||
compiled_dir := if os.is_dir(rdir) { rdir } else { os.dir(rdir) }
|
compiled_dir := if os.is_dir(rdir) { rdir } else { os.dir(rdir) }
|
||||||
mut table := table.new_table()
|
mut table := table.new_table()
|
||||||
table.is_fmt = false
|
table.is_fmt = false
|
||||||
|
table2 := &ast.Table{}
|
||||||
if pref.use_color == .always {
|
if pref.use_color == .always {
|
||||||
util.emanager.set_support_color(true)
|
util.emanager.set_support_color(true)
|
||||||
}
|
}
|
||||||
|
@ -53,7 +55,8 @@ pub fn new_builder(pref &pref.Preferences) Builder {
|
||||||
return Builder{
|
return Builder{
|
||||||
pref: pref
|
pref: pref
|
||||||
table: table
|
table: table
|
||||||
checker: checker.new_checker(table, pref)
|
table2: table2
|
||||||
|
checker: checker.new_checker(table, table2, pref)
|
||||||
global_scope: &ast.Scope{
|
global_scope: &ast.Scope{
|
||||||
parent: 0
|
parent: 0
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,7 @@ pub fn (mut b Builder) gen_c(v_files []string) string {
|
||||||
b.print_warnings_and_errors()
|
b.print_warnings_and_errors()
|
||||||
// TODO: move gen.cgen() to c.gen()
|
// TODO: move gen.cgen() to c.gen()
|
||||||
b.timing_start('C GEN')
|
b.timing_start('C GEN')
|
||||||
res := c.gen(b.parsed_files, b.table, b.pref)
|
res := c.gen(b.parsed_files, b.table, b.table2, b.pref)
|
||||||
b.timing_measure('C GEN')
|
b.timing_measure('C GEN')
|
||||||
// println('cgen done')
|
// println('cgen done')
|
||||||
// println(res)
|
// println(res)
|
||||||
|
|
|
@ -12,6 +12,8 @@ import v.pref
|
||||||
import v.util
|
import v.util
|
||||||
import v.errors
|
import v.errors
|
||||||
import v.pkgconfig
|
import v.pkgconfig
|
||||||
|
import v.walker
|
||||||
|
import time
|
||||||
|
|
||||||
const (
|
const (
|
||||||
max_nr_errors = 300
|
max_nr_errors = 300
|
||||||
|
@ -36,6 +38,7 @@ pub struct Checker {
|
||||||
pref &pref.Preferences // Preferences shared from V struct
|
pref &pref.Preferences // Preferences shared from V struct
|
||||||
pub mut:
|
pub mut:
|
||||||
table &table.Table
|
table &table.Table
|
||||||
|
table2 &ast.Table
|
||||||
file &ast.File = 0
|
file &ast.File = 0
|
||||||
nr_errors int
|
nr_errors int
|
||||||
nr_warnings int
|
nr_warnings int
|
||||||
|
@ -63,6 +66,7 @@ pub mut:
|
||||||
skip_flags bool // should `#flag` and `#include` be skipped
|
skip_flags bool // should `#flag` and `#include` be skipped
|
||||||
cur_generic_types []table.Type
|
cur_generic_types []table.Type
|
||||||
mut:
|
mut:
|
||||||
|
files []ast.File
|
||||||
expr_level int // to avoid infinite recursion segfaults due to compiler bugs
|
expr_level int // to avoid infinite recursion segfaults due to compiler bugs
|
||||||
inside_sql bool // to handle sql table fields pseudo variables
|
inside_sql bool // to handle sql table fields pseudo variables
|
||||||
cur_orm_ts table.TypeSymbol
|
cur_orm_ts table.TypeSymbol
|
||||||
|
@ -76,15 +80,18 @@ mut:
|
||||||
timers &util.Timers = util.new_timers(false)
|
timers &util.Timers = util.new_timers(false)
|
||||||
comptime_fields_type map[string]table.Type
|
comptime_fields_type map[string]table.Type
|
||||||
fn_scope &ast.Scope = voidptr(0)
|
fn_scope &ast.Scope = voidptr(0)
|
||||||
|
used_fns map[string]bool // used_fns['println'] == true
|
||||||
|
main_fn_decl_node ast.FnDecl
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_checker(table &table.Table, pref &pref.Preferences) Checker {
|
pub fn new_checker(table &table.Table, table2 &ast.Table, pref &pref.Preferences) Checker {
|
||||||
mut timers_should_print := false
|
mut timers_should_print := false
|
||||||
$if time_checking ? {
|
$if time_checking ? {
|
||||||
timers_should_print = true
|
timers_should_print = true
|
||||||
}
|
}
|
||||||
return Checker{
|
return Checker{
|
||||||
table: table
|
table: table
|
||||||
|
table2: table2
|
||||||
pref: pref
|
pref: pref
|
||||||
cur_fn: 0
|
cur_fn: 0
|
||||||
timers: util.new_timers(timers_should_print)
|
timers: util.new_timers(timers_should_print)
|
||||||
|
@ -140,6 +147,7 @@ pub fn (mut c Checker) check2(ast_file &ast.File) []errors.Error {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn (mut c Checker) check_files(ast_files []ast.File) {
|
pub fn (mut c Checker) check_files(ast_files []ast.File) {
|
||||||
|
// c.files = ast_files
|
||||||
mut has_main_mod_file := false
|
mut has_main_mod_file := false
|
||||||
mut has_main_fn := false
|
mut has_main_fn := false
|
||||||
mut files_from_main_module := []&ast.File{}
|
mut files_from_main_module := []&ast.File{}
|
||||||
|
@ -223,6 +231,27 @@ pub fn (mut c Checker) check_files(ast_files []ast.File) {
|
||||||
} else if !has_main_fn {
|
} else if !has_main_fn {
|
||||||
c.error('function `main` must be declared in the main module', token.Position{})
|
c.error('function `main` must be declared in the main module', token.Position{})
|
||||||
}
|
}
|
||||||
|
// Walk the tree starting at main() and mark all used fns.
|
||||||
|
if c.pref.experimental {
|
||||||
|
println('walking the ast')
|
||||||
|
// c.is_recursive = true
|
||||||
|
// c.fn_decl(mut c.table2.main_fn_decl_node)
|
||||||
|
t := time.ticks()
|
||||||
|
mut walker := walker.Walker{
|
||||||
|
files: ast_files
|
||||||
|
}
|
||||||
|
for stmt in c.main_fn_decl_node.stmts {
|
||||||
|
walker.stmt(stmt)
|
||||||
|
}
|
||||||
|
// walker.fn_decl(mut c.table2.main_fn_decl_node)
|
||||||
|
println('time = ${time.ticks() - t}ms, nr used fns=$walker.used_fns.len')
|
||||||
|
|
||||||
|
for key, _ in walker.used_fns {
|
||||||
|
println(key)
|
||||||
|
}
|
||||||
|
// println(walker.used_fns)
|
||||||
|
// c.walk(ast_files)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// do checks specific to files in main module
|
// do checks specific to files in main module
|
||||||
|
@ -1303,7 +1332,7 @@ fn (mut c Checker) check_map_and_filter(is_map bool, elem_typ table.Type, call_e
|
||||||
ast.Ident {
|
ast.Ident {
|
||||||
if arg_expr.kind == .function {
|
if arg_expr.kind == .function {
|
||||||
func := c.table.find_fn(arg_expr.name) or {
|
func := c.table.find_fn(arg_expr.name) or {
|
||||||
c.error('$arg_expr.name is not exist', arg_expr.pos)
|
c.error('$arg_expr.name does not exist', arg_expr.pos)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if func.params.len > 1 {
|
if func.params.len > 1 {
|
||||||
|
@ -1772,6 +1801,7 @@ pub fn (mut c Checker) call_fn(mut call_expr ast.CallExpr) table.Type {
|
||||||
call_expr.name = name_prefixed
|
call_expr.name = name_prefixed
|
||||||
found = true
|
found = true
|
||||||
f = f1
|
f = f1
|
||||||
|
c.table.fns[name_prefixed].is_used = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !found && call_expr.left is ast.IndexExpr {
|
if !found && call_expr.left is ast.IndexExpr {
|
||||||
|
@ -1805,6 +1835,7 @@ pub fn (mut c Checker) call_fn(mut call_expr ast.CallExpr) table.Type {
|
||||||
if f1 := c.table.find_fn(fn_name) {
|
if f1 := c.table.find_fn(fn_name) {
|
||||||
found = true
|
found = true
|
||||||
f = f1
|
f = f1
|
||||||
|
c.table.fns[fn_name].is_used = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if c.pref.is_script && !found {
|
if c.pref.is_script && !found {
|
||||||
|
@ -1816,6 +1847,7 @@ pub fn (mut c Checker) call_fn(mut call_expr ast.CallExpr) table.Type {
|
||||||
call_expr.name = os_name
|
call_expr.name = os_name
|
||||||
found = true
|
found = true
|
||||||
f = f1
|
f = f1
|
||||||
|
c.table.fns[os_name].is_used = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// check for arg (var) of fn type
|
// check for arg (var) of fn type
|
||||||
|
@ -3827,7 +3859,7 @@ fn (mut c Checker) comptime_call(mut node ast.ComptimeCall) table.Type {
|
||||||
...pref_
|
...pref_
|
||||||
is_vweb: true
|
is_vweb: true
|
||||||
}
|
}
|
||||||
mut c2 := new_checker(c.table, pref2)
|
mut c2 := new_checker(c.table, c.table2, pref2)
|
||||||
c2.check(node.vweb_tmpl)
|
c2.check(node.vweb_tmpl)
|
||||||
mut i := 0 // tmp counter var for skipping first three tmpl vars
|
mut i := 0 // tmp counter var for skipping first three tmpl vars
|
||||||
for k, _ in c2.file.scope.children[0].objects {
|
for k, _ in c2.file.scope.children[0].objects {
|
||||||
|
@ -5655,9 +5687,12 @@ fn (mut c Checker) fn_decl(mut node ast.FnDecl) {
|
||||||
if node.language == .v && !c.is_builtin_mod {
|
if node.language == .v && !c.is_builtin_mod {
|
||||||
c.check_valid_snake_case(node.name, 'function name', node.pos)
|
c.check_valid_snake_case(node.name, 'function name', node.pos)
|
||||||
}
|
}
|
||||||
|
if node.name == 'main.main' {
|
||||||
|
c.main_fn_decl_node = node
|
||||||
|
}
|
||||||
if node.return_type != table.void_type {
|
if node.return_type != table.void_type {
|
||||||
for attr in node.attrs {
|
for attr in node.attrs {
|
||||||
if attr.is_ctdefine {
|
if attr.is_comptime_define {
|
||||||
c.error('only functions that do NOT return values can have `[if $attr.name]` tags',
|
c.error('only functions that do NOT return values can have `[if $attr.name]` tags',
|
||||||
node.pos)
|
node.pos)
|
||||||
break
|
break
|
||||||
|
|
|
@ -32,6 +32,7 @@ struct Gen {
|
||||||
module_built string
|
module_built string
|
||||||
mut:
|
mut:
|
||||||
table &table.Table
|
table &table.Table
|
||||||
|
table2 &ast.Table
|
||||||
out strings.Builder
|
out strings.Builder
|
||||||
cheaders strings.Builder
|
cheaders strings.Builder
|
||||||
includes strings.Builder // all C #includes required by V modules
|
includes strings.Builder // all C #includes required by V modules
|
||||||
|
@ -150,9 +151,10 @@ mut:
|
||||||
force_main_console bool // true when [console] used on fn main()
|
force_main_console bool // true when [console] used on fn main()
|
||||||
as_cast_type_names map[string]string // table for type name lookup in runtime (for __as_cast)
|
as_cast_type_names map[string]string // table for type name lookup in runtime (for __as_cast)
|
||||||
obf_table map[string]string
|
obf_table map[string]string
|
||||||
|
// main_fn_decl_node ast.FnDecl
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn gen(files []ast.File, table &table.Table, pref &pref.Preferences) string {
|
pub fn gen(files []ast.File, table &table.Table, table2 &ast.Table, pref &pref.Preferences) string {
|
||||||
// println('start cgen2')
|
// println('start cgen2')
|
||||||
mut module_built := ''
|
mut module_built := ''
|
||||||
if pref.build_mode == .build_module {
|
if pref.build_mode == .build_module {
|
||||||
|
@ -191,6 +193,7 @@ pub fn gen(files []ast.File, table &table.Table, pref &pref.Preferences) string
|
||||||
enum_typedefs: strings.new_builder(100)
|
enum_typedefs: strings.new_builder(100)
|
||||||
sql_buf: strings.new_builder(100)
|
sql_buf: strings.new_builder(100)
|
||||||
table: table
|
table: table
|
||||||
|
table2: table2
|
||||||
pref: pref
|
pref: pref
|
||||||
fn_decl: 0
|
fn_decl: 0
|
||||||
is_autofree: true
|
is_autofree: true
|
||||||
|
|
|
@ -15,6 +15,23 @@ fn (mut g Gen) gen_fn_decl(node ast.FnDecl, skip bool) {
|
||||||
// || node.no_body {
|
// || node.no_body {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
// Skip [if xxx] if xxx is not defined
|
||||||
|
for attr in node.attrs {
|
||||||
|
if !attr.is_comptime_define {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if attr.name !in g.pref.compile_defines_all {
|
||||||
|
// println('skipping [if]')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if f := g.table.find_fn(node.name) {
|
||||||
|
if !f.is_used {
|
||||||
|
g.writeln('// fn $node.name UNUSED')
|
||||||
|
// return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
g.returned_var_name = ''
|
g.returned_var_name = ''
|
||||||
//
|
//
|
||||||
old_g_autofree := g.is_autofree
|
old_g_autofree := g.is_autofree
|
||||||
|
|
|
@ -828,7 +828,7 @@ fn (mut p Parser) attributes() {
|
||||||
p.error_with_pos('duplicate attribute `$attr.name`', start_pos.extend(p.prev_tok.position()))
|
p.error_with_pos('duplicate attribute `$attr.name`', start_pos.extend(p.prev_tok.position()))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if attr.is_ctdefine {
|
if attr.is_comptime_define {
|
||||||
if has_ctdefine {
|
if has_ctdefine {
|
||||||
p.error_with_pos('only one `[if flag]` may be applied at a time `$attr.name`',
|
p.error_with_pos('only one `[if flag]` may be applied at a time `$attr.name`',
|
||||||
start_pos.extend(p.prev_tok.position()))
|
start_pos.extend(p.prev_tok.position()))
|
||||||
|
@ -863,10 +863,9 @@ fn (mut p Parser) parse_attr() table.Attr {
|
||||||
pos: apos.extend(p.tok.position())
|
pos: apos.extend(p.tok.position())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mut is_ctdefine := false
|
is_comptime_define := p.tok.kind == .key_if
|
||||||
if p.tok.kind == .key_if {
|
if is_comptime_define {
|
||||||
p.next()
|
p.next()
|
||||||
is_ctdefine = true
|
|
||||||
}
|
}
|
||||||
mut name := ''
|
mut name := ''
|
||||||
mut arg := ''
|
mut arg := ''
|
||||||
|
@ -899,7 +898,7 @@ fn (mut p Parser) parse_attr() table.Attr {
|
||||||
return table.Attr{
|
return table.Attr{
|
||||||
name: name
|
name: name
|
||||||
is_string: is_string
|
is_string: is_string
|
||||||
is_ctdefine: is_ctdefine
|
is_comptime_define: is_comptime_define
|
||||||
arg: arg
|
arg: arg
|
||||||
is_string_arg: is_string_arg
|
is_string_arg: is_string_arg
|
||||||
pos: apos.extend(p.tok.position())
|
pos: apos.extend(p.tok.position())
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
// that can be found in the LICENSE file.
|
// that can be found in the LICENSE file.
|
||||||
module pref
|
module pref
|
||||||
|
|
||||||
|
// import v.ast // TODO this results in a compiler bug
|
||||||
import os.cmdline
|
import os.cmdline
|
||||||
import os
|
import os
|
||||||
import v.vcache
|
import v.vcache
|
||||||
|
|
|
@ -10,7 +10,7 @@ pub struct Attr {
|
||||||
pub:
|
pub:
|
||||||
name string // [name]
|
name string // [name]
|
||||||
is_string bool // ['name']
|
is_string bool // ['name']
|
||||||
is_ctdefine bool // [if name]
|
is_comptime_define bool // [if name]
|
||||||
arg string // [name: arg]
|
arg string // [name: arg]
|
||||||
is_string_arg bool // [name: 'arg']
|
is_string_arg bool // [name: 'arg']
|
||||||
pos token.Position
|
pos token.Position
|
||||||
|
@ -19,7 +19,7 @@ pub:
|
||||||
// no square brackets
|
// no square brackets
|
||||||
pub fn (attr Attr) str() string {
|
pub fn (attr Attr) str() string {
|
||||||
mut s := ''
|
mut s := ''
|
||||||
if attr.is_ctdefine {
|
if attr.is_comptime_define {
|
||||||
s += 'if '
|
s += 'if '
|
||||||
}
|
}
|
||||||
if attr.is_string {
|
if attr.is_string {
|
||||||
|
|
|
@ -34,11 +34,12 @@ pub:
|
||||||
is_placeholder bool
|
is_placeholder bool
|
||||||
no_body bool
|
no_body bool
|
||||||
mod string
|
mod string
|
||||||
ctdefine string // compile time define. myflag, when [if myflag] tag
|
ctdefine string // compile time define. "myflag", when [if myflag] tag
|
||||||
attrs []Attr
|
attrs []Attr
|
||||||
pub mut:
|
pub mut:
|
||||||
name string
|
name string
|
||||||
source_fn voidptr // set in the checker, while processing fn declarations
|
source_fn voidptr // set in the checker, while processing fn declarations
|
||||||
|
is_used bool
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (f &Fn) method_equals(o &Fn) bool {
|
fn (f &Fn) method_equals(o &Fn) bool {
|
||||||
|
|
|
@ -0,0 +1,129 @@
|
||||||
|
// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved.
|
||||||
|
// Use of this source code is governed by an MIT license that can be found in the LICENSE file.
|
||||||
|
module walker
|
||||||
|
|
||||||
|
// This module walks the entire program starting at fn main and marks used (called)
|
||||||
|
// functions.
|
||||||
|
// Unused functions can be safely skipped by the backends to save CPU time and space.
|
||||||
|
import v.ast
|
||||||
|
|
||||||
|
pub struct Walker {
|
||||||
|
pub mut:
|
||||||
|
used_fns map[string]bool // used_fns['println'] == true
|
||||||
|
mut:
|
||||||
|
files []ast.File
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
fn (mut w Walker) walk_files(ast_files []ast.File) {
|
||||||
|
t := time.ticks()
|
||||||
|
*/
|
||||||
|
|
||||||
|
pub fn (mut w Walker) stmt(node ast.Stmt) {
|
||||||
|
match mut node {
|
||||||
|
ast.AssignStmt {
|
||||||
|
for l in node.left {
|
||||||
|
w.expr(l)
|
||||||
|
}
|
||||||
|
for r in node.right {
|
||||||
|
w.expr(r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ast.ExprStmt {
|
||||||
|
w.expr(node.expr)
|
||||||
|
}
|
||||||
|
// ast.FnDecl {
|
||||||
|
// w.fn_decl(mut node)
|
||||||
|
//}
|
||||||
|
ast.ForStmt {
|
||||||
|
w.expr(node.cond)
|
||||||
|
for stmt in node.stmts {
|
||||||
|
w.stmt(stmt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut w Walker) expr(node ast.Expr) {
|
||||||
|
match mut node {
|
||||||
|
ast.CallExpr {
|
||||||
|
w.call_expr(mut node)
|
||||||
|
}
|
||||||
|
ast.GoExpr {
|
||||||
|
w.expr(node.go_stmt.call_expr)
|
||||||
|
}
|
||||||
|
ast.IndexExpr {
|
||||||
|
w.expr(node.left)
|
||||||
|
w.expr(node.index)
|
||||||
|
}
|
||||||
|
ast.IfExpr {
|
||||||
|
for b in node.branches {
|
||||||
|
w.expr(b.cond)
|
||||||
|
for stmt in b.stmts {
|
||||||
|
w.stmt(stmt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ast.MatchExpr {
|
||||||
|
w.expr(node.cond)
|
||||||
|
for b in node.branches {
|
||||||
|
for expr in b.exprs {
|
||||||
|
w.expr(expr)
|
||||||
|
}
|
||||||
|
for stmt in b.stmts {
|
||||||
|
w.stmt(stmt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
pub fn (mut w Walker) fn_decl(mut node ast.FnDecl) {
|
||||||
|
fn_name := if node.is_method { node.receiver.typ.str() + '.' + node.name } else { node.name }
|
||||||
|
if w.used_fns[fn_name] {
|
||||||
|
// This function is already known to be called, meaning it has been processed already.
|
||||||
|
// Save CPU time and do nothing.
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if node.language == .c {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
println('fn decl $fn_name')
|
||||||
|
w.used_fns[fn_name] = true
|
||||||
|
for stmt in node.stmts {
|
||||||
|
w.stmt(stmt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
pub fn (mut w Walker) call_expr(mut node ast.CallExpr) {
|
||||||
|
fn_name := if node.is_method { node.receiver_type.str() + '.' + node.name } else { node.name }
|
||||||
|
// fn_name := node.name
|
||||||
|
println('call_expr $fn_name')
|
||||||
|
// if node.is_method {
|
||||||
|
// println('M $node.name $node.receiver_type')
|
||||||
|
//}
|
||||||
|
if w.used_fns[fn_name] {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// w.used_fns[fn_name] = true
|
||||||
|
// Find the FnDecl for this CallExpr, mark the function as used, and process
|
||||||
|
// all its statements.
|
||||||
|
loop: for file in w.files {
|
||||||
|
for stmt in file.stmts {
|
||||||
|
if stmt is ast.FnDecl {
|
||||||
|
if stmt.name == node.name
|
||||||
|
&& (!node.is_method || (node.receiver_type == stmt.receiver.typ)) {
|
||||||
|
w.used_fns[fn_name] = true
|
||||||
|
for fn_stmt in stmt.stmts {
|
||||||
|
w.stmt(fn_stmt)
|
||||||
|
}
|
||||||
|
break loop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue