parser,ast,checker: add support for `[deprecated: "use another module"] module obsolete`

pull/13294/head
Delyan Angelov 2022-01-27 11:50:26 +02:00
parent 3bd528b218
commit e1a2ab345d
No known key found for this signature in database
GPG Key ID: 66886C0F12D595ED
11 changed files with 87 additions and 12 deletions

View File

@ -4,6 +4,7 @@
[has_globals] [has_globals]
module ast module ast
import time
import v.cflag import v.cflag
import v.token import v.token
import v.util import v.util
@ -36,8 +37,10 @@ pub mut:
cur_fn &FnDecl = 0 // previously stored in Checker.cur_fn and Gen.cur_fn cur_fn &FnDecl = 0 // previously stored in Checker.cur_fn and Gen.cur_fn
cur_concrete_types []Type // current concrete types, e.g. <int, string> cur_concrete_types []Type // current concrete types, e.g. <int, string>
gostmts int // how many `go` statements there were in the parsed files. gostmts int // how many `go` statements there were in the parsed files.
enum_decls map[string]EnumDecl
// When table.gostmts > 0, __VTHREADS__ is defined, which can be checked with `$if threads {` // When table.gostmts > 0, __VTHREADS__ is defined, which can be checked with `$if threads {`
enum_decls map[string]EnumDecl
mdeprecated_msg map[string]string // module deprecation message
mdeprecated_after map[string]time.Time // module deprecation date
} }
// used by vls to avoid leaks // used by vls to avoid leaks
@ -301,6 +304,15 @@ pub fn (t &Table) known_fn(name string) bool {
return true return true
} }
pub fn (mut t Table) mark_module_as_deprecated(mname string, message string) {
t.mdeprecated_msg[mname] = message
t.mdeprecated_after[mname] = time.now()
}
pub fn (mut t Table) mark_module_as_deprecated_after(mname string, after_date string) {
t.mdeprecated_after[mname] = time.parse_iso8601(after_date) or { time.now() }
}
pub fn (mut t Table) register_fn(new_fn Fn) { pub fn (mut t Table) register_fn(new_fn Fn) {
t.fns[new_fn.name] = new_fn t.fns[new_fn.name] = new_fn
} }

View File

@ -3,6 +3,7 @@
module checker module checker
import os import os
import time
import v.ast import v.ast
import v.vmod import v.vmod
import v.token import v.token
@ -2240,6 +2241,11 @@ fn (mut c Checker) import_stmt(node ast.Import) {
} }
c.error('module `$node.mod` has no constant or function `$sym.name`', sym.pos) c.error('module `$node.mod` has no constant or function `$sym.name`', sym.pos)
} }
if after_time := c.table.mdeprecated_after[node.mod] {
now := time.now()
deprecation_message := c.table.mdeprecated_msg[node.mod]
c.deprecate('module', node.mod, deprecation_message, now, after_time, node.pos)
}
} }
// stmts should be used for processing normal statement lists (fn bodies, for loop bodies etc). // stmts should be used for processing normal statement lists (fn bodies, for loop bodies etc).

View File

@ -1439,7 +1439,6 @@ pub fn (mut c Checker) method_call(mut node ast.CallExpr) ast.Type {
} }
fn (mut c Checker) deprecate_fnmethod(kind string, name string, the_fn ast.Fn, node ast.CallExpr) { fn (mut c Checker) deprecate_fnmethod(kind string, name string, the_fn ast.Fn, node ast.CallExpr) {
start_message := '$kind `$name`'
mut deprecation_message := '' mut deprecation_message := ''
now := time.now() now := time.now()
mut after_time := now mut after_time := now
@ -1454,19 +1453,24 @@ fn (mut c Checker) deprecate_fnmethod(kind string, name string, the_fn ast.Fn, n
} }
} }
} }
c.deprecate(kind, name, deprecation_message, now, after_time, node.pos)
}
fn (mut c Checker) deprecate(kind string, name string, deprecation_message string, now time.Time, after_time time.Time, pos token.Pos) {
start_message := '$kind `$name`'
error_time := after_time.add_days(180) error_time := after_time.add_days(180)
if error_time < now { if error_time < now {
c.error(semicolonize('$start_message has been deprecated since $after_time.ymmdd()', c.error(semicolonize('$start_message has been deprecated since $after_time.ymmdd()',
deprecation_message), node.pos) deprecation_message), pos)
} else if after_time < now { } else if after_time < now {
c.warn(semicolonize('$start_message has been deprecated since $after_time.ymmdd(), it will be an error after $error_time.ymmdd()', c.warn(semicolonize('$start_message has been deprecated since $after_time.ymmdd(), it will be an error after $error_time.ymmdd()',
deprecation_message), node.pos) deprecation_message), pos)
} else if after_time == now { } else if after_time == now {
c.warn(semicolonize('$start_message has been deprecated', deprecation_message), c.warn(semicolonize('$start_message has been deprecated', deprecation_message),
node.pos) pos)
} else { } else {
c.note(semicolonize('$start_message will be deprecated after $after_time.ymmdd(), and will become an error after $error_time.ymmdd()', c.note(semicolonize('$start_message will be deprecated after $after_time.ymmdd(), and will become an error after $error_time.ymmdd()',
deprecation_message), node.pos) deprecation_message), pos)
} }
} }

