fmt: smarter if condition wrapping (#8201)

pull/8286/head
Lukas Neubert 2021-01-23 09:33:22 +01:00 committed by GitHub
parent 9812230847
commit 8b61891348
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
65 changed files with 686 additions and 811 deletions

View File

@ -266,9 +266,8 @@ fn (vd VDoc) gen_html(d doc.Doc) string {
names := dc.head.name.split('.') names := dc.head.name.split('.')
submod_prefix = if names.len > 1 { names[0] } else { dc.head.name } submod_prefix = if names.len > 1 { names[0] } else { dc.head.name }
mut href_name := './${dc.head.name}.html' mut href_name := './${dc.head.name}.html'
if (cfg.is_vlib && dc.head.name == 'builtin' && !cfg.include_readme) || if (cfg.is_vlib && dc.head.name == 'builtin' && !cfg.include_readme)
dc.head.name == 'README' || dc.head.name == 'README' {
{
href_name = './index.html' href_name = './index.html'
} else if submod_prefix !in vd.docs.map(it.head.name) { } else if submod_prefix !in vd.docs.map(it.head.name) {
href_name = '#' href_name = '#'
@ -303,19 +302,19 @@ fn (vd VDoc) gen_html(d doc.Doc) string {
header_name).replace('{{ version }}', version).replace('{{ light_icon }}', vd.assets['light_icon']).replace('{{ dark_icon }}', header_name).replace('{{ version }}', version).replace('{{ light_icon }}', vd.assets['light_icon']).replace('{{ dark_icon }}',
vd.assets['dark_icon']).replace('{{ menu_icon }}', vd.assets['menu_icon']).replace('{{ head_assets }}', vd.assets['dark_icon']).replace('{{ menu_icon }}', vd.assets['menu_icon']).replace('{{ head_assets }}',
if cfg.inline_assets { if cfg.inline_assets {
'\n${tabs[0]}<style>' + vd.assets['doc_css'] + '</style>\n${tabs[0]}<style>' + vd.assets['normalize_css'] + '\n${tabs[0]}<style>' + vd.assets['doc_css'] + '</style>\n${tabs[0]}<style>' +
'</style>\n${tabs[0]}<script>' + vd.assets['dark_mode_js'] + '</script>' vd.assets['normalize_css'] + '</style>\n${tabs[0]}<script>' +
vd.assets['dark_mode_js'] + '</script>'
} else { } else {
'\n${tabs[0]}<link rel="stylesheet" href="' + vd.assets['doc_css'] + '" />\n${tabs[0]}<link rel="stylesheet" href="' + '\n${tabs[0]}<link rel="stylesheet" href="' + vd.assets['doc_css'] +
vd.assets['normalize_css'] + '" />\n${tabs[0]}<script src="' + vd.assets['dark_mode_js'] + '" />\n${tabs[0]}<link rel="stylesheet" href="' + vd.assets['normalize_css'] +
'"></script>' '" />\n${tabs[0]}<script src="' + vd.assets['dark_mode_js'] + '"></script>'
}).replace('{{ toc_links }}', if cfg.is_multi || vd.docs.len > 1 { }).replace('{{ toc_links }}', if cfg.is_multi || vd.docs.len > 1 {
modules_toc_str modules_toc_str
} else { } else {
symbols_toc_str symbols_toc_str
}).replace('{{ contents }}', contents.str()).replace('{{ right_content }}', if cfg.is_multi && }).replace('{{ contents }}', contents.str()).replace('{{ right_content }}', if cfg.is_multi
vd.docs.len > 1 && d.head.name != 'README' && vd.docs.len > 1&& d.head.name != 'README' {
{
'<div class="doc-toc"><ul>' + symbols_toc_str + '</ul></div>' '<div class="doc-toc"><ul>' + symbols_toc_str + '</ul></div>'
} else { } else {
'' ''
@ -405,9 +404,8 @@ fn html_highlight(code string, tb &table.Table) string {
else { else {
if token.is_key(tok.lit) || token.is_decl(tok.kind) { if token.is_key(tok.lit) || token.is_decl(tok.kind) {
tok_typ = .keyword tok_typ = .keyword
} else if tok.kind == .decl_assign || tok.kind.is_assign() || tok.is_unary() || } else if tok.kind == .decl_assign || tok.kind.is_assign() || tok.is_unary()
tok.kind.is_relational() || tok.kind.is_infix() || tok.kind.is_relational()|| tok.kind.is_infix() {
{
tok_typ = .operator tok_typ = .operator
} }
} }

View File

@ -467,8 +467,8 @@ fn parse_arguments(args []string) Config {
} $else { } $else {
cfg.input_path = cfg.input_path.replace('\\', os.path_separator) cfg.input_path = cfg.input_path.replace('\\', os.path_separator)
} }
is_path := cfg.input_path.ends_with('.v') || cfg.input_path.split(os.path_separator).len > is_path := cfg.input_path.ends_with('.v') || cfg.input_path.split(os.path_separator).len > 1
1 || cfg.input_path == '.' || cfg.input_path == '.'
if cfg.input_path.trim_right('/') == 'vlib' { if cfg.input_path.trim_right('/') == 'vlib' {
cfg.is_vlib = true cfg.is_vlib = true
cfg.is_multi = true cfg.is_multi = true

View File

@ -159,9 +159,8 @@ fn (mut vt Vet) vet_file(path string, is_regression_test bool) {
// vet_line vets the contents of `line` from `vet.file`. // vet_line vets the contents of `line` from `vet.file`.
fn (mut vet Vet) vet_line(lines []string, line string, lnumber int) { fn (mut vet Vet) vet_line(lines []string, line string, lnumber int) {
// Vet public functions // Vet public functions
if line.starts_with('pub fn') || if line.starts_with('pub fn') || (line.starts_with('fn ') && !(line.starts_with('fn C.')
(line.starts_with('fn ') && !(line.starts_with('fn C.') || line.starts_with('fn main'))) || line.starts_with('fn main'))) {
{
// Scan function declarations for missing documentation // Scan function declarations for missing documentation
is_pub_fn := line.starts_with('pub fn') is_pub_fn := line.starts_with('pub fn')
if lnumber > 0 { if lnumber > 0 {

View File

@ -2097,9 +2097,7 @@ fn (r Repo) find_user_by_id(id int) ?User {
fn main() { fn main() {
repo := Repo{ repo := Repo{
users: [User{1, 'Andrew'}, User{2, 'Bob'}, users: [User{1, 'Andrew'}, User{2, 'Bob'}, User{10, 'Charles'}]
User{10, 'Charles'},
]
} }
user := repo.find_user_by_id(10) or { // Option types must be handled by `or` blocks user := repo.find_user_by_id(10) or { // Option types must be handled by `or` blocks
return return

View File

@ -199,8 +199,7 @@ pub fn (b &Benchmark) total_message(msg string) string {
if b.nskip > 0 { if b.nskip > 0 {
tmsg += term.colorize(term.bold, term.colorize(term.yellow, '$b.nskip skipped')) + ', ' tmsg += term.colorize(term.bold, term.colorize(term.yellow, '$b.nskip skipped')) + ', '
} }
tmsg += '$b.ntotal total. ${term.colorize(term.bold, 'Runtime:')} ${b.bench_timer.elapsed().microseconds() / tmsg += '$b.ntotal total. ${term.colorize(term.bold, 'Runtime:')} ${b.bench_timer.elapsed().microseconds() / 1000} ms.\n'
1000} ms.\n'
tmsg += term.colorize(term.gray, msg) tmsg += term.colorize(term.gray, msg)
return tmsg return tmsg
} }

View File

@ -353,8 +353,8 @@ pub fn (input BitField) slice(_start int, _end int) BitField {
if start_offset != 0 { if start_offset != 0 {
for i in 0 .. output_slots - 1 { for i in 0 .. output_slots - 1 {
output.field[i] = u32(input.field[start_slot + i] >> u32(start_offset)) output.field[i] = u32(input.field[start_slot + i] >> u32(start_offset))
output.field[i] = output.field[i] | u32(input.field[start_slot + i + 1] << u32(slot_size - output.field[i] = output.field[i] | u32(input.field[start_slot + i +
start_offset)) 1] << u32(slot_size - start_offset))
} }
} else { } else {
for i in 0 .. output_slots - 1 { for i in 0 .. output_slots - 1 {
@ -458,8 +458,7 @@ fn (mut instance BitField) clear_tail() {
// create a mask for the tail // create a mask for the tail
mask := u32((1 << tail) - 1) mask := u32((1 << tail) - 1)
// clear the extra bits // clear the extra bits
instance.field[zbitnslots(instance.size) - 1] = instance.field[zbitnslots(instance.size) - instance.field[zbitnslots(instance.size) - 1] = instance.field[zbitnslots(instance.size) - 1] & mask
1] & mask
} }
} }

View File

@ -447,8 +447,8 @@ pub fn (mut a array) reverse_in_place() {
mut tmp_value := malloc(a.element_size) mut tmp_value := malloc(a.element_size)
for i in 0 .. a.len / 2 { for i in 0 .. a.len / 2 {
C.memcpy(tmp_value, byteptr(a.data) + i * a.element_size, a.element_size) C.memcpy(tmp_value, byteptr(a.data) + i * a.element_size, a.element_size)
C.memcpy(byteptr(a.data) + i * a.element_size, byteptr(a.data) + (a.len - 1 - i) * C.memcpy(byteptr(a.data) + i * a.element_size, byteptr(a.data) +
a.element_size, a.element_size) (a.len - 1 - i) * a.element_size, a.element_size)
C.memcpy(byteptr(a.data) + (a.len - 1 - i) * a.element_size, tmp_value, a.element_size) C.memcpy(byteptr(a.data) + (a.len - 1 - i) * a.element_size, tmp_value, a.element_size)
} }
free(tmp_value) free(tmp_value)

View File

@ -383,25 +383,20 @@ fn test_mutli_array_clone() {
assert b2_1 == [['1', '0', '3'], ['4', '5', '6']] assert b2_1 == [['1', '0', '3'], ['4', '5', '6']]
assert b2_2 == [['1', '2', '3'], ['0', '5', '6']] assert b2_2 == [['1', '2', '3'], ['0', '5', '6']]
// 3d array_int // 3d array_int
mut a3_1 := [[[1, 1], [2, 2], [3, 3]], mut a3_1 := [[[1, 1], [2, 2], [3, 3]], [[4, 4], [5, 5], [6, 6]]]
[[4, 4], [5, 5], [6, 6]],
]
mut a3_2 := a3_1.clone() mut a3_2 := a3_1.clone()
a3_1[0][0][1] = 0 a3_1[0][0][1] = 0
a3_2[0][1][0] = 0 a3_2[0][1][0] = 0
assert a3_1 == [[[1, 0], [2, 2], [3, 3]], [[4, 4], [5, 5], [6, 6]]] assert a3_1 == [[[1, 0], [2, 2], [3, 3]], [[4, 4], [5, 5], [6, 6]]]
assert a3_2 == [[[1, 1], [0, 2], [3, 3]], [[4, 4], [5, 5], [6, 6]]] assert a3_2 == [[[1, 1], [0, 2], [3, 3]], [[4, 4], [5, 5], [6, 6]]]
// 3d array_string // 3d array_string
mut b3_1 := [[['1', '1'], ['2', '2'], mut b3_1 := [[['1', '1'], ['2', '2'], ['3', '3']], [['4', '4'],
['3', '3'], ['5', '5'], ['6', '6']]]
], [['4', '4'], ['5', '5'], ['6', '6']]]
mut b3_2 := b3_1.clone() mut b3_2 := b3_1.clone()
b3_1[0][0][1] = '0' b3_1[0][0][1] = '0'
b3_2[0][1][0] = '0' b3_2[0][1][0] = '0'
assert b3_1 == assert b3_1 == [[['1', '0'], ['2', '2'], ['3', '3']], [['4', '4'], ['5', '5'], ['6', '6']]]
[[['1', '0'], ['2', '2'], ['3', '3']], [['4', '4'], ['5', '5'], ['6', '6']]] assert b3_2 == [[['1', '1'], ['0', '2'], ['3', '3']], [['4', '4'], ['5', '5'], ['6', '6']]]
assert b3_2 ==
[[['1', '1'], ['0', '2'], ['3', '3']], [['4', '4'], ['5', '5'], ['6', '6']]]
} }
fn test_doubling() { fn test_doubling() {
@ -718,8 +713,7 @@ fn test_sort() {
assert nums[3] == 67 assert nums[3] == 67
assert nums[4] == 108 assert nums[4] == 108
// //
mut users := [User{22, 'Peter'}, mut users := [User{22, 'Peter'}, User{20, 'Bob'}, User{25, 'Alice'}]
User{20, 'Bob'}, User{25, 'Alice'}]
users.sort(a.age < b.age) users.sort(a.age < b.age)
assert (users[0].age == 20) assert (users[0].age == 20)
assert (users[1].age == 22) assert (users[1].age == 22)

View File

@ -221,13 +221,13 @@ fn (mut flag Flag) parse(args []string, with_abbrev bool) ?[]string {
// matches returns `true` if first arg in `args` matches this flag. // matches returns `true` if first arg in `args` matches this flag.
fn (mut flag Flag) matches(args []string, with_abbrev bool) bool { fn (mut flag Flag) matches(args []string, with_abbrev bool) bool {
if with_abbrev { if with_abbrev {
return (flag.name != '' && args[0] == '--$flag.name') || return (flag.name != '' && args[0] == '--$flag.name')
(flag.name != '' && args[0].starts_with('--$flag.name=')) || || (flag.name != '' && args[0].starts_with('--$flag.name='))
(flag.abbrev != '' && args[0] == '-$flag.abbrev') || || (flag.abbrev != '' && args[0] == '-$flag.abbrev')
(flag.abbrev != '' && args[0].starts_with('-$flag.abbrev=')) || (flag.abbrev != '' && args[0].starts_with('-$flag.abbrev='))
} else { } else {
return (flag.name != '' && args[0] == '-$flag.name') || return (flag.name != '' && args[0] == '-$flag.name')
(flag.name != '' && args[0].starts_with('-$flag.name=')) || (flag.name != '' && args[0].starts_with('-$flag.name='))
} }
} }

View File

@ -111,8 +111,8 @@ fn (cmd Command) help_message() string {
} }
base_indent := ' '.repeat(base_indent_len) base_indent := ' '.repeat(base_indent_len)
description_indent := ' '.repeat(name_len - flag_name.len) description_indent := ' '.repeat(name_len - flag_name.len)
help += '$base_indent$flag_name$description_indent' + pretty_description(flag.description + help += '$base_indent$flag_name$description_indent' +
required, base_indent_len + name_len) + '\n' pretty_description(flag.description + required, base_indent_len + name_len) + '\n'
} }
} }
if cmd.commands.len > 0 { if cmd.commands.len > 0 {
@ -120,8 +120,8 @@ fn (cmd Command) help_message() string {
for command in cmd.commands { for command in cmd.commands {
base_indent := ' '.repeat(base_indent_len) base_indent := ' '.repeat(base_indent_len)
description_indent := ' '.repeat(name_len - command.name.len) description_indent := ' '.repeat(name_len - command.name.len)
help += '$base_indent$command.name$description_indent' + pretty_description(command.description, name_len) + help += '$base_indent$command.name$description_indent' +
'\n' pretty_description(command.description, name_len) + '\n'
} }
} }
return help return help

View File

@ -184,8 +184,8 @@ pub fn (ctx &Context) text_width(s string) int {
mut buf := [4]f32{} mut buf := [4]f32{}
C.fonsTextBounds(ctx.ft.fons, 0, 0, s.str, 0, buf) C.fonsTextBounds(ctx.ft.fons, 0, 0, s.str, 0, buf)
if s.ends_with(' ') { if s.ends_with(' ') {
return int((buf[2] - buf[0]) / return int((buf[2] - buf[0]) / ctx.scale) +
ctx.scale) + ctx.text_width('i') // TODO fix this in fontstash? ctx.text_width('i') // TODO fix this in fontstash?
} }
return int((buf[2] - buf[0]) / ctx.scale) return int((buf[2] - buf[0]) / ctx.scale)
} }

View File

@ -117,14 +117,12 @@ fn test_str() {
assert big.from_u64(4398046511104).str() == '4398046511104' assert big.from_u64(4398046511104).str() == '4398046511104'
assert big.from_int(4294967295).str() == '18446744073709551615' assert big.from_int(4294967295).str() == '18446744073709551615'
assert big.from_int(-1).str() == '18446744073709551615' assert big.from_int(-1).str() == '18446744073709551615'
assert big.from_hex_string('e'.repeat(80)).str() == assert big.from_hex_string('e'.repeat(80)).str() == '1993587900192849410235353592424915306962524220866209251950572167300738410728597846688097947807470'
'1993587900192849410235353592424915306962524220866209251950572167300738410728597846688097947807470'
} }
fn test_factorial() { fn test_factorial() {
f5 := big.factorial(big.from_u64(5)) f5 := big.factorial(big.from_u64(5))
assert f5.hexstr() == '78' assert f5.hexstr() == '78'
f100 := big.factorial(big.from_u64(100)) f100 := big.factorial(big.from_u64(100))
assert f100.hexstr() == assert f100.hexstr() == '1b30964ec395dc24069528d54bbda40d16e966ef9a70eb21b5b2943a321cdf10391745570cca9420c6ecb3b72ed2ee8b02ea2735c61a000000000000000000000000'
'1b30964ec395dc24069528d54bbda40d16e966ef9a70eb21b5b2943a321cdf10391745570cca9420c6ecb3b72ed2ee8b02ea2735c61a000000000000000000000000'
} }

View File

@ -107,8 +107,8 @@ pub fn ones_count_16(x u16) int {
// ones_count_32 returns the number of one bits ("population count") in x. // ones_count_32 returns the number of one bits ("population count") in x.
pub fn ones_count_32(x u32) int { pub fn ones_count_32(x u32) int {
return int(pop_8_tab[x >> 24] + pop_8_tab[x >> 16 & 0xff] + pop_8_tab[x >> 8 & 0xff] + pop_8_tab[x & return int(pop_8_tab[x >> 24] + pop_8_tab[x >> 16 & 0xff] + pop_8_tab[x >> 8 & 0xff] +
u32(0xff)]) pop_8_tab[x & u32(0xff)])
} }
// ones_count_64 returns the number of one bits ("population count") in x. // ones_count_64 returns the number of one bits ("population count") in x.

View File

@ -396,7 +396,7 @@ pub fn is_file(path string) bool {
// is_abs_path returns `true` if `path` is absolute. // is_abs_path returns `true` if `path` is absolute.
pub fn is_abs_path(path string) bool { pub fn is_abs_path(path string) bool {
$if windows { $if windows {
return path[0] == `/` || // incase we're in MingGW bash return path[0] == `/` || // incase we're in MingGW bash
(path[0].is_letter() && path[1] == `:`) (path[0].is_letter() && path[1] == `:`)
} }
return path[0] == `/` return path[0] == `/`

View File

@ -771,9 +771,8 @@ fn normalize_drive_letter(path string) string {
$if !windows { $if !windows {
return path return path
} }
if path.len > 2 && if path.len > 2 && path[0] >= `a` && path[0] <= `z` && path[1] == `:`
path[0] >= `a` && path[0] <= `z` && path[1] == `:` && path[2] == path_separator[0] && path[2] == path_separator[0] {
{
unsafe { unsafe {
x := &path.str[0] x := &path.str[0]
(*x) = *x - 32 (*x) = *x - 32

View File

@ -92,10 +92,8 @@ pub fn ls(path string) ?[]string {
} }
bptr := byteptr(ent.d_name) bptr := byteptr(ent.d_name)
unsafe { unsafe {
if bptr[0] == 0 || if bptr[0] == 0 || (bptr[0] == `.` && bptr[1] == 0)
(bptr[0] == `.` && bptr[1] == 0) || || (bptr[0] == `.` && bptr[1] == `.` && bptr[2] == 0) {
(bptr[0] == `.` && bptr[1] == `.` && bptr[2] == 0)
{
continue continue
} }
} }

View File

@ -135,7 +135,8 @@ pub fn mkdir(path string) ?bool {
} }
apath := real_path(path) apath := real_path(path)
if !C.CreateDirectory(apath.to_wide(), 0) { if !C.CreateDirectory(apath.to_wide(), 0) {
return error('mkdir failed for "$apath", because CreateDirectory returned ' + get_error_msg(int(C.GetLastError()))) return error('mkdir failed for "$apath", because CreateDirectory returned ' +
get_error_msg(int(C.GetLastError())))
} }
return true return true
} }

View File

@ -8,8 +8,8 @@ fn version_satisfies(ver Version, input string) bool {
} }
fn compare_eq(v1 Version, v2 Version) bool { fn compare_eq(v1 Version, v2 Version) bool {
return v1.major == v2.major && return v1.major == v2.major && v1.minor == v2.minor && v1.patch == v2.patch
v1.minor == v2.minor && v1.patch == v2.patch && v1.prerelease == v2.prerelease && v1.prerelease == v2.prerelease
} }
fn compare_gt(v1 Version, v2 Version) bool { fn compare_gt(v1 Version, v2 Version) bool {

View File

@ -43,8 +43,9 @@ fn (ver RawVersion) is_valid() bool {
if ver.raw_ints.len != 3 { if ver.raw_ints.len != 3 {
return false return false
} }
return is_valid_number(ver.raw_ints[ver_major]) && is_valid_number(ver.raw_ints[ver_minor]) && return is_valid_number(ver.raw_ints[ver_major]) && is_valid_number(ver.raw_ints[ver_minor])
is_valid_number(ver.raw_ints[ver_patch]) && is_valid_string(ver.prerelease) && is_valid_string(ver.metadata) && is_valid_number(ver.raw_ints[ver_patch]) && is_valid_string(ver.prerelease)
&& is_valid_string(ver.metadata)
} }
fn (ver RawVersion) is_missing(typ int) bool { fn (ver RawVersion) is_missing(typ int) bool {

View File

@ -139,8 +139,8 @@ fn parse_xrange(input string) ?Version {
} }
fn can_expand(input string) bool { fn can_expand(input string) bool {
return input[0] == `~` || return input[0] == `~` || input[0] == `^` || input.contains(hyphen_range_sep)
input[0] == `^` || input.contains(hyphen_range_sep) || input.index_any(x_range_symbols) > -1 || input.index_any(x_range_symbols) > -1
} }
fn expand_comparator_set(input string) ?ComparatorSet { fn expand_comparator_set(input string) ?ComparatorSet {

View File

@ -71,8 +71,11 @@ pub fn colorize(cfn fn (string) string, s string) string {
// If an empty string is passed in, print enough spaces to make a new line // If an empty string is passed in, print enough spaces to make a new line
pub fn h_divider(divider string) string { pub fn h_divider(divider string) string {
cols, _ := get_terminal_size() cols, _ := get_terminal_size()
result := if divider.len > 0 { divider.repeat(1 + (cols / divider.len)) } else { ' '.repeat(1 + result := if divider.len > 0 {
cols) } divider.repeat(1 + (cols / divider.len))
} else {
' '.repeat(1 + cols)
}
return result[0..cols] return result[0..cols]
} }
@ -85,12 +88,18 @@ pub fn header(text string, divider string) string {
} }
xcols, _ := get_terminal_size() xcols, _ := get_terminal_size()
cols := imax(1, xcols) cols := imax(1, xcols)
tlimit := imax(1, if cols > text.len + 2 + 2 * divider.len { text.len } else { cols - 3 - tlimit := imax(1, if cols > text.len + 2 + 2 * divider.len {
2 * divider.len }) text.len
} else {
cols - 3 - 2 * divider.len
})
tlimit_alligned := if (tlimit % 2) != (cols % 2) { tlimit + 1 } else { tlimit } tlimit_alligned := if (tlimit % 2) != (cols % 2) { tlimit + 1 } else { tlimit }
tstart := imax(0, (cols - tlimit_alligned) / 2) tstart := imax(0, (cols - tlimit_alligned) / 2)
ln := if divider.len > 0 { divider.repeat(1 + cols / divider.len)[0..cols] } else { ' '.repeat(1 + ln := if divider.len > 0 {
cols) } divider.repeat(1 + cols / divider.len)[0..cols]
} else {
' '.repeat(1 + cols)
}
if ln.len == 1 { if ln.len == 1 {
return ln + ' ' + text[0..tlimit] + ' ' + ln return ln + ' ' + text[0..tlimit] + ' ' + ln
} }

View File

@ -6,8 +6,8 @@ fn test_parse() {
assert false assert false
return return
} }
assert t.year == 2018 && assert t.year == 2018 && t.month == 1 && t.day == 27 && t.hour == 12 && t.minute == 48
t.month == 1 && t.day == 27 && t.hour == 12 && t.minute == 48 && t.second == 34 && t.second == 34
assert t.unix == 1517057314 assert t.unix == 1517057314
} }
@ -26,16 +26,16 @@ fn test_parse_rfc2822() {
assert false assert false
return return
} }
assert t1.year == 2019 && assert t1.year == 2019 && t1.month == 12 && t1.day == 12 && t1.hour == 6 && t1.minute == 7
t1.month == 12 && t1.day == 12 && t1.hour == 6 && t1.minute == 7 && t1.second == 45 && t1.second == 45
assert t1.unix == 1576130865 assert t1.unix == 1576130865
s2 := 'Thu 12 Dec 2019 06:07:45 +0800' s2 := 'Thu 12 Dec 2019 06:07:45 +0800'
t2 := time.parse_rfc2822(s2) or { t2 := time.parse_rfc2822(s2) or {
assert false assert false
return return
} }
assert t2.year == 2019 && assert t2.year == 2019 && t2.month == 12 && t2.day == 12 && t2.hour == 6 && t2.minute == 7
t2.month == 12 && t2.day == 12 && t2.hour == 6 && t2.minute == 7 && t2.second == 45 && t2.second == 45
assert t2.unix == 1576130865 assert t2.unix == 1576130865
} }

View File

@ -87,14 +87,12 @@ fn test_format_ss() {
} }
fn test_format_ss_milli() { fn test_format_ss_milli() {
assert '11.07.1980 21:23:42.123' == assert '11.07.1980 21:23:42.123' == time_to_test.get_fmt_str(.dot, .hhmmss24_milli, .ddmmyyyy)
time_to_test.get_fmt_str(.dot, .hhmmss24_milli, .ddmmyyyy)
assert '1980-07-11 21:23:42.123' == time_to_test.format_ss_milli() assert '1980-07-11 21:23:42.123' == time_to_test.format_ss_milli()
} }
fn test_format_ss_micro() { fn test_format_ss_micro() {
assert '11.07.1980 21:23:42.123456' == assert '11.07.1980 21:23:42.123456' == time_to_test.get_fmt_str(.dot, .hhmmss24_micro, .ddmmyyyy)
time_to_test.get_fmt_str(.dot, .hhmmss24_micro, .ddmmyyyy)
assert '1980-07-11 21:23:42.123456' == time_to_test.format_ss_micro() assert '1980-07-11 21:23:42.123456' == time_to_test.format_ss_micro()
} }

View File

@ -133,8 +133,9 @@ pub fn (node &FnDecl) stringify(t &table.Table, cur_mod string, m2a map[string]s
// must be enclosed in braces. // must be enclosed in braces.
pub fn (lit &StringInterLiteral) get_fspec_braces(i int) (string, bool) { pub fn (lit &StringInterLiteral) get_fspec_braces(i int) (string, bool) {
mut res := []string{} mut res := []string{}
needs_fspec := lit.need_fmts[i] || lit.pluss[i] || needs_fspec := lit.need_fmts[i] || lit.pluss[i]
(lit.fills[i] && lit.fwidths[i] >= 0) || lit.fwidths[i] != 0 || lit.precisions[i] != 987698 || (lit.fills[i] && lit.fwidths[i] >= 0) || lit.fwidths[i] != 0
|| lit.precisions[i] != 987698
mut needs_braces := needs_fspec mut needs_braces := needs_fspec
if !needs_braces { if !needs_braces {
if i + 1 < lit.vals.len && lit.vals[i + 1].len > 0 { if i + 1 < lit.vals.len && lit.vals[i + 1].len > 0 {

View File

@ -239,9 +239,8 @@ pub fn (b &Builder) find_module_path(mod string, fpath string) ?string {
vmod_file_location := mcache.get_by_file(fpath) vmod_file_location := mcache.get_by_file(fpath)
mod_path := module_path(mod) mod_path := module_path(mod)
mut module_lookup_paths := []string{} mut module_lookup_paths := []string{}
if vmod_file_location.vmod_file.len != 0 && if vmod_file_location.vmod_file.len != 0
vmod_file_location.vmod_folder !in b.module_search_paths && vmod_file_location.vmod_folder !in b.module_search_paths {
{
module_lookup_paths << vmod_file_location.vmod_folder module_lookup_paths << vmod_file_location.vmod_folder
} }
module_lookup_paths << b.module_search_paths module_lookup_paths << b.module_search_paths

View File

@ -211,9 +211,8 @@ fn (mut v Builder) setup_ccompiler_options(ccompiler string) {
// deliberately guessing only for -prod builds for performance reasons // deliberately guessing only for -prod builds for performance reasons
if ccversion := os.exec('cc --version') { if ccversion := os.exec('cc --version') {
if ccversion.exit_code == 0 { if ccversion.exit_code == 0 {
if ccversion.output.contains('This is free software;') && if ccversion.output.contains('This is free software;')
ccversion.output.contains('Free Software Foundation, Inc.') && ccversion.output.contains('Free Software Foundation, Inc.') {
{
ccoptions.guessed_compiler = 'gcc' ccoptions.guessed_compiler = 'gcc'
} }
if ccversion.output.contains('clang version ') { if ccversion.output.contains('clang version ') {
@ -353,9 +352,8 @@ fn (mut v Builder) setup_ccompiler_options(ccompiler string) {
} }
// Without these libs compilation will fail on Linux // Without these libs compilation will fail on Linux
// || os.user_os() == 'linux' // || os.user_os() == 'linux'
if !v.pref.is_bare && v.pref.build_mode != .build_module && v.pref.os in if !v.pref.is_bare && v.pref.build_mode != .build_module
[.linux, .freebsd, .openbsd, .netbsd, .dragonfly, .solaris, .haiku] && v.pref.os in [.linux, .freebsd, .openbsd, .netbsd, .dragonfly, .solaris, .haiku] {
{
ccoptions.linker_flags << '-lm' ccoptions.linker_flags << '-lm'
ccoptions.linker_flags << '-lpthread' ccoptions.linker_flags << '-lpthread'
// -ldl is a Linux only thing. BSDs have it in libc. // -ldl is a Linux only thing. BSDs have it in libc.
@ -402,9 +400,8 @@ fn (ccoptions CcompilerOptions) thirdparty_object_args(middle []string) []string
} }
fn (mut v Builder) setup_output_name() { fn (mut v Builder) setup_output_name() {
if !v.pref.is_shared && v.pref.build_mode != .build_module && os.user_os() == 'windows' && if !v.pref.is_shared && v.pref.build_mode != .build_module && os.user_os() == 'windows'
!v.pref.out_name.ends_with('.exe') && !v.pref.out_name.ends_with('.exe') {
{
v.pref.out_name += '.exe' v.pref.out_name += '.exe'
} }
// Output executable name // Output executable name
@ -658,8 +655,10 @@ fn (mut v Builder) cc() {
if res.exit_code == 127 { if res.exit_code == 127 {
verror('C compiler error, while attempting to run: \n' + verror('C compiler error, while attempting to run: \n' +
'-----------------------------------------------------------\n' + '$cmd\n' + '-----------------------------------------------------------\n' + '$cmd\n' +
'-----------------------------------------------------------\n' + 'Probably your C compiler is missing. \n' + '-----------------------------------------------------------\n' +
'Please reinstall it, or make it available in your PATH.\n\n' + missing_compiler_info()) 'Probably your C compiler is missing. \n' +
'Please reinstall it, or make it available in your PATH.\n\n' +
missing_compiler_info())
} }
} }
if !v.pref.show_c_output { if !v.pref.show_c_output {
@ -716,8 +715,7 @@ fn (mut v Builder) cc() {
println('install upx with `brew install upx`') println('install upx with `brew install upx`')
} }
$if linux { $if linux {
println('install upx\n' + println('install upx\n' + 'for example, on Debian/Ubuntu run `sudo apt install upx`')
'for example, on Debian/Ubuntu run `sudo apt install upx`')
} }
$if windows { $if windows {
// :) // :)

View File

@ -14,15 +14,13 @@ fn (mut v Builder) get_os_cflags() []cflag.CFlag {
if flag.value.ends_with('.o') { if flag.value.ends_with('.o') {
flag.cached = v.pref.cache_manager.postfix_with_key2cpath('.o', os.real_path(flag.value)) flag.cached = v.pref.cache_manager.postfix_with_key2cpath('.o', os.real_path(flag.value))
} }
if flag.os == '' || if flag.os == '' || (flag.os == 'linux' && v.pref.os == .linux)
(flag.os == 'linux' && v.pref.os == .linux) || || (flag.os == 'macos' && v.pref.os == .macos)
(flag.os == 'macos' && v.pref.os == .macos) || || (flag.os == 'darwin' && v.pref.os == .macos)
(flag.os == 'darwin' && v.pref.os == .macos) || || (flag.os == 'freebsd' && v.pref.os == .freebsd)
(flag.os == 'freebsd' && v.pref.os == .freebsd) || || (flag.os == 'windows' && v.pref.os == .windows)
(flag.os == 'windows' && v.pref.os == .windows) || || (flag.os == 'mingw' && v.pref.os == .windows && v.pref.ccompiler != 'msvc')
(flag.os == 'mingw' && v.pref.os == .windows && v.pref.ccompiler != 'msvc') || || (flag.os == 'solaris' && v.pref.os == .solaris) {
(flag.os == 'solaris' && v.pref.os == .solaris)
{
flags << flag flags << flag
} }
if flag.os in ctimedefines { if flag.os in ctimedefines {

View File

@ -15,7 +15,8 @@ fn (mut b Builder) get_vtmp_filename(base_file_name string, postfix string) stri
if !b.pref.reuse_tmpc { if !b.pref.reuse_tmpc {
uniq = '.$rand.u64()' uniq = '.$rand.u64()'
} }
return os.real_path(os.join_path(vtmp, os.file_name(os.real_path(base_file_name)) + '$uniq$postfix')) return os.real_path(os.join_path(vtmp, os.file_name(os.real_path(base_file_name)) +
'$uniq$postfix'))
} }
pub fn compile(command string, pref &pref.Preferences) { pub fn compile(command string, pref &pref.Preferences) {

View File

@ -81,8 +81,8 @@ fn find_windows_kit_root(host_arch string) ?WindowsKit {
$if windows { $if windows {
root_key := RegKey(0) root_key := RegKey(0)
path := 'SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots' path := 'SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots'
rc := C.RegOpenKeyEx(hkey_local_machine, path.to_wide(), 0, key_query_value | key_wow64_32key | rc := C.RegOpenKeyEx(hkey_local_machine, path.to_wide(), 0, key_query_value | key_wow64_32key | key_enumerate_sub_keys,
key_enumerate_sub_keys, &root_key) &root_key)
// TODO: Fix defer inside ifs // TODO: Fix defer inside ifs
// defer { // defer {
// C.RegCloseKey(root_key) // C.RegCloseKey(root_key)

View File

@ -8,7 +8,8 @@ import v.gen.x64
pub fn (mut b Builder) build_x64(v_files []string, out_file string) { pub fn (mut b Builder) build_x64(v_files []string, out_file string) {
$if !linux { $if !linux {
println('v -x64 can only generate Linux binaries for now') println('v -x64 can only generate Linux binaries for now')
println('You are not on a Linux system, so you will not ' + 'be able to run the resulting executable') println('You are not on a Linux system, so you will not ' +
'be able to run the resulting executable')
} }
b.timing_start('PARSE') b.timing_start('PARSE')
b.parsed_files = parser.parse_files(v_files, b.table, b.pref, b.global_scope) b.parsed_files = parser.parse_files(v_files, b.table, b.pref, b.global_scope)

View File

@ -56,26 +56,20 @@ pub fn (mut c Checker) check_basic(got table.Type, expected table.Type) bool {
return true return true
} }
// TODO i64 as int etc // TODO i64 as int etc
if (exp_idx in table.pointer_type_idxs || if (exp_idx in table.pointer_type_idxs || exp_idx in table.number_type_idxs)
exp_idx in table.number_type_idxs) && && (got_idx in table.pointer_type_idxs || got_idx in table.number_type_idxs) {
(got_idx in table.pointer_type_idxs || got_idx in table.number_type_idxs)
{
return true return true
} }
// if exp_idx in pointer_type_idxs && got_idx in pointer_type_idxs { // if exp_idx in pointer_type_idxs && got_idx in pointer_type_idxs {
// return true // return true
// } // }
// see hack in checker IndexExpr line #691 // see hack in checker IndexExpr line #691
if (got_idx == table.byte_type_idx && if (got_idx == table.byte_type_idx && exp_idx == table.byteptr_type_idx)
exp_idx == table.byteptr_type_idx) || || (exp_idx == table.byte_type_idx && got_idx == table.byteptr_type_idx) {
(exp_idx == table.byte_type_idx && got_idx == table.byteptr_type_idx)
{
return true return true
} }
if (got_idx == table.char_type_idx && if (got_idx == table.char_type_idx && exp_idx == table.charptr_type_idx)
exp_idx == table.charptr_type_idx) || || (exp_idx == table.char_type_idx && got_idx == table.charptr_type_idx) {
(exp_idx == table.char_type_idx && got_idx == table.charptr_type_idx)
{
return true return true
} }
// TODO: this should no longer be needed // TODO: this should no longer be needed
@ -110,16 +104,13 @@ pub fn (mut c Checker) check_basic(got table.Type, expected table.Type) bool {
} }
// TODO // TODO
// accept [] when an expected type is an array // accept [] when an expected type is an array
if got_type_sym.kind == .array && if got_type_sym.kind == .array && exp_type_sym.kind == .array
exp_type_sym.kind == .array && got_type_sym.name == 'array_void' && got_type_sym.name == 'array_void' {
{
return true return true
} }
// type alias // type alias
if (got_type_sym.kind == .alias && if (got_type_sym.kind == .alias && got_type_sym.parent_idx == exp_idx)
got_type_sym.parent_idx == exp_idx) || || (exp_type_sym.kind == .alias && exp_type_sym.parent_idx == got_idx) {
(exp_type_sym.kind == .alias && exp_type_sym.parent_idx == got_idx)
{
return true return true
} }
// sum type // sum type
@ -242,9 +233,8 @@ fn (c &Checker) promote_num(left_type table.Type, right_type table.Type) table.T
} }
} else if idx_lo >= table.byte_type_idx { // both operands are unsigned } else if idx_lo >= table.byte_type_idx { // both operands are unsigned
return type_hi return type_hi
} else if idx_lo >= table.i8_type_idx && } else if idx_lo >= table.i8_type_idx
(idx_hi <= table.i64_type_idx || idx_hi == table.rune_type_idx) && (idx_hi <= table.i64_type_idx || idx_hi == table.rune_type_idx) { // both signed
{ // both signed
return if idx_lo == table.i64_type_idx { return if idx_lo == table.i64_type_idx {
type_lo type_lo
} else { } else {
@ -363,11 +353,9 @@ pub fn (c &Checker) get_default_fmt(ftyp table.Type, typ table.Type) byte {
if sym.kind == .function { if sym.kind == .function {
return `s` return `s`
} }
if ftyp in [table.string_type, table.bool_type] || if ftyp in [table.string_type, table.bool_type]
sym.kind in || sym.kind in [.enum_, .array, .array_fixed, .struct_, .map, .multi_return, .sum_type, .none_]
[.enum_, .array, .array_fixed, .struct_, .map, .multi_return, .sum_type, .none_] || || ftyp.has_flag(.optional)|| sym.has_method('str') {
ftyp.has_flag(.optional) || sym.has_method('str')
{
return `s` return `s`
} else { } else {
return `_` return `_`
@ -382,9 +370,7 @@ pub fn (mut c Checker) string_inter_lit(mut node ast.StringInterLiteral) table.T
typ := c.table.unalias_num_type(ftyp) typ := c.table.unalias_num_type(ftyp)
mut fmt := node.fmts[i] mut fmt := node.fmts[i]
// analyze and validate format specifier // analyze and validate format specifier
if fmt !in if fmt !in [`E`, `F`, `G`, `e`, `f`, `g`, `d`, `u`, `x`, `X`, `o`, `c`, `s`, `p`, `_`] {
[`E`, `F`, `G`, `e`, `f`, `g`, `d`, `u`, `x`, `X`, `o`, `c`, `s`, `p`, `_`]
{
c.error('unknown format specifier `${fmt:c}`', node.fmt_poss[i]) c.error('unknown format specifier `${fmt:c}`', node.fmt_poss[i])
} }
if fmt == `_` { // set default representation for type if none has been given if fmt == `_` { // set default representation for type if none has been given
@ -405,14 +391,13 @@ pub fn (mut c Checker) string_inter_lit(mut node ast.StringInterLiteral) table.T
if node.pluss[i] && !typ.is_number() { if node.pluss[i] && !typ.is_number() {
c.error('plus prefix only allowed for numbers', node.fmt_poss[i]) c.error('plus prefix only allowed for numbers', node.fmt_poss[i])
} }
if (typ.is_unsigned() && fmt !in [`u`, `x`, `X`, `o`, `c`]) || if (typ.is_unsigned() && fmt !in [`u`, `x`, `X`, `o`, `c`])
(typ.is_signed() && fmt !in [`d`, `x`, `X`, `o`, `c`]) || || (typ.is_signed() && fmt !in [`d`, `x`, `X`, `o`, `c`])
(typ.is_int_literal() && fmt !in [`d`, `c`, `x`, `X`, `o`, `u`, `x`, `X`, `o`]) || || (typ.is_int_literal() && fmt !in [`d`, `c`, `x`, `X`, `o`, `u`, `x`, `X`, `o`])
(typ.is_float() && fmt !in [`E`, `F`, `G`, `e`, `f`, `g`]) || || (typ.is_float() && fmt !in [`E`, `F`, `G`, `e`, `f`, `g`])
(typ.is_pointer() && fmt !in [`p`, `x`, `X`]) || || (typ.is_pointer() && fmt !in [`p`, `x`, `X`])
(typ.is_string() && fmt != `s`) || || (typ.is_string() && fmt != `s`)
(typ.idx() in [table.i64_type_idx, table.f64_type_idx] && fmt == `c`) || (typ.idx() in [table.i64_type_idx, table.f64_type_idx] && fmt == `c`) {
{
c.error('illegal format specifier `${fmt:c}` for type `${c.table.get_type_name(ftyp)}`', c.error('illegal format specifier `${fmt:c}` for type `${c.table.get_type_name(ftyp)}`',
node.fmt_poss[i]) node.fmt_poss[i])
} }
@ -456,10 +441,8 @@ pub fn (mut c Checker) infer_fn_types(f table.Fn, mut call_expr ast.CallExpr) {
mut arg_elem_sym := c.table.get_type_symbol(arg_elem_info.elem_type) mut arg_elem_sym := c.table.get_type_symbol(arg_elem_info.elem_type)
mut param_elem_sym := c.table.get_type_symbol(param_elem_info.elem_type) mut param_elem_sym := c.table.get_type_symbol(param_elem_info.elem_type)
for { for {
if arg_elem_sym.kind == .array && if arg_elem_sym.kind == .array && param_elem_sym.kind == .array
param_elem_sym.kind == .array && c.cur_fn.generic_params.filter(it.name == && c.cur_fn.generic_params.filter(it.name == param_elem_sym.name).len == 0 {
param_elem_sym.name).len == 0
{
arg_elem_info = arg_elem_sym.info as table.Array arg_elem_info = arg_elem_sym.info as table.Array
arg_elem_sym = c.table.get_type_symbol(arg_elem_info.elem_type) arg_elem_sym = c.table.get_type_symbol(arg_elem_info.elem_type)
param_elem_info = param_elem_sym.info as table.Array param_elem_info = param_elem_sym.info as table.Array

View File

@ -562,16 +562,15 @@ pub fn (mut c Checker) struct_init(mut struct_init ast.StructInit) table.Type {
return table.void_type return table.void_type
} }
} }
if !type_sym.is_public && type_sym.kind != .placeholder && type_sym.mod != c.mod && if !type_sym.is_public && type_sym.kind != .placeholder && type_sym.mod != c.mod
type_sym.language != .c && type_sym.language != .c {
{
c.error('type `$type_sym.name` is private', struct_init.pos) c.error('type `$type_sym.name` is private', struct_init.pos)
} }
if type_sym.kind == .struct_ { if type_sym.kind == .struct_ {
info := type_sym.info as table.Struct info := type_sym.info as table.Struct
if info.attrs.len > 0 && info.attrs[0].name == 'noinit' && type_sym.mod != c.mod { if info.attrs.len > 0 && info.attrs[0].name == 'noinit' && type_sym.mod != c.mod {
c.error('struct `$type_sym.name` is declared with a `[noinit]` attribute, so ' + 'it cannot be initialized with `$type_sym.name{}`', c.error('struct `$type_sym.name` is declared with a `[noinit]` attribute, so ' +
struct_init.pos) 'it cannot be initialized with `$type_sym.name{}`', struct_init.pos)
} }
} }
if type_sym.name.len == 1 && c.cur_fn.generic_params.len == 0 { if type_sym.name.len == 1 && c.cur_fn.generic_params.len == 0 {
@ -681,9 +680,8 @@ pub fn (mut c Checker) struct_init(mut struct_init ast.StructInit) table.Type {
field.pos) field.pos)
} }
} }
if info_field.typ.is_ptr() && !expr_type.is_ptr() && !expr_type.is_pointer() && if info_field.typ.is_ptr() && !expr_type.is_ptr() && !expr_type.is_pointer()
!expr_type.is_number() && !expr_type.is_number() {
{
c.error('ref', field.pos) c.error('ref', field.pos)
} }
struct_init.fields[i].typ = expr_type struct_init.fields[i].typ = expr_type
@ -833,14 +831,12 @@ pub fn (mut c Checker) infix_expr(mut infix_expr ast.InfixExpr) table.Type {
return table.bool_type return table.bool_type
} }
.plus, .minus, .mul, .div, .mod, .xor, .amp, .pipe { // binary operators that expect matching types .plus, .minus, .mul, .div, .mod, .xor, .amp, .pipe { // binary operators that expect matching types
if right.info is table.Alias && if right.info is table.Alias
(right.info as table.Alias).language != .c && c.mod == c.table.type_to_str(right_type).split('.')[0] && (right.info as table.Alias).language != .c && c.mod == c.table.type_to_str(right_type).split('.')[0] {
{
right = c.table.get_type_symbol((right.info as table.Alias).parent_type) right = c.table.get_type_symbol((right.info as table.Alias).parent_type)
} }
if left.info is table.Alias && if left.info is table.Alias
(left.info as table.Alias).language != .c && c.mod == c.table.type_to_str(left_type).split('.')[0] && (left.info as table.Alias).language != .c && c.mod == c.table.type_to_str(left_type).split('.')[0] {
{
left = c.table.get_type_symbol((left.info as table.Alias).parent_type) left = c.table.get_type_symbol((left.info as table.Alias).parent_type)
} }
if left.kind in [.array, .array_fixed, .map, .struct_] { if left.kind in [.array, .array_fixed, .map, .struct_] {
@ -944,9 +940,8 @@ pub fn (mut c Checker) infix_expr(mut infix_expr ast.InfixExpr) table.Type {
// []T << T // []T << T
return table.void_type return table.void_type
} }
if right.kind == .array && if right.kind == .array
c.check_types(left_value_type, c.table.value_type(right_type)) && c.check_types(left_value_type, c.table.value_type(right_type)) {
{
// []T << []T // []T << []T
return table.void_type return table.void_type
} }
@ -1017,9 +1012,8 @@ pub fn (mut c Checker) infix_expr(mut infix_expr ast.InfixExpr) table.Type {
if left_type == table.bool_type && infix_expr.op !in [.eq, .ne, .logical_or, .and] { if left_type == table.bool_type && infix_expr.op !in [.eq, .ne, .logical_or, .and] {
c.error('bool types only have the following operators defined: `==`, `!=`, `||`, and `&&`', c.error('bool types only have the following operators defined: `==`, `!=`, `||`, and `&&`',
infix_expr.pos) infix_expr.pos)
} else if left_type == table.string_type && } else if left_type == table.string_type
infix_expr.op !in [.plus, .eq, .ne, .lt, .gt, .le, .ge] && infix_expr.op !in [.plus, .eq, .ne, .lt, .gt, .le, .ge] {
{
// TODO broken !in // TODO broken !in
c.error('string types only have the following operators defined: `==`, `!=`, `<`, `>`, `<=`, `>=`, and `+`', c.error('string types only have the following operators defined: `==`, `!=`, `<`, `>`, `<=`, `>=`, and `+`',
infix_expr.pos) infix_expr.pos)
@ -1221,15 +1215,15 @@ pub fn (mut c Checker) call_expr(mut call_expr ast.CallExpr) table.Type {
// Now call `call_method` or `call_fn` for specific checks. // Now call `call_method` or `call_fn` for specific checks.
typ := if call_expr.is_method { c.call_method(mut call_expr) } else { c.call_fn(mut call_expr) } typ := if call_expr.is_method { c.call_method(mut call_expr) } else { c.call_fn(mut call_expr) }
// autofree: mark args that have to be freed (after saving them in tmp exprs) // autofree: mark args that have to be freed (after saving them in tmp exprs)
free_tmp_arg_vars := c.pref.autofree && !c.is_builtin_mod && call_expr.args.len > 0 && !call_expr.args[0].typ.has_flag(.optional) free_tmp_arg_vars := c.pref.autofree && !c.is_builtin_mod && call_expr.args.len > 0
&& !call_expr.args[0].typ.has_flag(.optional)
if free_tmp_arg_vars && !c.inside_const { if free_tmp_arg_vars && !c.inside_const {
for i, arg in call_expr.args { for i, arg in call_expr.args {
if arg.typ != table.string_type { if arg.typ != table.string_type {
continue continue
} }
if arg.expr is ast.Ident || if arg.expr is ast.Ident || arg.expr is ast.StringLiteral
arg.expr is ast.StringLiteral || arg.expr is ast.SelectorExpr || arg.expr is ast.SelectorExpr {
{
// Simple expressions like variables, string literals, selector expressions // Simple expressions like variables, string literals, selector expressions
// (`x.field`) can't result in allocations and don't need to be assigned to // (`x.field`) can't result in allocations and don't need to be assigned to
// temporary vars. // temporary vars.
@ -1239,9 +1233,9 @@ pub fn (mut c Checker) call_expr(mut call_expr ast.CallExpr) table.Type {
call_expr.args[i].is_tmp_autofree = true call_expr.args[i].is_tmp_autofree = true
} }
// TODO copy pasta from above // TODO copy pasta from above
if call_expr.receiver_type == table.string_type && !(call_expr.left is ast.Ident || if call_expr.receiver_type == table.string_type && !(call_expr.left is ast.Ident
call_expr.left is ast.StringLiteral || call_expr.left is ast.SelectorExpr) || call_expr.left is ast.StringLiteral
{ || call_expr.left is ast.SelectorExpr) {
call_expr.free_receiver = true call_expr.free_receiver = true
} }
} }
@ -1263,13 +1257,11 @@ fn (mut c Checker) check_map_and_filter(is_map bool, elem_typ table.Type, call_e
ast.AnonFn { ast.AnonFn {
if arg_expr.decl.params.len > 1 { if arg_expr.decl.params.len > 1 {
c.error('function needs exactly 1 argument', arg_expr.decl.pos) c.error('function needs exactly 1 argument', arg_expr.decl.pos)
} else if is_map && } else if is_map
(arg_expr.decl.return_type == table.void_type || arg_expr.decl.params[0].typ != elem_typ) && (arg_expr.decl.return_type == table.void_type || arg_expr.decl.params[0].typ != elem_typ) {
{
c.error('type mismatch, should use `fn(a $elem_sym.name) T {...}`', arg_expr.decl.pos) c.error('type mismatch, should use `fn(a $elem_sym.name) T {...}`', arg_expr.decl.pos)
} else if !is_map && } else if !is_map
(arg_expr.decl.return_type != table.bool_type || arg_expr.decl.params[0].typ != elem_typ) && (arg_expr.decl.return_type != table.bool_type || arg_expr.decl.params[0].typ != elem_typ) {
{
c.error('type mismatch, should use `fn(a $elem_sym.name) bool {...}`', c.error('type mismatch, should use `fn(a $elem_sym.name) bool {...}`',
arg_expr.decl.pos) arg_expr.decl.pos)
} }
@ -1282,14 +1274,12 @@ fn (mut c Checker) check_map_and_filter(is_map bool, elem_typ table.Type, call_e
} }
if func.params.len > 1 { if func.params.len > 1 {
c.error('function needs exactly 1 argument', call_expr.pos) c.error('function needs exactly 1 argument', call_expr.pos)
} else if is_map && } else if is_map
(func.return_type == table.void_type || func.params[0].typ != elem_typ) && (func.return_type == table.void_type || func.params[0].typ != elem_typ) {
{
c.error('type mismatch, should use `fn(a $elem_sym.name) T {...}`', c.error('type mismatch, should use `fn(a $elem_sym.name) T {...}`',
arg_expr.pos) arg_expr.pos)
} else if !is_map && } else if !is_map
(func.return_type != table.bool_type || func.params[0].typ != elem_typ) && (func.return_type != table.bool_type || func.params[0].typ != elem_typ) {
{
c.error('type mismatch, should use `fn(a $elem_sym.name) bool {...}`', c.error('type mismatch, should use `fn(a $elem_sym.name) bool {...}`',
arg_expr.pos) arg_expr.pos)
} }
@ -1357,7 +1347,8 @@ pub fn (mut c Checker) call_method(mut call_expr ast.CallExpr) table.Type {
// Verify `.sort(a < b)` // Verify `.sort(a < b)`
if call_expr.args.len > 0 { if call_expr.args.len > 0 {
if call_expr.args[0].expr !is ast.InfixExpr { if call_expr.args[0].expr !is ast.InfixExpr {
c.error('`.sort()` requires a `<` or `>` comparison as the first and only argument' + c.error(
'`.sort()` requires a `<` or `>` comparison as the first and only argument' +
'\ne.g. `users.sort(a.id < b.id)`', call_expr.pos) '\ne.g. `users.sort(a.id < b.id)`', call_expr.pos)
} }
} }
@ -1473,9 +1464,8 @@ pub fn (mut c Checker) call_method(mut call_expr ast.CallExpr) table.Type {
} }
} }
if has_method { if has_method {
if !method.is_pub && !c.is_builtin_mod && !c.pref.is_test && left_type_sym.mod != c.mod && if !method.is_pub && !c.is_builtin_mod && !c.pref.is_test && left_type_sym.mod != c.mod
left_type_sym.mod != '' && left_type_sym.mod != '' { // method.mod != c.mod {
{ // method.mod != c.mod {
// If a private method is called outside of the module // If a private method is called outside of the module
// its receiver type is defined in, show an error. // its receiver type is defined in, show an error.
// println('warn $method_name lef.mod=$left_type_sym.mod c.mod=$c.mod') // println('warn $method_name lef.mod=$left_type_sym.mod c.mod=$c.mod')
@ -1485,14 +1475,16 @@ pub fn (mut c Checker) call_method(mut call_expr ast.CallExpr) table.Type {
c.fail_if_immutable(call_expr.left) c.fail_if_immutable(call_expr.left)
// call_expr.is_mut = true // call_expr.is_mut = true
} }
if method.return_type == table.void_type && if method.return_type == table.void_type && method.ctdefine.len > 0
method.ctdefine.len > 0 && method.ctdefine !in c.pref.compile_defines && method.ctdefine !in c.pref.compile_defines {
{
call_expr.should_be_skipped = true call_expr.should_be_skipped = true
} }
nr_args := if method.params.len == 0 { 0 } else { method.params.len - 1 } nr_args := if method.params.len == 0 { 0 } else { method.params.len - 1 }
min_required_args := method.params.len - if method.is_variadic && method.params.len > min_required_args := method.params.len - if method.is_variadic && method.params.len > 1 {
1 { 2 } else { 1 } 2
} else {
1
}
if call_expr.args.len < min_required_args { if call_expr.args.len < min_required_args {
c.error('expected $min_required_args arguments, but got $call_expr.args.len', c.error('expected $min_required_args arguments, but got $call_expr.args.len',
call_expr.pos) call_expr.pos)
@ -1508,8 +1500,11 @@ pub fn (mut c Checker) call_method(mut call_expr ast.CallExpr) table.Type {
// call_expr.args << method.args[0].typ // call_expr.args << method.args[0].typ
// call_expr.exp_arg_types << method.args[0].typ // call_expr.exp_arg_types << method.args[0].typ
for i, arg in call_expr.args { for i, arg in call_expr.args {
exp_arg_typ := if method.is_variadic && i >= method.params.len - 1 { method.params[method.params.len - exp_arg_typ := if method.is_variadic && i >= method.params.len - 1 {
1].typ } else { method.params[i + 1].typ } method.params[method.params.len - 1].typ
} else {
method.params[i + 1].typ
}
exp_arg_sym := c.table.get_type_symbol(exp_arg_typ) exp_arg_sym := c.table.get_type_symbol(exp_arg_typ)
c.expected_type = exp_arg_typ c.expected_type = exp_arg_typ
got_arg_typ := c.expr(arg.expr) got_arg_typ := c.expr(arg.expr)
@ -1537,8 +1532,11 @@ pub fn (mut c Checker) call_method(mut call_expr ast.CallExpr) table.Type {
call_expr.pos) call_expr.pos)
} }
} }
param := if method.is_variadic && i >= method.params.len - 1 { method.params[method.params.len - param := if method.is_variadic && i >= method.params.len - 1 {
1] } else { method.params[i + 1] } method.params[method.params.len - 1]
} else {
method.params[i + 1]
}
if arg.is_mut { if arg.is_mut {
c.fail_if_immutable(arg.expr) c.fail_if_immutable(arg.expr)
if !param.is_mut { if !param.is_mut {
@ -1551,8 +1549,8 @@ pub fn (mut c Checker) call_method(mut call_expr ast.CallExpr) table.Type {
} else { } else {
if param.is_mut && (!arg.is_mut || param.typ.share() != arg.share) { if param.is_mut && (!arg.is_mut || param.typ.share() != arg.share) {
tok := arg.share.str() tok := arg.share.str()
c.warn('`$call_expr.name` parameter `$param.name` is `$tok`, you need to provide `$tok` e.g. `$tok arg${i + c.warn('`$call_expr.name` parameter `$param.name` is `$tok`, you need to provide `$tok` e.g. `$tok arg${
1}`', arg.expr.position()) i + 1}`', arg.expr.position())
} }
} }
} }
@ -1605,8 +1603,7 @@ pub fn (mut c Checker) call_method(mut call_expr ast.CallExpr) table.Type {
elem_info := return_sym.info as table.Chan elem_info := return_sym.info as table.Chan
elem_sym := c.table.get_type_symbol(elem_info.elem_type) elem_sym := c.table.get_type_symbol(elem_info.elem_type)
if elem_sym.name in method.generic_names { if elem_sym.name in method.generic_names {
idx := c.table.find_or_register_chan(elem_info.elem_type, elem_info.elem_type.nr_muls() > idx := c.table.find_or_register_chan(elem_info.elem_type, elem_info.elem_type.nr_muls() > 0)
0)
return table.new_type(idx) return table.new_type(idx)
} }
} }
@ -1789,9 +1786,8 @@ pub fn (mut c Checker) call_fn(mut call_expr ast.CallExpr) table.Type {
if f.is_deprecated { if f.is_deprecated {
c.warn('function `$f.name` has been deprecated', call_expr.pos) c.warn('function `$f.name` has been deprecated', call_expr.pos)
} }
if f.is_unsafe && !c.inside_unsafe && f.language == .c && f.name[2] in [`m`, `s`] && if f.is_unsafe && !c.inside_unsafe && f.language == .c && f.name[2] in [`m`, `s`]
f.mod == 'builtin' && f.mod == 'builtin' {
{
// builtin C.m*, C.s* only - temp // builtin C.m*, C.s* only - temp
c.warn('function `$f.name` must be called from an `unsafe` block', call_expr.pos) c.warn('function `$f.name` must be called from an `unsafe` block', call_expr.pos)
} }
@ -1817,9 +1813,8 @@ pub fn (mut c Checker) call_fn(mut call_expr ast.CallExpr) table.Type {
} else { } else {
call_expr.return_type = f.return_type call_expr.return_type = f.return_type
} }
if f.return_type == table.void_type && if f.return_type == table.void_type && f.ctdefine.len > 0
f.ctdefine.len > 0 && f.ctdefine !in c.pref.compile_defines && f.ctdefine !in c.pref.compile_defines {
{
call_expr.should_be_skipped = true call_expr.should_be_skipped = true
} }
if f.language != .v || call_expr.language != .v { if f.language != .v || call_expr.language != .v {
@ -1891,8 +1886,8 @@ pub fn (mut c Checker) call_fn(mut call_expr ast.CallExpr) table.Type {
} else { } else {
if arg.is_mut && (!call_arg.is_mut || arg.typ.share() != call_arg.share) { if arg.is_mut && (!call_arg.is_mut || arg.typ.share() != call_arg.share) {
tok := call_arg.share.str() tok := call_arg.share.str()
c.warn('`$call_expr.name` parameter `$arg.name` is `$tok`, you need to provide `$tok` e.g. `$tok arg${i + c.warn('`$call_expr.name` parameter `$arg.name` is `$tok`, you need to provide `$tok` e.g. `$tok arg${
1}`', call_arg.expr.position()) i + 1}`', call_arg.expr.position())
} }
} }
// Handle expected interface // Handle expected interface
@ -2036,8 +2031,8 @@ pub fn (mut c Checker) check_or_expr(or_expr ast.OrExpr, ret_type table.Type, ex
c.expected_or_type = ret_type.clear_flag(.optional) c.expected_or_type = ret_type.clear_flag(.optional)
last_stmt_typ := c.expr(last_stmt.expr) last_stmt_typ := c.expr(last_stmt.expr)
c.expected_or_type = table.void_type c.expected_or_type = table.void_type
type_fits := c.check_types(last_stmt_typ, ret_type) && last_stmt_typ.nr_muls() == type_fits := c.check_types(last_stmt_typ, ret_type)
ret_type.nr_muls() && last_stmt_typ.nr_muls() == ret_type.nr_muls()
is_panic_or_exit := is_expr_panic_or_exit(last_stmt.expr) is_panic_or_exit := is_expr_panic_or_exit(last_stmt.expr)
if type_fits || is_panic_or_exit { if type_fits || is_panic_or_exit {
return return
@ -2233,9 +2228,8 @@ pub fn (mut c Checker) return_stmt(mut return_stmt ast.Return) {
c.error('too many arguments to return, current function does not return anything', c.error('too many arguments to return, current function does not return anything',
return_stmt.pos) return_stmt.pos)
return return
} else if return_stmt.exprs.len == 0 && !(c.expected_type == table.void_type || } else if return_stmt.exprs.len == 0 && !(c.expected_type == table.void_type
expected_type_sym.kind == .void) || expected_type_sym.kind == .void) {
{
c.error('too few arguments to return', return_stmt.pos) c.error('too few arguments to return', return_stmt.pos)
return return
} }
@ -2274,9 +2268,8 @@ pub fn (mut c Checker) return_stmt(mut return_stmt ast.Return) {
} }
for i, exp_type in expected_types { for i, exp_type in expected_types {
got_typ := c.unwrap_generic(got_types[i]) got_typ := c.unwrap_generic(got_types[i])
if got_typ.has_flag(.optional) && if got_typ.has_flag(.optional)
(!exp_type.has_flag(.optional) || c.table.type_to_str(got_typ) != c.table.type_to_str(exp_type)) && (!exp_type.has_flag(.optional) || c.table.type_to_str(got_typ) != c.table.type_to_str(exp_type)) {
{
pos := return_stmt.exprs[i].position() pos := return_stmt.exprs[i].position()
c.error('cannot use `${c.table.type_to_str(got_typ)}` as type `${c.table.type_to_str(exp_type)}` in return argument', c.error('cannot use `${c.table.type_to_str(got_typ)}` as type `${c.table.type_to_str(exp_type)}` in return argument',
pos) pos)
@ -2292,16 +2285,14 @@ pub fn (mut c Checker) return_stmt(mut return_stmt ast.Return) {
c.error('cannot use `$got_typ_sym.name` as type `$exp_typ_sym.name` in return argument', c.error('cannot use `$got_typ_sym.name` as type `$exp_typ_sym.name` in return argument',
pos) pos)
} }
if (got_typ.is_ptr() || got_typ.is_pointer()) && if (got_typ.is_ptr() || got_typ.is_pointer())
(!exp_type.is_ptr() && !exp_type.is_pointer()) && (!exp_type.is_ptr() && !exp_type.is_pointer()) {
{
pos := return_stmt.exprs[i].position() pos := return_stmt.exprs[i].position()
c.error('fn `$c.cur_fn.name` expects you to return a non reference type `${c.table.type_to_str(exp_type)}`, but you are returning `${c.table.type_to_str(got_typ)}` instead', c.error('fn `$c.cur_fn.name` expects you to return a non reference type `${c.table.type_to_str(exp_type)}`, but you are returning `${c.table.type_to_str(got_typ)}` instead',
pos) pos)
} }
if (exp_type.is_ptr() || exp_type.is_pointer()) && if (exp_type.is_ptr() || exp_type.is_pointer())
(!got_typ.is_ptr() && !got_typ.is_pointer()) && got_typ != table.int_literal_type && (!got_typ.is_ptr() && !got_typ.is_pointer())&& got_typ != table.int_literal_type {
{
pos := return_stmt.exprs[i].position() pos := return_stmt.exprs[i].position()
c.error('fn `$c.cur_fn.name` expects you to return a reference type `${c.table.type_to_str(exp_type)}`, but you are returning `${c.table.type_to_str(got_typ)}` instead', c.error('fn `$c.cur_fn.name` expects you to return a reference type `${c.table.type_to_str(exp_type)}`, but you are returning `${c.table.type_to_str(got_typ)}` instead',
pos) pos)
@ -2580,10 +2571,9 @@ pub fn (mut c Checker) assign_stmt(mut assign_stmt ast.AssignStmt) {
// TODO replace all c.pref.translated checks with `$if !translated` for performance // TODO replace all c.pref.translated checks with `$if !translated` for performance
continue continue
} }
if left_sym.kind == .array && !c.inside_unsafe && assign_stmt.op in [.assign, .decl_assign] && if left_sym.kind == .array && !c.inside_unsafe && assign_stmt.op in [.assign, .decl_assign]
right_sym.kind == .array && && right_sym.kind == .array && (left is ast.Ident && !left.is_blank_ident())
(left is ast.Ident && !left.is_blank_ident()) && right is ast.Ident && right is ast.Ident {
{
// Do not allow `a = b`, only `a = b.clone()` // Do not allow `a = b`, only `a = b.clone()`
c.error('use `array2 = array1.clone()` instead of `array2 = array1` (or use `unsafe`)', c.error('use `array2 = array1.clone()` instead of `array2 = array1` (or use `unsafe`)',
assign_stmt.pos) assign_stmt.pos)
@ -2622,10 +2612,12 @@ pub fn (mut c Checker) assign_stmt(mut assign_stmt ast.AssignStmt) {
c.error('invalid right operand: $left_sym.name $assign_stmt.op $right_sym.name', c.error('invalid right operand: $left_sym.name $assign_stmt.op $right_sym.name',
right.position()) right.position())
} }
} else if !left_sym.is_number() && left_sym.kind !in [.byteptr, .charptr, .struct_, .alias] { } else if !left_sym.is_number()
&& left_sym.kind !in [.byteptr, .charptr, .struct_, .alias] {
c.error('operator `$assign_stmt.op` not defined on left operand type `$left_sym.name`', c.error('operator `$assign_stmt.op` not defined on left operand type `$left_sym.name`',
left.position()) left.position())
} else if !right_sym.is_number() && left_sym.kind !in [.byteptr, .charptr, .struct_, .alias] { } else if !right_sym.is_number()
&& left_sym.kind !in [.byteptr, .charptr, .struct_, .alias] {
c.error('invalid right operand: $left_sym.name $assign_stmt.op $right_sym.name', c.error('invalid right operand: $left_sym.name $assign_stmt.op $right_sym.name',
right.position()) right.position())
} else if right is ast.IntegerLiteral { } else if right is ast.IntegerLiteral {
@ -2640,40 +2632,35 @@ pub fn (mut c Checker) assign_stmt(mut assign_stmt ast.AssignStmt) {
} }
} }
.mult_assign, .div_assign { .mult_assign, .div_assign {
if !left_sym.is_number() && if !left_sym.is_number()
!c.table.get_final_type_symbol(left_type_unwrapped).is_int() && left_sym.kind !in && !c.table.get_final_type_symbol(left_type_unwrapped).is_int()
[.struct_, .alias] && left_sym.kind !in [.struct_, .alias] {
{
c.error('operator $assign_stmt.op.str() not defined on left operand type `$left_sym.name`', c.error('operator $assign_stmt.op.str() not defined on left operand type `$left_sym.name`',
left.position()) left.position())
} else if !right_sym.is_number() && } else if !right_sym.is_number()
!c.table.get_final_type_symbol(left_type_unwrapped).is_int() && left_sym.kind !in && !c.table.get_final_type_symbol(left_type_unwrapped).is_int()
[.struct_, .alias] && left_sym.kind !in [.struct_, .alias] {
{
c.error('operator $assign_stmt.op.str() not defined on right operand type `$right_sym.name`', c.error('operator $assign_stmt.op.str() not defined on right operand type `$right_sym.name`',
right.position()) right.position())
} }
} }
.and_assign, .or_assign, .xor_assign, .mod_assign, .left_shift_assign, .right_shift_assign .and_assign, .or_assign, .xor_assign, .mod_assign, .left_shift_assign, .right_shift_assign
{ {
if !left_sym.is_int() && if !left_sym.is_int()
!c.table.get_final_type_symbol(left_type_unwrapped).is_int() && !c.table.get_final_type_symbol(left_type_unwrapped).is_int() {
{
c.error('operator $assign_stmt.op.str() not defined on left operand type `$left_sym.name`', c.error('operator $assign_stmt.op.str() not defined on left operand type `$left_sym.name`',
left.position()) left.position())
} else if !right_sym.is_int() && } else if !right_sym.is_int()
!c.table.get_final_type_symbol(right_type_unwrapped).is_int() && !c.table.get_final_type_symbol(right_type_unwrapped).is_int() {
{
c.error('operator $assign_stmt.op.str() not defined on right operand type `$right_sym.name`', c.error('operator $assign_stmt.op.str() not defined on right operand type `$right_sym.name`',
right.position()) right.position())
} }
} }
else {} else {}
} }
if assign_stmt.op in if assign_stmt.op in [.plus_assign, .minus_assign, .mod_assign, .mult_assign, .div_assign]
[.plus_assign, .minus_assign, .mod_assign, .mult_assign, .div_assign] && && ((left_sym.kind == .struct_ && right_sym.kind == .struct_)
((left_sym.kind == .struct_ && right_sym.kind == .struct_) || left_sym.kind == .alias) || left_sym.kind == .alias) {
{
left_name := c.table.type_to_str(left_type) left_name := c.table.type_to_str(left_type)
right_name := c.table.type_to_str(right_type) right_name := c.table.type_to_str(right_type)
parent_sym := c.table.get_final_type_symbol(left_type) parent_sym := c.table.get_final_type_symbol(left_type)
@ -2884,7 +2871,8 @@ pub fn (mut c Checker) array_init(mut array_init ast.ArrayInit) table.Type {
array_init.typ = table.new_type(idx) array_init.typ = table.new_type(idx)
} }
array_init.elem_type = elem_type array_init.elem_type = elem_type
} else if array_init.is_fixed && array_init.exprs.len == 1 && array_init.elem_type != table.void_type { } else if array_init.is_fixed && array_init.exprs.len == 1
&& array_init.elem_type != table.void_type {
// [50]byte // [50]byte
mut fixed_size := 0 mut fixed_size := 0
init_expr := array_init.exprs[0] init_expr := array_init.exprs[0]
@ -3070,7 +3058,8 @@ fn (mut c Checker) stmt(node ast.Stmt) {
node.scope.update_var_type(node.val_var, val_type) node.scope.update_var_type(node.val_var, val_type)
} else { } else {
if sym.kind == .map && !(node.key_var.len > 0 && node.val_var.len > 0) { if sym.kind == .map && !(node.key_var.len > 0 && node.val_var.len > 0) {
c.error('declare a key and a value variable when ranging a map: `for key, val in map {`\n' + c.error(
'declare a key and a value variable when ranging a map: `for key, val in map {`\n' +
'use `_` if you do not need the variable', node.pos) 'use `_` if you do not need the variable', node.pos)
} }
if node.key_var.len > 0 { if node.key_var.len > 0 {
@ -3129,7 +3118,8 @@ fn (mut c Checker) stmt(node ast.Stmt) {
arg.expr.position()) arg.expr.position())
} }
} }
if node.call_expr.is_method && node.call_expr.receiver_type.is_ptr() && !node.call_expr.left_type.is_ptr() { if node.call_expr.is_method && node.call_expr.receiver_type.is_ptr()
&& !node.call_expr.left_type.is_ptr() {
c.error('method in `go` statement cannot have non-reference mutable receiver', c.error('method in `go` statement cannot have non-reference mutable receiver',
node.call_expr.left.position()) node.call_expr.left.position())
} }
@ -3195,9 +3185,8 @@ fn (mut c Checker) hash_stmt(mut node ast.HashStmt) {
node.main = vroot node.main = vroot
} }
flag_no_comment := flag.all_before('//').trim_space() flag_no_comment := flag.all_before('//').trim_space()
if !((flag_no_comment.starts_with('"') && flag_no_comment.ends_with('"')) || if !((flag_no_comment.starts_with('"') && flag_no_comment.ends_with('"'))
(flag_no_comment.starts_with('<') && flag_no_comment.ends_with('>'))) || (flag_no_comment.starts_with('<') && flag_no_comment.ends_with('>'))) {
{
c.error('including C files should use either `"header_file.h"` or `<header_file.h>` quoting', c.error('including C files should use either `"header_file.h"` or `<header_file.h>` quoting',
node.pos) node.pos)
} }
@ -3578,10 +3567,8 @@ pub fn (mut c Checker) cast_expr(mut node ast.CastExpr) table.Type {
c.error('cannot convert type `$from_type_sym.name` to `$to_type_sym.name` (alias to `$parent_type_sym.name`)', c.error('cannot convert type `$from_type_sym.name` to `$to_type_sym.name` (alias to `$parent_type_sym.name`)',
node.pos) node.pos)
} }
} else if node.typ == table.string_type && } else if node.typ == table.string_type
(from_type_sym.kind in [.int_literal, .int, .byte, .byteptr, .bool] || && (from_type_sym.kind in [.int_literal, .int, .byte, .byteptr, .bool] || (from_type_sym.kind == .array && from_type_sym.name == 'array_byte')) {
(from_type_sym.kind == .array && from_type_sym.name == 'array_byte'))
{
type_name := c.table.type_to_str(node.expr_type) type_name := c.table.type_to_str(node.expr_type)
c.error('cannot cast type `$type_name` to string, use `x.str()` instead', node.pos) c.error('cannot cast type `$type_name` to string, use `x.str()` instead', node.pos)
} else if node.expr_type == table.string_type { } else if node.expr_type == table.string_type {
@ -3594,13 +3581,13 @@ pub fn (mut c Checker) cast_expr(mut node ast.CastExpr) table.Type {
} }
c.error(error_msg, node.pos) c.error(error_msg, node.pos)
} }
} else if to_type_sym.kind == .byte && } else if to_type_sym.kind == .byte && node.expr_type != table.voidptr_type
node.expr_type != table.voidptr_type && from_type_sym.kind != .enum_ && !node.expr_type.is_int() && && from_type_sym.kind != .enum_ && !node.expr_type.is_int() && !node.expr_type.is_float()
!node.expr_type.is_float() && !node.expr_type.is_ptr() && !node.expr_type.is_ptr() {
{
type_name := c.table.type_to_str(node.expr_type) type_name := c.table.type_to_str(node.expr_type)
c.error('cannot cast type `$type_name` to `byte`', node.pos) c.error('cannot cast type `$type_name` to `byte`', node.pos)
} else if to_type_sym.kind == .struct_ && !node.typ.is_ptr() && !(to_type_sym.info as table.Struct).is_typedef { } else if to_type_sym.kind == .struct_ && !node.typ.is_ptr()
&& !(to_type_sym.info as table.Struct).is_typedef {
// For now we ignore C typedef because of `C.Window(C.None)` in vlib/clipboard // For now we ignore C typedef because of `C.Window(C.None)` in vlib/clipboard
if from_type_sym.kind == .struct_ && !node.expr_type.is_ptr() { if from_type_sym.kind == .struct_ && !node.expr_type.is_ptr() {
from_type_info := from_type_sym.info as table.Struct from_type_info := from_type_sym.info as table.Struct
@ -3790,7 +3777,8 @@ pub fn (mut c Checker) ident(mut ident ast.Ident) table.Type {
c.error('undefined variable `$ident.name` (used before declaration)', c.error('undefined variable `$ident.name` (used before declaration)',
ident.pos) ident.pos)
} }
is_sum_type_cast := obj.sum_type_casts.len != 0 && !c.prevent_sum_type_unwrapping_once is_sum_type_cast := obj.sum_type_casts.len != 0
&& !c.prevent_sum_type_unwrapping_once
c.prevent_sum_type_unwrapping_once = false c.prevent_sum_type_unwrapping_once = false
mut typ := if is_sum_type_cast { obj.sum_type_casts.last() } else { obj.typ } mut typ := if is_sum_type_cast { obj.sum_type_casts.last() } else { obj.typ }
if typ == 0 { if typ == 0 {
@ -4293,9 +4281,9 @@ pub fn (mut c Checker) select_expr(mut node ast.SelectExpr) table.Type {
} }
} else { } else {
if branch.stmt.expr is ast.InfixExpr { if branch.stmt.expr is ast.InfixExpr {
if branch.stmt.expr.left !is ast.Ident && if branch.stmt.expr.left !is ast.Ident
branch.stmt.expr.left !is ast.SelectorExpr && branch.stmt.expr.left !is ast.IndexExpr && branch.stmt.expr.left !is ast.SelectorExpr
{ && branch.stmt.expr.left !is ast.IndexExpr {
c.error('channel in `select` key must be predefined', branch.stmt.expr.left.position()) c.error('channel in `select` key must be predefined', branch.stmt.expr.left.position())
} }
} else { } else {
@ -4307,9 +4295,8 @@ pub fn (mut c Checker) select_expr(mut node ast.SelectExpr) table.Type {
expr := branch.stmt.right[0] expr := branch.stmt.right[0]
match expr { match expr {
ast.PrefixExpr { ast.PrefixExpr {
if expr.right !is ast.Ident && if expr.right !is ast.Ident && expr.right !is ast.SelectorExpr
expr.right !is ast.SelectorExpr && expr.right !is ast.IndexExpr && expr.right !is ast.IndexExpr {
{
c.error('channel in `select` key must be predefined', expr.right.position()) c.error('channel in `select` key must be predefined', expr.right.position())
} }
if expr.or_block.kind != .absent { if expr.or_block.kind != .absent {
@ -4388,10 +4375,8 @@ fn (mut c Checker) for_stmt(mut node ast.ForStmt) {
if node.cond is ast.InfixExpr { if node.cond is ast.InfixExpr {
infix := node.cond infix := node.cond
if infix.op == .key_is { if infix.op == .key_is {
if (infix.left is ast.Ident || if (infix.left is ast.Ident || infix.left is ast.SelectorExpr)
infix.left is ast.SelectorExpr) && && infix.right is ast.Type {
infix.right is ast.Type
{
right_expr := infix.right as ast.Type right_expr := infix.right as ast.Type
is_variable := if mut infix.left is ast.Ident { is_variable := if mut infix.left is ast.Ident {
infix.left.kind == .variable infix.left.kind == .variable
@ -4440,7 +4425,8 @@ pub fn (mut c Checker) if_expr(mut node ast.IfExpr) table.Type {
// check condition type is boolean // check condition type is boolean
c.expected_type = table.bool_type c.expected_type = table.bool_type
cond_typ := c.expr(branch.cond) cond_typ := c.expr(branch.cond)
if cond_typ.idx() !in [table.bool_type_idx, table.void_type_idx] && !c.pref.translated { if cond_typ.idx() !in [table.bool_type_idx, table.void_type_idx]
&& !c.pref.translated {
// void types are skipped, because they mean the var was initialized incorrectly // void types are skipped, because they mean the var was initialized incorrectly
// (via missing function etc) // (via missing function etc)
typ_sym := c.table.get_type_symbol(cond_typ) typ_sym := c.table.get_type_symbol(cond_typ)
@ -4462,10 +4448,8 @@ pub fn (mut c Checker) if_expr(mut node ast.IfExpr) table.Type {
expr_str := c.table.type_to_str(expr_type) expr_str := c.table.type_to_str(expr_type)
c.error('cannot use type `$expect_str` as type `$expr_str`', branch.pos) c.error('cannot use type `$expect_str` as type `$expr_str`', branch.pos)
} }
if (infix.left is ast.Ident || if (infix.left is ast.Ident || infix.left is ast.SelectorExpr)
infix.left is ast.SelectorExpr) && && infix.right is ast.Type {
infix.right is ast.Type
{
is_variable := if mut infix.left is ast.Ident { is_variable := if mut infix.left is ast.Ident {
infix.left.kind == .variable infix.left.kind == .variable
} else { } else {
@ -4660,10 +4644,8 @@ fn (mut c Checker) comp_if_branch(cond ast.Expr, pos token.Position) bool {
return l && r // skip (return true) only if both should be skipped return l && r // skip (return true) only if both should be skipped
} }
.key_is, .not_is { .key_is, .not_is {
if (cond.left is ast.SelectorExpr || if (cond.left is ast.SelectorExpr || cond.left is ast.Type)
cond.left is ast.Type) && && cond.right is ast.Type {
cond.right is ast.Type
{
// $if method.@type is string // $if method.@type is string
} else { } else {
c.error('invalid `\$if` condition: $cond.left', cond.pos) c.error('invalid `\$if` condition: $cond.left', cond.pos)
@ -4725,9 +4707,8 @@ fn (mut c Checker) comp_if_branch(cond ast.Expr, pos token.Position) bool {
} else if cond.name !in c.pref.compile_defines_all { } else if cond.name !in c.pref.compile_defines_all {
// `$if some_var {}` // `$if some_var {}`
typ := c.expr(cond) typ := c.expr(cond)
if cond.obj !is ast.Var && if cond.obj !is ast.Var && cond.obj !is ast.ConstField
cond.obj !is ast.ConstField && cond.obj !is ast.GlobalField && cond.obj !is ast.GlobalField {
{
c.error('unknown var: `$cond.name`', pos) c.error('unknown var: `$cond.name`', pos)
return false return false
} }
@ -4924,9 +4905,8 @@ pub fn (mut c Checker) index_expr(mut node ast.IndexExpr) table.Type {
mut typ := c.expr(node.left) mut typ := c.expr(node.left)
node.left_type = typ node.left_type = typ
typ_sym := c.table.get_final_type_symbol(typ) typ_sym := c.table.get_final_type_symbol(typ)
if typ_sym.kind !in [.array, .array_fixed, .string, .map] && !typ.is_ptr() && typ !in [table.byteptr_type, table.charptr_type] && if typ_sym.kind !in [.array, .array_fixed, .string, .map] && !typ.is_ptr()
!typ.has_flag(.variadic) && typ !in [table.byteptr_type, table.charptr_type]&& !typ.has_flag(.variadic) {
{
c.error('type `$typ_sym.name` does not support indexing', node.pos) c.error('type `$typ_sym.name` does not support indexing', node.pos)
} }
if typ_sym.kind == .string && !typ.is_ptr() && node.is_setter { if typ_sym.kind == .string && !typ.is_ptr() && node.is_setter {
@ -5275,8 +5255,8 @@ fn (mut c Checker) sql_stmt(mut node ast.SqlStmt) table.Type {
} }
fn (mut c Checker) fetch_and_verify_orm_fields(info table.Struct, pos token.Position, table_name string) []table.Field { fn (mut c Checker) fetch_and_verify_orm_fields(info table.Struct, pos token.Position, table_name string) []table.Field {
fields := info.fields.filter(it.typ in fields := info.fields.filter(it.typ in [table.string_type, table.int_type, table.bool_type]
[table.string_type, table.int_type, table.bool_type] && !it.attrs.contains('skip')) && !it.attrs.contains('skip'))
if fields.len == 0 { if fields.len == 0 {
c.error('V orm: select: empty fields in `$table_name`', pos) c.error('V orm: select: empty fields in `$table_name`', pos)
return []table.Field{} return []table.Field{}
@ -5344,17 +5324,15 @@ fn (mut c Checker) fn_decl(mut node ast.FnDecl) {
// Make sure all types are valid // Make sure all types are valid
for arg in node.params { for arg in node.params {
sym := c.table.get_type_symbol(arg.typ) sym := c.table.get_type_symbol(arg.typ)
if sym.kind == .placeholder || if sym.kind == .placeholder
(sym.kind in [table.Kind.int_literal, .float_literal] && !c.is_builtin_mod) || (sym.kind in [table.Kind.int_literal, .float_literal] && !c.is_builtin_mod) {
{
c.error('unknown type `$sym.name`', node.pos) c.error('unknown type `$sym.name`', node.pos)
} }
} }
} }
return_sym := c.table.get_type_symbol(node.return_type) return_sym := c.table.get_type_symbol(node.return_type)
if node.language == .v && if node.language == .v && return_sym.kind in [.placeholder, .int_literal, .float_literal]
return_sym.kind in [.placeholder, .int_literal, .float_literal] && return_sym.language == .v && return_sym.language == .v {
{
c.error('unknown type `$return_sym.name`', node.pos) c.error('unknown type `$return_sym.name`', node.pos)
} }
if node.language == .v && node.is_method && node.name == 'str' { if node.language == .v && node.is_method && node.name == 'str' {
@ -5365,9 +5343,8 @@ fn (mut c Checker) fn_decl(mut node ast.FnDecl) {
c.error('.str() methods should have 0 arguments', node.pos) c.error('.str() methods should have 0 arguments', node.pos)
} }
} }
if node.language == .v && node.is_method && node.name in if node.language == .v && node.is_method
['+', '-', '*', '%', '/', '<', '>', '==', '!=', '>=', '<='] && node.name in ['+', '-', '*', '%', '/', '<', '>', '==', '!=', '>=', '<='] {
{
if node.params.len != 2 { if node.params.len != 2 {
c.error('operator methods should have exactly 1 argument', node.pos) c.error('operator methods should have exactly 1 argument', node.pos)
} else { } else {
@ -5385,9 +5362,8 @@ fn (mut c Checker) fn_decl(mut node ast.FnDecl) {
} else if node.receiver.typ != node.params[1].typ { } else if node.receiver.typ != node.params[1].typ {
c.error('expected `$receiver_sym.name` not `$param_sym.name` - both operands must be the same type for operator overloading', c.error('expected `$receiver_sym.name` not `$param_sym.name` - both operands must be the same type for operator overloading',
node.params[1].type_pos) node.params[1].type_pos)
} else if node.name in ['<', '>', '==', '!=', '>=', '<='] && } else if node.name in ['<', '>', '==', '!=', '>=', '<=']
node.return_type != table.bool_type && node.return_type != table.bool_type {
{
c.error('operator comparison methods should return `bool`', node.pos) c.error('operator comparison methods should return `bool`', node.pos)
} else if parent_sym.is_primitive() { } else if parent_sym.is_primitive() {
c.error('cannot define operator methods on type alias for `$parent_sym.name`', c.error('cannot define operator methods on type alias for `$parent_sym.name`',
@ -5397,7 +5373,8 @@ fn (mut c Checker) fn_decl(mut node ast.FnDecl) {
} }
} }
// TODO c.pref.is_vet // TODO c.pref.is_vet
if node.language == .v && !node.is_method && node.params.len == 0 && node.name.after('.').starts_with('test_') { if node.language == .v && !node.is_method && node.params.len == 0
&& node.name.after('.').starts_with('test_') {
if !c.file.path.ends_with('_test.v') { if !c.file.path.ends_with('_test.v') {
// simple heuristic // simple heuristic
for st in node.stmts { for st in node.stmts {
@ -5416,9 +5393,8 @@ fn (mut c Checker) fn_decl(mut node ast.FnDecl) {
c.expected_type = table.void_type c.expected_type = table.void_type
c.cur_fn = node c.cur_fn = node
// Add return if `fn(...) ? {...}` have no return at end // Add return if `fn(...) ? {...}` have no return at end
if node.return_type != table.void_type && node.return_type.has_flag(.optional) && if node.return_type != table.void_type && node.return_type.has_flag(.optional)
(node.stmts.len == 0 || node.stmts[node.stmts.len - 1] !is ast.Return) && (node.stmts.len == 0 || node.stmts[node.stmts.len - 1] !is ast.Return) {
{
sym := c.table.get_type_symbol(node.return_type) sym := c.table.get_type_symbol(node.return_type)
if sym.kind == .void { if sym.kind == .void {
node.stmts << ast.Return{ node.stmts << ast.Return{
@ -5429,9 +5405,8 @@ fn (mut c Checker) fn_decl(mut node ast.FnDecl) {
c.fn_scope = node.scope c.fn_scope = node.scope
c.stmts(node.stmts) c.stmts(node.stmts)
returns := c.returns || has_top_return(node.stmts) returns := c.returns || has_top_return(node.stmts)
if node.language == .v && !node.no_body && node.return_type != table.void_type && !returns && if node.language == .v && !node.no_body && node.return_type != table.void_type && !returns
node.name !in ['panic', 'exit'] && node.name !in ['panic', 'exit'] {
{
c.error('missing return at end of function `$node.name`', node.pos) c.error('missing return at end of function `$node.name`', node.pos)
} }
c.returns = false c.returns = false
@ -5487,9 +5462,8 @@ fn (mut c Checker) verify_all_vweb_routes() {
if isnil(f) { if isnil(f) {
continue continue
} }
if f.return_type == typ_vweb_result && if f.return_type == typ_vweb_result && f.receiver.typ == m.params[0].typ
f.receiver.typ == m.params[0].typ && f.name == m.name && f.name == m.name {
{
c.file = f.source_file // setup of file path for the warning c.file = f.source_file // setup of file path for the warning
c.warn('mismatched parameters count between vweb method `${sym_app.name}.$m.name` ($nargs) and route attribute $m.attrs ($nroute_attributes)', c.warn('mismatched parameters count between vweb method `${sym_app.name}.$m.name` ($nargs) and route attribute $m.attrs ($nroute_attributes)',
f.pos) f.pos)

View File

@ -372,8 +372,7 @@ pub fn (mut d Doc) generate() ? {
} }
filename := os.base(file_path) filename := os.base(file_path)
d.sources[filename] = util.read_file(file_path) or { '' } d.sources[filename] = util.read_file(file_path) or { '' }
file_asts << file_asts << parser.parse_file(file_path, d.table, comments_mode, d.prefs, global_scope)
parser.parse_file(file_path, d.table, comments_mode, d.prefs, global_scope)
} }
return d.file_asts(file_asts) return d.file_asts(file_asts)
} }

View File

@ -5,7 +5,6 @@ module fmt
import v.ast import v.ast
import v.table import v.table
import v.token
import strings import strings
import v.util import v.util
@ -15,7 +14,7 @@ const (
'\t\t\t\t\t\t\t\t', '\t\t\t\t\t\t\t\t',
] ]
// when to break a line dependant on penalty // when to break a line dependant on penalty
max_len = [0, 35, 85, 93, 100] max_len = [0, 35, 60, 85, 93, 100]
) )
pub struct Fmt { pub struct Fmt {
@ -27,13 +26,10 @@ pub mut:
indent int indent int
empty_line bool empty_line bool
line_len int line_len int
buffering bool // expressions will be analyzed later by adjust_complete_line() before finally written buffering bool // disables line wrapping for exprs that will be analyzed later
expr_bufs []string // and stored here in the meantime (expr_bufs.len-1 = penalties.len = precedences.len) par_level int // how many parentheses are put around the current expression
penalties []int // how hard should it be to break line after each expression array_init_break []bool // line breaks after elements in hierarchy level of multi dimensional array
precedences []int // operator/parenthese precedences for operator at end of each expression array_init_depth int // current level of hierarchie in array init
par_level int // how many parentheses are put around the current expression
array_init_break []bool // line breaks after elements in hierarchy level of multi dimensional array
array_init_depth int // current level of hierarchie in array init
single_line_if bool single_line_if bool
cur_mod string cur_mod string
file ast.File file ast.File
@ -92,43 +88,19 @@ pub fn (mut f Fmt) process_file_imports(file &ast.File) {
} }
pub fn (mut f Fmt) write(s string) { pub fn (mut f Fmt) write(s string) {
if !f.buffering {
if f.indent > 0 && f.empty_line {
f.write_indent()
}
f.out.write(s)
f.line_len += s.len
f.empty_line = false
} else {
f.out.write(s)
}
}
pub fn (mut f Fmt) writeln(s string) {
empty_fifo := f.buffering
if empty_fifo {
f.write(s)
f.expr_bufs << f.out.str()
f.out = f.out_save
f.adjust_complete_line()
f.buffering = false
for i, p in f.penalties {
f.write(f.expr_bufs[i])
f.wrap_long_line(p, true)
}
f.write(f.expr_bufs[f.expr_bufs.len - 1])
f.expr_bufs = []string{}
f.penalties = []int{}
f.precedences = []int{}
}
if f.indent > 0 && f.empty_line { if f.indent > 0 && f.empty_line {
f.write_indent() f.write_indent()
} }
f.out.writeln(if empty_fifo { f.out.write(s)
'' f.line_len += s.len
} else { f.empty_line = false
s }
})
pub fn (mut f Fmt) writeln(s string) {
if f.indent > 0 && f.empty_line {
f.write_indent()
}
f.out.writeln(s)
f.empty_line = true f.empty_line = true
f.line_len = 0 f.line_len = 0
} }
@ -145,52 +117,10 @@ fn (mut f Fmt) write_indent() {
f.line_len += f.indent * 4 f.line_len += f.indent * 4
} }
// adjustments that can only be done after full line is processed. For now
// only prevents line breaks if everything fits in max_len[last] by increasing
// penalties to maximum
fn (mut f Fmt) adjust_complete_line() {
for i, buf in f.expr_bufs {
// search for low penalties
if i == 0 || f.penalties[i - 1] <= 1 {
precedence := if i == 0 { -1 } else { f.precedences[i - 1] }
mut len_sub_expr := if i == 0 { buf.len + f.line_len } else { buf.len }
mut sub_expr_end_idx := f.penalties.len
// search for next position with low penalty and same precedence to form subexpression
for j in i .. f.penalties.len {
if f.penalties[j] <= 1 &&
f.precedences[j] == precedence && len_sub_expr >= max_len[1]
{
sub_expr_end_idx = j
break
} else if f.precedences[j] < precedence {
// we cannot form a sensible subexpression
len_sub_expr = C.INT32_MAX
break
} else {
len_sub_expr += f.expr_bufs[j + 1].len
}
}
// if subexpression would fit in single line adjust penalties to actually do so
if len_sub_expr <= max_len[max_len.len - 1] {
for j in i .. sub_expr_end_idx {
f.penalties[j] = max_len.len - 1
}
if i > 0 {
f.penalties[i - 1] = 0
}
if sub_expr_end_idx < f.penalties.len {
f.penalties[sub_expr_end_idx] = 0
}
}
}
// emergency fallback: decrease penalty in front of long unbreakable parts
if i > 0 && buf.len > 55 && f.penalties[i - 1] > 0 {
f.penalties[i - 1] = if buf.len >= 72 { 0 } else { 1 }
}
}
}
pub fn (mut f Fmt) wrap_long_line(penalty_idx int, add_indent bool) bool { pub fn (mut f Fmt) wrap_long_line(penalty_idx int, add_indent bool) bool {
if f.buffering {
return false
}
if f.line_len <= max_len[penalty_idx] { if f.line_len <= max_len[penalty_idx] {
return false return false
} }
@ -493,8 +423,9 @@ pub fn (mut f Fmt) type_decl(node ast.TypeDecl) {
} }
} }
is_last_arg := i == fn_info.params.len - 1 is_last_arg := i == fn_info.params.len - 1
should_add_type := true || is_last_arg || fn_info.params[i + 1].typ != arg.typ || should_add_type := true || is_last_arg
(fn_info.is_variadic && i == fn_info.params.len - 2) || fn_info.params[i + 1].typ != arg.typ
|| (fn_info.is_variadic && i == fn_info.params.len - 2)
if should_add_type { if should_add_type {
ns := if arg.name == '' { '' } else { ' ' } ns := if arg.name == '' { '' } else { ' ' }
if fn_info.is_variadic && is_last_arg { if fn_info.is_variadic && is_last_arg {
@ -532,7 +463,7 @@ pub fn (mut f Fmt) type_decl(node ast.TypeDecl) {
f.write(' | ') f.write(' | ')
} }
if i < sum_type_names.len - 1 { if i < sum_type_names.len - 1 {
f.wrap_long_line(2, true) f.wrap_long_line(3, true)
} }
} }
// f.write(sum_type_names.join(' | ')) // f.write(sum_type_names.join(' | '))
@ -762,7 +693,8 @@ pub fn (mut f Fmt) struct_decl(node ast.StructDecl) {
comment_align_i++ comment_align_i++
align = comment_aligns[comment_align_i] align = comment_aligns[comment_align_i]
} }
pad_len := align.max_attrs_len - attrs_len + align.max_type_len - field_types[i].len pad_len := align.max_attrs_len - attrs_len +
align.max_type_len - field_types[i].len
f.write(strings.repeat(` `, pad_len)) f.write(strings.repeat(` `, pad_len))
} }
f.write(' ') f.write(' ')
@ -1496,74 +1428,97 @@ pub fn (mut f Fmt) lock_expr(lex ast.LockExpr) {
pub fn (mut f Fmt) infix_expr(node ast.InfixExpr) { pub fn (mut f Fmt) infix_expr(node ast.InfixExpr) {
buffering_save := f.buffering buffering_save := f.buffering
if !f.buffering { if !f.buffering {
f.out_save = f.out
f.out = strings.new_builder(60)
f.buffering = true f.buffering = true
} }
infix_start := f.out.len
start_len := f.line_len
f.expr(node.left) f.expr(node.left)
is_one_val_array_init := node.op in [.key_in, .not_in] && is_one_val_array_init := node.op in [.key_in, .not_in] && node.right is ast.ArrayInit
node.right is ast.ArrayInit && (node.right as ast.ArrayInit).exprs.len == 1 && (node.right as ast.ArrayInit).exprs.len == 1
if is_one_val_array_init { if is_one_val_array_init {
// `var in [val]` => `var == val` // `var in [val]` => `var == val`
f.write(if node.op == .key_in { op := if node.op == .key_in { ' == ' } else { ' != ' }
' == ' f.write(op)
} else {
' != '
})
} else { } else {
f.write(' $node.op.str() ') f.write(' $node.op.str() ')
} }
f.expr_bufs << f.out.str()
mut penalty := 3
match mut node.left {
ast.InfixExpr {
if int(token.precedences[node.left.op]) > int(token.precedences[node.op]) {
penalty--
}
}
ast.ParExpr {
penalty = 1
}
else {}
}
match node.right {
ast.InfixExpr { penalty-- }
ast.ParExpr { penalty = 1 }
else {}
}
f.penalties << penalty
// combine parentheses level with operator precedence to form effective precedence
f.precedences << int(token.precedences[node.op]) | (f.par_level << 16)
f.out = strings.new_builder(60)
f.buffering = true
if is_one_val_array_init { if is_one_val_array_init {
// `var in [val]` => `var == val` // `var in [val]` => `var == val`
f.expr((node.right as ast.ArrayInit).exprs[0]) f.expr((node.right as ast.ArrayInit).exprs[0])
} else { } else {
f.expr(node.right) f.expr(node.right)
} }
if !buffering_save && f.buffering { // now decide if and where to break if !buffering_save && f.buffering {
f.expr_bufs << f.out.str()
f.out = f.out_save
f.buffering = false f.buffering = false
f.adjust_complete_line() if !f.single_line_if && f.line_len > max_len.last() {
for i, p in f.penalties { f.wrap_infix(infix_start, start_len)
f.write(f.expr_bufs[i])
f.wrap_long_line(p, true)
} }
f.write(f.expr_bufs[f.expr_bufs.len - 1])
f.expr_bufs = []string{}
f.penalties = []int{}
f.precedences = []int{}
} }
f.or_expr(node.or_block) f.or_expr(node.or_block)
} }
pub fn (mut f Fmt) wrap_infix(start_pos int, start_len int) {
cut_span := f.out.len - start_pos
condstr := f.out.cut_last(cut_span)
is_cond_infix := condstr.contains_any_substr(['&&', '||'])
if !is_cond_infix && !condstr.contains('+') {
f.write(condstr)
return
}
f.line_len = start_len
if start_len == 0 {
f.empty_line = true
}
or_pen := if condstr.contains('&&') { 3 } else { 5 }
cond_parts := condstr.split(' ')
mut grouped_cond := false
mut conditions := ['']
mut penalties := [5]
mut index := 0
for cp in cond_parts {
if is_cond_infix && cp in ['&&', '||'] {
if grouped_cond {
conditions[index] += '$cp '
} else {
p := if cp == '||' { or_pen } else { 5 }
penalties << p
conditions << '$cp '
index++
}
} else if !is_cond_infix && cp == '+' {
penalties << 5
conditions[index] += '$cp '
conditions << ''
index++
} else {
conditions[index] += '$cp '
if cp.starts_with('(') {
grouped_cond = true
} else if cp.ends_with(')') {
grouped_cond = false
}
}
}
for i, c in conditions {
cnd := c.trim_space()
if f.line_len + cnd.len < max_len[penalties[i]] {
if i > 0 && (!is_cond_infix || i < conditions.len - 1) {
f.write(' ')
}
f.write(cnd)
} else {
f.writeln('')
f.indent++
f.write(cnd)
f.indent--
}
}
}
pub fn (mut f Fmt) if_expr(it ast.IfExpr) { pub fn (mut f Fmt) if_expr(it ast.IfExpr) {
dollar := if it.is_comptime { '$' } else { '' } dollar := if it.is_comptime { '$' } else { '' }
mut single_line := it.branches.len == 2 && it.has_else && branch_is_single_line(it.branches[0]) && mut single_line := it.branches.len == 2 && it.has_else && branch_is_single_line(it.branches[0])
branch_is_single_line(it.branches[1]) && && branch_is_single_line(it.branches[1])&& (it.is_expr || f.is_assign)
(it.is_expr || f.is_assign)
f.single_line_if = single_line f.single_line_if = single_line
if_start := f.line_len if_start := f.line_len
for { for {
@ -1583,15 +1538,8 @@ pub fn (mut f Fmt) if_expr(it ast.IfExpr) {
} }
if i < it.branches.len - 1 || !it.has_else { if i < it.branches.len - 1 || !it.has_else {
f.write('${dollar}if ') f.write('${dollar}if ')
cur_pos := f.out.len
f.expr(branch.cond) f.expr(branch.cond)
cond_len := f.out.len - cur_pos f.write(' ')
is_cond_wrapped := if cond_len > 0 { '\n' in f.out.last_n(cond_len) } else { false }
if is_cond_wrapped {
f.writeln('')
} else {
f.write(' ')
}
} }
f.write('{') f.write('{')
if single_line { if single_line {
@ -1654,7 +1602,7 @@ pub fn (mut f Fmt) call_args(args []ast.CallArg) {
f.write(arg.share.str() + ' ') f.write(arg.share.str() + ' ')
} }
if i > 0 { if i > 0 {
f.wrap_long_line(2, true) f.wrap_long_line(3, true)
} }
f.expr(arg.expr) f.expr(arg.expr)
if i < args.len - 1 { if i < args.len - 1 {
@ -1790,7 +1738,7 @@ pub fn (mut f Fmt) match_expr(it ast.MatchExpr) {
if j < branch.exprs.len - 1 { if j < branch.exprs.len - 1 {
f.write(', ') f.write(', ')
} }
f.wrap_long_line(3, false) f.wrap_long_line(4, false)
} }
f.is_mbranch_expr = false f.is_mbranch_expr = false
} else { } else {
@ -1900,10 +1848,10 @@ pub fn (mut f Fmt) array_init(it ast.ArrayInit) {
f.array_init_break << (last_line_nr < line_nr) f.array_init_break << (last_line_nr < line_nr)
} }
} }
is_same_line_comment := i > 0 && is_same_line_comment := i > 0
(expr is ast.Comment && line_nr == it.exprs[i - 1].position().line_nr) && (expr is ast.Comment && line_nr == it.exprs[i - 1].position().line_nr)
line_break := f.array_init_break[f.array_init_depth - 1] line_break := f.array_init_break[f.array_init_depth - 1]
mut penalty := if line_break && !is_same_line_comment { 0 } else { 3 } mut penalty := if line_break && !is_same_line_comment { 0 } else { 4 }
if penalty > 0 { if penalty > 0 {
if i == 0 || should_decrease_arr_penalty(it.exprs[i - 1]) { if i == 0 || should_decrease_arr_penalty(it.exprs[i - 1]) {
penalty-- penalty--
@ -2066,10 +2014,9 @@ pub fn (mut f Fmt) struct_init(it ast.StructInit) {
f.writeln('') f.writeln('')
} }
f.comments(field.next_comments, inline: false, has_nl: true, level: .keep) f.comments(field.next_comments, inline: false, has_nl: true, level: .keep)
if single_line_fields && if single_line_fields
(field.comments.len > 0 || && (field.comments.len > 0 || field.next_comments.len > 0 || !expr_is_single_line(field.expr)
field.next_comments.len > 0 || !expr_is_single_line(field.expr) || f.line_len > max_len.last()) || f.line_len > max_len.last()) {
{
single_line_fields = false single_line_fields = false
f.out.go_back_to(fields_start) f.out.go_back_to(fields_start)
f.line_len = fields_start f.line_len = fields_start
@ -2389,7 +2336,7 @@ pub fn (mut f Fmt) sql_stmt(node ast.SqlStmt) {
} else { } else {
f.write(' ') f.write(' ')
} }
f.wrap_long_line(2, true) f.wrap_long_line(3, true)
} }
f.write('where ') f.write('where ')
f.expr(node.where_expr) f.expr(node.where_expr)

View File

@ -1,6 +1,17 @@
fn concatenation_of_strings() { fn concatenation_of_strings() {
_ := 'Simple' + 'Concat' _ := 'Simple' + 'Concat'
_ := 'Hello' + ' ' + 'World' + '!' _ := 'Hello' + ' ' + 'World' + '!'
_ := 'There' + ' ' + 'so' + ' ' + 'many' + ' ' + 'words' + ' ' + 'they' + ' ' + "don't" + _ := 'There' + ' ' + 'so' + ' ' + 'many' + ' ' + 'words' + ' ' + 'they' + ' ' + "don't" + ' ' +
' ' + 'fit' + ' ' + 'in' + ' ' + 'one' + ' ' + 'line' 'fit' + ' ' + 'in' + ' ' + 'one' + ' ' + 'line'
}
fn concat_inside_ternary() {
{ // This block is needed to force line wrapping
cline := if iline == pos.line_nr {
sline[..start_column] + color(kind, sline[start_column..end_column]) +
sline[end_column..]
} else {
sline
}
}
} }

View File

@ -5,3 +5,13 @@ _ := 'Simple' + 'Concat'
'World' + '!' 'World' + '!'
_ := 'There' + ' ' + 'so' + ' ' + 'many' + ' ' + 'words' + ' ' + 'they' + ' ' + "don't" + ' ' + 'fit' + ' ' + 'in' + ' ' + 'one' + ' ' + 'line' _ := 'There' + ' ' + 'so' + ' ' + 'many' + ' ' + 'words' + ' ' + 'they' + ' ' + "don't" + ' ' + 'fit' + ' ' + 'in' + ' ' + 'one' + ' ' + 'line'
} }
fn concat_inside_ternary() {
{ // This block is needed to force line wrapping
cline := if iline == pos.line_nr {
sline[..start_column] + color(kind, sline[start_column..end_column]) + sline[end_column..]
} else {
sline
}
}
}

View File

@ -11,9 +11,7 @@ fn string_inter_lit(mut c checker.Checker, mut node ast.StringInterLiteral) tabl
typ := c.table.unalias_num_type(ftyp) typ := c.table.unalias_num_type(ftyp)
mut fmt := node.fmts[i] mut fmt := node.fmts[i]
// analyze and validate format specifier // analyze and validate format specifier
if fmt !in if fmt !in [`E`, `F`, `G`, `e`, `f`, `g`, `d`, `u`, `x`, `X`, `o`, `c`, `s`, `p`, `_`] {
[`E`, `F`, `G`, `e`, `f`, `g`, `d`, `u`, `x`, `X`, `o`, `c`, `s`, `p`, `_`]
{
c.error('unknown format specifier `${fmt:c}`', node.fmt_poss[i]) c.error('unknown format specifier `${fmt:c}`', node.fmt_poss[i])
} }
if node.precisions[i] != 987698 && !typ.is_float() { if node.precisions[i] != 987698 && !typ.is_float() {
@ -22,14 +20,13 @@ fn string_inter_lit(mut c checker.Checker, mut node ast.StringInterLiteral) tabl
if node.pluss[i] && !typ.is_number() { if node.pluss[i] && !typ.is_number() {
c.error('plus prefix only allowd for numbers', node.fmt_poss[i]) c.error('plus prefix only allowd for numbers', node.fmt_poss[i])
} }
if (typ.is_unsigned() && fmt !in [`u`, `x`, `X`, `o`, `c`]) || if (typ.is_unsigned() && fmt !in [`u`, `x`, `X`, `o`, `c`])
(typ.is_signed() && fmt !in [`d`, `x`, `X`, `o`, `c`]) || || (typ.is_signed() && fmt !in [`d`, `x`, `X`, `o`, `c`])
(typ.is_int_literal() && fmt !in [`d`, `c`, `x`, `X`, `o`, `u`, `x`, `X`, `o`]) || || (typ.is_int_literal() && fmt !in [`d`, `c`, `x`, `X`, `o`, `u`, `x`, `X`, `o`])
(typ.is_float() && fmt !in [`E`, `F`, `G`, `e`, `f`, `g`]) || || (typ.is_float() && fmt !in [`E`, `F`, `G`, `e`, `f`, `g`])
(typ.is_pointer() && fmt !in [`p`, `x`, `X`]) || || (typ.is_pointer() && fmt !in [`p`, `x`, `X`])
(typ.is_string() && fmt != `s`) || || (typ.is_string() && fmt != `s`)
(typ.idx() in [table.i64_type_idx, table.f64_type_idx] && fmt == `c`) || (typ.idx() in [table.i64_type_idx, table.f64_type_idx] && fmt == `c`) {
{
c.error('illegal format specifier `${fmt:c}` for type `${c.table.get_type_name(ftyp)}`', c.error('illegal format specifier `${fmt:c}` for type `${c.table.get_type_name(ftyp)}`',
node.fmt_poss[i]) node.fmt_poss[i])
} }
@ -40,8 +37,8 @@ fn string_inter_lit(mut c checker.Checker, mut node ast.StringInterLiteral) tabl
} }
fn get_some_val(a_test f64, b_test f64, c_test f64, d_test f64, e_test f64, f_test f64) f64 { fn get_some_val(a_test f64, b_test f64, c_test f64, d_test f64, e_test f64, f_test f64) f64 {
return a_test * b_test * c_test * d_test + return a_test * b_test * c_test * d_test + e_test * f_test * a_test * d_test +
e_test * f_test * a_test * d_test + a_test * b_test * c_test a_test * b_test * c_test
} }
fn main() { fn main() {
@ -55,10 +52,8 @@ fn main() {
println(s) println(s)
println('this is quite a long string' + println('this is quite a long string' +
' that is followd by an even longer part that should go to another line') ' that is followd by an even longer part that should go to another line')
if (a == b && b > r) || if (a == b && b > r) || (d > r) || (a < b) || (b < d && a + b > r)
(d > r) || (a < b) || (b < d && a + b > r) || || (a + b + d >= 0 && r < 0)|| (a > b && d - r < b) {
(a + b + d >= 0 && r < 0) || (a > b && d - r < b)
{
println('ok') println('ok')
} }
} }
@ -76,9 +71,8 @@ struct Parser {
} }
fn (mut p Parser) name_expr() { fn (mut p Parser) name_expr() {
if p.peek_tok.kind == .lpar || if p.peek_tok.kind == .lpar
(p.peek_tok.kind == .lt && p.peek_tok2.kind == .name && p.peek_tok3.kind == .gt) || (p.peek_tok.kind == .lt && p.peek_tok2.kind == .name && p.peek_tok3.kind == .gt) {
{
println(p.peek_tok.lit) println(p.peek_tok.lit)
} }
} }

View File

@ -108,8 +108,7 @@ fn test_select_blocks() {
else { else {
h = 2 h = 2
} }
} } {
{
panic('channel is still open') panic('channel is still open')
} else { } else {
is_open = false is_open = false

View File

@ -40,9 +40,7 @@ fn (g &Gen) type_to_fmt(typ table.Type) string {
sym := g.table.get_type_symbol(typ) sym := g.table.get_type_symbol(typ)
if typ.is_ptr() && (typ.is_int() || typ.is_float()) { if typ.is_ptr() && (typ.is_int() || typ.is_float()) {
return '%.*s\\000' return '%.*s\\000'
} else if sym.kind in } else if sym.kind in [.struct_, .array, .array_fixed, .map, .bool, .enum_, .interface_, .sum_type, .function] {
[.struct_, .array, .array_fixed, .map, .bool, .enum_, .interface_, .sum_type, .function]
{
return '%.*s\\000' return '%.*s\\000'
} else if sym.kind == .string { } else if sym.kind == .string {
return "'%.*s\\000'" return "'%.*s\\000'"
@ -184,8 +182,11 @@ fn (mut g Gen) gen_str_for_array(info table.Array, styp string, str_fn_name stri
sym_has_str_method, str_method_expects_ptr, _ := sym.str_method_info() sym_has_str_method, str_method_expects_ptr, _ := sym.str_method_info()
mut elem_str_fn_name := '' mut elem_str_fn_name := ''
if sym_has_str_method { if sym_has_str_method {
elem_str_fn_name = if is_elem_ptr { field_styp.replace('*', '') + '_str' } else { field_styp + elem_str_fn_name = if is_elem_ptr {
'_str' } field_styp.replace('*', '') + '_str'
} else {
field_styp + '_str'
}
if sym.kind == .byte { if sym.kind == .byte {
elem_str_fn_name = elem_str_fn_name + '_escaped' elem_str_fn_name = elem_str_fn_name + '_escaped'
} }
@ -263,8 +264,11 @@ fn (mut g Gen) gen_str_for_array_fixed(info table.ArrayFixed, styp string, str_f
sym_has_str_method, str_method_expects_ptr, _ := sym.str_method_info() sym_has_str_method, str_method_expects_ptr, _ := sym.str_method_info()
mut elem_str_fn_name := '' mut elem_str_fn_name := ''
if sym_has_str_method { if sym_has_str_method {
elem_str_fn_name = if is_elem_ptr { field_styp.replace('*', '') + '_str' } else { field_styp + elem_str_fn_name = if is_elem_ptr {
'_str' } field_styp.replace('*', '') + '_str'
} else {
field_styp + '_str'
}
} else { } else {
elem_str_fn_name = styp_to_str_fn_name(field_styp) elem_str_fn_name = styp_to_str_fn_name(field_styp)
} }
@ -382,8 +386,11 @@ fn (mut g Gen) gen_str_for_multi_return(info table.MultiReturn, styp string, str
sym_has_str_method, str_method_expects_ptr, _ := sym.str_method_info() sym_has_str_method, str_method_expects_ptr, _ := sym.str_method_info()
mut arg_str_fn_name := '' mut arg_str_fn_name := ''
if sym_has_str_method { if sym_has_str_method {
arg_str_fn_name = if is_arg_ptr { field_styp.replace('*', '') + '_str' } else { field_styp + arg_str_fn_name = if is_arg_ptr {
'_str' } field_styp.replace('*', '') + '_str'
} else {
field_styp + '_str'
}
} else { } else {
arg_str_fn_name = styp_to_str_fn_name(field_styp) arg_str_fn_name = styp_to_str_fn_name(field_styp)
} }
@ -440,8 +447,8 @@ fn (mut g Gen) gen_str_for_struct(info table.Struct, styp string, str_fn_name st
if clean_struct_v_type_name.contains('_T_') { if clean_struct_v_type_name.contains('_T_') {
// TODO: this is a bit hacky. styp shouldn't be even parsed with _T_ // TODO: this is a bit hacky. styp shouldn't be even parsed with _T_
// use something different than g.typ for styp // use something different than g.typ for styp
clean_struct_v_type_name = clean_struct_v_type_name.replace('_T_', '<').replace('_', ', ') + clean_struct_v_type_name =
'>' clean_struct_v_type_name.replace('_T_', '<').replace('_', ', ') + '>'
} }
clean_struct_v_type_name = util.strip_main_name(clean_struct_v_type_name) clean_struct_v_type_name = util.strip_main_name(clean_struct_v_type_name)
// generate ident / indent length = 4 spaces // generate ident / indent length = 4 spaces

View File

@ -155,10 +155,8 @@ pub fn cgen(files []ast.File, table &table.Table, pref &pref.Preferences) string
mut module_built := '' mut module_built := ''
if pref.build_mode == .build_module { if pref.build_mode == .build_module {
for file in files { for file in files {
if pref.path in file.path && if pref.path in file.path
file.mod.short_name == && file.mod.short_name == pref.path.all_after_last(os.path_separator).trim_right(os.path_separator) {
pref.path.all_after_last(os.path_separator).trim_right(os.path_separator)
{
module_built = file.mod.name module_built = file.mod.name
break break
} }
@ -408,8 +406,10 @@ pub fn (mut g Gen) init() {
} }
if g.pref.compile_defines_all.len > 0 { if g.pref.compile_defines_all.len > 0 {
g.comptime_defines.writeln('// V compile time defines by -d or -define flags:') g.comptime_defines.writeln('// V compile time defines by -d or -define flags:')
g.comptime_defines.writeln('// All custom defines : ' + g.pref.compile_defines_all.join(',')) g.comptime_defines.writeln('// All custom defines : ' +
g.comptime_defines.writeln('// Turned ON custom defines: ' + g.pref.compile_defines.join(',')) g.pref.compile_defines_all.join(','))
g.comptime_defines.writeln('// Turned ON custom defines: ' +
g.pref.compile_defines.join(','))
for cdefine in g.pref.compile_defines { for cdefine in g.pref.compile_defines {
g.comptime_defines.writeln('#define CUSTOM_DEFINE_$cdefine') g.comptime_defines.writeln('#define CUSTOM_DEFINE_$cdefine')
} }
@ -1012,10 +1012,8 @@ fn (mut g Gen) stmt(node ast.Stmt) {
// TODO true for not just "builtin" // TODO true for not just "builtin"
// TODO: clean this up // TODO: clean this up
mod := if g.is_builtin_mod { 'builtin' } else { node.name.all_before_last('.') } mod := if g.is_builtin_mod { 'builtin' } else { node.name.all_before_last('.') }
if (mod != g.module_built && if (mod != g.module_built && node.mod != g.module_built.after('/'))
node.mod != g.module_built.after('/')) || || should_bundle_module {
should_bundle_module
{
// Skip functions that don't have to be generated for this module. // Skip functions that don't have to be generated for this module.
// println('skip bm $node.name mod=$node.mod module_built=$g.module_built') // println('skip bm $node.name mod=$node.mod module_built=$g.module_built')
skip = true skip = true
@ -1030,10 +1028,8 @@ fn (mut g Gen) stmt(node ast.Stmt) {
if g.pref.use_cache { if g.pref.use_cache {
// We are using prebuilt modules, we do not need to generate // We are using prebuilt modules, we do not need to generate
// their functions in main.c. // their functions in main.c.
if node.mod != 'main' && if node.mod != 'main' && node.mod != 'help' && !should_bundle_module
node.mod != 'help' && !should_bundle_module && !g.file.path.ends_with('_test.v') && && !g.file.path.ends_with('_test.v')&& node.generic_params.len == 0 {
node.generic_params.len == 0
{
skip = true skip = true
} }
} }
@ -1042,15 +1038,13 @@ fn (mut g Gen) stmt(node ast.Stmt) {
if node.name == 'main.main' { if node.name == 'main.main' {
g.has_main = true g.has_main = true
} }
if node.name == 'backtrace' || if node.name == 'backtrace' || node.name == 'backtrace_symbols'
node.name == 'backtrace_symbols' || node.name == 'backtrace_symbols_fd' || node.name == 'backtrace_symbols_fd' {
{
g.write('\n#ifndef __cplusplus\n') g.write('\n#ifndef __cplusplus\n')
} }
g.gen_fn_decl(node, skip) g.gen_fn_decl(node, skip)
if node.name == 'backtrace' || if node.name == 'backtrace' || node.name == 'backtrace_symbols'
node.name == 'backtrace_symbols' || node.name == 'backtrace_symbols_fd' || node.name == 'backtrace_symbols_fd' {
{
g.write('\n#endif\n') g.write('\n#endif\n')
} }
g.fn_decl = keep_fn_decl g.fn_decl = keep_fn_decl
@ -1504,7 +1498,8 @@ fn (mut g Gen) expr_with_cast(expr ast.Expr, got_type_raw table.Type, expected_t
got_is_ptr := got_type.is_ptr() got_is_ptr := got_type.is_ptr()
expected_is_ptr := expected_type.is_ptr() expected_is_ptr := expected_type.is_ptr()
neither_void := table.voidptr_type !in [got_type, expected_type] neither_void := table.voidptr_type !in [got_type, expected_type]
if got_is_ptr && !expected_is_ptr && neither_void && expected_sym.kind !in [.interface_, .placeholder] { if got_is_ptr && !expected_is_ptr && neither_void
&& expected_sym.kind !in [.interface_, .placeholder] {
got_deref_type := got_type.deref() got_deref_type := got_type.deref()
deref_sym := g.table.get_type_symbol(got_deref_type) deref_sym := g.table.get_type_symbol(got_deref_type)
deref_will_match := expected_type in [got_type, got_deref_type, deref_sym.parent_idx] deref_will_match := expected_type in [got_type, got_deref_type, deref_sym.parent_idx]
@ -1671,9 +1666,9 @@ fn (mut g Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) {
} }
// Free the old value assigned to this string var (only if it's `str = [new value]` // Free the old value assigned to this string var (only if it's `str = [new value]`
// or `x.str = [new value]` ) // or `x.str = [new value]` )
mut af := g.is_autofree && !g.is_builtin_mod && assign_stmt.op == .assign && assign_stmt.left_types.len == mut af := g.is_autofree && !g.is_builtin_mod && assign_stmt.op == .assign
1 && && assign_stmt.left_types.len == 1
(assign_stmt.left[0] is ast.Ident || assign_stmt.left[0] is ast.SelectorExpr) && (assign_stmt.left[0] is ast.Ident || assign_stmt.left[0] is ast.SelectorExpr)
// assign_stmt.left_types[0] in [table.string_type, table.array_type] && // assign_stmt.left_types[0] in [table.string_type, table.array_type] &&
mut sref_name := '' mut sref_name := ''
mut type_to_free := '' mut type_to_free := ''
@ -1728,9 +1723,8 @@ fn (mut g Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) {
// } // }
// int pos = *(int*)_t190.data; // int pos = *(int*)_t190.data;
mut tmp_opt := '' mut tmp_opt := ''
is_optional := g.is_autofree && is_optional := g.is_autofree && (assign_stmt.op in [.decl_assign, .assign])
(assign_stmt.op in [.decl_assign, .assign]) && assign_stmt.left_types.len == 1 && && assign_stmt.left_types.len == 1&& assign_stmt.right[0] is ast.CallExpr
assign_stmt.right[0] is ast.CallExpr
if is_optional { if is_optional {
// g.write('/* optional assignment */') // g.write('/* optional assignment */')
call_expr := assign_stmt.right[0] as ast.CallExpr call_expr := assign_stmt.right[0] as ast.CallExpr
@ -2035,12 +2029,9 @@ fn (mut g Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) {
str_add = true str_add = true
} }
// Assignment Operator Overloading // Assignment Operator Overloading
if ((left_sym.kind == .struct_ && if ((left_sym.kind == .struct_ && right_sym.kind == .struct_)
right_sym.kind == .struct_) || || (left_sym.kind == .alias && right_sym.kind == .alias))
(left_sym.kind == .alias && && assign_stmt.op in [.plus_assign, .minus_assign, .div_assign, .mult_assign, .mod_assign] {
right_sym.kind == .alias)) &&
assign_stmt.op in [.plus_assign, .minus_assign, .div_assign, .mult_assign, .mod_assign]
{
extracted_op := match assign_stmt.op { extracted_op := match assign_stmt.op {
.plus_assign { '+' } .plus_assign { '+' }
.minus_assign { '-' } .minus_assign { '-' }
@ -2075,7 +2066,8 @@ fn (mut g Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) {
g.prevent_sum_type_unwrapping_once = true g.prevent_sum_type_unwrapping_once = true
} }
if !is_fixed_array_copy || is_decl { if !is_fixed_array_copy || is_decl {
if !is_decl && left is ast.Ident && g.for_in_mul_val_name == (left as ast.Ident).name { if !is_decl && left is ast.Ident
&& g.for_in_mul_val_name == (left as ast.Ident).name {
g.write('*') g.write('*')
} }
g.expr(left) g.expr(left)
@ -2375,9 +2367,8 @@ fn (mut g Gen) autofree_scope_vars2(scope &ast.Scope, start_pos int, end_pos int
// } // }
// ``` // ```
// if !isnil(scope.parent) && line_nr > 0 { // if !isnil(scope.parent) && line_nr > 0 {
if free_parent_scopes && !isnil(scope.parent) && if free_parent_scopes && !isnil(scope.parent)
(stop_pos == -1 || scope.parent.start_pos >= stop_pos) && (stop_pos == -1 || scope.parent.start_pos >= stop_pos) {
{
g.writeln('// af parent scope:') g.writeln('// af parent scope:')
g.autofree_scope_vars2(scope.parent, start_pos, end_pos, line_nr, true, stop_pos) g.autofree_scope_vars2(scope.parent, start_pos, end_pos, line_nr, true, stop_pos)
} }
@ -2535,9 +2526,8 @@ fn (mut g Gen) expr(node ast.Expr) {
// if g.fileis('1.strings') { // if g.fileis('1.strings') {
// println('before:' + node.autofree_pregen) // println('before:' + node.autofree_pregen)
// } // }
if g.is_autofree && !g.is_builtin_mod && !g.is_js_call && g.strs_to_free0.len == 0 && if g.is_autofree && !g.is_builtin_mod && !g.is_js_call && g.strs_to_free0.len == 0
!g.inside_lambda && !g.inside_lambda { // && g.inside_ternary ==
{ // && g.inside_ternary ==
// if len != 0, that means we are handling call expr inside call expr (arg) // if len != 0, that means we are handling call expr inside call expr (arg)
// and it'll get messed up here, since it's handled recursively in autofree_call_pregen() // and it'll get messed up here, since it's handled recursively in autofree_call_pregen()
// so just skip it // so just skip it
@ -2594,7 +2584,8 @@ fn (mut g Gen) expr(node ast.Expr) {
g.write(')') g.write(')')
} else if sym.kind == .sum_type { } else if sym.kind == .sum_type {
g.expr_with_cast(node.expr, node.expr_type, node.typ) g.expr_with_cast(node.expr, node.expr_type, node.typ)
} else if sym.kind == .struct_ && !node.typ.is_ptr() && !(sym.info as table.Struct).is_typedef { } else if sym.kind == .struct_ && !node.typ.is_ptr()
&& !(sym.info as table.Struct).is_typedef {
styp := g.typ(node.typ) styp := g.typ(node.typ)
g.write('*(($styp *)(&') g.write('*(($styp *)(&')
g.expr(node.expr) g.expr(node.expr)
@ -2603,16 +2594,14 @@ fn (mut g Gen) expr(node ast.Expr) {
styp := g.typ(node.typ) styp := g.typ(node.typ)
mut cast_label := '' mut cast_label := ''
// `table.string_type` is done for MSVC's bug // `table.string_type` is done for MSVC's bug
if sym.kind != .alias || if sym.kind != .alias
(sym.info as table.Alias).parent_type !in [node.expr_type, table.string_type] || (sym.info as table.Alias).parent_type !in [node.expr_type, table.string_type] {
{
cast_label = '($styp)' cast_label = '($styp)'
} }
g.write('(${cast_label}(') g.write('(${cast_label}(')
g.expr(node.expr) g.expr(node.expr)
if node.expr is ast.IntegerLiteral && if node.expr is ast.IntegerLiteral
node.typ in [table.u64_type, table.u32_type, table.u16_type] && node.typ in [table.u64_type, table.u32_type, table.u16_type] {
{
g.write('U') g.write('U')
} }
g.write('))') g.write('))')
@ -3086,9 +3075,8 @@ fn (mut g Gen) infix_expr(node ast.InfixExpr) {
g.write(')') g.write(')')
} else if unaliased_left == table.string_type_idx && node.op !in [.key_in, .not_in] { } else if unaliased_left == table.string_type_idx && node.op !in [.key_in, .not_in] {
// `str == ''` -> `str.len == 0` optimization // `str == ''` -> `str.len == 0` optimization
if node.op in [.eq, .ne] && if node.op in [.eq, .ne] && node.right is ast.StringLiteral
node.right is ast.StringLiteral && (node.right as ast.StringLiteral).val == '' && (node.right as ast.StringLiteral).val == '' {
{
arrow := if left_type.is_ptr() { '->' } else { '.' } arrow := if left_type.is_ptr() { '->' } else { '.' }
g.write('(') g.write('(')
g.expr(node.left) g.expr(node.left)
@ -3144,9 +3132,8 @@ fn (mut g Gen) infix_expr(node ast.InfixExpr) {
} }
g.expr(node.right) g.expr(node.right)
g.write(')') g.write(')')
} else if node.op in [.eq, .ne] && } else if node.op in [.eq, .ne] && left_sym.kind == .array_fixed
left_sym.kind == .array_fixed && right_sym.kind == .array_fixed && right_sym.kind == .array_fixed {
{
ptr_typ := g.gen_fixed_array_equality_fn(left_type) ptr_typ := g.gen_fixed_array_equality_fn(left_type)
if node.op == .ne { if node.op == .ne {
g.write('!') g.write('!')
@ -3184,9 +3171,8 @@ fn (mut g Gen) infix_expr(node ast.InfixExpr) {
} }
g.expr(node.right) g.expr(node.right)
g.write(')') g.write(')')
} else if node.op in [.eq, .ne] && } else if node.op in [.eq, .ne] && left_sym.kind == .struct_ && right_sym.kind == .struct_
left_sym.kind == .struct_ && right_sym.kind == .struct_ && has_eq_overloaded && has_ne_overloaded && has_eq_overloaded&& has_ne_overloaded {
{
ptr_typ := g.gen_struct_equality_fn(left_type) ptr_typ := g.gen_struct_equality_fn(left_type)
if node.op == .eq { if node.op == .eq {
g.write('${ptr_typ}_struct_eq(') g.write('${ptr_typ}_struct_eq(')
@ -3327,41 +3313,45 @@ fn (mut g Gen) infix_expr(node ast.InfixExpr) {
if gen_or { if gen_or {
g.or_block(tmp_opt, node.or_block, table.void_type) g.or_block(tmp_opt, node.or_block, table.void_type)
} }
} else if unaliased_left.idx() in [table.u32_type_idx, table.u64_type_idx] && unaliased_right.is_signed() && } else if unaliased_left.idx() in [table.u32_type_idx, table.u64_type_idx]
node.op in [.eq, .ne, .gt, .lt, .ge, .le] && unaliased_right.is_signed()&& node.op in [.eq, .ne, .gt, .lt, .ge, .le] {
{ bitsize := if unaliased_left.idx() == table.u32_type_idx
bitsize := if unaliased_left.idx() == table.u32_type_idx && && unaliased_right.idx() != table.i64_type_idx {
unaliased_right.idx() != table.i64_type_idx 32
{ 32 } else { 64 } } else {
64
}
g.write('_us${bitsize}_${cmp_str[int(node.op) - int(token.Kind.eq)]}(') g.write('_us${bitsize}_${cmp_str[int(node.op) - int(token.Kind.eq)]}(')
g.expr(node.left) g.expr(node.left)
g.write(',') g.write(',')
g.expr(node.right) g.expr(node.right)
g.write(')') g.write(')')
} else if unaliased_right.idx() in [table.u32_type_idx, table.u64_type_idx] && unaliased_left.is_signed() && } else if unaliased_right.idx() in [table.u32_type_idx, table.u64_type_idx]
node.op in [.eq, .ne, .gt, .lt, .ge, .le] && unaliased_left.is_signed()&& node.op in [.eq, .ne, .gt, .lt, .ge, .le] {
{ bitsize := if unaliased_right.idx() == table.u32_type_idx
bitsize := if unaliased_right.idx() == table.u32_type_idx && && unaliased_left.idx() != table.i64_type_idx {
unaliased_left.idx() != table.i64_type_idx 32
{ 32 } else { 64 } } else {
64
}
g.write('_us${bitsize}_${cmp_rev[int(node.op) - int(token.Kind.eq)]}(') g.write('_us${bitsize}_${cmp_rev[int(node.op) - int(token.Kind.eq)]}(')
g.expr(node.right) g.expr(node.right)
g.write(',') g.write(',')
g.expr(node.left) g.expr(node.left)
g.write(')') g.write(')')
} else { } else {
a := (left_sym.name[0].is_capital() || left_sym.name.contains('.')) && a := (left_sym.name[0].is_capital() || left_sym.name.contains('.'))
left_sym.kind !in [.enum_, .function, .interface_, .sum_type] && left_sym.language != .c && left_sym.kind !in [.enum_, .function, .interface_, .sum_type]
&& left_sym.language != .c
b := left_sym.kind != .alias b := left_sym.kind != .alias
c := left_sym.kind == .alias && (left_sym.info as table.Alias).language == .c c := left_sym.kind == .alias && (left_sym.info as table.Alias).language == .c
// Check if aliased type is a struct // Check if aliased type is a struct
d := !b && d := !b
g.typ((left_sym.info as table.Alias).parent_type).split('__').last()[0].is_capital() && g.typ((left_sym.info as table.Alias).parent_type).split('__').last()[0].is_capital()
// Do not generate operator overloading with these `right_sym.kind`. // Do not generate operator overloading with these `right_sym.kind`.
e := right_sym.kind !in [.voidptr, .int_literal, .int] e := right_sym.kind !in [.voidptr, .int_literal, .int]
if node.op in [.plus, .minus, .mul, .div, .mod, .lt, .gt, .eq, .ne, .le, .ge] && if node.op in [.plus, .minus, .mul, .div, .mod, .lt, .gt, .eq, .ne, .le, .ge]
((a && b && e) || c || d) && ((a && b && e) || c|| d) {
{
// Overloaded operators // Overloaded operators
g.write(g.typ(if !d { g.write(g.typ(if !d {
left_type left_type
@ -3565,9 +3555,8 @@ fn (mut g Gen) match_expr_classic(node ast.MatchExpr, is_expr bool, cond_var str
// if type is unsigned and low is 0, check is unneeded // if type is unsigned and low is 0, check is unneeded
mut skip_low := false mut skip_low := false
if expr.low is ast.IntegerLiteral { if expr.low is ast.IntegerLiteral {
if node.cond_type in [table.u16_type, table.u32_type, table.u64_type] && if node.cond_type in [table.u16_type, table.u32_type, table.u64_type]
expr.low.val == '0' && expr.low.val == '0' {
{
skip_low = true skip_low = true
} }
} }
@ -3633,10 +3622,8 @@ fn (mut g Gen) select_expr(node ast.SelectExpr) {
// send expression // send expression
expr := branch.stmt.expr as ast.InfixExpr expr := branch.stmt.expr as ast.InfixExpr
channels << expr.left channels << expr.left
if expr.right is ast.Ident || if expr.right is ast.Ident || expr.right is ast.IndexExpr
expr.right is ast.IndexExpr || expr.right is ast.SelectorExpr || expr.right is || expr.right is ast.SelectorExpr|| expr.right is ast.StructInit {
ast.StructInit
{
// addressable objects in the `C` output // addressable objects in the `C` output
objs << expr.right objs << expr.right
tmp_objs << '' tmp_objs << ''
@ -3656,9 +3643,8 @@ fn (mut g Gen) select_expr(node ast.SelectExpr) {
channels << rec_expr.right channels << rec_expr.right
is_push << false is_push << false
// create tmp unless the object with *exactly* the type we need exists already // create tmp unless the object with *exactly* the type we need exists already
if branch.stmt.op == .decl_assign || if branch.stmt.op == .decl_assign
branch.stmt.right_types[0] != branch.stmt.left_types[0] || branch.stmt.right_types[0] != branch.stmt.left_types[0] {
{
tmp_obj := g.new_tmp_var() tmp_obj := g.new_tmp_var()
tmp_objs << tmp_obj tmp_objs << tmp_obj
el_stype := g.typ(branch.stmt.right_types[0]) el_stype := g.typ(branch.stmt.right_types[0])
@ -3850,11 +3836,8 @@ fn (mut g Gen) if_expr(node ast.IfExpr) {
// (as it used to be done). // (as it used to be done).
// Always use this in -autofree, since ?: can have tmp expressions that have to be freed. // Always use this in -autofree, since ?: can have tmp expressions that have to be freed.
first_branch := node.branches[0] first_branch := node.branches[0]
needs_tmp_var := node.is_expr && needs_tmp_var := node.is_expr
(g.is_autofree || (g.pref.experimental && && (g.is_autofree || (g.pref.experimental && (first_branch.stmts.len > 1 || (first_branch.stmts[0] is ast.ExprStmt && (first_branch.stmts[0] as ast.ExprStmt).expr is ast.IfExpr))))
(first_branch.stmts.len > 1 ||
(first_branch.stmts[0] is ast.ExprStmt &&
(first_branch.stmts[0] as ast.ExprStmt).expr is ast.IfExpr))))
/* /*
needs_tmp_var := node.is_expr && needs_tmp_var := node.is_expr &&
(g.autofree || g.pref.experimental) && (g.autofree || g.pref.experimental) &&
@ -4108,8 +4091,8 @@ fn (mut g Gen) index_expr(node ast.IndexExpr) {
.function { 'voidptr*' } .function { 'voidptr*' }
else { '$elem_type_str*' } else { '$elem_type_str*' }
} }
needs_clone := info.elem_type == table.string_type_idx && g.is_autofree && needs_clone := info.elem_type == table.string_type_idx && g.is_autofree
!g.is_assign_lhs && !g.is_assign_lhs
is_gen_or_and_assign_rhs := gen_or && g.is_assign_rhs is_gen_or_and_assign_rhs := gen_or && g.is_assign_rhs
cur_line := if is_gen_or_and_assign_rhs { cur_line := if is_gen_or_and_assign_rhs {
line := g.go_before_stmt(0) line := g.go_before_stmt(0)
@ -4228,9 +4211,9 @@ fn (mut g Gen) index_expr(node ast.IndexExpr) {
zero := g.type_default(info.value_type) zero := g.type_default(info.value_type)
g.write('$zero })))') g.write('$zero })))')
} }
} else if g.inside_map_postfix || g.inside_map_infix || g.inside_map_index || } else if g.inside_map_postfix || g.inside_map_infix
(g.is_assign_lhs && !g.is_array_set && get_and_set_types) || g.inside_map_index
{ || (g.is_assign_lhs && !g.is_array_set && get_and_set_types) {
zero := g.type_default(info.value_type) zero := g.type_default(info.value_type)
g.write('(*($elem_type_str*)map_get_and_set_1(') g.write('(*($elem_type_str*)map_get_and_set_1(')
if !left_is_ptr { if !left_is_ptr {
@ -4733,9 +4716,8 @@ fn (mut g Gen) struct_init(struct_init ast.StructInit) {
} }
} }
if !cloned { if !cloned {
if field.expected_type.is_ptr() && !(field.typ.is_ptr() || field.typ.is_pointer()) && if field.expected_type.is_ptr() && !(field.typ.is_ptr()
!field.typ.is_number() || field.typ.is_pointer())&& !field.typ.is_number() {
{
g.write('/* autoref */&') g.write('/* autoref */&')
} }
g.expr_with_cast(field.expr, field.typ, field.expected_type) g.expr_with_cast(field.expr, field.typ, field.expected_type)
@ -4794,8 +4776,8 @@ fn (mut g Gen) struct_init(struct_init ast.StructInit) {
expected_field_type_sym := g.table.get_type_symbol(sfield.expected_type) expected_field_type_sym := g.table.get_type_symbol(sfield.expected_type)
field_type_sym := g.table.get_type_symbol(sfield.typ) field_type_sym := g.table.get_type_symbol(sfield.typ)
mut cloned := false mut cloned := false
is_interface := expected_field_type_sym.kind == .interface_ && is_interface := expected_field_type_sym.kind == .interface_
field_type_sym.kind != .interface_ && field_type_sym.kind != .interface_
if g.is_autofree && !sfield.typ.is_ptr() && field_type_sym.kind in [.array, .string] { if g.is_autofree && !sfield.typ.is_ptr() && field_type_sym.kind in [.array, .string] {
g.write('/*clone1*/') g.write('/*clone1*/')
if g.gen_clone_assignment(sfield.expr, field_type_sym, false) { if g.gen_clone_assignment(sfield.expr, field_type_sym, false) {
@ -4806,9 +4788,8 @@ fn (mut g Gen) struct_init(struct_init ast.StructInit) {
if is_interface { if is_interface {
g.interface_call(sfield.typ, sfield.expected_type) g.interface_call(sfield.typ, sfield.expected_type)
} }
if sfield.expected_type.is_ptr() && !(sfield.typ.is_ptr() || sfield.typ.is_pointer()) && if sfield.expected_type.is_ptr() && !(sfield.typ.is_ptr()
!sfield.typ.is_number() || sfield.typ.is_pointer())&& !sfield.typ.is_number() {
{
g.write('/* autoref */&') g.write('/* autoref */&')
} }
g.expr_with_cast(sfield.expr, sfield.typ, sfield.expected_type) g.expr_with_cast(sfield.expr, sfield.typ, sfield.expected_type)
@ -5187,7 +5168,8 @@ fn (g &Gen) sort_structs(typesa []table.TypeSymbol) []table.TypeSymbol {
if !dep_graph_sorted.acyclic { if !dep_graph_sorted.acyclic {
// this should no longer be called since it's catched in the parser // this should no longer be called since it's catched in the parser
// TODO: should it be removed? // TODO: should it be removed?
verror('cgen.sort_structs(): the following structs form a dependency cycle:\n' + dep_graph_sorted.display_cycles() + verror('cgen.sort_structs(): the following structs form a dependency cycle:\n' +
dep_graph_sorted.display_cycles() +
'\nyou can solve this by making one or both of the dependant struct fields references, eg: field &MyStruct' + '\nyou can solve this by making one or both of the dependant struct fields references, eg: field &MyStruct' +
'\nif you feel this is an error, please create a new issue here: https://github.com/vlang/v/issues and tag @joe-conigliaro') '\nif you feel this is an error, please create a new issue here: https://github.com/vlang/v/issues and tag @joe-conigliaro')
} }
@ -5258,16 +5240,16 @@ fn (mut g Gen) or_block(var_name string, or_block ast.OrExpr, return_type table.
g.inside_or_block = false g.inside_or_block = false
} }
stmts := or_block.stmts stmts := or_block.stmts
if stmts.len > 0 && stmts[or_block.stmts.len - 1] is ast.ExprStmt && (stmts[stmts.len - if stmts.len > 0 && stmts[or_block.stmts.len - 1] is ast.ExprStmt
1] as ast.ExprStmt).typ != table.void_type && (stmts[stmts.len - 1] as ast.ExprStmt).typ != table.void_type {
{
g.indent++ g.indent++
for i, stmt in stmts { for i, stmt in stmts {
if i == stmts.len - 1 { if i == stmts.len - 1 {
expr_stmt := stmt as ast.ExprStmt expr_stmt := stmt as ast.ExprStmt
g.stmt_path_pos << g.out.len g.stmt_path_pos << g.out.len
g.write('*($mr_styp*) ${cvar_name}.data = ') g.write('*($mr_styp*) ${cvar_name}.data = ')
is_opt_call := expr_stmt.expr is ast.CallExpr && expr_stmt.typ.has_flag(.optional) is_opt_call := expr_stmt.expr is ast.CallExpr
&& expr_stmt.typ.has_flag(.optional)
if is_opt_call { if is_opt_call {
g.write('*($mr_styp*) ') g.write('*($mr_styp*) ')
} }
@ -5472,9 +5454,8 @@ fn (mut g Gen) comp_if_to_ifdef(name string, is_comptime_optional bool) ?string
return 'TARGET_ORDER_IS_BIG' return 'TARGET_ORDER_IS_BIG'
} }
else { else {
if is_comptime_optional || if is_comptime_optional
(g.pref.compile_defines_all.len > 0 && name in g.pref.compile_defines_all) || (g.pref.compile_defines_all.len > 0 && name in g.pref.compile_defines_all) {
{
return 'CUSTOM_DEFINE_$name' return 'CUSTOM_DEFINE_$name'
} }
return error('bad os ifdef name "$name"') // should never happen, caught in the checker return error('bad os ifdef name "$name"') // should never happen, caught in the checker
@ -5928,7 +5909,8 @@ fn (mut g Gen) interface_table() string {
// i.e. cctype is always just Cat, not Cat_ptr: // i.e. cctype is always just Cat, not Cat_ptr:
cctype := g.cc_type(st) cctype := g.cc_type(st)
$if debug_interface_table ? { $if debug_interface_table ? {
eprintln('>> interface name: $ityp.name | concrete type: $st.debug() | st symname: ' + eprintln(
'>> interface name: $ityp.name | concrete type: $st.debug() | st symname: ' +
g.table.get_type_symbol(st).name) g.table.get_type_symbol(st).name)
} }
// Speaker_Cat_index = 0 // Speaker_Cat_index = 0

View File

@ -17,9 +17,8 @@ fn (mut g Gen) comptime_selector(node ast.ComptimeSelector) {
// check for field.name // check for field.name
if node.field_expr is ast.SelectorExpr { if node.field_expr is ast.SelectorExpr {
if node.field_expr.expr is ast.Ident { if node.field_expr.expr is ast.Ident {
if node.field_expr.expr.name == g.comp_for_field_var && if node.field_expr.expr.name == g.comp_for_field_var
node.field_expr.field_name == 'name' && node.field_expr.field_name == 'name' {
{
g.write(g.comp_for_field_value.name) g.write(g.comp_for_field_value.name)
return return
} }
@ -316,7 +315,8 @@ fn (mut g Gen) comp_for(node ast.CompFor) {
g.writeln('\t${node.val_var}.attrs = __new_array_with_default(0, 0, sizeof(string), 0);') g.writeln('\t${node.val_var}.attrs = __new_array_with_default(0, 0, sizeof(string), 0);')
} else { } else {
attrs := cgen_attrs(method.attrs) 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]){' + g.writeln(
'\t${node.val_var}.attrs = new_array_from_c_array($attrs.len, $attrs.len, sizeof(string), _MOV((string[$attrs.len]){' +
attrs.join(', ') + '}));') attrs.join(', ') + '}));')
} }
if method.params.len < 2 { if method.params.len < 2 {
@ -385,7 +385,8 @@ fn (mut g Gen) comp_for(node ast.CompFor) {
g.writeln('\t${node.val_var}.attrs = __new_array_with_default(0, 0, sizeof(string), 0);') g.writeln('\t${node.val_var}.attrs = __new_array_with_default(0, 0, sizeof(string), 0);')
} else { } else {
attrs := cgen_attrs(field.attrs) 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]){' + g.writeln(
'\t${node.val_var}.attrs = new_array_from_c_array($attrs.len, $attrs.len, sizeof(string), _MOV((string[$attrs.len]){' +
attrs.join(', ') + '}));') attrs.join(', ') + '}));')
} }
// field_sym := g.table.get_type_symbol(field.typ) // field_sym := g.table.get_type_symbol(field.typ)

View File

@ -129,10 +129,8 @@ fn (mut g Gen) gen_fn_decl(it ast.FnDecl, skip bool) {
arg_start_pos := g.out.len arg_start_pos := g.out.len
fargs, fargtypes := g.fn_args(it.params, it.is_variadic) fargs, fargtypes := g.fn_args(it.params, it.is_variadic)
arg_str := g.out.after(arg_start_pos) arg_str := g.out.after(arg_start_pos)
if it.no_body || if it.no_body || ((g.pref.use_cache && g.pref.build_mode != .build_module) && it.is_builtin
((g.pref.use_cache && g.pref.build_mode != .build_module) && it.is_builtin && !g.is_test) || && !g.is_test)|| skip {
skip
{
// Just a function header. Builtin function bodies are defined in builtin.o // Just a function header. Builtin function bodies are defined in builtin.o
g.definitions.writeln(');') // // NO BODY') g.definitions.writeln(');') // // NO BODY')
g.writeln(');') g.writeln(');')
@ -295,9 +293,9 @@ fn (mut g Gen) call_expr(node ast.CallExpr) {
g.write('$styp $tmp_opt = ') g.write('$styp $tmp_opt = ')
} }
if node.is_method && !node.is_field { if node.is_method && !node.is_field {
if node.name == 'writeln' && g.pref.experimental && node.args.len > 0 && node.args[0].expr is if node.name == 'writeln' && g.pref.experimental && node.args.len > 0
ast.StringInterLiteral && g.table.get_type_symbol(node.receiver_type).name == 'strings.Builder' && node.args[0].expr is ast.StringInterLiteral
{ && g.table.get_type_symbol(node.receiver_type).name == 'strings.Builder' {
g.string_inter_literal_sb_optimized(node) g.string_inter_literal_sb_optimized(node)
} else { } else {
g.method_call(node) g.method_call(node)
@ -413,9 +411,8 @@ fn (mut g Gen) method_call(node ast.CallExpr) {
receiver_type_name = 'map' receiver_type_name = 'map'
} }
// TODO performance, detect `array` method differently // TODO performance, detect `array` method differently
if left_sym.kind == .array && node.name in if left_sym.kind == .array
['repeat', 'sort_with_compare', 'free', 'push_many', 'trim', 'first', 'last', 'pop', 'clone', 'reverse', 'slice'] && node.name in ['repeat', 'sort_with_compare', 'free', 'push_many', 'trim', 'first', 'last', 'pop', 'clone', 'reverse', 'slice'] {
{
// && rec_sym.name == 'array' { // && rec_sym.name == 'array' {
// && rec_sym.name == 'array' && receiver_name.starts_with('array') { // && rec_sym.name == 'array' && receiver_name.starts_with('array') {
// `array_byte_clone` => `array_clone` // `array_byte_clone` => `array_clone`
@ -480,9 +477,8 @@ fn (mut g Gen) method_call(node ast.CallExpr) {
if !is_range_slice { if !is_range_slice {
g.write('&') g.write('&')
} }
} else if !node.receiver_type.is_ptr() && node.left_type.is_ptr() && node.name != 'str' && } else if !node.receiver_type.is_ptr() && node.left_type.is_ptr() && node.name != 'str'
node.from_embed_type == 0 && node.from_embed_type == 0 {
{
g.write('/*rec*/*') g.write('/*rec*/*')
} }
if g.is_autofree && node.free_receiver && !g.inside_lambda && !g.is_builtin_mod { if g.is_autofree && node.free_receiver && !g.inside_lambda && !g.is_builtin_mod {
@ -505,8 +501,8 @@ fn (mut g Gen) method_call(node ast.CallExpr) {
if has_cast { if has_cast {
g.write(')') g.write(')')
} }
is_variadic := node.expected_arg_types.len > 0 && node.expected_arg_types[node.expected_arg_types.len - is_variadic := node.expected_arg_types.len > 0
1].has_flag(.variadic) && node.expected_arg_types[node.expected_arg_types.len - 1].has_flag(.variadic)
if node.args.len > 0 || is_variadic { if node.args.len > 0 || is_variadic {
g.write(', ') g.write(', ')
} }
@ -672,8 +668,8 @@ fn (mut g Gen) autofree_call_pregen(node ast.CallExpr) {
// g.writeln('// autofree_call_pregen()') // g.writeln('// autofree_call_pregen()')
// Create a temporary var before fn call for each argument in order to free it (only if it's a complex expression, // Create a temporary var before fn call for each argument in order to free it (only if it's a complex expression,
// like `foo(get_string())` or `foo(a + b)` // like `foo(get_string())` or `foo(a + b)`
mut free_tmp_arg_vars := g.is_autofree && !g.is_builtin_mod && node.args.len > 0 && mut free_tmp_arg_vars := g.is_autofree && !g.is_builtin_mod && node.args.len > 0
!node.args[0].typ.has_flag(.optional) // TODO copy pasta checker.v && !node.args[0].typ.has_flag(.optional) // TODO copy pasta checker.v
if !free_tmp_arg_vars { if !free_tmp_arg_vars {
return return
} }
@ -803,13 +799,14 @@ fn (mut g Gen) autofree_call_postgen(node_pos int) {
fn (mut g Gen) call_args(node ast.CallExpr) { fn (mut g Gen) call_args(node ast.CallExpr) {
args := if g.is_js_call { node.args[1..] } else { node.args } args := if g.is_js_call { node.args[1..] } else { node.args }
expected_types := node.expected_arg_types expected_types := node.expected_arg_types
is_variadic := expected_types.len > 0 && expected_types[expected_types.len - 1].has_flag(.variadic) is_variadic := expected_types.len > 0
&& expected_types[expected_types.len - 1].has_flag(.variadic)
for i, arg in args { for i, arg in args {
if is_variadic && i == expected_types.len - 1 { if is_variadic && i == expected_types.len - 1 {
break break
} }
use_tmp_var_autofree := g.is_autofree && arg.typ == table.string_type && arg.is_tmp_autofree && use_tmp_var_autofree := g.is_autofree && arg.typ == table.string_type && arg.is_tmp_autofree
!g.inside_const && !g.is_builtin_mod && !g.inside_const&& !g.is_builtin_mod
// g.write('/* af=$arg.is_tmp_autofree */') // g.write('/* af=$arg.is_tmp_autofree */')
mut is_interface := false mut is_interface := false
// some c fn definitions dont have args (cfns.v) or are not updated in checker // some c fn definitions dont have args (cfns.v) or are not updated in checker

View File

@ -128,7 +128,8 @@ fn (mut g Gen) gen_struct_enc_dec(type_info table.TypeInfo, styp string, mut enc
field_sym := g.table.get_type_symbol(field.typ) field_sym := g.table.get_type_symbol(field.typ)
// First generate decoding // First generate decoding
if field.attrs.contains('raw') { if field.attrs.contains('raw') {
dec.writeln('\tres.${c_name(field.name)} = tos4(cJSON_PrintUnformatted(' + 'js_get(root, "$name")));') dec.writeln('\tres.${c_name(field.name)} = tos4(cJSON_PrintUnformatted(' +
'js_get(root, "$name")));')
} else { } else {
// Now generate decoders for all field types in this struct // Now generate decoders for all field types in this struct
// need to do it here so that these functions are generated first // need to do it here so that these functions are generated first
@ -201,8 +202,7 @@ fn js_dec_name(typ string) string {
} }
fn is_js_prim(typ string) bool { fn is_js_prim(typ string) bool {
return typ in return typ in ['int', 'string', 'bool', 'f32', 'f64', 'i8', 'i16', 'i64', 'u16', 'u32', 'u64', 'byte']
['int', 'string', 'bool', 'f32', 'f64', 'i8', 'i16', 'i64', 'u16', 'u32', 'u64', 'byte']
} }
fn (mut g Gen) decode_array(value_type table.Type) string { fn (mut g Gen) decode_array(value_type table.Type) string {

View File

@ -45,8 +45,7 @@ fn (mut g Gen) generate_hotcode_reloader_code() {
phd = posix_hotcode_definitions_1 phd = posix_hotcode_definitions_1
} else { } else {
for so_fn in g.hotcode_fn_names { for so_fn in g.hotcode_fn_names {
load_code << load_code << 'impl_live_$so_fn = (void *)GetProcAddress(live_lib, "impl_live_$so_fn"); '
'impl_live_$so_fn = (void *)GetProcAddress(live_lib, "impl_live_$so_fn"); '
} }
phd = windows_hotcode_definitions_1 phd = windows_hotcode_definitions_1
} }

View File

@ -340,9 +340,8 @@ fn (mut g Gen) gen_expr_to_string(expr ast.Expr, etype table.Type) {
g.enum_expr(expr) g.enum_expr(expr)
g.write('")') g.write('")')
} }
} else if sym_has_str_method || sym.kind in } else if sym_has_str_method
[.array, .array_fixed, .map, .struct_, .multi_return, .sum_type, .interface_] || sym.kind in [.array, .array_fixed, .map, .struct_, .multi_return, .sum_type, .interface_] {
{
is_ptr := typ.is_ptr() is_ptr := typ.is_ptr()
str_fn_name := g.gen_str_for_type(typ) str_fn_name := g.gen_str_for_type(typ)
if is_ptr { if is_ptr {

View File

@ -65,7 +65,8 @@ fn (mut p Parser) check_cross_variables(exprs []ast.Expr, val ast.Expr) bool {
} }
} }
ast.InfixExpr { ast.InfixExpr {
return p.check_cross_variables(exprs, val_.left) || p.check_cross_variables(exprs, val_.right) return p.check_cross_variables(exprs, val_.left)
|| p.check_cross_variables(exprs, val_.right)
} }
ast.PrefixExpr { ast.PrefixExpr {
return p.check_cross_variables(exprs, val_.right) return p.check_cross_variables(exprs, val_.right)

View File

@ -275,9 +275,8 @@ fn (mut p Parser) fn_decl() ast.FnDecl {
} }
} }
} }
if p.tok.kind in [.plus, .minus, .mul, .div, .mod, .gt, .lt, .eq, .ne, .le, .ge] && if p.tok.kind in [.plus, .minus, .mul, .div, .mod, .gt, .lt, .eq, .ne, .le, .ge]
p.peek_tok.kind == .lpar && p.peek_tok.kind == .lpar {
{
name = p.tok.kind.str() // op_to_fn_name() name = p.tok.kind.str() // op_to_fn_name()
if rec_type == table.void_type { if rec_type == table.void_type {
p.error_with_pos('cannot use operator overloading with normal functions', p.error_with_pos('cannot use operator overloading with normal functions',
@ -311,9 +310,8 @@ fn (mut p Parser) fn_decl() ast.FnDecl {
mut end_pos := p.prev_tok.position() mut end_pos := p.prev_tok.position()
// Return type // Return type
mut return_type := table.void_type mut return_type := table.void_type
if p.tok.kind.is_start_of_type() || if p.tok.kind.is_start_of_type()
(p.tok.kind == .key_fn && p.tok.line_nr == p.prev_tok.line_nr) || (p.tok.kind == .key_fn && p.tok.line_nr == p.prev_tok.line_nr) {
{
return_type = p.parse_type() return_type = p.parse_type()
} }
mut type_sym_method_idx := 0 mut type_sym_method_idx := 0
@ -327,8 +325,8 @@ fn (mut p Parser) fn_decl() ast.FnDecl {
// check maps & arrays, must be defined in same module as the elem type // check maps & arrays, must be defined in same module as the elem type
if !is_non_local && type_sym.kind in [.array, .map] { if !is_non_local && type_sym.kind in [.array, .map] {
elem_type_sym := p.table.get_type_symbol(p.table.value_type(rec_type)) elem_type_sym := p.table.get_type_symbol(p.table.value_type(rec_type))
is_non_local = elem_type_sym.mod.len > 0 && is_non_local = elem_type_sym.mod.len > 0 && elem_type_sym.mod != p.mod
elem_type_sym.mod != p.mod && elem_type_sym.language == .v && elem_type_sym.language == .v
} }
if is_non_local { if is_non_local {
p.error_with_pos('cannot define new methods on non-local type $type_sym.name', p.error_with_pos('cannot define new methods on non-local type $type_sym.name',
@ -570,9 +568,9 @@ fn (mut p Parser) fn_args() ([]table.Param, bool, bool) {
} else { } else {
p.tok.lit p.tok.lit
} }
types_only := p.tok.kind in [.amp, .ellipsis, .key_fn] || types_only := p.tok.kind in [.amp, .ellipsis, .key_fn]
(p.peek_tok.kind == .comma && p.table.known_type(argname)) || p.peek_tok.kind == .dot || || (p.peek_tok.kind == .comma && p.table.known_type(argname))
p.peek_tok.kind == .rpar || p.peek_tok.kind == .dot|| p.peek_tok.kind == .rpar
// TODO copy pasta, merge 2 branches // TODO copy pasta, merge 2 branches
if types_only { if types_only {
// p.warn('types only') // p.warn('types only')
@ -668,7 +666,8 @@ fn (mut p Parser) fn_args() ([]table.Param, bool, bool) {
// `a, b, c int` // `a, b, c int`
for p.tok.kind == .comma { for p.tok.kind == .comma {
if !p.pref.is_fmt { if !p.pref.is_fmt {
p.warn('`fn f(x, y Type)` syntax has been deprecated and will soon be removed. ' + p.warn(
'`fn f(x, y Type)` syntax has been deprecated and will soon be removed. ' +
'Use `fn f(x Type, y Type)` instead. You can run `v fmt -w "$p.scanner.file_path"` to automatically fix your code.') 'Use `fn f(x Type, y Type)` instead. You can run `v fmt -w "$p.scanner.file_path"` to automatically fix your code.')
} }
p.next() p.next()
@ -746,11 +745,10 @@ fn (mut p Parser) fn_args() ([]table.Param, bool, bool) {
fn (mut p Parser) check_fn_mutable_arguments(typ table.Type, pos token.Position) { fn (mut p Parser) check_fn_mutable_arguments(typ table.Type, pos token.Position) {
sym := p.table.get_type_symbol(typ) sym := p.table.get_type_symbol(typ)
if sym.kind !in if sym.kind !in [.array, .array_fixed, .interface_, .map, .placeholder, .struct_, .sum_type]
[.array, .array_fixed, .interface_, .map, .placeholder, .struct_, .sum_type] && && !typ.is_ptr()&& !typ.is_pointer() {
!typ.is_ptr() && !typ.is_pointer() p.error_with_pos(
{ 'mutable arguments are only allowed for arrays, interfaces, maps, pointers and structs\n' +
p.error_with_pos('mutable arguments are only allowed for arrays, interfaces, maps, pointers and structs\n' +
'return values instead: `fn foo(mut n $sym.name) {` => `fn foo(n $sym.name) $sym.name {`', 'return values instead: `fn foo(mut n $sym.name) {` => `fn foo(n $sym.name) $sym.name {`',
pos) pos)
} }

View File

@ -78,9 +78,8 @@ fn (mut p Parser) for_stmt() ast.Stmt {
} }
p.close_scope() p.close_scope()
return for_c_stmt return for_c_stmt
} else if p.peek_tok.kind in [.key_in, .comma] || } else if p.peek_tok.kind in [.key_in, .comma]
(p.tok.kind == .key_mut && p.peek_tok2.kind in [.key_in, .comma]) || (p.tok.kind == .key_mut && p.peek_tok2.kind in [.key_in, .comma]) {
{
// `for i in vals`, `for i in start .. end`, `for mut user in users`, `for i, mut user in users` // `for i in vals`, `for i in start .. end`, `for mut user in users`, `for i, mut user in users`
mut val_is_mut := p.tok.kind == .key_mut mut val_is_mut := p.tok.kind == .key_mut
mut_pos := p.tok.position() mut_pos := p.tok.position()

View File

@ -179,13 +179,10 @@ fn (mut p Parser) match_expr() ast.MatchExpr {
if p.tok.kind == .key_else { if p.tok.kind == .key_else {
is_else = true is_else = true
p.next() p.next()
} else if (p.tok.kind == .name && !(p.tok.lit == 'C' && } else if (p.tok.kind == .name && !(p.tok.lit == 'C' && p.peek_tok.kind == .dot)
p.peek_tok.kind == .dot) && && (p.tok.lit in table.builtin_type_names || p.tok.lit[0].is_capital()
(p.tok.lit in table.builtin_type_names || p.tok.lit[0].is_capital() || || (p.peek_tok.kind == .dot && p.peek_tok2.lit.len > 0 && p.peek_tok2.lit[0].is_capital())))
(p.peek_tok.kind == .dot && || p.tok.kind == .lsbr {
p.peek_tok2.lit.len > 0 && p.peek_tok2.lit[0].is_capital()))) ||
p.tok.kind == .lsbr
{
mut types := []table.Type{} mut types := []table.Type{}
for { for {
// Sum type match // Sum type match

View File

@ -77,9 +77,8 @@ pub fn (mut p Parser) parse_map_type() table.Type {
// error is reported in parse_type // error is reported in parse_type
return 0 return 0
} }
if !(key_type in [table.string_type_idx, table.voidptr_type_idx] || if !(key_type in [table.string_type_idx, table.voidptr_type_idx]
(key_type.is_int() && !key_type.is_ptr())) || (key_type.is_int() && !key_type.is_ptr())) {
{
s := p.table.type_to_str(key_type) s := p.table.type_to_str(key_type)
p.error_with_pos('maps only support string, integer, rune or voidptr keys for now (not `$s`)', p.error_with_pos('maps only support string, integer, rune or voidptr keys for now (not `$s`)',
p.tok.position()) p.tok.position())

View File

@ -125,11 +125,11 @@ pub fn (mut p Parser) set_path(path string) {
p.file_name = path p.file_name = path
p.file_base = os.base(path) p.file_base = os.base(path)
p.file_name_dir = os.dir(path) p.file_name_dir = os.dir(path)
if path.ends_with('_c.v') || path.ends_with('.c.v') || path.ends_with('.c.vv') || path.ends_with('.c.vsh') { if path.ends_with('_c.v') || path.ends_with('.c.v') || path.ends_with('.c.vv')
|| path.ends_with('.c.vsh') {
p.file_backend_mode = .c p.file_backend_mode = .c
} else if path.ends_with('_js.v') || path.ends_with('.js.v') || path.ends_with('.js.vv') || } else if path.ends_with('_js.v') || path.ends_with('.js.v') || path.ends_with('.js.vv')
path.ends_with('.js.vsh') || path.ends_with('.js.vsh') {
{
p.file_backend_mode = .js p.file_backend_mode = .js
} else { } else {
p.file_backend_mode = .v p.file_backend_mode = .v
@ -677,9 +677,8 @@ pub fn (mut p Parser) stmt(is_top_level bool) ast.Stmt {
} else if p.peek_tok.kind == .name { } else if p.peek_tok.kind == .name {
p.error_with_pos('unexpected name `$p.peek_tok.lit`', p.peek_tok.position()) p.error_with_pos('unexpected name `$p.peek_tok.lit`', p.peek_tok.position())
return ast.Stmt{} return ast.Stmt{}
} else if !p.inside_if_expr && !p.inside_match_body && !p.inside_or_expr && } else if !p.inside_if_expr && !p.inside_match_body && !p.inside_or_expr
p.peek_tok.kind in [.rcbr, .eof] && !p.mark_var_as_used(p.tok.lit) && p.peek_tok.kind in [.rcbr, .eof]&& !p.mark_var_as_used(p.tok.lit) {
{
p.error_with_pos('`$p.tok.lit` evaluated but not used', p.tok.position()) p.error_with_pos('`$p.tok.lit` evaluated but not used', p.tok.position())
return ast.Stmt{} return ast.Stmt{}
} }
@ -1004,12 +1003,11 @@ fn (mut p Parser) parse_multi_expr(is_top_level bool) ast.Stmt {
} }
if p.tok.kind in [.assign, .decl_assign] || p.tok.kind.is_assign() { if p.tok.kind in [.assign, .decl_assign] || p.tok.kind.is_assign() {
return p.partial_assign_stmt(left, left_comments) return p.partial_assign_stmt(left, left_comments)
} else if tok.kind !in [.key_if, .key_match, .key_lock, .key_rlock, .key_select] && } else if tok.kind !in [.key_if, .key_match, .key_lock, .key_rlock, .key_select]
left0 !is ast.CallExpr && (is_top_level || p.tok.kind != .rcbr) && left0 !is ast.PostfixExpr && && left0 !is ast.CallExpr && (is_top_level || p.tok.kind != .rcbr)
!(left0 is ast.InfixExpr && && left0 !is ast.PostfixExpr && !(left0 is ast.InfixExpr
(left0 as ast.InfixExpr).op in [.left_shift, .arrow]) && left0 !is ast.ComptimeCall && && (left0 as ast.InfixExpr).op in [.left_shift, .arrow]) && left0 !is ast.ComptimeCall
left0 !is ast.SelectorExpr && left0 !is ast.SelectorExpr {
{
p.error_with_pos('expression evaluated but not used', left0.position()) p.error_with_pos('expression evaluated but not used', left0.position())
return ast.Stmt{} return ast.Stmt{}
} }
@ -1100,9 +1098,7 @@ fn (p &Parser) is_generic_call() bool {
return !lit0_is_capital && p.peek_tok.kind == .lt && (match p.peek_tok2.kind { return !lit0_is_capital && p.peek_tok.kind == .lt && (match p.peek_tok2.kind {
.name { .name {
// maybe `f<int>`, `f<map[`, f<string, // maybe `f<int>`, `f<map[`, f<string,
(p.peek_tok2.kind == .name && (p.peek_tok2.kind == .name && p.peek_tok3.kind in [.gt, .comma]) || (p.peek_tok2.lit == 'map' && p.peek_tok3.kind == .lsbr)
p.peek_tok3.kind in [.gt, .comma]) ||
(p.peek_tok2.lit == 'map' && p.peek_tok3.kind == .lsbr)
} }
.lsbr { .lsbr {
// maybe `f<[]T>`, assume `var < []` is invalid // maybe `f<[]T>`, assume `var < []` is invalid
@ -1206,9 +1202,8 @@ pub fn (mut p Parser) name_expr() ast.Expr {
} }
known_var := p.mark_var_as_used(p.tok.lit) known_var := p.mark_var_as_used(p.tok.lit)
mut is_mod_cast := false mut is_mod_cast := false
if p.peek_tok.kind == .dot && !known_var && if p.peek_tok.kind == .dot && !known_var && (language != .v || p.known_import(p.tok.lit)
(language != .v || p.known_import(p.tok.lit) || p.mod.all_after_last('.') == p.tok.lit) || p.mod.all_after_last('.') == p.tok.lit) {
{
// p.tok.lit has been recognized as a module // p.tok.lit has been recognized as a module
if language == .c { if language == .c {
mod = 'C' mod = 'C'
@ -1218,13 +1213,11 @@ pub fn (mut p Parser) name_expr() ast.Expr {
if p.tok.lit in p.imports { if p.tok.lit in p.imports {
// mark the imported module as used // mark the imported module as used
p.register_used_import(p.tok.lit) p.register_used_import(p.tok.lit)
if p.peek_tok.kind == .dot && if p.peek_tok.kind == .dot && p.peek_tok2.kind != .eof && p.peek_tok2.lit.len > 0
p.peek_tok2.kind != .eof && p.peek_tok2.lit.len > 0 && p.peek_tok2.lit[0].is_capital() && p.peek_tok2.lit[0].is_capital() {
{
is_mod_cast = true is_mod_cast = true
} else if p.peek_tok.kind == .dot && } else if p.peek_tok.kind == .dot && p.peek_tok2.kind != .eof
p.peek_tok2.kind != .eof && p.peek_tok2.lit.len == 0 && p.peek_tok2.lit.len == 0 {
{
// incomplete module selector must be handled by dot_expr instead // incomplete module selector must be handled by dot_expr instead
node = p.parse_ident(language) node = p.parse_ident(language)
return node return node
@ -1256,10 +1249,9 @@ pub fn (mut p Parser) name_expr() ast.Expr {
name_w_mod := p.prepend_mod(name) name_w_mod := p.prepend_mod(name)
// type cast. TODO: finish // type cast. TODO: finish
// if name in table.builtin_type_names { // if name in table.builtin_type_names {
if (!known_var && (name in p.table.type_idxs || if (!known_var && (name in p.table.type_idxs || name_w_mod in p.table.type_idxs)
name_w_mod in p.table.type_idxs) && name !in ['C.stat', 'C.sigaction']) || && name !in ['C.stat', 'C.sigaction']) || is_mod_cast
is_mod_cast || (language == .v && name[0].is_capital()) || (language == .v && name[0].is_capital()) {
{
// MainLetter(x) is *always* a cast, as long as it is not `C.` // MainLetter(x) is *always* a cast, as long as it is not `C.`
// TODO handle C.stat() // TODO handle C.stat()
start_pos := p.tok.position() start_pos := p.tok.position()
@ -1299,13 +1291,10 @@ pub fn (mut p Parser) name_expr() ast.Expr {
// println('calling $p.tok.lit') // println('calling $p.tok.lit')
node = p.call_expr(language, mod) node = p.call_expr(language, mod)
} }
} else if (p.peek_tok.kind == .lcbr || } else if (p.peek_tok.kind == .lcbr || (p.peek_tok.kind == .lt && lit0_is_capital))
(p.peek_tok.kind == .lt && lit0_is_capital)) && && (!p.inside_match || (p.inside_select && prev_tok_kind == .arrow && lit0_is_capital))
(!p.inside_match || (p.inside_select && prev_tok_kind == .arrow && lit0_is_capital)) && && !p.inside_match_case && (!p.inside_if || p.inside_select)
!p.inside_match_case && && (!p.inside_for || p.inside_select) { // && (p.tok.lit[0].is_capital() || p.builtin_mod) {
(!p.inside_if || p.inside_select) &&
(!p.inside_for || p.inside_select)
{ // && (p.tok.lit[0].is_capital() || p.builtin_mod) {
return p.struct_init(false) // short_syntax: false return p.struct_init(false) // short_syntax: false
} else if p.peek_tok.kind == .dot && (lit0_is_capital && !known_var && language == .v) { } else if p.peek_tok.kind == .dot && (lit0_is_capital && !known_var && language == .v) {
// T.name // T.name
@ -2044,10 +2033,9 @@ const (
// left hand side of `=` or `:=` in `a,b,c := 1,2,3` // left hand side of `=` or `:=` in `a,b,c := 1,2,3`
fn (mut p Parser) global_decl() ast.GlobalDecl { fn (mut p Parser) global_decl() ast.GlobalDecl {
if !p.pref.translated && !p.pref.is_livemain && !p.builtin_mod && !p.pref.building_v && if !p.pref.translated && !p.pref.is_livemain && !p.builtin_mod && !p.pref.building_v
p.mod != 'ui' && p.mod != 'gg2' && p.mod != 'uiold' && !p.pref.enable_globals && !p.pref.is_fmt && && p.mod != 'ui' && p.mod != 'gg2' && p.mod != 'uiold' && !p.pref.enable_globals
p.mod !in global_enabled_mods && !p.pref.is_fmt&& p.mod !in global_enabled_mods {
{
p.error('use `v --enable-globals ...` to enable globals') p.error('use `v --enable-globals ...` to enable globals')
return ast.GlobalDecl{} return ast.GlobalDecl{}
} }

View File

@ -385,9 +385,8 @@ fn (mut p Parser) infix_expr(left ast.Expr) ast.Expr {
} }
right = p.expr(precedence) right = p.expr(precedence)
p.expecting_type = prev_expecting_type p.expecting_type = prev_expecting_type
if p.pref.is_vet && op in [.key_in, .not_in] && right is ast.ArrayInit && (right as ast.ArrayInit).exprs.len == if p.pref.is_vet && op in [.key_in, .not_in] && right is ast.ArrayInit
1 && (right as ast.ArrayInit).exprs.len == 1 {
{
p.vet_error('Use `var == value` instead of `var in [value]`', pos.line_nr, vet.FixKind.vfmt) p.vet_error('Use `var == value` instead of `var in [value]`', pos.line_nr, vet.FixKind.vfmt)
} }
mut or_stmts := []ast.Stmt{} mut or_stmts := []ast.Stmt{}

View File

@ -65,7 +65,8 @@ fn (mut p Parser) struct_decl() ast.StructDecl {
p.error('`$p.tok.lit` lacks body') p.error('`$p.tok.lit` lacks body')
return ast.StructDecl{} return ast.StructDecl{}
} }
if language == .v && !p.builtin_mod && name.len > 0 && !name[0].is_capital() && !p.pref.translated { if language == .v && !p.builtin_mod && name.len > 0 && !name[0].is_capital()
&& !p.pref.translated {
p.error_with_pos('struct name `$name` must begin with capital letter', name_pos) p.error_with_pos('struct name `$name` must begin with capital letter', name_pos)
return ast.StructDecl{} return ast.StructDecl{}
} }
@ -176,9 +177,8 @@ fn (mut p Parser) struct_decl() ast.StructDecl {
} }
} }
field_start_pos := p.tok.position() field_start_pos := p.tok.position()
is_embed := ((p.tok.lit.len > 1 && p.tok.lit[0].is_capital()) || is_embed := ((p.tok.lit.len > 1 && p.tok.lit[0].is_capital())
p.peek_tok.kind == .dot) && || p.peek_tok.kind == .dot)&& language == .v
language == .v
is_on_top := ast_fields.len == 0 && !(is_field_mut || is_field_mut || is_field_global) is_on_top := ast_fields.len == 0 && !(is_field_mut || is_field_mut || is_field_global)
mut field_name := '' mut field_name := ''
mut typ := table.Type(0) mut typ := table.Type(0)
@ -385,8 +385,11 @@ fn (mut p Parser) struct_init(short_syntax bool) ast.StructInit {
expr = p.expr(0) expr = p.expr(0)
comments = p.eat_line_end_comments() comments = p.eat_line_end_comments()
last_field_pos := expr.position() last_field_pos := expr.position()
field_len := if last_field_pos.len > 0 { last_field_pos.pos - first_field_pos.pos + field_len := if last_field_pos.len > 0 {
last_field_pos.len } else { first_field_pos.len + 1 } last_field_pos.pos - first_field_pos.pos + last_field_pos.len
} else {
first_field_pos.len + 1
}
field_pos = token.Position{ field_pos = token.Position{
line_nr: first_field_pos.line_nr line_nr: first_field_pos.line_nr
pos: first_field_pos.pos pos: first_field_pos.pos

View File

@ -481,9 +481,8 @@ pub fn parse_args(args []string) (&Preferences, string) {
exit(tmp_result) exit(tmp_result)
} }
must_exist(res.path) must_exist(res.path)
if !res.path.ends_with('.v') && os.is_executable(res.path) && os.is_file(res.path) && if !res.path.ends_with('.v') && os.is_executable(res.path) && os.is_file(res.path)
os.is_file(res.path + '.v') && os.is_file(res.path + '.v') {
{
eprintln('It looks like you wanted to run "${res.path}.v", so we went ahead and did that since "$res.path" is an executable.') eprintln('It looks like you wanted to run "${res.path}.v", so we went ahead and did that since "$res.path" is an executable.')
res.path += '.v' res.path += '.v'
} }
@ -569,7 +568,8 @@ fn parse_define(mut prefs Preferences, define string) {
prefs.compile_defines << define_parts[0] prefs.compile_defines << define_parts[0]
} }
else { else {
println('V error: Unknown define argument value `${define_parts[1]}` for ${define_parts[0]}.' + println(
'V error: Unknown define argument value `${define_parts[1]}` for ${define_parts[0]}.' +
' Expected `0` or `1`.') ' Expected `0` or `1`.')
exit(1) exit(1)
} }

View File

@ -132,8 +132,8 @@ pub fn new_vet_scanner(text string, comments_mode CommentsMode, pref &pref.Prefe
[inline] [inline]
fn (s &Scanner) should_parse_comment() bool { fn (s &Scanner) should_parse_comment() bool {
return (s.comments_mode == .parse_comments) || return (s.comments_mode == .parse_comments)
(s.comments_mode == .toplevel_comments && !s.is_inside_toplvl_statement) || (s.comments_mode == .toplevel_comments && !s.is_inside_toplvl_statement)
} }
// NB: this is called by v's parser // NB: this is called by v's parser
@ -473,7 +473,8 @@ fn (mut s Scanner) end_of_file() token.Token {
s.eofs++ s.eofs++
if s.eofs > 50 { if s.eofs > 50 {
s.line_nr-- s.line_nr--
panic('the end of file `$s.file_path` has been reached 50 times already, the v parser is probably stuck.\n' + panic(
'the end of file `$s.file_path` has been reached 50 times already, the v parser is probably stuck.\n' +
'This should not happen. Please report the bug here, and include the last 2-3 lines of your source code:\n' + 'This should not happen. Please report the bug here, and include the last 2-3 lines of your source code:\n' +
'https://github.com/vlang/v/issues/new?labels=Bug&template=bug_report.md') 'https://github.com/vlang/v/issues/new?labels=Bug&template=bug_report.md')
} }
@ -603,7 +604,8 @@ fn (mut s Scanner) text_scan() token.Token {
} }
// end of `$expr` // end of `$expr`
// allow `'$a.b'` and `'$a.c()'` // allow `'$a.b'` and `'$a.c()'`
if s.is_inter_start && next_char == `\\` && s.look_ahead(2) !in [`x`, `n`, `r`, `\\`, `t`, `e`] { if s.is_inter_start && next_char == `\\`
&& s.look_ahead(2) !in [`x`, `n`, `r`, `\\`, `t`, `e`] {
s.warn('unknown escape sequence \\${s.look_ahead(2)}') s.warn('unknown escape sequence \\${s.look_ahead(2)}')
} }
if s.is_inter_start && next_char == `(` { if s.is_inter_start && next_char == `(` {
@ -901,14 +903,12 @@ fn (mut s Scanner) text_scan() token.Token {
if nextc == `=` { if nextc == `=` {
s.pos++ s.pos++
return s.new_token(.ne, '', 2) return s.new_token(.ne, '', 2)
} else if s.text.len > s.pos + 3 && } else if s.text.len > s.pos + 3 && nextc == `i` && s.text[s.pos + 2] == `n`
nextc == `i` && s.text[s.pos + 2] == `n` && s.text[s.pos + 3].is_space() && s.text[s.pos + 3].is_space() {
{
s.pos += 2 s.pos += 2
return s.new_token(.not_in, '', 3) return s.new_token(.not_in, '', 3)
} else if s.text.len > s.pos + 3 && } else if s.text.len > s.pos + 3 && nextc == `i` && s.text[s.pos + 2] == `s`
nextc == `i` && s.text[s.pos + 2] == `s` && s.text[s.pos + 3].is_space() && s.text[s.pos + 3].is_space() {
{
s.pos += 2 s.pos += 2
return s.new_token(.not_is, '', 3) return s.new_token(.not_is, '', 3)
} else { } else {
@ -1037,9 +1037,8 @@ fn (mut s Scanner) ident_string() string {
// } // }
mut n_cr_chars := 0 mut n_cr_chars := 0
mut start := s.pos mut start := s.pos
if s.text[start] == s.quote || if s.text[start] == s.quote
(s.text[start] == s.inter_quote && (s.is_inter_start || s.is_enclosed_inter)) || (s.text[start] == s.inter_quote && (s.is_inter_start || s.is_enclosed_inter)) {
{
start++ start++
} }
s.is_inside_string = false s.is_inside_string = false
@ -1068,9 +1067,8 @@ fn (mut s Scanner) ident_string() string {
} }
// Don't allow \0 // Don't allow \0
if c == `0` && s.pos > 2 && prevc == slash { if c == `0` && s.pos > 2 && prevc == slash {
if (s.pos < s.text.len - 1 && s.text[s.pos + 1].is_digit()) || if (s.pos < s.text.len - 1 && s.text[s.pos + 1].is_digit())
s.count_symbol_before(s.pos - 1, slash) % 2 == 0 || s.count_symbol_before(s.pos - 1, slash) % 2 == 0 {
{
} else if !is_cstr && !is_raw { } else if !is_cstr && !is_raw {
s.error(r'cannot use `\0` (NULL character) in the string literal') s.error(r'cannot use `\0` (NULL character) in the string literal')
} }
@ -1089,11 +1087,11 @@ fn (mut s Scanner) ident_string() string {
s.error(r'`\x` used with no following hex digits') s.error(r'`\x` used with no following hex digits')
} }
// Escape `\u` // Escape `\u`
if c == `u` && (s.text[s.pos + 1] == s.quote || if c == `u`
s.text[s.pos + 2] == s.quote || s.text[s.pos + 3] == s.quote || s.text[s.pos + && (s.text[s.pos + 1] == s.quote || s.text[s.pos + 2] == s.quote || s.text[s.pos + 3] == s.quote || s.text[s.pos + 4] == s.quote || !s.text[s.pos + 1].is_hex_digit()
4] == s.quote || !s.text[s.pos + 1].is_hex_digit() || !s.text[s.pos + 2].is_hex_digit() || || !s.text[s.pos + 2].is_hex_digit()
!s.text[s.pos + 3].is_hex_digit() || !s.text[s.pos + 4].is_hex_digit()) || !s.text[s.pos + 3].is_hex_digit()
{ || !s.text[s.pos + 4].is_hex_digit()) {
s.error(r'`\u` incomplete unicode character value') s.error(r'`\u` incomplete unicode character value')
} }
} }
@ -1106,9 +1104,8 @@ fn (mut s Scanner) ident_string() string {
break break
} }
// $var // $var
if prevc == `$` && util.is_name_char(c) && !is_raw && s.count_symbol_before(s.pos - 2, slash) % if prevc == `$` && util.is_name_char(c) && !is_raw
2 == 0 && s.count_symbol_before(s.pos - 2, slash) % 2 == 0 {
{
s.is_inside_string = true s.is_inside_string = true
s.is_inter_start = true s.is_inter_start = true
s.pos -= 2 s.pos -= 2
@ -1175,7 +1172,8 @@ fn (mut s Scanner) ident_char() string {
if len != 1 { if len != 1 {
u := c.ustring() u := c.ustring()
if u.len != 1 { if u.len != 1 {
s.error('invalid character literal (more than one character)\n' + 'use quotes for strings, backticks for characters') s.error('invalid character literal (more than one character)\n' +
'use quotes for strings, backticks for characters')
} }
} }
// Escapes a `'` character // Escapes a `'` character

View File

@ -41,9 +41,10 @@ pub mut:
} }
fn (f &Fn) method_equals(o &Fn) bool { fn (f &Fn) method_equals(o &Fn) bool {
return f.params[1..].equals(o.params[1..]) && f.return_type == o.return_type && f.is_variadic == return f.params[1..].equals(o.params[1..]) && f.return_type == o.return_type
o.is_variadic && f.language == o.language && f.generic_names == o.generic_names && && f.is_variadic == o.is_variadic && f.language == o.language
f.is_pub == o.is_pub && f.mod == o.mod && f.name == o.name && f.generic_names == o.generic_names && f.is_pub == o.is_pub && f.mod == o.mod
&& f.name == o.name
} }
pub struct Param { pub struct Param {

View File

@ -409,14 +409,13 @@ pub fn (tok Token) is_scalar() bool {
// is_unary returns true if the token can be in a unary expression // is_unary returns true if the token can be in a unary expression
pub fn (tok Token) is_unary() bool { pub fn (tok Token) is_unary() bool {
return tok.kind in // `+` | `-` | `!` | `~` | `*` | `&` | `<-`
[ return tok.kind in [ .plus, .minus, .not, .bit_not, .mul, .amp, .arrow]
/* `+` | `-` | `!` | `~` | `*` | `&` */.plus, .minus, .not, .bit_not, .mul, .amp, .arrow]
} }
pub fn (tok Kind) is_relational() bool { pub fn (tok Kind) is_relational() bool {
return tok in [ // `<` | `<=` | `>` | `>=` | `==` | `!=`
/* `<` | `<=` | `>` | `>=` */.lt, .le, .gt, .ge, .eq, .ne] return tok in [.lt, .le, .gt, .ge, .eq, .ne]
} }
pub fn (k Kind) is_start_of_type() bool { pub fn (k Kind) is_start_of_type() bool {
@ -429,7 +428,7 @@ pub fn (kind Kind) is_prefix() bool {
pub fn (kind Kind) is_infix() bool { pub fn (kind Kind) is_infix() bool {
return kind in return kind in
[.plus, .minus, .mod, .mul, .div, .eq, .ne, .gt, .lt, .key_in, /* */.key_as, .ge, .le, .logical_or, .xor, .not_in, .key_is, .not_is, /* */.and, .dot, .pipe, .amp, .left_shift, .right_shift, .arrow] [.plus, .minus, .mod, .mul, .div, .eq, .ne, .gt, .lt, .key_in, .key_as, .ge, .le, .logical_or, .xor, .not_in, .key_is, .not_is, .and, .dot, .pipe, .amp, .left_shift, .right_shift, .arrow]
} }
// Pass table.builtin_type_names // Pass table.builtin_type_names

View File

@ -12,8 +12,7 @@ pub fn find_working_diff_command() ?string {
if env_difftool.len > 0 { if env_difftool.len > 0 {
known_diff_tools << env_difftool known_diff_tools << env_difftool
} }
known_diff_tools << known_diff_tools << ['colordiff', 'gdiff', 'diff', 'colordiff.exe', 'diff.exe', 'opendiff', 'code', 'code.cmd']
['colordiff', 'gdiff', 'diff', 'colordiff.exe', 'diff.exe', 'opendiff', 'code', 'code.cmd']
// NOTE: code.cmd is the Windows variant of the `code` cli tool // NOTE: code.cmd is the Windows variant of the `code` cli tool
for diffcmd in known_diff_tools { for diffcmd in known_diff_tools {
if diffcmd == 'opendiff' { // opendiff has no `--version` option if diffcmd == 'opendiff' { // opendiff has no `--version` option

View File

@ -119,8 +119,12 @@ pub fn source_context(kind string, source string, column int, pos token.Position
sline := source_lines[iline] sline := source_lines[iline]
start_column := imax(0, imin(column, sline.len)) start_column := imax(0, imin(column, sline.len))
end_column := imax(0, imin(column + imax(0, pos.len), sline.len)) end_column := imax(0, imin(column + imax(0, pos.len), sline.len))
cline := if iline == pos.line_nr { sline[..start_column] + color(kind, sline[start_column..end_column]) + cline := if iline == pos.line_nr {
sline[end_column..] } else { sline } sline[..start_column] + color(kind, sline[start_column..end_column]) +
sline[end_column..]
} else {
sline
}
clines << '${iline + 1:5d} | ' + cline.replace('\t', tab_spaces) clines << '${iline + 1:5d} | ' + cline.replace('\t', tab_spaces)
// //
if iline == pos.line_nr { if iline == pos.line_nr {

View File

@ -90,9 +90,8 @@ pub fn mod_path_to_full_name(mod string, path string) ?string {
if ls := os.ls(parent) { if ls := os.ls(parent) {
// currently CI clones some modules into the v repo to test, the condition // currently CI clones some modules into the v repo to test, the condition
// after `'v.mod' in ls` can be removed once a proper solution is added // after `'v.mod' in ls` can be removed once a proper solution is added
if 'v.mod' in ls && if 'v.mod' in ls
(try_path_parts.len > i && try_path_parts[i] != 'v' && 'vlib' !in ls) && (try_path_parts.len > i && try_path_parts[i] != 'v' && 'vlib' !in ls) {
{
last_v_mod = j last_v_mod = j
} }
continue continue

View File

@ -157,8 +157,8 @@ pub fn launch_tool(is_verbose bool, tool_name string, args []string) {
} }
disabling_file := recompilation.disabling_file(vroot) disabling_file := recompilation.disabling_file(vroot)
is_recompilation_disabled := os.exists(disabling_file) is_recompilation_disabled := os.exists(disabling_file)
should_compile := !is_recompilation_disabled && should_compile := !is_recompilation_disabled
should_recompile_tool(vexe, tool_source, tool_name, tool_exe) && should_recompile_tool(vexe, tool_source, tool_name, tool_exe)
if is_verbose { if is_verbose {
println('launch_tool should_compile: $should_compile') println('launch_tool should_compile: $should_compile')
} }

View File

@ -491,11 +491,9 @@ fn handle_conn<T>(mut conn net.TcpConn, mut app T) {
// since such methods have a priority. // since such methods have a priority.
// For example URL `/register` matches route `/:user`, but `fn register()` // For example URL `/register` matches route `/:user`, but `fn register()`
// should be called first. // should be called first.
if (req_method_str == '' && if (req_method_str == '' && url_words[0] == method.name && url_words.len == 1)
url_words[0] == method.name && url_words.len == 1) || || (req_method_str == req.method.str() && url_words[0] == method.name
(req_method_str == req.method.str() && url_words[0] == method.name && url_words.len == && url_words.len == 1) {
1)
{
$if debug { $if debug {
println('easy match method=$method.name') println('easy match method=$method.name')
} }
@ -521,9 +519,8 @@ fn handle_conn<T>(mut conn net.TcpConn, mut app T) {
if route_words.len == 1 && route_words[0] in methods_without_first { if route_words.len == 1 && route_words[0] in methods_without_first {
req_method << route_words[0] req_method << route_words[0]
} }
if url_words.len == route_words.len || if url_words.len == route_words.len
(url_words.len >= route_words.len - 1 && route_words.len > 0 && route_words.last().ends_with('...')) || (url_words.len >= route_words.len - 1 && route_words.len > 0 && route_words.last().ends_with('...')) {
{
if req_method.len > 0 { if req_method.len > 0 {
if req_method_str.to_lower()[1..] !in req_method { if req_method_str.to_lower()[1..] !in req_method {
continue continue

View File

@ -118,9 +118,9 @@ fn (mut ws Client) read_handshake_str() ?string {
} }
msg[total_bytes_read] = buffer[0] msg[total_bytes_read] = buffer[0]
total_bytes_read++ total_bytes_read++
if total_bytes_read > 5 && msg[total_bytes_read - 1] == `\n` && msg[total_bytes_read - if total_bytes_read > 5 && msg[total_bytes_read - 1] == `\n`
2] == `\r` && msg[total_bytes_read - 3] == `\n` && msg[total_bytes_read - 4] == `\r` && msg[total_bytes_read - 2] == `\r` && msg[total_bytes_read - 3] == `\n`
{ && msg[total_bytes_read - 4] == `\r` {
break break
} }
} }

View File

@ -42,9 +42,8 @@ pub fn (mut ws Client) validate_frame(frame &Frame) ? {
ws.close(1002, 'rsv cannot be other than 0, not negotiated') ws.close(1002, 'rsv cannot be other than 0, not negotiated')
return error('rsv cannot be other than 0, not negotiated') return error('rsv cannot be other than 0, not negotiated')
} }
if (int(frame.opcode) >= 3 && int(frame.opcode) <= 7) || if (int(frame.opcode) >= 3 && int(frame.opcode) <= 7)
(int(frame.opcode) >= 11 && int(frame.opcode) <= 15) || (int(frame.opcode) >= 11 && int(frame.opcode) <= 15) {
{
ws.close(1002, 'use of reserved opcode') ? ws.close(1002, 'use of reserved opcode') ?
return error('use of reserved opcode') return error('use of reserved opcode')
} }

View File

@ -232,7 +232,11 @@ pub fn (mut ws Client) write_ptr(bytes byteptr, payload_len int, code OPCode) ?
// todo: send error here later // todo: send error here later
return error('trying to write on a closed socket!') return error('trying to write on a closed socket!')
} }
mut header_len := 2 + if payload_len > 125 { 2 } else { 0 } + if payload_len > 0xffff { 6 } else { 0 } mut header_len := 2 + if payload_len > 125 { 2 } else { 0 } + if payload_len > 0xffff {
6
} else {
0
}
if !ws.is_server { if !ws.is_server {
header_len += 4 header_len += 4
} }