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
|
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
|
imported_symbols map[string]string // used for `import {symbol}`, it maps symbol => module.symbol
|
||||||
errors []errors.Error // all the checker errors in the file
|
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
|
generic_fns []&FnDecl
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -287,7 +287,8 @@ fn (b &Builder) show_total_warns_and_errors_stats() {
|
||||||
if b.pref.is_stats {
|
if b.pref.is_stats {
|
||||||
estring := util.bold(b.checker.nr_errors.str())
|
estring := util.bold(b.checker.nr_errors.str())
|
||||||
wstring := util.bold(b.checker.nr_warnings.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 {
|
if b.pref.is_verbose && b.checker.nr_warnings > 1 {
|
||||||
println('$b.checker.nr_warnings warnings')
|
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 {
|
if b.checker.nr_warnings > 0 && !b.pref.skip_warnings {
|
||||||
for i, err in b.checker.warnings {
|
for i, err in b.checker.warnings {
|
||||||
kind := if b.pref.is_verbose {
|
kind := if b.pref.is_verbose {
|
||||||
|
|
|
@ -4,6 +4,7 @@ module checker
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import strings
|
import strings
|
||||||
|
import time
|
||||||
import v.ast
|
import v.ast
|
||||||
import v.vmod
|
import v.vmod
|
||||||
import v.table
|
import v.table
|
||||||
|
@ -36,8 +37,10 @@ pub mut:
|
||||||
file &ast.File = 0
|
file &ast.File = 0
|
||||||
nr_errors int
|
nr_errors int
|
||||||
nr_warnings int
|
nr_warnings int
|
||||||
|
nr_notices int
|
||||||
errors []errors.Error
|
errors []errors.Error
|
||||||
warnings []errors.Warning
|
warnings []errors.Warning
|
||||||
|
notices []errors.Notice
|
||||||
error_lines []int // to avoid printing multiple errors for the same line
|
error_lines []int // to avoid printing multiple errors for the same line
|
||||||
expected_type table.Type
|
expected_type table.Type
|
||||||
expected_or_type table.Type // fn() or { 'this type' } eg. string. expected or block 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)
|
call_expr.pos)
|
||||||
}
|
}
|
||||||
if !c.cur_fn.is_deprecated && method.is_deprecated {
|
if !c.cur_fn.is_deprecated && method.is_deprecated {
|
||||||
mut deprecation_message := 'method `${left_type_sym.name}.$method.name` has been deprecated'
|
c.deprecate_fnmethod('method', '${left_type_sym.name}.$method.name', method,
|
||||||
for attr in method.attrs {
|
call_expr)
|
||||||
if attr.name == 'deprecated' && attr.arg != '' {
|
|
||||||
deprecation_message += '; $attr.arg'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
c.warn(deprecation_message, call_expr.pos)
|
|
||||||
}
|
}
|
||||||
// TODO: typ optimize.. this node can get processed more than once
|
// TODO: typ optimize.. this node can get processed more than once
|
||||||
if call_expr.expected_arg_types.len == 0 {
|
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)
|
c.error('function `$f.name` is private', call_expr.pos)
|
||||||
}
|
}
|
||||||
if !c.cur_fn.is_deprecated && f.is_deprecated {
|
if !c.cur_fn.is_deprecated && f.is_deprecated {
|
||||||
mut deprecation_message := 'function `$f.name` has been deprecated'
|
c.deprecate_fnmethod('function', f.name, f, call_expr)
|
||||||
for d in f.attrs {
|
|
||||||
if d.name == 'deprecated' && d.arg != '' {
|
|
||||||
deprecation_message += '; $d.arg'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
c.warn(deprecation_message, call_expr.pos)
|
|
||||||
}
|
}
|
||||||
if f.is_unsafe && !c.inside_unsafe
|
if f.is_unsafe && !c.inside_unsafe
|
||||||
&& (f.language != .c || (f.name[2] in [`m`, `s`] && f.mod == 'builtin')) {
|
&& (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
|
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 {
|
fn (mut c Checker) type_implements(typ table.Type, inter_typ table.Type, pos token.Position) bool {
|
||||||
$if debug_interface_type_implements ? {
|
$if debug_interface_type_implements ? {
|
||||||
eprintln('> type_implements typ: $typ.debug() | inter_typ: $inter_typ.debug()')
|
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.warnings << c2.warnings
|
||||||
c.errors << c2.errors
|
c.errors << c2.errors
|
||||||
|
c.notices << c2.notices
|
||||||
c.nr_warnings += c2.nr_warnings
|
c.nr_warnings += c2.nr_warnings
|
||||||
c.nr_errors += c2.nr_errors
|
c.nr_errors += c2.nr_errors
|
||||||
|
c.nr_notices += c2.nr_notices
|
||||||
}
|
}
|
||||||
if node.method_name == 'html' {
|
if node.method_name == 'html' {
|
||||||
rtyp := c.table.find_type_idx('vweb.Result')
|
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) {
|
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
|
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) {
|
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
|
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) {
|
fn (mut c Checker) warn_or_error(message string, pos token.Position, warn bool) {
|
||||||
// add backtrace to issue struct, how?
|
// add backtrace to issue struct, how?
|
||||||
// if c.pref.is_verbose {
|
// 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
|
pos token.Position
|
||||||
reporter Reporter
|
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()
|
main_str = content.trim_space()
|
||||||
msg = ''
|
msg = ''
|
||||||
}
|
}
|
||||||
// p.trace('a.v', 'kind: ${kind:-10s} | pos: ${pos:-45s} | hash: $val')
|
|
||||||
return ast.HashStmt{
|
return ast.HashStmt{
|
||||||
mod: p.mod
|
mod: p.mod
|
||||||
source_file: p.file_name
|
source_file: p.file_name
|
||||||
|
|
|
@ -64,6 +64,7 @@ mut:
|
||||||
expecting_type bool // `is Type`, expecting type
|
expecting_type bool // `is Type`, expecting type
|
||||||
errors []errors.Error
|
errors []errors.Error
|
||||||
warnings []errors.Warning
|
warnings []errors.Warning
|
||||||
|
notices []errors.Notice
|
||||||
vet_errors []vet.Error
|
vet_errors []vet.Error
|
||||||
cur_fn_name string
|
cur_fn_name string
|
||||||
label_names []string
|
label_names []string
|
||||||
|
@ -1490,6 +1491,10 @@ pub fn (mut p Parser) warn(s string) {
|
||||||
p.warn_with_pos(s, p.tok.position())
|
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) {
|
pub fn (mut p Parser) error_with_pos(s string, pos token.Position) {
|
||||||
if p.pref.fatal_errors {
|
if p.pref.fatal_errors {
|
||||||
exit(1)
|
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) {
|
pub fn (mut p Parser) vet_error(msg string, line int, fix vet.FixKind) {
|
||||||
pos := token.Position{
|
pos := token.Position{
|
||||||
line_nr: line + 1
|
line_nr: line + 1
|
||||||
|
|
|
@ -51,6 +51,7 @@ pub mut:
|
||||||
pref &pref.Preferences
|
pref &pref.Preferences
|
||||||
errors []errors.Error
|
errors []errors.Error
|
||||||
warnings []errors.Warning
|
warnings []errors.Warning
|
||||||
|
notices []errors.Notice
|
||||||
vet_errors []vet.Error
|
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) {
|
pub fn (mut s Scanner) warn(msg string) {
|
||||||
if s.pref.warns_are_errors {
|
if s.pref.warns_are_errors {
|
||||||
s.error(msg)
|
s.error(msg)
|
||||||
|
|
|
@ -61,9 +61,11 @@ fn color(kind string, msg string) string {
|
||||||
}
|
}
|
||||||
if kind.contains('error') {
|
if kind.contains('error') {
|
||||||
return term.red(msg)
|
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'
|
// formatted_error - `kind` may be 'error' or 'warn'
|
||||||
|
|
Loading…
Reference in New Issue