diff options
Diffstat (limited to 'v_windows/v/cmd/tools/vvet')
14 files changed, 460 insertions, 0 deletions
| diff --git a/v_windows/v/cmd/tools/vvet/tests/array_init_one_val.out b/v_windows/v/cmd/tools/vvet/tests/array_init_one_val.out new file mode 100644 index 0000000..e10d511 --- /dev/null +++ b/v_windows/v/cmd/tools/vvet/tests/array_init_one_val.out @@ -0,0 +1,2 @@ +cmd/tools/vvet/tests/array_init_one_val.vv:2: error: Use `var == value` instead of `var in [value]` +NB: You can run `v fmt -w file.v` to fix these errors automatically diff --git a/v_windows/v/cmd/tools/vvet/tests/array_init_one_val.vv b/v_windows/v/cmd/tools/vvet/tests/array_init_one_val.vv new file mode 100644 index 0000000..2aa3514 --- /dev/null +++ b/v_windows/v/cmd/tools/vvet/tests/array_init_one_val.vv @@ -0,0 +1,5 @@ +fn main() { +	if 1 in [1] { +		println('hello world') +	} +} diff --git a/v_windows/v/cmd/tools/vvet/tests/indent_with_space.out b/v_windows/v/cmd/tools/vvet/tests/indent_with_space.out new file mode 100644 index 0000000..b307e20 --- /dev/null +++ b/v_windows/v/cmd/tools/vvet/tests/indent_with_space.out @@ -0,0 +1,6 @@ +cmd/tools/vvet/tests/indent_with_space.vv:2: error: Looks like you are using spaces for indentation. +cmd/tools/vvet/tests/indent_with_space.vv:10: error: Looks like you are using spaces for indentation. +cmd/tools/vvet/tests/indent_with_space.vv:17: error: Looks like you are using spaces for indentation. +cmd/tools/vvet/tests/indent_with_space.vv:20: error: Looks like you are using spaces for indentation. +cmd/tools/vvet/tests/indent_with_space.vv:22: error: Looks like you are using spaces for indentation. +NB: You can run `v fmt -w file.v` to fix these errors automatically diff --git a/v_windows/v/cmd/tools/vvet/tests/indent_with_space.vv b/v_windows/v/cmd/tools/vvet/tests/indent_with_space.vv new file mode 100644 index 0000000..9b466ef --- /dev/null +++ b/v_windows/v/cmd/tools/vvet/tests/indent_with_space.vv @@ -0,0 +1,24 @@ +fn main() { +    _ = 1 == 2 +} + +fn block_comments() { +	/* tab to indent the comment +  spaces before +  also spaces before +  same here */ +  /* spaces for comment indentation (ouch) +  and inside too +  */ +} + +fn space_inside_strings() { +	// Plain strings +  str := "Bad space usage for variable indentation. +  Here it's fine. +  Here too." +  str2 := 'linebreak and space\n  inside' +	// String interpolation +  si1 := 'Error here $foo +  and not here' +} diff --git a/v_windows/v/cmd/tools/vvet/tests/module_file_test.out b/v_windows/v/cmd/tools/vvet/tests/module_file_test.out new file mode 100644 index 0000000..b033e71 --- /dev/null +++ b/v_windows/v/cmd/tools/vvet/tests/module_file_test.out @@ -0,0 +1,5 @@ +cmd/tools/vvet/tests/module_file_test.vv:7: warning: Function documentation seems to be missing for "pub fn foo() string". +cmd/tools/vvet/tests/module_file_test.vv:13: warning: A function name is missing from the documentation of "pub fn bar() string". +cmd/tools/vvet/tests/module_file_test.vv:35: warning: Function documentation seems to be missing for "pub fn (f Foo) foo() string". +cmd/tools/vvet/tests/module_file_test.vv:46: warning: A function name is missing from the documentation of "pub fn (f Foo) fooo() string". +cmd/tools/vvet/tests/module_file_test.vv:52: warning: The documentation for "pub fn (f Foo) boo() string" seems incomplete.
\ No newline at end of file diff --git a/v_windows/v/cmd/tools/vvet/tests/module_file_test.vv b/v_windows/v/cmd/tools/vvet/tests/module_file_test.vv new file mode 100644 index 0000000..f0f5b24 --- /dev/null +++ b/v_windows/v/cmd/tools/vvet/tests/module_file_test.vv @@ -0,0 +1,55 @@ +module foo + +struct Foo { +	foo int +} + +pub fn foo() string { +	// Missing doc +	return 'foo' +} + +// foo does bar +pub fn bar() string { +	// not using convention style: '// <fn name>' +	return 'bar' +} + +// fooo does x +pub fn fooo() string { +	// Documented +	return 'fooo' +} + +// booo does x +fn booo() string { +	// Documented, but not pub +	return 'booo' +} + +fn boo() string { +	// Missing doc +	return 'boo' +} + +pub fn (f Foo) foo() string { +	// Missing doc +	return f.fo() +} + +fn (f Foo) fo() string { +	// Missing doc, but not pub +	return 'foo' +} + +// wrong doc +pub fn (f Foo) fooo() string { +	// not using convention +	return f.fo() +} + +// boo +pub fn (f Foo) boo() string { +	// Incomplete doc +	return f.fo() +} diff --git a/v_windows/v/cmd/tools/vvet/tests/parens_space_a.out b/v_windows/v/cmd/tools/vvet/tests/parens_space_a.out new file mode 100644 index 0000000..dbda99a --- /dev/null +++ b/v_windows/v/cmd/tools/vvet/tests/parens_space_a.out @@ -0,0 +1,2 @@ +cmd/tools/vvet/tests/parens_space_a.vv:1: error: Looks like you are adding a space after `(` +NB: You can run `v fmt -w file.v` to fix these errors automatically diff --git a/v_windows/v/cmd/tools/vvet/tests/parens_space_a.vv b/v_windows/v/cmd/tools/vvet/tests/parens_space_a.vv new file mode 100644 index 0000000..2b3b508 --- /dev/null +++ b/v_windows/v/cmd/tools/vvet/tests/parens_space_a.vv @@ -0,0 +1,4 @@ +fn main() { +	_ = 1 + ( 1 + 2) +} + diff --git a/v_windows/v/cmd/tools/vvet/tests/parens_space_b.out b/v_windows/v/cmd/tools/vvet/tests/parens_space_b.out new file mode 100644 index 0000000..d1d8791 --- /dev/null +++ b/v_windows/v/cmd/tools/vvet/tests/parens_space_b.out @@ -0,0 +1,2 @@ +cmd/tools/vvet/tests/parens_space_b.vv:1: error: Looks like you are adding a space before `)` +NB: You can run `v fmt -w file.v` to fix these errors automatically diff --git a/v_windows/v/cmd/tools/vvet/tests/parens_space_b.vv b/v_windows/v/cmd/tools/vvet/tests/parens_space_b.vv new file mode 100644 index 0000000..9ea8932 --- /dev/null +++ b/v_windows/v/cmd/tools/vvet/tests/parens_space_b.vv @@ -0,0 +1,4 @@ +fn main() { +	_ = 1 + (1 + 2 ) +} + diff --git a/v_windows/v/cmd/tools/vvet/tests/trailing_space.out b/v_windows/v/cmd/tools/vvet/tests/trailing_space.out new file mode 100644 index 0000000..1899a21 --- /dev/null +++ b/v_windows/v/cmd/tools/vvet/tests/trailing_space.out @@ -0,0 +1,7 @@ +cmd/tools/vvet/tests/trailing_space.vv:5: error: Looks like you have trailing whitespace. +cmd/tools/vvet/tests/trailing_space.vv:6: error: Looks like you have trailing whitespace. +cmd/tools/vvet/tests/trailing_space.vv:7: error: Looks like you have trailing whitespace. +cmd/tools/vvet/tests/trailing_space.vv:8: error: Looks like you have trailing whitespace. +cmd/tools/vvet/tests/trailing_space.vv:9: error: Looks like you have trailing whitespace. +cmd/tools/vvet/tests/trailing_space.vv:13: error: Looks like you have trailing whitespace. +cmd/tools/vvet/tests/trailing_space.vv:15: error: Looks like you have trailing whitespace. diff --git a/v_windows/v/cmd/tools/vvet/tests/trailing_space.vv b/v_windows/v/cmd/tools/vvet/tests/trailing_space.vv new file mode 100644 index 0000000..4fe733e --- /dev/null +++ b/v_windows/v/cmd/tools/vvet/tests/trailing_space.vv @@ -0,0 +1,16 @@ +// NB: This file has and *should* have trailing spaces. +// When making changes, please ensure they are not removed. + +fn after_comments() { +	// spaces after line comments give errors   +	/*   +	in block comments   +	too   +	*/   +} + +fn main() { +	var := 'error about the spaces right there'   +	no_err := "inside multi line strings it's fine.   +but not after"   +} diff --git a/v_windows/v/cmd/tools/vvet/vet_test.v b/v_windows/v/cmd/tools/vvet/vet_test.v new file mode 100644 index 0000000..f2c8523 --- /dev/null +++ b/v_windows/v/cmd/tools/vvet/vet_test.v @@ -0,0 +1,72 @@ +import os +import rand +import term +import v.util.vtest +import v.util.diff + +const diff_cmd = find_diff_cmd() + +fn find_diff_cmd() string { +	res := diff.find_working_diff_command() or { '' } +	return res +} + +fn test_vet() ? { +	vexe := os.getenv('VEXE') +	vroot := os.dir(vexe) +	os.chdir(vroot) ? +	test_dir := 'cmd/tools/vvet/tests' +	tests := get_tests_in_dir(test_dir) +	fails := check_path(vexe, test_dir, tests) +	assert fails == 0 +} + +fn get_tests_in_dir(dir string) []string { +	files := os.ls(dir) or { panic(err) } +	mut tests := files.filter(it.ends_with('.vv')) +	tests.sort() +	return tests +} + +fn check_path(vexe string, dir string, tests []string) int { +	mut nb_fail := 0 +	paths := vtest.filter_vtest_only(tests, basepath: dir) +	for path in paths { +		program := path +		print(path + ' ') +		// -force is needed so that `v vet` would not skip the regression files +		res := os.execute('$vexe vet -force -nocolor $program') +		if res.exit_code < 0 { +			panic(res.output) +		} +		mut expected := os.read_file(program.replace('.vv', '') + '.out') or { panic(err) } +		expected = clean_line_endings(expected) +		found := clean_line_endings(res.output) +		if expected != found { +			println(term.red('FAIL')) +			println('============') +			println('expected:') +			println(expected) +			println('============') +			println('found:') +			println(found) +			println('============\n') +			println('diff:') +			println(diff.color_compare_strings(diff_cmd, rand.ulid(), found, expected)) +			println('============\n') +			nb_fail++ +		} else { +			println(term.green('OK')) +		} +	} +	return nb_fail +} + +fn clean_line_endings(s string) string { +	mut res := s.trim_space() +	res = res.replace(' \n', '\n') +	res = res.replace(' \r\n', '\n') +	res = res.replace('\r\n', '\n') +	res = res.trim('\n') +	return res +} diff --git a/v_windows/v/cmd/tools/vvet/vvet.v b/v_windows/v/cmd/tools/vvet/vvet.v new file mode 100644 index 0000000..fd04b40 --- /dev/null +++ b/v_windows/v/cmd/tools/vvet/vvet.v @@ -0,0 +1,256 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license that can be found in the LICENSE file. +module main + +import os +import os.cmdline +import v.vet +import v.pref +import v.parser +import v.token +import v.ast +import term + +struct Vet { +	opt Options +mut: +	errors []vet.Error +	warns  []vet.Error +	file   string +} + +struct Options { +	is_force      bool +	is_werror     bool +	is_verbose    bool +	show_warnings bool +	use_color     bool +} + +const term_colors = term.can_show_color_on_stderr() + +fn main() { +	vet_options := cmdline.options_after(os.args, ['vet']) +	mut vt := Vet{ +		opt: Options{ +			is_force: '-force' in vet_options +			is_werror: '-W' in vet_options +			is_verbose: '-verbose' in vet_options || '-v' in vet_options +			show_warnings: '-hide-warnings' !in vet_options && '-w' !in vet_options +			use_color: '-color' in vet_options || (term_colors && '-nocolor' !in vet_options) +		} +	} +	mut paths := cmdline.only_non_options(vet_options) +	vtmp := os.getenv('VTMP') +	if vtmp != '' { +		// `v test-cleancode` passes also `-o tmpfolder` as well as all options in VFLAGS +		paths = paths.filter(!it.starts_with(vtmp)) +	} +	for path in paths { +		if !os.exists(path) { +			eprintln('File/folder $path does not exist') +			continue +		} +		if os.is_file(path) { +			vt.vet_file(path) +		} +		if os.is_dir(path) { +			vt.vprintln("vetting folder: '$path' ...") +			vfiles := os.walk_ext(path, '.v') +			vvfiles := os.walk_ext(path, '.vv') +			mut files := []string{} +			files << vfiles +			files << vvfiles +			for file in files { +				vt.vet_file(file) +			} +		} +	} +	vfmt_err_count := vt.errors.filter(it.fix == .vfmt).len +	if vt.opt.show_warnings { +		for w in vt.warns { +			eprintln(vt.e2string(w)) +		} +	} +	for err in vt.errors { +		eprintln(vt.e2string(err)) +	} +	if vfmt_err_count > 0 { +		eprintln('NB: You can run `v fmt -w file.v` to fix these errors automatically') +	} +	if vt.errors.len > 0 { +		exit(1) +	} +} + +// vet_file vets the file read from `path`. +fn (mut vt Vet) vet_file(path string) { +	if path.contains('/tests/') && !vt.opt.is_force { +		// skip all /tests/ files, since usually their content is not +		// important enough to be documented/vetted, and they may even +		// contain intentionally invalid code. +		vt.vprintln("skipping test file: '$path' ...") +		return +	} +	vt.file = path +	mut prefs := pref.new_preferences() +	prefs.is_vet = true +	prefs.is_vsh = path.ends_with('.vsh') +	table := ast.new_table() +	vt.vprintln("vetting file '$path'...") +	_, errors := parser.parse_vet_file(path, table, prefs) +	// Transfer errors from scanner and parser +	vt.errors << errors +	// Scan each line in file for things to improve +	source_lines := os.read_lines(vt.file) or { []string{} } +	for lnumber, line in source_lines { +		vt.vet_line(source_lines, line, lnumber) +	} +} + +// vet_line vets the contents of `line` from `vet.file`. +fn (mut vt Vet) vet_line(lines []string, line string, lnumber int) { +	// Vet public functions +	if line.starts_with('pub fn') || (line.starts_with('fn ') && !(line.starts_with('fn C.') +		|| line.starts_with('fn main'))) { +		// Scan function declarations for missing documentation +		is_pub_fn := line.starts_with('pub fn') +		if lnumber > 0 { +			collect_tags := fn (line string) []string { +				mut cleaned := line.all_before('/') +				cleaned = cleaned.replace_each(['[', '', ']', '', ' ', '']) +				return cleaned.split(',') +			} +			ident_fn_name := fn (line string) string { +				mut fn_idx := line.index(' fn ') or { return '' } +				if line.len < fn_idx + 5 { +					return '' +				} +				mut tokens := line[fn_idx + 4..].split(' ') +				// Skip struct identifier +				if tokens.first().starts_with('(') { +					fn_idx = line.index(')') or { return '' } +					tokens = line[fn_idx..].split(' ') +					if tokens.len > 1 { +						tokens = [tokens[1]] +					} +				} +				if tokens.len > 0 { +					return tokens[0].all_before('(') +				} +				return '' +			} +			mut line_above := lines[lnumber - 1] +			mut tags := []string{} +			if !line_above.starts_with('//') { +				mut grab := true +				for j := lnumber - 1; j >= 0; j-- { +					prev_line := lines[j] +					if prev_line.contains('}') { // We've looked back to the above scope, stop here +						break +					} else if prev_line.starts_with('[') { +						tags << collect_tags(prev_line) +						continue +					} else if prev_line.starts_with('//') { // Single-line comment +						grab = false +						break +					} +				} +				if grab { +					clean_line := line.all_before_last('{').trim(' ') +					if is_pub_fn { +						vt.warn('Function documentation seems to be missing for "$clean_line".', +							lnumber, .doc) +					} +				} +			} else { +				fn_name := ident_fn_name(line) +				mut grab := true +				for j := lnumber - 1; j >= 0; j-- { +					prev_line := lines[j] +					if prev_line.contains('}') { // We've looked back to the above scope, stop here +						break +					} else if prev_line.starts_with('// $fn_name ') { +						grab = false +						break +					} else if prev_line.starts_with('// $fn_name') { +						grab = false +						if is_pub_fn { +							clean_line := line.all_before_last('{').trim(' ') +							vt.warn('The documentation for "$clean_line" seems incomplete.', +								lnumber, .doc) +						} +						break +					} else if prev_line.starts_with('[') { +						tags << collect_tags(prev_line) +						continue +					} else if prev_line.starts_with('//') { // Single-line comment +						continue +					} +				} +				if grab { +					clean_line := line.all_before_last('{').trim(' ') +					if is_pub_fn { +						vt.warn('A function name is missing from the documentation of "$clean_line".', +							lnumber, .doc) +					} +				} +			} +		} +	} +} + +fn (vt &Vet) vprintln(s string) { +	if !vt.opt.is_verbose { +		return +	} +	println(s) +} + +fn (vt &Vet) e2string(err vet.Error) string { +	mut kind := '$err.kind:' +	mut location := '$err.file_path:$err.pos.line_nr:' +	if vt.opt.use_color { +		kind = match err.kind { +			.warning { term.magenta(kind) } +			.error { term.red(kind) } +		} +		kind = term.bold(kind) +		location = term.bold(location) +	} +	return '$location $kind $err.message' +} + +fn (mut vt Vet) error(msg string, line int, fix vet.FixKind) { +	pos := token.Position{ +		line_nr: line + 1 +	} +	vt.errors << vet.Error{ +		message: msg +		file_path: vt.file +		pos: pos +		kind: .error +		fix: fix +		typ: .default +	} +} + +fn (mut vt Vet) warn(msg string, line int, fix vet.FixKind) { +	pos := token.Position{ +		line_nr: line + 1 +	} +	mut w := vet.Error{ +		message: msg +		file_path: vt.file +		pos: pos +		kind: .warning +		fix: fix +		typ: .default +	} +	if vt.opt.is_werror { +		w.kind = .error +		vt.errors << w +	} else { +		vt.warns << w +	} +} | 
