diff --git a/doc/docs.md b/doc/docs.md index 433b08a801..f6a5b89d0e 100644 --- a/doc/docs.md +++ b/doc/docs.md @@ -2501,7 +2501,7 @@ On Unix-like platforms, the file can be run directly after making it executable V has several attributes that modify the behavior of functions and structs. -An attribute is specified inside `[]` right before the function/struct declaration and applies only to the following definition. +An attribute is specified inside `[]` right before a function/struct declaration and applies only to the following declaration. ```v // Calling this function will result in a deprecation warning diff --git a/vlib/v/fmt/fmt.v b/vlib/v/fmt/fmt.v index 1cb5f701ce..5a6783e32f 100644 --- a/vlib/v/fmt/fmt.v +++ b/vlib/v/fmt/fmt.v @@ -1115,18 +1115,7 @@ pub fn (mut f Fmt) or_expr(or_block ast.OrExpr) { fn (mut f Fmt) attrs(attrs []table.Attr) { for attr in attrs { - f.write('[') - if attr.is_ctdefine { - f.write('if ') - } - if attr.is_string { - f.write("'") - } - f.write(attr.name) - if attr.is_string { - f.write("'") - } - f.writeln(']') + f.writeln('[$attr]') } } @@ -1139,7 +1128,7 @@ fn (mut f Fmt) inline_attrs(attrs []table.Attr) { if i > 0 { f.write(';') } - f.write(attr.name) + f.write('$attr') } f.write(']') } diff --git a/vlib/v/fmt/tests/attrs_keep.vv b/vlib/v/fmt/tests/attrs_keep.vv index 78346edcc0..d7a56adf1c 100644 --- a/vlib/v/fmt/tests/attrs_keep.vv +++ b/vlib/v/fmt/tests/attrs_keep.vv @@ -1,5 +1,7 @@ [inline] [if debug] +[foo: bar] +[deprecated: 'use bar() instead'] fn keep_attributes() { println('hi !') } diff --git a/vlib/v/gen/comptime.v b/vlib/v/gen/comptime.v index 146a9062cd..fc3265363f 100644 --- a/vlib/v/gen/comptime.v +++ b/vlib/v/gen/comptime.v @@ -88,6 +88,19 @@ fn (mut g Gen) comptime_call(node ast.ComptimeCall) { } } +fn cgen_attrs(attrs []table.Attr) []string { + mut res := []string{cap: attrs.len} + for attr in attrs { + // we currently don't quote 'arg' (otherwise we could just use `s := attr.str()`) + mut s := attr.name + if attr.arg.len > 0 { + s += ': $attr.arg' + } + res << 'tos_lit("$s")' + } + return res +} + fn (mut g Gen) comp_if(mut it ast.CompIf) { if it.stmts.len == 0 && it.else_stmts.len == 0 { return @@ -175,10 +188,7 @@ fn (mut g Gen) comp_for(node ast.CompFor) { if method.attrs.len == 0 { g.writeln('\t${node.val_var}.attrs = __new_array_with_default(0, 0, sizeof(string), 0);') } else { - mut attrs := []string{} - for attrib in method.attrs { - attrs << 'tos_lit("$attrib.name")' - } + attrs := cgen_attrs(method.attrs) g.writeln('\t${node.val_var}.attrs = new_array_from_c_array($attrs.len, $attrs.len, sizeof(string), _MOV((string[$attrs.len]){' + attrs.join(', ') + '}));') } @@ -210,10 +220,7 @@ fn (mut g Gen) comp_for(node ast.CompFor) { if field.attrs.len == 0 { g.writeln('\t${node.val_var}.attrs = __new_array_with_default(0, 0, sizeof(string), 0);') } else { - mut attrs := []string{} - for attrib in field.attrs { - attrs << 'tos_lit("$attrib.name")' - } + attrs := cgen_attrs(field.attrs) g.writeln('\t${node.val_var}.attrs = new_array_from_c_array($attrs.len, $attrs.len, sizeof(string), _MOV((string[$attrs.len]){' + attrs.join(', ') + '}));') } diff --git a/vlib/v/gen/json.v b/vlib/v/gen/json.v index 505ce06a63..ff2386f5d3 100644 --- a/vlib/v/gen/json.v +++ b/vlib/v/gen/json.v @@ -83,8 +83,8 @@ $enc_fn_dec { } mut name := field.name for attr in field.attrs { - if attr.name.starts_with('json:') { - name = attr.name[5..] + if attr.name == 'json' { + name = attr.arg break } } diff --git a/vlib/v/parser/parser.v b/vlib/v/parser/parser.v index 81a27ae6c0..a5ba088609 100644 --- a/vlib/v/parser/parser.v +++ b/vlib/v/parser/parser.v @@ -432,7 +432,7 @@ pub fn (mut p Parser) top_stmt() ast.Stmt { } } .lsbr { - // attrs are stores in `p.attrs()` + // attrs are stored in `p.attrs` p.attributes() continue } @@ -737,7 +737,9 @@ fn (mut p Parser) parse_attr() table.Attr { is_ctdefine = true } mut name := '' + mut arg := '' is_string := p.tok.kind == .string + mut is_string_arg := false if is_string { name = p.tok.lit p.next() @@ -749,12 +751,13 @@ fn (mut p Parser) parse_attr() table.Attr { p.error_with_pos('please use `[trusted]` instead', p.tok.position()) } if p.tok.kind == .colon { - name += ':' p.next() + // `name: arg` if p.tok.kind == .name { - name += p.check_name() - } else if p.tok.kind == .string { - name += p.tok.lit + arg = p.check_name() + } else if p.tok.kind == .string { // `name: 'arg'` + arg = p.tok.lit + is_string_arg = true p.next() } } @@ -763,6 +766,8 @@ fn (mut p Parser) parse_attr() table.Attr { name: name is_string: is_string is_ctdefine: is_ctdefine + arg: arg + is_string_arg: is_string_arg } } diff --git a/vlib/v/table/attr.v b/vlib/v/table/attr.v index 134430ed29..c94c5a2575 100644 --- a/vlib/v/table/attr.v +++ b/vlib/v/table/attr.v @@ -6,9 +6,38 @@ module table // e.g. `[unsafe]` pub struct Attr { pub: - name string - is_string bool // `['xxx']` - is_ctdefine bool // `[if flag]` + name string // [name] + is_string bool // ['name'] + is_ctdefine bool // [if name] + arg string // [name: arg] + is_string_arg bool // [name: 'arg'] +} + +// no square brackets +pub fn (attr Attr) str() string { + mut s := '' + if attr.is_ctdefine { + s += 'if ' + } + if attr.is_string { + s += "'$attr.name'" + } + else { + s += attr.name + if attr.arg.len > 0 { + s += ': ' + if attr.is_string_arg { + mut a := attr.arg.replace('\\', '\\\\') + // FIXME: other escapes e.g. \r\n + a = a.replace("'", "\\'") + s += "'$a'" + } + else { + s += attr.arg + } + } + } + return s } pub fn (attrs []Attr) contains(str string) bool {