all: improve -skip-unused, track consts, walk all AST nodes, support tests
parent
26121d5ae7
commit
d77bb2f606
|
@ -26,7 +26,7 @@ pub fn (d float_literal) str() string {
|
|||
return f64(d).str()
|
||||
}
|
||||
|
||||
// strsci returns the `f64` as a `string` in scientific notation with `digit_num` deciamals displayed, max 17 digits.
|
||||
// strsci returns the `f64` as a `string` in scientific notation with `digit_num` decimals displayed, max 17 digits.
|
||||
// Example: assert f64(1.234).strsci(3) == '1.234e+00'
|
||||
[inline]
|
||||
pub fn (x f64) strsci(digit_num int) string {
|
||||
|
|
|
@ -8,46 +8,47 @@ import v.checker.mark_used_walker
|
|||
// mark_used walks the AST, starting at main() and marks all used fns transitively
|
||||
fn (mut c Checker) mark_used(ast_files []ast.File) {
|
||||
util.timing_start(@METHOD)
|
||||
mut walker := mark_used_walker.Walker{
|
||||
files: ast_files
|
||||
}
|
||||
|
||||
mut allfns := map[string]ast.FnDecl{}
|
||||
util.timing_start('all_fn_and_const')
|
||||
mut all_fns := map[string]ast.FnDecl{}
|
||||
mut all_consts := map[string]ast.ConstField{}
|
||||
for i in 0 .. ast_files.len {
|
||||
file := unsafe { &ast_files[i] }
|
||||
for node in file.stmts {
|
||||
if node is ast.FnDecl {
|
||||
fkey := if node.is_method {
|
||||
'${int(node.receiver.typ)}.$node.name'
|
||||
} else {
|
||||
node.name
|
||||
match node {
|
||||
ast.FnDecl {
|
||||
fkey := if node.is_method {
|
||||
'${int(node.receiver.typ)}.$node.name'
|
||||
} else {
|
||||
node.name
|
||||
}
|
||||
all_fns[fkey] = node
|
||||
}
|
||||
allfns[fkey] = node
|
||||
ast.ConstDecl {
|
||||
for cfield in node.fields {
|
||||
ckey := cfield.name
|
||||
all_consts[ckey] = cfield
|
||||
}
|
||||
}
|
||||
else {}
|
||||
}
|
||||
}
|
||||
}
|
||||
all_fn_root_names := [
|
||||
util.timing_measure('all_fn_and_const')
|
||||
|
||||
mut all_fn_root_names := [
|
||||
'main.main',
|
||||
'__new_array',
|
||||
'__new_array_with_default',
|
||||
'new_array_from_c_array',
|
||||
'panic',
|
||||
'memdup',
|
||||
'vstrlen',
|
||||
'tos2',
|
||||
'tos',
|
||||
'tos2',
|
||||
'tos3',
|
||||
'isnil',
|
||||
'utf8_char_len',
|
||||
/* utf8_str_visible_length is used by c/str.v */
|
||||
'utf8_str_visible_length',
|
||||
'builtin_init',
|
||||
'print_backtrace_skipping_top_frames',
|
||||
'print_backtrace_skipping_top_frames_mac',
|
||||
'print_backtrace_skipping_top_frames_linux',
|
||||
'print_backtrace_skipping_top_frames_freebsd',
|
||||
'print_backtrace_skipping_top_frames_windows',
|
||||
'print_backtrace_skipping_top_frames_mingw',
|
||||
'print_backtrace_skipping_top_frames_msvc',
|
||||
'print_backtrace_skipping_top_frames_tcc',
|
||||
/* byteptr and charptr */
|
||||
'3.vstring',
|
||||
'3.vstring_with_len',
|
||||
|
@ -55,8 +56,6 @@ fn (mut c Checker) mark_used(ast_files []ast.File) {
|
|||
'4.vstring_with_len',
|
||||
/* string. methods */
|
||||
'18.add',
|
||||
'18.all_after',
|
||||
'18.all_before',
|
||||
'18.trim_space',
|
||||
'18.replace',
|
||||
'18.clone',
|
||||
|
@ -81,9 +80,13 @@ fn (mut c Checker) mark_used(ast_files []ast.File) {
|
|||
'19.add',
|
||||
/* other array methods */
|
||||
'21.get',
|
||||
'21.set',
|
||||
'21.get_unsafe',
|
||||
'21.set_unsafe',
|
||||
'21.slice',
|
||||
'21.slice2',
|
||||
'59.get',
|
||||
'65557.free',
|
||||
'59.set',
|
||||
'65557.push',
|
||||
'65557.set',
|
||||
'65557.set_unsafe',
|
||||
|
@ -91,28 +94,75 @@ fn (mut c Checker) mark_used(ast_files []ast.File) {
|
|||
'os.getwd',
|
||||
'os.init_os_args',
|
||||
]
|
||||
// println( allfns.keys() )
|
||||
for fn_name in all_fn_root_names {
|
||||
walker.fn_decl(mut allfns[fn_name])
|
||||
|
||||
// implicit string builders are generated in auto_eq_methods.v
|
||||
mut sb_mut_type := ''
|
||||
if sbfn := c.table.find_fn('strings.new_builder') {
|
||||
sb_mut_type = sbfn.return_type.set_nr_muls(1).str() + '.'
|
||||
}
|
||||
|
||||
$if trace_skip_unused ? {
|
||||
for k, _ in all_fns {
|
||||
if k.ends_with('.init') {
|
||||
all_fn_root_names << k
|
||||
}
|
||||
if k.ends_with('.free') {
|
||||
all_fn_root_names << k
|
||||
}
|
||||
if c.pref.is_test && (k.starts_with('test_') || k.contains('.test_')) {
|
||||
all_fn_root_names << k
|
||||
}
|
||||
if sb_mut_type != '' && k.starts_with(sb_mut_type) {
|
||||
all_fn_root_names << k
|
||||
}
|
||||
}
|
||||
if c.pref.is_debug {
|
||||
all_fn_root_names << 'panic_debug'
|
||||
}
|
||||
if c.pref.is_test {
|
||||
all_fn_root_names << 'main.cb_assertion_ok'
|
||||
all_fn_root_names << 'main.cb_assertion_failed'
|
||||
if benched_tests_sym := c.table.find_type('main.BenchedTests') {
|
||||
bts_type := benched_tests_sym.methods[0].params[0].typ
|
||||
all_fn_root_names << '${bts_type}.testing_step_start'
|
||||
all_fn_root_names << '${bts_type}.testing_step_end'
|
||||
all_fn_root_names << '${bts_type}.end_testing'
|
||||
all_fn_root_names << 'main.start_testing'
|
||||
}
|
||||
}
|
||||
|
||||
mut walker := mark_used_walker.Walker{
|
||||
files: ast_files
|
||||
all_fns: all_fns
|
||||
all_consts: all_consts
|
||||
}
|
||||
// println( all_fns.keys() )
|
||||
walker.mark_root_fns(all_fn_root_names)
|
||||
|
||||
if walker.n_asserts > 0 {
|
||||
walker.fn_decl(mut all_fns['__print_assert_failure'])
|
||||
}
|
||||
if walker.n_maps > 0 {
|
||||
for k, mut mfn in all_fns {
|
||||
if k == 'new_map_2' || k.starts_with('map_') || k.ends_with('set_1')
|
||||
|| k.ends_with('exists_1')|| k.ends_with('get_1') {
|
||||
walker.fn_decl(mut mfn)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$if trace_skip_unused_fn_names ? {
|
||||
for key, _ in walker.used_fns {
|
||||
println('> used fn key: $key')
|
||||
}
|
||||
}
|
||||
|
||||
c.table.used_fns = walker.used_fns
|
||||
//
|
||||
c.table.used_fns['term.can_show_color_on_stdin'] = true
|
||||
c.table.used_fns['term.can_show_color_on_stdout'] = true
|
||||
c.table.used_fns['term.can_show_color_on_stderr'] = true
|
||||
//
|
||||
c.table.used_fns['main.can_use_relative_paths'] = true
|
||||
//
|
||||
// eprintln('>>> c.table.used_fns: $c.table.used_fns')
|
||||
util.timing_measure(@METHOD)
|
||||
c.table.used_consts = walker.used_consts
|
||||
|
||||
// println(walker.used_fns)
|
||||
// c.walk(ast_files)
|
||||
$if trace_skip_unused ? {
|
||||
eprintln('>> c.table.used_fns: $c.table.used_fns.keys()')
|
||||
eprintln('>> c.table.used_consts: $c.table.used_consts.keys()')
|
||||
eprintln('>> walker.n_maps: $walker.n_maps')
|
||||
}
|
||||
util.timing_measure(@METHOD)
|
||||
}
|
||||
|
|
|
@ -8,25 +8,61 @@ import v.ast
|
|||
|
||||
pub struct Walker {
|
||||
pub mut:
|
||||
used_fns map[string]bool // used_fns['println'] == true
|
||||
used_fns map[string]bool // used_fns['println'] == true
|
||||
used_consts map[string]bool // used_consts['os.args'] == true
|
||||
n_maps int
|
||||
n_asserts int
|
||||
mut:
|
||||
files []ast.File
|
||||
files []ast.File
|
||||
all_fns map[string]ast.FnDecl
|
||||
all_consts map[string]ast.ConstField
|
||||
}
|
||||
|
||||
/*
|
||||
fn (mut w Walker) walk_files(ast_files []ast.File) {
|
||||
t := time.ticks()
|
||||
*/
|
||||
pub fn (mut w Walker) mark_fn_as_used(fkey string) {
|
||||
$if trace_skip_unused_marked ? {
|
||||
eprintln(' fn > |$fkey|')
|
||||
}
|
||||
w.used_fns[fkey] = true
|
||||
}
|
||||
|
||||
pub fn (mut w Walker) mark_const_as_used(ckey string) {
|
||||
$if trace_skip_unused_marked ? {
|
||||
eprintln(' const > |$ckey|')
|
||||
}
|
||||
w.used_consts[ckey] = true
|
||||
cfield := w.all_consts[ckey] or { return }
|
||||
w.expr(cfield.expr)
|
||||
}
|
||||
|
||||
pub fn (mut w Walker) mark_root_fns(all_fn_root_names []string) {
|
||||
for fn_name in all_fn_root_names {
|
||||
if fn_name !in w.used_fns {
|
||||
$if trace_skip_unused_roots ? {
|
||||
println('>>>> $fn_name uses: ')
|
||||
}
|
||||
w.fn_decl(mut w.all_fns[fn_name])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn (mut w Walker) stmt(node ast.Stmt) {
|
||||
match mut node {
|
||||
ast.AssertStmt {
|
||||
w.expr(node.expr)
|
||||
w.n_asserts++
|
||||
}
|
||||
ast.AssignStmt {
|
||||
for l in node.left {
|
||||
w.expr(l)
|
||||
}
|
||||
for r in node.right {
|
||||
w.expr(r)
|
||||
}
|
||||
w.exprs(node.left)
|
||||
w.exprs(node.right)
|
||||
}
|
||||
ast.Block {
|
||||
w.stmts(node.stmts)
|
||||
}
|
||||
ast.CompFor {
|
||||
w.stmts(node.stmts)
|
||||
}
|
||||
ast.ConstDecl {
|
||||
w.const_fields(node.fields)
|
||||
}
|
||||
ast.ExprStmt {
|
||||
w.expr(node.expr)
|
||||
|
@ -34,92 +70,295 @@ pub fn (mut w Walker) stmt(node ast.Stmt) {
|
|||
ast.FnDecl {
|
||||
w.fn_decl(mut node)
|
||||
}
|
||||
ast.ForCStmt {
|
||||
w.expr(node.cond)
|
||||
w.stmt(node.inc)
|
||||
w.stmts(node.stmts)
|
||||
}
|
||||
ast.ForInStmt {
|
||||
w.expr(node.cond)
|
||||
w.expr(node.high)
|
||||
w.stmts(node.stmts)
|
||||
}
|
||||
ast.ForStmt {
|
||||
w.expr(node.cond)
|
||||
for stmt in node.stmts {
|
||||
w.stmt(stmt)
|
||||
w.stmts(node.stmts)
|
||||
}
|
||||
ast.GoStmt {
|
||||
w.expr(node.call_expr)
|
||||
}
|
||||
ast.Return {
|
||||
w.exprs(node.exprs)
|
||||
}
|
||||
ast.SqlStmt {
|
||||
w.expr(node.db_expr)
|
||||
w.expr(node.where_expr)
|
||||
w.exprs(node.update_exprs)
|
||||
}
|
||||
ast.StructDecl {
|
||||
w.struct_fields(node.fields)
|
||||
}
|
||||
ast.DeferStmt {
|
||||
w.stmts(node.stmts)
|
||||
}
|
||||
ast.GlobalDecl {
|
||||
for gf in node.fields {
|
||||
if gf.has_expr {
|
||||
w.expr(gf.expr)
|
||||
}
|
||||
}
|
||||
}
|
||||
else {}
|
||||
ast.BranchStmt {}
|
||||
ast.EnumDecl {}
|
||||
ast.GotoLabel {}
|
||||
ast.GotoStmt {}
|
||||
ast.HashStmt {}
|
||||
ast.Import {}
|
||||
ast.InterfaceDecl {}
|
||||
ast.Module {}
|
||||
ast.TypeDecl {}
|
||||
}
|
||||
}
|
||||
|
||||
fn (mut w Walker) defer_stmts(stmts []ast.DeferStmt) {
|
||||
for stmt in stmts {
|
||||
w.stmts(stmt.stmts)
|
||||
}
|
||||
}
|
||||
|
||||
fn (mut w Walker) stmts(stmts []ast.Stmt) {
|
||||
for stmt in stmts {
|
||||
w.stmt(stmt)
|
||||
}
|
||||
}
|
||||
|
||||
fn (mut w Walker) exprs(exprs []ast.Expr) {
|
||||
for expr in exprs {
|
||||
w.expr(expr)
|
||||
}
|
||||
}
|
||||
|
||||
fn (mut w Walker) expr(node ast.Expr) {
|
||||
match mut node {
|
||||
ast.AnonFn {
|
||||
w.fn_decl(mut node.decl)
|
||||
}
|
||||
ast.Assoc {
|
||||
w.exprs(node.exprs)
|
||||
}
|
||||
ast.ArrayInit {
|
||||
w.expr(node.len_expr)
|
||||
w.expr(node.cap_expr)
|
||||
w.expr(node.default_expr)
|
||||
w.exprs(node.exprs)
|
||||
}
|
||||
ast.ArrayDecompose {
|
||||
w.expr(node.expr)
|
||||
}
|
||||
ast.CallExpr {
|
||||
w.call_expr(mut node)
|
||||
}
|
||||
ast.CastExpr {
|
||||
w.expr(node.expr)
|
||||
w.expr(node.arg)
|
||||
}
|
||||
ast.ChanInit {
|
||||
w.expr(node.cap_expr)
|
||||
}
|
||||
ast.ConcatExpr {
|
||||
w.exprs(node.vals)
|
||||
}
|
||||
ast.ComptimeSelector {
|
||||
w.expr(node.left)
|
||||
w.expr(node.field_expr)
|
||||
}
|
||||
ast.ComptimeCall {
|
||||
w.expr(node.left)
|
||||
if node.is_vweb {
|
||||
w.stmts(node.vweb_tmpl.stmts)
|
||||
}
|
||||
}
|
||||
ast.GoExpr {
|
||||
w.expr(node.go_stmt.call_expr)
|
||||
}
|
||||
ast.IndexExpr {
|
||||
w.expr(node.left)
|
||||
w.expr(node.index)
|
||||
w.or_block(node.or_expr)
|
||||
}
|
||||
ast.InfixExpr {
|
||||
w.expr(node.left)
|
||||
w.expr(node.right)
|
||||
w.or_block(node.or_block)
|
||||
}
|
||||
ast.IfGuardExpr {
|
||||
w.expr(node.expr)
|
||||
}
|
||||
ast.IfExpr {
|
||||
w.expr(node.left)
|
||||
for b in node.branches {
|
||||
w.expr(b.cond)
|
||||
for stmt in b.stmts {
|
||||
w.stmt(stmt)
|
||||
}
|
||||
w.stmts(b.stmts)
|
||||
}
|
||||
}
|
||||
ast.Ident {
|
||||
if node.kind == .constant {
|
||||
w.mark_const_as_used(node.name)
|
||||
}
|
||||
}
|
||||
ast.Likely {
|
||||
w.expr(node.expr)
|
||||
}
|
||||
ast.MapInit {
|
||||
w.exprs(node.keys)
|
||||
w.exprs(node.vals)
|
||||
w.n_maps++
|
||||
}
|
||||
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)
|
||||
}
|
||||
w.exprs(b.exprs)
|
||||
w.stmts(b.stmts)
|
||||
}
|
||||
}
|
||||
else {}
|
||||
ast.None {
|
||||
w.mark_fn_as_used('opt_none')
|
||||
}
|
||||
ast.ParExpr {
|
||||
w.expr(node.expr)
|
||||
}
|
||||
ast.PrefixExpr {
|
||||
w.expr(node.right)
|
||||
}
|
||||
ast.PostfixExpr {
|
||||
w.expr(node.expr)
|
||||
}
|
||||
ast.RangeExpr {
|
||||
if node.has_low {
|
||||
w.expr(node.low)
|
||||
}
|
||||
if node.has_high {
|
||||
w.expr(node.high)
|
||||
}
|
||||
}
|
||||
ast.SizeOf {
|
||||
w.expr(node.expr)
|
||||
}
|
||||
ast.StringInterLiteral {
|
||||
w.exprs(node.exprs)
|
||||
}
|
||||
ast.SelectorExpr {
|
||||
w.expr(node.expr)
|
||||
}
|
||||
ast.SqlExpr {
|
||||
w.expr(node.db_expr)
|
||||
w.expr(node.offset_expr)
|
||||
w.expr(node.order_expr)
|
||||
w.expr(node.limit_expr)
|
||||
w.expr(node.where_expr)
|
||||
}
|
||||
ast.StructInit {
|
||||
// eprintln('>>>> ast.StructInit: $node')
|
||||
w.expr(node.update_expr)
|
||||
for sif in node.fields {
|
||||
w.expr(sif.expr)
|
||||
}
|
||||
for sie in node.embeds {
|
||||
w.expr(sie.expr)
|
||||
}
|
||||
}
|
||||
ast.TypeOf {
|
||||
w.expr(node.expr)
|
||||
}
|
||||
///
|
||||
ast.AsCast {
|
||||
w.expr(node.expr)
|
||||
}
|
||||
ast.AtExpr {}
|
||||
ast.BoolLiteral {}
|
||||
ast.FloatLiteral {}
|
||||
ast.CharLiteral {}
|
||||
ast.IntegerLiteral {}
|
||||
ast.StringLiteral {}
|
||||
ast.CTempVar {
|
||||
w.expr(node.orig)
|
||||
}
|
||||
ast.Comment {}
|
||||
ast.EnumVal {}
|
||||
ast.LockExpr {
|
||||
w.stmts(node.stmts)
|
||||
}
|
||||
ast.OffsetOf {}
|
||||
ast.OrExpr {
|
||||
w.or_block(node)
|
||||
}
|
||||
ast.SelectExpr {
|
||||
for branch in node.branches {
|
||||
w.stmt(branch.stmt)
|
||||
w.stmts(branch.stmts)
|
||||
}
|
||||
}
|
||||
ast.Type {}
|
||||
ast.UnsafeExpr {
|
||||
w.expr(node.expr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn (mut w Walker) fn_decl(mut node ast.FnDecl) {
|
||||
if node.language == .c {
|
||||
return
|
||||
}
|
||||
fkey := if node.is_method { '${int(node.receiver.typ)}.$node.name' } else { node.name }
|
||||
if w.used_fns[fkey] {
|
||||
// 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
|
||||
}
|
||||
w.used_fns[fkey] = true
|
||||
for stmt in node.stmts {
|
||||
w.stmt(stmt)
|
||||
}
|
||||
w.mark_fn_as_used(fkey)
|
||||
w.stmts(node.stmts)
|
||||
w.defer_stmts(node.defer_stmts)
|
||||
}
|
||||
|
||||
pub fn (mut w Walker) call_expr(mut node ast.CallExpr) {
|
||||
if node.language == .c {
|
||||
return
|
||||
}
|
||||
w.expr(node.left)
|
||||
for arg in node.args {
|
||||
w.expr(arg.expr)
|
||||
}
|
||||
w.or_block(node.or_block)
|
||||
//
|
||||
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
|
||||
}
|
||||
}
|
||||
stmt := w.all_fns[fn_name] or { return }
|
||||
if stmt.name == node.name {
|
||||
if !node.is_method || (node.receiver_type == stmt.receiver.typ) {
|
||||
w.mark_fn_as_used(fn_name)
|
||||
w.stmts(stmt.stmts)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn (mut w Walker) struct_fields(sfields []ast.StructField) {
|
||||
for sf in sfields {
|
||||
if sf.has_default_expr {
|
||||
w.expr(sf.default_expr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn (mut w Walker) const_fields(cfields []ast.ConstField) {
|
||||
for cf in cfields {
|
||||
w.expr(cf.expr)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn (mut w Walker) or_block(node ast.OrExpr) {
|
||||
if node.kind == .block {
|
||||
w.stmts(node.stmts)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4725,6 +4725,15 @@ fn (mut g Gen) const_decl(node ast.ConstDecl) {
|
|||
g.inside_const = false
|
||||
}
|
||||
for field in node.fields {
|
||||
if g.pref.skip_unused {
|
||||
if field.name !in g.table.used_consts {
|
||||
$if trace_skip_unused_consts ? {
|
||||
eprintln('>> skipping unused const name: $field.name')
|
||||
}
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
name := c_name(field.name)
|
||||
// TODO hack. Cut the generated value and paste it into definitions.
|
||||
pos := g.out.len
|
||||
|
|
|
@ -30,7 +30,7 @@ fn (mut g Gen) gen_fn_decl(node ast.FnDecl, skip bool) {
|
|||
if g.pref.skip_unused {
|
||||
fkey := if node.is_method { '${int(node.receiver.typ)}.$node.name' } else { node.name }
|
||||
is_used_by_main := g.table.used_fns[fkey]
|
||||
$if trace_skip_unused ? {
|
||||
$if trace_skip_unused_fns ? {
|
||||
println('> is_used_by_main: $is_used_by_main | node.name: $node.name | fkey: $fkey | node.is_method: $node.is_method')
|
||||
}
|
||||
if !is_used_by_main {
|
||||
|
|
|
@ -20,6 +20,7 @@ pub mut:
|
|||
cmod_prefix string // needed for table.type_to_str(Type) while vfmt; contains `os.`
|
||||
is_fmt bool
|
||||
used_fns map[string]bool // filled in by the checker, when pref.skip_unused = true;
|
||||
used_consts map[string]bool // filled in by the checker, when pref.skip_unused = true;
|
||||
}
|
||||
|
||||
pub struct Fn {
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
fn test_abc() {
|
||||
assert 'abc' == 'abc'
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
vlib/v/tests/skip_unused/assert_works_test.vv:2: fn test_abc
|
||||
> assert 'abc' == 'xyz'
|
||||
Left value: abc
|
||||
Right value: xyz
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
vlib/v/tests/skip_unused/assert_works_test.vv:2: fn test_abc
|
||||
> assert 'abc' == 'xyz'
|
||||
Left value: abc
|
||||
Right value: xyz
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
fn test_abc() {
|
||||
assert 'abc' == 'xyz'
|
||||
}
|
Loading…
Reference in New Issue