parser/checker: check capital letters in interface names/methods
							parent
							
								
									99cf520bd4
								
							
						
					
					
						commit
						cc66eb1194
					
				| 
						 | 
				
			
			@ -159,6 +159,7 @@ pub:
 | 
			
		|||
	name        string
 | 
			
		||||
	field_names []string
 | 
			
		||||
	methods     []FnDecl
 | 
			
		||||
	pos token.Position
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub struct StructInitField {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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)
 | 
			
		||||
			}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -36,6 +36,7 @@ pub:
 | 
			
		|||
	name      string
 | 
			
		||||
	is_mut    bool
 | 
			
		||||
	typ       Type
 | 
			
		||||
	is_hidden bool // interface first arg
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub struct Var {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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) {
 | 
			
		||||
| 
						 | 
				
			
			@ -65,11 +63,17 @@ fn perform_speak(a Animal) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
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 {
 | 
			
		||||
| 
						 | 
				
			
			@ -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,7 +133,6 @@ interface Speaker2 {
 | 
			
		|||
	speak()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
struct Foo {
 | 
			
		||||
	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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue