Skip to content

Commit bfbc1b4

Browse files
committed
sema: check duplicate fn arguments
1 parent 3160aa6 commit bfbc1b4

File tree

9 files changed

+85
-12
lines changed

9 files changed

+85
-12
lines changed

compiler/ast/Scope.v

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ pub fn (sc &Scope) derive() &Scope {
3333

3434
pub fn (mut sc Scope) add_symbol(sym Symbol) ! {
3535
if _ := sc.lookup(sym.name) {
36-
return error('duplicate variable `${sym.name}`')
36+
return error('duplicate ${sym.type_of()} `${sym.name}`')
3737
}
3838
sc.syms << sym
3939
}

compiler/ast/Stmt.v

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ pub:
3333
return_type Type
3434
pub mut:
3535
stmts []Stmt
36+
sym &Function = unsafe { nil }
37+
scope &Scope = unsafe { nil }
3638
}
3739

3840
pub struct FnArg {
@@ -41,6 +43,7 @@ pub:
4143
name_pos FilePos
4244
type Type
4345
default_expr ?Expr
46+
pos FilePos
4447
}
4548

4649
pub struct WhileStmt {

compiler/ast/Symbol.v

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,28 @@
33

44
module ast
55

6-
pub type Symbol = Function | Constant | TypeSym
6+
pub type Symbol = Function | Variable | Constant | TypeSym
7+
8+
pub fn (sym Symbol) type_of() string {
9+
return match sym {
10+
Function {
11+
'function'
12+
}
13+
Variable {
14+
if sym.is_arg {
15+
'argument'
16+
} else {
17+
'variable'
18+
}
19+
}
20+
Constant {
21+
'constant'
22+
}
23+
TypeSym {
24+
'type'
25+
}
26+
}
27+
}
728

829
pub struct TypeSym {
930
pub:

compiler/parser/stmt.v

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,7 @@ fn (mut p Parser) parse_fn_stmt(is_pub bool) ast.FnStmt {
153153
mut args := []ast.FnArg{}
154154
if !p.accept(.rparen) {
155155
for {
156+
mut arg_pos := p.tok.pos
156157
arg_name := p.parse_ident()
157158
arg_name_pos := p.prev_tok.pos
158159
p.expect(.colon)
@@ -161,7 +162,8 @@ fn (mut p Parser) parse_fn_stmt(is_pub bool) ast.FnStmt {
161162
if p.accept(.assign) {
162163
arg_default_expr = p.parse_expr()
163164
}
164-
args << ast.FnArg{arg_name, arg_name_pos, arg_type, arg_default_expr}
165+
arg_pos += p.prev_tok.pos
166+
args << ast.FnArg{arg_name, arg_name_pos, arg_type, arg_default_expr, arg_pos}
165167
if !p.accept(.comma) || p.should_abort() {
166168
break
167169
}
@@ -174,7 +176,15 @@ fn (mut p Parser) parse_fn_stmt(is_pub bool) ast.FnStmt {
174176
p.ctx.void_type
175177
}
176178
stmts := p.parse_stmts()
177-
return ast.FnStmt{p.tags, is_pub, name, name_pos, args, return_type, stmts}
179+
return ast.FnStmt{
180+
tags: p.tags
181+
is_pub: is_pub
182+
name: name
183+
name_pos: name_pos
184+
args: args
185+
return_type: return_type
186+
stmts: stmts
187+
}
178188
}
179189

180190
fn (mut p Parser) parse_let_stmt(is_pub bool) ast.LetStmt {

compiler/sema/mod.v

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,13 +76,37 @@ fn (mut sema Sema) stmt(mut stmt ast.Stmt) {
7676
}
7777

7878
fn (mut sema Sema) fn_stmt(mut stmt ast.FnStmt) {
79+
old_scope := sema.scope
80+
defer {
81+
sema.scope = old_scope
82+
}
83+
7984
if sema.first_pass {
80-
sema.scope.add_local_symbol(ast.Function{
85+
stmt.sym = &ast.Function{
8186
name: stmt.name
8287
args: stmt.args
8388
node: unsafe { stmt }
84-
}) or { context.error(err.msg(), stmt.name_pos) }
89+
}
90+
stmt.scope = ast.Scope.new(sema.scope, ?ast.Symbol(stmt.sym))
91+
sema.scope.add_local_symbol(stmt.sym) or { context.error(err.msg(), stmt.name_pos) }
92+
sema.scope = stmt.scope
93+
for arg in stmt.args {
94+
sema.scope.add_symbol(ast.Variable{
95+
name: arg.name
96+
is_local: true
97+
is_arg: true
98+
type: arg.type
99+
}) or {
100+
context.error(err.msg(), arg.pos, context.Hint{
101+
kind: .note
102+
msg: 'inside function `${stmt.name}`'
103+
})
104+
}
105+
}
85106
sema.stmts(mut stmt.stmts)
86107
return
87108
}
109+
110+
sema.scope = stmt.scope
111+
sema.stmts(mut stmt.stmts)
88112
}

tests/gen_out_files.vsh

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,22 +3,26 @@
33
import os
44
import term
55

6-
if !os.exists('rivetc') {
7-
panic('rivetc executable not found')
6+
const rivetc = './bin/rivetc'
7+
8+
if !os.exists(rivetc) {
9+
panic('`${rivetc}` executable not found')
810
}
911

1012
files := os.walk_ext('tests/', '.ri')
1113
if files.len == 0 {
1214
return
1315
}
16+
1417
for file in files {
15-
if !file.ends_with('.err.ri') {
18+
out_file := file#[..-3] + '.out'
19+
if !file.ends_with('.err.ri') || os.is_file(out_file) {
1620
continue
1721
}
1822
println(term.bold('>> generating .out file for `${file}`'))
19-
res := os.execute('./rivetc ${file}')
23+
res := os.execute('${rivetc} ${file}')
2024
if res.exit_code != 0 {
21-
os.write_file(file#[..-3] + '.out', res.output.trim_space())!
25+
os.write_file(out_file, res.output.trim_space())!
2226
} else {
2327
println(' >> unexpected .exit_code == 0 for `${file}`')
2428
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
tests/invalid/sema/duplicate_argument.err.ri:5:16: error: duplicate argument `a`
2+
|
3+
5 | fn win(a: int, a: int) {} // FAIL
4+
| ^~~~~~
5+
= note: inside function `win`
6+
error: could not compile `duplicate_argument` module, aborting due to previous error
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
fn main() {}
2+
3+
fn my_fn(a: int, b: int) {} // OK
4+
5+
fn win(a: int, a: int) {} // FAIL

tests/run_tests.vsh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import term
66
// test.err.ri -> test.err.out
77
// test.ok.ri -> test.ok.out
88

9-
const rivetc = 'bin/rivetc'
9+
const rivetc = './bin/rivetc'
1010

1111
if !os.exists(rivetc) {
1212
panic('`${rivetc}` executable not found')

0 commit comments

Comments
 (0)