parser/cgen: multiple attributes & better errors (closes #5334)
parent
91df872c36
commit
0cd9066f44
|
@ -162,7 +162,7 @@ pub:
|
||||||
pub_mut_pos int // pub mut:
|
pub_mut_pos int // pub mut:
|
||||||
language table.Language
|
language table.Language
|
||||||
is_union bool
|
is_union bool
|
||||||
attr string
|
attrs []string
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct InterfaceDecl {
|
pub struct InterfaceDecl {
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
vlib/v/checker/tests/fn_attributes_empty_err.v:1:1: error: attributes cannot be empty
|
|
||||||
1 | [] fn tt() {
|
|
||||||
| ~~
|
|
||||||
2 | println('text')
|
|
||||||
3 | }
|
|
|
@ -1,4 +0,0 @@
|
||||||
vlib/v/checker/tests/multiple_fn_attributes.v:1:1: error: multiple attributes detected
|
|
||||||
1 | [inline;deprecated]
|
|
||||||
| ~~~~~~~~~~~~~~~~~~~
|
|
||||||
2 | fn foo(name string) string {}
|
|
|
@ -1,2 +0,0 @@
|
||||||
[inline;deprecated]
|
|
||||||
fn foo(name string) string {}
|
|
|
@ -98,7 +98,7 @@ mut:
|
||||||
is_json_fn bool // inside json.encode()
|
is_json_fn bool // inside json.encode()
|
||||||
json_types []string // to avoid json gen duplicates
|
json_types []string // to avoid json gen duplicates
|
||||||
pcs []ProfileCounterMeta // -prof profile counter fn_names => fn counter name
|
pcs []ProfileCounterMeta // -prof profile counter fn_names => fn counter name
|
||||||
attr string
|
attrs []string // attributes before next decl stmt
|
||||||
is_builtin_mod bool
|
is_builtin_mod bool
|
||||||
hotcode_fn_names []string
|
hotcode_fn_names []string
|
||||||
fn_main &ast.FnDecl // the FnDecl of the main function. Needed in order to generate the main function code *last*
|
fn_main &ast.FnDecl // the FnDecl of the main function. Needed in order to generate the main function code *last*
|
||||||
|
@ -573,6 +573,10 @@ fn (mut g Gen) stmts(stmts []ast.Stmt) {
|
||||||
if g.inside_ternary > 0 && i < stmts.len - 1 {
|
if g.inside_ternary > 0 && i < stmts.len - 1 {
|
||||||
g.write(',')
|
g.write(',')
|
||||||
}
|
}
|
||||||
|
// clear attrs on next non Attr stmt
|
||||||
|
if stmt !is ast.Attr {
|
||||||
|
g.attrs = []
|
||||||
|
}
|
||||||
}
|
}
|
||||||
g.indent--
|
g.indent--
|
||||||
if g.inside_ternary > 0 {
|
if g.inside_ternary > 0 {
|
||||||
|
@ -593,7 +597,7 @@ fn (mut g Gen) stmt(node ast.Stmt) {
|
||||||
g.gen_assign_stmt(it)
|
g.gen_assign_stmt(it)
|
||||||
}
|
}
|
||||||
ast.Attr {
|
ast.Attr {
|
||||||
g.attr = it.name
|
g.attrs << it.name
|
||||||
g.writeln('// Attr: [$it.name]')
|
g.writeln('// Attr: [$it.name]')
|
||||||
}
|
}
|
||||||
ast.Block {
|
ast.Block {
|
||||||
|
@ -685,8 +689,6 @@ fn (mut g Gen) stmt(node ast.Stmt) {
|
||||||
if it.language != .c {
|
if it.language != .c {
|
||||||
g.writeln('')
|
g.writeln('')
|
||||||
}
|
}
|
||||||
// g.attr has to be reset after each function
|
|
||||||
g.attr = ''
|
|
||||||
}
|
}
|
||||||
ast.ForCStmt {
|
ast.ForCStmt {
|
||||||
g.write('for (')
|
g.write('for (')
|
||||||
|
|
|
@ -38,9 +38,9 @@ fn (mut g Gen) gen_fn_decl(it ast.FnDecl) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
fn_start_pos := g.out.len
|
fn_start_pos := g.out.len
|
||||||
msvc_attrs := g.write_fn_attr()
|
msvc_attrs := g.write_fn_attrs()
|
||||||
// Live
|
// Live
|
||||||
is_livefn := g.attr == 'live'
|
is_livefn := 'live' in g.attrs
|
||||||
is_livemain := g.pref.is_livemain && is_livefn
|
is_livemain := g.pref.is_livemain && is_livefn
|
||||||
is_liveshared := g.pref.is_liveshared && is_livefn
|
is_liveshared := g.pref.is_liveshared && is_livefn
|
||||||
is_livemode := g.pref.is_livemain || g.pref.is_liveshared
|
is_livemode := g.pref.is_livemain || g.pref.is_liveshared
|
||||||
|
@ -703,9 +703,10 @@ fn (g &Gen) fileis(s string) bool {
|
||||||
return g.file.path.contains(s)
|
return g.file.path.contains(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (mut g Gen) write_fn_attr() string{
|
fn (mut g Gen) write_fn_attrs() string{
|
||||||
mut msvc_attrs := ''
|
mut msvc_attrs := ''
|
||||||
match g.attr {
|
for attr in g.attrs {
|
||||||
|
match attr {
|
||||||
'inline' {
|
'inline' {
|
||||||
g.write('inline ')
|
g.write('inline ')
|
||||||
}
|
}
|
||||||
|
@ -775,6 +776,6 @@ fn (mut g Gen) write_fn_attr() string{
|
||||||
// nothing but keep V happy
|
// nothing but keep V happy
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return msvc_attrs
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
return msvc_attrs
|
||||||
|
}
|
||||||
|
|
|
@ -12,7 +12,7 @@ fn (mut g Gen) profile_fn(fn_name string, is_main bool){
|
||||||
g.writeln('\tatexit(vprint_profile_stats);')
|
g.writeln('\tatexit(vprint_profile_stats);')
|
||||||
g.writeln('')
|
g.writeln('')
|
||||||
}
|
}
|
||||||
if g.pref.profile_no_inline && g.attr == 'inline' {
|
if g.pref.profile_no_inline && 'inline' in g.attrs {
|
||||||
g.defer_profile_code = ''
|
g.defer_profile_code = ''
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -119,7 +119,7 @@ pub fn (mut p Parser) call_args() []ast.CallArg {
|
||||||
fn (mut p Parser) fn_decl() ast.FnDecl {
|
fn (mut p Parser) fn_decl() ast.FnDecl {
|
||||||
p.top_level_statement_start()
|
p.top_level_statement_start()
|
||||||
start_pos := p.tok.position()
|
start_pos := p.tok.position()
|
||||||
is_deprecated := p.attr == 'deprecated'
|
is_deprecated := 'deprecated' in p.attrs
|
||||||
is_pub := p.tok.kind == .key_pub
|
is_pub := p.tok.kind == .key_pub
|
||||||
if is_pub {
|
if is_pub {
|
||||||
p.next()
|
p.next()
|
||||||
|
@ -273,8 +273,6 @@ fn (mut p Parser) fn_decl() ast.FnDecl {
|
||||||
stmts = p.parse_block_no_scope(true)
|
stmts = p.parse_block_no_scope(true)
|
||||||
}
|
}
|
||||||
p.close_scope()
|
p.close_scope()
|
||||||
p.attr = ''
|
|
||||||
p.attr_ctdefine = ''
|
|
||||||
return ast.FnDecl{
|
return ast.FnDecl{
|
||||||
name: name
|
name: name
|
||||||
mod: p.mod
|
mod: p.mod
|
||||||
|
|
|
@ -36,7 +36,7 @@ mut:
|
||||||
pref &pref.Preferences
|
pref &pref.Preferences
|
||||||
builtin_mod bool // are we in the `builtin` module?
|
builtin_mod bool // are we in the `builtin` module?
|
||||||
mod string // current module name
|
mod string // current module name
|
||||||
attr string
|
attrs []string // attributes before next decl stmt
|
||||||
attr_ctdefine string
|
attr_ctdefine string
|
||||||
expr_mod string // for constructing full type names in parse_type()
|
expr_mod string // for constructing full type names in parse_type()
|
||||||
scope &ast.Scope
|
scope &ast.Scope
|
||||||
|
@ -154,7 +154,13 @@ fn (mut p Parser) parse() ast.File {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
// println('stmt at ' + p.tok.str())
|
// println('stmt at ' + p.tok.str())
|
||||||
stmts << p.top_stmt()
|
stmt := p.top_stmt()
|
||||||
|
// clear the attribtes at the end of next non Attr top level stmt
|
||||||
|
if stmt !is ast.Attr {
|
||||||
|
p.attrs = []
|
||||||
|
p.attr_ctdefine = ''
|
||||||
|
}
|
||||||
|
stmts << stmt
|
||||||
}
|
}
|
||||||
// println('nr stmts = $stmts.len')
|
// println('nr stmts = $stmts.len')
|
||||||
// println(stmts[0])
|
// println(stmts[0])
|
||||||
|
@ -409,10 +415,7 @@ pub fn (mut p Parser) top_stmt() ast.Stmt {
|
||||||
.lsbr {
|
.lsbr {
|
||||||
start_pos := p.tok.position()
|
start_pos := p.tok.position()
|
||||||
attrs := p.attributes()
|
attrs := p.attributes()
|
||||||
if attrs.len > 1 {
|
if attrs.len == 0 {
|
||||||
end_pos := p.tok.position()
|
|
||||||
p.error_with_pos('multiple attributes detected', start_pos.extend(end_pos))
|
|
||||||
} else if attrs.len == 0 {
|
|
||||||
end_pos := p.tok.position()
|
end_pos := p.tok.position()
|
||||||
p.error_with_pos('attributes cannot be empty', start_pos.extend(end_pos))
|
p.error_with_pos('attributes cannot be empty', start_pos.extend(end_pos))
|
||||||
}
|
}
|
||||||
|
@ -634,6 +637,7 @@ fn (mut p Parser) attributes() []ast.Attr {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (mut p Parser) parse_attr() ast.Attr {
|
fn (mut p Parser) parse_attr() ast.Attr {
|
||||||
|
start_pos := p.prev_tok.position()
|
||||||
mut is_if_attr := false
|
mut is_if_attr := false
|
||||||
if p.tok.kind == .key_if {
|
if p.tok.kind == .key_if {
|
||||||
p.next()
|
p.next()
|
||||||
|
@ -650,7 +654,10 @@ fn (mut p Parser) parse_attr() ast.Attr {
|
||||||
p.next()
|
p.next()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
p.attr = name
|
if name in p.attrs {
|
||||||
|
p.error_with_pos('duplicate attribute `$name`', start_pos.extend(p.tok.position()))
|
||||||
|
}
|
||||||
|
p.attrs << name
|
||||||
if is_if_attr {
|
if is_if_attr {
|
||||||
p.attr_ctdefine = name
|
p.attr_ctdefine = name
|
||||||
}
|
}
|
||||||
|
@ -1460,8 +1467,7 @@ fn (mut p Parser) enum_decl() ast.EnumDecl {
|
||||||
}
|
}
|
||||||
p.top_level_statement_end()
|
p.top_level_statement_end()
|
||||||
p.check(.rcbr)
|
p.check(.rcbr)
|
||||||
attr := p.attr
|
is_flag := 'flag' in p.attrs
|
||||||
is_flag := attr == 'flag'
|
|
||||||
if is_flag {
|
if is_flag {
|
||||||
if fields.len > 32 {
|
if fields.len > 32 {
|
||||||
p.error('when an enum is used as bit field, it must have a max of 32 fields')
|
p.error('when an enum is used as bit field, it must have a max of 32 fields')
|
||||||
|
|
|
@ -32,7 +32,7 @@ fn (mut p Parser) struct_decl() ast.StructDecl {
|
||||||
p.next() // C || JS
|
p.next() // C || JS
|
||||||
p.next() // .
|
p.next() // .
|
||||||
}
|
}
|
||||||
is_typedef := p.attr == 'typedef'
|
is_typedef := 'typedef' in p.attrs
|
||||||
no_body := p.peek_tok.kind != .lcbr
|
no_body := p.peek_tok.kind != .lcbr
|
||||||
if language == .v && no_body {
|
if language == .v && no_body {
|
||||||
p.error('`$p.tok.lit` lacks body')
|
p.error('`$p.tok.lit` lacks body')
|
||||||
|
@ -180,7 +180,7 @@ fn (mut p Parser) struct_decl() ast.StructDecl {
|
||||||
fields: fields
|
fields: fields
|
||||||
is_typedef: is_typedef
|
is_typedef: is_typedef
|
||||||
is_union: is_union
|
is_union: is_union
|
||||||
is_ref_only: p.attr == 'ref_only'
|
is_ref_only: 'ref_only' in p.attrs
|
||||||
}
|
}
|
||||||
mod: p.mod
|
mod: p.mod
|
||||||
is_public: is_pub
|
is_public: is_pub
|
||||||
|
@ -208,7 +208,7 @@ fn (mut p Parser) struct_decl() ast.StructDecl {
|
||||||
pub_mut_pos: pub_mut_pos
|
pub_mut_pos: pub_mut_pos
|
||||||
language: language
|
language: language
|
||||||
is_union: is_union
|
is_union: is_union
|
||||||
attr: p.attr
|
attrs: p.attrs
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
vlib/v/parser/tests/fn_attributes_duplicate_multiple.v:2:1: error: duplicate attribute `inline`
|
||||||
|
1 | [inline]
|
||||||
|
2 | [inline]
|
||||||
|
| ~~~~~~~~
|
||||||
|
3 | fn foo() {}
|
||||||
|
4 |
|
|
@ -0,0 +1,7 @@
|
||||||
|
[inline]
|
||||||
|
[inline]
|
||||||
|
fn foo() {}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
foo()
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
vlib/v/parser/tests/fn_attributes_duplicate_single.v:1:8: error: duplicate attribute `inline`
|
||||||
|
1 | [inline; inline]
|
||||||
|
| ~~~~~~~~~
|
||||||
|
2 | fn foo() {}
|
||||||
|
3 |
|
|
@ -0,0 +1,6 @@
|
||||||
|
[inline; inline]
|
||||||
|
fn foo() {}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
foo()
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
vlib/v/parser/tests/fn_attributes_empty_err.v:1:1: error: attributes cannot be empty
|
||||||
|
1 | [] fn tt() {
|
||||||
|
| ~~
|
||||||
|
2 | println('text')
|
||||||
|
3 | }
|
|
@ -22,6 +22,14 @@ pub enum PubEnumAttrTest {
|
||||||
two
|
two
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[attrone]
|
||||||
|
[attrtwo]
|
||||||
|
pub enum EnumMultiAttrTest {
|
||||||
|
one
|
||||||
|
two
|
||||||
|
}
|
||||||
|
|
||||||
[testing]
|
[testing]
|
||||||
fn test_fn_attribute() {
|
fn test_fn_attribute() {
|
||||||
assert true
|
assert true
|
||||||
|
@ -31,3 +39,14 @@ fn test_fn_attribute() {
|
||||||
pub fn test_pub_fn_attribute() {
|
pub fn test_pub_fn_attribute() {
|
||||||
assert true
|
assert true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[attrone]
|
||||||
|
[attrtwo]
|
||||||
|
fn test_fn_multi_attribute() {
|
||||||
|
assert true
|
||||||
|
}
|
||||||
|
|
||||||
|
[attrone; attrtwo]
|
||||||
|
fn test_fn_multi_attribute_single() {
|
||||||
|
assert true
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue