checker: prohibit using non-lvalue as mut receiver (#10790)

pull/10833/head
shadowninja55 2021-07-16 06:03:40 -04:00 committed by GitHub
parent 7c5f012cbc
commit 0d587d3580
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 33 additions and 39 deletions

View File

@ -70,9 +70,10 @@ pub fn (mut b Builder) front_stages(v_files []string) ? {
util.timing_start('PARSE')
b.parsed_files = parser.parse_files(v_files, b.table, b.pref, b.global_scope)
b.parse_imports()
util.get_timers().show('SCAN')
util.get_timers().show('PARSE')
util.get_timers().show_if_exists('PARSE stmt')
mut timers := util.get_timers()
timers.show('SCAN')
timers.show('PARSE')
timers.show_if_exists('PARSE stmt')
if b.pref.only_check_syntax {
return error('stop_after_parser')
}

View File

@ -43,7 +43,8 @@ pub fn compile(command string, pref &pref.Preferences) {
.js { b.compile_js() }
.native { b.compile_native() }
}
util.get_timers().show_remaining()
mut timers := util.get_timers()
timers.show_remaining()
if pref.is_stats {
compilation_time_micros := 1 + sw.elapsed().microseconds()
scompilation_time_ms := util.bold('${f64(compilation_time_micros) / 1000.0:6.3f}')

View File

@ -2106,6 +2106,9 @@ pub fn (mut c Checker) method_call(mut call_expr ast.CallExpr) ast.Type {
}
if method.params[0].is_mut {
to_lock, pos := c.fail_if_immutable(call_expr.left)
if !call_expr.left.is_lvalue() {
c.error('cannot pass expression as `mut`', call_expr.left.position())
}
// call_expr.is_mut = true
if to_lock != '' && rec_share != .shared_t {
c.error('$to_lock is `shared` and must be `lock`ed to be passed as `mut`',

View File

@ -0,0 +1,5 @@
vlib/v/checker/tests/mut_receiver_lit.vv:10:1: error: cannot pass expression as `mut`
8 | }
9 |
10 | Box{}.set(0)
| ~~~~~

View File

@ -0,0 +1,10 @@
struct Box {
mut:
value int
}
fn (mut box Box) set(value int) {
box.value = value
}
Box{}.set(0)

View File

@ -537,11 +537,12 @@ fn (mut s Scanner) end_of_file() token.Token {
}
pub fn (mut s Scanner) scan_all_tokens_in_buffer(mode CommentsMode) {
util.get_timers().measure_pause('PARSE')
mut timers := util.get_timers()
timers.measure_pause('PARSE')
util.timing_start('SCAN')
defer {
util.timing_measure_cumulative('SCAN')
util.get_timers().measure_resume('PARSE')
timers.measure_resume('PARSE')
}
oldmode := s.comments_mode
s.comments_mode = mode

View File

@ -1,30 +0,0 @@
struct Test {
mut:
val int
}
// this must return a reference, or else you'll get a C error
// TODO: add a proper checker check for that case
fn new(x int) &Test {
return &Test{x}
}
fn (mut t Test) inc() &Test {
t.val++
return unsafe { t }
}
fn (mut t Test) add(x int) &Test {
t.val += x
return unsafe { t }
}
fn (mut t Test) div(x int) &Test {
t.val /= x
return unsafe { t }
}
fn test_method_call_chains() {
mut x := new(4).inc().inc().inc().inc().add(4).div(2).inc()
assert x.val == 7
}

View File

@ -28,15 +28,18 @@ pub fn get_timers() &Timers {
}
pub fn timing_start(label string) {
get_timers().start(label)
mut t := get_timers()
t.start(label)
}
pub fn timing_measure(label string) {
get_timers().show(label)
mut t := get_timers()
t.show(label)
}
pub fn timing_measure_cumulative(label string) {
get_timers().measure_cumulative(label)
mut t := get_timers()
t.measure_cumulative(label)
}
pub fn timing_set_should_print(should_print bool) {