View File

@ -0,0 +1,12 @@
vlib/v/checker/tests/modules/deprecated_module/main.v:2:1: notice: module `deprecated_module.www.ttt` will be deprecated after 2999-01-01, and will become an error after 2999-06-30; use xxx.yyy
1 | import deprecated_module.bbb.ccc
2 | import deprecated_module.www.ttt
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3 | import deprecated_module.xxx.yyy
4 |
vlib/v/checker/tests/modules/deprecated_module/main.v:12:11: error: undefined ident: `deprecated_module.www.ttt.non_existing`
10 | dump(ttt.f())
11 | dump(yyy.f())
12 | dump(ttt.non_existing)
| ~~~~~~~~~~~~
13 | }

View File

@ -0,0 +1,5 @@
module ccc
pub fn f() int {
return 142
}

View File

@ -0,0 +1,13 @@
import deprecated_module.bbb.ccc
import deprecated_module.www.ttt
import deprecated_module.xxx.yyy
// NB: www.ttt has been deprecated.
// => compiling this should produce an error,
// showing the deprecation message
fn main() {
dump(ccc.f())
dump(ttt.f())
dump(yyy.f())
dump(ttt.non_existing)
}

View File

@ -0,0 +1,7 @@
[deprecated: 'use xxx.yyy']
[deprecated_after: '2999-01-01']
module ttt
pub fn f() int {
return 1142
}

View File

@ -0,0 +1,5 @@
module yyy
pub fn f() int {
return 42
}

View File

@ -478,7 +478,7 @@ pub fn (mut f Fmt) stmt(node ast.Stmt) {
f.interface_decl(node) f.interface_decl(node)
} }
ast.Module { ast.Module {
f.mod(node) f.module_stmt(node)
} }
ast.Return { ast.Return {
f.return_stmt(node) f.return_stmt(node)
@ -1135,7 +1135,7 @@ pub fn (mut f Fmt) interface_method(method ast.FnDecl) {
f.mark_types_import_as_used(method.return_type) f.mark_types_import_as_used(method.return_type)
} }
pub fn (mut f Fmt) mod(mod ast.Module) { pub fn (mut f Fmt) module_stmt(mod ast.Module) {
f.set_current_module_name(mod.name) f.set_current_module_name(mod.name)
if mod.is_skipped { if mod.is_skipped {
return return

View File

@ -2967,16 +2967,19 @@ fn (mut p Parser) parse_number_literal() ast.Expr {
fn (mut p Parser) module_decl() ast.Module { fn (mut p Parser) module_decl() ast.Module {
mut module_attrs := []ast.Attr{} mut module_attrs := []ast.Attr{}
mut attrs_pos := p.tok.pos() mut attrs_pos := p.tok.pos()
if p.tok.kind == .lsbr { for p.tok.kind == .lsbr {
p.attributes() p.attributes()
module_attrs = p.attrs
} }
module_attrs << p.attrs
mut name := 'main' mut name := 'main'
is_skipped := p.tok.kind != .key_module
mut module_pos := token.Pos{} mut module_pos := token.Pos{}
mut name_pos := token.Pos{} mut name_pos := token.Pos{}
mut mod_node := ast.Module{} mut mod_node := ast.Module{}
if !is_skipped { is_skipped := p.tok.kind != .key_module
if is_skipped {
// the attributes were for something else != module, like a struct/fn/type etc.
module_attrs = []
} else {
p.attrs = [] p.attrs = []
module_pos = p.tok.pos() module_pos = p.tok.pos()
p.next() p.next()
@ -3020,6 +3023,14 @@ fn (mut p Parser) module_decl() ast.Module {
if !is_skipped { if !is_skipped {
for ma in module_attrs { for ma in module_attrs {
match ma.name { match ma.name {
'deprecated' {
// [deprecated: 'use a replacement']
p.table.mark_module_as_deprecated(p.mod, ma.arg)
}
'deprecated_after' {
// [deprecated_after: '2027-12-30']
p.table.mark_module_as_deprecated_after(p.mod, ma.arg)
}
'manualfree' { 'manualfree' {
p.is_manualfree = true p.is_manualfree = true
} }