v.parser, v.checker, v.gen: add support for [translated] tag (#13373)

pull/13380/head
div72 2022-02-06 01:16:02 +03:00 committed by GitHub
parent 054c8b1f13
commit cec7e91714
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 82 additions and 65 deletions

View File

@ -668,12 +668,13 @@ pub mut:
[heap]
pub struct File {
pub:
nr_lines int // number of source code lines in the file (including newlines and comments)
nr_bytes int // number of processed source code bytes
mod Module // the module of the source file (from `module xyz` at the top)
global_scope &Scope
is_test bool // true for _test.v files
is_generated bool // true for `[generated] module xyz` files; turn off notices
nr_lines int // number of source code lines in the file (including newlines and comments)
nr_bytes int // number of processed source code bytes
mod Module // the module of the source file (from `module xyz` at the top)
global_scope &Scope
is_test bool // true for _test.v files
is_generated bool // true for `[generated] module xyz` files; turn off notices
is_translated bool // true for `[translated] module xyz` files; turn off some checks
pub mut:
path string // absolute path of the source file - '/projects/v/file.v'
path_base string // file name - 'file.v' (useful for tracing)

View File

@ -177,7 +177,7 @@ pub fn (mut c Checker) assign_stmt(mut node ast.AssignStmt) {
}
if obj.is_stack_obj && !c.inside_unsafe {
type_sym := c.table.sym(obj.typ.set_nr_muls(0))
if !type_sym.is_heap() && !c.pref.translated {
if !type_sym.is_heap() && !c.pref.translated && !c.file.is_translated {
suggestion := if type_sym.kind == .struct_ {
'declaring `$type_sym.name` as `[heap]`'
} else {
@ -278,7 +278,7 @@ pub fn (mut c Checker) assign_stmt(mut node ast.AssignStmt) {
ast.PrefixExpr {
// Do now allow `*x = y` outside `unsafe`
if left.op == .mul {
if !c.inside_unsafe && !c.pref.translated {
if !c.inside_unsafe && !c.pref.translated && !c.file.is_translated {
c.error('modifying variables via dereferencing can only be done in `unsafe` blocks',
node.pos)
} else {
@ -325,7 +325,7 @@ pub fn (mut c Checker) assign_stmt(mut node ast.AssignStmt) {
// right type was a generic `T`
continue
}
if c.pref.translated {
if c.pref.translated || c.file.is_translated {
// TODO fix this in C2V instead, for example cast enums to int before using `|` on them.
// TODO replace all c.pref.translated checks with `$if !translated` for performance
continue

View File

@ -369,7 +369,7 @@ fn (mut c Checker) check_shift(mut node ast.InfixExpr, left_type ast.Type, right
ast.u64_type { 63 }
else { 64 }
}
if ival > moffset && !c.pref.translated {
if ival > moffset && !c.pref.translated && !c.file.is_translated {
c.error('shift count for type `$left_sym_final.name` too large (maximum: $moffset bits)',
node.right.pos())
return left_type

View File

@ -390,11 +390,13 @@ fn (mut c Checker) file_has_main_fn(file &ast.File) bool {
}
fn (mut c Checker) check_valid_snake_case(name string, identifier string, pos token.Pos) {
if !c.pref.is_vweb && !c.pref.translated && name.len > 0
&& (name[0] == `_` || name.contains('._')) {
if c.pref.translated || c.file.is_translated {
return
}
if !c.pref.is_vweb && name.len > 0 && (name[0] == `_` || name.contains('._')) {
c.error('$identifier `$name` cannot start with `_`', pos)
}
if !c.pref.experimental && !c.pref.translated && util.contains_capital(name) {
if !c.pref.experimental && util.contains_capital(name) {
c.error('$identifier `$name` cannot contain uppercase letters, use snake_case instead',
pos)
}
@ -407,7 +409,7 @@ fn stripped_name(name string) string {
fn (mut c Checker) check_valid_pascal_case(name string, identifier string, pos token.Pos) {
sname := stripped_name(name)
if sname.len > 0 && !sname[0].is_capital() && !c.pref.translated {
if sname.len > 0 && !sname[0].is_capital() && !c.pref.translated && !c.file.is_translated {
c.error('$identifier `$name` must begin with capital letter', pos)
}
}
@ -952,7 +954,7 @@ pub fn (mut c Checker) infix_expr(mut node ast.InfixExpr) ast.Type {
return ast.void_type
}
.and, .logical_or {
if !c.pref.translated {
if !c.pref.translated && !c.file.is_translated {
if node.left_type != ast.bool_type_idx {
c.error('left operand for `$node.op` is not a boolean', node.left.pos())
}
@ -987,7 +989,7 @@ pub fn (mut c Checker) infix_expr(mut node ast.InfixExpr) ast.Type {
c.error('only `==`, `!=`, `|` and `&` are defined on `[flag]` tagged `enum`, use an explicit cast to `int` if needed',
node.pos)
}
} else if !c.pref.translated {
} else if !c.pref.translated && !c.file.is_translated {
// Regular enums
c.error('only `==` and `!=` are defined on `enum`, use an explicit cast to `int` if needed',
node.pos)
@ -1008,7 +1010,8 @@ pub fn (mut c Checker) infix_expr(mut node ast.InfixExpr) ast.Type {
}
// Dual sides check (compatibility check)
if !(c.symmetric_check(left_type, right_type) && c.symmetric_check(right_type, left_type))
&& !c.pref.translated && !node.left.is_auto_deref_var() && !node.right.is_auto_deref_var() {
&& !c.pref.translated && !c.file.is_translated && !node.left.is_auto_deref_var()
&& !node.right.is_auto_deref_var() {
// for type-unresolved consts
if left_type == ast.void_type || right_type == ast.void_type {
return ast.void_type
@ -1047,7 +1050,7 @@ fn (mut c Checker) fail_if_immutable(expr ast.Expr) (string, token.Pos) {
ast.Ident {
if expr.obj is ast.Var {
mut v := expr.obj as ast.Var
if !v.is_mut && !c.pref.translated && !c.inside_unsafe {
if !v.is_mut && !c.pref.translated && !c.file.is_translated && !c.inside_unsafe {
c.error('`$expr.name` is immutable, declare it with `mut` to make it mutable',
expr.pos)
}
@ -1137,7 +1140,7 @@ fn (mut c Checker) fail_if_immutable(expr ast.Expr) (string, token.Pos) {
pos = expr.pos
}
} else {
if !field_info.is_mut && !c.pref.translated {
if !field_info.is_mut && !c.pref.translated && !c.file.is_translated {
type_str := c.table.type_to_str(expr.expr_type)
c.error('field `$expr.field_name` of struct `$type_str` is immutable',
expr.pos)
@ -1696,7 +1699,8 @@ pub fn (mut c Checker) enum_decl(mut node ast.EnumDecl) {
val := field.expr.val.i64()
if val < checker.int_min || val > checker.int_max {
c.error('enum value `$val` overflows int', field.expr.pos)
} else if !c.pref.translated && !node.is_multi_allowed && i64(val) in seen {
} else if !c.pref.translated && !c.file.is_translated && !node.is_multi_allowed
&& i64(val) in seen {
c.error('enum value `$val` already exists', field.expr.pos)
}
seen << i64(val)
@ -1728,7 +1732,8 @@ pub fn (mut c Checker) enum_decl(mut node ast.EnumDecl) {
last := seen[seen.len - 1]
if last == checker.int_max {
c.error('enum value overflows', field.pos)
} else if !c.pref.translated && !node.is_multi_allowed && last + 1 in seen {
} else if !c.pref.translated && !c.file.is_translated && !node.is_multi_allowed
&& last + 1 in seen {
c.error('enum value `${last + 1}` already exists', field.pos)
}
seen << last + 1
@ -2720,7 +2725,7 @@ pub fn (mut c Checker) cast_expr(mut node ast.CastExpr) ast.Type {
}
}
} else if to_type == ast.bool_type && from_type != ast.bool_type && !c.inside_unsafe
&& !c.pref.translated {
&& !c.pref.translated && !c.file.is_translated {
c.error('cannot cast to bool - use e.g. `some_int != 0` instead', node.pos)
} else if from_type == ast.none_type && !to_type.has_flag(.optional) {
type_name := c.table.type_to_str(to_type)
@ -3404,21 +3409,16 @@ pub fn (mut c Checker) mark_as_referenced(mut node ast.Expr, as_interface bool)
obj = c.fn_scope.find_var(node.obj.name) or { obj }
}
type_sym := c.table.sym(obj.typ.set_nr_muls(0))
if obj.is_stack_obj && !type_sym.is_heap() && !c.pref.translated {
if obj.is_stack_obj && !type_sym.is_heap() && !c.pref.translated
&& !c.file.is_translated {
suggestion := if type_sym.kind == .struct_ {
'declaring `$type_sym.name` as `[heap]`'
} else {
'wrapping the `$type_sym.name` object in a `struct` declared as `[heap]`'
}
if !c.pref.translated {
mischief := if as_interface {
'used as interface object'
} else {
'referenced'
}
c.error('`$node.name` cannot be $mischief outside `unsafe` blocks as it might be stored on stack. Consider ${suggestion}.',
node.pos)
}
mischief := if as_interface { 'used as interface object' } else { 'referenced' }
c.error('`$node.name` cannot be $mischief outside `unsafe` blocks as it might be stored on stack. Consider ${suggestion}.',
node.pos)
} else if type_sym.kind == .array_fixed {
c.error('cannot reference fixed array `$node.name` outside `unsafe` blocks as it is supposed to be stored on stack',
node.pos)
@ -3532,15 +3532,16 @@ pub fn (mut c Checker) prefix_expr(mut node ast.PrefixExpr) ast.Type {
if right_type.is_ptr() {
return right_type.deref()
}
if !right_type.is_pointer() && !c.pref.translated {
if !right_type.is_pointer() && !c.pref.translated && !c.file.is_translated {
s := c.table.type_to_str(right_type)
c.error('invalid indirect of `$s`', node.pos)
}
}
if node.op == .bit_not && !right_type.is_int() && !c.pref.translated {
if node.op == .bit_not && !right_type.is_int() && !c.pref.translated && !c.file.is_translated {
c.error('operator ~ only defined on int types', node.pos)
}
if node.op == .not && right_type != ast.bool_type_idx && !c.pref.translated {
if node.op == .not && right_type != ast.bool_type_idx && !c.pref.translated
&& !c.file.is_translated {
c.error('! operator can only be used with bool types', node.pos)
}
// FIXME
@ -3654,7 +3655,7 @@ pub fn (mut c Checker) index_expr(mut node ast.IndexExpr) ast.Type {
is_ok = v.is_mut && v.is_arg && !typ.deref().is_ptr()
}
}
if !is_ok && !c.pref.translated {
if !is_ok && !c.pref.translated && !c.file.is_translated {
c.warn('pointer indexing is only allowed in `unsafe` blocks', node.pos)
}
}
@ -3736,7 +3737,7 @@ pub fn (mut c Checker) enum_val(mut node ast.EnumVal) ast.Type {
}
}
mut typ := ast.new_type(typ_idx)
if c.pref.translated {
if c.pref.translated || c.file.is_translated {
// TODO make more strict
node.typ = typ
return typ
@ -3752,7 +3753,7 @@ pub fn (mut c Checker) enum_val(mut node ast.EnumVal) ast.Type {
typ_sym = c.table.sym(typ)
}
fsym := c.table.final_sym(typ)
if fsym.kind != .enum_ && !c.pref.translated {
if fsym.kind != .enum_ && !c.pref.translated && !c.file.is_translated {
// TODO in C int fields can be compared to enums, need to handle that in C2V
c.error('expected type is not an enum (`$typ_sym.name`)', node.pos)
return ast.void_type
@ -3839,7 +3840,7 @@ pub fn (mut c Checker) error(message string, pos token.Pos) {
print_backtrace()
exit(1)
}
if c.pref.translated && message.starts_with('mismatched types') {
if (c.pref.translated || c.file.is_translated) && message.starts_with('mismatched types') {
// TODO move this
return
}

View File

@ -207,7 +207,8 @@ fn (mut c Checker) fn_decl(mut node ast.FnDecl) {
}
}
}
if c.pref.translated && node.is_variadic && node.params.len == 1 && param.typ.is_ptr() {
if (c.pref.translated || c.file.is_translated) && node.is_variadic
&& node.params.len == 1 && param.typ.is_ptr() {
// TODO c2v hack to fix `(const char *s, ...)`
param.typ = ast.int_type.ref()
}
@ -679,7 +680,7 @@ pub fn (mut c Checker) fn_call(mut node ast.CallExpr, mut continue_check &bool)
}
node.is_keep_alive = func.is_keep_alive
if func.mod != 'builtin' && func.language == .v && func.no_body && !c.pref.translated
&& !func.is_unsafe {
&& !c.file.is_translated && !func.is_unsafe {
c.error('cannot call a function that does not have a body', node.pos)
}
for concrete_type in node.concrete_types {
@ -827,7 +828,8 @@ pub fn (mut c Checker) fn_call(mut node ast.CallExpr, mut continue_check &bool)
if call_arg.typ != param.typ
&& (param.typ == ast.voidptr_type || final_param_sym.idx == ast.voidptr_type_idx)
&& !call_arg.typ.is_any_kind_of_pointer() && func.language == .v
&& !call_arg.expr.is_lvalue() && func.name != 'json.encode' && !c.pref.translated {
&& !call_arg.expr.is_lvalue() && func.name != 'json.encode' && !c.pref.translated
&& !c.file.is_translated {
c.error('expression cannot be passed as `voidptr`', call_arg.expr.pos())
}
// Handle expected interface
@ -853,7 +855,7 @@ pub fn (mut c Checker) fn_call(mut node ast.CallExpr, mut continue_check &bool)
if param.typ.has_flag(.generic) {
continue
}
if c.pref.translated {
if c.pref.translated || c.file.is_translated {
// TODO duplicated logic in check_types() (check_types.v)
// Allow enums to be used as ints and vice versa in translated code
if param.typ == ast.int_type && typ_sym.kind == .enum_ {

View File

@ -144,7 +144,7 @@ fn (mut c Checker) for_stmt(mut node ast.ForStmt) {
prev_loop_label := c.loop_label
c.expected_type = ast.bool_type
typ := c.expr(node.cond)
if !node.is_inf && typ.idx() != ast.bool_type_idx && !c.pref.translated {
if !node.is_inf && typ.idx() != ast.bool_type_idx && !c.pref.translated && !c.file.is_translated {
c.error('non-bool used as for condition', node.pos)
}
if mut node.cond is ast.InfixExpr {

View File

@ -28,7 +28,7 @@ pub fn (mut c Checker) if_expr(mut node ast.IfExpr) ast.Type {
mut is_comptime_type_is_expr := false // if `$if T is string`
for i in 0 .. node.branches.len {
mut branch := node.branches[i]
if branch.cond is ast.ParExpr && !c.pref.translated {
if branch.cond is ast.ParExpr && !c.pref.translated && !c.file.is_translated {
c.error('unnecessary `()` in `$if_kind` condition, use `$if_kind expr {` instead of `$if_kind (expr) {`.',
branch.pos)
}
@ -41,7 +41,7 @@ pub fn (mut c Checker) if_expr(mut node ast.IfExpr) ast.Type {
c.expected_type = ast.bool_type
cond_typ := c.unwrap_generic(c.expr(branch.cond))
if (cond_typ.idx() != ast.bool_type_idx || cond_typ.has_flag(.optional))
&& !c.pref.translated {
&& !c.pref.translated && !c.file.is_translated {
c.error('non-bool type `${c.table.type_to_str(cond_typ)}` used as if condition',
branch.cond.pos())
}

View File

@ -8,7 +8,7 @@ import strings
pub fn (mut c Checker) match_expr(mut node ast.MatchExpr) ast.Type {
node.is_expr = c.expected_type != ast.void_type
node.expected_type = c.expected_type
if mut node.cond is ast.ParExpr && !c.pref.translated {
if mut node.cond is ast.ParExpr && !c.pref.translated && !c.file.is_translated {
c.error('unnecessary `()` in `match` condition, use `match expr {` instead of `match (expr) {`.',
node.cond.pos)
}
@ -333,7 +333,7 @@ fn (mut c Checker) match_exprs(mut node ast.MatchExpr, cond_type_sym ast.TypeSym
}
}
if is_exhaustive {
if has_else && !c.pref.translated {
if has_else && !c.pref.translated && !c.file.is_translated {
c.error('match expression is exhaustive, `else` is unnecessary', else_branch.pos)
}
return

View File

@ -113,7 +113,7 @@ pub fn (mut c Checker) return_stmt(mut node ast.Return) {
}
if (exp_type.is_ptr() || exp_type.is_pointer())
&& (!got_typ.is_ptr() && !got_typ.is_pointer()) && got_typ != ast.int_literal_type
&& !c.pref.translated {
&& !c.pref.translated && !c.file.is_translated {
pos := node.exprs[i].pos()
if node.exprs[i].is_auto_deref_var() {
continue
@ -131,7 +131,7 @@ pub fn (mut c Checker) return_stmt(mut node ast.Return) {
}
if obj.is_stack_obj && !c.inside_unsafe {
type_sym := c.table.sym(obj.typ.set_nr_muls(0))
if !type_sym.is_heap() && !c.pref.translated {
if !type_sym.is_heap() && !c.pref.translated && !c.file.is_translated {
suggestion := if type_sym.kind == .struct_ {
'declaring `$type_sym.name` as `[heap]`'
} else {

View File

@ -313,7 +313,7 @@ pub fn (mut c Checker) struct_init(mut node ast.StructInit) ast.Type {
}
if obj.is_stack_obj && !c.inside_unsafe {
sym := c.table.sym(obj.typ.set_nr_muls(0))
if !sym.is_heap() && !c.pref.translated {
if !sym.is_heap() && !c.pref.translated && !c.file.is_translated {
suggestion := if sym.kind == .struct_ {
'declaring `$sym.name` as `[heap]`'
} else {
@ -348,7 +348,7 @@ pub fn (mut c Checker) struct_init(mut node ast.StructInit) ast.Type {
continue
}
if field.typ.is_ptr() && !field.typ.has_flag(.shared_f) && !node.has_update_expr
&& !c.pref.translated {
&& !c.pref.translated && !c.file.is_translated {
c.error('reference field `${type_sym.name}.$field.name` must be initialized',
node.pos)
}

View File

@ -1,4 +1,4 @@
vlib/v/checker/tests/static_vars_in_translated_mode.vv:2:13: error: static variables are supported only in -translated mode or in [unsafe] fn
vlib/v/checker/tests/static_vars_in_translated_mode.vv:2:13: error: static variables are supported only in translated mode or in [unsafe] fn
1 | fn counter() int {
2 | mut static icounter := 0
| ~~~~~~~~

View File

@ -4474,7 +4474,7 @@ fn (mut g Gen) ident(node ast.Ident) {
}
}
} else if node_info is ast.IdentFn {
if g.pref.translated {
if g.pref.translated || g.file.is_translated {
// `p_mobjthinker` => `P_MobjThinker`
if f := g.table.find_fn(node.name) {
// TODO PERF fn lookup for each fn call in translated mode
@ -4511,7 +4511,7 @@ fn (mut g Gen) cast_expr(node ast.CastExpr) {
g.expr(node.expr)
} else {
styp := g.typ(node.typ)
if g.pref.translated && sym.kind == .function {
if (g.pref.translated || g.file.is_translated) && sym.kind == .function {
// TODO handle the type in fn casts, not just exprs
/*
info := sym.info as ast.FnType

View File

@ -436,7 +436,7 @@ fn (mut g Gen) c_fn_name(node &ast.FnDecl) ?string {
name = g.generic_fn_name(g.cur_concrete_types, name, true)
}
if g.pref.translated && node.attrs.contains('c') {
if (g.pref.translated || g.file.is_translated) && node.attrs.contains('c') {
// This fixes unknown symbols errors when building separate .c => .v files
// into .o files
//
@ -1176,7 +1176,7 @@ fn (mut g Gen) fn_call(node ast.CallExpr) {
} else {
name = c_name(name)
}
if g.pref.translated {
if g.pref.translated || g.file.is_translated {
// For `[c: 'P_TryMove'] fn p_trymove( ... `
// every time `p_trymove` is called, `P_TryMove` must be generated instead.
if f := g.table.find_fn(node.name) {
@ -1573,7 +1573,7 @@ fn (mut g Gen) call_args(node ast.CallExpr) {
}
}
elem_type := g.typ(arr_info.elem_type)
if g.pref.translated && args.len == 1 {
if (g.pref.translated || g.file.is_translated) && args.len == 1 {
// Handle `foo(c'str')` for `fn foo(args ...&u8)`
// TODOC2V handle this in a better place
// println(g.table.type_to_str(args[0].typ))

View File

@ -158,8 +158,9 @@ fn (mut p Parser) partial_assign_stmt(left []ast.Expr, left_comments []ast.Comme
iv := lx.info as ast.IdentVar
share = iv.share
if iv.is_static {
if !p.pref.translated && !p.pref.is_fmt && !p.inside_unsafe_fn {
return p.error_with_pos('static variables are supported only in -translated mode or in [unsafe] fn',
if !p.pref.translated && !p.is_translated && !p.pref.is_fmt
&& !p.inside_unsafe_fn {
return p.error_with_pos('static variables are supported only in translated mode or in [unsafe] fn',
lx.pos)
}
is_static = true

View File

@ -255,7 +255,8 @@ fn (mut p Parser) fn_decl() ast.FnDecl {
if p.tok.kind == .name {
// TODO high order fn
name = if language == .js { p.check_js_name() } else { p.check_name() }
if language == .v && !p.pref.translated && util.contains_capital(name) && !p.builtin_mod {
if language == .v && !p.pref.translated && !p.is_translated && util.contains_capital(name)
&& !p.builtin_mod {
p.error_with_pos('function names cannot contain uppercase letters, use snake_case instead',
name_pos)
return ast.FnDecl{

View File

@ -63,6 +63,7 @@ mut:
is_manualfree bool // true when `[manualfree] module abc`, makes *all* fns in the current .v file, opt out of autofree
has_globals bool // `[has_globals] module abc` - allow globals declarations, even without -enable-globals, in that single .v file __only__
is_generated bool // `[generated] module abc` - turn off compiler notices for that single .v file __only__.
is_translated bool // `[translated] module abc` - mark a file as translated, to relax some compiler checks for translated code.
attrs []ast.Attr // attributes before next decl stmt
expr_mod string // for constructing full type names in parse_type()
scope &ast.Scope
@ -324,6 +325,7 @@ pub fn (mut p Parser) parse() &ast.File {
path_base: p.file_base
is_test: p.inside_test_file
is_generated: p.is_generated
is_translated: p.is_translated
nr_lines: p.scanner.line_nr
nr_bytes: p.scanner.text.len
mod: module_decl
@ -1886,7 +1888,7 @@ fn (mut p Parser) parse_multi_expr(is_top_level bool) ast.Stmt {
// TODO remove translated
if p.tok.kind in [.assign, .decl_assign] || p.tok.kind.is_assign() {
return p.partial_assign_stmt(left, left_comments)
} else if !p.pref.translated && !p.pref.is_fmt
} else if !p.pref.translated && !p.is_translated && !p.pref.is_fmt
&& tok.kind !in [.key_if, .key_match, .key_lock, .key_rlock, .key_select] {
for node in left {
if (is_top_level || p.tok.kind != .rcbr) && node !is ast.CallExpr
@ -3046,6 +3048,9 @@ fn (mut p Parser) module_decl() ast.Module {
ma.pos)
}
}
'translated' {
p.is_translated = true
}
else {
p.error_with_pos('unknown module attribute `[$ma.name]`', ma.pos)
return mod_node
@ -3315,7 +3320,7 @@ fn (mut p Parser) global_decl() ast.GlobalDecl {
}
if !p.has_globals && !p.pref.enable_globals && !p.pref.is_fmt && !p.pref.translated
&& !p.pref.is_livemain && !p.pref.building_v && !p.builtin_mod {
&& !p.is_translated && !p.pref.is_livemain && !p.pref.building_v && !p.builtin_mod {
p.error('use `v -enable-globals ...` to enable globals')
return ast.GlobalDecl{}
}

View File

@ -53,8 +53,8 @@ fn (mut p Parser) struct_decl() ast.StructDecl {
p.error('`$p.tok.lit` lacks body')
return ast.StructDecl{}
}
if language == .v && !p.builtin_mod && name.len > 0 && !name[0].is_capital()
&& !p.pref.translated {
if language == .v && !p.builtin_mod && !p.is_translated && name.len > 0 && !name[0].is_capital()
&& !p.pref.translated && !p.is_translated {
p.error_with_pos('struct name `$name` must begin with capital letter', name_pos)
return ast.StructDecl{}
}

View File

@ -0,0 +1,6 @@
[translated]
module main
import math
fn test_NotSnakeCaseFunction() {}