parser,checker: support `$compile_error('message')` and `$compile_warn('message')` (#14320)

StunxFS 2022-05-09 01:18:26 -04:00 committed by Jef Roosens
parent bd7e53a777
commit 2cc8cb64aa
Signed by: Jef Roosens
GPG Key ID: B75D4F293C7052DB
6 changed files with 78 additions and 28 deletions

View File

@ -2,6 +2,7 @@
-*Not yet released, changelog is not full* -*Not yet released, changelog is not full*
- Introduce `isize` and `usize` types, deprecate `size_t` in favor of `usize`. - Introduce `isize` and `usize` types, deprecate `size_t` in favor of `usize`.
- Add `datatypes` and `datatypes.fsm` modules. - Add `datatypes` and `datatypes.fsm` modules.
- Add `compile_error` and `compile_warn` comptime functions.
-## V 0.2.4 -## V 0.2.4
-*Not yet released, changelog is not full* -*Not yet released, changelog is not full*

View File

@ -5433,9 +5433,6 @@ numbers: [1, 2, 3]
3 3
``` ```
#### `$env` #### `$env`
```v ```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: `$env('ENV_VAR')` can also be used in top-level `#flag` and `#include` statements:
`#flag linux -I $env('JAVA_HOME')/include`. `#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 ### Environment specific files
If a file has an environment-specific suffix, it will only be compiled for that environment. If a file has an environment-specific suffix, it will only be compiled for that environment.

View File

@ -10,6 +10,13 @@ import v.pkgconfig
fn (mut c Checker) comptime_call(mut node ast.ComptimeCall) ast.Type { fn (mut c Checker) comptime_call(mut node ast.ComptimeCall) ast.Type {
node.left_type = c.expr(node.left) 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 { if node.is_env {
env_value := util.resolve_env_value("\$env('$node.args_var')", false) or { env_value := util.resolve_env_value("\$env('$node.args_var')", false) or {
c.error(err.msg(), node.env_pos) c.error(err.msg(), node.env_pos)

View File

@ -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 |

View File

@ -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')
}

View File

@ -9,7 +9,8 @@ import v.pref
import v.token import v.token
const ( 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', comptime_types = ['Map', 'Array', 'Int', 'Float', 'Struct', 'Interface', 'Enum',
'Sumtype'] 'Sumtype']
) )
@ -85,9 +86,9 @@ fn (mut p Parser) comptime_call() ast.ComptimeCall {
err_node := ast.ComptimeCall{ err_node := ast.ComptimeCall{
scope: 0 scope: 0
} }
start_pos := p.tok.pos()
p.check(.dollar) p.check(.dollar)
start_pos := p.prev_tok.pos() error_msg := 'only `\$tmpl()`, `\$env()`, `\$embed_file()`, `\$pkgconfig()`, `\$vweb.html()`, `\$compile_error()` and `\$compile_warn()` comptime functions are supported right now'
error_msg := 'only `\$tmpl()`, `\$env()`, `\$embed_file()`, `\$pkgconfig()` and `\$vweb.html()` comptime functions are supported right now'
if p.peek_tok.kind == .dot { if p.peek_tok.kind == .dot {
name := p.check_name() // skip `vweb.html()` TODO name := p.check_name() // skip `vweb.html()` TODO
if name != 'vweb' { if name != 'vweb' {
@ -96,7 +97,7 @@ fn (mut p Parser) comptime_call() ast.ComptimeCall {
} }
p.check(.dot) p.check(.dot)
} }
method_name := p.check_name() // (.name) method_name := p.check_name()
if method_name !in parser.supported_comptime_calls { if method_name !in parser.supported_comptime_calls {
p.error(error_msg) p.error(error_msg)
return err_node return err_node
@ -105,8 +106,7 @@ fn (mut p Parser) comptime_call() ast.ComptimeCall {
is_html := method_name == 'html' is_html := method_name == 'html'
// $env('ENV_VAR_NAME') // $env('ENV_VAR_NAME')
p.check(.lpar) p.check(.lpar)
spos := p.tok.pos() if method_name in ['env', 'pkgconfig', 'compile_error', 'compile_warn'] {
if method_name == 'env' {
s := p.tok.lit s := p.tok.lit
p.check(.string) p.check(.string)
p.check(.rpar) p.check(.rpar)
@ -114,22 +114,10 @@ fn (mut p Parser) comptime_call() ast.ComptimeCall {
scope: 0 scope: 0
method_name: method_name method_name: method_name
args_var: s args_var: s
is_env: true is_env: method_name == 'env'
env_pos: spos is_pkgconfig: method_name == 'pkgconfig'
pos: spos.extend(p.prev_tok.pos()) env_pos: start_pos
} pos: start_pos.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())
} }
} }
literal_string_param := if is_html { '' } else { p.tok.lit } 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. // Validate that the epath exists, and that it is actually a file.
if epath == '' { if epath == '' {
p.error_with_pos('supply a valid relative or absolute file path to the file to embed', p.error_with_pos('supply a valid relative or absolute file path to the file to embed',
spos) start_pos)
return err_node return err_node
} }
if !p.pref.is_fmt { 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)) epath = os.real_path(os.join_path_single(os.dir(p.file_name), epath))
if !os.exists(epath) { if !os.exists(epath) {
p.error_with_pos('"$epath" does not exist so it cannot be embedded', p.error_with_pos('"$epath" does not exist so it cannot be embedded',
spos) start_pos)
return err_node return err_node
} }
if !os.is_file(epath) { if !os.is_file(epath) {
p.error_with_pos('"$epath" is not a file so it cannot be embedded', p.error_with_pos('"$epath" is not a file so it cannot be embedded',
spos) start_pos)
return err_node return err_node
} }
} else { } else {