mirror of
https://github.com/vlang/v.git
synced 2023-08-10 21:13:21 +03:00
parser: check generic parameters error of generic struct declaration (#12418)
This commit is contained in:
parent
3f841edec1
commit
8f4180ea09
@ -38,7 +38,7 @@ pub fn (mut p Parser) call_expr(language ast.Language, mod string) ast.CallExpr
|
||||
if p.tok.kind == .lt {
|
||||
// `foo<int>(10)`
|
||||
p.expr_mod = ''
|
||||
concrete_types = p.parse_generic_type_list()
|
||||
concrete_types = p.parse_concrete_types()
|
||||
concrete_list_pos = concrete_list_pos.extend(p.prev_tok.position())
|
||||
// In case of `foo<T>()`
|
||||
// T is unwrapped and registered in the checker.
|
||||
@ -313,7 +313,7 @@ fn (mut p Parser) fn_decl() ast.FnDecl {
|
||||
}
|
||||
}
|
||||
// <T>
|
||||
mut generic_names := p.parse_generic_names()
|
||||
_, mut generic_names := p.parse_generic_types()
|
||||
// generic names can be infer with receiver's generic names
|
||||
if is_method && rec.typ.has_flag(.generic) {
|
||||
sym := p.table.get_type_symbol(rec.typ)
|
||||
@ -608,57 +608,6 @@ fn (mut p Parser) fn_receiver(mut params []ast.Param, mut rec ReceiverParsingInf
|
||||
return
|
||||
}
|
||||
|
||||
fn (mut p Parser) parse_generic_names() []string {
|
||||
mut param_names := []string{}
|
||||
if p.tok.kind != .lt {
|
||||
return param_names
|
||||
}
|
||||
p.check(.lt)
|
||||
mut first_done := false
|
||||
mut count := 0
|
||||
for p.tok.kind !in [.gt, .eof] {
|
||||
if first_done {
|
||||
p.check(.comma)
|
||||
}
|
||||
name := p.tok.lit
|
||||
if name.len > 0 && !name[0].is_capital() {
|
||||
p.error('generic parameter needs to be uppercase')
|
||||
}
|
||||
if name.len > 1 {
|
||||
p.error('generic parameter name needs to be exactly one char')
|
||||
}
|
||||
if !util.is_generic_type_name(p.tok.lit) {
|
||||
p.error('`$p.tok.lit` is a reserved name and cannot be used for generics')
|
||||
}
|
||||
if name in param_names {
|
||||
p.error('duplicated generic parameter `$name`')
|
||||
}
|
||||
if count > 8 {
|
||||
p.error('cannot have more than 9 generic parameters')
|
||||
}
|
||||
p.check(.name)
|
||||
param_names << name
|
||||
if p.table.find_type_idx(name) == 0 {
|
||||
p.table.register_type_symbol(ast.TypeSymbol{
|
||||
name: name
|
||||
cname: util.no_dots(name)
|
||||
mod: p.mod
|
||||
kind: .any
|
||||
is_public: true
|
||||
})
|
||||
}
|
||||
first_done = true
|
||||
count++
|
||||
}
|
||||
p.check(.gt)
|
||||
return param_names
|
||||
}
|
||||
|
||||
// is_generic_name returns true if the current token is a generic name.
|
||||
fn (p Parser) is_generic_name() bool {
|
||||
return p.tok.kind == .name && util.is_generic_type_name(p.tok.lit)
|
||||
}
|
||||
|
||||
fn (mut p Parser) anon_fn() ast.AnonFn {
|
||||
pos := p.tok.position()
|
||||
p.check(.key_fn)
|
||||
|
@ -2440,7 +2440,7 @@ fn (mut p Parser) dot_expr(left ast.Expr) ast.Expr {
|
||||
mut concrete_list_pos := p.tok.position()
|
||||
if is_generic_call {
|
||||
// `g.foo<int>(10)`
|
||||
concrete_types = p.parse_generic_type_list()
|
||||
concrete_types = p.parse_concrete_types()
|
||||
concrete_list_pos = concrete_list_pos.extend(p.prev_tok.position())
|
||||
// In case of `foo<T>()`
|
||||
// T is unwrapped and registered in the checker.
|
||||
@ -2533,7 +2533,57 @@ fn (mut p Parser) dot_expr(left ast.Expr) ast.Expr {
|
||||
return sel_expr
|
||||
}
|
||||
|
||||
fn (mut p Parser) parse_generic_type_list() []ast.Type {
|
||||
fn (mut p Parser) parse_generic_types() ([]ast.Type, []string) {
|
||||
mut types := []ast.Type{}
|
||||
mut param_names := []string{}
|
||||
if p.tok.kind != .lt {
|
||||
return types, param_names
|
||||
}
|
||||
p.check(.lt)
|
||||
mut first_done := false
|
||||
mut count := 0
|
||||
for p.tok.kind !in [.gt, .eof] {
|
||||
if first_done {
|
||||
p.check(.comma)
|
||||
}
|
||||
name := p.tok.lit
|
||||
if name.len > 0 && !name[0].is_capital() {
|
||||
p.error('generic parameter needs to be uppercase')
|
||||
}
|
||||
if name.len > 1 {
|
||||
p.error('generic parameter name needs to be exactly one char')
|
||||
}
|
||||
if !util.is_generic_type_name(p.tok.lit) {
|
||||
p.error('`$p.tok.lit` is a reserved name and cannot be used for generics')
|
||||
}
|
||||
if name in param_names {
|
||||
p.error('duplicated generic parameter `$name`')
|
||||
}
|
||||
if count > 8 {
|
||||
p.error('cannot have more than 9 generic parameters')
|
||||
}
|
||||
p.check(.name)
|
||||
param_names << name
|
||||
|
||||
mut idx := p.table.find_type_idx(name)
|
||||
if idx == 0 {
|
||||
idx = p.table.register_type_symbol(ast.TypeSymbol{
|
||||
name: name
|
||||
cname: util.no_dots(name)
|
||||
mod: p.mod
|
||||
kind: .any
|
||||
is_public: true
|
||||
})
|
||||
}
|
||||
types << ast.new_type(idx).set_flag(.generic)
|
||||
first_done = true
|
||||
count++
|
||||
}
|
||||
p.check(.gt)
|
||||
return types, param_names
|
||||
}
|
||||
|
||||
fn (mut p Parser) parse_concrete_types() []ast.Type {
|
||||
mut types := []ast.Type{}
|
||||
if p.tok.kind != .lt {
|
||||
return types
|
||||
@ -2551,6 +2601,11 @@ fn (mut p Parser) parse_generic_type_list() []ast.Type {
|
||||
return types
|
||||
}
|
||||
|
||||
// is_generic_name returns true if the current token is a generic name.
|
||||
fn (p Parser) is_generic_name() bool {
|
||||
return p.tok.kind == .name && util.is_generic_type_name(p.tok.lit)
|
||||
}
|
||||
|
||||
// `.green`
|
||||
// `pref.BuildMode.default_mode`
|
||||
fn (mut p Parser) enum_val() ast.EnumVal {
|
||||
@ -3238,7 +3293,7 @@ fn (mut p Parser) type_decl() ast.TypeDecl {
|
||||
return ast.AliasTypeDecl{}
|
||||
}
|
||||
mut sum_variants := []ast.TypeNode{}
|
||||
generic_types := p.parse_generic_type_list()
|
||||
generic_types, _ := p.parse_generic_types()
|
||||
decl_pos_with_generics := decl_pos.extend(p.prev_tok.position())
|
||||
p.check(.assign)
|
||||
mut type_pos := p.tok.position()
|
||||
|
@ -47,7 +47,7 @@ fn (mut p Parser) struct_decl() ast.StructDecl {
|
||||
name_pos)
|
||||
return ast.StructDecl{}
|
||||
}
|
||||
generic_types := p.parse_generic_type_list()
|
||||
generic_types, _ := p.parse_generic_types()
|
||||
no_body := p.tok.kind != .lcbr
|
||||
if language == .v && no_body {
|
||||
p.error('`$p.tok.lit` lacks body')
|
||||
@ -451,7 +451,7 @@ fn (mut p Parser) interface_decl() ast.InterfaceDecl {
|
||||
p.check_for_impure_v(language, name_pos)
|
||||
modless_name := p.check_name()
|
||||
interface_name := p.prepend_mod(modless_name).clone()
|
||||
generic_types := p.parse_generic_type_list()
|
||||
generic_types, _ := p.parse_generic_types()
|
||||
// println('interface decl $interface_name')
|
||||
p.check(.lcbr)
|
||||
pre_comments := p.eat_comments()
|
||||
|
5
vlib/v/parser/tests/generic_struct_type_decl_err.out
Normal file
5
vlib/v/parser/tests/generic_struct_type_decl_err.out
Normal file
@ -0,0 +1,5 @@
|
||||
vlib/v/parser/tests/generic_struct_type_decl_err.vv:1:13: error: generic parameter name needs to be exactly one char
|
||||
1 | struct GMap<Hashable<K>, V> {
|
||||
| ~~~~~~~~
|
||||
2 | map_ map[int]V
|
||||
3 | }
|
17
vlib/v/parser/tests/generic_struct_type_decl_err.vv
Normal file
17
vlib/v/parser/tests/generic_struct_type_decl_err.vv
Normal file
@ -0,0 +1,17 @@
|
||||
struct GMap<Hashable<K>, V> {
|
||||
map_ map[int]V
|
||||
}
|
||||
|
||||
fn (t GMap<Hashable<K>, V>) contains(k K) bool {
|
||||
return true // TODO
|
||||
}
|
||||
|
||||
fn (t GMap<Hashable<K>, V>) set(k K, v V) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
fn main() {
|
||||
x := GMap<string, string>{}
|
||||
x.set("hello", "world")
|
||||
println(x.contains("hello"))
|
||||
}
|
Loading…
Reference in New Issue
Block a user