@@ -4,29 +4,36 @@ module main
44
55import v.ast
66import v.token
7+ import os
78import arrays
89
910// cutoffs
10- const indexexpr_cutoff = 10
11- const infixexpr_cutoff = 10
12- const selectorexpr_cutoff = 10
13- const callexpr_cutoff = 10
14- const stringinterliteral_cutoff = 10
15- const stringliteral_cutoff = 10
16- const ascast_cutoff = 10
17- const stringconcat_cutoff = 10
11+ const indexexpr_cutoff = os.getenv_opt ('VET_INDEXEXPR_CUTOFF' ) or { '10' }.int ()
12+ const infixexpr_cutoff = os.getenv_opt ('VET_INFIXEXPR_CUTOFF' ) or { '10' }.int ()
13+ const selectorexpr_cutoff = os.getenv_opt ('VET_SELECTOREXPR_CUTOFF' ) or { '10' }.int ()
14+ const callexpr_cutoff = os.getenv_opt ('VET_CALLEXPR_CUTOFF' ) or { '10' }.int ()
15+ const stringinterliteral_cutoff = os.getenv_opt ('STRINGINTERLITERAL_CUTOFF' ) or { '10' }.int ()
16+ const stringliteral_cutoff = os.getenv_opt ('STRINGLITERAL_CUTOFF' ) or { '10' }.int ()
17+ const ascast_cutoff = os.getenv_opt ('ASCAST_CUTOFF' ) or { '10' }.int ()
18+ const stringconcat_cutoff = os.getenv_opt ('STRINGCONCAT_CUTOFF' ) or { '10' }.int ()
19+
20+ // possibly inline fn cutoff
21+ const fns_call_cutoff = os.getenv_opt ('VET_FNS_CALL_CUTOFF' ) or { '10' }.int () // at least N calls
22+ const short_fns_cutoff = os.getenv_opt ('VET_SHORT_FNS_CUTOFF' ) or { '3' }.int () // lines
1823
1924// minimum size for string literals
20- const stringliteral_min_size = 20
25+ const stringliteral_min_size = os. getenv_opt ( 'VET_STRINGLITERAL_MIN_SIZE' ) or { '20' }. int ()
2126
2227// long functions cutoff
23- const long_fns_cutoff = 300
28+ const long_fns_cutoff = os. getenv_opt ( 'VET_LONG_FNS_CUTOFF' ) or { ' 300' }. int ()
2429
2530struct VetAnalyze {
2631mut :
27- repeated_expr_cutoff shared map [string ]int // repeated code cutoff
28- repeated_expr shared map [string ]map [string ]map [string ][]token.Pos // repeated exprs in fn scope
29- cur_fn ast.FnDecl // current fn declaration
32+ repeated_expr_cutoff shared map [string ]int // repeated code cutoff
33+ repeated_expr shared map [string ]map [string ]map [string ][]token.Pos // repeated exprs in fn scope
34+ potential_non_inlined shared map [string ]map [string ]token.Pos // fns might be inlined
35+ call_counter shared map [string ]int // fn call counter
36+ cur_fn ast.FnDecl // current fn declaration
3037}
3138
3239// stmt checks for repeated code in statements
@@ -80,9 +87,18 @@ fn (mut vt VetAnalyze) expr(vet &Vet, expr ast.Expr) {
8087 }
8188 ast.CallExpr {
8289 if expr.is_static_method || expr.is_method {
83- vt.save_expr (callexpr_cutoff, '${expr .left }.${expr .name }(${expr .args .map (it .str ()).join (', ')})' ,
90+ left_str := expr.left.str ()
91+ lock vt.call_counter {
92+ if vt.cur_fn.receiver.name == left_str {
93+ vt.call_counter['${int (vt .cur_fn .receiver .typ )}.${expr .name }' ]++
94+ }
95+ }
96+ vt.save_expr (callexpr_cutoff, '${left_str }.${expr .name }(${expr .args .map (it .str ()).join (', ')})' ,
8497 vet.file, expr.pos)
8598 } else {
99+ lock vt.call_counter {
100+ vt.call_counter[expr.name]++
101+ }
86102 vt.save_expr (callexpr_cutoff, '${expr .name }(${expr .args .map (it .str ()).join (', ')})' ,
87103 vet.file, expr.pos)
88104 }
@@ -104,18 +120,27 @@ fn (mut vt VetAnalyze) expr(vet &Vet, expr ast.Expr) {
104120
105121// long_or_empty_fns checks for long or empty functions
106122fn (mut vt VetAnalyze) long_or_empty_fns (mut vet Vet, fn_decl ast.FnDecl) {
107- nr_lines := if fn_decl.stmts.len == 0 {
108- 0
109- } else {
110- fn_decl.stmts.last ().pos.line_nr - fn_decl.pos.line_nr
111- }
123+ nr_lines := fn_decl.end_pos.line_nr - fn_decl.pos.line_nr - 2
112124 if nr_lines > long_fns_cutoff {
113125 vet.notice ('Long function - ${nr_lines } lines long.' , fn_decl.pos.line_nr, .long_fns)
114126 } else if nr_lines == 0 {
115127 vet.notice ('Empty function.' , fn_decl.pos.line_nr, .empty_fn)
116128 }
117129}
118130
131+ // potential_non_inlined checks for potential fns to be inlined
132+ fn (mut vt VetAnalyze) potential_non_inlined (mut vet Vet, fn_decl ast.FnDecl) {
133+ nr_lines := fn_decl.end_pos.line_nr - fn_decl.pos.line_nr - 2
134+ if nr_lines < short_fns_cutoff {
135+ attr := fn_decl.attrs.find_first ('inline' )
136+ if attr == none {
137+ lock vt.potential_non_inlined {
138+ vt.potential_non_inlined[fn_decl.fkey ()][vet.file] = fn_decl.pos
139+ }
140+ }
141+ }
142+ }
143+
119144// vet_fn_analysis reports repeated code by scope
120145fn (mut vt VetAnalyze) vet_repeated_code (mut vet Vet) {
121146 rlock vt.repeated_expr {
@@ -137,9 +162,26 @@ fn (mut vt VetAnalyze) vet_repeated_code(mut vet Vet) {
137162 }
138163}
139164
165+ // vet_inlining_fn reports possible fn to be inlined
166+ fn (mut vt VetAnalyze) vet_inlining_fn (mut vet Vet) {
167+ for fn_name, info in vt.potential_non_inlined {
168+ for file, pos in info {
169+ calls := vt.call_counter[fn_name] or { 0 }
170+ if calls < fns_call_cutoff {
171+ continue
172+ }
173+ vet.notice_with_file (file, '${fn_name .all_after ('.')} fn might be inlined (possibly called at least ${calls } times)' ,
174+ pos.line_nr, .inline_fn)
175+ }
176+ }
177+ }
178+
140179// vet_code_analyze performs code analysis
141180fn (mut vt Vet) vet_code_analyze () {
142181 if vt.opt.repeated_code {
143182 vt.analyze.vet_repeated_code (mut vt)
144183 }
184+ if vt.opt.fn_inlining {
185+ vt.analyze.vet_inlining_fn (mut vt)
186+ }
145187}
0 commit comments