From cc66eb1194223a7c133ba9f4170fdb46d1d38c3f Mon Sep 17 00:00:00 2001 From: Alexander Medvednikov Date: Wed, 6 May 2020 12:26:00 +0200 Subject: [PATCH] parser/checker: check capital letters in interface names/methods --- vlib/v/ast/ast.v | 1 + vlib/v/ast/str.v | 4 +++ vlib/v/checker/checker.v | 20 ++++++++++--- vlib/v/parser/struct.v | 7 +++++ vlib/v/table/table.v | 7 +++-- vlib/v/tests/interface_test.v | 55 ++++++++++++++++++++--------------- 6 files changed, 64 insertions(+), 30 deletions(-) diff --git a/vlib/v/ast/ast.v b/vlib/v/ast/ast.v index 0d645761ce..e7cd4eaf00 100644 --- a/vlib/v/ast/ast.v +++ b/vlib/v/ast/ast.v @@ -159,6 +159,7 @@ pub: name string field_names []string methods []FnDecl + pos token.Position } pub struct StructInitField { diff --git a/vlib/v/ast/str.v b/vlib/v/ast/str.v index 635c9ad0cb..76bbb64ad8 100644 --- a/vlib/v/ast/str.v +++ b/vlib/v/ast/str.v @@ -40,9 +40,13 @@ pub fn (node &FnDecl) str(t &table.Table) string { f.write('fn ${receiver}${name}(') for i, arg in node.args { // skip receiver + // if (node.is_method || node.is_interface) && i == 0 { if node.is_method && i == 0 { continue } + if arg.is_hidden { + continue + } is_last_arg := i == node.args.len - 1 should_add_type := is_last_arg || node.args[i + 1].typ != arg.typ || (node.is_variadic && i == node.args.len - 2) diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 93f2c54799..596b46fffa 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -90,7 +90,8 @@ pub fn (mut c Checker) check_files(ast_files []ast.File) { return } if !has_main_mod_file { - c.error('projet must include a `main` module or be a shared library (compile with `v -shared`)', token.Position{}) + c.error('projet must include a `main` module or be a shared library (compile with `v -shared`)', + token.Position{}) } else if !has_main_fn { c.error('function `main` must be declared in the main module', token.Position{}) } @@ -886,7 +887,8 @@ fn (mut c Checker) type_implements(typ, inter_typ table.Type, pos token.Position for imethod in inter_sym.methods { if method := typ_sym.find_method(imethod.name) { if !imethod.is_same_method_as(method) { - c.error('`$styp` incorrectly implements method `$imethod.name` of interface `$inter_sym.name`, expected `${c.table.fn_to_str(imethod)}`', pos) + c.error('`$styp` incorrectly implements method `$imethod.name` of interface `$inter_sym.name`, expected `${c.table.fn_to_str(imethod)}`', + pos) } continue } @@ -1474,6 +1476,16 @@ fn (mut c Checker) stmt(node ast.Stmt) { } // ast.HashStmt {} ast.Import {} + ast.InterfaceDecl { + if !it.name[0].is_capital() { + pos := token.Position{ + line_nr: it.pos.line_nr + pos: it.pos.pos + 'interface'.len + len: it.name.len + } + c.error('interface name must begin with capital letter', pos) + } + } ast.Module { c.mod = it.name c.is_builtin_mod = it.name == 'builtin' @@ -1561,8 +1573,8 @@ pub fn (mut c Checker) expr(node ast.Expr) table.Type { ast.CastExpr { it.expr_type = c.expr(it.expr) sym := c.table.get_type_symbol(it.expr_type) - if it.typ == table.string_type && !(sym.kind in [.byte, .byteptr] || - sym.kind == .array && sym.name == 'array_byte') { + if it.typ == table.string_type && !(sym.kind in [.byte, .byteptr] || sym.kind == + .array && sym.name == 'array_byte') { type_name := c.table.type_to_str(it.expr_type) c.error('cannot cast type `$type_name` to string', it.pos) } diff --git a/vlib/v/parser/struct.v b/vlib/v/parser/struct.v index a358ef1c8f..62d8ba12f5 100644 --- a/vlib/v/parser/struct.v +++ b/vlib/v/parser/struct.v @@ -6,6 +6,7 @@ module parser import v.ast import v.table import v.token +import v.util fn (mut p Parser) struct_decl() ast.StructDecl { start_pos := p.tok.position() @@ -255,6 +256,7 @@ fn (mut p Parser) struct_init(short_syntax bool) ast.StructInit { } fn (mut p Parser) interface_decl() ast.InterfaceDecl { + start_pos := p.tok.position() is_pub := p.tok.kind == .key_pub if is_pub { p.next() @@ -278,11 +280,15 @@ fn (mut p Parser) interface_decl() ast.InterfaceDecl { for p.tok.kind != .rcbr && p.tok.kind != .eof { line_nr := p.tok.line_nr name := p.check_name() + if util.contains_capital(name) { + p.error('interface methods cannot contain uppercase letters, use snake_case instead') + } // field_names << name args2, _ := p.fn_args() mut args := [table.Arg{ name: 'x' typ: typ + is_hidden: true }] args << args2 mut method := ast.FnDecl{ @@ -306,5 +312,6 @@ fn (mut p Parser) interface_decl() ast.InterfaceDecl { return ast.InterfaceDecl{ name: interface_name methods: methods + pos: start_pos } } diff --git a/vlib/v/table/table.v b/vlib/v/table/table.v index 37ba4018a3..4970335921 100644 --- a/vlib/v/table/table.v +++ b/vlib/v/table/table.v @@ -33,9 +33,10 @@ pub mut: pub struct Arg { pub: - name string - is_mut bool - typ Type + name string + is_mut bool + typ Type + is_hidden bool // interface first arg } pub struct Var { diff --git a/vlib/v/tests/interface_test.v b/vlib/v/tests/interface_test.v index 2c874971a9..68807e860b 100644 --- a/vlib/v/tests/interface_test.v +++ b/vlib/v/tests/interface_test.v @@ -1,4 +1,3 @@ - struct Dog { breed string } @@ -46,11 +45,10 @@ fn (d Dog) name_detailed(pet_name string) string { } // do not add to Dog the utility function 'str', as a sample - - fn test_todo() { - if true {} - else {} + if true { + } else { + } } fn perform_speak(a Animal) { @@ -58,24 +56,30 @@ fn perform_speak(a Animal) { assert true name := a.name() assert name == 'Dog' || name == 'Cat' - //if a is Dog { - //assert name == 'Dog' - //} + // if a is Dog { + // assert name == 'Dog' + // } println(a.name()) } fn test_perform_speak() { - dog := Dog{breed: 'Labrador Retriever'} + dog := Dog{ + breed: 'Labrador Retriever' + } perform_speak(dog) - cat := Cat{breed: 'Persian'} + cat := Cat{ + breed: 'Persian' + } perform_speak(cat) - perform_speak(Cat{breed: 'Persian'}) + perform_speak(Cat{ + breed: 'Persian' + }) handle_animals([dog, cat]) /* f := Foo { speaker: dog } -*/ + */ } fn perform_name_detailed(a Animal) { @@ -85,22 +89,24 @@ fn perform_name_detailed(a Animal) { } fn test_perform_name_detailed() { - dog := Dog{breed: 'Labrador Retriever'} + dog := Dog{ + breed: 'Labrador Retriever' + } println('Test on Dog: $dog ...') perform_name_detailed(dog) - cat := Cat{} println('Test on Cat: $cat ...') perform_speak(cat) - println('Test on another Cat: ...') - perform_speak(Cat{breed: 'Persian'}) - + perform_speak(Cat{ + breed: 'Persian' + }) println('Test (dummy/empty) on array of animals ...') handle_animals([dog, cat]) } -fn handle_animals(a []Animal) {} +fn handle_animals(a []Animal) { +} interface Register { register() @@ -110,9 +116,11 @@ struct RegTest { a int } -fn (f RegTest) register() {} +fn (f RegTest) register() { +} -fn handle_reg(r Register) {} +fn handle_reg(r Register) { +} fn test_register() { f := RegTest{} @@ -125,9 +133,8 @@ interface Speaker2 { speak() } - struct Foo { - animal Animal + animal Animal animals []Animal } @@ -140,7 +147,9 @@ interface Animal { fn test_interface_array() { println('Test on array of animals ...') mut animals := []Animal{} - animals = [ Cat{}, Dog{breed: 'Labrador Retriever'} ] + animals = [Cat{}, Dog{ + breed: 'Labrador Retriever' + }] animals << Cat{} assert true // TODO .str() from the real types should be called