Compare commits

...

40 Commits

Author SHA1 Message Date
WoodyAtHome 12ec3b9d53
builtin.string: new fn (s string) is_ascii() bool (#14418)
ci/woodpecker/push/vc Pipeline was successful Details
ci/woodpecker/push/docker Pipeline was successful Details
ci/woodpecker/push/arch Pipeline was successful Details
2022-05-17 10:03:13 +02:00
Alexander Medvednikov d118af0dac
cgen: fix c2v struct eq 2022-05-17 10:03:13 +02:00
playX 15b84567ec
checker: c2v array fix (#14426) 2022-05-17 10:03:13 +02:00
Delyan Angelov ec5559a379
checker: apply fix for ui suggested by spaceface 2022-05-17 10:03:13 +02:00
Delyan Angelov 972c1855bc
Revert "Revert "checker: allow using methods as vars when expecting a ctx arg (#14414)""
This reverts commit e5c7fe3006.
2022-05-17 10:03:13 +02:00
yuyi 57055a9c63
cgen: fix struct field array index error (#14417) 2022-05-17 10:03:13 +02:00
Delyan Angelov 1064085656
Revert "checker: allow using methods as vars when expecting a ctx arg (#14414)"
This reverts commit 36bec823c2.
2022-05-17 10:03:13 +02:00
Delyan Angelov e814497831
clipboard: fix `v -gc boehm run x.v`, where x.v does c.paste() (fix #14281) 2022-05-17 10:03:12 +02:00
yuyi cdfdf19697
ast, cgen: fix generic method with variadic generic argument (#14404) 2022-05-17 10:03:12 +02:00
Larpon f1b35aff22
vvet: fix false positive, add test (#14403) 2022-05-17 10:03:12 +02:00
yuyi 9eba367a46
cgen: minor cleanup in struct_init() (#14405) 2022-05-17 10:03:12 +02:00
spaceface 4179e1cac2
checker: allow using methods as vars when expecting a ctx arg (#14414) 2022-05-17 10:03:12 +02:00
WoodyAtHome 9e179ab11f
net.smtp: handle UTF-8 subjects according to RFC 1342 (#14410) 2022-05-17 10:03:12 +02:00
Delyan Angelov ff02e5667b
checker: add a suggestion for misspelled mod.const_name + a test 2022-05-17 10:03:12 +02:00
Delyan Angelov baf72a7459
tests: simplify cmd/tools/modules/testing/common.v 2022-05-17 10:03:12 +02:00
Ben 6a87650935
os: fix is_abs_path function for Windows systems (#14397) 2022-05-17 10:03:12 +02:00
yuyi 689f3f7128
fmt: fix fmt error of anon fn with if expr (fix #14393) (#14413) 2022-05-17 10:03:12 +02:00
Larpon cf2f34cf3f
ftp: document all public methods (#14408) 2022-05-17 10:03:12 +02:00
Jah-On cd43bae0b4
clipboard: add `[heap]` tag to the Clipboard structs (#14412) 2022-05-17 10:03:12 +02:00
WoodyAtHome 8ce7860e8a
net.openssl: read doesn't block infinitely (#14406) 2022-05-17 10:03:11 +02:00
spaceface ec2346d45c
checker,gen: allow using methods as function pointers (#14407) 2022-05-17 10:03:11 +02:00
Delyan Angelov a133f038bd
sync: only run channel_select_test.v when VTEST_RUN_FLAKY=1 2022-05-17 10:03:11 +02:00
crthpl 82018034ef
checker,cgen: fix if expressions in lock expression (#14384) 2022-05-17 10:03:11 +02:00
David 'Epper' Marshall fd17b62ea6
time: fix calculate_date_from_offset (#14399) 2022-05-17 10:03:11 +02:00
Delyan Angelov 771ec47a04
ci: simplify ci_sanitized.yml again; use `v.exe fmt -verify vlib/builtin` for windows/msvc instead of `v test-cleancode` 2022-05-17 10:03:11 +02:00
Delyan Angelov 3f2bb98152
ci: add a skip list in compiler_test.v (for tmpl_parse_html.vv) 2022-05-17 10:03:11 +02:00
Delyan Angelov 4f1f764485
ci: restore .github/workflows/ to its state at cee7856, when all checks were done 2022-05-17 10:03:11 +02:00
Larpon 7492907e77
examples: fix 2048 scaling on Android (#14380) 2022-05-17 10:03:11 +02:00
David 'Epper' Marshall 153b095518
math: cbrt fix (#14395) 2022-05-17 10:03:11 +02:00
Delyan Angelov 46ee0ff572
ci: further cleanup of ci_sanitized.yml 2022-05-17 10:03:11 +02:00
Delyan Angelov 1f6849e89c
ci: simplify ci_sanitized.yml 2022-05-17 10:03:10 +02:00
yuyi f983dd8a95
cgen: fix appending struct to interface array (#14388) 2022-05-17 10:03:10 +02:00
Alexander Medvednikov 5a427907d5
tmpl: fix a test 2022-05-17 10:03:10 +02:00
Alexander Medvednikov 58c54a5c05
vweb: simplify @foo by removing V_TEMPLATE rule 2022-05-17 10:03:10 +02:00
Daniel Däschle c6b0ebe06f
ci: require code to be formatted before everything else (minimise CI queue length) (#14396) 2022-05-17 10:03:10 +02:00
JalonSolov b2f520028f
examples: add missing v.mod file (#14392) 2022-05-17 10:03:10 +02:00
j. redhead 9abaadb690
checker: fix optionals in infix expression check (fix #14354) (#14390) 2022-05-17 10:03:10 +02:00
Delyan Angelov 638b267e4e
ci: add a quick `v test-cleancode` check, before more costlier tasks 2022-05-17 10:03:10 +02:00
Delyan Angelov b74461f255
ci: fix .out regression after d407a64 2022-05-17 10:03:10 +02:00
Delyan Angelov d1b8a67bca
ci: extract v_apps_and_modules_compile.yml 2022-05-17 10:03:10 +02:00
75 changed files with 1029 additions and 353 deletions

View File

@ -33,6 +33,7 @@ jobs:
run: |
echo $VFLAGS
make
./v test-cleancode
./v -d debug_malloc -d debug_realloc -o v cmd/v
./v -cg -cstrict -o v cmd/v
# Test v -realloc arena allocation
@ -609,40 +610,40 @@ jobs:
## run: .\v.exe -o v2.exe cmd/v && .\v2.exe -o v3.exe cmd/v
# ubuntu-autofree-selfcompile:
# runs-on: ubuntu-20.04
# timeout-minutes: 121
# env:
# VFLAGS: -cc gcc
# steps:
# - uses: actions/checkout@v2
# - name: Build V
# run: make -j4
# - name: V self compilation with -autofree
# run: ./v -o v2 -autofree cmd/v && ./v2 -o v3 -autofree cmd/v && ./v3 -o v4 -autofree cmd/v
# ubuntu-autofree-selfcompile:
# runs-on: ubuntu-20.04
# timeout-minutes: 121
# env:
# VFLAGS: -cc gcc
# steps:
# - uses: actions/checkout@v2
# - name: Build V
# run: make -j4
# - name: V self compilation with -autofree
# run: ./v -o v2 -autofree cmd/v && ./v2 -o v3 -autofree cmd/v && ./v3 -o v4 -autofree cmd/v
# ubuntu-musl:
# runs-on: ubuntu-20.04
# timeout-minutes: 121
# env:
# VFLAGS: -cc musl-gcc
# V_CI_MUSL: 1
# steps:
# - uses: actions/checkout@v2
# - uses: actions/setup-node@v1
# with:
# node-version: 12.x
# - name: Install dependencies
# run: |
# sudo apt-get install --quiet -y musl musl-tools libssl-dev sqlite3 libsqlite3-dev valgrind
# - name: Build v
# run: echo $VFLAGS && make -j4 && ./v -cg -o v cmd/v
# # - name: Test v binaries
# # run: ./v build-vbinaries
# ## - name: Test v->js
# ## run: ./v -o hi.js examples/hello_v_js.v && node hi.js
# - name: quick debug
# run: ./v -stats vlib/strconv/format_test.v
# - name: Self tests
# run: ./v test-self
# ubuntu-musl:
# runs-on: ubuntu-20.04
# timeout-minutes: 121
# env:
# VFLAGS: -cc musl-gcc
# V_CI_MUSL: 1
# steps:
# - uses: actions/checkout@v2
# - uses: actions/setup-node@v1
# with:
# node-version: 12.x
# - name: Install dependencies
# run: |
# sudo apt-get install --quiet -y musl musl-tools libssl-dev sqlite3 libsqlite3-dev valgrind
# - name: Build v
# run: echo $VFLAGS && make -j4 && ./v -cg -o v cmd/v
# # - name: Test v binaries
# # run: ./v build-vbinaries
# ## - name: Test v->js
# ## run: ./v -o hi.js examples/hello_v_js.v && node hi.js
# - name: quick debug
# run: ./v -stats vlib/strconv/format_test.v
# - name: Self tests
# run: ./v test-self

View File

@ -16,7 +16,7 @@ on:
paths:
- '!**'
- 'cmd/tools/vtest*'
- 'cmd/tools/builders/**.v'
- 'cmd/tools/builders/**.v'
- 'vlib/builtin/**.v'
- 'vlib/strconv/**.v'
- 'vlib/strings/**.v'
@ -43,7 +43,7 @@ on:
paths:
- '!**'
- 'cmd/tools/vtest*'
- 'cmd/tools/builders/**.v'
- 'cmd/tools/builders/**.v'
- 'vlib/builtin/**.v'
- 'vlib/strconv/**.v'
- 'vlib/strings/**.v'
@ -91,7 +91,9 @@ jobs:
sudo apt-get install --quiet -y libfreetype6-dev libxi-dev libxcursor-dev libgl-dev
sudo apt-get install clang
- name: Build V
run: make -j4 && ./v -cg -cstrict -o v cmd/v
run: make && ./v -cg -cstrict -o v cmd/v
- name: Ensure code is well formatted
run: ./v test-cleancode
- name: Self tests (-fsanitize=undefined)
run: ./v -cflags "-fsanitize=undefined" -o v2 cmd/v && ./v2 -cflags -fsanitize=undefined test-self
- name: Build examples (V compiled with -fsanitize=undefined)
@ -115,7 +117,9 @@ jobs:
sudo apt-get install --quiet -y postgresql libpq-dev libssl-dev sqlite3 libsqlite3-dev valgrind
sudo apt-get install --quiet -y libfreetype6-dev libxi-dev libxcursor-dev libgl-dev
- name: Build V
run: make -j4 && ./v -cg -cstrict -o v cmd/v
run: make && ./v -cg -cstrict -o v cmd/v
- name: Ensure code is well formatted
run: ./v test-cleancode
- name: Self tests (-fsanitize=undefined)
run: ./v -cflags "-fsanitize=undefined" -o v2 cmd/v && ./v2 -cflags -fsanitize=undefined test-self
- name: Build examples (V compiled with -fsanitize=undefined)
@ -140,7 +144,9 @@ jobs:
sudo apt-get install --quiet -y libfreetype6-dev libxi-dev libxcursor-dev libgl-dev
sudo apt-get install clang
- name: Build V
run: make -j4 && ./v -cg -cstrict -o v cmd/v
run: make && ./v -cg -cstrict -o v cmd/v
- name: Ensure code is well formatted
run: ./v test-cleancode
- name: Self tests (-fsanitize=address)
run: ASAN_OPTIONS=detect_leaks=0 ./v -cflags "-fsanitize=address,pointer-compare,pointer-subtract" test-self
- name: Self tests (V compiled with -fsanitize=address)
@ -168,6 +174,11 @@ jobs:
echo $VFLAGS
.\make.bat -msvc
.\v.exe self
- name: Ensure code is well formatted
run: |
.\v.exe fmt -verify vlib/builtin/ vlib/v/scanner/ vlib/v/parser/ vlib/v/gen/
## TODO: check to see why `v test-cleancode` does not work with msvc on windows
## - name: Install dependencies
## run: |
## .\v.exe setup-freetype
@ -195,7 +206,9 @@ jobs:
sudo apt-get install --quiet -y libfreetype6-dev libxi-dev libxcursor-dev libgl-dev
sudo apt-get install clang
- name: Build V
run: make -j4 && ./v -cg -cstrict -o v cmd/v
run: make && ./v -cg -cstrict -o v cmd/v
- name: Ensure code is well formatted
run: ./v test-cleancode
- name: Self tests (-fsanitize=address)
run: ASAN_OPTIONS=detect_leaks=0 ./v -cflags -fsanitize=address test-self
- name: Self tests (V compiled with -fsanitize=address)
@ -224,11 +237,14 @@ jobs:
sudo apt-get install --quiet -y libfreetype6-dev libxi-dev libxcursor-dev libgl-dev
sudo apt-get install clang
- name: Build V
run: make -j4 && ./v -cc clang -cg -cstrict -o v cmd/v
run: make && ./v -cc clang -cg -cstrict -o v cmd/v
- name: Ensure code is well formatted
run: ./v test-cleancode
- name: Self tests (-fsanitize=memory)
run: ./v -cflags -fsanitize=memory test-self
- name: Self tests (V compiled with -fsanitize=memory)
run:
./v -cflags -fsanitize=memory -o v cmd/v && ./v -cc tcc test-self -msan-compiler
run: |
./v -cflags -fsanitize=memory -o v cmd/v
./v -cc tcc test-self -msan-compiler
- name: Build examples (V compiled with -fsanitize=memory)
run: ./v build-examples

View File

@ -85,6 +85,7 @@ jobs:
- name: g++ version
run: g++-9 --version
- name: V self compilation with g++
continue-on-error: true
run: ./v -cc g++-9 -no-std -cflags -std=c++11 -o v2 cmd/v && ./v2 -cc g++-9 -no-std -cflags -std=c++11 -o v3 cmd/v
## - name: Running tests with g++
## run: ./v -cc g++-9 test-self
@ -93,6 +94,7 @@ jobs:
run: ./v -autofree -o v2 cmd/v ## NB: this does not mean it runs, but at least keeps it from regressing
- name: Shader examples can be build
continue-on-error: true
run: |
wget https://github.com/floooh/sokol-tools-bin/raw/33d2e4cc26088c6c28eaef5467990f8940d15aab/bin/linux/sokol-shdc
chmod +x ./sokol-shdc
@ -146,117 +148,3 @@ jobs:
./v test-parser -S examples/regex_example_fuzz.v
./v test-parser -S examples/2048/2048_fuzz.v
v-apps-compile:
runs-on: ubuntu-20.04
timeout-minutes: 121
steps:
- uses: actions/checkout@v2
- name: Build V
run: make && sudo ./v symlink
- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install --quiet -y libgc-dev
sudo apt-get install --quiet -y libsodium-dev libssl-dev sqlite3 libsqlite3-dev valgrind
sudo apt-get install --quiet -y libfreetype6-dev libxi-dev libxcursor-dev libgl-dev
sudo apt-get install --quiet -y xfonts-75dpi xfonts-base
## vls
- name: Clone VLS
run: git clone --depth 1 https://github.com/vlang/vls
- name: Build VLS
run: pushd vls; v cmd/vls ; popd
- name: Build VLS with -prod
run: pushd vls; v -prod cmd/vls; popd
- name: Build VLS with -gc boehm -skip-unused
run: pushd vls; v -gc boehm -skip-unused cmd/vls; popd
## vsl
- name: Clone VSL
run: git clone --depth 1 https://github.com/vlang/vsl ~/.vmodules/vsl
- name: Install dependencies
run: sudo apt-get install --quiet -y --no-install-recommends gfortran liblapacke-dev libopenblas-dev libgc-dev
- name: Execute Tests using Pure V Backend
run: ~/.vmodules/vsl/bin/test
- name: Execute Tests using Pure V Backend with Pure V Math
run: ~/.vmodules/vsl/bin/test --use-cblas
- name: Execute Tests using Pure V Backend and Garbage Collection enabled
run: ~/.vmodules/vsl/bin/test --use-gc boehm
- name: Execute Tests using Pure V Backend with Pure V Math and Garbage Collection enabled
run: ~/.vmodules/vsl/bin/test --use-cblas --use-gc boehm
## vtl
- name: Clone VTL
run: git clone --depth 1 https://github.com/vlang/vtl ~/.vmodules/vtl
- name: Install dependencies
run: sudo apt-get install --quiet -y --no-install-recommends gfortran liblapacke-dev libopenblas-dev libgc-dev
- name: Execute Tests using Pure V Backend
run: ~/.vmodules/vtl/bin/test
- name: Execute Tests using Pure V Backend with Pure V Math
run: ~/.vmodules/vtl/bin/test --use-cblas
- name: Execute Tests using Pure V Backend and Garbage Collection enabled
run: ~/.vmodules/vtl/bin/test --use-gc boehm
- name: Execute Tests using Pure V Backend with Pure V Math and Garbage Collection enabled
run: ~/.vmodules/vtl/bin/test --use-cblas --use-gc boehm
## vab
- name: Clone vab
run: git clone --depth 1 https://github.com/vlang/vab
- name: Build vab
run: cd vab; ../v ./vab.v ; cd ..
- name: Build vab with -gc boehm -skip-unused
run: cd vab; ../v -gc boehm -skip-unused ./vab.v ; cd ..
## gitly
- name: Install markdown
run: git clone https://github.com/vlang/markdown ~/.vmodules/markdown
- name: Build Gitly
run: |
git clone --depth 1 https://github.com/vlang/gitly
cd gitly
../v .
# ./gitly -ci_run
../v -autofree .
../v -o x tests/first_run.v
./x
cd ..
## libsodium
- name: Install libsodium-dev package
run: sudo apt-get install --quiet -y libsodium-dev
- name: Install the libsodium wrapper
run: git clone https://github.com/vlang/libsodium ~/.vmodules/libsodium
- name: Test libsodium
run: VJOBS=1 ./v -stats test ~/.vmodules/libsodium
## vex
- name: Install Vex dependencies
run: sudo apt-get install --quiet -y libsodium-dev libssl-dev sqlite3 libsqlite3-dev
- name: Install Vex
run: mkdir -p ~/.vmodules/nedpals; git clone https://github.com/nedpals/vex ~/.vmodules/nedpals/vex
- name: Compile all of the Vex examples
run: ./v should-compile-all ~/.vmodules/nedpals/vex/examples
- name: Compile the simple Vex example with -gc boehm -skip-unused
run: ./v -gc boehm -skip-unused ~/.vmodules/nedpals/vex/examples/simple_example.v
- name: Run Vex Tests
run: ./v test ~/.vmodules/nedpals/vex
## Go2V
- name: Clone & Build go2v
run: git clone --depth=1 https://github.com/vlang/go2v go2v/
- name: Build go2v
run: ./v go2v/
## - name: Run tests for go2v
## run: VJOBS=1 ./v -stats test go2v/
## vlang/pdf
- name: Clone & Build vlang/pdf
run: git clone --depth=1 https://github.com/vlang/pdf ~/.vmodules/pdf/
- name: PDF examples should compile
run: ./v should-compile-all ~/.vmodules/pdf/examples
## vpm modules
- name: Install UI through VPM
run: ./v install ui

View File

@ -0,0 +1,145 @@
name: V Apps and Modules
on:
push:
paths-ignore:
- "**.md"
pull_request:
paths-ignore:
- "**.md"
concurrency:
group: build-other-${{ github.event.pull_request.number || github.sha }}
cancel-in-progress: true
jobs:
v-apps-compile:
runs-on: ubuntu-20.04
timeout-minutes: 121
steps:
- uses: actions/checkout@v2
- name: Build V
run: make && sudo ./v symlink
- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install --quiet -y libgc-dev
sudo apt-get install --quiet -y libsodium-dev libssl-dev sqlite3 libsqlite3-dev valgrind
sudo apt-get install --quiet -y libfreetype6-dev libxi-dev libxcursor-dev libgl-dev
sudo apt-get install --quiet -y xfonts-75dpi xfonts-base
- name: Install UI through VPM
continue-on-error: true
run: |
echo "Official VPM modules should be installable"
./v install ui
- name: Build V Language Server (VLS)
continue-on-error: true
run: |
echo "Clone VLS"
git clone --depth 1 https://github.com/vlang/vls
echo "Build VLS"
pushd vls; v cmd/vls ; popd
echo "Build VLS with -prod"
pushd vls; v -prod cmd/vls; popd
echo "Build VLS with -gc boehm -skip-unused"
pushd vls; v -gc boehm -skip-unused cmd/vls; popd
- name: Build VSL
continue-on-error: true
run: |
git clone --depth 1 https://github.com/vlang/vsl ~/.vmodules/vsl
sudo apt-get install --quiet -y --no-install-recommends gfortran liblapacke-dev libopenblas-dev libgc-dev
echo "Execute Tests using Pure V Backend"
~/.vmodules/vsl/bin/test
echo "Execute Tests using Pure V Backend with Pure V Math"
~/.vmodules/vsl/bin/test --use-cblas
echo "Execute Tests using Pure V Backend and Garbage Collection enabled"
~/.vmodules/vsl/bin/test --use-gc boehm
echo "Execute Tests using Pure V Backend with Pure V Math and Garbage Collection enabled"
~/.vmodules/vsl/bin/test --use-cblas --use-gc boehm
- name: Build VTL
continue-on-error: true
run: |
echo "Clone VTL"
git clone --depth 1 https://github.com/vlang/vtl ~/.vmodules/vtl
echo "Install dependencies"
sudo apt-get install --quiet -y --no-install-recommends gfortran liblapacke-dev libopenblas-dev libgc-dev
echo "Execute Tests using Pure V Backend"
~/.vmodules/vtl/bin/test
echo "Execute Tests using Pure V Backend with Pure V Math"
~/.vmodules/vtl/bin/test --use-cblas
echo "Execute Tests using Pure V Backend and Garbage Collection enabled"
~/.vmodules/vtl/bin/test --use-gc boehm
echo "Execute Tests using Pure V Backend with Pure V Math and Garbage Collection enabled"
~/.vmodules/vtl/bin/test --use-cblas --use-gc boehm
- name: Build VAB
continue-on-error: true
run: |
echo "Clone vab"
git clone --depth 1 https://github.com/vlang/vab
echo "Build vab"
cd vab; ../v ./vab.v ; cd ..
echo "Build vab with -gc boehm -skip-unused"
cd vab; ../v -gc boehm -skip-unused ./vab.v ; cd ..
- name: Build Gitly
continue-on-error: true
run: |
echo "Clone markdown"
git clone https://github.com/vlang/markdown ~/.vmodules/markdown
echo "Clone Gitly"
git clone --depth 1 https://github.com/vlang/gitly
cd gitly
echo "Build Gitly"
../v .
echo "Build Gitly with -autofree"
../v -autofree .
echo "Run first_run.v"
../v run tests/first_run.v
# ./gitly -ci_run
- name: Build libsodium
continue-on-error: true
run: |
echo "Install libsodium-dev package"
sudo apt-get install --quiet -y libsodium-dev
echo "Clone the libsodium wrapper"
git clone https://github.com/vlang/libsodium ~/.vmodules/libsodium
echo "Test libsodium"
VJOBS=1 ./v -stats test ~/.vmodules/libsodium
- name: Build VEX
continue-on-error: true
run: |
echo "Install Vex dependencies"
sudo apt-get install --quiet -y libsodium-dev libssl-dev sqlite3 libsqlite3-dev
echo "Clone Vex"
mkdir -p ~/.vmodules/nedpals; git clone https://github.com/nedpals/vex ~/.vmodules/nedpals/vex
echo "Compile all of the Vex examples"
./v should-compile-all ~/.vmodules/nedpals/vex/examples
echo "Compile the simple Vex example with -gc boehm -skip-unused"
./v -gc boehm -skip-unused ~/.vmodules/nedpals/vex/examples/simple_example.v
echo "Run Vex Tests"
./v test ~/.vmodules/nedpals/vex
- name: Build go2v
continue-on-error: true
run: |
echo "Clone go2v"
clone --depth=1 https://github.com/vlang/go2v go2v/
echo "Build go2v"
./v go2v/
## echo "Run tests for go2v"
## VJOBS=1 ./v -stats test go2v/
- name: Build vlang/pdf
continue-on-error: true
run: |
git clone --depth=1 https://github.com/vlang/pdf ~/.vmodules/pdf/
echo "PDF examples should compile"
./v should-compile-all ~/.vmodules/pdf/examples

View File

@ -37,7 +37,6 @@ pub mut:
vroot string
vtmp_dir string
vargs string
failed bool
fail_fast bool
benchmark benchmark.Benchmark
rm_binaries bool = true
@ -288,7 +287,7 @@ pub fn (mut ts TestSession) test() {
fn worker_trunner(mut p pool.PoolProcessor, idx int, thread_id int) voidptr {
mut ts := &TestSession(p.get_shared_context())
if ts.fail_fast {
if ts.failed {
if ts.failed_cmds.len > 0 {
return pool.no_result
}
}
@ -380,7 +379,6 @@ fn worker_trunner(mut p pool.PoolProcessor, idx int, thread_id int) voidptr {
goto test_passed_system
}
}
ts.failed = true
ts.benchmark.fail()
tls_bench.fail()
ts.add_failed_cmd(cmd)
@ -396,7 +394,6 @@ fn worker_trunner(mut p pool.PoolProcessor, idx int, thread_id int) voidptr {
}
mut r := os.execute(cmd)
if r.exit_code < 0 {
ts.failed = true
ts.benchmark.fail()
tls_bench.fail()
ts.append_message(.fail, tls_bench.step_message_fail(normalised_relative_file))
@ -422,7 +419,6 @@ fn worker_trunner(mut p pool.PoolProcessor, idx int, thread_id int) voidptr {
goto test_passed_execute
}
}
ts.failed = true
ts.benchmark.fail()
tls_bench.fail()
ending_newline := if r.output.ends_with('\n') { '\n' } else { '' }
@ -510,7 +506,7 @@ pub fn v_build_failing_skipped(zargs string, folder string, oskipped []string, c
cb(mut session)
session.test()
eprintln(session.benchmark.total_message(finish_label))
return session.failed
return session.failed_cmds.len > 0
}
pub fn build_v_cmd_failed(cmd string) bool {

View File

@ -43,7 +43,7 @@ fn main() {
// eprintln('> session.skip_files: $session.skip_files')
session.test()
eprintln(session.benchmark.total_message(finish_label))
if session.failed {
if session.failed_cmds.len > 0 {
exit(1)
}
//

View File

@ -8,6 +8,8 @@ const github_job = os.getenv('GITHUB_JOB')
const (
skip_test_files = [
'cmd/tools/vdoc/html_tag_escape_test.v', /* can't locate local module: markdown */
'cmd/tools/vdoc/tests/vdoc_file_test.v', /* fails on Windows; order of output is not as expected */
'vlib/context/onecontext/onecontext_test.v',
'vlib/context/deadline_test.v' /* sometimes blocks */,
'vlib/mysql/mysql_orm_test.v' /* mysql not installed */,
@ -164,6 +166,7 @@ fn main() {
cmd_prefix := args_string.all_before('test-self')
title := 'testing vlib'
mut all_test_files := os.walk_ext(os.join_path(vroot, 'vlib'), '_test.v')
all_test_files << os.walk_ext(os.join_path(vroot, 'cmd'), '_test.v')
test_js_files := os.walk_ext(os.join_path(vroot, 'vlib'), '_test.js.v')
all_test_files << test_js_files
testing.eheader(title)

View File

@ -69,7 +69,7 @@ fn main() {
testing.header('Testing...')
ts.test()
println(ts.benchmark.total_message('all V _test.v files'))
if ts.failed {
if ts.failed_cmds.len > 0 {
exit(1)
}
}

View File

@ -1,2 +1,2 @@
cmd/tools/vvet/tests/array_init_one_val.vv:2: error: Use `var == value` instead of `var in [value]`
NB: You can run `v fmt -w file.v` to fix these errors automatically
Note: You can run `v fmt -w file.v` to fix these errors automatically

View File

@ -3,4 +3,4 @@ cmd/tools/vvet/tests/indent_with_space.vv:10: error: Looks like you are using sp
cmd/tools/vvet/tests/indent_with_space.vv:17: error: Looks like you are using spaces for indentation.
cmd/tools/vvet/tests/indent_with_space.vv:20: error: Looks like you are using spaces for indentation.
cmd/tools/vvet/tests/indent_with_space.vv:22: error: Looks like you are using spaces for indentation.
NB: You can run `v fmt -w file.v` to fix these errors automatically
Note: You can run `v fmt -w file.v` to fix these errors automatically

View File

@ -0,0 +1,6 @@
// Some header comment
// read_response is a carefully constructed comment.
// read_response_body. <-- this would earlier trigger a false
// postive.
pub fn read_response() ?(string, string) {}

View File

@ -1,2 +1,2 @@
cmd/tools/vvet/tests/parens_space_a.vv:1: error: Looks like you are adding a space after `(`
NB: You can run `v fmt -w file.v` to fix these errors automatically
Note: You can run `v fmt -w file.v` to fix these errors automatically

View File

@ -1,2 +1,2 @@
cmd/tools/vvet/tests/parens_space_b.vv:1: error: Looks like you are adding a space before `)`
NB: You can run `v fmt -w file.v` to fix these errors automatically
Note: You can run `v fmt -w file.v` to fix these errors automatically

View File

@ -182,13 +182,17 @@ fn (mut vt Vet) vet_fn_documentation(lines []string, line string, lnumber int) {
fn_name := ident_fn_name(line)
mut grab := true
for j := lnumber - 1; j >= 0; j-- {
mut prev_prev_line := ''
if j - 1 >= 0 {
prev_prev_line = lines[j - 1]
}
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') {
} else if prev_line.starts_with('// $fn_name') && !prev_prev_line.starts_with('//') {
grab = false
clean_line := line.all_before_last('{').trim(' ')
vt.warn('The documentation for "$clean_line" seems incomplete.', lnumber,

View File

@ -556,11 +556,11 @@ fn (mut app App) set_theme(idx int) {
}
fn (mut app App) resize() {
mut s := gg.dpi_scale()
mut s := app.gg.scale
if s == 0.0 {
s = 1.0
}
window_size := gg.window_size()
window_size := app.gg.window_size()
w := window_size.width
h := window_size.height
m := f32(math.min(w, h))

View File

@ -0,0 +1,7 @@
Module {
name: 'submodule'
description: ''
version: ''
license: ''
dependencies: []
}

View File

@ -2002,3 +2002,8 @@ pub fn (name string) match_glob(pattern string) bool {
// Matched all of `pattern` to all of `name`
return true
}
// is_ascii returns true if all characters belong to the US-ASCII set ([` `..`~`])
pub fn (s string) is_ascii() bool {
return !s.bytes().any(it < u8(` `) || it > u8(`~`))
}

View File

@ -989,6 +989,16 @@ fn test_string_f32() {
assert '-123.456'.f32() - (-123.456) <= f32_epsilon
}
fn test_string_is_ascii() {
assert ''.is_ascii() == true
assert ' '.is_ascii() == true
assert '~~'.is_ascii() == true
assert ' Az~'.is_ascii() == true
assert ' Aö~'.is_ascii() == false
assert '👋'.is_ascii() == false
assert 'a👋bc'.is_ascii() == false
}
fn test_string_with_zero_byte_escape() {
assert '\x00'.bytes() == [u8(0)]
}

View File

@ -8,6 +8,7 @@ module clipboard
// Clipboard represents a system clipboard.
//
// System "copy" and "paste" actions utilize the clipboard for temporary storage.
[heap]
pub struct Clipboard {
pb voidptr
last_cb_serial i64

View File

@ -54,6 +54,7 @@ fn C.DestroyWindow(hwnd C.HWND)
// Clipboard represents a system clipboard.
//
// System "copy" and "paste" actions utilize the clipboard for temporary storage.
[heap]
struct Clipboard {
max_retries int
retry_delay int

View File

@ -3,6 +3,7 @@ module dummy
// Clipboard represents a system clipboard.
//
// System "copy" and "paste" actions utilize the clipboard for temporary storage.
[heap]
pub struct Clipboard {
mut:
text string // text data sent or received

View File

@ -138,6 +138,7 @@ enum AtomType {
text_html = 9
}
[heap]
pub struct Clipboard {
display &C.Display
mut:
@ -291,6 +292,7 @@ fn (mut cb Clipboard) start_listener() {
mut sent_request := false
mut to_be_requested := Atom(0)
for {
time.sleep(1 * time.millisecond)
C.XNextEvent(cb.display, &event)
if unsafe { event.@type == 0 } {
println('error')

View File

@ -1,5 +1,17 @@
module math
// The vlang code is a modified version of the original C code from
// http://www.netlib.org/fdlibm/s_cbrt.c and came with this notice.
//
// ====================================================
// Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
//
// Developed at SunSoft, a Sun Microsystems, Inc. business.
// Permission to use, copy, modify, and distribute this
// software is freely granted, provided that this notice
// is preserved.
// ====================================================
// cbrt returns the cube root of a.
//
// special cases are:
@ -25,12 +37,12 @@ pub fn cbrt(a f64) f64 {
sign = true
}
// rough cbrt to 5 bits
mut t := f64_from_bits(f64_bits(x) / u64(3 + (u64(b1) << 32)))
mut t := f64_from_bits(f64_bits(x) / u64(3) + (u64(b1) << 32))
if x < smallest_normal {
// subnormal number
t = f64(u64(1) << 54) // set t= 2**54
t *= x
t = f64_from_bits(f64_bits(t) / u64(3 + (u64(b2) << 32)))
t = f64_from_bits(f64_bits(t) / u64(3) + (u64(b2) << 32))
}
// new cbrt to 23 bits
mut r := t * t / x

View File

@ -506,6 +506,13 @@ fn test_mod() {
assert (0.6447968302508578) == f
}
fn test_cbrt() {
cbrts := [2.0, 10, 56]
for idx, i in [8.0, 1000, 175_616] {
assert cbrt(i) == cbrts[idx]
}
}
fn test_exp() {
for i := 0; i < math.vf_.len; i++ {
f := exp(math.vf_[i])

View File

@ -66,6 +66,7 @@ mut:
buffer_size int
}
// new returns an `FTP` instance.
pub fn new() FTP {
mut f := FTP{
conn: 0
@ -101,6 +102,7 @@ fn (mut zftp FTP) read() ?(int, string) {
return code, data
}
// connect establishes an FTP connection to the host at `ip` port 21.
pub fn (mut zftp FTP) connect(ip string) ?bool {
zftp.conn = net.dial_tcp('$ip:21')?
zftp.reader = io.new_buffered_reader(reader: zftp.conn)
@ -111,6 +113,7 @@ pub fn (mut zftp FTP) connect(ip string) ?bool {
return false
}
// login sends the "USER `user`" and "PASS `passwd`" commands to the remote host.
pub fn (mut zftp FTP) login(user string, passwd string) ?bool {
zftp.write('USER $user') or {
$if debug {
@ -138,11 +141,13 @@ pub fn (mut zftp FTP) login(user string, passwd string) ?bool {
return false
}
// close closes the FTP connection.
pub fn (mut zftp FTP) close() ? {
zftp.write('QUIT')?
zftp.conn.close()?
}
// pwd returns the current working directory on the remote host for the logged in user.
pub fn (mut zftp FTP) pwd() ?string {
zftp.write('PWD')?
_, data := zftp.read()?
@ -153,6 +158,7 @@ pub fn (mut zftp FTP) pwd() ?string {
return data
}
// cd changes the current working directory to the specified remote directory `dir`.
pub fn (mut zftp FTP) cd(dir string) ? {
zftp.write('CWD $dir') or { return }
mut code, mut data := zftp.read()?
@ -201,6 +207,7 @@ fn (mut zftp FTP) pasv() ?&DTP {
return dtp
}
// dir returns a list of the files in the current working directory.
pub fn (mut zftp FTP) dir() ?[]string {
mut dtp := zftp.pasv() or { return error('Cannot establish data connection') }
zftp.write('LIST')?
@ -227,6 +234,7 @@ pub fn (mut zftp FTP) dir() ?[]string {
return dir
}
// get retrieves `file` from the remote host.
pub fn (mut zftp FTP) get(file string) ?[]u8 {
mut dtp := zftp.pasv() or { return error('Cannot stablish data connection') }
zftp.write('RETR $file')?

View File

@ -151,30 +151,31 @@ pub fn (mut s SSLConn) socket_read_into_ptr(buf_ptr &u8, len int) ?int {
mut res := 0
for {
res = C.SSL_read(voidptr(s.ssl), buf_ptr, len)
if res < 0 {
if res >= 0 {
return res
} else {
err_res := ssl_error(res, s.ssl)?
if err_res == .ssl_error_want_read {
for {
match err_res {
.ssl_error_want_read {
ready := @select(s.handle, .read, s.duration)?
if ready {
break
if !ready {
return net.err_timed_out
}
}
continue
} else if err_res == .ssl_error_want_write {
for {
.ssl_error_want_write {
ready := @select(s.handle, .write, s.duration)?
if ready {
break
if !ready {
return net.err_timed_out
}
}
continue
} else if err_res == .ssl_error_zero_return {
return 0
.ssl_error_zero_return {
return 0
}
else {
return error('Could not read using SSL. ($err_res)')
}
}
return error('Could not read using SSL. ($err_res)')
}
break
}
return res
}

View File

@ -228,13 +228,20 @@ fn (mut c Client) send_data() ? {
fn (mut c Client) send_body(cfg Mail) ? {
is_html := cfg.body_type == .html
date := cfg.date.custom_format('ddd, D MMM YYYY HH:mm ZZ')
nonascii_subject := cfg.subject.bytes().any(it < u8(` `) || it > u8(`~`))
mut sb := strings.new_builder(200)
sb.write_string('From: $cfg.from\r\n')
sb.write_string('To: <$cfg.to>\r\n')
sb.write_string('Cc: <$cfg.cc>\r\n')
sb.write_string('Bcc: <$cfg.bcc>\r\n')
sb.write_string('Date: $date\r\n')
sb.write_string('Subject: $cfg.subject\r\n')
if nonascii_subject {
// handle UTF-8 subjects according RFC 1342
sb.write_string('Subject: =?utf-8?B?' + base64.encode_str(cfg.subject) + '?=\r\n')
} else {
sb.write_string('Subject: $cfg.subject\r\n')
}
if is_html {
sb.write_string('Content-Type: text/html; charset=UTF-8')
} else {

86
vlib/os/filepath.v 100644
View File

@ -0,0 +1,86 @@
module os
// Collection of useful functions for manipulation, validation and analysis of system paths.
// The following functions handle paths depending on the operating system,
// therefore results may be different for certain operating systems.
const (
fslash = `/`
bslash = `\\`
dot = `.`
)
// is_abs_path returns `true` if the given `path` is absolute.
pub fn is_abs_path(path string) bool {
if path.len == 0 {
return false
}
$if windows {
return is_device_path(path) || is_drive_rooted(path) || is_normal_path(path)
}
return path[0] == os.fslash
}
// win_volume_len returns the length of the
// Windows volume/drive from the given `path`.
fn win_volume_len(path string) int {
plen := path.len
if plen < 2 {
return 0
}
if has_drive_letter(path) {
return 2
}
// its UNC path / DOS device path?
if path.len >= 5 && starts_w_slash_slash(path) && !is_slash(path[2]) {
for i := 3; i < plen; i++ {
if is_slash(path[i]) {
if i + 1 >= plen || is_slash(path[i + 1]) {
break
}
i++
for ; i < plen; i++ {
if is_slash(path[i]) {
return i
}
}
return i
}
}
}
return 0
}
fn is_slash(b u8) bool {
$if windows {
return b == os.bslash || b == os.fslash
}
return b == os.fslash
}
fn is_device_path(path string) bool {
return win_volume_len(path) >= 5 && starts_w_slash_slash(path)
}
fn has_drive_letter(path string) bool {
return path.len >= 2 && path[0].is_letter() && path[1] == `:`
}
fn starts_w_slash_slash(path string) bool {
return path.len >= 2 && is_slash(path[0]) && is_slash(path[1])
}
fn is_drive_rooted(path string) bool {
return path.len >= 3 && has_drive_letter(path) && is_slash(path[2])
}
// is_normal_path returns `true` if the given
// `path` is NOT a network or Windows device path.
fn is_normal_path(path string) bool {
plen := path.len
if plen == 0 {
return false
}
return (plen == 1 && is_slash(path[0])) || (plen >= 2 && is_slash(path[0])
&& !is_slash(path[1]))
}

View File

@ -0,0 +1,29 @@
module os
fn test_is_abs_path() {
$if windows {
assert is_abs_path('/')
assert is_abs_path('\\')
assert !is_abs_path('\\\\')
assert is_abs_path(r'C:\path\to\files\file.v')
assert is_abs_path(r'\\Host\share')
assert is_abs_path(r'//Host\share\files\file.v')
assert is_abs_path(r'\\.\BootPartition\Windows')
assert !is_abs_path(r'\\.\')
assert !is_abs_path(r'\\?\\')
assert !is_abs_path(r'C:path\to\dir')
assert !is_abs_path(r'dir')
assert !is_abs_path(r'.\')
assert !is_abs_path(r'.')
assert !is_abs_path(r'\\Host')
assert !is_abs_path(r'\\Host\')
return
}
assert is_abs_path('/')
assert is_abs_path('/path/to/files/file.v')
assert !is_abs_path('\\')
assert !is_abs_path('path/to/files/file.v')
assert !is_abs_path('dir')
assert !is_abs_path('./')
assert !is_abs_path('.')
}

View File

@ -454,18 +454,6 @@ pub fn is_file(path string) bool {
return exists(path) && !is_dir(path)
}
// is_abs_path returns `true` if `path` is absolute.
pub fn is_abs_path(path string) bool {
if path.len == 0 {
return false
}
$if windows {
return path[0] == `/` || // incase we're in MingGW bash
(path[0].is_letter() && path.len > 1 && path[1] == `:`)
}
return path[0] == `/`
}
// join_path returns a path as string from input string parameter(s).
[manualfree]
pub fn join_path(base string, dirs ...string) string {

View File

@ -590,14 +590,6 @@ fn test_ext() {
assert os.file_ext('file') == ''
}
fn test_is_abs() {
assert os.is_abs_path('/home/user')
assert os.is_abs_path('v/vlib') == false
$if windows {
assert os.is_abs_path('C:\\Windows\\')
}
}
fn test_join() {
$if windows {
assert os.join_path('v', 'vlib', 'os') == 'v\\vlib\\os'

View File

@ -7,8 +7,17 @@ module sync
// For that, please look at `channel_select_2_test.v` or `channel_select_3_test.v`
// This test case uses the implementation in `sync/channels.v` directly
// in order to test it independently from the support in the core language
import os
import time
fn test_should_run_flaky_test() {
if os.getenv('VTEST_RUN_FLAKY') != '1' {
eprintln('> skipping running flaky test, set VTEST_RUN_FLAKY to 1, to run it')
exit(0)
}
assert true
}
fn do_rec_i64(mut ch Channel) {
mut sum := i64(0)
for _ in 0 .. 300 {

View File

@ -184,3 +184,17 @@ fn test_parse_rfc3339() {
assert expected == output
}
}
fn test_ad_second_to_parse_result_in_2001() ? {
now_tm := time.parse('2001-01-01 04:00:00')?
future_tm := now_tm.add_seconds(60)
assert future_tm.str() == '2001-01-01 04:01:00'
assert now_tm.unix < future_tm.unix
}
fn test_ad_second_to_parse_result_pre_2001() ? {
now_tm := time.parse('2000-01-01 04:00:00')?
future_tm := now_tm.add_seconds(60)
assert future_tm.str() == '2000-01-01 04:01:00'
assert now_tm.unix < future_tm.unix
}

View File

@ -16,9 +16,10 @@ pub const (
seconds_per_hour = 60 * seconds_per_minute
seconds_per_day = 24 * seconds_per_hour
seconds_per_week = 7 * seconds_per_day
days_per_400_years = 365 * 400 + 97
days_per_100_years = 365 * 100 + 24
days_per_4_years = 365 * 4 + 1
days_per_400_years = days_in_year * 400 + 97
days_per_100_years = days_in_year * 100 + 24
days_per_4_years = days_in_year * 4 + 1
days_in_year = 365
days_before = [
0,
31,
@ -179,13 +180,13 @@ pub fn (t Time) relative() string {
}
return '$prefix$d days$suffix'
}
if secs < time.seconds_per_hour * 24 * 365 {
if secs < time.seconds_per_hour * 24 * time.days_in_year {
if prefix == 'in ' {
return 'on $t.md()'
}
return 'last $t.md()'
}
y := secs / time.seconds_per_hour / 24 / 365
y := secs / time.seconds_per_hour / 24 / time.days_in_year
if y == 1 {
return '${prefix}1 year$suffix'
}
@ -234,14 +235,14 @@ pub fn (t Time) relative_short() string {
}
return '$prefix${h}h$suffix'
}
if secs < time.seconds_per_hour * 24 * 365 {
if secs < time.seconds_per_hour * 24 * time.days_in_year {
d := secs / time.seconds_per_hour / 24
if d == 1 {
return '${prefix}1d$suffix'
}
return '$prefix${d}d$suffix'
}
y := secs / time.seconds_per_hour / 24 / 365
y := secs / time.seconds_per_hour / 24 / time.days_in_year
if y == 1 {
return '${prefix}1y$suffix'
}

View File

@ -5,13 +5,13 @@ fn test_add_to_day_in_the_previous_century() ? {
aa := a.add_days(180)
dump(a.debug())
dump(aa.debug())
assert aa.ymmdd() == '1900-06-29'
assert aa.ymmdd() == '1900-06-30'
}
fn test_add_to_day_in_the_past() ? {
a := time.parse_iso8601('1990-03-01')?
aa := a.add_days(180)
assert aa.ymmdd() == '1990-08-27'
assert aa.ymmdd() == '1990-08-28'
}
fn test_add_to_day_in_the_recent_past() ? {

View File

@ -267,3 +267,9 @@ fn test_recursive_local_call() {
fn test_strftime() {
assert '1980 July 11' == time_to_test.strftime('%Y %B %d')
}
fn test_add_seconds_to_time() {
now_tm := time.now()
future_tm := now_tm.add_seconds(60)
assert now_tm.unix < future_tm.unix
}

View File

@ -48,67 +48,39 @@ pub fn unix2(abs i64, microsecond int) Time {
fn calculate_date_from_offset(day_offset_ i64) (int, int, int) {
mut day_offset := day_offset_
// Move offset to year 2001 as it's the start of a new 400-year cycle
// Code below this rely on the fact that the day_offset is lined up with the 400-year cycle
// 1970-2000 (inclusive) has 31 years (8 of which are leap years)
mut year := 2001
day_offset -= 31 * 365 + 8
// Account for 400 year cycle
year += int(day_offset / days_per_400_years) * 400
day_offset %= days_per_400_years
// Account for 100 year cycle
if day_offset == days_per_100_years * 4 {
year += 300
day_offset -= days_per_100_years * 3
// source: http://howardhinnant.github.io/date_algorithms.html#civil_from_days
// shift from 1970-01-01 to 0000-03-01
day_offset += 719468 // int(days_per_400_years * 1970 / 400 - (28+31))
mut era := 0
if day_offset >= 0 {
era = int(day_offset / days_per_400_years)
} else {
year += int(day_offset / days_per_100_years) * 100
day_offset %= days_per_100_years
era = int((day_offset - days_per_400_years - 1) / days_per_400_years)
}
// Account for 4 year cycle
if day_offset == days_per_4_years * 25 {
year += 96
day_offset -= days_per_4_years * 24
// doe(day of era) [0, 146096]
doe := day_offset - era * days_per_400_years
// yoe(year of era) [0, 399]
yoe := (doe - doe / (days_per_4_years - 1) + doe / days_per_100_years - doe / (days_per_400_years - 1)) / days_in_year
// year number
mut y := int(yoe + era * 400)
// doy (day of year), with year beginning Mar 1 [0, 365]
doy := doe - (days_in_year * yoe + yoe / 4 - yoe / 100)
mp := (5 * doy + 2) / 153
d := int(doy - (153 * mp + 2) / 5 + 1)
mut m := int(mp)
if mp < 10 {
m += 3
} else {
year += int(day_offset / days_per_4_years) * 4
day_offset %= days_per_4_years
m -= 9
}
// Account for every year
if day_offset == 365 * 4 {
year += 3
day_offset -= 365 * 3
} else {
year += int(day_offset / 365)
day_offset %= 365
if m <= 2 {
y += 1
}
if day_offset < 0 {
year--
if is_leap_year(year) {
day_offset += 366
} else {
day_offset += 365
}
}
if is_leap_year(year) {
if day_offset > 31 + 29 - 1 {
// After leap day; pretend it wasn't there.
day_offset--
} else if day_offset == 31 + 29 - 1 {
// Leap day.
return year, 2, 29
}
}
mut estimated_month := day_offset / 31
for day_offset >= days_before[estimated_month + 1] {
estimated_month++
}
for day_offset < days_before[estimated_month] {
if estimated_month == 0 {
break
}
estimated_month--
}
day_offset -= days_before[estimated_month]
return year, int(estimated_month + 1), int(day_offset + 1)
return y, m, d
}
fn calculate_time_from_offset(second_offset_ i64) (int, int, int) {

View File

@ -256,13 +256,14 @@ pub:
mut_pos token.Pos
next_token token.Kind
pub mut:
expr Expr // expr.field_name
expr_type Type // type of `Foo` in `Foo.bar`
typ Type // type of the entire thing (`Foo.bar`)
name_type Type // T in `T.name` or typeof in `typeof(expr).name`
gkind_field GenericKindField // `T.name` => ast.GenericKindField.name, `T.typ` => ast.GenericKindField.typ, or .unknown
scope &Scope
from_embed_types []Type // holds the type of the embed that the method is called from
expr Expr // expr.field_name
expr_type Type // type of `Foo` in `Foo.bar`
typ Type // type of the entire thing (`Foo.bar`)
name_type Type // T in `T.name` or typeof in `typeof(expr).name`
gkind_field GenericKindField // `T.name` => ast.GenericKindField.name, `T.typ` => ast.GenericKindField.typ, or .unknown
scope &Scope
from_embed_types []Type // holds the type of the embed that the method is called from
has_hidden_receiver bool
}
// root_ident returns the origin ident where the selector started.

View File

@ -1464,7 +1464,6 @@ pub fn (t &TypeSymbol) find_method_with_generic_parent(name string) ?Fn {
param.typ = pt
}
}
method.generic_names.clear()
return method
}
else {}

View File

@ -927,9 +927,10 @@ pub fn (mut c Checker) infix_expr(mut node ast.InfixExpr) ast.Type {
}
}
}
} else if left_type.has_flag(.optional) && right_type.has_flag(.optional) {
} else if left_type.has_flag(.optional) || right_type.has_flag(.optional) {
opt_comp_pos := if left_type.has_flag(.optional) { left_pos } else { right_pos }
c.error('unwrapped optional cannot be compared in an infix expression',
left_right_pos)
opt_comp_pos)
}
}
.left_shift {
@ -1143,8 +1144,11 @@ pub fn (mut c Checker) infix_expr(mut node ast.InfixExpr) ast.Type {
// TODO move this to symmetric_check? Right now it would break `return 0` for `fn()?int `
left_is_optional := left_type.has_flag(.optional)
right_is_optional := right_type.has_flag(.optional)
if (left_is_optional && !right_is_optional) || (!left_is_optional && right_is_optional) {
c.error('unwrapped optional cannot be used in an infix expression', left_right_pos)
if left_is_optional && right_is_optional {
c.error('unwrapped optionals cannot be used in an infix expression', left_right_pos)
} else if left_is_optional || right_is_optional {
opt_infix_pos := if left_is_optional { left_pos } else { right_pos }
c.error('unwrapped optional cannot be used in an infix expression', opt_infix_pos)
}
// Dual sides check (compatibility check)
if !(c.symmetric_check(left_type, right_type) && c.symmetric_check(right_type, left_type))
@ -1846,6 +1850,37 @@ pub fn (mut c Checker) selector_expr(mut node ast.SelectorExpr) ast.Type {
node.typ = field.typ
return field.typ
}
if mut method := c.table.find_method(sym, field_name) {
if c.expected_type != 0 && c.expected_type != ast.none_type {
fn_type := ast.new_type(c.table.find_or_register_fn_type(c.mod, method, false,
true))
// if the expected type includes the receiver, don't hide it behind a closure
if c.check_types(fn_type, c.expected_type) {
return fn_type
}
}
receiver := method.params[0].typ
if receiver.nr_muls() > 0 {
if !c.inside_unsafe {
rec_sym := c.table.sym(receiver.set_nr_muls(0))
if !rec_sym.is_heap() {
suggestion := if rec_sym.kind == .struct_ {
'declaring `$rec_sym.name` as `[heap]`'
} else {
'wrapping the `$rec_sym.name` object in a `struct` declared as `[heap]`'
}
c.error('method `${c.table.type_to_str(receiver.idx())}.$method.name` cannot be used as a variable outside `unsafe` blocks as its receiver might refer to an object stored on stack. Consider ${suggestion}.',
node.expr.pos().extend(node.pos))
}
}
}
method.params = method.params[1..]
node.has_hidden_receiver = true
method.name = ''
fn_type := ast.new_type(c.table.find_or_register_fn_type(c.mod, method, false,
true))
return fn_type
}
if sym.kind !in [.struct_, .aggregate, .interface_, .sum_type] {
if sym.kind != .placeholder {
unwrapped_sym := c.table.sym(c.unwrap_generic(typ))
@ -3371,7 +3406,21 @@ pub fn (mut c Checker) ident(mut node ast.Ident) ast.Type {
c.note('`[if $node.name]` is deprecated. Use `[if $node.name?]` instead',
node.pos)
} else {
c.error('undefined ident: `$node.name`', node.pos)
cname_mod := node.name.all_before('.')
if cname_mod.len != node.name.len {
mut const_names_in_mod := []string{}
for _, so in c.table.global_scope.objects {
if so is ast.ConstField {
if so.mod == cname_mod {
const_names_in_mod << so.name
}
}
}
c.error(util.new_suggestion(node.name, const_names_in_mod).say('undefined ident: `$node.name`'),
node.pos)
} else {
c.error('undefined ident: `$node.name`', node.pos)
}
}
}
if c.table.known_type(node.name) {
@ -3533,6 +3582,7 @@ pub fn (mut c Checker) select_expr(mut node ast.SelectExpr) ast.Type {
}
pub fn (mut c Checker) lock_expr(mut node ast.LockExpr) ast.Type {
expected_type := c.expected_type
if c.rlocked_names.len > 0 || c.locked_names.len > 0 {
c.error('nested `lock`/`rlock` not allowed', node.pos)
}
@ -3556,16 +3606,17 @@ pub fn (mut c Checker) lock_expr(mut node ast.LockExpr) ast.Type {
}
}
c.stmts(node.stmts)
c.rlocked_names = []
c.locked_names = []
// handle `x := rlock a { a.getval() }`
mut ret_type := ast.void_type
if node.stmts.len > 0 {
last_stmt := node.stmts.last()
if last_stmt is ast.ExprStmt {
ret_type = last_stmt.typ
c.expected_type = expected_type
ret_type = c.expr(last_stmt.expr)
}
}
c.rlocked_names = []
c.locked_names = []
if ret_type != ast.void_type {
node.is_expr = true
}

View File

@ -958,6 +958,9 @@ pub fn (mut c Checker) fn_call(mut node ast.CallExpr, mut continue_check &bool)
if param.typ == ast.voidptr_type_idx || arg_typ == ast.voidptr_type_idx {
continue
}
if param.typ.is_any_kind_of_pointer() && arg_typ.is_any_kind_of_pointer() {
continue
}
param_typ_sym_ := c.table.sym(c.table.unaliased_type(param.typ))
arg_typ_sym_ := c.table.sym(c.table.unaliased_type(arg_typ))
// Allow `[32]i8` as `&i8` etc
@ -968,6 +971,17 @@ pub fn (mut c Checker) fn_call(mut node ast.CallExpr, mut continue_check &bool)
&& (typ_is_number || c.table.unaliased_type(arg_typ).is_any_kind_of_pointer())) {
continue
}
// Allow `[N]anyptr` as `[N]anyptr`
if arg_typ_sym_.kind == .array && param_typ_sym_.kind == .array {
if (arg_typ_sym_.info as ast.Array).elem_type.is_any_kind_of_pointer()
&& (param_typ_sym_.info as ast.Array).elem_type.is_any_kind_of_pointer() {
continue
}
} else if arg_typ_sym_.kind == .array_fixed && param_typ_sym_.kind == .array_fixed {
if (arg_typ_sym_.info as ast.ArrayFixed).elem_type.is_any_kind_of_pointer()&& (param_typ_sym_.info as ast.ArrayFixed).elem_type.is_any_kind_of_pointer() {
continue
}
}
// Allow `int` as `&i8`
if param.typ.is_any_kind_of_pointer() && typ_is_number {
continue

View File

@ -1,6 +1,6 @@
vlib/v/checker/tests/infix_compare_optional_err.vv:6:5: error: unwrapped optional cannot be compared in an infix expression
4 |
4 |
5 | fn main(){
6 | if foo() > foo() {}
| ~~~~~~~~~~~~~
| ~~~~~
7 | }

View File

@ -26,11 +26,11 @@ vlib/v/checker/tests/infix_err.vv:11:7: error: `+` cannot be used with `?int`
| ^
12 | _ = int(0) + g() // FIXME not detected
13 | _ = g() + int(3)
vlib/v/checker/tests/infix_err.vv:12:5: error: unwrapped optional cannot be used in an infix expression
vlib/v/checker/tests/infix_err.vv:12:14: error: unwrapped optional cannot be used in an infix expression
10 |
11 | _ = 4 + g()
12 | _ = int(0) + g() // FIXME not detected
| ~~~~~~~~~~~~
| ~~~
13 | _ = g() + int(3)
14 | _ = g() + 3
vlib/v/checker/tests/infix_err.vv:13:9: error: `+` cannot be used with `?int`

View File

@ -5,6 +5,13 @@ vlib/v/checker/tests/lock_already_locked.vv:11:3: error: nested `lock`/`rlock` n
| ~~~~~
12 | a.x++
13 | }
vlib/v/checker/tests/lock_already_locked.vv:12:4: error: a has an `rlock` but needs a `lock`
10 | lock a {
11 | rlock a {
12 | a.x++
| ^
13 | }
14 | }
vlib/v/checker/tests/lock_already_locked.vv:15:10: error: `a` is `shared` and must be `rlock`ed or `lock`ed to be used as non-mut argument to print
13 | }
14 | }

View File

@ -0,0 +1,6 @@
vlib/v/checker/tests/misspelled_mod_const_should_have_suggestion.vv:3:21: error: undefined ident: `time.secondz`.
Did you mean `time.second`?
1 | import time
2 |
3 | time.sleep(1 * time.secondz)
| ~~~~~~~

View File

@ -0,0 +1,3 @@
import time
time.sleep(1 * time.secondz)

View File

@ -0,0 +1,7 @@
vlib/v/checker/tests/unsafe_method_as_field.vv:23:7: error: method `Foo.ref` cannot be used as a variable outside `unsafe` blocks as its receiver might refer to an object stored on stack. Consider declaring `Foo` as `[heap]`.
21 | f := Foo{}
22 | _ := f.no_ref // no error
23 | _ := f.ref // error
| ~~~~~
24 |
25 | b := Bar{}

View File

@ -0,0 +1,27 @@
struct Foo {
}
fn (f Foo) no_ref() int {
return 1
}
fn (f &Foo) ref() int {
return 1
}
[heap]
struct Bar {
}
fn (f &Bar) ref() int {
return 1
}
fn main() {
f := Foo{}
_ := f.no_ref // no error
_ := f.ref // error
b := Bar{}
_ := b.ref // no error
}

View File

@ -2,5 +2,4 @@ vlib/v/checker/tests/unwrapped_optional_infix.vv:5:9: error: unwrapped optional
3 | }
4 |
5 | println(test() == "")
| ~~~~~~~~~~~~
| ~~~~~~

View File

@ -1951,7 +1951,8 @@ pub fn (mut f Fmt) if_expr(node ast.IfExpr) {
}
fn branch_is_single_line(b ast.IfBranch) bool {
if b.stmts.len == 1 && b.comments.len == 0 && stmt_is_single_line(b.stmts[0]) {
if b.stmts.len == 1 && b.comments.len == 0 && stmt_is_single_line(b.stmts[0])
&& b.pos.line_nr == b.stmts[0].pos.line_nr {
return true
}
return false

View File

@ -0,0 +1,10 @@
fn main() {
a := fn () {
if true {
println('a')
} else {
println('a')
}
}
a()
}

View File

@ -0,0 +1,10 @@
fn main() {
a := fn () {
if true {
println('a')
} else {
println('a')
}
}
a()
}

View File

@ -64,8 +64,7 @@ a: 5
lock x { // wait for ongoing reads to finish, don't start new ones
x.a = 17 // this value should never be read
time.sleep(50* time.millisecond)
x.a = if (i & 1) == 0 {
7} else {5}
x.a = if (i & 1) == 0 { 7 } else { 5 }
} // now new reads are possible again
time.sleep(20*time.millisecond)
}

View File

@ -211,7 +211,11 @@ fn (mut g Gen) gen_assign_stmt(node_ ast.AssignStmt) {
} else if g.inside_for_c_stmt {
g.expr(val)
} else {
g.write('{$styp _ = ')
if left_sym.kind == .function {
g.write('{void* _ = ')
} else {
g.write('{$styp _ = ')
}
g.expr(val)
g.writeln(';}')
}
@ -456,6 +460,8 @@ fn (mut g Gen) gen_assign_stmt(node_ ast.AssignStmt) {
}
if val is ast.ArrayInit {
g.array_init(val, ident.name)
} else if val_type.has_flag(.shared_f) {
g.expr_with_cast(val, val_type, var_type)
} else {
g.expr(val)
}

View File

@ -3339,6 +3339,68 @@ fn (mut g Gen) selector_expr(node ast.SelectorExpr) {
}
}
}
} else if m := g.table.find_method(sym, node.field_name) {
mut has_embeds := false
if sym.info in [ast.Struct, ast.Aggregate] {
if node.from_embed_types.len > 0 {
has_embeds = true
}
}
if !has_embeds {
if !node.has_hidden_receiver {
g.write('${g.typ(node.expr_type.idx())}_$m.name')
return
}
receiver := m.params[0]
expr_styp := g.typ(node.expr_type.idx())
data_styp := g.typ(receiver.typ.idx())
mut sb := strings.new_builder(256)
name := '_V_closure_${expr_styp}_${m.name}_$node.pos.pos'
sb.write_string('${g.typ(m.return_type)} ${name}(')
for i in 1 .. m.params.len {
param := m.params[i]
if i != 1 {
sb.write_string(', ')
}
sb.write_string('${g.typ(param.typ)} a$i')
}
sb.writeln(') {')
sb.writeln('\t$data_styp* a0 = *($data_styp**)(__RETURN_ADDRESS() - __CLOSURE_DATA_OFFSET);')
if m.return_type != ast.void_type {
sb.write_string('\treturn ')
} else {
sb.write_string('\t')
}
sb.write_string('${expr_styp}_${m.name}(')
if !receiver.typ.is_ptr() {
sb.write_string('*')
}
for i in 0 .. m.params.len {
if i != 0 {
sb.write_string(', ')
}
sb.write_string('a$i')
}
sb.writeln(');')
sb.writeln('}')
g.anon_fn_definitions << sb.str()
g.nr_closures++
g.write('__closure_create($name, ')
if !receiver.typ.is_ptr() {
g.write('memdup(')
}
if !node.expr_type.is_ptr() {
g.write('&')
}
g.expr(node.expr)
if !receiver.typ.is_ptr() {
g.write(', sizeof($expr_styp))')
}
g.write(')')
return
}
}
n_ptr := node.expr_type.nr_muls() - 1
if n_ptr > 0 {

View File

@ -1625,15 +1625,29 @@ fn (mut g Gen) call_args(node ast.CallExpr) {
arr_sym := g.table.sym(varg_type)
mut arr_info := arr_sym.info as ast.Array
if varg_type.has_flag(.generic) {
if fn_def := g.table.find_fn(node.name) {
mut muttable := unsafe { &ast.Table(g.table) }
if utyp := muttable.resolve_generic_to_concrete(arr_info.elem_type, fn_def.generic_names,
node.concrete_types)
{
arr_info.elem_type = utyp
if node.is_method {
left_sym := g.table.sym(node.left_type)
if fn_def := left_sym.find_method_with_generic_parent(node.name) {
mut muttable := unsafe { &ast.Table(g.table) }
if utyp := muttable.resolve_generic_to_concrete(arr_info.elem_type,
fn_def.generic_names, node.concrete_types)
{
arr_info.elem_type = utyp
}
} else {
g.error('unable to find method $node.name', node.pos)
}
} else {
g.error('unable to find function $node.name', node.pos)
if fn_def := g.table.find_fn(node.name) {
mut muttable := unsafe { &ast.Table(g.table) }
if utyp := muttable.resolve_generic_to_concrete(arr_info.elem_type,
fn_def.generic_names, node.concrete_types)
{
arr_info.elem_type = utyp
}
} else {
g.error('unable to find function $node.name', node.pos)
}
}
}
elem_type := g.typ(arr_info.elem_type)

View File

@ -78,7 +78,8 @@ fn (mut g Gen) if_expr(node ast.IfExpr) {
g.write(' ? ')
}
prev_expected_cast_type := g.expected_cast_type
if node.is_expr && g.table.sym(node.typ).kind == .sum_type {
if node.is_expr
&& (g.table.sym(node.typ).kind == .sum_type || node.typ.has_flag(.shared_f)) {
g.expected_cast_type = node.typ
}
g.stmts(branch.stmts)
@ -204,7 +205,8 @@ fn (mut g Gen) if_expr(node ast.IfExpr) {
}
if needs_tmp_var {
prev_expected_cast_type := g.expected_cast_type
if node.is_expr && g.table.sym(node.typ).kind == .sum_type {
if node.is_expr
&& (g.table.sym(node.typ).kind == .sum_type || node.typ.has_flag(.shared_f)) {
g.expected_cast_type = node.typ
}
g.stmts_with_tmp_var(branch.stmts, tmp)

View File

@ -172,8 +172,7 @@ fn (mut g Gen) index_of_array(node ast.IndexExpr, sym ast.TypeSymbol) {
}
// `vals[i].field = x` is an exception and requires `array_get`:
// `(*(Val*)array_get(vals, i)).field = x;`
is_selector := node.left is ast.SelectorExpr
if g.is_assign_lhs && !is_selector && node.is_setter {
if g.is_assign_lhs && node.is_setter {
is_direct_array_access := (g.fn_decl != 0 && g.fn_decl.is_direct_arr) || node.is_direct
is_op_assign := g.assign_op != .assign && info.elem_type != ast.string_type
if is_direct_array_access {

View File

@ -201,21 +201,25 @@ fn (mut g Gen) infix_expr_eq_op(node ast.InfixExpr) {
g.write(')')
}
.struct_ {
ptr_typ := g.equality_fn(left.unaliased)
if node.op == .ne {
g.write('!')
if g.pref.translated {
g.gen_plain_infix_expr(node)
} else {
ptr_typ := g.equality_fn(left.unaliased)
if node.op == .ne {
g.write('!')
}
g.write('${ptr_typ}_struct_eq(')
if left.typ.is_ptr() {
g.write('*')
}
g.expr(node.left)
g.write(', ')
if right.typ.is_ptr() {
g.write('*')
}
g.expr(node.right)
g.write(')')
}
g.write('${ptr_typ}_struct_eq(')
if left.typ.is_ptr() {
g.write('*')
}
g.expr(node.left)
g.write(', ')
if right.typ.is_ptr() {
g.write('*')
}
g.expr(node.right)
g.write(')')
}
.sum_type {
ptr_typ := g.equality_fn(left.unaliased)

View File

@ -46,18 +46,7 @@ fn (mut g Gen) struct_init(node ast.StructInit) {
g.write('($styp){')
}
}
// mut fields := []string{}
mut inited_fields := map[string]int{} // TODO this is done in checker, move to ast node
/*
if node.fields.len == 0 && node.exprs.len > 0 {
// Get fields for {a,b} short syntax. Fields array wasn't set in the parser.
for f in info.fields {
fields << f.name
}
} else {
fields = node.fields
}
*/
mut inited_fields := map[string]int{}
if is_multiline {
g.indent++
}
@ -149,8 +138,6 @@ fn (mut g Gen) struct_init(node ast.StructInit) {
}
g.is_shared = old_is_shared2
}
// g.zero_struct_fields(info, inited_fields)
// nr_fields = info.fields.len
for mut field in info.fields {
if !field.typ.has_flag(.shared_f) {
g.is_shared = false
@ -183,6 +170,9 @@ fn (mut g Gen) struct_init(node ast.StructInit) {
}
}
if !cloned {
inside_cast_in_heap := g.inside_cast_in_heap
g.inside_cast_in_heap = 0 // prevent use of pointers in child structs
if field_type_sym.kind == .array_fixed && sfield.expr is ast.Ident {
fixed_array_info := field_type_sym.info as ast.ArrayFixed
g.write('{')
@ -202,6 +192,7 @@ fn (mut g Gen) struct_init(node ast.StructInit) {
}
g.expr_with_cast(sfield.expr, sfield.typ, sfield.expected_type)
}
g.inside_cast_in_heap = inside_cast_in_heap // restore value for further struct inits
}
if is_multiline {
g.writeln(',')

View File

@ -378,6 +378,11 @@ fn (mut w Walker) expr(node_ ast.Expr) {
}
ast.SelectorExpr {
w.expr(node.expr)
if node.expr_type != 0 {
if method := w.table.find_method(w.table.sym(node.expr_type), node.field_name) {
w.fn_by_name(method.fkey())
}
}
}
ast.SqlExpr {
w.expr(node.db_expr)

View File

@ -1,3 +1,3 @@
vlib/v/parser/tests/dollar_err.vv:1:1: error: unexpected eof, expecting `if`
vlib/v/parser/tests/dollar_err.vv:1:1: error: unexpected eof
1 | $
| ^
| ^

View File

@ -287,13 +287,13 @@ fn vweb_tmpl_${fn_name}() string {
}
}
.js {
if line.contains('//V_TEMPLATE') {
source.writeln(insert_template_code(fn_name, tmpl_str_start, line))
} else {
// replace `$` to `\$` at first to escape JavaScript template literal syntax
source.writeln(line.replace(r'$', r'\$').replace(r'$$', r'@').replace(r'.$',
r'.@').replace(r"'", r"\'"))
}
// if line.contains('//V_TEMPLATE') {
source.writeln(insert_template_code(fn_name, tmpl_str_start, line))
//} else {
// replace `$` to `\$` at first to escape JavaScript template literal syntax
// source.writeln(line.replace(r'$', r'\$').replace(r'$$', r'@').replace(r'.$',
// r'.@').replace(r"'", r"\'"))
//}
continue
}
.css {

View File

@ -0,0 +1,43 @@
struct Vec {
mut:
x f64
y f64
z f64
}
struct Plane {
position Vec
normal Vec
}
struct Sphere {
position Vec
radius f64
}
interface Object {
position Vec
}
fn test_append_struct_to_interface_array() {
mut scene := []Object{}
scene << Plane{
position: Vec{0, -10, 0}
normal: Vec{0, -1, 0}
}
scene << Sphere{
position: Vec{0, 0, -20}
radius: 7
}
println(scene)
assert scene.len == 2
assert scene[0].position.x == 0
assert scene[0].position.y == -10
assert scene[0].position.z == 0
assert scene[1].position.x == 0
assert scene[1].position.y == 0
assert scene[1].position.z == -20
}

View File

@ -60,7 +60,11 @@ fn main() {
(aa % gcd) == big.zero_int,
(bb % gcd) == big.zero_int,
].all(it == true)
{ true } else { false }
{
true
} else {
false
}
}
cfgs := [

View File

@ -0,0 +1,17 @@
struct Foo<T> {
mut:
arr []T
}
fn (mut foo Foo<T>) push<T>(items ...T) {
for item in items {
foo.arr << item
}
}
fn test_generic_method_with_variadic_args() {
mut f := Foo<int>{}
f.push(1, 2, 3, 5, 8, 13, 21, 34, 55)
println(f.arr)
assert f.arr == [1, 2, 3, 5, 8, 13, 21, 34, 55]
}

View File

@ -9,6 +9,11 @@ import v.util.vtest
const turn_off_vcolors = os.setenv('VCOLORS', 'never', true)
const skip_files = [
'do_not_remove_this',
'tmpl_parse_html.vv', // skipped, due to a V template compilation problem after b42c824
]
fn test_all() {
mut total_errors := 0
vexe := os.getenv('VEXE')
@ -25,6 +30,11 @@ fn test_all() {
paths := vtest.filter_vtest_only(tests, basepath: dir)
for path in paths {
print(path + ' ')
fname := os.file_name(path)
if fname in skip_files {
println(term.bright_yellow('SKIP'))
continue
}
program := path
tname := rand.ulid()
compilation := os.execute('${os.quoted_path(vexe)} -o $tname -cflags "-w" -cg ${os.quoted_path(program)}')

View File

@ -0,0 +1,119 @@
struct Foo {
s string
mut:
i int
}
fn (f Foo) get_s() string {
return f.s
}
fn (f &Foo) get_s_ref() string {
return f.s
}
fn (f Foo) add(a int) int {
return a + f.i
}
fn (f &Foo) add_ref(a int) int {
return a + f.i
}
fn (mut f Foo) set(a int) {
f.i = a
}
fn (f_ Foo) set_val(a int) int {
mut f := unsafe { &f_ }
old := f.i
f.i = a
return old
}
fn test_methods_as_fields() {
mut f := Foo{
s: 'hello'
i: 1
}
get_s := f.get_s
get_s_ref := unsafe { f.get_s_ref }
add := f.add
add_ref := unsafe { f.add_ref }
set := unsafe { f.set }
set_val := f.set_val
assert typeof(get_s).str() == 'fn () string'
assert typeof(get_s_ref).str() == 'fn () string'
assert typeof(add).str() == 'fn (int) int'
assert typeof(add_ref).str() == 'fn (int) int'
assert get_s() == 'hello'
assert get_s_ref() == 'hello'
assert add(2) == 3
assert add_ref(2) == 3
assert f.i == 1
set(2)
assert f.i == 2
old := set_val(3)
assert f.i == 2
new := set_val(5)
assert old == new && old == 1
}
// the difference between these two tests is that here `f` is &Foo
fn test_methods_as_fields_ref() {
mut f := &Foo{
s: 'hello'
i: 1
}
get_s := f.get_s
get_s_ref := unsafe { f.get_s_ref }
add := f.add
add_ref := unsafe { f.add_ref }
set := unsafe { f.set }
set_val := f.set_val
assert typeof(get_s).str() == 'fn () string'
assert typeof(get_s_ref).str() == 'fn () string'
assert typeof(add).str() == 'fn (int) int'
assert typeof(add_ref).str() == 'fn (int) int'
assert get_s() == 'hello'
assert get_s_ref() == 'hello'
assert add(2) == 3
assert add_ref(2) == 3
assert f.i == 1
set(2)
assert f.i == 2
old := set_val(3)
assert f.i == 2
new := set_val(5)
assert old == new && old == 1
}
struct GG_Ctx {
frame_fn fn (voidptr) int
}
[heap]
struct App {
msg string = 'hello'
}
fn (app &App) frame() int {
return app.msg.len
}
fn test_ctx_arg_expected() {
mut app := &App{}
mut ctx := &GG_Ctx{
frame_fn: app.frame
}
assert typeof(ctx.frame_fn).str() == 'fn (voidptr) int'
assert ctx.frame_fn(app) == 5
}

View File

@ -0,0 +1,19 @@
type AA = bool | int
fn test_shared_if_expr() {
shared a := [1, 2, 3]
b := [4, 5, 6]
c := lock a {
if a == b { a } else { b }
}
assert c == [4, 5, 6]
d := lock a {
if a != b {
a << 5
a
} else {
b
}
}
assert d == [1, 2, 3, 5]
}

View File

@ -0,0 +1,12 @@
struct Foo {
x int
}
fn (f Foo) hi() int {
return f.x
}
fn main() {
f := Foo{123}
_ = f.hi
}

View File

@ -0,0 +1,17 @@
struct App {
mut:
buffer []string
}
fn test_struct_field_array_index() {
mut app := &App{
buffer: []string{len: 2}
}
app.buffer[0] += 'hello'
app.buffer[1] += 'world'
println(app)
assert app.buffer == ['hello', 'world']
}

View File

@ -20,8 +20,8 @@ fn test_template_interpolation_can_be_selectively_turned_on_in_script_tags() {
text := $tmpl('tmpl/selective_interpolation_in_script_tag.html')
dump(text)
assert text.contains('Username: abcd')
assert text.contains('var non_interpolated_labels = @benchmark_plot_data.dates;')
assert text.contains('var non_interpolated_values = @benchmark_plot_data.numerical_result;')
assert text.contains("var non_interpolated_labels = ['2012-11-30', '2022-12-29'];")
assert text.contains('var non_interpolated_values = [5, 6, 7, 1];')
assert text.contains("var real_labels = ['2012-11-30', '2022-12-29']; //V_TEMPLATE")
assert text.contains('var real_values = [5, 6, 7, 1]; //V_TEMPLATE')
assert text.contains('Year: 2022')