tools: support a `v vet -p file.v` option that will warn about private functions with missing documentation
							parent
							
								
									45f2348a24
								
							
						
					
					
						commit
						9e502d7bf2
					
				|  | @ -20,11 +20,12 @@ mut: | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| struct Options { | struct Options { | ||||||
| 	is_force      bool | 	is_force            bool | ||||||
| 	is_werror     bool | 	is_werror           bool | ||||||
| 	is_verbose    bool | 	is_verbose          bool | ||||||
| 	show_warnings bool | 	show_warnings       bool | ||||||
| 	use_color     bool | 	use_color           bool | ||||||
|  | 	doc_private_fns_too bool | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| const term_colors = term.can_show_color_on_stderr() | const term_colors = term.can_show_color_on_stderr() | ||||||
|  | @ -38,6 +39,7 @@ fn main() { | ||||||
| 			is_verbose: '-verbose' in vet_options || '-v' in vet_options | 			is_verbose: '-verbose' in vet_options || '-v' in vet_options | ||||||
| 			show_warnings: '-hide-warnings' !in vet_options && '-w' !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) | 			use_color: '-color' in vet_options || (term_colors && '-nocolor' !in vet_options) | ||||||
|  | 			doc_private_fns_too: '-p' in vet_options | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	mut paths := cmdline.only_non_options(vet_options) | 	mut paths := cmdline.only_non_options(vet_options) | ||||||
|  | @ -110,92 +112,100 @@ fn (mut vt Vet) vet_file(path string) { | ||||||
| 
 | 
 | ||||||
| // vet_line vets the contents of `line` from `vet.file`.
 | // vet_line vets the contents of `line` from `vet.file`.
 | ||||||
| fn (mut vt Vet) vet_line(lines []string, line string, lnumber int) { | fn (mut vt Vet) vet_line(lines []string, line string, lnumber int) { | ||||||
| 	// Vet public functions
 | 	vt.vet_fn_documentation(lines, line, lnumber) | ||||||
| 	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
 | // vet_fn_documentation ensures that functions are documented
 | ||||||
| 		is_pub_fn := line.starts_with('pub fn') | fn (mut vt Vet) vet_fn_documentation(lines []string, line string, lnumber int) { | ||||||
| 		if lnumber > 0 { | 	if line.starts_with('fn C.') { | ||||||
| 			collect_tags := fn (line string) []string { | 		return | ||||||
| 				mut cleaned := line.all_before('/') | 	} | ||||||
| 				cleaned = cleaned.replace_each(['[', '', ']', '', ' ', '']) | 	is_pub_fn := line.starts_with('pub fn ') | ||||||
| 				return cleaned.split(',') | 	is_fn := is_pub_fn || line.starts_with('fn ') | ||||||
| 			} | 	if !is_fn { | ||||||
| 			ident_fn_name := fn (line string) string { | 		return | ||||||
| 				mut fn_idx := line.index(' fn ') or { return '' } | 	} | ||||||
| 				if line.len < fn_idx + 5 { | 	if line.starts_with('fn main') { | ||||||
| 					return '' | 		return | ||||||
| 				} | 	} | ||||||
| 				mut tokens := line[fn_idx + 4..].split(' ') | 	if !(is_pub_fn || vt.opt.doc_private_fns_too) { | ||||||
| 				// Skip struct identifier
 | 		return | ||||||
| 				if tokens.first().starts_with('(') { | 	} | ||||||
| 					fn_idx = line.index(')') or { return '' } | 	// Scan function declarations for missing documentation
 | ||||||
| 					tokens = line[fn_idx..].split(' ') | 	if lnumber > 0 { | ||||||
| 					if tokens.len > 1 { | 		collect_tags := fn (line string) []string { | ||||||
| 						tokens = [tokens[1]] | 			mut cleaned := line.all_before('/') | ||||||
| 					} | 			cleaned = cleaned.replace_each(['[', '', ']', '', ' ', '']) | ||||||
| 				} | 			return cleaned.split(',') | ||||||
| 				if tokens.len > 0 { | 		} | ||||||
| 					return tokens[0].all_before('(') | 		ident_fn_name := fn (line string) string { | ||||||
| 				} | 			mut fn_idx := line.index(' fn ') or { return '' } | ||||||
|  | 			if line.len < fn_idx + 5 { | ||||||
| 				return '' | 				return '' | ||||||
| 			} | 			} | ||||||
| 			mut line_above := lines[lnumber - 1] | 			mut tokens := line[fn_idx + 4..].split(' ') | ||||||
| 			mut tags := []string{} | 			// Skip struct identifier
 | ||||||
| 			if !line_above.starts_with('//') { | 			if tokens.first().starts_with('(') { | ||||||
| 				mut grab := true | 				fn_idx = line.index(')') or { return '' } | ||||||
| 				for j := lnumber - 1; j >= 0; j-- { | 				tokens = line[fn_idx..].split(' ') | ||||||
| 					prev_line := lines[j] | 				if tokens.len > 1 { | ||||||
| 					if prev_line.contains('}') { // We've looked back to the above scope, stop here
 | 					tokens = [tokens[1]] | ||||||
| 						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 { | 			} | ||||||
|  | 			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(' ') | ||||||
|  | 				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 | ||||||
| 					clean_line := line.all_before_last('{').trim(' ') | 					clean_line := line.all_before_last('{').trim(' ') | ||||||
| 					if is_pub_fn { | 					vt.warn('The documentation for "$clean_line" seems incomplete.', lnumber, | ||||||
| 						vt.warn('Function documentation seems to be missing for "$clean_line".', | 						.doc) | ||||||
| 							lnumber, .doc) | 					break | ||||||
| 					} | 				} else if prev_line.starts_with('[') { | ||||||
| 				} | 					tags << collect_tags(prev_line) | ||||||
| 			} else { | 					continue | ||||||
| 				fn_name := ident_fn_name(line) | 				} else if prev_line.starts_with('//') { // Single-line comment
 | ||||||
| 				mut grab := true | 					continue | ||||||
| 				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) |  | ||||||
| 					} |  | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
|  | 			if grab { | ||||||
|  | 				clean_line := line.all_before_last('{').trim(' ') | ||||||
|  | 				vt.warn('A function name is missing from the documentation of "$clean_line".', | ||||||
|  | 					lnumber, .doc) | ||||||
|  | 			} | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -14,5 +14,8 @@ Options: | ||||||
|   -v, -verbose |   -v, -verbose | ||||||
|      Enable verbose logging. |      Enable verbose logging. | ||||||
| 
 | 
 | ||||||
|  |   -p | ||||||
|  |      Report private functions with missing documentation too (by default, only the `pub fn` functions will be reported). | ||||||
|  | 
 | ||||||
|   -force |   -force | ||||||
|      (NB: vet development only!) Do not skip the vet regression tests. |      (NB: vet development only!) Do not skip the vet regression tests. | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue