parser,ast,checker: add support for `[deprecated: "use another module"] module obsolete`
							parent
							
								
									3bd528b218
								
							
						
					
					
						commit
						e1a2ab345d
					
				|  | @ -4,6 +4,7 @@ | |||
| [has_globals] | ||||
| module ast | ||||
| 
 | ||||
| import time | ||||
| import v.cflag | ||||
| import v.token | ||||
| import v.util | ||||
|  | @ -36,8 +37,10 @@ pub mut: | |||
| 	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>
 | ||||
| 	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 {`
 | ||||
| 	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
 | ||||
|  | @ -301,6 +304,15 @@ pub fn (t &Table) known_fn(name string) bool { | |||
| 	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) { | ||||
| 	t.fns[new_fn.name] = new_fn | ||||
| } | ||||
|  |  | |||
|  | @ -3,6 +3,7 @@ | |||
| module checker | ||||
| 
 | ||||
| import os | ||||
| import time | ||||
| import v.ast | ||||
| import v.vmod | ||||
| 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) | ||||
| 	} | ||||
| 	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).
 | ||||
|  |  | |||
|  | @ -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) { | ||||
| 	start_message := '$kind `$name`' | ||||
| 	mut deprecation_message := '' | ||||
| 	now := 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) | ||||
| 	if error_time < now { | ||||
| 		c.error(semicolonize('$start_message has been deprecated since $after_time.ymmdd()', | ||||
| 			deprecation_message), node.pos) | ||||
| 			deprecation_message), pos) | ||||
| 	} 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()', | ||||
| 			deprecation_message), node.pos) | ||||
| 			deprecation_message), pos) | ||||
| 	} else if after_time == now { | ||||
| 		c.warn(semicolonize('$start_message has been deprecated', deprecation_message), | ||||
| 			node.pos) | ||||
| 			pos) | ||||
| 	} else { | ||||
| 		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) | ||||
| 		} | ||||
| 		ast.Module { | ||||
| 			f.mod(node) | ||||
| 			f.module_stmt(node) | ||||
| 		} | ||||
| 		ast.Return { | ||||
| 			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) | ||||
| } | ||||
| 
 | ||||
| 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) | ||||
| 	if mod.is_skipped { | ||||
| 		return | ||||
|  |  | |||
|  | @ -2967,16 +2967,19 @@ fn (mut p Parser) parse_number_literal() ast.Expr { | |||
| fn (mut p Parser) module_decl() ast.Module { | ||||
| 	mut module_attrs := []ast.Attr{} | ||||
| 	mut attrs_pos := p.tok.pos() | ||||
| 	if p.tok.kind == .lsbr { | ||||
| 	for p.tok.kind == .lsbr { | ||||
| 		p.attributes() | ||||
| 		module_attrs = p.attrs | ||||
| 	} | ||||
| 	module_attrs << p.attrs | ||||
| 	mut name := 'main' | ||||
| 	is_skipped := p.tok.kind != .key_module | ||||
| 	mut module_pos := token.Pos{} | ||||
| 	mut name_pos := token.Pos{} | ||||
| 	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 = [] | ||||
| 		module_pos = p.tok.pos() | ||||
| 		p.next() | ||||
|  | @ -3020,6 +3023,14 @@ fn (mut p Parser) module_decl() ast.Module { | |||
| 	if !is_skipped { | ||||
| 		for ma in module_attrs { | ||||
| 			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' { | ||||
| 					p.is_manualfree = true | ||||
| 				} | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue