diff options
Diffstat (limited to 'v_windows/v/cmd/tools/missdoc.v')
-rw-r--r-- | v_windows/v/cmd/tools/missdoc.v | 141 |
1 files changed, 141 insertions, 0 deletions
diff --git a/v_windows/v/cmd/tools/missdoc.v b/v_windows/v/cmd/tools/missdoc.v new file mode 100644 index 0000000..188fca1 --- /dev/null +++ b/v_windows/v/cmd/tools/missdoc.v @@ -0,0 +1,141 @@ +// Copyright (c) 2020 Lars Pontoppidan. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +import os +import flag + +const ( + tool_name = os.file_name(os.executable()) + tool_version = '0.0.2' + tool_description = 'Prints all V functions in .v files under PATH/, that do not yet have documentation comments.' +) + +struct UndocumentedFN { + line int + signature string + tags []string +} + +struct Options { + show_help bool + collect_tags bool + deprecated bool +} + +fn collect(path string, mut l []string, f fn (string, mut []string)) { + if !os.is_dir(path) { + return + } + mut files := os.ls(path) or { return } + for file in files { + p := path + os.path_separator + file + if os.is_dir(p) && !os.is_link(p) { + collect(p, mut l, f) + } else if os.exists(p) { + f(p, mut l) + } + } + return +} + +fn report_undocumented_functions_in_path(opt Options, path string) { + mut files := []string{} + collect_fn := fn (path string, mut l []string) { + if os.file_ext(path) == '.v' { + l << os.real_path(path) + } + } + collect(path, mut files, collect_fn) + for file in files { + if file.ends_with('_test.v') { + continue + } + report_undocumented_functions_in_file(opt, file) + } +} + +fn report_undocumented_functions_in_file(opt Options, file string) { + contents := os.read_file(file) or { panic(err) } + lines := contents.split('\n') + mut info := []UndocumentedFN{} + for i, line in lines { + if line.starts_with('pub fn') || (line.starts_with('fn ') && !(line.starts_with('fn C.') + || line.starts_with('fn main'))) { + // println('Match: $line') + if i > 0 && lines.len > 0 { + mut line_above := lines[i - 1] + if !line_above.starts_with('//') { + mut tags := []string{} + mut grab := true + for j := i - 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(' {') + info << UndocumentedFN{i + 1, clean_line, tags} + } + } + } + } + } + if info.len > 0 { + for undocumented_fn in info { + tags_str := if opt.collect_tags && undocumented_fn.tags.len > 0 { + '$undocumented_fn.tags' + } else { + '' + } + if opt.deprecated { + println('$file:$undocumented_fn.line:0:$undocumented_fn.signature $tags_str') + } else { + if 'deprecated' !in undocumented_fn.tags { + println('$file:$undocumented_fn.line:0:$undocumented_fn.signature $tags_str') + } + } + } + } +} + +fn collect_tags(line string) []string { + mut cleaned := line.all_before('/') + cleaned = cleaned.replace_each(['[', '', ']', '', ' ', '']) + return cleaned.split(',') +} + +fn main() { + if os.args.len == 1 { + println('Usage: $tool_name PATH \n$tool_description\n$tool_name -h for more help...') + exit(1) + } + mut fp := flag.new_flag_parser(os.args[1..]) + fp.application(tool_name) + fp.version(tool_version) + fp.description(tool_description) + fp.arguments_description('PATH [PATH]...') + // Collect tool options + opt := Options{ + show_help: fp.bool('help', `h`, false, 'Show this help text.') + deprecated: fp.bool('deprecated', `d`, false, 'Include deprecated functions in output.') + collect_tags: fp.bool('tags', `t`, false, 'Also print function tags if any is found.') + } + if opt.show_help { + println(fp.usage()) + exit(0) + } + for path in os.args[1..] { + if os.is_file(path) { + report_undocumented_functions_in_file(opt, path) + } else { + report_undocumented_functions_in_path(opt, path) + } + } +} |