v: support compiler notices. Use them for `[deprecated_after: '2021-05-01']` tags
Compiler notices are like warnings, with these differences: a) notices use a different color. b) notices use a different label. c) notices do not prevent compilation with -prod. (warnings are converted to errors with -prod)pull/9425/head
parent
c76c69ec35
commit
a00c80b98f
|
@ -516,7 +516,8 @@ pub mut:
|
|||
embedded_files []EmbeddedFile // list of files to embed in the binary
|
||||
imported_symbols map[string]string // used for `import {symbol}`, it maps symbol => module.symbol
|
||||
errors []errors.Error // all the checker errors in the file
|
||||
warnings []errors.Warning // all the checker warings in the file
|
||||
warnings []errors.Warning // all the checker warnings in the file
|
||||
notices []errors.Notice // all the checker notices in the file
|
||||
generic_fns []&FnDecl
|
||||
}
|
||||
|
||||
|
|
|
@ -287,7 +287,8 @@ fn (b &Builder) show_total_warns_and_errors_stats() {
|
|||
if b.pref.is_stats {
|
||||
estring := util.bold(b.checker.nr_errors.str())
|
||||
wstring := util.bold(b.checker.nr_warnings.str())
|
||||
println('checker summary: $estring V errors, $wstring V warnings')
|
||||
nstring := util.bold(b.checker.nr_notices.str())
|
||||
println('checker summary: $estring V errors, $wstring V warnings, $nstring V notices')
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -304,6 +305,26 @@ fn (b &Builder) print_warnings_and_errors() {
|
|||
if b.pref.is_verbose && b.checker.nr_warnings > 1 {
|
||||
println('$b.checker.nr_warnings warnings')
|
||||
}
|
||||
if b.pref.is_verbose && b.checker.nr_notices > 1 {
|
||||
println('$b.checker.nr_notices notices')
|
||||
}
|
||||
if b.checker.nr_notices > 0 && !b.pref.skip_warnings {
|
||||
for i, err in b.checker.notices {
|
||||
kind := if b.pref.is_verbose {
|
||||
'$err.reporter notice #$b.checker.nr_notices:'
|
||||
} else {
|
||||
'notice:'
|
||||
}
|
||||
ferror := util.formatted_error(kind, err.message, err.file_path, err.pos)
|
||||
eprintln(ferror)
|
||||
if err.details.len > 0 {
|
||||
eprintln('Details: $err.details')
|
||||
}
|
||||
if i > b.max_nr_errors {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
if b.checker.nr_warnings > 0 && !b.pref.skip_warnings {
|
||||
for i, err in b.checker.warnings {
|
||||
kind := if b.pref.is_verbose {
|
||||
|
|
|
@ -4,6 +4,7 @@ module checker
|
|||
|
||||
import os
|
||||
import strings
|
||||
import time
|
||||
import v.ast
|
||||
import v.vmod
|
||||
import v.table
|
||||
|
@ -36,8 +37,10 @@ pub mut:
|
|||
file &ast.File = 0
|
||||
nr_errors int
|
||||
nr_warnings int
|
||||
nr_notices int
|
||||
errors []errors.Error
|
||||
warnings []errors.Warning
|
||||
notices []errors.Notice
|
||||
error_lines []int // to avoid printing multiple errors for the same line
|
||||
expected_type table.Type
|
||||
expected_or_type table.Type // fn() or { 'this type' } eg. string. expected or block type
|
||||
|
@ -1566,13 +1569,8 @@ pub fn (mut c Checker) call_method(mut call_expr ast.CallExpr) table.Type {
|
|||
call_expr.pos)
|
||||
}
|
||||
if !c.cur_fn.is_deprecated && method.is_deprecated {
|
||||
mut deprecation_message := 'method `${left_type_sym.name}.$method.name` has been deprecated'
|
||||
for attr in method.attrs {
|
||||
if attr.name == 'deprecated' && attr.arg != '' {
|
||||
deprecation_message += '; $attr.arg'
|
||||
}
|
||||
}
|
||||
c.warn(deprecation_message, call_expr.pos)
|
||||
c.deprecate_fnmethod('method', '${left_type_sym.name}.$method.name', method,
|
||||
call_expr)
|
||||
}
|
||||
// TODO: typ optimize.. this node can get processed more than once
|
||||
if call_expr.expected_arg_types.len == 0 {
|
||||
|
@ -1924,13 +1922,7 @@ pub fn (mut c Checker) call_fn(mut call_expr ast.CallExpr) table.Type {
|
|||
c.error('function `$f.name` is private', call_expr.pos)
|
||||
}
|
||||
if !c.cur_fn.is_deprecated && f.is_deprecated {
|
||||
mut deprecation_message := 'function `$f.name` has been deprecated'
|
||||
for d in f.attrs {
|
||||
if d.name == 'deprecated' && d.arg != '' {
|
||||
deprecation_message += '; $d.arg'
|
||||
}
|
||||
}
|
||||
c.warn(deprecation_message, call_expr.pos)
|
||||
c.deprecate_fnmethod('function', f.name, f, call_expr)
|
||||
}
|
||||
if f.is_unsafe && !c.inside_unsafe
|
||||
&& (f.language != .c || (f.name[2] in [`m`, `s`] && f.mod == 'builtin')) {
|
||||
|
@ -2103,6 +2095,41 @@ pub fn (mut c Checker) call_fn(mut call_expr ast.CallExpr) table.Type {
|
|||
return f.return_type
|
||||
}
|
||||
|
||||
fn (mut c Checker) deprecate_fnmethod(kind string, name string, the_fn table.Fn, call_expr ast.CallExpr) {
|
||||
start_message := '$kind `$name`'
|
||||
mut deprecation_message := ''
|
||||
now := time.now()
|
||||
mut after_time := now
|
||||
for attr in the_fn.attrs {
|
||||
if attr.name == 'deprecated' && attr.arg != '' {
|
||||
deprecation_message = attr.arg
|
||||
}
|
||||
if attr.name == 'deprecated_after' && attr.arg != '' {
|
||||
after_time = time.parse_iso8601(attr.arg) or {
|
||||
c.error('invalid time format', attr.pos)
|
||||
time.now()
|
||||
}
|
||||
}
|
||||
}
|
||||
if after_time < now {
|
||||
c.warn(semicolonize('$start_message has been deprecated since $after_time.ymmdd()',
|
||||
deprecation_message), call_expr.pos)
|
||||
} else if after_time == now {
|
||||
c.warn(semicolonize('$start_message has been deprecated', deprecation_message),
|
||||
call_expr.pos)
|
||||
} else {
|
||||
c.note(semicolonize('$start_message will be deprecated after $after_time.ymmdd()',
|
||||
deprecation_message), call_expr.pos)
|
||||
}
|
||||
}
|
||||
|
||||
fn semicolonize(main string, details string) string {
|
||||
if details == '' {
|
||||
return main
|
||||
}
|
||||
return '$main; $details'
|
||||
}
|
||||
|
||||
fn (mut c Checker) type_implements(typ table.Type, inter_typ table.Type, pos token.Position) bool {
|
||||
$if debug_interface_type_implements ? {
|
||||
eprintln('> type_implements typ: $typ.debug() | inter_typ: $inter_typ.debug()')
|
||||
|
@ -4291,8 +4318,10 @@ fn (mut c Checker) comptime_call(mut node ast.ComptimeCall) table.Type {
|
|||
}
|
||||
c.warnings << c2.warnings
|
||||
c.errors << c2.errors
|
||||
c.notices << c2.notices
|
||||
c.nr_warnings += c2.nr_warnings
|
||||
c.nr_errors += c2.nr_errors
|
||||
c.nr_notices += c2.nr_notices
|
||||
}
|
||||
if node.method_name == 'html' {
|
||||
rtyp := c.table.find_type_idx('vweb.Result')
|
||||
|
@ -5853,7 +5882,7 @@ pub fn (mut c Checker) add_error_detail(s string) {
|
|||
|
||||
pub fn (mut c Checker) warn(s string, pos token.Position) {
|
||||
allow_warnings := !(c.pref.is_prod || c.pref.warns_are_errors) // allow warnings only in dev builds
|
||||
c.warn_or_error(s, pos, allow_warnings) // allow warnings only in dev builds
|
||||
c.warn_or_error(s, pos, allow_warnings)
|
||||
}
|
||||
|
||||
pub fn (mut c Checker) error(message string, pos token.Position) {
|
||||
|
@ -5897,6 +5926,24 @@ fn (c &Checker) check_struct_signature(from table.Struct, to table.Struct) bool
|
|||
return true
|
||||
}
|
||||
|
||||
pub fn (mut c Checker) note(message string, pos token.Position) {
|
||||
mut details := ''
|
||||
if c.error_details.len > 0 {
|
||||
details = c.error_details.join('\n')
|
||||
c.error_details = []
|
||||
}
|
||||
wrn := errors.Notice{
|
||||
reporter: errors.Reporter.checker
|
||||
pos: pos
|
||||
file_path: c.file.path
|
||||
message: message
|
||||
details: details
|
||||
}
|
||||
c.file.notices << wrn
|
||||
c.notices << wrn
|
||||
c.nr_notices++
|
||||
}
|
||||
|
||||
fn (mut c Checker) warn_or_error(message string, pos token.Position, warn bool) {
|
||||
// add backtrace to issue struct, how?
|
||||
// if c.pref.is_verbose {
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
vlib/v/checker/tests/deprecations.vv:54:2: notice: function `future` will be deprecated after 9999-12-30; custom message 4
|
||||
52 |
|
||||
53 | fn main() {
|
||||
54 | future()
|
||||
| ~~~~~~~~
|
||||
55 | past()
|
||||
56 | simply_deprecated()
|
||||
vlib/v/checker/tests/deprecations.vv:60:4: notice: method `Abc.future` will be deprecated after 9999-11-01; custom message 1
|
||||
58 | //
|
||||
59 | a := Abc{}
|
||||
60 | a.future()
|
||||
| ~~~~~~~~
|
||||
61 | a.past()
|
||||
62 | a.simply_deprecated()
|
||||
vlib/v/checker/tests/deprecations.vv:55:2: error: function `past` has been deprecated since 2021-03-01; custom message 5
|
||||
53 | fn main() {
|
||||
54 | future()
|
||||
55 | past()
|
||||
| ~~~~~~
|
||||
56 | simply_deprecated()
|
||||
57 | just_deprecated()
|
||||
vlib/v/checker/tests/deprecations.vv:56:2: error: function `simply_deprecated` has been deprecated; custom message 6
|
||||
54 | future()
|
||||
55 | past()
|
||||
56 | simply_deprecated()
|
||||
| ~~~~~~~~~~~~~~~~~~~
|
||||
57 | just_deprecated()
|
||||
58 | //
|
||||
vlib/v/checker/tests/deprecations.vv:57:2: error: function `just_deprecated` has been deprecated
|
||||
55 | past()
|
||||
56 | simply_deprecated()
|
||||
57 | just_deprecated()
|
||||
| ~~~~~~~~~~~~~~~~~
|
||||
58 | //
|
||||
59 | a := Abc{}
|
||||
vlib/v/checker/tests/deprecations.vv:61:4: error: method `Abc.past` has been deprecated since 2021-03-01; custom message 2
|
||||
59 | a := Abc{}
|
||||
60 | a.future()
|
||||
61 | a.past()
|
||||
| ~~~~~~
|
||||
62 | a.simply_deprecated()
|
||||
63 | }
|
||||
vlib/v/checker/tests/deprecations.vv:62:4: error: method `Abc.simply_deprecated` has been deprecated; custom message 3
|
||||
60 | a.future()
|
||||
61 | a.past()
|
||||
62 | a.simply_deprecated()
|
||||
| ~~~~~~~~~~~~~~~~~~~
|
||||
63 | }
|
|
@ -0,0 +1,63 @@
|
|||
// methods using [deprecated_after]:
|
||||
struct Abc {
|
||||
x int
|
||||
}
|
||||
|
||||
fn (a Abc) str() string {
|
||||
return 'Abc { x: $a.x }'
|
||||
}
|
||||
|
||||
[deprecated: 'custom message 1']
|
||||
[deprecated_after: '9999-11-01']
|
||||
fn (a Abc) future() {
|
||||
dump(@METHOD)
|
||||
dump(a)
|
||||
}
|
||||
|
||||
[deprecated: 'custom message 2']
|
||||
[deprecated_after: '2021-03-01']
|
||||
fn (a Abc) past() {
|
||||
dump(@METHOD)
|
||||
dump(a)
|
||||
}
|
||||
|
||||
[deprecated: 'custom message 3']
|
||||
fn (a Abc) simply_deprecated() {
|
||||
dump(@METHOD)
|
||||
dump(a)
|
||||
}
|
||||
|
||||
// functions using [deprecated_after]:
|
||||
[deprecated: 'custom message 4']
|
||||
[deprecated_after: '9999-12-30']
|
||||
fn future() {
|
||||
dump(@FN)
|
||||
}
|
||||
|
||||
[deprecated: 'custom message 5']
|
||||
[deprecated_after: '2021-03-01']
|
||||
fn past() {
|
||||
dump(@FN)
|
||||
}
|
||||
|
||||
[deprecated: 'custom message 6']
|
||||
fn simply_deprecated() {
|
||||
dump(@FN)
|
||||
}
|
||||
|
||||
[deprecated]
|
||||
fn just_deprecated() {
|
||||
dump(@FN)
|
||||
}
|
||||
|
||||
fn main() {
|
||||
future()
|
||||
past()
|
||||
simply_deprecated()
|
||||
just_deprecated()
|
||||
//
|
||||
a := Abc{}
|
||||
a.future()
|
||||
a.past()
|
||||
a.simply_deprecated()
|
||||
}
|
|
@ -27,3 +27,12 @@ pub:
|
|||
pos token.Position
|
||||
reporter Reporter
|
||||
}
|
||||
|
||||
pub struct Notice {
|
||||
pub:
|
||||
message string
|
||||
details string
|
||||
file_path string
|
||||
pos token.Position
|
||||
reporter Reporter
|
||||
}
|
||||
|
|
|
@ -29,7 +29,6 @@ fn (mut p Parser) hash() ast.HashStmt {
|
|||
main_str = content.trim_space()
|
||||
msg = ''
|
||||
}
|
||||
// p.trace('a.v', 'kind: ${kind:-10s} | pos: ${pos:-45s} | hash: $val')
|
||||
return ast.HashStmt{
|
||||
mod: p.mod
|
||||
source_file: p.file_name
|
||||
|
|
|
@ -64,6 +64,7 @@ mut:
|
|||
expecting_type bool // `is Type`, expecting type
|
||||
errors []errors.Error
|
||||
warnings []errors.Warning
|
||||
notices []errors.Notice
|
||||
vet_errors []vet.Error
|
||||
cur_fn_name string
|
||||
label_names []string
|
||||
|
@ -1490,6 +1491,10 @@ pub fn (mut p Parser) warn(s string) {
|
|||
p.warn_with_pos(s, p.tok.position())
|
||||
}
|
||||
|
||||
pub fn (mut p Parser) note(s string) {
|
||||
p.note_with_pos(s, p.tok.position())
|
||||
}
|
||||
|
||||
pub fn (mut p Parser) error_with_pos(s string, pos token.Position) {
|
||||
if p.pref.fatal_errors {
|
||||
exit(1)
|
||||
|
@ -1566,6 +1571,23 @@ pub fn (mut p Parser) warn_with_pos(s string, pos token.Position) {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn (mut p Parser) note_with_pos(s string, pos token.Position) {
|
||||
if p.pref.skip_warnings {
|
||||
return
|
||||
}
|
||||
if p.pref.output_mode == .stdout {
|
||||
ferror := util.formatted_error('notice:', s, p.file_name, pos)
|
||||
eprintln(ferror)
|
||||
} else {
|
||||
p.notices << errors.Notice{
|
||||
file_path: p.file_name
|
||||
pos: pos
|
||||
reporter: .parser
|
||||
message: s
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn (mut p Parser) vet_error(msg string, line int, fix vet.FixKind) {
|
||||
pos := token.Position{
|
||||
line_nr: line + 1
|
||||
|
|
|
@ -51,6 +51,7 @@ pub mut:
|
|||
pref &pref.Preferences
|
||||
errors []errors.Error
|
||||
warnings []errors.Warning
|
||||
notices []errors.Notice
|
||||
vet_errors []vet.Error
|
||||
}
|
||||
|
||||
|
@ -1305,6 +1306,23 @@ fn (mut s Scanner) inc_line_number() {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn (mut s Scanner) note(msg string) {
|
||||
pos := token.Position{
|
||||
line_nr: s.line_nr
|
||||
pos: s.pos
|
||||
}
|
||||
if s.pref.output_mode == .stdout {
|
||||
eprintln(util.formatted_error('notice:', msg, s.file_path, pos))
|
||||
} else {
|
||||
s.notices << errors.Notice{
|
||||
file_path: s.file_path
|
||||
pos: pos
|
||||
reporter: .scanner
|
||||
message: msg
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn (mut s Scanner) warn(msg string) {
|
||||
if s.pref.warns_are_errors {
|
||||
s.error(msg)
|
||||
|
|
|
@ -61,9 +61,11 @@ fn color(kind string, msg string) string {
|
|||
}
|
||||
if kind.contains('error') {
|
||||
return term.red(msg)
|
||||
} else {
|
||||
return term.magenta(msg)
|
||||
}
|
||||
if kind.contains('notice') {
|
||||
return term.yellow(msg)
|
||||
}
|
||||
return term.magenta(msg)
|
||||
}
|
||||
|
||||
// formatted_error - `kind` may be 'error' or 'warn'
|
||||
|
|
Loading…
Reference in New Issue