From d24dce8eb33d60395e8dd8326bbfd0e81a0bcf7b Mon Sep 17 00:00:00 2001 From: StunxFS <56417208+StunxFS@users.noreply.github.com> Date: Mon, 9 May 2022 01:18:26 -0400 Subject: [PATCH] parser,checker: support `$compile_error('message')` and `$compile_warn('message')` (#14320) --- CHANGELOG.md | 1 + doc/docs.md | 31 +++++++++++++++++++-- vlib/v/checker/comptime.v | 7 +++++ vlib/v/checker/tests/compile_error.out | 14 ++++++++++ vlib/v/checker/tests/compile_error.vv | 15 ++++++++++ vlib/v/parser/comptime.v | 38 +++++++++----------------- 6 files changed, 78 insertions(+), 28 deletions(-) create mode 100644 vlib/v/checker/tests/compile_error.out create mode 100644 vlib/v/checker/tests/compile_error.vv diff --git a/CHANGELOG.md b/CHANGELOG.md index b75bbef459..8c8ad94b3e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ -*Not yet released, changelog is not full* - Introduce `isize` and `usize` types, deprecate `size_t` in favor of `usize`. - Add `datatypes` and `datatypes.fsm` modules. +- Add `compile_error` and `compile_warn` comptime functions. -## V 0.2.4 -*Not yet released, changelog is not full* diff --git a/doc/docs.md b/doc/docs.md index f3a60daf07..a9a1480737 100644 --- a/doc/docs.md +++ b/doc/docs.md @@ -5433,9 +5433,6 @@ numbers: [1, 2, 3] 3 ``` - - - #### `$env` ```v @@ -5451,6 +5448,34 @@ V can bring in values at compile time from environment variables. `$env('ENV_VAR')` can also be used in top-level `#flag` and `#include` statements: `#flag linux -I $env('JAVA_HOME')/include`. +#### `$compile_error` and `$compile_warn` + +These two comptime functions are very useful for displaying custom errors/warnings during +compile time. + +Both receive as their only argument a string literal that contains the message to display: + +```v failcompile nofmt +// x.v +module main + +$if linux { + $compile_error('Linux is not supported') +} + +fn main() { +} + +$ v run x.v +x.v:4:5: error: Linux is not supported + 2 | + 3 | $if linux { + 4 | $compile_error('Linux is not supported') + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + 5 | } + 6 | +``` + ### Environment specific files If a file has an environment-specific suffix, it will only be compiled for that environment. diff --git a/vlib/v/checker/comptime.v b/vlib/v/checker/comptime.v index 0175364894..7dfab0c4e5 100644 --- a/vlib/v/checker/comptime.v +++ b/vlib/v/checker/comptime.v @@ -10,6 +10,13 @@ import v.pkgconfig fn (mut c Checker) comptime_call(mut node ast.ComptimeCall) ast.Type { node.left_type = c.expr(node.left) + if node.method_name == 'compile_error' { + c.error(node.args_var, node.pos) + return ast.void_type + } else if node.method_name == 'compile_warn' { + c.warn(node.args_var, node.pos) + return ast.void_type + } if node.is_env { env_value := util.resolve_env_value("\$env('$node.args_var')", false) or { c.error(err.msg(), node.env_pos) diff --git a/vlib/v/checker/tests/compile_error.out b/vlib/v/checker/tests/compile_error.out new file mode 100644 index 0000000000..c2e7e82c3f --- /dev/null +++ b/vlib/v/checker/tests/compile_error.out @@ -0,0 +1,14 @@ +vlib/v/checker/tests/compile_error.vv:4:5: error: Only Serenity is supported + 2 | + 3 | $if !serenity { + 4 | $compile_error('Only Serenity is supported') + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + 5 | } + 6 | +vlib/v/checker/tests/compile_error.vv:8:5: error: On non Vinix this warning should be shown + 6 | + 7 | $if !vinix { + 8 | $compile_warn('On non Vinix this warning should be shown') + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + 9 | } + 10 | diff --git a/vlib/v/checker/tests/compile_error.vv b/vlib/v/checker/tests/compile_error.vv new file mode 100644 index 0000000000..c0786b6a78 --- /dev/null +++ b/vlib/v/checker/tests/compile_error.vv @@ -0,0 +1,15 @@ +module main + +$if !serenity { + $compile_error('Only Serenity is supported') +} + +$if !vinix { + $compile_warn('On non Vinix this warning should be shown') +} + +fn main() { + println('Hello from Serenity') +} + + diff --git a/vlib/v/parser/comptime.v b/vlib/v/parser/comptime.v index 76752823e8..54e1f2a8df 100644 --- a/vlib/v/parser/comptime.v +++ b/vlib/v/parser/comptime.v @@ -9,7 +9,8 @@ import v.pref import v.token const ( - supported_comptime_calls = ['html', 'tmpl', 'env', 'embed_file', 'pkgconfig'] + supported_comptime_calls = ['html', 'tmpl', 'env', 'embed_file', 'pkgconfig', 'compile_error', + 'compile_warn'] comptime_types = ['Map', 'Array', 'Int', 'Float', 'Struct', 'Interface', 'Enum', 'Sumtype'] ) @@ -85,9 +86,9 @@ fn (mut p Parser) comptime_call() ast.ComptimeCall { err_node := ast.ComptimeCall{ scope: 0 } + start_pos := p.tok.pos() p.check(.dollar) - start_pos := p.prev_tok.pos() - error_msg := 'only `\$tmpl()`, `\$env()`, `\$embed_file()`, `\$pkgconfig()` and `\$vweb.html()` comptime functions are supported right now' + error_msg := 'only `\$tmpl()`, `\$env()`, `\$embed_file()`, `\$pkgconfig()`, `\$vweb.html()`, `\$compile_error()` and `\$compile_warn()` comptime functions are supported right now' if p.peek_tok.kind == .dot { name := p.check_name() // skip `vweb.html()` TODO if name != 'vweb' { @@ -96,7 +97,7 @@ fn (mut p Parser) comptime_call() ast.ComptimeCall { } p.check(.dot) } - method_name := p.check_name() // (.name) + method_name := p.check_name() if method_name !in parser.supported_comptime_calls { p.error(error_msg) return err_node @@ -105,8 +106,7 @@ fn (mut p Parser) comptime_call() ast.ComptimeCall { is_html := method_name == 'html' // $env('ENV_VAR_NAME') p.check(.lpar) - spos := p.tok.pos() - if method_name == 'env' { + if method_name in ['env', 'pkgconfig', 'compile_error', 'compile_warn'] { s := p.tok.lit p.check(.string) p.check(.rpar) @@ -114,22 +114,10 @@ fn (mut p Parser) comptime_call() ast.ComptimeCall { scope: 0 method_name: method_name args_var: s - is_env: true - env_pos: spos - pos: spos.extend(p.prev_tok.pos()) - } - } - if method_name == 'pkgconfig' { - s := p.tok.lit - p.check(.string) - p.check(.rpar) - return ast.ComptimeCall{ - scope: 0 - method_name: method_name - args_var: s - is_pkgconfig: true - env_pos: spos - pos: spos.extend(p.prev_tok.pos()) + is_env: method_name == 'env' + is_pkgconfig: method_name == 'pkgconfig' + env_pos: start_pos + pos: start_pos.extend(p.prev_tok.pos()) } } literal_string_param := if is_html { '' } else { p.tok.lit } @@ -152,7 +140,7 @@ fn (mut p Parser) comptime_call() ast.ComptimeCall { // Validate that the epath exists, and that it is actually a file. if epath == '' { p.error_with_pos('supply a valid relative or absolute file path to the file to embed', - spos) + start_pos) return err_node } if !p.pref.is_fmt { @@ -163,12 +151,12 @@ fn (mut p Parser) comptime_call() ast.ComptimeCall { epath = os.real_path(os.join_path_single(os.dir(p.file_name), epath)) if !os.exists(epath) { p.error_with_pos('"$epath" does not exist so it cannot be embedded', - spos) + start_pos) return err_node } if !os.is_file(epath) { p.error_with_pos('"$epath" is not a file so it cannot be embedded', - spos) + start_pos) return err_node } } else {