From 6538b624e135684960987af58636a6a336166e67 Mon Sep 17 00:00:00 2001 From: Delyan Angelov Date: Wed, 13 Aug 2025 14:21:21 +0300 Subject: [PATCH] vdoc: implement -unsafe-run-examples and -check-examples support, add tests and document them --- cmd/tools/vdoc/main.v | 25 +++++++++++++-- cmd/tools/vdoc/run_examples.v | 14 +++++--- .../vdoc/testdata/run_examples_bad/main.v | 2 +- cmd/tools/vdoc/vdoc.v | 7 ++++ cmd/tools/vdoc/vdoc_run_examples_test.v | 32 ++++++++++++++++--- vlib/v/help/common/doc.txt | 5 +++ 6 files changed, 72 insertions(+), 13 deletions(-) diff --git a/cmd/tools/vdoc/main.v b/cmd/tools/vdoc/main.v index a801e646c4..ef7a46a61d 100644 --- a/cmd/tools/vdoc/main.v +++ b/cmd/tools/vdoc/main.v @@ -12,10 +12,17 @@ const vroot = os.dir(vexe) const allowed_formats = ['md', 'markdown', 'json', 'text', 'ansi', 'html', 'htm'] +enum RunExampleMode { + skip + run + check +} + struct Config { mut: pub_only bool = true show_loc bool // for plaintext + show_time bool // show the total time spend generating content is_color bool is_multi bool is_vlib bool @@ -31,7 +38,8 @@ mut: input_path string symbol_name string platform doc.Platform - run_examples bool // `-run-examples` will run all `// Example: assert mod.abc() == y` comments in the processed modules + run_examples RunExampleMode // `-unsafe-run-examples` will run all `// Example: assert mod.abc() == y` comments in the processed modules. + // -check-examples will only check/validate that they compile, but without running any code. // The options below are useful for generating a more stable HTML, that is easier to regression test: html_only_contents bool // `-html-only-contents` will produce only the content of any given page, without styling tags etc. html_no_vhash bool // `-html-no-vhash` will remove the version hash from the generated html @@ -61,7 +69,7 @@ fn main() { } vd.vprintln('Setting output type to "${cfg.output_type}"') vd.generate_docs_from_file() - if cfg.run_examples { + if cfg.run_examples != .skip { println('') if vd.example_oks == 0 && vd.example_failures == 0 { println(term.colorize(term.bright_yellow, 'Found NO examples.')) @@ -137,8 +145,19 @@ fn parse_arguments(args []string) Config { cfg.platform = selected_platform i++ } + '-time' { + cfg.show_time = true + } + '-check-examples' { + cfg.run_examples = .check + } + '-unsafe-run-examples' { + cfg.run_examples = .run + } '-run-examples' { - cfg.run_examples = true + eprintln('WARNING: the `-run-examples` option is deprecated, and will be removed after 2025-11-13.') + eprintln(' Use `-unsafe-run-examples` instead of `-run-examples` .') + cfg.run_examples = .run } '-no-timestamp' { cfg.no_timestamp = true diff --git a/cmd/tools/vdoc/run_examples.v b/cmd/tools/vdoc/run_examples.v index 838ea3aea2..ac7442ea14 100644 --- a/cmd/tools/vdoc/run_examples.v +++ b/cmd/tools/vdoc/run_examples.v @@ -32,9 +32,14 @@ fn get_mod_name_by_file_path(file_path string) string { } fn (mut vd VDoc) run_examples(dn doc.DocNode) { - if dn.comments.len == 0 || !vd.cfg.run_examples { + if dn.comments.len == 0 || vd.cfg.run_examples == .skip { return } + voptions := match vd.cfg.run_examples { + .run { ' -g run ' } + .check { '-N -W -check' } + .skip { '' } + } examples := dn.examples() if examples.len == 0 { return @@ -56,12 +61,13 @@ fn (mut vd VDoc) run_examples(dn doc.DocNode) { import_clause := if mod_name in ['builtin', ''] { '' } else { 'import ${mod_name}\n' } source := '${import_clause}fn main() {\n\t${code}\n}\n' os.write_file(vsource_path, source) or { continue } - cmd := '${os.quoted_path(vexe)} -g run ${os.quoted_path(vsource_path)}' + cmd := '${os.quoted_path(vexe)} ${voptions} ${os.quoted_path(vsource_path)}' res := os.execute(cmd) if res.exit_code != 0 { eprintln('${dn_to_location(dn)}:${term.ecolorize(term.red, 'error in documentation example')}') - eprintln('cmd: ${cmd}') - eprintln('result: ${res.output}') + eprintln(' cmd: ${cmd}') + eprintln(' result:') + eprintln(res.output) failures++ continue } diff --git a/cmd/tools/vdoc/testdata/run_examples_bad/main.v b/cmd/tools/vdoc/testdata/run_examples_bad/main.v index 57fecdf5d0..a064ff6100 100644 --- a/cmd/tools/vdoc/testdata/run_examples_bad/main.v +++ b/cmd/tools/vdoc/testdata/run_examples_bad/main.v @@ -1,5 +1,5 @@ // abc just prints 'xyz'. The important thing however is the next line, that does an assertion, -// that should FAIL to be executed with `v doc -run-examples good.v`: +// that should FAIL to be executed with `v doc -unsafe-run-examples main.v`, and should compile with -check-examples: // Example: assert 5 * 5 == 77 pub fn abc() { println('xyz') diff --git a/cmd/tools/vdoc/vdoc.v b/cmd/tools/vdoc/vdoc.v index 1c41c5095f..bc2f137f7f 100644 --- a/cmd/tools/vdoc/vdoc.v +++ b/cmd/tools/vdoc/vdoc.v @@ -277,6 +277,13 @@ fn (vd &VDoc) emit_generate_err(err IError) { } fn (mut vd VDoc) generate_docs_from_file() { + sw := time.new_stopwatch() + defer { + if vd.cfg.show_time { + println('Generation took: ${sw.elapsed().milliseconds()} ms.') + } + } + cfg := vd.cfg mut out := Output{ path: cfg.output_path diff --git a/cmd/tools/vdoc/vdoc_run_examples_test.v b/cmd/tools/vdoc/vdoc_run_examples_test.v index 4b034dee01..12fd1ff294 100644 --- a/cmd/tools/vdoc/vdoc_run_examples_test.v +++ b/cmd/tools/vdoc/vdoc_run_examples_test.v @@ -4,10 +4,34 @@ const vexe_path = @VEXE const vexe = os.quoted_path(vexe_path) const vroot = os.dir(vexe_path) -fn test_run_examples_good() { +fn testsuite_begin() { os.setenv('VCOLORS', 'never', true) os.chdir(vroot)! - cmd := '${vexe} doc -comments -run-examples cmd/tools/vdoc/testdata/run_examples_good/main.v' +} + +fn test_check_examples_good() { + cmd := '${vexe} doc -comments -check-examples cmd/tools/vdoc/testdata/run_examples_good/main.v' + println('${@METHOD:30} running ${cmd} ...') + res := os.execute(cmd) + assert res.exit_code == 0 + assert res.output.contains('module main'), res.output + assert res.output.contains('fn abc()'), res.output + assert res.output.contains("abc just prints 'xyz'"), res.output + assert res.output.contains('and should succeed'), res.output + assert res.output.contains('Example: assert 5 * 5 == 25'), res.output +} + +fn test_check_examples_bad() { + cmd := '${vexe} doc -comments -check-examples cmd/tools/vdoc/testdata/run_examples_bad/main.v' + println('${@METHOD:30} running ${cmd} ...') + res := os.execute(cmd) + assert res.exit_code == 0 + assert res.output.contains('module main'), res.output + assert res.output.contains('Example: assert 5 * 5 == 77'), res.output +} + +fn test_run_examples_good() { + cmd := '${vexe} doc -comments -unsafe-run-examples cmd/tools/vdoc/testdata/run_examples_good/main.v' println('${@METHOD:30} running ${cmd} ...') res := os.execute(cmd) assert res.exit_code == 0 @@ -19,9 +43,7 @@ fn test_run_examples_good() { } fn test_run_examples_bad() { - os.setenv('VCOLORS', 'never', true) - os.chdir(vroot)! - cmd := '${vexe} doc -comments -run-examples cmd/tools/vdoc/testdata/run_examples_bad/main.v' + cmd := '${vexe} doc -comments -unsafe-run-examples cmd/tools/vdoc/testdata/run_examples_bad/main.v' println('${@METHOD:30} running ${cmd} ...') res := os.execute(cmd) assert res.exit_code != 0 diff --git a/vlib/v/help/common/doc.txt b/vlib/v/help/common/doc.txt index bbdf91d613..0037f1ff27 100644 --- a/vlib/v/help/common/doc.txt +++ b/vlib/v/help/common/doc.txt @@ -29,6 +29,11 @@ Options: -v Enables verbose logging. For debugging purposes. -no-timestamp Omits the timestamp in the output file. + -check-examples Find and check that all example comments are compilable, + but * without * running them. + -unsafe-run-examples Find and run all `// Example: assert 1 + 1 == 2` lines + in the comments. + For HTML mode: -inline-assets Embeds the contents of the CSS and JS assets into the webpage directly.