parser,ast,checker: add support for `[deprecated: "use another module"] module obsolete`
parent
3bd528b218
commit
e1a2ab345d
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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).
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 | }
|
|
@ -0,0 +1,5 @@
|
||||||
|
module ccc
|
||||||
|
|
||||||
|
pub fn f() int {
|
||||||
|
return 142
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
[deprecated: 'use xxx.yyy']
|
||||||
|
[deprecated_after: '2999-01-01']
|
||||||
|
module ttt
|
||||||
|
|
||||||
|
pub fn f() int {
|
||||||
|
return 1142
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
module yyy
|
||||||
|
|
||||||
|
pub fn f() int {
|
||||||
|
return 42
|
||||||
|
}
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue