diff --git a/BUGS-TO-REPORT.md b/BUGS-TO-REPORT.md index 608163ecf..5afd4d9f7 100644 --- a/BUGS-TO-REPORT.md +++ b/BUGS-TO-REPORT.md @@ -1499,7 +1499,7 @@ Severity: low in practice, since a program rarely passes a null pointer, but a C ## A failure code from a late-bound call is raised as the HRESULT, where VB6 raises its own error number, such as 450 for DISP_E_BADPARAMCOUNT **Describe the bug** -Follow-up to #2490, which is fixed in BETA 1005 for a name the object does not have: a late-bound statement and `CallByName` now raise error 438, as VB6 does. This is the rest of the conversion VB6 makes when `IDispatch::Invoke` fails. For ten failure codes VB6 raises one of its own error numbers, and twinBASIC raises the `HRESULT` itself, so a handler written for VB6 or VBA does not recognise the error. The common one is a call with the wrong number of arguments to an object such as a `Collection`, a `Scripting.Dictionary` or a `Scripting.FileSystemObject`: VB6 raises 450, *Wrong number of arguments or invalid property assignment*, and twinBASIC raises `&H8002000E` (`DISP_E_BADPARAMCOUNT`), *Invalid number of parameters.* Observed by running the reproducer's test cases. +Follow-up to #2490, which is fixed in BETA 1005 for a name the object does not have: a late-bound statement and `CallByName` now raise error 438, as VB6 does. This is the rest of the conversion VB6 makes when `IDispatch::Invoke` fails. For eleven failure codes VB6 raises one of its own error numbers, and twinBASIC raises the `HRESULT` itself, so a handler written for VB6 or VBA does not recognise the error. The common one is a call with the wrong number of arguments to an object such as a `Collection`, a `Scripting.Dictionary` or a `Scripting.FileSystemObject`: VB6 raises 450, *Wrong number of arguments or invalid property assignment*, and twinBASIC raises `&H8002000E` (`DISP_E_BADPARAMCOUNT`), *Invalid number of parameters.* Observed by running the reproducer's test cases. **To Reproduce** Steps to reproduce the behavior: @@ -1519,8 +1519,11 @@ Steps to reproduce the behavior: | `E_NOINTERFACE` `&H80004002` | 430 | -2147467262 | | `E_ACCESSDENIED` `&H80070005` | 70 | -2147024891 | | `E_ABORT` `&H80004004` | 287 | -2147467260 | + | `DISP_E_NOTACOLLECTION` `&H80020011` | 451 | -2147352559 | -The module below is the reproducer's, and is meant to be adapted as a regression test. Each expected number is VB6's, from the VB6 project. On BETA 1005 the three cases from #2490 and the three codes twinBASIC already converts pass, and the ten codes of the table and the `Collection` call of step 2 fail. + An ordinary object gives the last code as well: `x = d.Count(1)`, where `d` is an `Object` holding a `Scripting.Dictionary`, raises -2147352559 in twinBASIC and 451, *Property let procedure not defined and property get procedure did not return an object*, in VB6 and 64-bit VBA. + +The module below is the reproducer's, and is meant to be adapted as a regression test. Each expected number is VB6's, from the VB6 project. On BETA 1005 the three cases from #2490 and the three codes twinBASIC already converts pass, and the codes of the table and the `Collection` and `Dictionary` calls fail. ```vb ' Regression tests for #2490 and its follow-up: the error number a late-bound ' call raises when the object fails it. The expected numbers are what VB6 6.0 @@ -1751,6 +1754,23 @@ Module LateBoundErrorTests Assert.Strict.AreEqual 287, Err.Number ' VB6: 287, Application-defined or object-defined error End Sub + [TestCase] + Public Sub NotACollection_Raises451() + Dim o As Object = New HResultObject + On Error Resume Next + o.Hr80020011 ' DISP_E_NOTACOLLECTION + Assert.Strict.AreEqual 451, Err.Number ' VB6: 451, Property let procedure not defined and property get procedure did not return an object + End Sub + + [TestCase] + Public Sub NotACollection_Dictionary_Raises451() + Dim d As Object = CreateObject("Scripting.Dictionary") + Dim x As Variant + On Error Resume Next + x = d.Count(1) ' Count takes no index + Assert.Strict.AreEqual 451, Err.Number ' VB6: 451 + End Sub + End Module ``` @@ -1770,7 +1790,7 @@ Not part of this report: VB6's `CallByName` raises 440, *Automation error*, for Severity: code ported from VB6 that tests `Err.Number` for 450, 449 or 9 after a late-bound call does not recognise the error, and the description is the system's text for a COM code instead of VB's. - + --- @@ -5780,18 +5800,18 @@ End Module ``` **Expected behavior** -Error 6, *Overflow*, at each of the six, as in VB6, and the `Date` keeps its old value. The two ends of the range stay accepted. A `Date` then always holds a value that `CStr`, `Format` and `Year` can read. +Error 6, *Overflow*, at each of the six, as in VB6 and in 64-bit VBA, and the `Date` keeps its old value. The two ends of the range stay accepted. A `Date` then always holds a value that `CStr`, `Format` and `Year` can read. **Desktop:** - OS: Windows 10 Pro 22H2 (build 19045) - twinBASIC compiler version: BETA 1010 **Additional context** -BETA 1007 gives the same results, and so do IDE runs and built exes, 32-bit and 64-bit. `DateAdd("d", 1, #12/31/9999#)` and `DateSerial(10000, 1, 1)` raise error 5, and `CDate("12/31/10000")` raises error 13, so those do not accept the value. VB6 6.0 raises error 6 for each of the six cases, and builds the controls (the reproducer's `vb6/` project prints them, attached as `date-out-of-range-no-error-vb6.zip`). +BETA 1007 gives the same results, and so do IDE runs and built exes, 32-bit and 64-bit. `DateAdd("d", 1, #12/31/9999#)` and `DateSerial(10000, 1, 1)` raise error 5, and `CDate("12/31/10000")` raises error 13, so those do not accept the value. VB6 6.0 raises error 6 for each of the six cases, and builds the controls (the reproducer's `vb6/` project prints them, attached as `date-out-of-range-no-error-vb6.zip`). So does 64-bit VBA, VBA 7.1 (VBE7.DLL 7.01.1158) in 64-bit Microsoft 365 Access. Severity: medium. An out-of-range date is stored silently, and the failure comes later, where the value is formatted or split into parts, or never, where the number is only compared or saved. - + --- @@ -5879,18 +5899,18 @@ End Module ``` **Expected behavior** -Error 6, *Overflow*, at each of the four, as for `CLngLng(1E+19)` and for the same value in a variable. VB6 has no `LongLong`; the nearest case, the first `Double` beyond the `Long` range, `CLng(2 ^ 31)`, `CLng(2147483648#)` and `n = 2147483648#`, raises error 6 there as a constant and as a variable (the reproducer's `vb6/` project prints them, attached as `clnglng-2-to-63-no-overflow-vb6.zip`). +Error 6, *Overflow*, at each of the four, as for `CLngLng(1E+19)` and for the same value in a variable. VB6 has no `LongLong`; the nearest case, the first `Double` beyond the `Long` range, `CLng(2 ^ 31)`, `CLng(2147483648#)` and `n = 2147483648#`, raises error 6 there as a constant and as a variable (the reproducer's `vb6/` project prints them, attached as `clnglng-2-to-63-no-overflow-vb6.zip`). 64-bit VBA, which has `LongLong`, raises error 6 at each of the four and passes the four controls. **Desktop:** - OS: Windows 10 Pro 22H2 (build 19045) - twinBASIC compiler version: BETA 1010 **Additional context** -BETA 1007 gives the same results, and so do IDE runs and built exes, 32-bit and 64-bit. The narrower conversions of constants are right: `CLng(2 ^ 31)`, `CLng(CSng(2 ^ 31))`, `CInt(32768#)`, `CByte(256#)` and `CLngPtr(2 ^ 63)` in a 32-bit build raise error 6. A related defect seen on the way has its own entry: *In a 32-bit build, a constant `Double` expression raises error 6 after a constant that overflows a `Long` or `LongLong`*. +BETA 1007 gives the same results, and so do IDE runs and built exes, 32-bit and 64-bit. The narrower conversions of constants are right: `CLng(2 ^ 31)`, `CLng(CSng(2 ^ 31))`, `CInt(32768#)`, `CByte(256#)` and `CLngPtr(2 ^ 63)` in a 32-bit build raise error 6. A related defect seen on the way has its own entry: *In a 32-bit build, a constant `Double` expression raises error 6 after a constant that overflows a `Long` or `LongLong`*. 64-bit VBA here is VBA 7.1 (VBE7.DLL 7.01.1158) in 64-bit Microsoft 365 Access. Severity: medium. A silently wrong value, but only for one constant, and constants this size are rare. The unsuffixed `9223372036854775807` is the one a program is likely to write. - + --- @@ -6286,222 +6306,6 @@ Severity: low. The error is raised and is easy to see; the statement that raises --- -## A 64-bit build prints a negative zero as `-0` - -**Describe the bug** -A `Single` or `Double` that holds a negative zero (the sign bit set, every other bit clear) converts to the text `-0` in a 64-bit build: `CStr`, `Str$` (with no leading space), `Format$`, concatenation with `&` and `Debug.Print` all give it. A 32-bit twinBASIC build gives `0` (`Str$` gives ` 0`), and so does VB6. Both builds hold the negative zero: the sign bit (`&H80000000` in a `Single`, `&H8000000000000000` in a `Double`) is read back through `RtlMoveMemory`, and the value compares equal to 0. It was found on a page of the documentation whose sample prints a GDI+ matrix offset that is a negative zero, `0 5 -0` in a 64-bit build where the page says `0 5 0`. It was observed in IDE runs. - -**To Reproduce** -Steps to reproduce the behavior: -1. Open `negative-zero-text-win64.twinproj` (attached as `negative-zero-text-win64.zip`). Its module `NegativeZeroTextTests` has one `[TestCase]` for each conversion, asserting what VB6 gives, two cases that check the sign bit, and two controls. -2. Run the test cases as a 64-bit build (`tbrun --tests --arch win64`). The seven conversion cases fail, with `-0` as `Actual`; the sign-bit cases and the controls pass. As a 32-bit build all eleven pass. -3. The shortest form of it, in a 64-bit build: -```vb -Dim s As Single -s = 0! -s = -s -Debug.Print CStr(s), Str$(s), Format$(s), "x=" & s ' VB6 and 32-bit: 0 " 0" 0 x=0 ; 64-bit: -0 -0 -0 x=-0 -``` - -**Expected behavior** -The same text in a 64-bit build as in a 32-bit one and in VB6: `0` from `CStr`, `Format$` and `&`, and ` 0` from `Str$`. The reproducer's `vb6/` project prints them (attached as `negative-zero-text-win64-vb6.zip`). - -**Desktop:** - - OS: Windows 10 Pro 22H2 (build 19045) - - twinBASIC compiler version: BETA 1010 - -**Additional context** -The reproducer's `repro.json` names the 64-bit build (`"arch": "win64"`). A positive zero and an ordinary negative number (`-1.5`) print the same in both builds. Severity: low. A negative zero is the result of `-x` for a zero `x`, and a COM or API result can hold one, so a program meets it, and one that shows or compares the text behaves differently in the two builds. - - - ---- - -## A class field whose initializer reads another field of the class fails the build with a codegen error - -**Describe the bug** -A field initializer in a class that reads another field of the same class (`Public Second As Long = First + 1`) compiles with no diagnostic, and the build fails once the class is used: `[LINKER] compilation (codegen) error detected in 'Counter.{fields_init}' at line #1`, then `[LINKER] FAILED due to compilation errors`. A run in the IDE goes ahead instead, and the field holds an arbitrary number: with `A` = 1, `X = A + 1` read `1073741889`. Observed with `tbbuild --build` and with `tbrun` (a `[RunAfterBuild]` procedure in the IDE). - -**To Reproduce** -Steps to reproduce the behavior: -1. Open `class-field-init-reads-field.twinproj` (attached as `class-field-init-reads-field.zip`). Its file `Startup.twin` is: - ``` - Module Startup - Public Sub Main() - Dim c As New Counter - Debug.Print c.First & ", " & c.Second - End Sub - End Module - - Class Counter - Public First As Long = 1 - Public Second As Long = First + 1 - End Class - ``` -2. Compile the project: no diagnostic. -3. Build it. The DEBUG CONSOLE prints `[LINKER] compilation (codegen) error detected in 'Counter.{fields_init}' at line #1` and `[BUILD] failed`. - -**Expected behavior** -The build succeeds and `Main` prints `1, 2`: `Second`'s initializer reads `First`, whose initializer comes before it. If reading another field is not meant to be allowed, the compiler should say so at the declaration, as it does for a call to a procedure of the project (TB5079). Field initializers are a twinBASIC extension, so there is no VB6 behaviour to compare with. - -**Desktop:** - - OS: Windows 10 Pro 22H2 (build 19045) - - twinBASIC compiler version: BETA 1010 - -**Additional context** -The same codegen error, one line per class, for `X = A` with no arithmetic, `X = Me.A + 1`, `String` fields (`X = S & "b"`), a `ReadOnly` field read, a field declared below the one that reads it, and a field with no initializer of its own, in 32-bit and 64-bit builds. These are right: a field that reads a module-level variable (`X = Mods.MV + 1`) or a constant, a module-level variable that reads a variable of its own module (`MW = MV + 1`), and a class that is never created (it is not compiled, so the build does not fail). - -Severity: medium. The build fails with a message that names the class but not the field or the reason, and the IDE run gives a wrong value with no message at all. Setting the field in `Class_Initialize` or `Sub New` avoids it. - -Not tried: an `Object` or `Variant` field, LLVM. - - - ---- - -## In the IDE, a module-level initializer that reads another module's variable leaves every module-level variable at 0 - -**Describe the bug** -When one module-level variable's initializer reads a variable of another module (`Public FromConfig As Long = Config.Limit + 100`), code run in the IDE reads every module-level variable of the project as 0, including ones whose initializers are constants (`Public Limit As Long = 5`). The built exe gets them all right. Observed with a `[RunAfterBuild]` procedure, through `tbrun` and `bug_repro.mjs run`; the exe was run with `tbrun --exe`. - -**To Reproduce** -Steps to reproduce the behavior: -1. Open `module-init-reads-other-module-ide.twinproj` (attached as `module-init-reads-other-module-ide.zip`). Its file `Startup.twin` is: - ``` - Module Config - Public Const Base As Long = 7 - Public Limit As Long = 5 - Public Doubled As Long = Base * 2 - End Module - - Module Other - Public FromConfig As Long = Config.Limit + 100 - End Module - - Class Box - Public Size As Long = 3 - End Class - - Module Startup - Public Sub Main() - Dim b As New Box - Debug.Print "Limit=" & Config.Limit & " Doubled=" & Config.Doubled & " FromConfig=" & Other.FromConfig & " Box.Size=" & b.Size - End Sub - End Module - ``` -2. Add a module with a `[RunAfterBuild]` procedure that calls `Startup.Main`, and build the project. -3. The DEBUG CONSOLE prints `Limit=0 Doubled=0 FromConfig=0 Box.Size=3`. - -**Expected behavior** -`Limit=5 Doubled=14 FromConfig=105 Box.Size=3`, which is what the built exe prints. Module-level initializers are a twinBASIC extension, so there is no VB6 behaviour to compare with. - -**Desktop:** - - OS: Windows 10 Pro 22H2 (build 19045) - - twinBASIC compiler version: BETA 1010 - -**Additional context** -Every module-level variable reads 0 as long as one initializer reads another module's variable. Each of these makes the IDE print `Limit=5 Doubled=14`: the same project without `Other`, `FromConfig = 100`, `FromConfig = Config.Base + 100` (another module's constant), and `FromConfig = Limit2 + 100`, where `Limit2` is a variable of `Other` itself. A class field initializer runs either way (`Box.Size=3`). In the exe, a module whose initializer reads a module that is set up after it reads 0 there; that is a matter of order, and the IDE gives 0 for every module, whatever the order. - -Severity: medium. A `[RunAfterBuild]` procedure that signs or post-processes the build sees wrong settings with no message, and code that runs correctly as an exe behaves differently in the IDE. - -Not tried: a run with F5 (only `[RunAfterBuild]` was observed), a variable of the module's own read with a qualified name (`Other.Limit2`), 64-bit, LLVM. - - - ---- - -## `ReDim` with an upper bound less than its lower bound raises -2147467259, *Unspecified error*, where VB6 raises error 9 - -**Describe the bug** -`ReDim X(0 To -1)`, `ReDim X(-1)` and `ReDim X(5 To 4, 1)` on a dynamic array (`Dim X() As Long`), all of which give a dimension an upper bound below its lower bound, raise error -2147467259 (`&H80004005`), *Unspecified error*, where VB6 raises error 9, *Subscript out of range*. A handler that tests for 9 does not recognise it. Observed in the compiler's test mode, 32-bit and 64-bit, and in a probe run after a build. - -**To Reproduce** -Steps to reproduce the behavior: -1. Open `redim-inverted-bounds-error.twinproj` (attached as `redim-inverted-bounds-error.zip`). -2. Run its test cases, the module below. On BETA 1010 the three controls pass (`ReDim X(0 To 0)`, `ReDim X(-3 To -1)`, and a subscript above a fixed-size array's range, which raises 9) and the four cases on an inverted bound fail with `Actual: -2147467259`; `Err.Description` is *Unspecified error*. - -The module is meant to be adapted as a regression test. Each case asserts VB6's result, which the attached VB6 project (`redim-inverted-bounds-error-vb6.zip`) prints: 9, *Subscript out of range*, for each of the three statements. -```vb -' Regression tests: ReDim with an upper bound less than its lower bound raises error 9, -' "Subscript out of range". Expected numbers are VB6's (the VB6 project beside this one -' prints them). tbrun --tests runs each case and prints PASS or FAIL. -[TestFixture] -Module RegressionTests - - ' Controls: bounds in order, an empty-looking but valid range, and a subscript out of range. - [TestCase] - Public Sub ReDim_OneElement_Succeeds() - Dim X() As Long - ReDim X(0 To 0) - Assert.Strict.AreEqual 0&, UBound(X) - End Sub - - [TestCase] - Public Sub ReDim_NegativeBoundsInOrder_Succeeds() - Dim X() As Long - ReDim X(-3 To -1) - Assert.Strict.AreEqual -3&, LBound(X) - End Sub - - [TestCase] - Public Sub FixedArray_SubscriptAboveRange_Raises9() - Dim A(1 To 3) As Long - On Error Resume Next - A(4) = 1 - Assert.Strict.AreEqual 9&, Err.Number - End Sub - - [TestCase] - Public Sub ReDim_ZeroToMinusOne_Raises9() - Dim X() As Long - On Error Resume Next - ReDim X(0 To -1) - Assert.Strict.AreEqual 9&, Err.Number - End Sub - - [TestCase] - Public Sub ReDim_MinusOne_Raises9() - Dim X() As Long - On Error Resume Next - ReDim X(-1) - Assert.Strict.AreEqual 9&, Err.Number - End Sub - - [TestCase] - Public Sub ReDim_FiveToFour_SecondDimension_Raises9() - Dim X() As Long - On Error Resume Next - ReDim X(5 To 4, 1) - Assert.Strict.AreEqual 9&, Err.Number - End Sub - - [TestCase] - Public Sub ReDim_ErrorDescription() - Dim X() As Long - On Error Resume Next - ReDim X(0 To -1) - Assert.Strict.AreEqual "Subscript out of range", Err.Description - End Sub - -End Module -``` - -**Expected behavior** -Error 9, *Subscript out of range*, as VB6 raises, and as twinBASIC itself raises for a subscript outside a fixed-size array. - -**Desktop:** - - OS: Windows 10 Pro 22H2 (build 19045) - - twinBASIC compiler version: BETA 1010 - -**Additional context** -Severity: low. An error is raised wherever VB6 raises one, so no failure goes unreported, but a handler that tests for 9 does not recognise it, and *Unspecified error* does not say what is wrong. It is the same symptom as "An index out of range on a fixed-size array member of a UDT raises -2147467259, *Unspecified error*, where VB6 raises error 9": a bounds failure that reaches the program as *Unspecified error*, here from the allocation rather than from a subscript. - -What does not reproduce it: bounds in order (`0 To 0`, `-3 To -1`), and a subscript outside a fixed-size array that is not in a UDT. - - - ---- - ## A *compare* of 2 (`vbDatabaseCompare`) raises no error in `InStr`, `StrComp` and `Filter`, where VB6 raises error 5 **Describe the bug** @@ -6741,7 +6545,7 @@ End Module ``` **Expected behavior** -No error, and the result `InStr` gives for a zero-length string with a valid *compare* (0 for a zero-length *string1*, *start* for a zero-length *string2*), as VB6 returns, and as twinBASIC's `InStrB` and `StrComp` return. +No error, and the result `InStr` gives for a zero-length string with a valid *compare* (0 for a zero-length *string1*, *start* for a zero-length *string2*), as VB6 and 64-bit VBA return, and as twinBASIC's `InStrB` and `StrComp` return. **Desktop:** - OS: Windows 10 Pro 22H2 (build 19045) @@ -6750,102 +6554,14 @@ No error, and the result `InStr` gives for a zero-length string with a valid *co **Additional context** Severity: low. Only a *compare* that VB6 would refuse for any other strings is affected, but a program that ran under VB6 stops with error 5 where a string is empty. +A 64-bit build gives the same results. 64-bit VBA here is VBA 7.1 (VBE7.DLL 7.01.1158) in 64-bit Microsoft 365 Access. + What does not reproduce it: an invalid *compare* with neither string zero-length, or with *start* past the end of *string1*, which VB6 refuses too; `InStrRev` and `Replace`, which check *compare* first in VB6 as well; and `InStrB` and `StrComp`, which already behave as VB6 does. --- -## `String` with a *character* of `Null` raises error 94, where VB6 returns `Null` - -**Describe the bug** -`String(3, Null)`, the **Variant** form of `String`, raises error 94, *Invalid use of Null*. VB6 raises no error and returns **Null** (`VarType` 1), as it does for `String(0, Null)`. `String$(3, Null)` raises 94 in both, and so does a *number* of **Null** in either form, so only the **Variant** form with a **Null** *character* differs. Observed in the compiler's test mode, 32-bit and 64-bit. - -**To Reproduce** -Steps to reproduce the behavior: -1. Open `string-null-character.twinproj` (attached as `string-null-character.zip`). -2. Run its test cases, the module below. On BETA 1010 the four controls pass (`String(3, "a")`, a **Null** *number* in `String` and `String$`, and `String$(3, Null)`, which raise 94 in VB6 too) and the three cases on `String` with a **Null** *character* fail: `Actual: 94`, or `runtime error 94: Invalid use of Null`. - -The module is meant to be adapted as a regression test. Each case asserts VB6's result, which the attached VB6 project (`string-null-character-vb6.zip`) prints: no error and `VarType` 1 (**Null**) for `String(3, Null)` and `String(0, Null)`, and 94 for the controls. -```vb -' Regression tests: the Variant form String, given a character of Null, returns Null -' with no error; String$ raises error 94. Expected results are VB6's (the VB6 project -' beside this one prints them). tbrun --tests runs each case and prints PASS or FAIL. -[TestFixture] -Module RegressionTests - - ' Controls: a letter, a Null number in either form, and String$ with a Null character. - [TestCase] - Public Sub String_Letter_Repeats() - Assert.Strict.AreEqual CVar("aaa"), String(3, "a") - End Sub - - [TestCase] - Public Sub String_NullNumber_Raises94() - On Error Resume Next - Dim R As Variant - R = String(Null, "a") - Assert.Strict.AreEqual 94&, Err.Number - End Sub - - [TestCase] - Public Sub StringDollar_NullNumber_Raises94() - On Error Resume Next - Dim R As Variant - R = String$(Null, "a") - Assert.Strict.AreEqual 94&, Err.Number - End Sub - - [TestCase] - Public Sub StringDollar_NullCharacter_Raises94() - On Error Resume Next - Dim R As Variant - R = String$(3, Null) - Assert.Strict.AreEqual 94&, Err.Number - End Sub - - ' The defect: String with a Null character returns Null. - [TestCase] - Public Sub String_NullCharacter_NoError() - On Error Resume Next - Dim R As Variant - R = String(3, Null) - Assert.Strict.AreEqual 0&, Err.Number - End Sub - - [TestCase] - Public Sub String_NullCharacter_ReturnsNull() - Dim R As Variant - R = String(3, Null) - Assert.Strict.AreEqual vbNull, VarType(R) - End Sub - - [TestCase] - Public Sub String_ZeroNullCharacter_ReturnsNull() - Dim R As Variant - R = String(0, Null) - Assert.Strict.AreEqual vbNull, VarType(R) - End Sub - -End Module -``` - -**Expected behavior** -`String(number, Null)` returns **Null** with no error, as VB6 does, and as the **Variant** forms of other string functions propagate **Null**; `String$` keeps raising 94. - -**Desktop:** - - OS: Windows 10 Pro 22H2 (build 19045) - - twinBASIC compiler version: BETA 1010 - -**Additional context** -Severity: low. Code that relies on **Null** propagating through `String`, as VB6 code reading database fields can, stops with error 94. - -What does not reproduce it: `String$` with a **Null** *character*, and a **Null** *number* in either form, all of which raise 94 in VB6 as well. - - - ---- - ## `StrConv` raises error 5 for `vbWide`, `vbNarrow`, `vbKatakana` and `vbHiragana` with an East Asian *LCID* on a system whose locale is not East Asian, where VB6 converts **Describe the bug** @@ -6924,7 +6640,7 @@ End Module ``` **Expected behavior** -The conversion, made with the rules of the locale *LCID* names, as VB6 makes it. Error 5 only when neither *LCID* nor, without one, the system's locale is East Asian. +The conversion, made with the rules of the locale *LCID* names, as VB6 and 64-bit VBA make it. Error 5 only when neither *LCID* nor, without one, the system's locale is East Asian. **Desktop:** - OS: Windows 10 Pro 22H2 (build 19045), system locale 1033 @@ -6933,6 +6649,1265 @@ The conversion, made with the rules of the locale *LCID* names, as VB6 makes it. **Additional context** Severity: low. A program that converts Japanese, Chinese or Korean text with an explicit *LCID*, so that it runs on any system, stops with error 5 on a Western one. +A 64-bit build gives the same results. 64-bit VBA here is VBA 7.1 (VBE7.DLL 7.01.1158) in 64-bit Microsoft 365 Access. + What does not reproduce it: another conversion (`vbUpperCase`) with an East Asian *LCID*; and `vbWide` with no *LCID* or with 1033, which VB6 refuses as well. Not checked on a system whose locale is East Asian. + +--- + +## Assigning to `Date`, `Date$`, `Time` and `Time$` raises other errors than VB6, and `Date$` and `Time$` refuse forms VB6 accepts + +**Describe the bug** +Four differences from VB6 when the system date or time is set: + +- `Date = Null` and `Time = Null` raise 94, *Invalid use of Null*; VB6 raises 13, *Type mismatch*. +- `Date$` and `Time$` given a string they cannot read raise -2147467259 (`&H80004005`), *Unspecified error*; VB6 raises 5, *Invalid procedure call or argument*. +- `Date$` accepts only *mm-dd-yyyy* and `Time$` only *hh:mm:ss*. VB6 also accepts *m/d/yyyy*, a two-digit year and *hh:mm*, which twinBASIC refuses with -2147467259. +- When Windows refuses the date or time, such as a date before January 1, 1601 (`Date = #12/31/1600#`) or February 30 (`Date$ = "02-30-2000"`), twinBASIC raises -2147467259 and VB6 raises 5. Neither runtime checks these itself. This needs the privilege to set the clock to see, so the module below does not test it; the documentation's own probe saw it with the clock setters replaced in its process by ones that refuse what Windows refuses. + +Observed in the compiler's test mode, 32-bit, and in a built exe. The test cases set the clock to what it already holds, so a value that is accepted raises 70, *Permission denied*, in a process without the privilege to set the clock, and changes nothing in one with it. + +**To Reproduce** +Steps to reproduce the behavior: +1. Open `date-time-set-errors.twinproj` (attached as `date-time-set-errors.zip`). +2. Run its test cases, the module below. On BETA 1010 the four controls pass and the other seven fail. + +The module is meant to be adapted as a regression test. Each case asserts VB6's result, which the attached VB6 project (`date-time-set-errors-vb6.zip`) prints. +```vb +[TestFixture] +Module RegressionTests + + ' 0 or 70: the value was accepted, and only setting the clock could fail. + Private Function Accepted(ByVal N As Long) As Boolean + Return N = 0 Or N = 70 + End Function + + ' Controls: a string that is not a date raises 13, Date$ = Null raises 94, and a + ' value of the documented form is accepted. + [TestCase] + Public Sub Date_SetNotADate_Raises13() + On Error Resume Next + Date = "abc" + Assert.Strict.AreEqual 13&, Err.Number + End Sub + + [TestCase] + Public Sub DateString_SetNull_Raises94() + On Error Resume Next + Date$ = Null + Assert.Strict.AreEqual 94&, Err.Number + End Sub + + [TestCase] + Public Sub DateString_SetMmDdYyyy_Accepted() + On Error Resume Next + Date$ = Format$(Date, "mm-dd-yyyy") + Assert.Strict.IsTrue Accepted(Err.Number) + End Sub + + [TestCase] + Public Sub TimeString_SetHhMmSs_Accepted() + On Error Resume Next + Time$ = Format$(Time, "hh:mm:ss") + Assert.Strict.IsTrue Accepted(Err.Number) + End Sub + + ' The defect: Null raises 94 where VB6 raises 13. + [TestCase] + Public Sub Date_SetNull_Raises13() + On Error Resume Next + Date = Null + Assert.Strict.AreEqual 13&, Err.Number + End Sub + + [TestCase] + Public Sub Time_SetNull_Raises13() + On Error Resume Next + Time = Null + Assert.Strict.AreEqual 13&, Err.Number + End Sub + + ' The defect: a string Date$ or Time$ cannot read raises -2147467259, Unspecified + ' error, where VB6 raises 5. + [TestCase] + Public Sub DateString_SetNotADate_Raises5() + On Error Resume Next + Date$ = "abc" + Assert.Strict.AreEqual 5&, Err.Number + End Sub + + [TestCase] + Public Sub TimeString_SetNotATime_Raises5() + On Error Resume Next + Time$ = "abc" + Assert.Strict.AreEqual 5&, Err.Number + End Sub + + ' The defect: forms VB6 accepts raise -2147467259. + [TestCase] + Public Sub DateString_SetSlashes_Accepted() + On Error Resume Next + Date$ = Format$(Date, "m/d/yyyy") + Assert.Strict.IsTrue Accepted(Err.Number) + End Sub + + [TestCase] + Public Sub DateString_SetTwoDigitYear_Accepted() + On Error Resume Next + Date$ = Format$(Date, "mm-dd-yy") + Assert.Strict.IsTrue Accepted(Err.Number) + End Sub + + [TestCase] + Public Sub TimeString_SetHhMm_Accepted() + On Error Resume Next + Time$ = Format$(Time, "hh:mm") + Assert.Strict.IsTrue Accepted(Err.Number) + End Sub + +End Module +``` + +**Expected behavior** +Error 13 for `Null` assigned to `Date` or `Time`, error 5 for a string `Date$` or `Time$` cannot read, and the forms VB6 reads accepted, as VB6 and 64-bit VBA do. + +**Desktop:** + - OS: Windows 10 Pro 22H2 (build 19045), system locale 1033 + - twinBASIC compiler version: BETA 1010 + +**Additional context** +Severity: low. A handler that tests for VB6's numbers misses these, and a program that sets the clock from a string such as `"10:30"` fails. + +A 64-bit build gives the same results. 64-bit VBA here is VBA 7.1 (VBE7.DLL 7.01.1158) in 64-bit Microsoft 365 Access. + +What does not reproduce it: `Date$ = Null` and `Time$ = Null`, which raise 94 in both; `Date` and `Time` given a string that is not a date or time, which raise 13 in both. + + + +--- + +## `DateAdd` with the "yyyy", "q" or "m" interval returns a date in the 1900s or 2000s when the result would be before year 100, where VB6 raises error 5 + +**Describe the bug** +`DateAdd("yyyy", -1, #1/1/100#)` returns January 1, 1999, with no error: a result before January 1, 100 seems to be taken as a two-digit year. The same happens for the "q" and "m" intervals: `DateAdd("m", -1, #1/1/100#)` returns December 1, 1999. The other intervals raise 5, *Invalid procedure call or argument*, as VB6 does for all of them. Observed in the compiler's test mode, 32-bit and 64-bit, and in a built exe. + +**To Reproduce** +Steps to reproduce the behavior: +1. Open `dateadd-year-below-100.twinproj` (attached as `dateadd-year-below-100.zip`). +2. Run its test cases, the module below. On BETA 1010 the two controls pass and the other three fail with `Actual: 0`. + +The module is meant to be adapted as a regression test. Each case asserts VB6's result, which the attached VB6 project (`dateadd-year-below-100-vb6.zip`) prints. +```vb +[TestFixture] +Module RegressionTests + + ' Controls: a day before January 1, 100 raises 5, and a year that reaches year 100 + ' exactly gives that date. + [TestCase] + Public Sub DateAdd_DayBefore100_Raises5() + Dim V As Variant + On Error Resume Next + V = DateAdd("d", -1, #1/1/100#) + Assert.Strict.AreEqual 5&, Err.Number + End Sub + + [TestCase] + Public Sub DateAdd_YearsToYear100_GivesYear100() + Dim V As Variant = DateAdd("yyyy", -1900, #1/1/2000#) + Assert.Strict.AreEqual #1/1/100#, CDate(V) + End Sub + + ' The defect: no error, and a date in the 1900s or 2000s: DateAdd("yyyy", -1, #1/1/100#) + ' returns January 1, 1999. + [TestCase] + Public Sub DateAdd_YearBefore100_Raises5() + Dim V As Variant + On Error Resume Next + V = DateAdd("yyyy", -1, #1/1/100#) + Assert.Strict.AreEqual 5&, Err.Number + End Sub + + [TestCase] + Public Sub DateAdd_QuarterBefore100_Raises5() + Dim V As Variant + On Error Resume Next + V = DateAdd("q", -1, #1/1/100#) + Assert.Strict.AreEqual 5&, Err.Number + End Sub + + [TestCase] + Public Sub DateAdd_MonthBefore100_Raises5() + Dim V As Variant + On Error Resume Next + V = DateAdd("m", -1, #1/1/100#) + Assert.Strict.AreEqual 5&, Err.Number + End Sub + +End Module +``` + +**Expected behavior** +Error 5 whenever the result would be before January 1, 100, whatever the interval, as VB6 and 64-bit VBA do. + +**Desktop:** + - OS: Windows 10 Pro 22H2 (build 19045) + - twinBASIC compiler version: BETA 1010 + +**Additional context** +Severity: medium. The result is a valid date about 1,900 years away from the right one, returned without an error: `DateAdd("yyyy", -1901, #1/1/2000#)` gives January 1, 1999, and `DateAdd("yyyy", -2000, #1/1/2000#)` January 1, 2000. + +A 64-bit build gives the same results. 64-bit VBA here is VBA 7.1 (VBE7.DLL 7.01.1158) in 64-bit Microsoft 365 Access. + +What does not reproduce it: a result far enough below year 100 that it is not a year from 0 to 99 (`DateAdd("yyyy", -10000, #1/1/2000#)` raises 5); the intervals "d", "y", "w", "ww", "h", "n" and "s"; and the upper end, where a result after December 31, 9999 raises 5 for every interval. + + + +--- + +## `DateAdd` and `DateDiff` raise error 6 for a date that is a number outside the range of dates, where VB6 raises error 13 + +**Describe the bug** +`DateAdd("d", 0, 2958466)` and `DateDiff("d", #1/1/2000#, 3000000)` raise 6, *Overflow*. VB6 raises 13, *Type mismatch*, and so do `DatePart`, `Day` and the other functions that take a date, in twinBASIC and VB6 alike. Observed in the compiler's test mode, 32-bit, and in a built exe. + +**To Reproduce** +Steps to reproduce the behavior: +1. Open `dateadd-date-out-of-range-overflow.twinproj` (attached as `dateadd-date-out-of-range-overflow.zip`). +2. Run its test cases, the module below. On BETA 1010 the two controls pass and the other three fail with `Actual: 6`. + +The module is meant to be adapted as a regression test. Each case asserts VB6's result, which the attached VB6 project (`dateadd-date-out-of-range-overflow-vb6.zip`) prints. +```vb +[TestFixture] +Module RegressionTests + + ' Controls: DatePart and Day raise 13 for the same number. + [TestCase] + Public Sub DatePart_NumberOutOfRange_Raises13() + Dim V As Variant + On Error Resume Next + V = DatePart("d", 3000000) + Assert.Strict.AreEqual 13&, Err.Number + End Sub + + [TestCase] + Public Sub Day_NumberOutOfRange_Raises13() + Dim V As Variant + On Error Resume Next + V = Day(3000000) + Assert.Strict.AreEqual 13&, Err.Number + End Sub + + ' The defect: DateAdd and DateDiff raise 6, Overflow. + [TestCase] + Public Sub DateAdd_NumberAboveRange_Raises13() + Dim V As Variant + On Error Resume Next + V = DateAdd("d", 0, 2958466) + Assert.Strict.AreEqual 13&, Err.Number + End Sub + + [TestCase] + Public Sub DateAdd_NumberBelowRange_Raises13() + Dim V As Variant + On Error Resume Next + V = DateAdd("d", 0, -657435) + Assert.Strict.AreEqual 13&, Err.Number + End Sub + + [TestCase] + Public Sub DateDiff_NumberOutOfRange_Raises13() + Dim V As Variant + On Error Resume Next + V = DateDiff("d", #1/1/2000#, 3000000) + Assert.Strict.AreEqual 13&, Err.Number + End Sub + +End Module +``` + +**Expected behavior** +Error 13, as VB6 and 64-bit VBA raise, and as `DatePart` and `Day` raise. + +**Desktop:** + - OS: Windows 10 Pro 22H2 (build 19045) + - twinBASIC compiler version: BETA 1010 + +**Additional context** +Severity: low. A handler that tests for 13 misses it. + +A 64-bit build gives the same results. 64-bit VBA here is VBA 7.1 (VBE7.DLL 7.01.1158) in 64-bit Microsoft 365 Access. + +What does not reproduce it: the last and first days of the range (2958465 and -657434), which both accept; a string that is not a date, which raises 13 in both. + + + +--- + +## `DatePart` raises error 5 for an invalid interval when the date is `Null`, where VB6 returns `Null` + +**Describe the bug** +`DatePart("x", Null)` raises 5, *Invalid procedure call or argument*. VB6 returns `Null` without checking the interval, and twinBASIC's own `DateAdd("x", 1, Null)` and `DateDiff("x", Null, #1/1/2001#)` return `Null` as VB6's do. Observed in the compiler's test mode, 32-bit and 64-bit, and in a built exe. + +**To Reproduce** +Steps to reproduce the behavior: +1. Open `datepart-null-checks-interval.twinproj` (attached as `datepart-null-checks-interval.zip`). +2. Run its test cases, the module below. On BETA 1010 the three controls pass and `DatePart_BadIntervalWithNull_NoError` fails with `Actual: 5`. + +The module is meant to be adapted as a regression test. Each case asserts VB6's result, which the attached VB6 project (`datepart-null-checks-interval-vb6.zip`) prints. +```vb +[TestFixture] +Module RegressionTests + + ' Controls: DatePart raises 5 for the interval with a date, and DateAdd and DateDiff + ' return Null for a Null date with the same interval. + [TestCase] + Public Sub DatePart_BadIntervalWithDate_Raises5() + Dim V As Variant + On Error Resume Next + V = DatePart("x", #1/1/2000#) + Assert.Strict.AreEqual 5&, Err.Number + End Sub + + [TestCase] + Public Sub DateAdd_BadIntervalWithNull_ReturnsNull() + Dim V As Variant = DateAdd("x", 1, Null) + Assert.Strict.IsTrue IsNull(V) + End Sub + + [TestCase] + Public Sub DateDiff_BadIntervalWithNull_ReturnsNull() + Dim V As Variant = DateDiff("x", Null, #1/1/2001#) + Assert.Strict.IsTrue IsNull(V) + End Sub + + ' The defect: DatePart raises 5. + [TestCase] + Public Sub DatePart_BadIntervalWithNull_NoError() + Dim V As Variant + On Error Resume Next + V = DatePart("x", Null) + Assert.Strict.AreEqual 0&, Err.Number + End Sub + +End Module +``` + +**Expected behavior** +`Null`, with no error, as VB6 and 64-bit VBA return. + +**Desktop:** + - OS: Windows 10 Pro 22H2 (build 19045) + - twinBASIC compiler version: BETA 1010 + +**Additional context** +Severity: low. Only an invalid interval with a `Null` date is affected; twinBASIC's behaviour is arguably stricter, but it differs from VB6 and from twinBASIC's own `DateAdd` and `DateDiff`. + +A 64-bit build gives the same results. 64-bit VBA here is VBA 7.1 (VBE7.DLL 7.01.1158) in 64-bit Microsoft 365 Access. + +What does not reproduce it: a `Null` interval, which raises 94 in both. + + + +--- + +## `DatePart("ww", ...)` of a date in year 100 before its first full week never returns with `vbFirstFullWeek`, and returns -98980 with `vbFirstFourDays` + +**Describe the bug** +January 1, 100 is a Friday, so with Sunday as the first day of the week its week belongs to year 99. `DatePart("ww", #1/1/100#, vbSunday, vbFirstFullWeek)` never returns: the program stops responding. `DatePart("ww", #1/1/100#, vbSunday, vbFirstFourDays)` returns -98980. VB6 returns 52 for both. Observed in an IDE run and in a built exe, 32-bit. + +**To Reproduce** +Steps to reproduce the behavior: +1. Open `datepart-week-year-100.twinproj` (attached as `datepart-week-year-100.zip`). +2. Run it. On BETA 1010 it prints the four lines below and stops responding at the last call. + +``` +ww of 1/1/100, vbFirstFourDays: -98980 +ww of 1/3/100, vbFirstFullWeek: 1 +ww of 1/1/2000, vbFirstFullWeek: 52 +calling DatePart("ww", #1/1/100#, vbSunday, vbFirstFullWeek) +``` + +The module: +```vb +Module Startup + + Public Sub Main() + ' Controls: the same date with vbFirstFourDays, and the first date of the first + ' full week with vbFirstFullWeek, return. + Debug.Print "ww of 1/1/100, vbFirstFourDays: " & DatePart("ww", #1/1/100#, vbSunday, vbFirstFourDays) + Debug.Print "ww of 1/3/100, vbFirstFullWeek: " & DatePart("ww", #1/3/100#, vbSunday, vbFirstFullWeek) + Debug.Print "ww of 1/1/2000, vbFirstFullWeek: " & DatePart("ww", #1/1/2000#, vbSunday, vbFirstFullWeek) + ' The defect: January 1, 100 is a Friday, so its week belongs to year 99. + Debug.Print "calling DatePart(""ww"", #1/1/100#, vbSunday, vbFirstFullWeek)" + Debug.Print "returned " & DatePart("ww", #1/1/100#, vbSunday, vbFirstFullWeek) + End Sub + +End Module +``` + +The attached VB6 project (`datepart-week-year-100-vb6.zip`) prints 52, 1, 52 and 52. + +**Expected behavior** +52 for both, the week of year 99 that the date belongs to, as VB6 and 64-bit VBA return. The first line's value is wrong too. + +**Desktop:** + - OS: Windows 10 Pro 22H2 (build 19045) + - twinBASIC compiler version: BETA 1010 + +**Additional context** +Severity: medium. A hang loses unsaved data, and only dates in the first days of year 100 reach it. + +A 64-bit build gives the same results: -98980, then the hang. 64-bit VBA, VBA 7.1 (VBE7.DLL 7.01.1158) in 64-bit Microsoft 365 Access, prints 52, 1, 52 and 52, as VB6 does. + +What does not reproduce it: January 1, 2000, whose week also belongs to the year before (52); January 3, 100, the first day of the first full week (1); `Format(#1/1/100#, "ww", vbSunday, vbFirstFullWeek)`, and `DateDiff("ww", ...)` with `vbFirstFullWeek` from January 1, 100, which return; and `DatePart("yyyy", #1/1/100#, vbSunday, vbFirstFullWeek)`. The hang also happens with `vbMonday` and `vbUseSystem` as the first day, and for January 2, 100. + + + +--- + +## `DatePart` and `DateDiff` take a *firstdayofweek* outside 0 to 7 without an error and return values that follow no rule, such as a weekday of -2 + +**Describe the bug** +Where the interval uses *firstdayofweek* ("w" and "ww" in `DatePart`, "ww" in `DateDiff`), VB6 raises 5, *Invalid procedure call or argument*, for a value outside 0 to 7. twinBASIC raises nothing, and what it returns follows no rule. For January 1, 2000, a Saturday, `DatePart("w", d, 8)` returns 7, as with `vbSunday`, `DatePart("w", d, 15)` returns 0 and `DatePart("w", d, 255)` returns -2, which are not weekdays. Over every date of 1999 to 2001: + +- a negative value gives the same results as one value from 0 to 7, as if taken modulo 7 (-1 as 6, -7 as 7); +- for `DatePart("w")`, 8 gives the same results as 1, and 9 to 32767 match no value from 0 to 7; +- for `DatePart("ww")`, 8 and 9 match 1, and 10 up match nothing; +- for `DateDiff("ww")`, 8 to 13 match 1, and 14 up match nothing. + +Observed in the compiler's test mode, 32-bit, and in a built exe. + +**To Reproduce** +Steps to reproduce the behavior: +1. Open `datepart-firstdayofweek-out-of-range.twinproj` (attached as `datepart-firstdayofweek-out-of-range.zip`). +2. Run its test cases, the module below. On BETA 1010 the three controls pass and the other five fail with `Actual: 0`. + +The module is meant to be adapted as a regression test. Each case asserts VB6's result, which the attached VB6 project (`datepart-firstdayofweek-out-of-range-vb6.zip`) prints. +```vb +[TestFixture] +Module RegressionTests + + ' Controls: the weekday with Sunday and with Saturday as the first day, and a + ' firstdayofweek of 8 with an interval that does not use it. + [TestCase] + Public Sub DatePart_W_Sunday_Is7() + Assert.Strict.AreEqual CVar(7), DatePart("w", #1/1/2000#, vbSunday) + End Sub + + [TestCase] + Public Sub DatePart_W_Saturday_Is1() + Assert.Strict.AreEqual CVar(1), DatePart("w", #1/1/2000#, vbSaturday) + End Sub + + [TestCase] + Public Sub DatePart_D_Eight_NoError() + Dim V As Variant + On Error Resume Next + V = DatePart("d", #1/1/2000#, 8) + Assert.Strict.AreEqual 0&, Err.Number + End Sub + + ' The defect: no error, and a value that follows no rule: 8 acts as 1, 9 to 13 match + ' no setting, and 255 gives a weekday of -2. + [TestCase] + Public Sub DatePart_W_Eight_Raises5() + Dim V As Variant + On Error Resume Next + V = DatePart("w", #1/1/2000#, 8) + Assert.Strict.AreEqual 5&, Err.Number + End Sub + + [TestCase] + Public Sub DatePart_W_255_Raises5() + Dim V As Variant + On Error Resume Next + V = DatePart("w", #1/1/2000#, 255) + Assert.Strict.AreEqual 5&, Err.Number + End Sub + + [TestCase] + Public Sub DatePart_W_MinusOne_Raises5() + Dim V As Variant + On Error Resume Next + V = DatePart("w", #1/1/2000#, -1) + Assert.Strict.AreEqual 5&, Err.Number + End Sub + + [TestCase] + Public Sub DatePart_WW_Eight_Raises5() + Dim V As Variant + On Error Resume Next + V = DatePart("ww", #1/1/2000#, 8) + Assert.Strict.AreEqual 5&, Err.Number + End Sub + + [TestCase] + Public Sub DateDiff_WW_Eight_Raises5() + Dim V As Variant + On Error Resume Next + V = DateDiff("ww", #1/1/2000#, #1/1/2001#, 8) + Assert.Strict.AreEqual 5&, Err.Number + End Sub + +End Module +``` + +**Expected behavior** +Error 5, as VB6 and 64-bit VBA raise. Taking the value modulo 7 throughout would be a reasonable extension too, but not the mix above. + +**Desktop:** + - OS: Windows 10 Pro 22H2 (build 19045) + - twinBASIC compiler version: BETA 1010 + +**Additional context** +Severity: medium. A wrong *firstdayofweek*, such as one read from a setting, goes unreported and gives weekdays outside 1 to 7 and week counts that are off. + +A 64-bit build gives the same results. 64-bit VBA here is VBA 7.1 (VBE7.DLL 7.01.1158) in 64-bit Microsoft 365 Access. + +What does not reproduce it: an interval that does not use *firstdayofweek* (`DatePart("d", d, 8)`, `DateDiff("w", d1, d2, 8)`), which VB6 accepts as well; a `Null` *firstdayofweek*, which raises 94 in both. A *firstweekofyear* outside 0 to 3 is reported separately. + + + +--- + +## `DatePart` takes a *firstweekofyear* outside 0 to 3 without an error, where VB6 raises error 5 + +**Describe the bug** +With the "ww" interval, VB6 raises 5, *Invalid procedure call or argument*, for a *firstweekofyear* outside 0 to 3. twinBASIC raises nothing and counts the weeks as with `vbUseSystem` or `vbFirstJan1`: over every date of 1999 to 2001, each value tried from -15 to 2147483647 gives the same results as 0 and 1, which agree on the system tested (its own setting is the week of January 1). Observed in the compiler's test mode, 32-bit, and in a built exe. + +**To Reproduce** +Steps to reproduce the behavior: +1. Open `datepart-firstweekofyear-out-of-range.twinproj` (attached as `datepart-firstweekofyear-out-of-range.zip`). +2. Run its test cases, the module below. On BETA 1010 the three controls pass and the other two fail with `Actual: 0`. + +The module is meant to be adapted as a regression test. Each case asserts VB6's result, which the attached VB6 project (`datepart-firstweekofyear-out-of-range-vb6.zip`) prints. +```vb +[TestFixture] +Module RegressionTests + + ' Controls: the week with vbFirstJan1 and vbFirstFullWeek, and a firstweekofyear of 4 + ' with an interval that does not use it. + [TestCase] + Public Sub DatePart_WW_FirstJan1_Is1() + Assert.Strict.AreEqual CVar(1), DatePart("ww", #1/1/2000#, vbSunday, vbFirstJan1) + End Sub + + [TestCase] + Public Sub DatePart_WW_FirstFullWeek_Is52() + Assert.Strict.AreEqual CVar(52), DatePart("ww", #1/1/2000#, vbSunday, vbFirstFullWeek) + End Sub + + [TestCase] + Public Sub DatePart_D_Four_NoError() + Dim V As Variant + On Error Resume Next + V = DatePart("d", #1/1/2000#, vbSunday, 4) + Assert.Strict.AreEqual 0&, Err.Number + End Sub + + ' The defect: no error; the weeks are counted as with 0 or 1. + [TestCase] + Public Sub DatePart_WW_Four_Raises5() + Dim V As Variant + On Error Resume Next + V = DatePart("ww", #1/1/2000#, vbSunday, 4) + Assert.Strict.AreEqual 5&, Err.Number + End Sub + + [TestCase] + Public Sub DatePart_WW_MinusOne_Raises5() + Dim V As Variant + On Error Resume Next + V = DatePart("ww", #1/1/2000#, vbSunday, -1) + Assert.Strict.AreEqual 5&, Err.Number + End Sub + +End Module +``` + +**Expected behavior** +Error 5, as VB6 and 64-bit VBA raise. + +**Desktop:** + - OS: Windows 10 Pro 22H2 (build 19045), system locale 1033 + - twinBASIC compiler version: BETA 1010 + +**Additional context** +Severity: low. A wrong *firstweekofyear*, such as one read from a setting, goes unreported, and the week numbers are those of another setting. + +A 64-bit build gives the same results. 64-bit VBA here is VBA 7.1 (VBE7.DLL 7.01.1158) in 64-bit Microsoft 365 Access. + +What does not reproduce it: an interval that does not use *firstweekofyear* (`DatePart("d", d, vbSunday, 4)`), which VB6 accepts as well; `DateDiff`, which ignores *firstweekofyear* in both; a `Null` *firstweekofyear*, which raises 94 in both. Not checked on a system whose own first week of the year is not the week of January 1, where 0 and 1 differ. + + + +--- + +## Refused arguments raise -2147467259, *Unspecified error*, where VB6 and VBA raise 5, 6, 9, 13 or 438 + +**Describe the bug** +Given an argument they refuse, several runtime procedures raise -2147467259 (`&H80004005`), *Unspecified error*, where VB6 and 64-bit VBA raise one of VB's own numbers: +- `LBound` and `UBound` with a *dimension* less than 1 or greater than the number of dimensions: VB raises 9, *Subscript out of range*; with a *dimension* greater than 32,767: 6, *Overflow*. +- `Switch` with an odd number of arguments: 5, *Invalid procedure call or argument*. +- `Partition` with *start* less than 0, *stop* not greater than *start*, or *interval* less than 1: 5; with a string that is not a number: 13, *Type mismatch*; with a value outside the `LongLong` range: 6. +- `Environ` with a *number* less than 1, a position no entry has, or a zero-length name: 5; with a *number* greater than 32,767: 6. +- `GetObject()` with no arguments: 13. +- `CallByName` with a *calltype* that is not a `VbCallType` value: 438, *Object doesn't support this property or method*; with an object of `Nothing`: 5. +- `SaveSetting`, `GetSetting`, `GetAllSettings` and `DeleteSetting` with a zero-length application, section or key name, and `DeleteSetting` of an application, section or key that does not exist: 5. + +It is the pattern of #2544 (`ReDim` with inverted bounds raises -2147467259 where VB6 raises 9). Observed in the compiler's test mode and in built exes, 32-bit and 64-bit. + +**To Reproduce** +Steps to reproduce the behavior: +1. Open `argument-errors-unspecified-error.twinproj` (attached as `argument-errors-unspecified-error.zip`). +2. Run its test cases, the module below. On BETA 1010 the three controls pass and the other seventeen fail, each with `Err.Number` -2147467259. + +The module is meant to be adapted as a regression test. Each case asserts VB6's number, which the attached VB6 project (`argument-errors-unspecified-error-vb6.zip`) prints; 64-bit VBA raises the same numbers. No case writes to the registry: the registry procedures are given names that do not exist. +```vb +' Regression tests: a procedure that refuses an argument raises the number VB6 and 64-bit VBA +' raise (the VB6 project beside this one prints them), not -2147467259, Unspecified error. +' No case writes to the registry: GetSetting and DeleteSetting are given names that do not +' exist. tbrun --tests runs each case and prints PASS or FAIL. +[TestFixture] +Module RegressionTests + + ' Controls: errors twinBASIC already raises with VB's numbers. + [TestCase] + Public Sub LBound_UnsizedArray_Raises9() + Dim A() As Long + Dim N As Long + On Error Resume Next + N = LBound(A) + Assert.Strict.AreEqual 9&, Err.Number + End Sub + + [TestCase] + Public Sub QBColor_Sixteen_Raises5() + Dim N As Long + On Error Resume Next + N = QBColor(16) + Assert.Strict.AreEqual 5&, Err.Number + End Sub + + [TestCase] + Public Sub Environ_Null_Raises94() + Dim S As String + On Error Resume Next + S = Environ(Null) + Assert.Strict.AreEqual 94&, Err.Number + End Sub + + ' The defect: each of these raises -2147467259. + [TestCase] + Public Sub LBound_DimensionZero_Raises9() + Dim A(1 To 3) As Long + Dim N As Long + On Error Resume Next + N = LBound(A, 0) + Assert.Strict.AreEqual 9&, Err.Number + End Sub + + [TestCase] + Public Sub UBound_DimensionPastRank_Raises9() + Dim A(1 To 3) As Long + Dim N As Long + On Error Resume Next + N = UBound(A, 2) + Assert.Strict.AreEqual 9&, Err.Number + End Sub + + [TestCase] + Public Sub LBound_Dimension32768_Raises6() + Dim A(1 To 3) As Long + Dim N As Long + On Error Resume Next + N = LBound(A, 32768) + Assert.Strict.AreEqual 6&, Err.Number + End Sub + + [TestCase] + Public Sub Switch_OddArguments_Raises5() + Dim V As Variant + On Error Resume Next + V = Switch(True, 1, False) + Assert.Strict.AreEqual 5&, Err.Number + End Sub + + [TestCase] + Public Sub Partition_NegativeStart_Raises5() + Dim V As Variant + On Error Resume Next + V = Partition(5, -1, 10, 1) + Assert.Strict.AreEqual 5&, Err.Number + End Sub + + [TestCase] + Public Sub Partition_IntervalZero_Raises5() + Dim V As Variant + On Error Resume Next + V = Partition(5, 0, 10, 0) + Assert.Strict.AreEqual 5&, Err.Number + End Sub + + [TestCase] + Public Sub Partition_StringNumber_Raises13() + Dim V As Variant + On Error Resume Next + V = Partition("abc", 0, 10, 1) + Assert.Strict.AreEqual 13&, Err.Number + End Sub + + [TestCase] + Public Sub Partition_BeyondLongLong_Raises6() + Dim V As Variant + On Error Resume Next + V = Partition(1E+20, 0, 10, 1) + Assert.Strict.AreEqual 6&, Err.Number + End Sub + + [TestCase] + Public Sub Environ_Zero_Raises5() + Dim S As String + On Error Resume Next + S = Environ(0) + Assert.Strict.AreEqual 5&, Err.Number + End Sub + + [TestCase] + Public Sub Environ_PositionWithNoEntry_Raises5() + Dim S As String + On Error Resume Next + S = Environ(32767) + Assert.Strict.AreEqual 5&, Err.Number + End Sub + + [TestCase] + Public Sub Environ_EmptyName_Raises5() + Dim S As String + On Error Resume Next + S = Environ("") + Assert.Strict.AreEqual 5&, Err.Number + End Sub + + [TestCase] + Public Sub Environ_Above32767_Raises6() + Dim S As String + On Error Resume Next + S = Environ(32768) + Assert.Strict.AreEqual 6&, Err.Number + End Sub + + [TestCase] + Public Sub GetObject_NoArguments_Raises13() + Dim O As Object + On Error Resume Next + Set O = GetObject() + Assert.Strict.AreEqual 13&, Err.Number + End Sub + + [TestCase] + Public Sub CallByName_CallTypeZero_Raises438() + Dim D As Object + Dim V As Variant + Set D = CreateObject("Scripting.Dictionary") + On Error Resume Next + V = CallByName(D, "Count", 0) + Assert.Strict.AreEqual 438&, Err.Number + End Sub + + [TestCase] + Public Sub CallByName_Nothing_Raises5() + Dim D As Object + Dim V As Variant + On Error Resume Next + V = CallByName(D, "Count", VbGet) + Assert.Strict.AreEqual 5&, Err.Number + End Sub + + [TestCase] + Public Sub GetSetting_EmptyAppName_Raises5() + Dim S As String + On Error Resume Next + S = GetSetting("", "Section", "Key") + Assert.Strict.AreEqual 5&, Err.Number + End Sub + + [TestCase] + Public Sub DeleteSetting_MissingApplication_Raises5() + On Error Resume Next + DeleteSetting "NoSuchApplication-argument-errors-unspecified-error" + Assert.Strict.AreEqual 5&, Err.Number + End Sub + +End Module +``` + +**Expected behavior** +VB's own number in each case, with its description, as VB6 and 64-bit VBA raise. + +**Desktop:** + - OS: Windows 10 Pro 22H2 (build 19045) + - twinBASIC compiler version: BETA 1010 + +**Additional context** +Severity: low. A handler that tests `Err.Number` for the number VB6 raises misses these, and *Unspecified error* tells a user nothing about what was wrong. + +A 64-bit build gives the same results. 64-bit VBA here is VBA 7.1 (VBE7.DLL 7.01.1158) in 64-bit Microsoft 365 Access. + +What does not reproduce it: the errors these procedures already raise with VB's numbers, such as `LBound` of an array not yet sized (9), `QBColor` outside 0 to 15 (5), `RGB` with a negative component (5), and `Null` for any of the arguments above (94). + + + +--- + +## `CreateObject` and `GetObject` raise the COM `HRESULT` where VB6 and VBA raise 429 or 432 + +**Describe the bug** +When `CreateObject` or `GetObject` cannot create or find the object, twinBASIC raises the `HRESULT` COM returned, where VB6 and 64-bit VBA raise 429, *ActiveX component can't create object*, or 432, *File name or class name not found during Automation operation*: + +| Call | VB6 and 64-bit VBA | twinBASIC | +|---|---|---| +| `CreateObject("No.Such.Class")`, a CLSID in braces without `new:`, a number | 429 | -2147221020 (`MK_E_SYNTAX`, *Invalid syntax*) | +| `CreateObject("new:{00000000-0000-0000-0000-000000000000}")` | 429 | -2147221164 (`REGDB_E_CLASSNOTREG`, *Class not registered*) | +| `CreateObject("new:not-a-guid")` | 429 | -2147221005 (`CO_E_CLASSSTRING`, *Invalid class string*) | +| `CreateObject("")` | 429 | 5 | +| `GetObject("C:\no\such\file.xyz")` | 432 | -2147221014 (`MK_E_CANTOPENFILE`, *Moniker cannot open file*) | +| `GetObject("C:\no\such\file.xyz", "Scripting.Dictionary")` | 432 | -2147467262 (`E_NOINTERFACE`, *No such interface supported*) | +| `GetObject(, "No.Such.Class")`, `GetObject("", "No.Such.Class")` | 429 | -2147221005 | +| `GetObject(, "Scripting.Dictionary")`, no instance running | 429 | -2147221021 (`MK_E_UNAVAILABLE`, *Operation unavailable*) | + +Observed in the compiler's test mode and in built exes, 32-bit and 64-bit. + +**To Reproduce** +Steps to reproduce the behavior: +1. Open `createobject-getobject-raw-hresults.twinproj` (attached as `createobject-getobject-raw-hresults.zip`). +2. Run its test cases, the module below. On BETA 1010 the three controls pass and the other eight fail, each with the number in the table. + +The module is meant to be adapted as a regression test. Each case asserts VB6's number, which the attached VB6 project (`createobject-getobject-raw-hresults-vb6.zip`) prints; 64-bit VBA raises the same numbers. +```vb +' Regression tests: CreateObject and GetObject raise 429, ActiveX component can't create +' object, or 432, File name or class name not found during Automation operation, as VB6 and +' 64-bit VBA do (the VB6 project beside this one prints them), not the HRESULT COM returned. +' tbrun --tests runs each case and prints PASS or FAIL. +[TestFixture] +Module RegressionTests + + ' Controls: a class that exists is created, and Null raises 94. + [TestCase] + Public Sub CreateObject_RegisteredClass_Creates() + Dim O As Object + On Error Resume Next + Set O = CreateObject("Scripting.Dictionary") + Assert.Strict.AreEqual 0&, Err.Number + End Sub + + [TestCase] + Public Sub CreateObject_Null_Raises94() + Dim O As Object + On Error Resume Next + Set O = CreateObject(Null) + Assert.Strict.AreEqual 94&, Err.Number + End Sub + + [TestCase] + Public Sub GetObject_EmptyPathWithClass_Creates() + Dim O As Object + On Error Resume Next + Set O = GetObject("", "Scripting.Dictionary") + Assert.Strict.AreEqual 0&, Err.Number + End Sub + + ' The defect: each of these raises the HRESULT, or 5. + [TestCase] + Public Sub CreateObject_UnknownClass_Raises429() + Dim O As Object + On Error Resume Next + Set O = CreateObject("No.Such.Class") + Assert.Strict.AreEqual 429&, Err.Number + End Sub + + [TestCase] + Public Sub CreateObject_UnregisteredClsid_Raises429() + Dim O As Object + On Error Resume Next + Set O = CreateObject("new:{00000000-0000-0000-0000-000000000000}") + Assert.Strict.AreEqual 429&, Err.Number + End Sub + + [TestCase] + Public Sub CreateObject_MalformedClsid_Raises429() + Dim O As Object + On Error Resume Next + Set O = CreateObject("new:not-a-guid") + Assert.Strict.AreEqual 429&, Err.Number + End Sub + + [TestCase] + Public Sub CreateObject_EmptyClass_Raises429() + Dim O As Object + On Error Resume Next + Set O = CreateObject("") + Assert.Strict.AreEqual 429&, Err.Number + End Sub + + [TestCase] + Public Sub GetObject_MissingFile_Raises432() + Dim O As Object + On Error Resume Next + Set O = GetObject("C:\no\such\file.xyz") + Assert.Strict.AreEqual 432&, Err.Number + End Sub + + [TestCase] + Public Sub GetObject_MissingFileWithClass_Raises432() + Dim O As Object + On Error Resume Next + Set O = GetObject("C:\no\such\file.xyz", "Scripting.Dictionary") + Assert.Strict.AreEqual 432&, Err.Number + End Sub + + [TestCase] + Public Sub GetObject_UnknownClass_Raises429() + Dim O As Object + On Error Resume Next + Set O = GetObject(, "No.Such.Class") + Assert.Strict.AreEqual 429&, Err.Number + End Sub + + [TestCase] + Public Sub GetObject_NoRunningInstance_Raises429() + Dim O As Object + On Error Resume Next + Set O = GetObject(, "Scripting.Dictionary") + Assert.Strict.AreEqual 429&, Err.Number + End Sub + +End Module +``` + +**Expected behavior** +429 or 432, as in the table's VB6 column, as VB6 and 64-bit VBA raise. + +**Desktop:** + - OS: Windows 10 Pro 22H2 (build 19045) + - twinBASIC compiler version: BETA 1010 + +**Additional context** +Severity: low. A handler written for VB6 or VBA that tests for 429, the usual check that a component is installed, misses every one of these. + +A 64-bit build gives the same results. 64-bit VBA here is VBA 7.1 (VBE7.DLL 7.01.1158) in 64-bit Microsoft 365 Access. + +What does not reproduce it: a `servername` that cannot be reached (-2147023174, *The RPC server is unavailable.*, in all four), and a `pathname` that is not a moniker Windows can read (`GetObject("notaprogid:xyz")`, -2147221020 in all four). VB6 gives the `GetObject` of a missing file with a class 432 with the description *Class not registered*. + + + +--- + +## A COM object's own error reaches `CallByName` and a late-bound property assignment as &H80020009, *Exception occurred.* + +**Describe the bug** +When a COM object such as a `Scripting.Dictionary` raises an error of its own, a late-bound method call gets the object's number, as in VB6: `d.Add "x", 2` on a dictionary that holds `"x"` raises 457, *This key is already associated with an element of this collection*. The same call through `CallByName`, with any call type, and a late-bound property assignment, raise -2147352567 (`&H80020009`, `DISP_E_EXCEPTION`), *Exception occurred.*, instead: +- `CallByName d, "Add", VbMethod, "x", 2`: VB6 and 64-bit VBA raise 457. +- `CallByName d, "Remove", VbMethod, "nope"`: VB6 and 64-bit VBA raise 32811, which the late-bound statement `d.Remove "nope"` raises in twinBASIC too. +- `d.CompareMode = 1` on a dictionary that has items, and `CallByName d, "CompareMode", VbLet, 1`: VB6 and 64-bit VBA raise 5. + +Also, `CallByName(d, "", VbGet)`, with a zero-length name, raises 438, *Object doesn't support this property or method*, where VB6 and 64-bit VBA raise 450, *Wrong number of arguments or invalid property assignment*. + +Related: the queued entry *An error raised with a constant number, in a member called late-bound, reaches the caller as &H80020009, Exception occurred.* is the same number for a twinBASIC member's `Err.Raise`; this one is a COM object's error, which a late-bound method statement already gets right. The queued entry *A failure code from a late-bound call is raised as the HRESULT* covers `CallByName` with the wrong number of arguments (450) and arguments given to a property that takes none (451). Observed in the compiler's test mode and in built exes, 32-bit and 64-bit. + +**To Reproduce** +Steps to reproduce the behavior: +1. Open `callbyname-dispatch-hresults.twinproj` (attached as `callbyname-dispatch-hresults.zip`). +2. Run its test cases, the module below. On BETA 1010 the three controls pass and the other five fail: four with -2147352567 and the empty name with 438. + +The module is meant to be adapted as a regression test. Each case asserts VB6's number, which the attached VB6 project (`callbyname-dispatch-hresults-vb6.zip`) prints; 64-bit VBA raises the same numbers. +```vb +' Regression tests: an error a COM object raises reaches CallByName, and a late-bound property +' assignment, as the object's own number, as it reaches a late-bound method call, and as VB6 +' and 64-bit VBA give it (the VB6 project beside this one prints them); CallByName with an empty +' name raises 450. The object is a Scripting.Dictionary. tbrun --tests runs each case and prints +' PASS or FAIL. +[TestFixture] +Module RegressionTests + + Private Function NewDictionary() As Object + Set NewDictionary = CreateObject("Scripting.Dictionary") + End Function + + ' Controls: a late-bound method call gets the object's own number, and CallByName of a + ' name the object does not have raises 438. + [TestCase] + Public Sub LateBound_AddExistingKey_Raises457() + Dim D As Object + Set D = NewDictionary() + D.Add "x", 1 + On Error Resume Next + D.Add "x", 2 + Assert.Strict.AreEqual 457&, Err.Number + End Sub + + [TestCase] + Public Sub LateBound_RemoveMissingKey_Raises32811() + Dim D As Object + Set D = NewDictionary() + On Error Resume Next + D.Remove "nope" + Assert.Strict.AreEqual 32811&, Err.Number + End Sub + + [TestCase] + Public Sub CallByName_UnknownName_Raises438() + Dim D As Object + Dim V As Variant + Set D = NewDictionary() + On Error Resume Next + V = CallByName(D, "Nope", VbGet) + Assert.Strict.AreEqual 438&, Err.Number + End Sub + + ' The defect: the object's own error arrives as -2147352567, Exception occurred. + [TestCase] + Public Sub CallByName_AddExistingKey_Raises457() + Dim D As Object + Set D = NewDictionary() + D.Add "x", 1 + On Error Resume Next + CallByName D, "Add", VbMethod, "x", 2 + Assert.Strict.AreEqual 457&, Err.Number + End Sub + + [TestCase] + Public Sub CallByName_RemoveMissingKey_Raises32811() + Dim D As Object + Set D = NewDictionary() + On Error Resume Next + CallByName D, "Remove", VbMethod, "nope" + Assert.Strict.AreEqual 32811&, Err.Number + End Sub + + [TestCase] + Public Sub LateBound_CompareModeAfterAdd_Raises5() + Dim D As Object + Set D = NewDictionary() + D.Add "x", 1 + On Error Resume Next + D.CompareMode = 1 + Assert.Strict.AreEqual 5&, Err.Number + End Sub + + [TestCase] + Public Sub CallByName_CompareModeAfterAdd_Raises5() + Dim D As Object + Set D = NewDictionary() + D.Add "x", 1 + On Error Resume Next + CallByName D, "CompareMode", VbLet, 1 + Assert.Strict.AreEqual 5&, Err.Number + End Sub + + ' The defect: an empty name raises 438, where VB6 raises 450. + [TestCase] + Public Sub CallByName_EmptyName_Raises450() + Dim D As Object + Dim V As Variant + Set D = NewDictionary() + On Error Resume Next + V = CallByName(D, "", VbGet) + Assert.Strict.AreEqual 450&, Err.Number + End Sub + +End Module +``` + +**Expected behavior** +The object's own error number, as a late-bound method statement already gives it, and as VB6 and 64-bit VBA give it through `CallByName` and a late-bound property assignment; 450 for an empty name. + +**Desktop:** + - OS: Windows 10 Pro 22H2 (build 19045) + - twinBASIC compiler version: BETA 1010 + +**Additional context** +Severity: low. Code that calls an object by name, or sets its properties late-bound, cannot tell its errors apart: every one is *Exception occurred.* + +A 64-bit build gives the same results. 64-bit VBA here is VBA 7.1 (VBE7.DLL 7.01.1158) in 64-bit Microsoft 365 Access. + +What does not reproduce it: a late-bound method statement (`d.Add "x", 2` raises 457, `d.Remove "nope"` 32811); `CallByName` of a name the object does not have, which raises 438 in all four. + + + +--- + +## `RaiseEventByName` and `RaiseEventByName2` end the program for an object that declares no events, and raise an error for an event no handler is attached to + +**Describe the bug** +Two defects of the same two functions: +- Given an object that declares no events, such as a `Collection` or a `Scripting.Dictionary`, `RaiseEventByName` and `RaiseEventByName2` end the program with an access violation (`&HC0000005`), with no error a handler can catch. `Nothing` raises 91, as it should. +- Given an object that declares the event, with no handler attached to it, both raise -2147467259 (`&H80004005`), *Unspecified error*. The `RaiseEvent` statement inside the class raises nothing for an event no handler is attached to. With a handler attached, both functions call it. + +The crash was observed in the compiler's test mode, where the access violation ends the case and not the run, and in a built exe, where it ends the program; the error without a handler in the test mode; each 32-bit and 64-bit. + +**To Reproduce** +Steps to reproduce the behavior: +1. Open `raiseeventbyname-no-events-crash.twinproj` (attached as `raiseeventbyname-no-events-crash.zip`). It holds the class `EventSource`, which declares one event, the class `Listener`, which handles it, and the test module. +2. Run its test cases. On BETA 1010 the five controls pass, the two cases without a handler fail with -2147467259, and the three cases with a `Collection` or a `Dictionary` fail with `NATIVE EXCEPTION: ACCESS_VIOLATION`. + +The modules are meant to be adapted as regression tests. VB6 has no `RaiseEventByName`, so the crash cases assert only that some error is raised. +```vb +' A class that declares one event, for the controls and the no-handler cases. Raise is what +' the RaiseEvent statement does with the same event: with no handler attached, nothing. +Class EventSource + + Public Event Changed(ByVal Value As Long) + + Public Sub Raise(ByVal Value As Long) + RaiseEvent Changed(Value) + End Sub + +End Class +``` +```vb +' Handles EventSource's event and counts the calls, for the control that raises it with a +' handler attached. +Class Listener + + Private WithEvents Source As EventSource + Public Calls As Long + Public LastValue As Long + + Public Sub Attach(ByVal S As EventSource) + Set Source = S + End Sub + + Private Sub Source_Changed(ByVal Value As Long) + Calls = Calls + 1 + LastValue = Value + End Sub + +End Class +``` +```vb +' Regression tests: RaiseEventByName and RaiseEventByName2 raise an error, and do not end the +' program, for an object that declares no events; and an event with no handler attached is +' raised with no error, as the RaiseEvent statement raises it. tbrun --tests runs each case in +' the compiler's test mode, where an access violation ends the case and not the run, and prints +' PASS or FAIL. The crash cases assert only that some error is raised: VB6 has no +' RaiseEventByName to say which. +[TestFixture] +Module RegressionTests + + ' Controls: Nothing raises 91, a declared event with a handler attached calls it, and a name + ' the object does not declare raises an error. + [TestCase] + Public Sub RaiseEventByName_Nothing_Raises91() + Dim O As Object + On Error Resume Next + RaiseEventByName O, "Changed", Array(1) + Assert.Strict.AreEqual 91&, Err.Number + End Sub + + [TestCase] + Public Sub RaiseEventByName_DeclaredEventWithHandler_CallsIt() + Dim S As New EventSource + Dim L As New Listener + L.Attach S + On Error Resume Next + RaiseEventByName S, "Changed", Array(7) + Assert.Strict.AreEqual 0&, Err.Number + Assert.Strict.AreEqual 1&, L.Calls + Assert.Strict.AreEqual 7&, L.LastValue + End Sub + + [TestCase] + Public Sub RaiseEventByName2_DeclaredEventWithHandler_CallsIt() + Dim S As New EventSource + Dim L As New Listener + L.Attach S + On Error Resume Next + RaiseEventByName2 S, "Changed", 7 + Assert.Strict.AreEqual 0&, Err.Number + Assert.Strict.AreEqual 1&, L.Calls + End Sub + + [TestCase] + Public Sub RaiseEvent_NoHandler_DoesNothing() + Dim S As New EventSource + On Error Resume Next + S.Raise 1 + Assert.Strict.AreEqual 0&, Err.Number + End Sub + + [TestCase] + Public Sub RaiseEventByName_UndeclaredEventName_RaisesAnError() + Dim S As New EventSource + Dim L As New Listener + L.Attach S + On Error Resume Next + RaiseEventByName S, "Nope", Array(1) + Assert.Strict.AreNotEqual 0&, Err.Number + End Sub + + ' The defect: a declared event with no handler attached raises -2147467259, where + ' RaiseEvent Changed(1) inside the class does nothing. + [TestCase] + Public Sub RaiseEventByName_DeclaredEventNoHandler_Succeeds() + Dim O As Object + Set O = New EventSource + On Error Resume Next + RaiseEventByName O, "Changed", Array(1) + Assert.Strict.AreEqual 0&, Err.Number + End Sub + + [TestCase] + Public Sub RaiseEventByName2_DeclaredEventNoHandler_Succeeds() + Dim O As Object + Set O = New EventSource + On Error Resume Next + RaiseEventByName2 O, "Changed", 1 + Assert.Strict.AreEqual 0&, Err.Number + End Sub + + ' The defect: an object that declares no events ends the program. + [TestCase] + Public Sub RaiseEventByName_Collection_RaisesAnError() + Dim O As Object + Set O = New Collection + On Error Resume Next + RaiseEventByName O, "Changed", Array(1) + Assert.Strict.AreNotEqual 0&, Err.Number + End Sub + + [TestCase] + Public Sub RaiseEventByName_Dictionary_RaisesAnError() + Dim O As Object + Set O = CreateObject("Scripting.Dictionary") + On Error Resume Next + RaiseEventByName O, "Changed", Array(1) + Assert.Strict.AreNotEqual 0&, Err.Number + End Sub + + [TestCase] + Public Sub RaiseEventByName2_Collection_RaisesAnError() + Dim O As Object + Set O = New Collection + On Error Resume Next + RaiseEventByName2 O, "Changed", 1 + Assert.Strict.AreNotEqual 0&, Err.Number + End Sub + +End Module +``` + +**Expected behavior** +An error for an object that declares no events, as for a name the object does not declare (which raises an error today), and no error for an event no handler is attached to, as `RaiseEvent` does. + +**Desktop:** + - OS: Windows 10 Pro 22H2 (build 19045) + - twinBASIC compiler version: BETA 1010 + +**Additional context** +Severity: high for the crash: a program loses its unsaved data, and an error handler cannot prevent it. Low for the error without a handler, though it makes the function unusable for an event a caller may or may not have handled. + +A 64-bit build gives the same results. + + diff --git a/WIP.Errors.md b/WIP.Errors.md index cfec70d2e..6c1e731b7 100644 --- a/WIP.Errors.md +++ b/WIP.Errors.md @@ -7,20 +7,65 @@ exactly that. Like WIP.md, this file is not rendered through tbdocs. -## Where things stand (2026-10-21) - -**Batches 0, 1 and 2 are done, reviewed by the owner, and committed on `staging`** (not -pushed; last commit `d3f3e514`). The tooling, the central page and every page of the VBA -`Strings` module are in place: 339 cases in `errors/VBA.Strings.yml`, all agreeing in -twinBASIC 32-bit and VB6 on BETA 1010, and a Common Errors section on 28 of the module's 30 -pages (Len raises nothing; the index documents nothing). No row is `bug: pending`. Every -difference from VB6 found so far is queued in BUGS-TO-REPORT.md with a reproducer under -`bugs/`, for the owner to file: `redim-inverted-bounds-error`, `database-compare-no-error`, -`instr-invalid-lcid-empty-string`, `string-null-character`, `strconv-east-asian-lcid`. - -**Next: batch 3, VBA `Conversion`** (or any other batch: they are independent). Follow *How a -batch runs* below; the style is settled, so a batch no longer needs the owner's review of its -first pages, only of the questions it raises. +## Where things stand (2026-10-22) + +**Batches 0, 1 and 2 are done, reviewed by the owner, and committed on `staging`.** The +tooling, the central page and every page of the VBA `Strings` module are in place: 339 cases +in `errors/VBA.Strings.yml`, and a Common Errors section on 28 of the module's 30 pages. + +**Batch 3, VBA `Conversion`, is done and committed on `staging`** (not pushed; 2026-10-21): +299 cases in `errors/VBA.Conversion.yml`, all agreeing with the data in twinBASIC 32-bit and +VB6 on BETA 1010, and a section on 23 of the module's 28 pages (CVar, Fix, Int and Nz raise +nothing of their own; the index documents nothing). No row is `bug: pending`, and no question +is open: the owner settled the last one (the width of a negative **Double**'s `Hex`, an +extension) on 2026-10-21. + +Every difference from VB6 found so far has a reproducer. Filed upstream, under `bugs/filed/` +(2026-10-22): `redim-inverted-bounds-error`, `string-null-character`, and from batch 3 +`val-suffix-overflow-no-error`, `error-function-above-65535`, +and `cdate-string-decimal-out-of-range`. (`hex-double-above-2-to-62` was withdrawn on +2026-10-22, owner: 64-bit VBA does the same; its reproducer is deleted.) +Still queued in BUGS-TO-REPORT.md, under `bugs/`: `database-compare-no-error`, +`instr-invalid-lcid-empty-string` and `strconv-east-asian-lcid`. Batch 3 also cites two +entries queued before it, `date-out-of-range-no-error` and `clnglng-2-to-63-no-overflow`, +whose HTML comments now name the Conversion pages too. + +**Batch 4, VBA `Math`, is done and committed on `staging`** (not pushed; 2026-10-22): 132 +cases in `errors/VBA.Math.yml`, all agreeing with the data in twinBASIC 32-bit, twinBASIC +64-bit and VB6 on BETA 1010, and a section on 15 of the module's 17 pages (ByteSwap raises +nothing; the index documents nothing). The owner ruled each difference a bug, and the three +are filed: `abs-minimum-widens-type` (twinbasic#2541), `trig-above-2-to-63` (Cos, Sin and Tan +in one report, #2539) and `round-null-negative-digits` (#2540). No row is `bug: pending` and no question is open. + +**Batch 5, VBA `DateTime`, is done and committed on `staging`** (not pushed; 2026-10-22): +328 cases in `errors/VBA.DateTime.yml`, all agreeing with the data in twinBASIC 32-bit and +VB6 on BETA 1010, and a section on 17 of the module's 20 pages (Now and Timer raise nothing; +the index documents nothing). Seven differences are queued in BUGS-TO-REPORT.md, under `bugs/`: +`date-time-set-errors`, `dateadd-year-below-100`, `dateadd-date-out-of-range-overflow`, +`datepart-null-checks-interval`, `datepart-firstdayofweek-out-of-range`, +`datepart-firstweekofyear-out-of-range` and +`datepart-week-year-100` (a hang, so a WARNING by hand and not in the data). The owner +answered batch 5's questions on 2026-10-22 (*Batch 5 findings*). No row is `bug: pending` +and no question is open. + +**Four runtimes: 64-bit twinBASIC answers to 64-bit VBA** (owner, 2026-10-22, after fafalone +pointed it out). `errors.mjs run` and `messages` now measure twinBASIC 32-bit (`tb`), twinBASIC +64-bit (`win64`), VB6 (`vb6`) and 64-bit VBA (`vba64`, run in 64-bit Access: `scripts/lib/vba.mjs`) +by default, for every file. Batches 2 to 5 and `Core.yml` were re-checked on BETA 1010: **all +1,109 cases agree with the data in all four runtimes** (1,005 of them run in 64-bit VBA), and +`messages` agrees in all four. See *The 64-bit VBA re-check* below. + +The 64-bit VBA results are in the queued entries of BUGS-TO-REPORT.md (2026-10-22): nothing +of the re-check is left. + +**Batch 6a is done** (2026-10-22), VBA `Information` and the non-interactive procedures of +`Interaction` (the owner split batch 6, 2026-10-22; 6b, the interactive ones, comes after). +302 cases, all agreeing in all four runtimes; sections on 27 pages; the owner's two answers +applied; four new entries queued in BUGS-TO-REPORT.md, and `DISP_E_NOTACOLLECTION` added to +`latebound-hresult-mapping` (*Batch 6a findings*). No row is `bug: pending`. + +**Next: batch 6b**, `Interaction`'s interactive procedures (the batch table's row says what a +probe may and may not call). ## Owner's decisions @@ -30,8 +75,11 @@ first pages, only of the questions it raises. - **Measured, not copied.** Every row is observed in twinBASIC and in VB6. Microsoft's pages are a source of cases to try, never of answers. A statement about VB6 names only what was run in VB6 (a `Long` array, not "any typed array"). -- **Runtimes.** twinBASIC 32-bit and VB6 for everything; twinBASIC 64-bit as well for the - operators and the `Math` module only. +- **Runtimes** (replaced 2026-10-22). Four, for everything: twinBASIC 32-bit, compared with + VB6, and twinBASIC 64-bit, compared with **64-bit VBA, whose behaviour must be respected** + (owner): a difference between twinBASIC 64-bit and 64-bit VBA is a bug, exactly as one from + VB6 is, with a NOTE and a queued reproducer. (Before: twinBASIC 64-bit for operators and + `Math` only, and no VBA.) - **Scope.** `docs/Reference/Default/` (VBA, VB, VBRUN), `docs/Reference/Core/` (statements and operators) and `docs/Reference/Built-In/`. Not `Contributed/`. - **Existing error prose moves into the tables.** A page that states an error in a sentence @@ -43,8 +91,14 @@ first pages, only of the questions it raises. [BUGS-TO-REPORT.md](BUGS-TO-REPORT.md). Where VB6 has no counterpart (LongLong, twinBASIC additions), there is nothing to compare and nothing to file on that ground. **Ask the owner when twinBASIC accepts what VB6 refuses**: it may be an extension (2026-10-21: `Join` and - `Filter` of a `Long` or `Double` array are; a NOTE in the page's prose, twinBASIC-only quiet - cases, no `vb6:` row). + `Filter` of a `Long` or `Double` array are, and so are `Hex` and `Oct` of any number in the + `LongLong` range, with a negative non-integer type in the `LongLong` form; a NOTE in the + page's prose, twinBASIC-only quiet cases, no `vb6:` row). The owner also ruled, for batch 3, + that `Val` ignoring a suffix's range, `Error$` above 65535 and `CDate` out of range are bugs. +- **An error that only the test setup raises is not listed** (2026-10-22): 70 when the probe + lacks the privilege to set the clock documents the setup, not twinBASIC. Nor is a NOTE + written for it. An extension is decided the same way for every argument (2026-10-22): + it may be one where its behaviour makes sense and has no bugs; otherwise it is a bug. - **Content that twinBASIC and VB6 both contradict is the page's fault** (2026-10-21): it is commented out, not deleted, as ``, outside any table, so the VBA text is kept for docs that will one day switch between VB6, VBA and twinBASIC @@ -118,7 +172,8 @@ What `errors.mjs messages` found (BETA 1010): these the two messages differ, and the file says `{ raised, error }`. The sections show `Err.Description` (owner: settled); showing `Error$(n)` instead would be a one-line change in `renderSection` (`.raised` to `.error`). -- **Differences from VB6 that batch 8 (`ErrObject`) should take up**, none queued yet: +- **Differences from VB6 that batch 8 (`ErrObject`) should take up**, none queued yet (batch 3 + queued `Error$` above 65535, `error-function-above-65535`): `Err.Raise n` of a number from 1 to 65535 without a message of its own leaves `Err.Description` empty in twinBASIC below 747, and from 747 up gives the Windows system message of that number (*Page fault was a transition fault.* for 747); VB6 gives @@ -139,10 +194,12 @@ Their reference is on the Tools pages: - `errors/` --- the data: one YAML file per area (`VBA.Strings.yml`, `Core.yml`, ...) and `messages.yml`, each error number's message. +- `errors/harnesses/.bas` --- the harnesses (*Harnesses*, below). - `node scripts/errors.mjs run [] [--only ]` --- builds one probe from the data - per runtime, runs it, and compares each case with the data. twinBASIC 32-bit and VB6 by - default, win64 for a file that says `win64: true` or with `--runtime win64`. By hand only: - it needs twinBASIC and VB6. A run of the whole Strings file takes a few minutes. A case a + per runtime, runs it, and compares each case with the data. All four runtimes by default + (`--runtime tb|win64|vb6|vba64`, repeatable, picks some). By hand only: it needs twinBASIC, + VB6 and 64-bit Office. A run of every file in all four takes a few minutes; one page about 15 s. + `--only` is a plain regex (a leading `/` is read as a delimiter: write `Mid\.md$`, not `/Mid\.md`). A case a compiler refuses is reported and dropped, and the probe rebuilt. `generate ` writes the probes without running them; `--keep` keeps the ones `run` built. - `node scripts/errors.mjs write` --- writes each page's section and the central page's index. @@ -152,7 +209,20 @@ Their reference is on the Tools pages: shortcut. No runtime needed. A data problem makes `run` exit 2 with only "the data has problems"; `check_errors.mjs` names it. - `scripts/lib/errors-data.mjs` --- what the two share (`renderSection`, `renderIndex`, - `errorAnchor`, `CENTRAL_SHORTCUT`, ...). + `errorAnchor`, `CENTRAL_SHORTCUT`, `RUNTIMES`, `expected`, `runsIn`, ...). +- `scripts/lib/vba.mjs` --- `VbaSession`, 64-bit VBA. Its opening comment says why each part + is as it is; in short: VBA has no host but Office (VBE7.DLL cannot be hosted without the + vendor-only SDK); Access, not Excel, because Excel needs "Trust access to the VBA project + object model", a security setting that is the user's; a PowerShell driver (as + `-EncodedCommand`) started by `launchOnDesktop`, so Access's windows are on a private desktop; + **one Access for the whole run** (owner: the overhead), taking jobs from `job.txt`, each a new + database in its own folder, the module imported through the VBE, `Application.Run "Main"`, the + module removed again (else closing the database asks to save it) and the database closed. + A compile error is a VBE box that Run waits on; answering it does not free Run (the VBE stays in + break mode), so any box ends that Access by its pid and the next job starts another (about 2 s). + VBA compiles a Sub when it is first called, so the refused case is the first that did not + report; the vba dialect's Main reports `start` first, so an error in the declarations is told + apart. `findAccess64` reads Access.Application's LocalServer32 and checks the exe is x64. How a probe runs a case: a `Private Sub C()` of its own, `Dim Result As Variant`, the case's `decl` lines, `On Error Resume Next`, its `setup` lines (an error there is reported as @@ -160,14 +230,16 @@ a setup failure, not as the case's result), then `Result = ` or its `run` `case `. twinBASIC: a tree for `tbrun --exe`, module `Probe` with `Sub Main` and a `[RunAfterBuild]` that only prints, so the IDE run passes and the exe runs the cases. VB6: `Probe.vbp` and `Module1.bas` through `runRepro`, appending to -`out.txt` line by line, so a case that kills the exe still leaves the ones before it. +`out.txt` line by line, so a case that kills the exe still leaves the ones before it. A case +that runs under a harness calls `_Begin` before its `setup` lines and `_End` +after its `run` lines (and before a setup failure is reported); the harness's code is in the +module once, before `Report`. ### The data format ```yaml # errors/VBA.Strings.yml beta: 1010 # the BETA the data was last measured on; titles the VB6 NOTE -win64: false # optional: run measures win64 too (operators, Math) pages: Reference/Default/VBA/Strings/Mid.md: # the page, as a path under docs/ rows: # one table row each, in this order @@ -203,6 +275,11 @@ pages: - { decl: Dim X() As Long, run: "ReDim X(5 To 4, 1)" } quiet: - { decl: Dim X() As Long, setup: ReDim X(3), run: ReDim Preserve X(5) } + Reference/Default/VBA/DateTime/Date.md: + harness: clock # every case of the page runs under errors/harnesses/clock.bas + quiet: + - { run: "Date = #1/1/1601#\nclock_Expect #1/1/1601#, Null" } # and checks the value + - { run: "Date = 36526\nclock_Expect #1/1/2000#, Null", only: tb } Reference/Default/VBA/Strings/Join.md: quiet: - decl: Dim A(1) As Long # block form for a case whose text holds commas @@ -212,16 +289,67 @@ pages: ``` A case is an expression, or a mapping with `expr` or `run` (statements; a multi-line string -for several) and optionally `decl` (`Dim` lines), `setup` (statements before it) and -`only: tb`. A row's `win64:` is the number win64 raises where it differs. A row of `error: 0` -needs a `vb6:` other than 0 and a `bug:`; its NOTE reads *When ..., VB6 raises error N, ...; -twinBASIC raises none.* A page with no rows gets no section; one with `none: ""` and +for several) and optionally `decl` (`Dim` lines), `setup` (statements before it), +`only:` and `harness: ` (also on a row or a page; a case takes its own, else its +row's, else its page's). **`only:`** names the runtimes a case runs in: `tb` (both twinBASIC +builds only: twinBASIC syntax, an extension, a twinBASIC addition), or a list holding `tb` and +one of `vb6`, `vba64`: `[tb, vba64]` for `LongLong` code (64-bit VBA has it, VB6 not), +`[tb, vb6]` for a case Access cannot answer for (vbDatabaseCompare, below). + +What each runtime is expected to raise for a row (`expected` in errors-data.mjs): `tb` its +`error:`; `win64` its `win64:` if given, else `error:`; `vb6` its `vb6:` if given, else +`error:`; `vba64` its `vba64:` if given, else `vb6:`, else `win64:`, else `error:`. So a row +that differs from VB6 expects 64-bit VBA to agree with VB6, and every other row expects it to +agree with twinBASIC 64-bit; `vba64:` is written only where that is not so (refused when it +equals the default). A row where 64-bit VBA's number differs from twinBASIC 64-bit's needs a +`bug:`, as a `vb6:` row does. A row of `error: 0` needs a `vb6:` or `vba64:` other than 0 and +a `bug:`. **The NOTE** names both runtimes when they agree (*When ..., VB6 and 64-bit VBA +raise error N, ...; twinBASIC raises none.* / *..., instead.*), else one sentence each, the +VBA one compared with twinBASIC 64-bit; a row none of whose cases runs in 64-bit VBA says +nothing of it. **A row's `message:`** is twinBASIC's own `Err.Description` for its number, +where it is not the number's message in `messages.yml` (Array's destructuring form raises 5 +as *expected an array*): the table shows it, and `run` checks it in twinBASIC. `run` does +not check an HRESULT's description in VB6 or VBA, which describe every HRESULT as *Automation +error* with Windows' text on a second line (`expectedDescription`). A page with no rows gets no section; one with `none: ""` and no rows gets a section holding that sentence. `messages.yml` maps each number to its message, or to `{ raised: , error: }` where they differ; `run` fails a case whose `Err.Description` is not the `raised` text. A number a row names must be in the file; a new HRESULT is added by hand and then measured with `errors.mjs messages`. A data page must have `title:` and `permalink:` in its front matter. +### Harnesses + +A harness is code a case runs under, for a procedure whose effect must not happen in a probe +(setting the system clock) or must be observed (the value it would set). It is one file, +`errors/harnesses/.bas` (a lowercase name), **the same source for twinBASIC, VB6 and +VBA** (owner, 2026-10-22): module-level code that defines `Private Sub _Begin()` and +`_End()`, and whatever helpers its cases call (`_...`). VB6 compiles only the +branch of an `#If` that is true, so `#If VBA7` holds what VB6 cannot parse (`PtrSafe`, +`LongPtr`; `VBA7` is true in twinBASIC and in VBA 7, and 64-bit VBA refuses a `Declare` +without `PtrSafe`, so never `#If TWINBASIC` for this), and `#If Win64` what differs in a 64-bit build; a pointer is kept in a +`Variant` between calls, and in a typed local for an `As Any` parameter (a `Variant` passed +`ByVal` to one passes the Variant). **A harness must refuse what the real thing refuses**: one +that accepts more hides errors (the clock harness first passed `Time$ = "24:00:00"`, which +Windows refuses). `check_errors.mjs` reports a harness the data names and has no file for. + +- **`clock`** --- `Date`, `Date$`, `Time`, `Time$`. `clock_Begin` patches `SetLocalTime` and + `SetSystemTime` (kernelbase and kernel32) with a jump to a function that records the + SYSTEMTIME and succeeds where Windows would (it asks `SystemTimeToFileTime`, and fails with + error 87 otherwise), and `AdjustTokenPrivileges`, which VB6 calls first and twinBASIC never + does, with one that succeeds; `clock_End` puts the bytes back. The patch is of the probe's + own exe, for one case at a time, and the real clock is never set. `clock_Expect Day, Clock` + raises 1001 when the date part or the time part set is not the one given (`Null` skips + it); it does nothing when the case has already raised. 32-bit and 64-bit; in 64-bit VBA it + patches Access's own process for the case, and works (every Date and Time case agrees). +- **`registry`** (batch 6a) --- `SaveSetting`, `GetSetting`, `GetAllSettings`, + `DeleteSetting`. A case names the application `registry_App`, `tbdocsErrorsProbe` and the + process id (the four runtimes run at once), and `registry_Begin` and `registry_End` delete + that application's whole key under `HKCU\Software\VB and VBA Program Settings` with + `SHDeleteKeyW` (`DeleteSetting` raises 5 in VB6 and VBA for a key that has a subkey), keeping + the case's error (raised again with its description once the key is gone), so nothing a case + writes stays in the user's registry. A case's `setup` may save what it reads. Check after a run + that no `tbdocsErrorsProbe*` key is left. + **YAML pitfalls**, each met in batch 2: - **A flow list breaks on a comma inside a case**: `cases: [Mid(Null, 1)]` is two items. Use a @@ -240,7 +368,10 @@ Read first: [WIP.Harness.md](WIP.Harness.md) (`tbbuild`, `tbrun`), [WIP.Build.md every sentence that mentions an error, **Null** or a limit. 2. **Explore first.** Write a temporary `errors/Explore.yml` that names the real pages and puts every candidate case under `quiet:`, then `node scripts/errors.mjs run Explore.yml`: each - case's line shows what both runtimes raised, `[tb 5, vb6 5]`. Try, for every parameter: + case's line shows what every runtime raised, `[tb 5, win64 5, vb6 5, vba64 5]`. Mark + `LongLong` cases `only: [tb, vba64]`, not `only: tb`. A case VBA refuses costs an Access + restart and a rebuild, and `run` gives up after 25 rebuilds: keep twinBASIC-only cases + (`Bin`, `CType(Of ...)`, `ValDec`, `Shift*`) marked `only: tb` while exploring. Try, for every parameter: **Null**; 0, -1, -2; the upper limit and one past it (strings stop at 1,073,741,823; 2147483647); a value just past a **Long** (2147483648#, which raises 6 and is not listed); an empty string; a 2-D array, an unallocated one and a typed one for an array parameter; @@ -320,6 +451,295 @@ later batch knows what to look for. - **Extensions** (owner): `Join` and `Filter` take a `Long` or `Double` array; VB6 raises 5 and 13 for a `Long` one. +## Batch 3 findings (VBA `Conversion`, BETA 1010) + +Both runtimes agree unless said. + +- **What a C function lists**: 6 for a value outside its type (after rounding: `CByte(255.5)`, + `CInt(32767.5)`; -0.5 rounds to 0 and passes), 13 for a string that is not a number, 94 for + **Null**. Their parameter is any expression, so the conversion is their own work, and listed; + 13 for an argument of the wrong kind on a page that names a numeric parameter (`Fix("abc")`, + `Str("abc")`, `CVErr("abc")`) is not. An **Error** value converts quietly in `CBool`, `CByte`, + `CInt`, `CLng`, `CDbl` and `CStr`, and raises 13 in `Fix`, `Int`, `Hex`, `Str`, `Val` and + `CDate`. An object argument (91 for **Nothing**) was left out: it is the default member's error. +- **Constants**: a probe of a `Double` boundary near 2^62 or 2^63 uses a variable (`X = 2 ^ 63`), + never a literal: the constant 2^63 converts wrongly (`clnglng-2-to-63-no-overflow`), and a + 32-bit build can raise a late 6 after a constant overflow (`const-double-expression-after-overflow-32bit`). +- **`CLngPtr`**: its 32-bit row has `win64: 0` (`run --runtime win64 --only CLngPtr` checks it). +- **`CDate`** takes any number other than a **Decimal** with no error (`date-out-of-range-no-error`, + VB6 6), a string holding such a number with none (VB6 13), and raises 6 for a **Decimal** (VB6 + 13; both `cdate-string-decimal-out-of-range`). The page has a WARNING. `CVDate` raises 13 for + all of these in both, and returns **Null** for **Null**. +- **`Val`**: 13 for a fraction with `%` or `&`; VB6 raises 6 for a whole number outside the + suffix's type (`%`, `&`, `@`), twinBASIC returns it (`val-suffix-overflow-no-error`, owner: a + bug). `ValDec` ignores the suffixes. +- **`Error`/`Error$`**: VB6 raises 6 above 65,535 (after rounding), twinBASIC returns a message + (`error-function-above-65535`, owner: a bug; taken from batch 8's list). Negative numbers pass. +- **`CVErr`** raises 5 outside 0--65,535 (after rounding). +- **Extension** (owner): `Hex` and `Oct` take any number in the `LongLong` range, where VB6 + raises 6 outside a `Long`. A `Single` or `Double` of 2^62 or more in magnitude raises 6 in + `Hex`, `Oct` and `Bin` (-2^62 itself passes); a `LongLong`, `Decimal` or string does not. + 64-bit VBA does both (the re-check, below), so neither is an extension nor a bug against it. +- **Extension** (owner, 2026-10-21): a negative `Single`, `Double`, `Currency`, `Decimal` or + numeric string gives `Hex` 16 characters (`Oct` 22, `Bin` 64) in twinBASIC, the `LongLong` + form; VB6 gives 8 (11), the `Long` form: `Hex(-1#)` is `"FFFFFFFF"` in VB6. A negative + `Integer` or `Long` agrees. The Hex and Oct pages state it in their extension NOTE, and the + tables give each type's width. Nothing to queue; not an error, so not in the data. + +## Batch 4 findings (VBA `Math`, BETA 1010) + +Both runtimes agree unless said; win64 agrees with win32 on every case. + +- **Null raises 94** in Atn, Cos, Sin, Tan, Exp, Log, Sqr, Sgn, Rnd and Randomize, and as + Round's *numdecimalplaces* or the Shift functions' *shiftamount*. Abs and Round return + **Null** for a **Null** first argument. +- **Abs** keeps its argument's type, so the smallest `Currency` raises 6 in both. For the + smallest typed `Integer` or `Long` VB6 raises 6 and twinBASIC returns a `Long` or a + `Double` (`abs-minimum-widens-type`; the smallest `LongLong` gives a `Double` too). A + `Variant` holding either widens in both. +- **Trigonometry past 2^63**: VB6 raises 5 for Cos, Sin and Tan from 9.223372E+18 in magnitude + (found by bisection). twinBASIC returns a value up to 2^63; from 2^63 Cos and Sin return + the argument itself, and Tan raises -2147348207 (`&H80021111`), *Automation error*, whose + `Error$` is generic (`trig-above-2-to-63`; WARNINGs on Cos and Sin). The number is in + `messages.yml`. +- **Limits**: Exp raises 6 above 709.782712893384; Log raises 5 for 0 or less (**Empty** and + -0 included); Sqr 5 for a negative number (-0 passes). Rnd's 6 above the `Single` range is + the conversion of its parameter, not listed. +- **Round**: 5 when *numdecimalplaces* rounds below 0 (-0.4 passes), and above 22 only for a + `Single`, `Double`, `Date` or string *expression*; `Integer`, `Long`, `Currency`, + `Decimal`, `Boolean` and **Empty** take any `Long`. VB6 raises 5 for `Round(Null, -1)`, + twinBASIC returns **Null** (`round-null-negative-digits`); `Round(Null, 23)` is **Null** in + both. + +## Batch 5 findings (VBA `DateTime`, BETA 1010) + +Both runtimes agree unless said. + +- **A Calendar case must set `vbCalGreg` again.** In VB6 `Calendar = vbCalHijri` succeeds and + stays, so every later case of the probe runs under the Hijri calendar and the VB6 column + drifts. The data's cases end with `Calendar = vbCalGreg`. In twinBASIC every assignment + raises -2147467259 and the compiler warns TB0009, *[Calendar] has not yet been implemented*. + The owner chose not to report it (2026-10-22): its cases are `only: tb`, with no VB6 NOTE, + and a NOTE by hand on Calendar and on the module's index says it is not implemented. +- **Setting the clock**: Date and Time run under the `clock` harness (*Harnesses*), so an + accepted value raises nothing and `clock_Expect` checks what would be set; without it a + probe has no privilege and every accepted value raises 70. **A 70 is never a row** (owner, + 2026-10-22: it documents the test setup, not twinBASIC). Neither runtime checks a date or a + time it parses: both pass month 13 or February 30 to Windows, and Windows refuses them, and + any date before January 1, 1601. Where Windows refuses, twinBASIC raises -2147467259 and VB6 + 5. `Date = Null` and `Time = Null` raise 94 (VB6 13); `Date$` takes only *mm-dd-yyyy* and + `Time$` only *hh:mm:ss* (VB6 also takes *m-d-yyyy*, *m/d/yyyy*, *mm-dd-yy*, *hh:mm*, *hh*); + all in `date-time-set-errors`. Both read the string "0.25" given to `Time` as 0:25. +- **`Date =` and `Time =` of a number are an extension** (owner, 2026-10-22). twinBASIC reads + the number, or a string holding one, as `CDate` does: whole days from December 30, 1899 + (`Empty` and `False` are that day, `True` the day before, being -1), `Byte`, `Long`, + `Single`, `Currency` and `Decimal` alike, and 13 outside the range of dates. `Date` sets + the date part and keeps the time of day; `Time` sets the time part of the `CDate` value + (0.25 and 1.25 are 6:00, -0.25 and -1.25 too; 0.999999 rounds to 0:00 of the same day). VB6 + sets exactly the same for a **Date** value (`Time = CDate(1.25)` is 6:00), and raises 13 for + a number. Each is a twinBASIC-only quiet case that checks the value, and a NOTE on Date and + on Time says it. Checked in 32-bit and 64-bit. +- **The 13 rule**: a function whose parameter is "any expression that can represent a date" + (Day, Month, Year, Weekday, Hour, Minute, Second, DatePart, DateValue, TimeValue) raises 13 + for a string that is not a date, a number outside -657434 to 2958465, and an **Error** + value; **Null** returns **Null** in the part functions and raises 94 in DateValue and + TimeValue (TimeValue's page said **Null** is returned: now a `` comment). + DateValue refuses any number with a whole part (`DateValue(36526)` is 13), and a **Date** + passes. +- **DateAdd and DateDiff raise 6** for a date number outside the range (VB6 13, + `dateadd-date-out-of-range-overflow`). Listed, though the page gives the parameter as + **Variant** (**Date**): it differs from VB6 and from the part functions. +- **DateAdd**: 5 for an interval that is not a setting (case-insensitive, no spaces) and for a + result after 9999 or before year 100; with "yyyy", "q" or "m" a result in years 0 to 99 + becomes 1930--2029 silently (`dateadd-year-below-100`, VB6 5; a WARNING). +- **_firstdayofweek_ outside 0 to 7 is a bug** (owner: an extension only if it makes sense + and has no bugs). Over every date of 1999 to 2001: a negative value acts modulo 7, but + `DatePart("w")` takes 8 as 1 and 9 up as nothing at all (255 gives a weekday of -2), + `DatePart("ww")` takes 8 and 9 as 1, `DateDiff("ww")` 8 to 13 as 1 + (`datepart-firstdayofweek-out-of-range`; WARNINGs on DatePart and DateDiff). VB6 checks it + only where the interval uses it. +- **_firstweekofyear_ outside 0 to 3** gives, for every value tried (-15 to 2147483647), the + same results as 0 and 1, which agree on this system (its own setting is January 1). That + could be an extension, but whether twinBASIC takes it as 0 (the system's setting) or 1 + cannot be told apart here; the owner ruled it a bug (`datepart-firstweekofyear-out-of-range`). +- **DatePart**: `DatePart("x", Null)` raises 5, VB6 returns **Null** + (`datepart-null-checks-interval`). `DatePart("ww", ...)` of January 1 or 2, 100 never + returns with `vbFirstFullWeek` and returns -98980 with `vbFirstFourDays` + (`datepart-week-year-100`; a WARNING). **A case that hangs stops the probe, and every + later case reports `none`**: run a suspect alone before reading the cases after it. +- **DateSerial**: 5 when the date is outside January 1, 100 to December 31, 9999, after its + two-digit-year reading (`DateSerial(-1900, 1, 1)` passes, -1901 raises); 6 above an + **Integer** is the conversion, not listed. **TimeSerial** cannot reach a date outside the + range with **Integer** arguments: its page's sentence saying it can is a `` + comment. +- **`pageLevel`** now takes the level of the page's last Example or See Also heading, so Date + and Time (an Example a level down under each property) get `###` like their See Also. Their + sections were placed before See Also by hand, as markers; `write` fills them. + +Owner's answers (2026-10-22): 1, `Date =` and `Time =` of a number are an extension; 2, +the week arguments may be extensions where they make sense and have no bugs; 3, Calendar is +not reported; 4, no 70 rows. Then: a *firstweekofyear* outside 0 to 3 is a bug. + +## Batch 6a findings (VBA `Information` and non-interactive `Interaction`, BETA 1010) + +Status (2026-10-22): **data, sections and page edits done and committed**: +`errors/VBA.Information.yml` and `errors/VBA.Interaction.yml`, 302 cases, all agreeing with the +data in all four runtimes; sections on 27 pages. **Left**: the owner's answers to the questions +below (bug grouping; large numbers in Partition and Choose), then each `bug: pending` row's +reproducer and entry in BUGS-TO-REPORT.md, and the `RaiseEventByName` crash's entry (the +WARNINGs on both pages are written; put the slug in an HTML comment beside each). Page edits +made: LBound/UBound's IMPORTANT no longer names error 9; the error sentences of DeleteSetting, +SaveSetting, CreateObject, GetObject and Switch went into the tables; Environ's "a zero-length +string is returned" for a missing position is a `` comment (both VB runtimes raise +5); Array's *RhsArray* no longer says it raises; QBColor's and RGB's parameters are stated as +**Integer**. twinBASIC 64-bit agrees with 32-bit on every case. The registry harness names the +application with the process id, since `run` probes the four runtimes at once and they shared +one key (a VB6 case failed when another runtime's cleanup removed its section). + +- **twinBASIC raises -2147467259 (`&H80004005`, *Unspecified error*) where VB6 and 64-bit VBA + raise 5, 9 or 13 for an argument the procedure refuses**: LBound/UBound's *dimension* out of + range (VB 9), Switch with an odd number of arguments (5), Partition's *start* below 0, + *stop* not above *start*, *interval* below 1 (after rounding) (5) and a string *number* (13), + Environ's *number* of 0 or less (after rounding), `""`, **Empty**, **True**, and a position + with no entry (5: the page's "zero-length string is returned" is wrong for both VB runtimes), + GetObject() with no arguments (13), CallByName's *calltype* not 1 to 4 (VB 438) and an + object of **Nothing** (5), and the four registry procedures' `""` *appname*, *section* or + *key*, and DeleteSetting of a missing application, section or key (5). The same family as + `date-time-set-errors` and `redim-inverted-bounds-error`. **Proposal for the owner**: one bug + entry for the family. Above the VB `Integer` range VB raises 6 where twinBASIC raises + -2147467259 (Environ's *number* from 32,768, LBound's *dimension* 32,768). +- **COM creation raises the raw HRESULT where VB maps it**: CreateObject of an unknown ProgID, + a CLSID without `new:`, a number: -2147221020 (`MK_E_SYNTAX`); `new:` with an unregistered + CLSID: -2147221164 (`REGDB_E_CLASSNOTREG`); `new:` with a malformed one: -2147221005 + (`CO_E_CLASSSTRING`); `""`: 5. VB6 and 64-bit VBA raise 429 for each. GetObject: a missing file + -2147221014 (`MK_E_CANTOPENFILE`), with a class -2147467262 (`E_NOINTERFACE`) where VB raises + 432; an unknown class -2147221005 and no running instance -2147221021 (`MK_E_UNAVAILABLE`) + where VB raises 429. A remote server that does not resolve: -2147023174 in all four; a + `"notaprogid:xyz"` moniker: -2147221020 in all four. **Proposal**: a second bug entry. +- **CallByName raises DISP_E_ HRESULTs where VB maps them**: a wrong argument count + -2147352562 (`DISP_E_BADPARAMCOUNT`; VB 450), a property given arguments it does not take + -2147352559 (VB 451), and an error the COM member raises -2147352567 (`DISP_E_EXCEPTION`; + VB raises the member's own number: Dictionary.Add of a key that exists 457, Remove of a + missing key 32811, CompareMode set after an Add 5). `""` as *procname*: 438, VB 450. The + page already has a NOTE for a twinBASIC member's constant Err.Raise; this widens it. + **Proposal**: a third entry, or fold into the page's existing NOTE's bug. +- **A VBA Collection is not reachable through CallByName in VB6 or 64-bit VBA**: even + `CallByName(C, "Count", vbGet)` raises 438 there (twinBASIC: 0). The data uses + `Scripting.Dictionary`. Not queued: the VB runtimes' own limitation. +- **`RaiseEventByName` and `RaiseEventByName2` crash** (access violation, 32-bit and 64-bit) + given an object that is not an event source (`Collection`, `Dictionary`); **Nothing** raises + 91. A crash cannot be a case: a WARNING by hand on both pages, and a bug to queue. +- **32-bit twinBASIC follows 64-bit VBA, not VB6, for large numbers**: Partition of a *number* + or *stop* beyond a **Long** (VB6 6, twinBASIC and 64-bit VBA none); Choose of an *index* beyond + the **Single** range (VB6 none, twinBASIC and 64-bit VBA 6). **Owner, 2026-10-22: a documented + extension** ("tB can extend VB6"): the cases are `only: [tb, vba64]`, no `vb6:` and no bug, + and each page has a NOTE in its prose saying what VB6 does. +- **Owner, 2026-10-22: the bugs are grouped as proposed**: one entry for the -2147467259 family, + one for CreateObject's and GetObject's raw HRESULTs, one for CallByName's `DISP_E_` HRESULTs, + and one for the RaiseEventByName crash. Progress (reproducers under `bugs/`, each a `tests` + reproducer with a `vb6/` side, run and `repro.json` written; entries in BUGS-TO-REPORT.md + not written yet unless ticked): + - [x] reproducer `argument-errors-unspecified-error` (3 PASS controls, 17 FAIL), [ ] entry + - [x] reproducer `createobject-getobject-raw-hresults` (3 PASS, 8 FAIL), [ ] entry + - [x] reproducer `callbyname-dispatch-hresults` (3 PASS, 5 FAIL), [ ] entry. Narrowed: + `latebound-hresult-mapping` (queued) already has `DISP_E_BADPARAMCOUNT` (450), so the + wrong-count rows cite it; this entry is what is left: a COM object's own error reaches + `CallByName` (any call type) and a late-bound property `Let` statement as -2147352567, + though a late-bound method statement gets it right (`D.Add "x", 2` raises 457 in all four); + and `""` as *procname* raises 438, VB 450. + - [x] `&H80020011` (`DISP_E_NOTACOLLECTION`, VB 451, for a statement too: `D.Count(1)`) is in + `latebound-hresult-mapping`'s table and its reproducer (two cases, VB6 side `SetCode 25`); + the CallByName rows for argument counts (450) and arguments to a property (451) cite it + - [x] reproducer `raiseeventbyname-no-events-crash` (5 PASS, 5 FAIL, 32-bit and 64-bit; + classes `EventSource` and `Listener`), [ ] entry. Found with it: an event that **no handler** + is attached to raises -2147467259, where `RaiseEvent` raises nothing; with a handler both + functions work. A NOTE on both pages, and the entry's second point + - [x] the data's `bug: pending` replaced by the slugs; the WARNINGs' HTML comments + - [x] the four entries' text in BUGS-TO-REPORT.md (`argument-errors-unspecified-error`, + `createobject-getobject-raw-hresults`, `callbyname-dispatch-hresults`, + `raiseeventbyname-no-events-crash`), each with its module(s) verbatim; all five + reproducers packed (`latebound-hresult-mapping` again). **Batch 6a is done**; next, 6b. +- **Agree in all four**: LBound/UBound of an unsized or erased array 9, of a **Variant** holding + no array 13, *dimension* **Null** 94; QBColor outside 0 to 15 after rounding 5; RGB's negative + component (after rounding) 5, above 255 taken as 255; Null 94 in QBColor, RGB, Choose's + *index*, Environ, CreateObject, GetObject, CallByName, and every registry argument; Switch, + IIf and If of a string that is not a Boolean 13; the Is* functions, TypeName, VarType, + ObjPtr, StrPtr, IMEStatus, Erl and Command raise nothing. +- **twinBASIC-only**: RGBA's negative component 5; RGB_R/G/B and RGBA_A raise only 94 for + **Null**; TranslateColor raises 5 for a high byte other than 0, 1, 2 or &H80, and for a + system colour index above 30 (&H8000001E passes, &H8000001F raises); Array's destructuring + form raises 5 for a right side that is not an array (**Null** too) and 9 for one with fewer + elements or unsized; CallByDispId: an unknown DISPID 438, *calltype* outside 1 to 4 and + **Nothing** -2147467259, a wrong argument count -2147352562. +- **Type of a parameter the page leaves out**: QBColor's and RGB's parameters are **Integer** + in VB (32,768 raises 6 in all four): state the type on the page, and the 6 is then the + conversion, not listed. + +## The 64-bit VBA re-check (BETA 1010, Office 365 x64, VBA 7.1 VBE7.DLL 7.01.1158, 2026-10-22) + +Batches 2 to 5 and `Core.yml` in all four runtimes. Every runtime agreed with the data as it +stood, apart from the cases that follow, and twinBASIC 64-bit agreed with 32-bit everywhere. + +- **Every `vb6:` row: 64-bit VBA agrees with VB6.** So each existing bug holds against 64-bit + VBA as well, and `write` now words those NOTEs *VB6 and 64-bit VBA raise ...* (17 pages). + `Abs` of the smallest `Integer`/`Long` raises 6 in 64-bit VBA too (`abs-minimum-widens-type`). +- **`vbDatabaseCompare` (2)**: in Access, 64-bit VBA takes it (the database's sort order) and + raises nothing for `InStr`, `Filter`, `StrComp`, as twinBASIC does; VB6 raises 5 + (`database-compare-no-error`). That is Access's own meaning, which VBA documents as + Access-only, so Access cannot answer for VBA in Excel or Word: the three cases are + `only: [tb, vb6]`, and the NOTEs stay VB6-only. Whether VBA outside Access raises 5 is left + unchecked (owner, 2026-10-22: not worth it; Excel would need the VBA object-model trust + setting, the user's). +- **`only: tb` cases 64-bit VBA can run**: `CLngLng` (13 cases), `CLngPtr` (10), `Hex` and `Oct` + beyond a `Long` (15), `Round` of a `LongLong` (1) are now `only: [tb, vba64]`, and all agree: + `CLngPtr` of 2^31 raises nothing in 64-bit VBA, as the rows' `win64: 0` say. +- **`Hex`/`Oct` in 64-bit VBA behave exactly as in twinBASIC**: the whole `LongLong` range, and + a negative non-integer gives 16 (22) characters: `Hex(-1#)` is `FFFFFFFFFFFFFFFF`, `Hex(-1&)` + `FFFFFFFF`. So the batch-3 "extensions" are 64-bit VBA's own behaviour; the Hex and Oct pages' + NOTEs now say so. **And a `Double` or `Single` of 2^62 or more raises 6 in 64-bit VBA too** + (`Hex(2^62)`, `Oct(2^62)`, `-2^62` passes). The filed bug `hex-double-above-2-to-62` said + otherwise: **the owner withdrew it** (2026-10-22, closed upstream), its reproducer is deleted, + and the Hex, Oct and Bin NOTEs are now unlabelled statements of the limit (*as in 64-bit + VBA*; Bin, which VBA lacks, *as it does in Hex and Oct*). +- **`CLngLng(2 ^ 63)`, `CLngLng(9223372036854775808#)` and `CLngLng(9223372036854775807)` raise + 6 in 64-bit VBA**; twinBASIC returns the smallest `LongLong` (`clnglng-2-to-63-no-overflow`, + queued): the bug is confirmed against 64-bit VBA, and the CLngLng WARNING now says so. Add + this to the entry's text in BUGS-TO-REPORT.md (not done yet). +- **Extensions stay extensions, and 64-bit VBA sides with VB6**: `Date =`/`Time =` a number + (13), `Join` of a `Long` array (5), `Filter` of one (13), `StrConv` 256/512 (5). Their hand + NOTEs now say *VB6 and 64-bit VBA raise ...*. `Date = -657434` stays `only: tb` (it is a + number). `Bin`, `CType(Of ...)`, `ValDec`, `Shift*` and `Calendar` do not exist in VBA. +- **`messages`**: all 89 numbers agree in all four runtimes. + +### State of the work (committed on `staging`, 2026-10-22; `build.bat`, `check.bat`, `test.bat` pass) + +Done: + +- `scripts/lib/vba.mjs` (new), `scripts/errors.mjs` (four runtimes, `generate` writes + `/vba`, the VBA refusal reading), `scripts/lib/errors-data.mjs` (`vba64:`, `only:` lists, + `expected`, `runsIn`, `RUNTIMES`, the vba dialect, the NOTE wording; the file key `win64:` is + gone), `scripts/check_errors.mjs` (probes for all of it), `errors/harnesses/clock.bas` + (`#If VBA7`), `errors/VBA.Math.yml` (no `win64: true`), `errors/VBA.Conversion.yml` and + `VBA.Math.yml` (`only: [tb, vba64]`), `errors/VBA.Strings.yml` (the three `[tb, vb6]` cases), + `docs/Documentation/Tools-Defects.md` (the errors.mjs entry). +- `errors.mjs write` run: 17 pages' NOTEs regenerated. Hand NOTEs edited: Hex, Oct, Join, + Filter, Date, Time, CLngLng. + +Then (2026-10-22, done): **the 64-bit VBA results in the queued entries of +BUGS-TO-REPORT.md.** In every one 64-bit VBA does what VB6 does, and twinBASIC 64-bit what +32-bit does. Each entry's **Expected behavior** now says *as VB6 and 64-bit VBA ...*, and its +**Additional context** that a 64-bit build gives the same results and which VBA was used +(*VBA 7.1 (VBE7.DLL 7.01.1158) in 64-bit Microsoft 365 Access*), the way a later entry should +say it too. The entries: `clnglng-2-to-63-no-overflow` (the four constants and four controls, +checked by a VbaSession probe), `date-out-of-range-no-error` (its six cases and three controls, +by a probe), `date-time-set-errors`, `dateadd-date-out-of-range-overflow`, +`dateadd-year-below-100`, `datepart-firstdayofweek-out-of-range`, +`datepart-firstweekofyear-out-of-range`, `datepart-null-checks-interval`, +`instr-invalid-lcid-empty-string`, `strconv-east-asian-lcid` (all from the data's run), and +`datepart-week-year-100` (a probe: 52, 1, 52, 52; `bug_repro.mjs run --arch win64` hangs as +32-bit does). Filed entries were not touched: their issues are the owner's to edit. +`database-compare-no-error` was left alone: Access's VBA takes 2 by design (above). + ## Batches Each batch is one session. They are independent after batch 0, so any order works, and @@ -330,17 +750,19 @@ each can be run when there is budget. Status is kept here. | 0 | Tooling: data format, probe generator (twinBASIC and VB6), checker, section writer, the gate | --- | done; reviewed 2026-10-20 | | 1 | The central Run-time errors page | 1 | done; reviewed 2026-10-20; list of pages per number and `tb:Errors` shortcut 2026-10-21 | | 2 | **Pilot: VBA `Strings`** --- settled the section's style with the owner | 30 | done: 28 sections, 339 cases; reviewed 2026-10-21 | -| 3 | VBA `Conversion` | 28 | not started | -| 4 | VBA `Math` (also win64) | 17 | not started | -| 5 | VBA `DateTime` | 20 | not started | -| 6 | VBA `Information` and `Interaction` | 54 | not started | +| 3 | VBA `Conversion` | 28 | done: 23 sections, 299 cases; owner's questions answered 2026-10-21 | +| 4 | VBA `Math` | 17 | done: 15 sections, 132 cases; 2026-10-22 | +| 2--5 | Re-check of batches 2 to 5 and Core in four runtimes (64-bit VBA added) | --- | done: all agree; committed 2026-10-22, see *The 64-bit VBA re-check* | +| 5 | VBA `DateTime` | 20 | done: 17 sections, 328 cases; owner's questions answered 2026-10-22 | +| 6a | VBA `Information`, and `Interaction`'s non-interactive procedures: `CallByName`, `CallByDispId`, `Choose`, `Command`, `CreateObject`, `GetObject`, `Environ`, `IIf`, `If`, `Partition`, `Switch`, `RaiseEventByName`, `RaiseEventByName2`, `SaveSetting`, `GetSetting`, `GetAllSettings`, `DeleteSetting` (owner split batch 6, 2026-10-22) | 45 | in progress | +| 6b | `Interaction`'s interactive procedures: `AppActivate`, `Beep`, `DoEvents`, `InputBox`, `MsgBox`, `SendKeys`, `Shell` (a probe never calls `MsgBox` or `InputBox`; `SendKeys` only with strings refused before a key is sent; `Shell` only with a missing program or `cmd.exe /c exit`) | 7 | not started | | 7 | VBA `FileSystem` | 23 | not started | | 8 | VBA `Collection` and `ErrObject` (and the `Err.Raise` differences under *The central page*) | 22 | not started | | 9 | VBA `Financial` | 14 | not started | | 10 | VBA `HiddenModule`, then `Compilation` and `TbExpressionService` (`Constants` has nothing to raise) | 68 | not started | | 11 | Core file I/O statements (`Open`, `Get`, `Put`, `Input`, ...: errors 52--75) | 15 | not started | | 12 | Core arrays and control flow (`ReDim`, `Erase`, `Resume`, `Return`, ...); ReDim has data already, in `errors/Core.yml` | 15 | not started | -| 13 | Operators: the matrix generator and its pages (also win64) | 25 | not started | +| 13 | Operators: the matrix generator and its pages | 25 | not started | | 14 | VB package (37 pages) and the VBRUN pages that document members rather than constants: properties and methods that raise 380, 383, 387 and the like; needs forms | 37 + part of 134 | not started | | 15 | Built-In packages | 195 | not started | diff --git a/WIP.md b/WIP.md index 1f1ba134d..9795da882 100644 --- a/WIP.md +++ b/WIP.md @@ -588,7 +588,7 @@ Why the report separates the wedged task from the merely blocked ones, and why - **The help archive is the `helpArchive` task of the build's graph.** `builder/help-archive-step.mjs` writes `add-in/Resources/HELP/site.zip` from `docs/_site-offline` through `lib/help-archive.mjs`, once every task that writes into the offline tree has finished (`flushJoin`, `scss`, `writeOffline`; the archive task lists exactly those) and **before** the Gantt injection. **The archive leaves out `HELP_ARCHIVE_SKIP`** (`builder/help-archive-step.mjs`): the Documentation Development section (`BuildInfo.html` and `assets/images/gantt.svg` with it, so the chart never reaches the zip), Videos and their poster frames, Challenges, and **every download, which stays online** (owner): a file readers download goes in a `downloads` folder beside its page, and the step fails the build when the Downloads page offers a file the archive would hold. The add-in's server redirects any file the archive lacks to the website, because pages link to all of these; a new entry needs nothing else. It reads the file back to verify it. A mismatch fails the build (exit 1). After the Gantt injection `recheckHelpArchive` checks that the offline tree on disk lists the same files as the archive and differs from it only in `BuildInfo.html` and `gantt.svg`; a task that wrote into the offline tree late fails the build there. The help add-in embeds the zip as a resource and serves the pages from it with its own server; a build of the add-in without it uses the live site. The file is gitignored (about 26 MB), so a build of the add-in from a clean clone is the online one until `build.bat` has run. The download carries the same zip (previous bullet). **`--no-help-archive` turns the step off, and neither CI workflow may pass it** (`check_ci_workflows.mjs` refuses it, in `ALLOWED.forbiddenBuildFlags`), because CI's published download would then hold no archive. It runs only for the guarded source tree (`docs`) built into the default `docs/_site-offline` (not another `--dest`, so `compare_trees` and `check_links_diff` leave it alone), never under `--serve` or `--dry-run`, never without an offline tree, and not keyed on `$CI`. There is no command-line tool for the archive: the `help-offline` lane makes its own with `writeHelpArchive`. The zip's format is fixed by the add-in's reader, which does no inflating ([WIP.HelpAddin.md](WIP.HelpAddin.md), Stage 4, increment 5). - `node scripts/bug_repro.mjs` --- the reproducer projects under `bugs//` for the entries of [BUGS-TO-REPORT.md](BUGS-TO-REPORT.md): `new`, `pack` (impexp, then the zip, written in Node), `compile`, `build` and `run` through `tbbuild` and `tbrun`, `vb6` (builds the optional `vb6/` VB6 project beside the twinBASIC one in a temp copy, runs its exe on a private desktop, and prints its `out.txt`; a project with a form or a user control is built without Unattended Execution, which VB6 refuses for it; `pack` zips its sources into `-vb6.zip`; VB6 is started only by `scripts/lib/vb6.mjs`, never from a shell; a graphical defect's `images` key makes `run` and `vb6` keep what the `PngDump` modules of `test/repro-templates/png/` draw as `images/-tb.png` and `-vb6.png`, with a `-compare.png` of both, tested by `test/png.test.mjs`), `file` (moves a filed entry and its reproducer to `bugs/filed//`; `file --marked` does every marked entry), and `verify`, which reads each `bugs/*/repro.json` and `bugs/filed/*/repro.json` and says whether the bug still reproduces on the newest beta; a `lane` reproducer names the `ide-test` or `addin-test` lane and tests that assert its bug, which `verify` runs last through that runner, a `probe` one a script under `scripts/`, and a `test` one its own `[TestCase]` Subs and compile cases through `tbrun --tests`, judged by the `PASS` lines of what is fixed and the `FAIL` lines of the bug (`new --template tests` starts one). **No reproducer should need a person at the IDE** (owner): a bug the IDE has to be operated for gets a lane test, and `manual` is left for what cannot be automated at all. It has no wrapper. **`verify` is run by a person only**, never by a gate or CI: it needs a twinBASIC install, like `examples.bat`. Each `tbbuild` and `tbrun` it starts takes a slot of its own from `tbbroker`; `--jobs N` runs N at once. Its command-line cases are in `scripts/lib/cli-cases.mjs`, and need no IDE. - `node scripts/vb6run.mjs ` and `node scripts/vb6run.mjs --docs [--only ]` --- build and run VB6 code, so that what a sample prints in twinBASIC can be compared with VB6: a stand-alone file of statements or a `.bas` module, or `--docs`, which builds the documentation's `check_run` fences in VB6 and says for each whether it prints what its page says (`same`, `differs`, `not VB6`, `error`, `refused`; a `projname=` group is built as a project of its own, its `slot=file` fences translated into VB6 `.cls` and `.bas` components). It has no wrapper and is outside every gate and outside CI: it needs VB6 (`--vb6`, `VB6_EXE`, or the standard install folder). **Never start `VB6.EXE` from a shell**: in Git Bash `/make` is rewritten as a path and VB6 answers with a modal box on the desktop; the tool spawns it from Node with an argument array, and a sample that calls `MsgBox` or `InputBox` or contains `End` is refused. Its `Debug.Print` rewrite and its file translation have probes in `test/example-batches.test.mjs`, and its command-line cases are in `scripts/lib/cli-cases.mjs`; both need no VB6. What it relies on is in [WIP.ExamplesBuild.md](WIP.ExamplesBuild.md). -- `node scripts/errors.mjs run|generate|write` --- the run-time errors of the documented procedures, as data under `errors/` ([WIP.Errors.md](WIP.Errors.md), [Tools-Defects](docs/Documentation/Tools-Defects.md#errorsmjs)): `run` builds and runs a probe of the cases in twinBASIC (`tbrun --exe`; win64 on request) and VB6 and compares each with the data, `write` writes each page's Errors section. Its gate is `check_errors.mjs`; `run` is by hand, never CI. +- `node scripts/errors.mjs run|generate|write` --- the run-time errors of the documented procedures, as data under `errors/` ([WIP.Errors.md](WIP.Errors.md), [Tools-Defects](docs/Documentation/Tools-Defects.md#errorsmjs)): `run` builds and runs a probe of the cases in twinBASIC 32-bit and 64-bit (`tbrun --exe`), VB6 and 64-bit VBA (in 64-bit Access, `scripts/lib/vba.mjs`) and compares each with the data, `write` writes each page's Errors section. Its gate is `check_errors.mjs`; `run` is by hand, never CI. - `node scripts/ide_js.mjs ` --- reads, names and patches the IDE's minified `ide/main.js` ([Tools-Defects](docs/Documentation/Tools-Defects.md#ide_jsmjs)): `survey`, `units`, `list`, `name`, `seal`, `check`, `view`, `patch` and `shape`. It reads files and starts nothing; the rules for it are under [Driving the twinBASIC compiler](#driving-the-twinbasic-compiler) (*Fixing the IDE itself*). It has no wrapper and is outside every gate and CI, since it needs an install; its tests are `test/ide-js.test.mjs`, in `test.bat`, and its command-line cases are in `scripts/lib/cli-cases.mjs`. **After a new BETA, run `check` on it**, and `seal` the map once each `review` and `lost` name has been looked at. Three generators sit outside that loop and produce committed artifacts rather than build output — none runs during a build, and none is needed for one. `python scripts/build_fonts.py` rebuilds the subset webfaces under `docs/assets/fonts/` and needs a network connection; `node scripts/build_dot_metrics.mjs` regenerates `builder/inter-metrics.json` from those webfaces and needs only a browser. See [Typography](#typography). `node indexer/twin-index.mjs derive` regenerates `builder/package-api.json`, the packages' declared API that the build's symbol index (`tB/symbols.json`, for the IDE help add-in) is annotated from; it needs a twinBASIC install (or `--release`), so **run it when the reference is re-indexed against a newer build** and commit it with the pages and the snapshots. See [WIP.HelpAddin.md, Stage 3](WIP.HelpAddin.md#stage-3-the-symbol-index-generated-by-the-docs-build). diff --git a/bugs/argument-errors-unspecified-error/argument-errors-unspecified-error.twinproj b/bugs/argument-errors-unspecified-error/argument-errors-unspecified-error.twinproj new file mode 100644 index 000000000..ead45fc11 Binary files /dev/null and b/bugs/argument-errors-unspecified-error/argument-errors-unspecified-error.twinproj differ diff --git a/bugs/argument-errors-unspecified-error/repro.json b/bugs/argument-errors-unspecified-error/repro.json new file mode 100644 index 000000000..f979b0c58 --- /dev/null +++ b/bugs/argument-errors-unspecified-error/repro.json @@ -0,0 +1,28 @@ +{ + "mode": "test", + "expect": { + "exit": 7, + "output": [ + "^PASS RegressionTests.LBound_UnsizedArray_Raises9$", + "^PASS RegressionTests.QBColor_Sixteen_Raises5$", + "^PASS RegressionTests.Environ_Null_Raises94$", + "^FAIL RegressionTests.LBound_DimensionZero_Raises9: .*Actual: -2147467259 ", + "^FAIL RegressionTests.UBound_DimensionPastRank_Raises9: .*Actual: -2147467259 ", + "^FAIL RegressionTests.LBound_Dimension32768_Raises6: .*Actual: -2147467259 ", + "^FAIL RegressionTests.Switch_OddArguments_Raises5: .*Actual: -2147467259 ", + "^FAIL RegressionTests.Partition_NegativeStart_Raises5: .*Actual: -2147467259 ", + "^FAIL RegressionTests.Partition_IntervalZero_Raises5: .*Actual: -2147467259 ", + "^FAIL RegressionTests.Partition_StringNumber_Raises13: .*Actual: -2147467259 ", + "^FAIL RegressionTests.Partition_BeyondLongLong_Raises6: .*Actual: -2147467259 ", + "^FAIL RegressionTests.Environ_Zero_Raises5: .*Actual: -2147467259 ", + "^FAIL RegressionTests.Environ_PositionWithNoEntry_Raises5: .*Actual: -2147467259 ", + "^FAIL RegressionTests.Environ_EmptyName_Raises5: .*Actual: -2147467259 ", + "^FAIL RegressionTests.Environ_Above32767_Raises6: .*Actual: -2147467259 ", + "^FAIL RegressionTests.GetObject_NoArguments_Raises13: .*Actual: -2147467259 ", + "^FAIL RegressionTests.CallByName_CallTypeZero_Raises438: .*Actual: -2147467259 ", + "^FAIL RegressionTests.CallByName_Nothing_Raises5: .*Actual: -2147467259 ", + "^FAIL RegressionTests.GetSetting_EmptyAppName_Raises5: .*Actual: -2147467259 ", + "^FAIL RegressionTests.DeleteSetting_MissingApplication_Raises5: .*Actual: -2147467259 " + ] + } +} diff --git a/bugs/argument-errors-unspecified-error/src/Settings b/bugs/argument-errors-unspecified-error/src/Settings new file mode 100644 index 000000000..b0b9585ee --- /dev/null +++ b/bugs/argument-errors-unspecified-error/src/Settings @@ -0,0 +1,75 @@ +{ + "configuration.inherits": "Defaults", + "project.appTitle": "ArgumentErrorsUnspecifiedError", + "project.buildPath": "${SourcePath}\\Build\\${ProjectName}_${Architecture}.${FileExtension}", + "project.buildType": "Standard EXE", + "project.description": "Reproduces: Refused arguments raise -2147467259, Unspecified error, where VB6 and VBA raise 5, 6, 9, 13 or 438", + "project.exportPathIsV2": true, + "project.id": "{23508218-7D9B-448E-A092-737BAD43E9BF}", + "project.name": "ArgumentErrorsUnspecifiedError", + "project.optionExplicit": true, + "project.references": [ + { + "id": "{00020430-0000-0000-C000-000000000046}", + "lcid": 0, + "name": "OLE Automation", + "path32": "C:\\Windows\\SysWOW64\\stdole2.tlb", + "path64": "C:\\Windows\\System32\\stdole2.tlb", + "symbolId": "stdole", + "versionMajor": 2, + "versionMinor": 0 + }, + { + "id": "{F50B82D0-DCAB-43FE-9631-11959D4A4728}", + "isCompilerPackage": true, + "licence": "MIT", + "name": "[COMPILER PACKAGE] twinBASIC - VB Compatibility Package (Forms)", + "path32": "", + "path64": "", + "publisher": "TWINBASIC-COMPILER", + "symbolId": "VB", + "versionBuild": 0, + "versionMajor": 0, + "versionMinor": 0, + "versionRevision": 31 + }, + { + "id": "{C192FB39-64CA-4D9B-B477-A5502F48EFCC}", + "isCompilerPackage": true, + "licence": "MIT", + "name": "[COMPILER PACKAGE] twinBASIC - App global class object", + "path32": "", + "path64": "", + "publisher": "TWINBASIC-COMPILER", + "symbolId": "AppGlobalClassProject", + "versionBuild": 0, + "versionMajor": 1, + "versionMinor": 0, + "versionRevision": 0 + }, + { + "id": "{9847A10D-8987-44B2-8B87-EA77DEF7F1D9}", + "isCompilerPackage": true, + "licence": "MIT", + "name": "[COMPILER PACKAGE] Assert", + "path32": "", + "path64": "", + "publisher": "TWINBASIC-COMPILER", + "symbolId": "Assert", + "versionBuild": 0, + "versionMajor": 0, + "versionMinor": 0, + "versionRevision": 24 + } + ], + "project.settingsVersion": 1, + "project.startupObject": "Sub Main", + "project.warnings": { + "errors": [], + "hints": [], + "ignored": [], + "info": [], + "warnings": [] + }, + "runtime.useUnicodeStandardLibrary": true +} diff --git a/bugs/argument-errors-unspecified-error/src/Sources/RegressionTests.twin b/bugs/argument-errors-unspecified-error/src/Sources/RegressionTests.twin new file mode 100644 index 000000000..6cff6054f --- /dev/null +++ b/bugs/argument-errors-unspecified-error/src/Sources/RegressionTests.twin @@ -0,0 +1,176 @@ +' Regression tests: a procedure that refuses an argument raises the number VB6 and 64-bit VBA +' raise (the VB6 project beside this one prints them), not -2147467259, Unspecified error. +' No case writes to the registry: GetSetting and DeleteSetting are given names that do not +' exist. tbrun --tests runs each case and prints PASS or FAIL. +[TestFixture] +Module RegressionTests + + ' Controls: errors twinBASIC already raises with VB's numbers. + [TestCase] + Public Sub LBound_UnsizedArray_Raises9() + Dim A() As Long + Dim N As Long + On Error Resume Next + N = LBound(A) + Assert.Strict.AreEqual 9&, Err.Number + End Sub + + [TestCase] + Public Sub QBColor_Sixteen_Raises5() + Dim N As Long + On Error Resume Next + N = QBColor(16) + Assert.Strict.AreEqual 5&, Err.Number + End Sub + + [TestCase] + Public Sub Environ_Null_Raises94() + Dim S As String + On Error Resume Next + S = Environ(Null) + Assert.Strict.AreEqual 94&, Err.Number + End Sub + + ' The defect: each of these raises -2147467259. + [TestCase] + Public Sub LBound_DimensionZero_Raises9() + Dim A(1 To 3) As Long + Dim N As Long + On Error Resume Next + N = LBound(A, 0) + Assert.Strict.AreEqual 9&, Err.Number + End Sub + + [TestCase] + Public Sub UBound_DimensionPastRank_Raises9() + Dim A(1 To 3) As Long + Dim N As Long + On Error Resume Next + N = UBound(A, 2) + Assert.Strict.AreEqual 9&, Err.Number + End Sub + + [TestCase] + Public Sub LBound_Dimension32768_Raises6() + Dim A(1 To 3) As Long + Dim N As Long + On Error Resume Next + N = LBound(A, 32768) + Assert.Strict.AreEqual 6&, Err.Number + End Sub + + [TestCase] + Public Sub Switch_OddArguments_Raises5() + Dim V As Variant + On Error Resume Next + V = Switch(True, 1, False) + Assert.Strict.AreEqual 5&, Err.Number + End Sub + + [TestCase] + Public Sub Partition_NegativeStart_Raises5() + Dim V As Variant + On Error Resume Next + V = Partition(5, -1, 10, 1) + Assert.Strict.AreEqual 5&, Err.Number + End Sub + + [TestCase] + Public Sub Partition_IntervalZero_Raises5() + Dim V As Variant + On Error Resume Next + V = Partition(5, 0, 10, 0) + Assert.Strict.AreEqual 5&, Err.Number + End Sub + + [TestCase] + Public Sub Partition_StringNumber_Raises13() + Dim V As Variant + On Error Resume Next + V = Partition("abc", 0, 10, 1) + Assert.Strict.AreEqual 13&, Err.Number + End Sub + + [TestCase] + Public Sub Partition_BeyondLongLong_Raises6() + Dim V As Variant + On Error Resume Next + V = Partition(1E+20, 0, 10, 1) + Assert.Strict.AreEqual 6&, Err.Number + End Sub + + [TestCase] + Public Sub Environ_Zero_Raises5() + Dim S As String + On Error Resume Next + S = Environ(0) + Assert.Strict.AreEqual 5&, Err.Number + End Sub + + [TestCase] + Public Sub Environ_PositionWithNoEntry_Raises5() + Dim S As String + On Error Resume Next + S = Environ(32767) + Assert.Strict.AreEqual 5&, Err.Number + End Sub + + [TestCase] + Public Sub Environ_EmptyName_Raises5() + Dim S As String + On Error Resume Next + S = Environ("") + Assert.Strict.AreEqual 5&, Err.Number + End Sub + + [TestCase] + Public Sub Environ_Above32767_Raises6() + Dim S As String + On Error Resume Next + S = Environ(32768) + Assert.Strict.AreEqual 6&, Err.Number + End Sub + + [TestCase] + Public Sub GetObject_NoArguments_Raises13() + Dim O As Object + On Error Resume Next + Set O = GetObject() + Assert.Strict.AreEqual 13&, Err.Number + End Sub + + [TestCase] + Public Sub CallByName_CallTypeZero_Raises438() + Dim D As Object + Dim V As Variant + Set D = CreateObject("Scripting.Dictionary") + On Error Resume Next + V = CallByName(D, "Count", 0) + Assert.Strict.AreEqual 438&, Err.Number + End Sub + + [TestCase] + Public Sub CallByName_Nothing_Raises5() + Dim D As Object + Dim V As Variant + On Error Resume Next + V = CallByName(D, "Count", VbGet) + Assert.Strict.AreEqual 5&, Err.Number + End Sub + + [TestCase] + Public Sub GetSetting_EmptyAppName_Raises5() + Dim S As String + On Error Resume Next + S = GetSetting("", "Section", "Key") + Assert.Strict.AreEqual 5&, Err.Number + End Sub + + [TestCase] + Public Sub DeleteSetting_MissingApplication_Raises5() + On Error Resume Next + DeleteSetting "NoSuchApplication-argument-errors-unspecified-error" + Assert.Strict.AreEqual 5&, Err.Number + End Sub + +End Module diff --git a/bugs/redim-inverted-bounds-error/src/Sources/Startup.twin b/bugs/argument-errors-unspecified-error/src/Sources/Startup.twin similarity index 100% rename from bugs/redim-inverted-bounds-error/src/Sources/Startup.twin rename to bugs/argument-errors-unspecified-error/src/Sources/Startup.twin diff --git a/bugs/argument-errors-unspecified-error/vb6/Module1.bas b/bugs/argument-errors-unspecified-error/vb6/Module1.bas new file mode 100644 index 000000000..ceb505775 --- /dev/null +++ b/bugs/argument-errors-unspecified-error/vb6/Module1.bas @@ -0,0 +1,41 @@ +Attribute VB_Name = "Module1" +Option Explicit + +' What VB6 raises for each argument the twinBASIC test cases give, in the same order. +' Build and run it with: node scripts/bug_repro.mjs vb6 argument-errors-unspecified-error +' No case writes to the registry: GetSetting and DeleteSetting are given names that do not exist. + +Private Sub Report(ByVal Label As String) + Print #9, Label & ": " & Err.Number & " " & Err.Description + Err.Clear +End Sub + +Sub Main() + Dim A() As Long, B(1 To 3) As Long, N As Long, S As String, V As Variant + Dim O As Object, D As Object + Open App.Path & "\out.txt" For Output As #9 + On Error Resume Next + N = LBound(A): Report "LBound_UnsizedArray" + N = QBColor(16): Report "QBColor_Sixteen" + S = Environ(Null): Report "Environ_Null" + N = LBound(B, 0): Report "LBound_DimensionZero" + N = UBound(B, 2): Report "UBound_DimensionPastRank" + N = LBound(B, 32768): Report "LBound_Dimension32768" + V = Switch(True, 1, False): Report "Switch_OddArguments" + V = Partition(5, -1, 10, 1): Report "Partition_NegativeStart" + V = Partition(5, 0, 10, 0): Report "Partition_IntervalZero" + V = Partition("abc", 0, 10, 1): Report "Partition_StringNumber" + V = Partition(1E+20, 0, 10, 1): Report "Partition_BeyondLongLong" + S = Environ(0): Report "Environ_Zero" + S = Environ(32767): Report "Environ_PositionWithNoEntry" + S = Environ(""): Report "Environ_EmptyName" + S = Environ(32768): Report "Environ_Above32767" + Set O = GetObject(): Report "GetObject_NoArguments" + Set D = CreateObject("Scripting.Dictionary") + V = CallByName(D, "Count", 0): Report "CallByName_CallTypeZero" + Set D = Nothing + V = CallByName(D, "Count", VbGet): Report "CallByName_Nothing" + S = GetSetting("", "Section", "Key"): Report "GetSetting_EmptyAppName" + DeleteSetting "NoSuchApplication-argument-errors-unspecified-error": Report "DeleteSetting_MissingApplication" + Close #9 +End Sub diff --git a/bugs/negative-zero-text-win64/vb6/Probe.vbp b/bugs/argument-errors-unspecified-error/vb6/Probe.vbp similarity index 100% rename from bugs/negative-zero-text-win64/vb6/Probe.vbp rename to bugs/argument-errors-unspecified-error/vb6/Probe.vbp diff --git a/bugs/callbyname-dispatch-hresults/callbyname-dispatch-hresults.twinproj b/bugs/callbyname-dispatch-hresults/callbyname-dispatch-hresults.twinproj new file mode 100644 index 000000000..58876a9ad Binary files /dev/null and b/bugs/callbyname-dispatch-hresults/callbyname-dispatch-hresults.twinproj differ diff --git a/bugs/callbyname-dispatch-hresults/repro.json b/bugs/callbyname-dispatch-hresults/repro.json new file mode 100644 index 000000000..9e6094e7f --- /dev/null +++ b/bugs/callbyname-dispatch-hresults/repro.json @@ -0,0 +1,16 @@ +{ + "mode": "test", + "expect": { + "exit": 7, + "output": [ + "^PASS RegressionTests.LateBound_AddExistingKey_Raises457$", + "^PASS RegressionTests.LateBound_RemoveMissingKey_Raises32811$", + "^PASS RegressionTests.CallByName_UnknownName_Raises438$", + "^FAIL RegressionTests.CallByName_AddExistingKey_Raises457: .*Actual: -2147352567 ", + "^FAIL RegressionTests.CallByName_RemoveMissingKey_Raises32811: .*Actual: -2147352567 ", + "^FAIL RegressionTests.LateBound_CompareModeAfterAdd_Raises5: .*Actual: -2147352567 ", + "^FAIL RegressionTests.CallByName_CompareModeAfterAdd_Raises5: .*Actual: -2147352567 ", + "^FAIL RegressionTests.CallByName_EmptyName_Raises450: .*Actual: 438 " + ] + } +} diff --git a/bugs/callbyname-dispatch-hresults/src/Settings b/bugs/callbyname-dispatch-hresults/src/Settings new file mode 100644 index 000000000..71f3f473b --- /dev/null +++ b/bugs/callbyname-dispatch-hresults/src/Settings @@ -0,0 +1,75 @@ +{ + "configuration.inherits": "Defaults", + "project.appTitle": "CallbynameDispatchHresults", + "project.buildPath": "${SourcePath}\\Build\\${ProjectName}_${Architecture}.${FileExtension}", + "project.buildType": "Standard EXE", + "project.description": "Reproduces: CallByName raises the IDispatch HRESULT where VB6 and VBA raise 450, 451 or the member's own error", + "project.exportPathIsV2": true, + "project.id": "{6FB08943-4DF4-4E0B-B168-911A7C80F1CA}", + "project.name": "CallbynameDispatchHresults", + "project.optionExplicit": true, + "project.references": [ + { + "id": "{00020430-0000-0000-C000-000000000046}", + "lcid": 0, + "name": "OLE Automation", + "path32": "C:\\Windows\\SysWOW64\\stdole2.tlb", + "path64": "C:\\Windows\\System32\\stdole2.tlb", + "symbolId": "stdole", + "versionMajor": 2, + "versionMinor": 0 + }, + { + "id": "{F50B82D0-DCAB-43FE-9631-11959D4A4728}", + "isCompilerPackage": true, + "licence": "MIT", + "name": "[COMPILER PACKAGE] twinBASIC - VB Compatibility Package (Forms)", + "path32": "", + "path64": "", + "publisher": "TWINBASIC-COMPILER", + "symbolId": "VB", + "versionBuild": 0, + "versionMajor": 0, + "versionMinor": 0, + "versionRevision": 31 + }, + { + "id": "{C192FB39-64CA-4D9B-B477-A5502F48EFCC}", + "isCompilerPackage": true, + "licence": "MIT", + "name": "[COMPILER PACKAGE] twinBASIC - App global class object", + "path32": "", + "path64": "", + "publisher": "TWINBASIC-COMPILER", + "symbolId": "AppGlobalClassProject", + "versionBuild": 0, + "versionMajor": 1, + "versionMinor": 0, + "versionRevision": 0 + }, + { + "id": "{9847A10D-8987-44B2-8B87-EA77DEF7F1D9}", + "isCompilerPackage": true, + "licence": "MIT", + "name": "[COMPILER PACKAGE] Assert", + "path32": "", + "path64": "", + "publisher": "TWINBASIC-COMPILER", + "symbolId": "Assert", + "versionBuild": 0, + "versionMajor": 0, + "versionMinor": 0, + "versionRevision": 24 + } + ], + "project.settingsVersion": 1, + "project.startupObject": "Sub Main", + "project.warnings": { + "errors": [], + "hints": [], + "ignored": [], + "info": [], + "warnings": [] + }, + "runtime.useUnicodeStandardLibrary": true +} diff --git a/bugs/callbyname-dispatch-hresults/src/Sources/RegressionTests.twin b/bugs/callbyname-dispatch-hresults/src/Sources/RegressionTests.twin new file mode 100644 index 000000000..67567c1f4 --- /dev/null +++ b/bugs/callbyname-dispatch-hresults/src/Sources/RegressionTests.twin @@ -0,0 +1,95 @@ +' Regression tests: an error a COM object raises reaches CallByName, and a late-bound property +' assignment, as the object's own number, as it reaches a late-bound method call, and as VB6 +' and 64-bit VBA give it (the VB6 project beside this one prints them); CallByName with an empty +' name raises 450. The object is a Scripting.Dictionary. tbrun --tests runs each case and prints +' PASS or FAIL. +[TestFixture] +Module RegressionTests + + Private Function NewDictionary() As Object + Set NewDictionary = CreateObject("Scripting.Dictionary") + End Function + + ' Controls: a late-bound method call gets the object's own number, and CallByName of a + ' name the object does not have raises 438. + [TestCase] + Public Sub LateBound_AddExistingKey_Raises457() + Dim D As Object + Set D = NewDictionary() + D.Add "x", 1 + On Error Resume Next + D.Add "x", 2 + Assert.Strict.AreEqual 457&, Err.Number + End Sub + + [TestCase] + Public Sub LateBound_RemoveMissingKey_Raises32811() + Dim D As Object + Set D = NewDictionary() + On Error Resume Next + D.Remove "nope" + Assert.Strict.AreEqual 32811&, Err.Number + End Sub + + [TestCase] + Public Sub CallByName_UnknownName_Raises438() + Dim D As Object + Dim V As Variant + Set D = NewDictionary() + On Error Resume Next + V = CallByName(D, "Nope", VbGet) + Assert.Strict.AreEqual 438&, Err.Number + End Sub + + ' The defect: the object's own error arrives as -2147352567, Exception occurred. + [TestCase] + Public Sub CallByName_AddExistingKey_Raises457() + Dim D As Object + Set D = NewDictionary() + D.Add "x", 1 + On Error Resume Next + CallByName D, "Add", VbMethod, "x", 2 + Assert.Strict.AreEqual 457&, Err.Number + End Sub + + [TestCase] + Public Sub CallByName_RemoveMissingKey_Raises32811() + Dim D As Object + Set D = NewDictionary() + On Error Resume Next + CallByName D, "Remove", VbMethod, "nope" + Assert.Strict.AreEqual 32811&, Err.Number + End Sub + + [TestCase] + Public Sub LateBound_CompareModeAfterAdd_Raises5() + Dim D As Object + Set D = NewDictionary() + D.Add "x", 1 + On Error Resume Next + D.CompareMode = 1 + Assert.Strict.AreEqual 5&, Err.Number + End Sub + + [TestCase] + Public Sub CallByName_CompareModeAfterAdd_Raises5() + Dim D As Object + Set D = NewDictionary() + D.Add "x", 1 + On Error Resume Next + CallByName D, "CompareMode", VbLet, 1 + Assert.Strict.AreEqual 5&, Err.Number + End Sub + + ' The defect: an empty name raises 438, where VB6 raises 450. + [TestCase] + Public Sub CallByName_EmptyName_Raises450() + Dim D As Object + Dim V As Variant + Set D = NewDictionary() + On Error Resume Next + V = CallByName(D, "", VbGet) + Assert.Strict.AreEqual 450&, Err.Number + End Sub + +End Module diff --git a/bugs/string-null-character/src/Sources/Startup.twin b/bugs/callbyname-dispatch-hresults/src/Sources/Startup.twin similarity index 100% rename from bugs/string-null-character/src/Sources/Startup.twin rename to bugs/callbyname-dispatch-hresults/src/Sources/Startup.twin diff --git a/bugs/callbyname-dispatch-hresults/vb6/Module1.bas b/bugs/callbyname-dispatch-hresults/vb6/Module1.bas new file mode 100644 index 000000000..a86bb4bfa --- /dev/null +++ b/bugs/callbyname-dispatch-hresults/vb6/Module1.bas @@ -0,0 +1,36 @@ +Attribute VB_Name = "Module1" +Option Explicit + +' What VB6 raises for each call the twinBASIC test cases make, in the same order. +' Build and run it with: node scripts/bug_repro.mjs vb6 callbyname-dispatch-hresults + +Private Sub Report(ByVal Label As String) + Print #9, Label & ": " & Err.Number & " " & Replace(Err.Description, vbCrLf, " / ") + Err.Clear +End Sub + +Private Function NewDictionary() As Object + Set NewDictionary = CreateObject("Scripting.Dictionary") +End Function + +Sub Main() + Dim D As Object, V As Variant + Open App.Path & "\out.txt" For Output As #9 + On Error Resume Next + Set D = NewDictionary(): D.Add "x", 1 + D.Add "x", 2: Report "LateBound_AddExistingKey" + Set D = NewDictionary() + D.Remove "nope": Report "LateBound_RemoveMissingKey" + V = CallByName(D, "Nope", VbGet): Report "CallByName_UnknownName" + Set D = NewDictionary(): D.Add "x", 1 + CallByName D, "Add", VbMethod, "x", 2: Report "CallByName_AddExistingKey" + Set D = NewDictionary() + CallByName D, "Remove", VbMethod, "nope": Report "CallByName_RemoveMissingKey" + Set D = NewDictionary(): D.Add "x", 1 + D.CompareMode = 1: Report "LateBound_CompareModeAfterAdd" + Set D = NewDictionary(): D.Add "x", 1 + CallByName D, "CompareMode", VbLet, 1: Report "CallByName_CompareModeAfterAdd" + Set D = NewDictionary() + V = CallByName(D, "", VbGet): Report "CallByName_EmptyName" + Close #9 +End Sub diff --git a/bugs/redim-inverted-bounds-error/vb6/Probe.vbp b/bugs/callbyname-dispatch-hresults/vb6/Probe.vbp similarity index 100% rename from bugs/redim-inverted-bounds-error/vb6/Probe.vbp rename to bugs/callbyname-dispatch-hresults/vb6/Probe.vbp diff --git a/bugs/createobject-getobject-raw-hresults/createobject-getobject-raw-hresults.twinproj b/bugs/createobject-getobject-raw-hresults/createobject-getobject-raw-hresults.twinproj new file mode 100644 index 000000000..7fe466f2b Binary files /dev/null and b/bugs/createobject-getobject-raw-hresults/createobject-getobject-raw-hresults.twinproj differ diff --git a/bugs/createobject-getobject-raw-hresults/repro.json b/bugs/createobject-getobject-raw-hresults/repro.json new file mode 100644 index 000000000..937a217e0 --- /dev/null +++ b/bugs/createobject-getobject-raw-hresults/repro.json @@ -0,0 +1,19 @@ +{ + "mode": "test", + "expect": { + "exit": 7, + "output": [ + "^PASS RegressionTests.CreateObject_RegisteredClass_Creates$", + "^PASS RegressionTests.CreateObject_Null_Raises94$", + "^PASS RegressionTests.GetObject_EmptyPathWithClass_Creates$", + "^FAIL RegressionTests.CreateObject_UnknownClass_Raises429: .*Actual: -2147221020 ", + "^FAIL RegressionTests.CreateObject_UnregisteredClsid_Raises429: .*Actual: -2147221164 ", + "^FAIL RegressionTests.CreateObject_MalformedClsid_Raises429: .*Actual: -2147221005 ", + "^FAIL RegressionTests.CreateObject_EmptyClass_Raises429: .*Actual: 5 ", + "^FAIL RegressionTests.GetObject_MissingFile_Raises432: .*Actual: -2147221014 ", + "^FAIL RegressionTests.GetObject_MissingFileWithClass_Raises432: .*Actual: -2147467262 ", + "^FAIL RegressionTests.GetObject_UnknownClass_Raises429: .*Actual: -2147221005 ", + "^FAIL RegressionTests.GetObject_NoRunningInstance_Raises429: .*Actual: -2147221021 " + ] + } +} diff --git a/bugs/createobject-getobject-raw-hresults/src/Settings b/bugs/createobject-getobject-raw-hresults/src/Settings new file mode 100644 index 000000000..58b6c47f8 --- /dev/null +++ b/bugs/createobject-getobject-raw-hresults/src/Settings @@ -0,0 +1,75 @@ +{ + "configuration.inherits": "Defaults", + "project.appTitle": "CreateobjectGetobjectRawHresults", + "project.buildPath": "${SourcePath}\\Build\\${ProjectName}_${Architecture}.${FileExtension}", + "project.buildType": "Standard EXE", + "project.description": "Reproduces: CreateObject and GetObject raise the COM HRESULT where VB6 and VBA raise 429 or 432", + "project.exportPathIsV2": true, + "project.id": "{F7812AF0-A98A-4D70-9E8C-81FE3C76442B}", + "project.name": "CreateobjectGetobjectRawHresults", + "project.optionExplicit": true, + "project.references": [ + { + "id": "{00020430-0000-0000-C000-000000000046}", + "lcid": 0, + "name": "OLE Automation", + "path32": "C:\\Windows\\SysWOW64\\stdole2.tlb", + "path64": "C:\\Windows\\System32\\stdole2.tlb", + "symbolId": "stdole", + "versionMajor": 2, + "versionMinor": 0 + }, + { + "id": "{F50B82D0-DCAB-43FE-9631-11959D4A4728}", + "isCompilerPackage": true, + "licence": "MIT", + "name": "[COMPILER PACKAGE] twinBASIC - VB Compatibility Package (Forms)", + "path32": "", + "path64": "", + "publisher": "TWINBASIC-COMPILER", + "symbolId": "VB", + "versionBuild": 0, + "versionMajor": 0, + "versionMinor": 0, + "versionRevision": 31 + }, + { + "id": "{C192FB39-64CA-4D9B-B477-A5502F48EFCC}", + "isCompilerPackage": true, + "licence": "MIT", + "name": "[COMPILER PACKAGE] twinBASIC - App global class object", + "path32": "", + "path64": "", + "publisher": "TWINBASIC-COMPILER", + "symbolId": "AppGlobalClassProject", + "versionBuild": 0, + "versionMajor": 1, + "versionMinor": 0, + "versionRevision": 0 + }, + { + "id": "{9847A10D-8987-44B2-8B87-EA77DEF7F1D9}", + "isCompilerPackage": true, + "licence": "MIT", + "name": "[COMPILER PACKAGE] Assert", + "path32": "", + "path64": "", + "publisher": "TWINBASIC-COMPILER", + "symbolId": "Assert", + "versionBuild": 0, + "versionMajor": 0, + "versionMinor": 0, + "versionRevision": 24 + } + ], + "project.settingsVersion": 1, + "project.startupObject": "Sub Main", + "project.warnings": { + "errors": [], + "hints": [], + "ignored": [], + "info": [], + "warnings": [] + }, + "runtime.useUnicodeStandardLibrary": true +} diff --git a/bugs/createobject-getobject-raw-hresults/src/Sources/RegressionTests.twin b/bugs/createobject-getobject-raw-hresults/src/Sources/RegressionTests.twin new file mode 100644 index 000000000..b15dd5d59 --- /dev/null +++ b/bugs/createobject-getobject-raw-hresults/src/Sources/RegressionTests.twin @@ -0,0 +1,98 @@ +' Regression tests: CreateObject and GetObject raise 429, ActiveX component can't create +' object, or 432, File name or class name not found during Automation operation, as VB6 and +' 64-bit VBA do (the VB6 project beside this one prints them), not the HRESULT COM returned. +' tbrun --tests runs each case and prints PASS or FAIL. +[TestFixture] +Module RegressionTests + + ' Controls: a class that exists is created, and Null raises 94. + [TestCase] + Public Sub CreateObject_RegisteredClass_Creates() + Dim O As Object + On Error Resume Next + Set O = CreateObject("Scripting.Dictionary") + Assert.Strict.AreEqual 0&, Err.Number + End Sub + + [TestCase] + Public Sub CreateObject_Null_Raises94() + Dim O As Object + On Error Resume Next + Set O = CreateObject(Null) + Assert.Strict.AreEqual 94&, Err.Number + End Sub + + [TestCase] + Public Sub GetObject_EmptyPathWithClass_Creates() + Dim O As Object + On Error Resume Next + Set O = GetObject("", "Scripting.Dictionary") + Assert.Strict.AreEqual 0&, Err.Number + End Sub + + ' The defect: each of these raises the HRESULT, or 5. + [TestCase] + Public Sub CreateObject_UnknownClass_Raises429() + Dim O As Object + On Error Resume Next + Set O = CreateObject("No.Such.Class") + Assert.Strict.AreEqual 429&, Err.Number + End Sub + + [TestCase] + Public Sub CreateObject_UnregisteredClsid_Raises429() + Dim O As Object + On Error Resume Next + Set O = CreateObject("new:{00000000-0000-0000-0000-000000000000}") + Assert.Strict.AreEqual 429&, Err.Number + End Sub + + [TestCase] + Public Sub CreateObject_MalformedClsid_Raises429() + Dim O As Object + On Error Resume Next + Set O = CreateObject("new:not-a-guid") + Assert.Strict.AreEqual 429&, Err.Number + End Sub + + [TestCase] + Public Sub CreateObject_EmptyClass_Raises429() + Dim O As Object + On Error Resume Next + Set O = CreateObject("") + Assert.Strict.AreEqual 429&, Err.Number + End Sub + + [TestCase] + Public Sub GetObject_MissingFile_Raises432() + Dim O As Object + On Error Resume Next + Set O = GetObject("C:\no\such\file.xyz") + Assert.Strict.AreEqual 432&, Err.Number + End Sub + + [TestCase] + Public Sub GetObject_MissingFileWithClass_Raises432() + Dim O As Object + On Error Resume Next + Set O = GetObject("C:\no\such\file.xyz", "Scripting.Dictionary") + Assert.Strict.AreEqual 432&, Err.Number + End Sub + + [TestCase] + Public Sub GetObject_UnknownClass_Raises429() + Dim O As Object + On Error Resume Next + Set O = GetObject(, "No.Such.Class") + Assert.Strict.AreEqual 429&, Err.Number + End Sub + + [TestCase] + Public Sub GetObject_NoRunningInstance_Raises429() + Dim O As Object + On Error Resume Next + Set O = GetObject(, "Scripting.Dictionary") + Assert.Strict.AreEqual 429&, Err.Number + End Sub + +End Module diff --git a/bugs/createobject-getobject-raw-hresults/src/Sources/Startup.twin b/bugs/createobject-getobject-raw-hresults/src/Sources/Startup.twin new file mode 100644 index 000000000..be8f5c8bc --- /dev/null +++ b/bugs/createobject-getobject-raw-hresults/src/Sources/Startup.twin @@ -0,0 +1,6 @@ +Module Startup + + Public Sub Main() + End Sub + +End Module diff --git a/bugs/createobject-getobject-raw-hresults/vb6/Module1.bas b/bugs/createobject-getobject-raw-hresults/vb6/Module1.bas new file mode 100644 index 000000000..529a36383 --- /dev/null +++ b/bugs/createobject-getobject-raw-hresults/vb6/Module1.bas @@ -0,0 +1,28 @@ +Attribute VB_Name = "Module1" +Option Explicit + +' What VB6 raises for each call the twinBASIC test cases make, in the same order. +' Build and run it with: node scripts/bug_repro.mjs vb6 createobject-getobject-raw-hresults + +Private Sub Report(ByVal Label As String) + Print #9, Label & ": " & Err.Number & " " & Replace(Err.Description, vbCrLf, " / ") + Err.Clear +End Sub + +Sub Main() + Dim O As Object + Open App.Path & "\out.txt" For Output As #9 + On Error Resume Next + Set O = CreateObject("Scripting.Dictionary"): Report "CreateObject_RegisteredClass" + Set O = CreateObject(Null): Report "CreateObject_Null" + Set O = GetObject("", "Scripting.Dictionary"): Report "GetObject_EmptyPathWithClass" + Set O = CreateObject("No.Such.Class"): Report "CreateObject_UnknownClass" + Set O = CreateObject("new:{00000000-0000-0000-0000-000000000000}"): Report "CreateObject_UnregisteredClsid" + Set O = CreateObject("new:not-a-guid"): Report "CreateObject_MalformedClsid" + Set O = CreateObject(""): Report "CreateObject_EmptyClass" + Set O = GetObject("C:\no\such\file.xyz"): Report "GetObject_MissingFile" + Set O = GetObject("C:\no\such\file.xyz", "Scripting.Dictionary"): Report "GetObject_MissingFileWithClass" + Set O = GetObject(, "No.Such.Class"): Report "GetObject_UnknownClass" + Set O = GetObject(, "Scripting.Dictionary"): Report "GetObject_NoRunningInstance" + Close #9 +End Sub diff --git a/bugs/string-null-character/vb6/Probe.vbp b/bugs/createobject-getobject-raw-hresults/vb6/Probe.vbp similarity index 100% rename from bugs/string-null-character/vb6/Probe.vbp rename to bugs/createobject-getobject-raw-hresults/vb6/Probe.vbp diff --git a/bugs/date-time-set-errors/date-time-set-errors.twinproj b/bugs/date-time-set-errors/date-time-set-errors.twinproj new file mode 100644 index 000000000..a70cf878f Binary files /dev/null and b/bugs/date-time-set-errors/date-time-set-errors.twinproj differ diff --git a/bugs/date-time-set-errors/repro.json b/bugs/date-time-set-errors/repro.json new file mode 100644 index 000000000..7eb41fd88 --- /dev/null +++ b/bugs/date-time-set-errors/repro.json @@ -0,0 +1,19 @@ +{ + "mode": "test", + "expect": { + "exit": 7, + "output": [ + "^PASS RegressionTests.Date_SetNotADate_Raises13$", + "^PASS RegressionTests.DateString_SetNull_Raises94$", + "^PASS RegressionTests.DateString_SetMmDdYyyy_Accepted$", + "^PASS RegressionTests.TimeString_SetHhMmSs_Accepted$", + "^FAIL RegressionTests.Date_SetNull_Raises13: .*Actual: 94 ", + "^FAIL RegressionTests.Time_SetNull_Raises13: .*Actual: 94 ", + "^FAIL RegressionTests.DateString_SetNotADate_Raises5: .*Actual: -2147467259 ", + "^FAIL RegressionTests.TimeString_SetNotATime_Raises5: .*Actual: -2147467259 ", + "^FAIL RegressionTests.DateString_SetSlashes_Accepted: .*Actual: False[.]", + "^FAIL RegressionTests.DateString_SetTwoDigitYear_Accepted: .*Actual: False[.]", + "^FAIL RegressionTests.TimeString_SetHhMm_Accepted: .*Actual: False[.]" + ] + } +} diff --git a/bugs/date-time-set-errors/src/Settings b/bugs/date-time-set-errors/src/Settings new file mode 100644 index 000000000..f104d0fa6 --- /dev/null +++ b/bugs/date-time-set-errors/src/Settings @@ -0,0 +1,75 @@ +{ + "configuration.inherits": "Defaults", + "project.appTitle": "DateTimeSetErrors", + "project.buildPath": "${SourcePath}\\Build\\${ProjectName}_${Architecture}.${FileExtension}", + "project.buildType": "Standard EXE", + "project.description": "Reproduces: x", + "project.exportPathIsV2": true, + "project.id": "{8CEE9429-9865-4DCF-80FE-FD8177E44BE1}", + "project.name": "DateTimeSetErrors", + "project.optionExplicit": true, + "project.references": [ + { + "id": "{00020430-0000-0000-C000-000000000046}", + "lcid": 0, + "name": "OLE Automation", + "path32": "C:\\Windows\\SysWOW64\\stdole2.tlb", + "path64": "C:\\Windows\\System32\\stdole2.tlb", + "symbolId": "stdole", + "versionMajor": 2, + "versionMinor": 0 + }, + { + "id": "{F50B82D0-DCAB-43FE-9631-11959D4A4728}", + "isCompilerPackage": true, + "licence": "MIT", + "name": "[COMPILER PACKAGE] twinBASIC - VB Compatibility Package (Forms)", + "path32": "", + "path64": "", + "publisher": "TWINBASIC-COMPILER", + "symbolId": "VB", + "versionBuild": 0, + "versionMajor": 0, + "versionMinor": 0, + "versionRevision": 31 + }, + { + "id": "{C192FB39-64CA-4D9B-B477-A5502F48EFCC}", + "isCompilerPackage": true, + "licence": "MIT", + "name": "[COMPILER PACKAGE] twinBASIC - App global class object", + "path32": "", + "path64": "", + "publisher": "TWINBASIC-COMPILER", + "symbolId": "AppGlobalClassProject", + "versionBuild": 0, + "versionMajor": 1, + "versionMinor": 0, + "versionRevision": 0 + }, + { + "id": "{9847A10D-8987-44B2-8B87-EA77DEF7F1D9}", + "isCompilerPackage": true, + "licence": "MIT", + "name": "[COMPILER PACKAGE] Assert", + "path32": "", + "path64": "", + "publisher": "TWINBASIC-COMPILER", + "symbolId": "Assert", + "versionBuild": 0, + "versionMajor": 0, + "versionMinor": 0, + "versionRevision": 24 + } + ], + "project.settingsVersion": 1, + "project.startupObject": "Sub Main", + "project.warnings": { + "errors": [], + "hints": [], + "ignored": [], + "info": [], + "warnings": [] + }, + "runtime.useUnicodeStandardLibrary": true +} diff --git a/bugs/date-time-set-errors/src/Sources/RegressionTests.twin b/bugs/date-time-set-errors/src/Sources/RegressionTests.twin new file mode 100644 index 000000000..ca542280e --- /dev/null +++ b/bugs/date-time-set-errors/src/Sources/RegressionTests.twin @@ -0,0 +1,97 @@ +' Regression tests: assigning to Date, Date$, Time and Time$ raises the errors VB6 raises +' (the VB6 project beside this one prints them). Every case sets the clock to what it +' already holds, or to a value that cannot be read, so a run with the privilege to set +' the clock changes nothing. Without the privilege, a value that is accepted raises 70, +' Permission denied; with it, none. tbrun --tests runs each case and prints PASS or FAIL. +[TestFixture] +Module RegressionTests + + ' 0 or 70: the value was accepted, and only setting the clock could fail. + Private Function Accepted(ByVal N As Long) As Boolean + Return N = 0 Or N = 70 + End Function + + ' Controls: a string that is not a date raises 13, Date$ = Null raises 94, and a + ' value of the documented form is accepted. + [TestCase] + Public Sub Date_SetNotADate_Raises13() + On Error Resume Next + Date = "abc" + Assert.Strict.AreEqual 13&, Err.Number + End Sub + + [TestCase] + Public Sub DateString_SetNull_Raises94() + On Error Resume Next + Date$ = Null + Assert.Strict.AreEqual 94&, Err.Number + End Sub + + [TestCase] + Public Sub DateString_SetMmDdYyyy_Accepted() + On Error Resume Next + Date$ = Format$(Date, "mm-dd-yyyy") + Assert.Strict.IsTrue Accepted(Err.Number) + End Sub + + [TestCase] + Public Sub TimeString_SetHhMmSs_Accepted() + On Error Resume Next + Time$ = Format$(Time, "hh:mm:ss") + Assert.Strict.IsTrue Accepted(Err.Number) + End Sub + + ' The defect: Null raises 94 where VB6 raises 13. + [TestCase] + Public Sub Date_SetNull_Raises13() + On Error Resume Next + Date = Null + Assert.Strict.AreEqual 13&, Err.Number + End Sub + + [TestCase] + Public Sub Time_SetNull_Raises13() + On Error Resume Next + Time = Null + Assert.Strict.AreEqual 13&, Err.Number + End Sub + + ' The defect: a string Date$ or Time$ cannot read raises -2147467259, Unspecified + ' error, where VB6 raises 5. + [TestCase] + Public Sub DateString_SetNotADate_Raises5() + On Error Resume Next + Date$ = "abc" + Assert.Strict.AreEqual 5&, Err.Number + End Sub + + [TestCase] + Public Sub TimeString_SetNotATime_Raises5() + On Error Resume Next + Time$ = "abc" + Assert.Strict.AreEqual 5&, Err.Number + End Sub + + ' The defect: forms VB6 accepts raise -2147467259. + [TestCase] + Public Sub DateString_SetSlashes_Accepted() + On Error Resume Next + Date$ = Format$(Date, "m/d/yyyy") + Assert.Strict.IsTrue Accepted(Err.Number) + End Sub + + [TestCase] + Public Sub DateString_SetTwoDigitYear_Accepted() + On Error Resume Next + Date$ = Format$(Date, "mm-dd-yy") + Assert.Strict.IsTrue Accepted(Err.Number) + End Sub + + [TestCase] + Public Sub TimeString_SetHhMm_Accepted() + On Error Resume Next + Time$ = Format$(Time, "hh:mm") + Assert.Strict.IsTrue Accepted(Err.Number) + End Sub + +End Module diff --git a/bugs/date-time-set-errors/src/Sources/Startup.twin b/bugs/date-time-set-errors/src/Sources/Startup.twin new file mode 100644 index 000000000..be8f5c8bc --- /dev/null +++ b/bugs/date-time-set-errors/src/Sources/Startup.twin @@ -0,0 +1,6 @@ +Module Startup + + Public Sub Main() + End Sub + +End Module diff --git a/bugs/date-time-set-errors/vb6/Module1.bas b/bugs/date-time-set-errors/vb6/Module1.bas new file mode 100644 index 000000000..fea41e17f --- /dev/null +++ b/bugs/date-time-set-errors/vb6/Module1.bas @@ -0,0 +1,29 @@ +Attribute VB_Name = "Module1" +Option Explicit + +' What VB6 raises when Date, Date$, Time and Time$ are assigned. Every value is the clock's +' own or one that cannot be read, so a run with the privilege to set the clock changes +' nothing; without it, an accepted value raises 70, Permission denied. +' Build and run it with: node scripts/bug_repro.mjs vb6 date-time-set-errors + +Private Sub Report(ByVal Label As String) + Print #9, Label & ": " & Err.Number & " " & Err.Description + Err.Clear +End Sub + +Sub Main() + Open App.Path & "\out.txt" For Output As #9 + On Error Resume Next + Date = "abc": Report "Date_SetNotADate" + Date$ = Null: Report "DateString_SetNull" + Date$ = Format$(Date, "mm-dd-yyyy"): Report "DateString_SetMmDdYyyy" + Time$ = Format$(Time, "hh:mm:ss"): Report "TimeString_SetHhMmSs" + Date = Null: Report "Date_SetNull" + Time = Null: Report "Time_SetNull" + Date$ = "abc": Report "DateString_SetNotADate" + Time$ = "abc": Report "TimeString_SetNotATime" + Date$ = Format$(Date, "m/d/yyyy"): Report "DateString_SetSlashes" + Date$ = Format$(Date, "mm-dd-yy"): Report "DateString_SetTwoDigitYear" + Time$ = Format$(Time, "hh:mm"): Report "TimeString_SetHhMm" + Close #9 +End Sub diff --git a/bugs/date-time-set-errors/vb6/Probe.vbp b/bugs/date-time-set-errors/vb6/Probe.vbp new file mode 100644 index 000000000..4d210b0e8 --- /dev/null +++ b/bugs/date-time-set-errors/vb6/Probe.vbp @@ -0,0 +1,7 @@ +Type=Exe +Module=Module1; Module1.bas +Startup="Sub Main" +ExeName32="Probe.exe" +Name="Probe" +CompilationType=0 +OptimizationType=0 diff --git a/bugs/dateadd-date-out-of-range-overflow/dateadd-date-out-of-range-overflow.twinproj b/bugs/dateadd-date-out-of-range-overflow/dateadd-date-out-of-range-overflow.twinproj new file mode 100644 index 000000000..2fe5e2b7d Binary files /dev/null and b/bugs/dateadd-date-out-of-range-overflow/dateadd-date-out-of-range-overflow.twinproj differ diff --git a/bugs/dateadd-date-out-of-range-overflow/repro.json b/bugs/dateadd-date-out-of-range-overflow/repro.json new file mode 100644 index 000000000..d096ee5e3 --- /dev/null +++ b/bugs/dateadd-date-out-of-range-overflow/repro.json @@ -0,0 +1,13 @@ +{ + "mode": "test", + "expect": { + "exit": 7, + "output": [ + "^PASS RegressionTests.DatePart_NumberOutOfRange_Raises13$", + "^PASS RegressionTests.Day_NumberOutOfRange_Raises13$", + "^FAIL RegressionTests.DateAdd_NumberAboveRange_Raises13: .*Actual: 6 ", + "^FAIL RegressionTests.DateAdd_NumberBelowRange_Raises13: .*Actual: 6 ", + "^FAIL RegressionTests.DateDiff_NumberOutOfRange_Raises13: .*Actual: 6 " + ] + } +} diff --git a/bugs/dateadd-date-out-of-range-overflow/src/Settings b/bugs/dateadd-date-out-of-range-overflow/src/Settings new file mode 100644 index 000000000..a586a0383 --- /dev/null +++ b/bugs/dateadd-date-out-of-range-overflow/src/Settings @@ -0,0 +1,75 @@ +{ + "configuration.inherits": "Defaults", + "project.appTitle": "DateaddDateOutOfRangeOverflow", + "project.buildPath": "${SourcePath}\\Build\\${ProjectName}_${Architecture}.${FileExtension}", + "project.buildType": "Standard EXE", + "project.description": "Reproduces: x", + "project.exportPathIsV2": true, + "project.id": "{C3C30BC8-29D9-482E-B7AC-66BDFF70292B}", + "project.name": "DateaddDateOutOfRangeOverflow", + "project.optionExplicit": true, + "project.references": [ + { + "id": "{00020430-0000-0000-C000-000000000046}", + "lcid": 0, + "name": "OLE Automation", + "path32": "C:\\Windows\\SysWOW64\\stdole2.tlb", + "path64": "C:\\Windows\\System32\\stdole2.tlb", + "symbolId": "stdole", + "versionMajor": 2, + "versionMinor": 0 + }, + { + "id": "{F50B82D0-DCAB-43FE-9631-11959D4A4728}", + "isCompilerPackage": true, + "licence": "MIT", + "name": "[COMPILER PACKAGE] twinBASIC - VB Compatibility Package (Forms)", + "path32": "", + "path64": "", + "publisher": "TWINBASIC-COMPILER", + "symbolId": "VB", + "versionBuild": 0, + "versionMajor": 0, + "versionMinor": 0, + "versionRevision": 31 + }, + { + "id": "{C192FB39-64CA-4D9B-B477-A5502F48EFCC}", + "isCompilerPackage": true, + "licence": "MIT", + "name": "[COMPILER PACKAGE] twinBASIC - App global class object", + "path32": "", + "path64": "", + "publisher": "TWINBASIC-COMPILER", + "symbolId": "AppGlobalClassProject", + "versionBuild": 0, + "versionMajor": 1, + "versionMinor": 0, + "versionRevision": 0 + }, + { + "id": "{9847A10D-8987-44B2-8B87-EA77DEF7F1D9}", + "isCompilerPackage": true, + "licence": "MIT", + "name": "[COMPILER PACKAGE] Assert", + "path32": "", + "path64": "", + "publisher": "TWINBASIC-COMPILER", + "symbolId": "Assert", + "versionBuild": 0, + "versionMajor": 0, + "versionMinor": 0, + "versionRevision": 24 + } + ], + "project.settingsVersion": 1, + "project.startupObject": "Sub Main", + "project.warnings": { + "errors": [], + "hints": [], + "ignored": [], + "info": [], + "warnings": [] + }, + "runtime.useUnicodeStandardLibrary": true +} diff --git a/bugs/dateadd-date-out-of-range-overflow/src/Sources/RegressionTests.twin b/bugs/dateadd-date-out-of-range-overflow/src/Sources/RegressionTests.twin new file mode 100644 index 000000000..13d2a9590 --- /dev/null +++ b/bugs/dateadd-date-out-of-range-overflow/src/Sources/RegressionTests.twin @@ -0,0 +1,49 @@ +' Regression tests: DateAdd and DateDiff raise error 13 for a date that is a number +' outside the range of dates, as DatePart and Day do, and as VB6 does (the VB6 project +' beside this one prints it). tbrun --tests runs each case and prints PASS or FAIL. +[TestFixture] +Module RegressionTests + + ' Controls: DatePart and Day raise 13 for the same number. + [TestCase] + Public Sub DatePart_NumberOutOfRange_Raises13() + Dim V As Variant + On Error Resume Next + V = DatePart("d", 3000000) + Assert.Strict.AreEqual 13&, Err.Number + End Sub + + [TestCase] + Public Sub Day_NumberOutOfRange_Raises13() + Dim V As Variant + On Error Resume Next + V = Day(3000000) + Assert.Strict.AreEqual 13&, Err.Number + End Sub + + ' The defect: DateAdd and DateDiff raise 6, Overflow. + [TestCase] + Public Sub DateAdd_NumberAboveRange_Raises13() + Dim V As Variant + On Error Resume Next + V = DateAdd("d", 0, 2958466) + Assert.Strict.AreEqual 13&, Err.Number + End Sub + + [TestCase] + Public Sub DateAdd_NumberBelowRange_Raises13() + Dim V As Variant + On Error Resume Next + V = DateAdd("d", 0, -657435) + Assert.Strict.AreEqual 13&, Err.Number + End Sub + + [TestCase] + Public Sub DateDiff_NumberOutOfRange_Raises13() + Dim V As Variant + On Error Resume Next + V = DateDiff("d", #1/1/2000#, 3000000) + Assert.Strict.AreEqual 13&, Err.Number + End Sub + +End Module diff --git a/bugs/dateadd-date-out-of-range-overflow/src/Sources/Startup.twin b/bugs/dateadd-date-out-of-range-overflow/src/Sources/Startup.twin new file mode 100644 index 000000000..be8f5c8bc --- /dev/null +++ b/bugs/dateadd-date-out-of-range-overflow/src/Sources/Startup.twin @@ -0,0 +1,6 @@ +Module Startup + + Public Sub Main() + End Sub + +End Module diff --git a/bugs/dateadd-date-out-of-range-overflow/vb6/Module1.bas b/bugs/dateadd-date-out-of-range-overflow/vb6/Module1.bas new file mode 100644 index 000000000..a24686816 --- /dev/null +++ b/bugs/dateadd-date-out-of-range-overflow/vb6/Module1.bas @@ -0,0 +1,23 @@ +Attribute VB_Name = "Module1" +Option Explicit + +' What VB6 raises for a date that is a number outside the range of dates: 13, in DatePart, +' Day, DateAdd and DateDiff alike. +' Build and run it with: node scripts/bug_repro.mjs vb6 dateadd-date-out-of-range-overflow + +Private Sub Report(ByVal Label As String) + Print #9, Label & ": " & Err.Number & " " & Err.Description + Err.Clear +End Sub + +Sub Main() + Dim V As Variant + Open App.Path & "\out.txt" For Output As #9 + On Error Resume Next + V = DatePart("d", 3000000): Report "DatePart_NumberOutOfRange" + V = Day(3000000): Report "Day_NumberOutOfRange" + V = DateAdd("d", 0, 2958466): Report "DateAdd_NumberAboveRange" + V = DateAdd("d", 0, -657435): Report "DateAdd_NumberBelowRange" + V = DateDiff("d", #1/1/2000#, 3000000): Report "DateDiff_NumberOutOfRange" + Close #9 +End Sub diff --git a/bugs/dateadd-date-out-of-range-overflow/vb6/Probe.vbp b/bugs/dateadd-date-out-of-range-overflow/vb6/Probe.vbp new file mode 100644 index 000000000..4d210b0e8 --- /dev/null +++ b/bugs/dateadd-date-out-of-range-overflow/vb6/Probe.vbp @@ -0,0 +1,7 @@ +Type=Exe +Module=Module1; Module1.bas +Startup="Sub Main" +ExeName32="Probe.exe" +Name="Probe" +CompilationType=0 +OptimizationType=0 diff --git a/bugs/dateadd-year-below-100/dateadd-year-below-100.twinproj b/bugs/dateadd-year-below-100/dateadd-year-below-100.twinproj new file mode 100644 index 000000000..473082e54 Binary files /dev/null and b/bugs/dateadd-year-below-100/dateadd-year-below-100.twinproj differ diff --git a/bugs/dateadd-year-below-100/repro.json b/bugs/dateadd-year-below-100/repro.json new file mode 100644 index 000000000..92d0d5c59 --- /dev/null +++ b/bugs/dateadd-year-below-100/repro.json @@ -0,0 +1,13 @@ +{ + "mode": "test", + "expect": { + "exit": 7, + "output": [ + "^PASS RegressionTests.DateAdd_DayBefore100_Raises5$", + "^PASS RegressionTests.DateAdd_YearsToYear100_GivesYear100$", + "^FAIL RegressionTests.DateAdd_YearBefore100_Raises5: .*Actual: 0 ", + "^FAIL RegressionTests.DateAdd_QuarterBefore100_Raises5: .*Actual: 0 ", + "^FAIL RegressionTests.DateAdd_MonthBefore100_Raises5: .*Actual: 0 " + ] + } +} diff --git a/bugs/dateadd-year-below-100/src/Settings b/bugs/dateadd-year-below-100/src/Settings new file mode 100644 index 000000000..38b487f18 --- /dev/null +++ b/bugs/dateadd-year-below-100/src/Settings @@ -0,0 +1,75 @@ +{ + "configuration.inherits": "Defaults", + "project.appTitle": "DateaddYearBelow100", + "project.buildPath": "${SourcePath}\\Build\\${ProjectName}_${Architecture}.${FileExtension}", + "project.buildType": "Standard EXE", + "project.description": "Reproduces: x", + "project.exportPathIsV2": true, + "project.id": "{E78506E2-9F0B-4216-8551-9B7EFB02E119}", + "project.name": "DateaddYearBelow100", + "project.optionExplicit": true, + "project.references": [ + { + "id": "{00020430-0000-0000-C000-000000000046}", + "lcid": 0, + "name": "OLE Automation", + "path32": "C:\\Windows\\SysWOW64\\stdole2.tlb", + "path64": "C:\\Windows\\System32\\stdole2.tlb", + "symbolId": "stdole", + "versionMajor": 2, + "versionMinor": 0 + }, + { + "id": "{F50B82D0-DCAB-43FE-9631-11959D4A4728}", + "isCompilerPackage": true, + "licence": "MIT", + "name": "[COMPILER PACKAGE] twinBASIC - VB Compatibility Package (Forms)", + "path32": "", + "path64": "", + "publisher": "TWINBASIC-COMPILER", + "symbolId": "VB", + "versionBuild": 0, + "versionMajor": 0, + "versionMinor": 0, + "versionRevision": 31 + }, + { + "id": "{C192FB39-64CA-4D9B-B477-A5502F48EFCC}", + "isCompilerPackage": true, + "licence": "MIT", + "name": "[COMPILER PACKAGE] twinBASIC - App global class object", + "path32": "", + "path64": "", + "publisher": "TWINBASIC-COMPILER", + "symbolId": "AppGlobalClassProject", + "versionBuild": 0, + "versionMajor": 1, + "versionMinor": 0, + "versionRevision": 0 + }, + { + "id": "{9847A10D-8987-44B2-8B87-EA77DEF7F1D9}", + "isCompilerPackage": true, + "licence": "MIT", + "name": "[COMPILER PACKAGE] Assert", + "path32": "", + "path64": "", + "publisher": "TWINBASIC-COMPILER", + "symbolId": "Assert", + "versionBuild": 0, + "versionMajor": 0, + "versionMinor": 0, + "versionRevision": 24 + } + ], + "project.settingsVersion": 1, + "project.startupObject": "Sub Main", + "project.warnings": { + "errors": [], + "hints": [], + "ignored": [], + "info": [], + "warnings": [] + }, + "runtime.useUnicodeStandardLibrary": true +} diff --git a/bugs/dateadd-year-below-100/src/Sources/RegressionTests.twin b/bugs/dateadd-year-below-100/src/Sources/RegressionTests.twin new file mode 100644 index 000000000..af08a6fe7 --- /dev/null +++ b/bugs/dateadd-year-below-100/src/Sources/RegressionTests.twin @@ -0,0 +1,49 @@ +' Regression tests: DateAdd raises error 5 when the result would be before January 1, 100, +' for the "yyyy", "q" and "m" intervals as for the others, as VB6 does (the VB6 project +' beside this one prints it). tbrun --tests runs each case and prints PASS or FAIL. +[TestFixture] +Module RegressionTests + + ' Controls: a day before January 1, 100 raises 5, and a year that reaches year 100 + ' exactly gives that date. + [TestCase] + Public Sub DateAdd_DayBefore100_Raises5() + Dim V As Variant + On Error Resume Next + V = DateAdd("d", -1, #1/1/100#) + Assert.Strict.AreEqual 5&, Err.Number + End Sub + + [TestCase] + Public Sub DateAdd_YearsToYear100_GivesYear100() + Dim V As Variant = DateAdd("yyyy", -1900, #1/1/2000#) + Assert.Strict.AreEqual #1/1/100#, CDate(V) + End Sub + + ' The defect: no error, and a date in the 1900s or 2000s: DateAdd("yyyy", -1, #1/1/100#) + ' returns January 1, 1999. + [TestCase] + Public Sub DateAdd_YearBefore100_Raises5() + Dim V As Variant + On Error Resume Next + V = DateAdd("yyyy", -1, #1/1/100#) + Assert.Strict.AreEqual 5&, Err.Number + End Sub + + [TestCase] + Public Sub DateAdd_QuarterBefore100_Raises5() + Dim V As Variant + On Error Resume Next + V = DateAdd("q", -1, #1/1/100#) + Assert.Strict.AreEqual 5&, Err.Number + End Sub + + [TestCase] + Public Sub DateAdd_MonthBefore100_Raises5() + Dim V As Variant + On Error Resume Next + V = DateAdd("m", -1, #1/1/100#) + Assert.Strict.AreEqual 5&, Err.Number + End Sub + +End Module diff --git a/bugs/dateadd-year-below-100/src/Sources/Startup.twin b/bugs/dateadd-year-below-100/src/Sources/Startup.twin new file mode 100644 index 000000000..be8f5c8bc --- /dev/null +++ b/bugs/dateadd-year-below-100/src/Sources/Startup.twin @@ -0,0 +1,6 @@ +Module Startup + + Public Sub Main() + End Sub + +End Module diff --git a/bugs/dateadd-year-below-100/vb6/Module1.bas b/bugs/dateadd-year-below-100/vb6/Module1.bas new file mode 100644 index 000000000..4799b30d8 --- /dev/null +++ b/bugs/dateadd-year-below-100/vb6/Module1.bas @@ -0,0 +1,22 @@ +Attribute VB_Name = "Module1" +Option Explicit + +' What VB6 does when DateAdd's result would be before January 1, 100: error 5, whatever +' the interval. Build and run it with: node scripts/bug_repro.mjs vb6 dateadd-year-below-100 + +Private Sub Report(ByVal Label As String, ByVal Result As Variant) + Print #9, Label & ": " & Err.Number & " " & Err.Description & " (" & Result & ")" + Err.Clear +End Sub + +Sub Main() + Dim V As Variant + Open App.Path & "\out.txt" For Output As #9 + On Error Resume Next + V = Empty: V = DateAdd("d", -1, #1/1/100#): Report "DateAdd_DayBefore100", V + V = Empty: V = DateAdd("yyyy", -1900, #1/1/2000#): Report "DateAdd_YearsToYear100", V + V = Empty: V = DateAdd("yyyy", -1, #1/1/100#): Report "DateAdd_YearBefore100", V + V = Empty: V = DateAdd("q", -1, #1/1/100#): Report "DateAdd_QuarterBefore100", V + V = Empty: V = DateAdd("m", -1, #1/1/100#): Report "DateAdd_MonthBefore100", V + Close #9 +End Sub diff --git a/bugs/dateadd-year-below-100/vb6/Probe.vbp b/bugs/dateadd-year-below-100/vb6/Probe.vbp new file mode 100644 index 000000000..4d210b0e8 --- /dev/null +++ b/bugs/dateadd-year-below-100/vb6/Probe.vbp @@ -0,0 +1,7 @@ +Type=Exe +Module=Module1; Module1.bas +Startup="Sub Main" +ExeName32="Probe.exe" +Name="Probe" +CompilationType=0 +OptimizationType=0 diff --git a/bugs/datepart-firstdayofweek-out-of-range/datepart-firstdayofweek-out-of-range.twinproj b/bugs/datepart-firstdayofweek-out-of-range/datepart-firstdayofweek-out-of-range.twinproj new file mode 100644 index 000000000..9d61b8eb5 Binary files /dev/null and b/bugs/datepart-firstdayofweek-out-of-range/datepart-firstdayofweek-out-of-range.twinproj differ diff --git a/bugs/datepart-firstdayofweek-out-of-range/repro.json b/bugs/datepart-firstdayofweek-out-of-range/repro.json new file mode 100644 index 000000000..61d75105e --- /dev/null +++ b/bugs/datepart-firstdayofweek-out-of-range/repro.json @@ -0,0 +1,16 @@ +{ + "mode": "test", + "expect": { + "exit": 7, + "output": [ + "^PASS RegressionTests.DatePart_W_Sunday_Is7$", + "^PASS RegressionTests.DatePart_W_Saturday_Is1$", + "^PASS RegressionTests.DatePart_D_Eight_NoError$", + "^FAIL RegressionTests.DatePart_W_Eight_Raises5: .*Actual: 0 ", + "^FAIL RegressionTests.DatePart_W_255_Raises5: .*Actual: 0 ", + "^FAIL RegressionTests.DatePart_W_MinusOne_Raises5: .*Actual: 0 ", + "^FAIL RegressionTests.DatePart_WW_Eight_Raises5: .*Actual: 0 ", + "^FAIL RegressionTests.DateDiff_WW_Eight_Raises5: .*Actual: 0 " + ] + } +} diff --git a/bugs/datepart-firstdayofweek-out-of-range/src/Settings b/bugs/datepart-firstdayofweek-out-of-range/src/Settings new file mode 100644 index 000000000..90186a379 --- /dev/null +++ b/bugs/datepart-firstdayofweek-out-of-range/src/Settings @@ -0,0 +1,75 @@ +{ + "configuration.inherits": "Defaults", + "project.appTitle": "DatepartFirstdayofweekOutOfRange", + "project.buildPath": "${SourcePath}\\Build\\${ProjectName}_${Architecture}.${FileExtension}", + "project.buildType": "Standard EXE", + "project.description": "Reproduces: x", + "project.exportPathIsV2": true, + "project.id": "{4DF640BD-6F03-49BC-9C55-8C9901FE1BC7}", + "project.name": "DatepartFirstdayofweekOutOfRange", + "project.optionExplicit": true, + "project.references": [ + { + "id": "{00020430-0000-0000-C000-000000000046}", + "lcid": 0, + "name": "OLE Automation", + "path32": "C:\\Windows\\SysWOW64\\stdole2.tlb", + "path64": "C:\\Windows\\System32\\stdole2.tlb", + "symbolId": "stdole", + "versionMajor": 2, + "versionMinor": 0 + }, + { + "id": "{F50B82D0-DCAB-43FE-9631-11959D4A4728}", + "isCompilerPackage": true, + "licence": "MIT", + "name": "[COMPILER PACKAGE] twinBASIC - VB Compatibility Package (Forms)", + "path32": "", + "path64": "", + "publisher": "TWINBASIC-COMPILER", + "symbolId": "VB", + "versionBuild": 0, + "versionMajor": 0, + "versionMinor": 0, + "versionRevision": 31 + }, + { + "id": "{C192FB39-64CA-4D9B-B477-A5502F48EFCC}", + "isCompilerPackage": true, + "licence": "MIT", + "name": "[COMPILER PACKAGE] twinBASIC - App global class object", + "path32": "", + "path64": "", + "publisher": "TWINBASIC-COMPILER", + "symbolId": "AppGlobalClassProject", + "versionBuild": 0, + "versionMajor": 1, + "versionMinor": 0, + "versionRevision": 0 + }, + { + "id": "{9847A10D-8987-44B2-8B87-EA77DEF7F1D9}", + "isCompilerPackage": true, + "licence": "MIT", + "name": "[COMPILER PACKAGE] Assert", + "path32": "", + "path64": "", + "publisher": "TWINBASIC-COMPILER", + "symbolId": "Assert", + "versionBuild": 0, + "versionMajor": 0, + "versionMinor": 0, + "versionRevision": 24 + } + ], + "project.settingsVersion": 1, + "project.startupObject": "Sub Main", + "project.warnings": { + "errors": [], + "hints": [], + "ignored": [], + "info": [], + "warnings": [] + }, + "runtime.useUnicodeStandardLibrary": true +} diff --git a/bugs/datepart-firstdayofweek-out-of-range/src/Sources/RegressionTests.twin b/bugs/datepart-firstdayofweek-out-of-range/src/Sources/RegressionTests.twin new file mode 100644 index 000000000..30bd9415b --- /dev/null +++ b/bugs/datepart-firstdayofweek-out-of-range/src/Sources/RegressionTests.twin @@ -0,0 +1,69 @@ +' Regression tests: DatePart and DateDiff raise error 5 for a firstdayofweek outside 0 to 7 +' where the interval uses it, as VB6 does (the VB6 project beside this one prints it). +' January 1, 2000 is a Saturday. tbrun --tests runs each case and prints PASS or FAIL. +[TestFixture] +Module RegressionTests + + ' Controls: the weekday with Sunday and with Saturday as the first day, and a + ' firstdayofweek of 8 with an interval that does not use it. + [TestCase] + Public Sub DatePart_W_Sunday_Is7() + Assert.Strict.AreEqual CVar(7), DatePart("w", #1/1/2000#, vbSunday) + End Sub + + [TestCase] + Public Sub DatePart_W_Saturday_Is1() + Assert.Strict.AreEqual CVar(1), DatePart("w", #1/1/2000#, vbSaturday) + End Sub + + [TestCase] + Public Sub DatePart_D_Eight_NoError() + Dim V As Variant + On Error Resume Next + V = DatePart("d", #1/1/2000#, 8) + Assert.Strict.AreEqual 0&, Err.Number + End Sub + + ' The defect: no error, and a value that follows no rule: 8 acts as 1, 9 to 13 match + ' no setting, and 255 gives a weekday of -2. + [TestCase] + Public Sub DatePart_W_Eight_Raises5() + Dim V As Variant + On Error Resume Next + V = DatePart("w", #1/1/2000#, 8) + Assert.Strict.AreEqual 5&, Err.Number + End Sub + + [TestCase] + Public Sub DatePart_W_255_Raises5() + Dim V As Variant + On Error Resume Next + V = DatePart("w", #1/1/2000#, 255) + Assert.Strict.AreEqual 5&, Err.Number + End Sub + + [TestCase] + Public Sub DatePart_W_MinusOne_Raises5() + Dim V As Variant + On Error Resume Next + V = DatePart("w", #1/1/2000#, -1) + Assert.Strict.AreEqual 5&, Err.Number + End Sub + + [TestCase] + Public Sub DatePart_WW_Eight_Raises5() + Dim V As Variant + On Error Resume Next + V = DatePart("ww", #1/1/2000#, 8) + Assert.Strict.AreEqual 5&, Err.Number + End Sub + + [TestCase] + Public Sub DateDiff_WW_Eight_Raises5() + Dim V As Variant + On Error Resume Next + V = DateDiff("ww", #1/1/2000#, #1/1/2001#, 8) + Assert.Strict.AreEqual 5&, Err.Number + End Sub + +End Module diff --git a/bugs/datepart-firstdayofweek-out-of-range/src/Sources/Startup.twin b/bugs/datepart-firstdayofweek-out-of-range/src/Sources/Startup.twin new file mode 100644 index 000000000..be8f5c8bc --- /dev/null +++ b/bugs/datepart-firstdayofweek-out-of-range/src/Sources/Startup.twin @@ -0,0 +1,6 @@ +Module Startup + + Public Sub Main() + End Sub + +End Module diff --git a/bugs/datepart-firstdayofweek-out-of-range/vb6/Module1.bas b/bugs/datepart-firstdayofweek-out-of-range/vb6/Module1.bas new file mode 100644 index 000000000..4b67e9fce --- /dev/null +++ b/bugs/datepart-firstdayofweek-out-of-range/vb6/Module1.bas @@ -0,0 +1,25 @@ +Attribute VB_Name = "Module1" +Option Explicit + +' What VB6 does with a firstdayofweek outside 0 to 7: error 5 where the interval uses it. +' Build and run it with: node scripts/bug_repro.mjs vb6 datepart-firstdayofweek-out-of-range + +Private Sub Report(ByVal Label As String, ByVal Result As Variant) + Print #9, Label & ": " & Err.Number & " " & Err.Description & " (" & Result & ")" + Err.Clear +End Sub + +Sub Main() + Dim V As Variant + Open App.Path & "\out.txt" For Output As #9 + On Error Resume Next + V = Empty: V = DatePart("w", #1/1/2000#, vbSunday): Report "DatePart_W_Sunday", V + V = Empty: V = DatePart("w", #1/1/2000#, vbSaturday): Report "DatePart_W_Saturday", V + V = Empty: V = DatePart("d", #1/1/2000#, 8): Report "DatePart_D_Eight", V + V = Empty: V = DatePart("w", #1/1/2000#, 8): Report "DatePart_W_Eight", V + V = Empty: V = DatePart("w", #1/1/2000#, 255): Report "DatePart_W_255", V + V = Empty: V = DatePart("w", #1/1/2000#, -1): Report "DatePart_W_MinusOne", V + V = Empty: V = DatePart("ww", #1/1/2000#, 8): Report "DatePart_WW_Eight", V + V = Empty: V = DateDiff("ww", #1/1/2000#, #1/1/2001#, 8): Report "DateDiff_WW_Eight", V + Close #9 +End Sub diff --git a/bugs/datepart-firstdayofweek-out-of-range/vb6/Probe.vbp b/bugs/datepart-firstdayofweek-out-of-range/vb6/Probe.vbp new file mode 100644 index 000000000..4d210b0e8 --- /dev/null +++ b/bugs/datepart-firstdayofweek-out-of-range/vb6/Probe.vbp @@ -0,0 +1,7 @@ +Type=Exe +Module=Module1; Module1.bas +Startup="Sub Main" +ExeName32="Probe.exe" +Name="Probe" +CompilationType=0 +OptimizationType=0 diff --git a/bugs/datepart-firstweekofyear-out-of-range/datepart-firstweekofyear-out-of-range.twinproj b/bugs/datepart-firstweekofyear-out-of-range/datepart-firstweekofyear-out-of-range.twinproj new file mode 100644 index 000000000..a0eaf3e6a Binary files /dev/null and b/bugs/datepart-firstweekofyear-out-of-range/datepart-firstweekofyear-out-of-range.twinproj differ diff --git a/bugs/datepart-firstweekofyear-out-of-range/repro.json b/bugs/datepart-firstweekofyear-out-of-range/repro.json new file mode 100644 index 000000000..d49bd8680 --- /dev/null +++ b/bugs/datepart-firstweekofyear-out-of-range/repro.json @@ -0,0 +1,13 @@ +{ + "mode": "test", + "expect": { + "exit": 7, + "output": [ + "^PASS RegressionTests.DatePart_WW_FirstJan1_Is1$", + "^PASS RegressionTests.DatePart_WW_FirstFullWeek_Is52$", + "^PASS RegressionTests.DatePart_D_Four_NoError$", + "^FAIL RegressionTests.DatePart_WW_Four_Raises5: .*Actual: 0 ", + "^FAIL RegressionTests.DatePart_WW_MinusOne_Raises5: .*Actual: 0 " + ] + } +} diff --git a/bugs/datepart-firstweekofyear-out-of-range/src/Settings b/bugs/datepart-firstweekofyear-out-of-range/src/Settings new file mode 100644 index 000000000..bb9622d37 --- /dev/null +++ b/bugs/datepart-firstweekofyear-out-of-range/src/Settings @@ -0,0 +1,75 @@ +{ + "configuration.inherits": "Defaults", + "project.appTitle": "DatepartFirstweekofyearOutOfRange", + "project.buildPath": "${SourcePath}\\Build\\${ProjectName}_${Architecture}.${FileExtension}", + "project.buildType": "Standard EXE", + "project.description": "Reproduces: x", + "project.exportPathIsV2": true, + "project.id": "{72EC493D-D206-4251-8B4D-A7A8A03DB4F8}", + "project.name": "DatepartFirstweekofyearOutOfRange", + "project.optionExplicit": true, + "project.references": [ + { + "id": "{00020430-0000-0000-C000-000000000046}", + "lcid": 0, + "name": "OLE Automation", + "path32": "C:\\Windows\\SysWOW64\\stdole2.tlb", + "path64": "C:\\Windows\\System32\\stdole2.tlb", + "symbolId": "stdole", + "versionMajor": 2, + "versionMinor": 0 + }, + { + "id": "{F50B82D0-DCAB-43FE-9631-11959D4A4728}", + "isCompilerPackage": true, + "licence": "MIT", + "name": "[COMPILER PACKAGE] twinBASIC - VB Compatibility Package (Forms)", + "path32": "", + "path64": "", + "publisher": "TWINBASIC-COMPILER", + "symbolId": "VB", + "versionBuild": 0, + "versionMajor": 0, + "versionMinor": 0, + "versionRevision": 31 + }, + { + "id": "{C192FB39-64CA-4D9B-B477-A5502F48EFCC}", + "isCompilerPackage": true, + "licence": "MIT", + "name": "[COMPILER PACKAGE] twinBASIC - App global class object", + "path32": "", + "path64": "", + "publisher": "TWINBASIC-COMPILER", + "symbolId": "AppGlobalClassProject", + "versionBuild": 0, + "versionMajor": 1, + "versionMinor": 0, + "versionRevision": 0 + }, + { + "id": "{9847A10D-8987-44B2-8B87-EA77DEF7F1D9}", + "isCompilerPackage": true, + "licence": "MIT", + "name": "[COMPILER PACKAGE] Assert", + "path32": "", + "path64": "", + "publisher": "TWINBASIC-COMPILER", + "symbolId": "Assert", + "versionBuild": 0, + "versionMajor": 0, + "versionMinor": 0, + "versionRevision": 24 + } + ], + "project.settingsVersion": 1, + "project.startupObject": "Sub Main", + "project.warnings": { + "errors": [], + "hints": [], + "ignored": [], + "info": [], + "warnings": [] + }, + "runtime.useUnicodeStandardLibrary": true +} diff --git a/bugs/datepart-firstweekofyear-out-of-range/src/Sources/RegressionTests.twin b/bugs/datepart-firstweekofyear-out-of-range/src/Sources/RegressionTests.twin new file mode 100644 index 000000000..e3be04584 --- /dev/null +++ b/bugs/datepart-firstweekofyear-out-of-range/src/Sources/RegressionTests.twin @@ -0,0 +1,44 @@ +' Regression tests: DatePart raises error 5 for a firstweekofyear outside 0 to 3 with the +' "ww" interval, as VB6 does (the VB6 project beside this one prints it). January 1, 2000 +' is a Saturday. tbrun --tests runs each case and prints PASS or FAIL. +[TestFixture] +Module RegressionTests + + ' Controls: the week with vbFirstJan1 and vbFirstFullWeek, and a firstweekofyear of 4 + ' with an interval that does not use it. + [TestCase] + Public Sub DatePart_WW_FirstJan1_Is1() + Assert.Strict.AreEqual CVar(1), DatePart("ww", #1/1/2000#, vbSunday, vbFirstJan1) + End Sub + + [TestCase] + Public Sub DatePart_WW_FirstFullWeek_Is52() + Assert.Strict.AreEqual CVar(52), DatePart("ww", #1/1/2000#, vbSunday, vbFirstFullWeek) + End Sub + + [TestCase] + Public Sub DatePart_D_Four_NoError() + Dim V As Variant + On Error Resume Next + V = DatePart("d", #1/1/2000#, vbSunday, 4) + Assert.Strict.AreEqual 0&, Err.Number + End Sub + + ' The defect: no error; the weeks are counted as with 0 or 1. + [TestCase] + Public Sub DatePart_WW_Four_Raises5() + Dim V As Variant + On Error Resume Next + V = DatePart("ww", #1/1/2000#, vbSunday, 4) + Assert.Strict.AreEqual 5&, Err.Number + End Sub + + [TestCase] + Public Sub DatePart_WW_MinusOne_Raises5() + Dim V As Variant + On Error Resume Next + V = DatePart("ww", #1/1/2000#, vbSunday, -1) + Assert.Strict.AreEqual 5&, Err.Number + End Sub + +End Module diff --git a/bugs/datepart-firstweekofyear-out-of-range/src/Sources/Startup.twin b/bugs/datepart-firstweekofyear-out-of-range/src/Sources/Startup.twin new file mode 100644 index 000000000..be8f5c8bc --- /dev/null +++ b/bugs/datepart-firstweekofyear-out-of-range/src/Sources/Startup.twin @@ -0,0 +1,6 @@ +Module Startup + + Public Sub Main() + End Sub + +End Module diff --git a/bugs/datepart-firstweekofyear-out-of-range/vb6/Module1.bas b/bugs/datepart-firstweekofyear-out-of-range/vb6/Module1.bas new file mode 100644 index 000000000..58a5b5ba0 --- /dev/null +++ b/bugs/datepart-firstweekofyear-out-of-range/vb6/Module1.bas @@ -0,0 +1,22 @@ +Attribute VB_Name = "Module1" +Option Explicit + +' What VB6 does with a firstweekofyear outside 0 to 3: error 5 with the "ww" interval. +' Build and run it with: node scripts/bug_repro.mjs vb6 datepart-firstweekofyear-out-of-range + +Private Sub Report(ByVal Label As String, ByVal Result As Variant) + Print #9, Label & ": " & Err.Number & " " & Err.Description & " (" & Result & ")" + Err.Clear +End Sub + +Sub Main() + Dim V As Variant + Open App.Path & "\out.txt" For Output As #9 + On Error Resume Next + V = Empty: V = DatePart("ww", #1/1/2000#, vbSunday, vbFirstJan1): Report "DatePart_WW_FirstJan1", V + V = Empty: V = DatePart("ww", #1/1/2000#, vbSunday, vbFirstFullWeek): Report "DatePart_WW_FirstFullWeek", V + V = Empty: V = DatePart("d", #1/1/2000#, vbSunday, 4): Report "DatePart_D_Four", V + V = Empty: V = DatePart("ww", #1/1/2000#, vbSunday, 4): Report "DatePart_WW_Four", V + V = Empty: V = DatePart("ww", #1/1/2000#, vbSunday, -1): Report "DatePart_WW_MinusOne", V + Close #9 +End Sub diff --git a/bugs/datepart-firstweekofyear-out-of-range/vb6/Probe.vbp b/bugs/datepart-firstweekofyear-out-of-range/vb6/Probe.vbp new file mode 100644 index 000000000..4d210b0e8 --- /dev/null +++ b/bugs/datepart-firstweekofyear-out-of-range/vb6/Probe.vbp @@ -0,0 +1,7 @@ +Type=Exe +Module=Module1; Module1.bas +Startup="Sub Main" +ExeName32="Probe.exe" +Name="Probe" +CompilationType=0 +OptimizationType=0 diff --git a/bugs/datepart-null-checks-interval/datepart-null-checks-interval.twinproj b/bugs/datepart-null-checks-interval/datepart-null-checks-interval.twinproj new file mode 100644 index 000000000..bd77a45a9 Binary files /dev/null and b/bugs/datepart-null-checks-interval/datepart-null-checks-interval.twinproj differ diff --git a/bugs/datepart-null-checks-interval/repro.json b/bugs/datepart-null-checks-interval/repro.json new file mode 100644 index 000000000..c0c595976 --- /dev/null +++ b/bugs/datepart-null-checks-interval/repro.json @@ -0,0 +1,12 @@ +{ + "mode": "test", + "expect": { + "exit": 7, + "output": [ + "^PASS RegressionTests.DatePart_BadIntervalWithDate_Raises5$", + "^PASS RegressionTests.DateAdd_BadIntervalWithNull_ReturnsNull$", + "^PASS RegressionTests.DateDiff_BadIntervalWithNull_ReturnsNull$", + "^FAIL RegressionTests.DatePart_BadIntervalWithNull_NoError: .*Actual: 5 " + ] + } +} diff --git a/bugs/datepart-null-checks-interval/src/Settings b/bugs/datepart-null-checks-interval/src/Settings new file mode 100644 index 000000000..a8020664d --- /dev/null +++ b/bugs/datepart-null-checks-interval/src/Settings @@ -0,0 +1,75 @@ +{ + "configuration.inherits": "Defaults", + "project.appTitle": "DatepartNullChecksInterval", + "project.buildPath": "${SourcePath}\\Build\\${ProjectName}_${Architecture}.${FileExtension}", + "project.buildType": "Standard EXE", + "project.description": "Reproduces: x", + "project.exportPathIsV2": true, + "project.id": "{F88F8073-E55B-4D72-815E-D109EA3C783F}", + "project.name": "DatepartNullChecksInterval", + "project.optionExplicit": true, + "project.references": [ + { + "id": "{00020430-0000-0000-C000-000000000046}", + "lcid": 0, + "name": "OLE Automation", + "path32": "C:\\Windows\\SysWOW64\\stdole2.tlb", + "path64": "C:\\Windows\\System32\\stdole2.tlb", + "symbolId": "stdole", + "versionMajor": 2, + "versionMinor": 0 + }, + { + "id": "{F50B82D0-DCAB-43FE-9631-11959D4A4728}", + "isCompilerPackage": true, + "licence": "MIT", + "name": "[COMPILER PACKAGE] twinBASIC - VB Compatibility Package (Forms)", + "path32": "", + "path64": "", + "publisher": "TWINBASIC-COMPILER", + "symbolId": "VB", + "versionBuild": 0, + "versionMajor": 0, + "versionMinor": 0, + "versionRevision": 31 + }, + { + "id": "{C192FB39-64CA-4D9B-B477-A5502F48EFCC}", + "isCompilerPackage": true, + "licence": "MIT", + "name": "[COMPILER PACKAGE] twinBASIC - App global class object", + "path32": "", + "path64": "", + "publisher": "TWINBASIC-COMPILER", + "symbolId": "AppGlobalClassProject", + "versionBuild": 0, + "versionMajor": 1, + "versionMinor": 0, + "versionRevision": 0 + }, + { + "id": "{9847A10D-8987-44B2-8B87-EA77DEF7F1D9}", + "isCompilerPackage": true, + "licence": "MIT", + "name": "[COMPILER PACKAGE] Assert", + "path32": "", + "path64": "", + "publisher": "TWINBASIC-COMPILER", + "symbolId": "Assert", + "versionBuild": 0, + "versionMajor": 0, + "versionMinor": 0, + "versionRevision": 24 + } + ], + "project.settingsVersion": 1, + "project.startupObject": "Sub Main", + "project.warnings": { + "errors": [], + "hints": [], + "ignored": [], + "info": [], + "warnings": [] + }, + "runtime.useUnicodeStandardLibrary": true +} diff --git a/bugs/datepart-null-checks-interval/src/Sources/RegressionTests.twin b/bugs/datepart-null-checks-interval/src/Sources/RegressionTests.twin new file mode 100644 index 000000000..9a5c574dc --- /dev/null +++ b/bugs/datepart-null-checks-interval/src/Sources/RegressionTests.twin @@ -0,0 +1,38 @@ +' Regression tests: DatePart returns Null for a Null date without checking its interval, +' as DateAdd and DateDiff do, and as VB6 does (the VB6 project beside this one prints it). +' tbrun --tests runs each case and prints PASS or FAIL. +[TestFixture] +Module RegressionTests + + ' Controls: DatePart raises 5 for the interval with a date, and DateAdd and DateDiff + ' return Null for a Null date with the same interval. + [TestCase] + Public Sub DatePart_BadIntervalWithDate_Raises5() + Dim V As Variant + On Error Resume Next + V = DatePart("x", #1/1/2000#) + Assert.Strict.AreEqual 5&, Err.Number + End Sub + + [TestCase] + Public Sub DateAdd_BadIntervalWithNull_ReturnsNull() + Dim V As Variant = DateAdd("x", 1, Null) + Assert.Strict.IsTrue IsNull(V) + End Sub + + [TestCase] + Public Sub DateDiff_BadIntervalWithNull_ReturnsNull() + Dim V As Variant = DateDiff("x", Null, #1/1/2001#) + Assert.Strict.IsTrue IsNull(V) + End Sub + + ' The defect: DatePart raises 5. + [TestCase] + Public Sub DatePart_BadIntervalWithNull_NoError() + Dim V As Variant + On Error Resume Next + V = DatePart("x", Null) + Assert.Strict.AreEqual 0&, Err.Number + End Sub + +End Module diff --git a/bugs/datepart-null-checks-interval/src/Sources/Startup.twin b/bugs/datepart-null-checks-interval/src/Sources/Startup.twin new file mode 100644 index 000000000..be8f5c8bc --- /dev/null +++ b/bugs/datepart-null-checks-interval/src/Sources/Startup.twin @@ -0,0 +1,6 @@ +Module Startup + + Public Sub Main() + End Sub + +End Module diff --git a/bugs/datepart-null-checks-interval/vb6/Module1.bas b/bugs/datepart-null-checks-interval/vb6/Module1.bas new file mode 100644 index 000000000..17fabbe05 --- /dev/null +++ b/bugs/datepart-null-checks-interval/vb6/Module1.bas @@ -0,0 +1,22 @@ +Attribute VB_Name = "Module1" +Option Explicit + +' What VB6 does with an interval that is not a setting and a Null date: DatePart, DateAdd +' and DateDiff all return Null. +' Build and run it with: node scripts/bug_repro.mjs vb6 datepart-null-checks-interval + +Private Sub Report(ByVal Label As String, ByVal Result As Variant) + Print #9, Label & ": " & Err.Number & " " & Err.Description & " (" & TypeName(Result) & ")" + Err.Clear +End Sub + +Sub Main() + Dim V As Variant + Open App.Path & "\out.txt" For Output As #9 + On Error Resume Next + V = Empty: V = DatePart("x", #1/1/2000#): Report "DatePart_BadIntervalWithDate", V + V = Empty: V = DateAdd("x", 1, Null): Report "DateAdd_BadIntervalWithNull", V + V = Empty: V = DateDiff("x", Null, #1/1/2001#): Report "DateDiff_BadIntervalWithNull", V + V = Empty: V = DatePart("x", Null): Report "DatePart_BadIntervalWithNull", V + Close #9 +End Sub diff --git a/bugs/datepart-null-checks-interval/vb6/Probe.vbp b/bugs/datepart-null-checks-interval/vb6/Probe.vbp new file mode 100644 index 000000000..4d210b0e8 --- /dev/null +++ b/bugs/datepart-null-checks-interval/vb6/Probe.vbp @@ -0,0 +1,7 @@ +Type=Exe +Module=Module1; Module1.bas +Startup="Sub Main" +ExeName32="Probe.exe" +Name="Probe" +CompilationType=0 +OptimizationType=0 diff --git a/bugs/datepart-week-year-100/datepart-week-year-100.twinproj b/bugs/datepart-week-year-100/datepart-week-year-100.twinproj new file mode 100644 index 000000000..505281fda Binary files /dev/null and b/bugs/datepart-week-year-100/datepart-week-year-100.twinproj differ diff --git a/bugs/datepart-week-year-100/repro.json b/bugs/datepart-week-year-100/repro.json new file mode 100644 index 000000000..0efc8a3a0 --- /dev/null +++ b/bugs/datepart-week-year-100/repro.json @@ -0,0 +1,12 @@ +{ + "mode": "run", + "expect": { + "exit": 5, + "output": [ + "^ww of 1/1/100, vbFirstFourDays: -98980$", + "^ww of 1/3/100, vbFirstFullWeek: 1$", + "^ww of 1/1/2000, vbFirstFullWeek: 52$", + "^calling DatePart" + ] + } +} diff --git a/bugs/datepart-week-year-100/src/Settings b/bugs/datepart-week-year-100/src/Settings new file mode 100644 index 000000000..407a3dac7 --- /dev/null +++ b/bugs/datepart-week-year-100/src/Settings @@ -0,0 +1,75 @@ +{ + "configuration.inherits": "Defaults", + "project.appTitle": "DatepartWeekYear100", + "project.buildPath": "${SourcePath}\\Build\\${ProjectName}_${Architecture}.${FileExtension}", + "project.buildType": "Standard EXE", + "project.description": "Reproduces: x", + "project.exportPathIsV2": true, + "project.id": "{E68709BD-F6E7-4927-A4F4-67DA358D6935}", + "project.name": "DatepartWeekYear100", + "project.optionExplicit": true, + "project.references": [ + { + "id": "{00020430-0000-0000-C000-000000000046}", + "lcid": 0, + "name": "OLE Automation", + "path32": "C:\\Windows\\SysWOW64\\stdole2.tlb", + "path64": "C:\\Windows\\System32\\stdole2.tlb", + "symbolId": "stdole", + "versionMajor": 2, + "versionMinor": 0 + }, + { + "id": "{F50B82D0-DCAB-43FE-9631-11959D4A4728}", + "isCompilerPackage": true, + "licence": "MIT", + "name": "[COMPILER PACKAGE] twinBASIC - VB Compatibility Package (Forms)", + "path32": "", + "path64": "", + "publisher": "TWINBASIC-COMPILER", + "symbolId": "VB", + "versionBuild": 0, + "versionMajor": 0, + "versionMinor": 0, + "versionRevision": 31 + }, + { + "id": "{C192FB39-64CA-4D9B-B477-A5502F48EFCC}", + "isCompilerPackage": true, + "licence": "MIT", + "name": "[COMPILER PACKAGE] twinBASIC - App global class object", + "path32": "", + "path64": "", + "publisher": "TWINBASIC-COMPILER", + "symbolId": "AppGlobalClassProject", + "versionBuild": 0, + "versionMajor": 1, + "versionMinor": 0, + "versionRevision": 0 + }, + { + "id": "{9847A10D-8987-44B2-8B87-EA77DEF7F1D9}", + "isCompilerPackage": true, + "licence": "MIT", + "name": "[COMPILER PACKAGE] Assert", + "path32": "", + "path64": "", + "publisher": "TWINBASIC-COMPILER", + "symbolId": "Assert", + "versionBuild": 0, + "versionMajor": 0, + "versionMinor": 0, + "versionRevision": 24 + } + ], + "project.settingsVersion": 1, + "project.startupObject": "Sub Main", + "project.warnings": { + "errors": [], + "hints": [], + "ignored": [], + "info": [], + "warnings": [] + }, + "runtime.useUnicodeStandardLibrary": true +} diff --git a/bugs/datepart-week-year-100/src/Sources/Startup.twin b/bugs/datepart-week-year-100/src/Sources/Startup.twin new file mode 100644 index 000000000..55d60a7d7 --- /dev/null +++ b/bugs/datepart-week-year-100/src/Sources/Startup.twin @@ -0,0 +1,17 @@ +' DatePart("ww", ...) with vbFirstFullWeek never returns for a date of year 100 before +' its first full week. VB6 returns (the VB6 project beside this one prints it). The +' controls print; the last call never prints "returned". +Module Startup + + Public Sub Main() + ' Controls: the same date with vbFirstFourDays, and the first date of the first + ' full week with vbFirstFullWeek, return. + Debug.Print "ww of 1/1/100, vbFirstFourDays: " & DatePart("ww", #1/1/100#, vbSunday, vbFirstFourDays) + Debug.Print "ww of 1/3/100, vbFirstFullWeek: " & DatePart("ww", #1/3/100#, vbSunday, vbFirstFullWeek) + Debug.Print "ww of 1/1/2000, vbFirstFullWeek: " & DatePart("ww", #1/1/2000#, vbSunday, vbFirstFullWeek) + ' The defect: January 1, 100 is a Friday, so its week belongs to year 99. + Debug.Print "calling DatePart(""ww"", #1/1/100#, vbSunday, vbFirstFullWeek)" + Debug.Print "returned " & DatePart("ww", #1/1/100#, vbSunday, vbFirstFullWeek) + End Sub + +End Module diff --git a/bugs/datepart-week-year-100/vb6/Module1.bas b/bugs/datepart-week-year-100/vb6/Module1.bas new file mode 100644 index 000000000..37c5a5b11 --- /dev/null +++ b/bugs/datepart-week-year-100/vb6/Module1.bas @@ -0,0 +1,18 @@ +Attribute VB_Name = "Module1" +Option Explicit + +' What VB6 gives for DatePart("ww", ...) of January 1, 100, whose week belongs to year 99. +' Build and run it with: node scripts/bug_repro.mjs vb6 datepart-week-year-100 + +Sub Main() + Open App.Path & "\out.txt" For Output As #9 + On Error Resume Next + Print #9, "ww of 1/1/100, vbFirstFourDays: " & DatePart("ww", #1/1/100#, vbSunday, vbFirstFourDays) & " error " & Err.Number + Err.Clear + Print #9, "ww of 1/3/100, vbFirstFullWeek: " & DatePart("ww", #1/3/100#, vbSunday, vbFirstFullWeek) & " error " & Err.Number + Err.Clear + Print #9, "ww of 1/1/2000, vbFirstFullWeek: " & DatePart("ww", #1/1/2000#, vbSunday, vbFirstFullWeek) & " error " & Err.Number + Err.Clear + Print #9, "ww of 1/1/100, vbFirstFullWeek: " & DatePart("ww", #1/1/100#, vbSunday, vbFirstFullWeek) & " error " & Err.Number + Close #9 +End Sub diff --git a/bugs/datepart-week-year-100/vb6/Probe.vbp b/bugs/datepart-week-year-100/vb6/Probe.vbp new file mode 100644 index 000000000..4d210b0e8 --- /dev/null +++ b/bugs/datepart-week-year-100/vb6/Probe.vbp @@ -0,0 +1,7 @@ +Type=Exe +Module=Module1; Module1.bas +Startup="Sub Main" +ExeName32="Probe.exe" +Name="Probe" +CompilationType=0 +OptimizationType=0 diff --git a/bugs/filed/abs-minimum-widens-type/REPORT.md b/bugs/filed/abs-minimum-widens-type/REPORT.md new file mode 100644 index 000000000..ae933b658 --- /dev/null +++ b/bugs/filed/abs-minimum-widens-type/REPORT.md @@ -0,0 +1,90 @@ +Filed as [twinbasic/twinbasic#2541](https://github.com/twinbasic/twinbasic/issues/2541). + +## `Abs` of the smallest `Integer` or `Long` returns a `Long` or a `Double`, where VB6 raises error 6 + +**Describe the bug** +`Abs` returns a value of the type it is given: `Abs` of the `Integer` -32767 is the `Integer` 32767. The smallest `Integer`, -32768, has no positive counterpart in an `Integer`, and VB6 raises error 6, *Overflow*, for it, as for the smallest `Long` and the smallest `Currency`. twinBASIC raises 6 for the `Currency`, but for the `Integer` it returns the `Long` 32768, for the `Long` -2147483648 the `Double` 2147483648, and for the smallest `LongLong` a `Double`. Observed in the compiler's test mode, 32-bit and 64-bit, and in a built exe. + +**To Reproduce** +Steps to reproduce the behavior: +1. Open `abs-minimum-widens-type.twinproj` (attached as `abs-minimum-widens-type.zip`). +2. Run its test cases, the module below. On BETA 1010 the three controls pass (an `Integer` one above the smallest keeps its type, the smallest `Currency` raises 6, and a `Variant` holding the smallest `Integer` gives a `Long`, as in VB6) and the three cases fail with `Actual: 0`. + +The module is meant to be adapted as a regression test. Each case asserts VB6's result, which the attached VB6 project (`abs-minimum-widens-type-vb6.zip`) prints: error 6 for the `Integer`, the `Long` and the `Currency`, and the `Integer` 32767 and the `Long` 32768 for the controls. VB6 has no `LongLong`; its case asserts the same rule. +```vb +' Regression tests: Abs of the smallest Integer or Long raises error 6, as Abs of the +' smallest Currency does, because the result keeps the argument's type and the type +' cannot hold it. Expected results are VB6's (the VB6 project beside this one prints +' them). tbrun --tests runs each case and prints PASS or FAIL. +[TestFixture] +Module RegressionTests + + ' Controls: a value one above the smallest keeps its type; the smallest Currency + ' raises 6; a Variant holding the smallest Integer gives a Long (in VB6 too). + [TestCase] + Public Sub Abs_IntegerAboveSmallest_KeepsInteger() + Dim I As Integer = -32767 + Dim V As Variant = Abs(I) + Assert.Strict.AreEqual "Integer", TypeName(V) + End Sub + + [TestCase] + Public Sub Abs_SmallestCurrency_Raises6() + Dim C As Currency = -922337203685477.5807@ - 0.0001@ + Dim V As Variant + On Error Resume Next + V = Abs(C) + Assert.Strict.AreEqual 6&, Err.Number + End Sub + + [TestCase] + Public Sub Abs_VariantSmallestInteger_GivesLong() + Dim V As Variant = CInt(-32768) + V = Abs(V) + Assert.Strict.AreEqual "Long", TypeName(V) + End Sub + + ' The defect: the result is widened to a Long or a Double, and no error is raised. + [TestCase] + Public Sub Abs_SmallestInteger_Raises6() + Dim I As Integer = -32768 + Dim V As Variant + On Error Resume Next + V = Abs(I) + Assert.Strict.AreEqual 6&, Err.Number + End Sub + + [TestCase] + Public Sub Abs_SmallestLong_Raises6() + Dim L As Long = -2147483647 - 1 + Dim V As Variant + On Error Resume Next + V = Abs(L) + Assert.Strict.AreEqual 6&, Err.Number + End Sub + + [TestCase] + Public Sub Abs_SmallestLongLong_Raises6() + Dim L As LongLong = -9223372036854775807^ - 1 + Dim V As Variant + On Error Resume Next + V = Abs(L) + Assert.Strict.AreEqual 6&, Err.Number + End Sub + +End Module +``` + +**Expected behavior** +Error 6, *Overflow*, for the smallest `Integer`, `Long` and `LongLong`, as for the smallest `Currency` and as VB6 does: the result has the argument's type. + +**Desktop:** + - OS: Windows 10 Pro 22H2 (build 19045) + - twinBASIC compiler version: BETA 1010 + +**Additional context** +Severity: low. The value is right, and assigning it back to the argument's type raises 6 there (`I = Abs(I)`); a program that relies on the error from `Abs` itself, or on the result's type (`TypeName`, a `Variant` passed on), sees a difference. + +What does not reproduce it: any other `Integer` or `Long`; the smallest `Currency` (6 in both); a `Variant` holding the smallest `Integer` or `Long`, which gives a `Long` or a `Double` in VB6 too. + + diff --git a/bugs/filed/abs-minimum-widens-type/abs-minimum-widens-type.twinproj b/bugs/filed/abs-minimum-widens-type/abs-minimum-widens-type.twinproj new file mode 100644 index 000000000..0cfb65d58 Binary files /dev/null and b/bugs/filed/abs-minimum-widens-type/abs-minimum-widens-type.twinproj differ diff --git a/bugs/filed/abs-minimum-widens-type/repro.json b/bugs/filed/abs-minimum-widens-type/repro.json new file mode 100644 index 000000000..08908ce04 --- /dev/null +++ b/bugs/filed/abs-minimum-widens-type/repro.json @@ -0,0 +1,15 @@ +{ + "mode": "test", + "expect": { + "exit": 7, + "output": [ + "^PASS RegressionTests.Abs_IntegerAboveSmallest_KeepsInteger$", + "^PASS RegressionTests.Abs_SmallestCurrency_Raises6$", + "^PASS RegressionTests.Abs_VariantSmallestInteger_GivesLong$", + "^FAIL RegressionTests.Abs_SmallestInteger_Raises6: .*Actual: 0 ", + "^FAIL RegressionTests.Abs_SmallestLong_Raises6: .*Actual: 0 ", + "^FAIL RegressionTests.Abs_SmallestLongLong_Raises6: .*Actual: 0 " + ] + }, + "issue": 2541 +} diff --git a/bugs/filed/abs-minimum-widens-type/src/Settings b/bugs/filed/abs-minimum-widens-type/src/Settings new file mode 100644 index 000000000..21cf1b986 --- /dev/null +++ b/bugs/filed/abs-minimum-widens-type/src/Settings @@ -0,0 +1,75 @@ +{ + "configuration.inherits": "Defaults", + "project.appTitle": "AbsMinimumWidensType", + "project.buildPath": "${SourcePath}\\Build\\${ProjectName}_${Architecture}.${FileExtension}", + "project.buildType": "Standard EXE", + "project.description": "Reproduces: x", + "project.exportPathIsV2": true, + "project.id": "{4D72BBC3-1881-41A9-9DC4-B36AF77DF2B3}", + "project.name": "AbsMinimumWidensType", + "project.optionExplicit": true, + "project.references": [ + { + "id": "{00020430-0000-0000-C000-000000000046}", + "lcid": 0, + "name": "OLE Automation", + "path32": "C:\\Windows\\SysWOW64\\stdole2.tlb", + "path64": "C:\\Windows\\System32\\stdole2.tlb", + "symbolId": "stdole", + "versionMajor": 2, + "versionMinor": 0 + }, + { + "id": "{F50B82D0-DCAB-43FE-9631-11959D4A4728}", + "isCompilerPackage": true, + "licence": "MIT", + "name": "[COMPILER PACKAGE] twinBASIC - VB Compatibility Package (Forms)", + "path32": "", + "path64": "", + "publisher": "TWINBASIC-COMPILER", + "symbolId": "VB", + "versionBuild": 0, + "versionMajor": 0, + "versionMinor": 0, + "versionRevision": 31 + }, + { + "id": "{C192FB39-64CA-4D9B-B477-A5502F48EFCC}", + "isCompilerPackage": true, + "licence": "MIT", + "name": "[COMPILER PACKAGE] twinBASIC - App global class object", + "path32": "", + "path64": "", + "publisher": "TWINBASIC-COMPILER", + "symbolId": "AppGlobalClassProject", + "versionBuild": 0, + "versionMajor": 1, + "versionMinor": 0, + "versionRevision": 0 + }, + { + "id": "{9847A10D-8987-44B2-8B87-EA77DEF7F1D9}", + "isCompilerPackage": true, + "licence": "MIT", + "name": "[COMPILER PACKAGE] Assert", + "path32": "", + "path64": "", + "publisher": "TWINBASIC-COMPILER", + "symbolId": "Assert", + "versionBuild": 0, + "versionMajor": 0, + "versionMinor": 0, + "versionRevision": 24 + } + ], + "project.settingsVersion": 1, + "project.startupObject": "Sub Main", + "project.warnings": { + "errors": [], + "hints": [], + "ignored": [], + "info": [], + "warnings": [] + }, + "runtime.useUnicodeStandardLibrary": true +} diff --git a/bugs/filed/abs-minimum-widens-type/src/Sources/RegressionTests.twin b/bugs/filed/abs-minimum-widens-type/src/Sources/RegressionTests.twin new file mode 100644 index 000000000..fdd134d6d --- /dev/null +++ b/bugs/filed/abs-minimum-widens-type/src/Sources/RegressionTests.twin @@ -0,0 +1,61 @@ +' Regression tests: Abs of the smallest Integer or Long raises error 6, as Abs of the +' smallest Currency does, because the result keeps the argument's type and the type +' cannot hold it. Expected results are VB6's (the VB6 project beside this one prints +' them). tbrun --tests runs each case and prints PASS or FAIL. +[TestFixture] +Module RegressionTests + + ' Controls: a value one above the smallest keeps its type; the smallest Currency + ' raises 6; a Variant holding the smallest Integer gives a Long (in VB6 too). + [TestCase] + Public Sub Abs_IntegerAboveSmallest_KeepsInteger() + Dim I As Integer = -32767 + Dim V As Variant = Abs(I) + Assert.Strict.AreEqual "Integer", TypeName(V) + End Sub + + [TestCase] + Public Sub Abs_SmallestCurrency_Raises6() + Dim C As Currency = -922337203685477.5807@ - 0.0001@ + Dim V As Variant + On Error Resume Next + V = Abs(C) + Assert.Strict.AreEqual 6&, Err.Number + End Sub + + [TestCase] + Public Sub Abs_VariantSmallestInteger_GivesLong() + Dim V As Variant = CInt(-32768) + V = Abs(V) + Assert.Strict.AreEqual "Long", TypeName(V) + End Sub + + ' The defect: the result is widened to a Long or a Double, and no error is raised. + [TestCase] + Public Sub Abs_SmallestInteger_Raises6() + Dim I As Integer = -32768 + Dim V As Variant + On Error Resume Next + V = Abs(I) + Assert.Strict.AreEqual 6&, Err.Number + End Sub + + [TestCase] + Public Sub Abs_SmallestLong_Raises6() + Dim L As Long = -2147483647 - 1 + Dim V As Variant + On Error Resume Next + V = Abs(L) + Assert.Strict.AreEqual 6&, Err.Number + End Sub + + [TestCase] + Public Sub Abs_SmallestLongLong_Raises6() + Dim L As LongLong = -9223372036854775807^ - 1 + Dim V As Variant + On Error Resume Next + V = Abs(L) + Assert.Strict.AreEqual 6&, Err.Number + End Sub + +End Module diff --git a/bugs/filed/abs-minimum-widens-type/src/Sources/Startup.twin b/bugs/filed/abs-minimum-widens-type/src/Sources/Startup.twin new file mode 100644 index 000000000..be8f5c8bc --- /dev/null +++ b/bugs/filed/abs-minimum-widens-type/src/Sources/Startup.twin @@ -0,0 +1,6 @@ +Module Startup + + Public Sub Main() + End Sub + +End Module diff --git a/bugs/filed/abs-minimum-widens-type/vb6/Module1.bas b/bugs/filed/abs-minimum-widens-type/vb6/Module1.bas new file mode 100644 index 000000000..a2c945794 --- /dev/null +++ b/bugs/filed/abs-minimum-widens-type/vb6/Module1.bas @@ -0,0 +1,22 @@ +Attribute VB_Name = "Module1" +Option Explicit + +' What VB6 does with Abs of the smallest Integer, Long and Currency: error 6. +' Build and run it with: node scripts/bug_repro.mjs vb6 abs-minimum-widens-type + +Private Sub Report(ByVal Label As String, ByVal Result As Variant) + Print #9, Label & ": " & Err.Number & " " & Err.Description & " (" & TypeName(Result) & " " & Result & ")" + Err.Clear +End Sub + +Sub Main() + Dim I As Integer, L As Long, C As Currency, V As Variant + Open App.Path & "\out.txt" For Output As #9 + On Error Resume Next + I = -32767: V = Empty: V = Abs(I): Report "Abs_IntegerAboveSmallest", V + C = -922337203685477.5807@ - 0.0001@: V = Empty: V = Abs(C): Report "Abs_SmallestCurrency", V + V = CInt(-32768): V = Abs(V): Report "Abs_VariantSmallestInteger", V + I = -32768: V = Empty: V = Abs(I): Report "Abs_SmallestInteger", V + L = -2147483647 - 1: V = Empty: V = Abs(L): Report "Abs_SmallestLong", V + Close #9 +End Sub diff --git a/bugs/filed/abs-minimum-widens-type/vb6/Probe.vbp b/bugs/filed/abs-minimum-widens-type/vb6/Probe.vbp new file mode 100644 index 000000000..4d210b0e8 --- /dev/null +++ b/bugs/filed/abs-minimum-widens-type/vb6/Probe.vbp @@ -0,0 +1,7 @@ +Type=Exe +Module=Module1; Module1.bas +Startup="Sub Main" +ExeName32="Probe.exe" +Name="Probe" +CompilationType=0 +OptimizationType=0 diff --git a/bugs/filed/cdate-string-decimal-out-of-range/REPORT.md b/bugs/filed/cdate-string-decimal-out-of-range/REPORT.md new file mode 100644 index 000000000..9edeaa826 --- /dev/null +++ b/bugs/filed/cdate-string-decimal-out-of-range/REPORT.md @@ -0,0 +1,95 @@ +Filed as [twinbasic/twinbasic#2537](https://github.com/twinbasic/twinbasic/issues/2537). + +## `CDate` of a string holding a number outside the `Date` range raises no error, and of such a `Decimal` raises error 6, where VB6 raises error 13 + +**Describe the bug** +A `Date` holds a day number from -657434 (January 1, 100) to 2958465 (December 31, 9999). VB6's `CDate` raises error 13, *Type mismatch*, for a string that holds a number outside that range (`CDate("2958466")`, `CDate("-657435")`) and for a `Decimal` outside it (`CDate(CDec(2958466))`). twinBASIC raises nothing for the string, and the `Date` holds the number, as in the entry *A `Date` outside January 1, 100 to December 31, 9999 is accepted with no error*; for the `Decimal` it raises error 6, *Overflow*, so a handler that tests for 13 does not recognise it. Observed in the compiler's test mode, 32-bit and 64-bit, and in a built exe. + +**To Reproduce** +Steps to reproduce the behavior: +1. Open `cdate-string-decimal-out-of-range.twinproj` (attached as `cdate-string-decimal-out-of-range.zip`). +2. Run its test cases, the module below. On BETA 1010 the four controls pass (the two ends of the range as strings, a `Decimal` in range, and `CDate("abc")`, which raises 13) and the four cases fail: `Actual: 0` for the strings, `Actual: 6` for the `Decimal`s. + +The module is meant to be adapted as a regression test. Each case asserts VB6's result, which the attached VB6 project (`cdate-string-decimal-out-of-range-vb6.zip`) prints: error 13 for the four, and the day numbers and 13 for the controls. +```vb +' Regression tests: CDate of a string that holds a number outside the Date range, and of +' a Decimal outside it, raise error 13. Expected results are VB6's (the VB6 project beside +' this one prints them). tbrun --tests runs each case and prints PASS or FAIL. +[TestFixture] +Module RegressionTests + + ' Controls: the ends of the range as strings and as Decimals, and a string that is + ' not a date (error 13 in both). + [TestCase] + Public Sub CDate_StringLargest_Accepted() + Assert.Strict.AreEqual 2958465#, CDbl(CDate("2958465")) + End Sub + + [TestCase] + Public Sub CDate_StringSmallest_Accepted() + Assert.Strict.AreEqual -657434#, CDbl(CDate("-657434")) + End Sub + + [TestCase] + Public Sub CDate_DecimalInRange_Accepted() + Assert.Strict.AreEqual 5#, CDbl(CDate(CDec(5))) + End Sub + + [TestCase] + Public Sub CDate_NotADate_Raises13() + Dim D As Date + On Error Resume Next + D = CDate("abc") + Assert.Strict.AreEqual 13&, Err.Number + End Sub + + ' The defect: a string holding a number past either end raises nothing. + [TestCase] + Public Sub CDate_StringPastLargest_Raises13() + Dim D As Date + On Error Resume Next + D = CDate("2958466") + Assert.Strict.AreEqual 13&, Err.Number + End Sub + + [TestCase] + Public Sub CDate_StringBeforeSmallest_Raises13() + Dim D As Date + On Error Resume Next + D = CDate("-657435") + Assert.Strict.AreEqual 13&, Err.Number + End Sub + + ' The defect: a Decimal past either end raises 6. + [TestCase] + Public Sub CDate_DecimalPastLargest_Raises13() + Dim D As Date + On Error Resume Next + D = CDate(CDec(2958466)) + Assert.Strict.AreEqual 13&, Err.Number + End Sub + + [TestCase] + Public Sub CDate_DecimalBeforeSmallest_Raises13() + Dim D As Date + On Error Resume Next + D = CDate(CDec(-657435)) + Assert.Strict.AreEqual 13&, Err.Number + End Sub + +End Module +``` + +**Expected behavior** +Error 13, *Type mismatch*, for both, as VB6 does. `CVDate` already raises 13 for both in twinBASIC. + +**Desktop:** + - OS: Windows 10 Pro 22H2 (build 19045) + - twinBASIC compiler version: BETA 1010 + +**Additional context** +Severity: medium for the string, which stores an out-of-range `Date` silently (`CStr`, `Format` and `Year` of it then raise error 5); low for the `Decimal`, which raises an error, only another one. + +What does not reproduce it: `CVDate` of the same values (13 in both); a string that is not a number (`"1/1/10000"` raises 13 in both). A `Double`, `Long`, `Single` or `Currency` outside the range is the other entry: VB6 raises 6 for it, twinBASIC nothing. + + diff --git a/bugs/filed/cdate-string-decimal-out-of-range/cdate-string-decimal-out-of-range.twinproj b/bugs/filed/cdate-string-decimal-out-of-range/cdate-string-decimal-out-of-range.twinproj new file mode 100644 index 000000000..95102c799 Binary files /dev/null and b/bugs/filed/cdate-string-decimal-out-of-range/cdate-string-decimal-out-of-range.twinproj differ diff --git a/bugs/filed/cdate-string-decimal-out-of-range/repro.json b/bugs/filed/cdate-string-decimal-out-of-range/repro.json new file mode 100644 index 000000000..69e0c558c --- /dev/null +++ b/bugs/filed/cdate-string-decimal-out-of-range/repro.json @@ -0,0 +1,17 @@ +{ + "mode": "test", + "expect": { + "exit": 7, + "output": [ + "^PASS RegressionTests.CDate_StringLargest_Accepted$", + "^PASS RegressionTests.CDate_StringSmallest_Accepted$", + "^PASS RegressionTests.CDate_DecimalInRange_Accepted$", + "^PASS RegressionTests.CDate_NotADate_Raises13$", + "^FAIL RegressionTests.CDate_StringPastLargest_Raises13: .*Actual: 0 ", + "^FAIL RegressionTests.CDate_StringBeforeSmallest_Raises13: .*Actual: 0 ", + "^FAIL RegressionTests.CDate_DecimalPastLargest_Raises13: .*Actual: 6 ", + "^FAIL RegressionTests.CDate_DecimalBeforeSmallest_Raises13: .*Actual: 6 " + ] + }, + "issue": 2537 +} diff --git a/bugs/filed/cdate-string-decimal-out-of-range/src/Settings b/bugs/filed/cdate-string-decimal-out-of-range/src/Settings new file mode 100644 index 000000000..f5362c8bd --- /dev/null +++ b/bugs/filed/cdate-string-decimal-out-of-range/src/Settings @@ -0,0 +1,75 @@ +{ + "configuration.inherits": "Defaults", + "project.appTitle": "CdateStringDecimalOutOfRange", + "project.buildPath": "${SourcePath}\\Build\\${ProjectName}_${Architecture}.${FileExtension}", + "project.buildType": "Standard EXE", + "project.description": "Reproduces: CDate of a string holding a number outside the Date range raises no error, and of such a Decimal raises error 6, where VB6 raises error 13 for both", + "project.exportPathIsV2": true, + "project.id": "{02D867BD-5D79-4D06-B87D-258B50EEA603}", + "project.name": "CdateStringDecimalOutOfRange", + "project.optionExplicit": true, + "project.references": [ + { + "id": "{00020430-0000-0000-C000-000000000046}", + "lcid": 0, + "name": "OLE Automation", + "path32": "C:\\Windows\\SysWOW64\\stdole2.tlb", + "path64": "C:\\Windows\\System32\\stdole2.tlb", + "symbolId": "stdole", + "versionMajor": 2, + "versionMinor": 0 + }, + { + "id": "{F50B82D0-DCAB-43FE-9631-11959D4A4728}", + "isCompilerPackage": true, + "licence": "MIT", + "name": "[COMPILER PACKAGE] twinBASIC - VB Compatibility Package (Forms)", + "path32": "", + "path64": "", + "publisher": "TWINBASIC-COMPILER", + "symbolId": "VB", + "versionBuild": 0, + "versionMajor": 0, + "versionMinor": 0, + "versionRevision": 31 + }, + { + "id": "{C192FB39-64CA-4D9B-B477-A5502F48EFCC}", + "isCompilerPackage": true, + "licence": "MIT", + "name": "[COMPILER PACKAGE] twinBASIC - App global class object", + "path32": "", + "path64": "", + "publisher": "TWINBASIC-COMPILER", + "symbolId": "AppGlobalClassProject", + "versionBuild": 0, + "versionMajor": 1, + "versionMinor": 0, + "versionRevision": 0 + }, + { + "id": "{9847A10D-8987-44B2-8B87-EA77DEF7F1D9}", + "isCompilerPackage": true, + "licence": "MIT", + "name": "[COMPILER PACKAGE] Assert", + "path32": "", + "path64": "", + "publisher": "TWINBASIC-COMPILER", + "symbolId": "Assert", + "versionBuild": 0, + "versionMajor": 0, + "versionMinor": 0, + "versionRevision": 24 + } + ], + "project.settingsVersion": 1, + "project.startupObject": "Sub Main", + "project.warnings": { + "errors": [], + "hints": [], + "ignored": [], + "info": [], + "warnings": [] + }, + "runtime.useUnicodeStandardLibrary": true +} diff --git a/bugs/filed/cdate-string-decimal-out-of-range/src/Sources/RegressionTests.twin b/bugs/filed/cdate-string-decimal-out-of-range/src/Sources/RegressionTests.twin new file mode 100644 index 000000000..67972cfec --- /dev/null +++ b/bugs/filed/cdate-string-decimal-out-of-range/src/Sources/RegressionTests.twin @@ -0,0 +1,66 @@ +' Regression tests: CDate of a string that holds a number outside the Date range, and of +' a Decimal outside it, raise error 13. Expected results are VB6's (the VB6 project beside +' this one prints them). tbrun --tests runs each case and prints PASS or FAIL. +[TestFixture] +Module RegressionTests + + ' Controls: the ends of the range as strings and as Decimals, and a string that is + ' not a date (error 13 in both). + [TestCase] + Public Sub CDate_StringLargest_Accepted() + Assert.Strict.AreEqual 2958465#, CDbl(CDate("2958465")) + End Sub + + [TestCase] + Public Sub CDate_StringSmallest_Accepted() + Assert.Strict.AreEqual -657434#, CDbl(CDate("-657434")) + End Sub + + [TestCase] + Public Sub CDate_DecimalInRange_Accepted() + Assert.Strict.AreEqual 5#, CDbl(CDate(CDec(5))) + End Sub + + [TestCase] + Public Sub CDate_NotADate_Raises13() + Dim D As Date + On Error Resume Next + D = CDate("abc") + Assert.Strict.AreEqual 13&, Err.Number + End Sub + + ' The defect: a string holding a number past either end raises nothing. + [TestCase] + Public Sub CDate_StringPastLargest_Raises13() + Dim D As Date + On Error Resume Next + D = CDate("2958466") + Assert.Strict.AreEqual 13&, Err.Number + End Sub + + [TestCase] + Public Sub CDate_StringBeforeSmallest_Raises13() + Dim D As Date + On Error Resume Next + D = CDate("-657435") + Assert.Strict.AreEqual 13&, Err.Number + End Sub + + ' The defect: a Decimal past either end raises 6. + [TestCase] + Public Sub CDate_DecimalPastLargest_Raises13() + Dim D As Date + On Error Resume Next + D = CDate(CDec(2958466)) + Assert.Strict.AreEqual 13&, Err.Number + End Sub + + [TestCase] + Public Sub CDate_DecimalBeforeSmallest_Raises13() + Dim D As Date + On Error Resume Next + D = CDate(CDec(-657435)) + Assert.Strict.AreEqual 13&, Err.Number + End Sub + +End Module diff --git a/bugs/filed/cdate-string-decimal-out-of-range/src/Sources/Startup.twin b/bugs/filed/cdate-string-decimal-out-of-range/src/Sources/Startup.twin new file mode 100644 index 000000000..be8f5c8bc --- /dev/null +++ b/bugs/filed/cdate-string-decimal-out-of-range/src/Sources/Startup.twin @@ -0,0 +1,6 @@ +Module Startup + + Public Sub Main() + End Sub + +End Module diff --git a/bugs/filed/cdate-string-decimal-out-of-range/vb6/Module1.bas b/bugs/filed/cdate-string-decimal-out-of-range/vb6/Module1.bas new file mode 100644 index 000000000..c05249c4e --- /dev/null +++ b/bugs/filed/cdate-string-decimal-out-of-range/vb6/Module1.bas @@ -0,0 +1,26 @@ +Attribute VB_Name = "Module1" +Option Explicit + +' What VB6 does with CDate of a string holding a number outside the Date range, and of a +' Decimal outside it: error 13 for both. +' Build and run it with: node scripts/bug_repro.mjs vb6 cdate-string-decimal-out-of-range + +Private Sub Report(ByVal Label As String, ByVal Result As Variant) + Print #9, Label & ": " & Err.Number & " " & Err.Description & " (" & Result & ")" + Err.Clear +End Sub + +Sub Main() + Dim R As Variant + Open App.Path & "\out.txt" For Output As #9 + On Error Resume Next + R = Empty: R = CDbl(CDate("2958465")): Report "CDate_StringLargest", R + R = Empty: R = CDbl(CDate("-657434")): Report "CDate_StringSmallest", R + R = Empty: R = CDbl(CDate(CDec(5))): Report "CDate_DecimalInRange", R + R = Empty: R = CDate("abc"): Report "CDate_NotADate", R + R = Empty: R = CDate("2958466"): Report "CDate_StringPastLargest", R + R = Empty: R = CDate("-657435"): Report "CDate_StringBeforeSmallest", R + R = Empty: R = CDate(CDec(2958466)): Report "CDate_DecimalPastLargest", R + R = Empty: R = CDate(CDec(-657435)): Report "CDate_DecimalBeforeSmallest", R + Close #9 +End Sub diff --git a/bugs/filed/cdate-string-decimal-out-of-range/vb6/Probe.vbp b/bugs/filed/cdate-string-decimal-out-of-range/vb6/Probe.vbp new file mode 100644 index 000000000..4d210b0e8 --- /dev/null +++ b/bugs/filed/cdate-string-decimal-out-of-range/vb6/Probe.vbp @@ -0,0 +1,7 @@ +Type=Exe +Module=Module1; Module1.bas +Startup="Sub Main" +ExeName32="Probe.exe" +Name="Probe" +CompilationType=0 +OptimizationType=0 diff --git a/bugs/filed/class-field-init-reads-field/REPORT.md b/bugs/filed/class-field-init-reads-field/REPORT.md new file mode 100644 index 000000000..2b2fbd401 --- /dev/null +++ b/bugs/filed/class-field-init-reads-field/REPORT.md @@ -0,0 +1,41 @@ +Filed as [twinbasic/twinbasic#2546](https://github.com/twinbasic/twinbasic/issues/2546). + +## A class field whose initializer reads another field of the class fails the build with a codegen error + +**Describe the bug** +A field initializer in a class that reads another field of the same class (`Public Second As Long = First + 1`) compiles with no diagnostic, and the build fails once the class is used: `[LINKER] compilation (codegen) error detected in 'Counter.{fields_init}' at line #1`, then `[LINKER] FAILED due to compilation errors`. A run in the IDE goes ahead instead, and the field holds an arbitrary number: with `A` = 1, `X = A + 1` read `1073741889`. Observed with `tbbuild --build` and with `tbrun` (a `[RunAfterBuild]` procedure in the IDE). + +**To Reproduce** +Steps to reproduce the behavior: +1. Open `class-field-init-reads-field.twinproj` (attached as `class-field-init-reads-field.zip`). Its file `Startup.twin` is: + ``` + Module Startup + Public Sub Main() + Dim c As New Counter + Debug.Print c.First & ", " & c.Second + End Sub + End Module + + Class Counter + Public First As Long = 1 + Public Second As Long = First + 1 + End Class + ``` +2. Compile the project: no diagnostic. +3. Build it. The DEBUG CONSOLE prints `[LINKER] compilation (codegen) error detected in 'Counter.{fields_init}' at line #1` and `[BUILD] failed`. + +**Expected behavior** +The build succeeds and `Main` prints `1, 2`: `Second`'s initializer reads `First`, whose initializer comes before it. If reading another field is not meant to be allowed, the compiler should say so at the declaration, as it does for a call to a procedure of the project (TB5079). Field initializers are a twinBASIC extension, so there is no VB6 behaviour to compare with. + +**Desktop:** + - OS: Windows 10 Pro 22H2 (build 19045) + - twinBASIC compiler version: BETA 1010 + +**Additional context** +The same codegen error, one line per class, for `X = A` with no arithmetic, `X = Me.A + 1`, `String` fields (`X = S & "b"`), a `ReadOnly` field read, a field declared below the one that reads it, and a field with no initializer of its own, in 32-bit and 64-bit builds. These are right: a field that reads a module-level variable (`X = Mods.MV + 1`) or a constant, a module-level variable that reads a variable of its own module (`MW = MV + 1`), and a class that is never created (it is not compiled, so the build does not fail). + +Severity: medium. The build fails with a message that names the class but not the field or the reason, and the IDE run gives a wrong value with no message at all. Setting the field in `Class_Initialize` or `Sub New` avoids it. + +Not tried: an `Object` or `Variant` field, LLVM. + + diff --git a/bugs/class-field-init-reads-field/class-field-init-reads-field.twinproj b/bugs/filed/class-field-init-reads-field/class-field-init-reads-field.twinproj similarity index 100% rename from bugs/class-field-init-reads-field/class-field-init-reads-field.twinproj rename to bugs/filed/class-field-init-reads-field/class-field-init-reads-field.twinproj diff --git a/bugs/class-field-init-reads-field/repro.json b/bugs/filed/class-field-init-reads-field/repro.json similarity index 87% rename from bugs/class-field-init-reads-field/repro.json rename to bugs/filed/class-field-init-reads-field/repro.json index a8cf82943..644710dae 100644 --- a/bugs/class-field-init-reads-field/repro.json +++ b/bugs/filed/class-field-init-reads-field/repro.json @@ -3,5 +3,6 @@ "expect": { "exit": 5, "message": "compilation \\(codegen\\) error detected in 'Counter\\.\\{fields_init\\}'" - } + }, + "issue": 2546 } diff --git a/bugs/class-field-init-reads-field/src/Settings b/bugs/filed/class-field-init-reads-field/src/Settings similarity index 100% rename from bugs/class-field-init-reads-field/src/Settings rename to bugs/filed/class-field-init-reads-field/src/Settings diff --git a/bugs/class-field-init-reads-field/src/Sources/Startup.twin b/bugs/filed/class-field-init-reads-field/src/Sources/Startup.twin similarity index 100% rename from bugs/class-field-init-reads-field/src/Sources/Startup.twin rename to bugs/filed/class-field-init-reads-field/src/Sources/Startup.twin diff --git a/bugs/filed/error-function-above-65535/REPORT.md b/bugs/filed/error-function-above-65535/REPORT.md new file mode 100644 index 000000000..e5eb4bc11 --- /dev/null +++ b/bugs/filed/error-function-above-65535/REPORT.md @@ -0,0 +1,101 @@ +Filed as [twinbasic/twinbasic#2538](https://github.com/twinbasic/twinbasic/issues/2538). + +## `Error` and `Error$` of a number above 65535 return a message, where VB6 raises error 6 + +**Describe the bug** +`Error$(n)` returns the message of error number *n*. VB6 raises error 6, *Overflow*, when *n* is greater than 65535 (after rounding: `Error(65535.5)` raises it): `Error$(65536)`, `Error(100000)`, `Error(2147483647)`. twinBASIC raises nothing and returns a message. A negative number, an HRESULT such as -2147467259 included, gives a message in both. Observed in the compiler's test mode, 32-bit and 64-bit, and in a built exe. + +**To Reproduce** +Steps to reproduce the behavior: +1. Open `error-function-above-65535.twinproj` (attached as `error-function-above-65535.zip`). +2. Run its test cases, the module below. On BETA 1010 the three controls pass (65535, -1 and -2147467259) and the four cases above 65535 fail with `Actual: 0`. + +The module is meant to be adapted as a regression test. Each case asserts VB6's result, which the attached VB6 project (`error-function-above-65535-vb6.zip`) prints: error 6 for the four, and a message for the controls. +```vb +' Regression tests: Error and Error$ of a number above 65535 raise error 6. Expected +' results are VB6's (the VB6 project beside this one prints them). tbrun --tests runs +' each case and prints PASS or FAIL. +[TestFixture] +Module RegressionTests + + ' Controls: the largest number, a negative one and an HRESULT give a message. + [TestCase] + Public Sub Error_Largest_Accepted() + Dim N As Long = 65535 + Dim S As String + On Error Resume Next + S = Error$(N) + Assert.Strict.AreEqual 0&, Err.Number + End Sub + + [TestCase] + Public Sub Error_Negative_Accepted() + Dim N As Long = -1 + Dim S As String + On Error Resume Next + S = Error$(N) + Assert.Strict.AreEqual 0&, Err.Number + End Sub + + [TestCase] + Public Sub Error_HResult_Accepted() + Dim N As Long = -2147467259 + Dim S As String + On Error Resume Next + S = Error$(N) + Assert.Strict.AreEqual 0&, Err.Number + End Sub + + ' The defect: a number above 65535 returns a message. + [TestCase] + Public Sub ErrorDollar_65536_Raises6() + Dim N As Long = 65536 + Dim S As String + On Error Resume Next + S = Error$(N) + Assert.Strict.AreEqual 6&, Err.Number + End Sub + + [TestCase] + Public Sub Error_65536_Raises6() + Dim N As Long = 65536 + Dim V As Variant + On Error Resume Next + V = Error(N) + Assert.Strict.AreEqual 6&, Err.Number + End Sub + + [TestCase] + Public Sub Error_LargestLong_Raises6() + Dim N As Long = 2147483647 + Dim V As Variant + On Error Resume Next + V = Error(N) + Assert.Strict.AreEqual 6&, Err.Number + End Sub + + [TestCase] + Public Sub Error_RoundsTo65536_Raises6() + Dim X As Double = 65535.5 + Dim V As Variant + On Error Resume Next + V = Error(X) + Assert.Strict.AreEqual 6&, Err.Number + End Sub + +End Module +``` + +**Expected behavior** +Error 6, *Overflow*, for a number above 65535, as VB6 does. + +**Desktop:** + - OS: Windows 10 Pro 22H2 (build 19045) + - twinBASIC compiler version: BETA 1010 + +**Additional context** +Severity: low. A program that passes an arbitrary number gets a message where VB6 code expected an error. + +Related, and not part of this report: `Err.Raise 65536` raises 5 in VB6, and twinBASIC raises 65536 as *Automation error*. + + diff --git a/bugs/filed/error-function-above-65535/error-function-above-65535.twinproj b/bugs/filed/error-function-above-65535/error-function-above-65535.twinproj new file mode 100644 index 000000000..29de1fa07 Binary files /dev/null and b/bugs/filed/error-function-above-65535/error-function-above-65535.twinproj differ diff --git a/bugs/filed/error-function-above-65535/repro.json b/bugs/filed/error-function-above-65535/repro.json new file mode 100644 index 000000000..6ad92043b --- /dev/null +++ b/bugs/filed/error-function-above-65535/repro.json @@ -0,0 +1,16 @@ +{ + "mode": "test", + "expect": { + "exit": 7, + "output": [ + "^PASS RegressionTests.Error_Largest_Accepted$", + "^PASS RegressionTests.Error_Negative_Accepted$", + "^PASS RegressionTests.Error_HResult_Accepted$", + "^FAIL RegressionTests.ErrorDollar_65536_Raises6: .*Actual: 0 ", + "^FAIL RegressionTests.Error_65536_Raises6: .*Actual: 0 ", + "^FAIL RegressionTests.Error_LargestLong_Raises6: .*Actual: 0 ", + "^FAIL RegressionTests.Error_RoundsTo65536_Raises6: .*Actual: 0 " + ] + }, + "issue": 2538 +} diff --git a/bugs/filed/error-function-above-65535/src/Settings b/bugs/filed/error-function-above-65535/src/Settings new file mode 100644 index 000000000..198c625ee --- /dev/null +++ b/bugs/filed/error-function-above-65535/src/Settings @@ -0,0 +1,75 @@ +{ + "configuration.inherits": "Defaults", + "project.appTitle": "ErrorFunctionAbove65535", + "project.buildPath": "${SourcePath}\\Build\\${ProjectName}_${Architecture}.${FileExtension}", + "project.buildType": "Standard EXE", + "project.description": "Reproduces: Error and Error$ of a number above 65535 return a message, where VB6 raises error 6", + "project.exportPathIsV2": true, + "project.id": "{10BCC70D-70A8-43FF-B964-857A90974DDB}", + "project.name": "ErrorFunctionAbove65535", + "project.optionExplicit": true, + "project.references": [ + { + "id": "{00020430-0000-0000-C000-000000000046}", + "lcid": 0, + "name": "OLE Automation", + "path32": "C:\\Windows\\SysWOW64\\stdole2.tlb", + "path64": "C:\\Windows\\System32\\stdole2.tlb", + "symbolId": "stdole", + "versionMajor": 2, + "versionMinor": 0 + }, + { + "id": "{F50B82D0-DCAB-43FE-9631-11959D4A4728}", + "isCompilerPackage": true, + "licence": "MIT", + "name": "[COMPILER PACKAGE] twinBASIC - VB Compatibility Package (Forms)", + "path32": "", + "path64": "", + "publisher": "TWINBASIC-COMPILER", + "symbolId": "VB", + "versionBuild": 0, + "versionMajor": 0, + "versionMinor": 0, + "versionRevision": 31 + }, + { + "id": "{C192FB39-64CA-4D9B-B477-A5502F48EFCC}", + "isCompilerPackage": true, + "licence": "MIT", + "name": "[COMPILER PACKAGE] twinBASIC - App global class object", + "path32": "", + "path64": "", + "publisher": "TWINBASIC-COMPILER", + "symbolId": "AppGlobalClassProject", + "versionBuild": 0, + "versionMajor": 1, + "versionMinor": 0, + "versionRevision": 0 + }, + { + "id": "{9847A10D-8987-44B2-8B87-EA77DEF7F1D9}", + "isCompilerPackage": true, + "licence": "MIT", + "name": "[COMPILER PACKAGE] Assert", + "path32": "", + "path64": "", + "publisher": "TWINBASIC-COMPILER", + "symbolId": "Assert", + "versionBuild": 0, + "versionMajor": 0, + "versionMinor": 0, + "versionRevision": 24 + } + ], + "project.settingsVersion": 1, + "project.startupObject": "Sub Main", + "project.warnings": { + "errors": [], + "hints": [], + "ignored": [], + "info": [], + "warnings": [] + }, + "runtime.useUnicodeStandardLibrary": true +} diff --git a/bugs/filed/error-function-above-65535/src/Sources/RegressionTests.twin b/bugs/filed/error-function-above-65535/src/Sources/RegressionTests.twin new file mode 100644 index 000000000..9ba9ebdb1 --- /dev/null +++ b/bugs/filed/error-function-above-65535/src/Sources/RegressionTests.twin @@ -0,0 +1,72 @@ +' Regression tests: Error and Error$ of a number above 65535 raise error 6. Expected +' results are VB6's (the VB6 project beside this one prints them). tbrun --tests runs +' each case and prints PASS or FAIL. +[TestFixture] +Module RegressionTests + + ' Controls: the largest number, a negative one and an HRESULT give a message. + [TestCase] + Public Sub Error_Largest_Accepted() + Dim N As Long = 65535 + Dim S As String + On Error Resume Next + S = Error$(N) + Assert.Strict.AreEqual 0&, Err.Number + End Sub + + [TestCase] + Public Sub Error_Negative_Accepted() + Dim N As Long = -1 + Dim S As String + On Error Resume Next + S = Error$(N) + Assert.Strict.AreEqual 0&, Err.Number + End Sub + + [TestCase] + Public Sub Error_HResult_Accepted() + Dim N As Long = -2147467259 + Dim S As String + On Error Resume Next + S = Error$(N) + Assert.Strict.AreEqual 0&, Err.Number + End Sub + + ' The defect: a number above 65535 returns a message. + [TestCase] + Public Sub ErrorDollar_65536_Raises6() + Dim N As Long = 65536 + Dim S As String + On Error Resume Next + S = Error$(N) + Assert.Strict.AreEqual 6&, Err.Number + End Sub + + [TestCase] + Public Sub Error_65536_Raises6() + Dim N As Long = 65536 + Dim V As Variant + On Error Resume Next + V = Error(N) + Assert.Strict.AreEqual 6&, Err.Number + End Sub + + [TestCase] + Public Sub Error_LargestLong_Raises6() + Dim N As Long = 2147483647 + Dim V As Variant + On Error Resume Next + V = Error(N) + Assert.Strict.AreEqual 6&, Err.Number + End Sub + + [TestCase] + Public Sub Error_RoundsTo65536_Raises6() + Dim X As Double = 65535.5 + Dim V As Variant + On Error Resume Next + V = Error(X) + Assert.Strict.AreEqual 6&, Err.Number + End Sub + +End Module diff --git a/bugs/filed/error-function-above-65535/src/Sources/Startup.twin b/bugs/filed/error-function-above-65535/src/Sources/Startup.twin new file mode 100644 index 000000000..be8f5c8bc --- /dev/null +++ b/bugs/filed/error-function-above-65535/src/Sources/Startup.twin @@ -0,0 +1,6 @@ +Module Startup + + Public Sub Main() + End Sub + +End Module diff --git a/bugs/filed/error-function-above-65535/vb6/Module1.bas b/bugs/filed/error-function-above-65535/vb6/Module1.bas new file mode 100644 index 000000000..86339299f --- /dev/null +++ b/bugs/filed/error-function-above-65535/vb6/Module1.bas @@ -0,0 +1,26 @@ +Attribute VB_Name = "Module1" +Option Explicit + +' What VB6 does with Error and Error$ of a number above 65535: error 6. +' Build and run it with: node scripts/bug_repro.mjs vb6 error-function-above-65535 + +Private Sub Report(ByVal Label As String, ByVal Result As Variant) + Print #9, Label & ": " & Err.Number & " " & Err.Description & " (" & Result & ")" + Err.Clear +End Sub + +Sub Main() + Dim R As Variant + Dim N As Long + Dim X As Double + Open App.Path & "\out.txt" For Output As #9 + On Error Resume Next + N = 65535: R = Empty: R = Error$(N): Report "Error_Largest", R + N = -1: R = Empty: R = Error$(N): Report "Error_Negative", R + N = -2147467259: R = Empty: R = Error$(N): Report "Error_HResult", R + N = 65536: R = Empty: R = Error$(N): Report "ErrorDollar_65536", R + N = 65536: R = Empty: R = Error(N): Report "Error_65536", R + N = 2147483647: R = Empty: R = Error(N): Report "Error_LargestLong", R + X = 65535.5: R = Empty: R = Error(X): Report "Error_RoundsTo65536", R + Close #9 +End Sub diff --git a/bugs/filed/error-function-above-65535/vb6/Probe.vbp b/bugs/filed/error-function-above-65535/vb6/Probe.vbp new file mode 100644 index 000000000..4d210b0e8 --- /dev/null +++ b/bugs/filed/error-function-above-65535/vb6/Probe.vbp @@ -0,0 +1,7 @@ +Type=Exe +Module=Module1; Module1.bas +Startup="Sub Main" +ExeName32="Probe.exe" +Name="Probe" +CompilationType=0 +OptimizationType=0 diff --git a/bugs/filed/module-init-reads-other-module-ide/REPORT.md b/bugs/filed/module-init-reads-other-module-ide/REPORT.md new file mode 100644 index 000000000..9b179248b --- /dev/null +++ b/bugs/filed/module-init-reads-other-module-ide/REPORT.md @@ -0,0 +1,50 @@ +Filed as [twinbasic/twinbasic#2545](https://github.com/twinbasic/twinbasic/issues/2545). + +## In the IDE, a module-level initializer that reads another module's variable leaves every module-level variable at 0 + +**Describe the bug** +When one module-level variable's initializer reads a variable of another module (`Public FromConfig As Long = Config.Limit + 100`), code run in the IDE reads every module-level variable of the project as 0, including ones whose initializers are constants (`Public Limit As Long = 5`). The built exe gets them all right. Observed with a `[RunAfterBuild]` procedure, through `tbrun` and `bug_repro.mjs run`; the exe was run with `tbrun --exe`. + +**To Reproduce** +Steps to reproduce the behavior: +1. Open `module-init-reads-other-module-ide.twinproj` (attached as `module-init-reads-other-module-ide.zip`). Its file `Startup.twin` is: + ``` + Module Config + Public Const Base As Long = 7 + Public Limit As Long = 5 + Public Doubled As Long = Base * 2 + End Module + + Module Other + Public FromConfig As Long = Config.Limit + 100 + End Module + + Class Box + Public Size As Long = 3 + End Class + + Module Startup + Public Sub Main() + Dim b As New Box + Debug.Print "Limit=" & Config.Limit & " Doubled=" & Config.Doubled & " FromConfig=" & Other.FromConfig & " Box.Size=" & b.Size + End Sub + End Module + ``` +2. Add a module with a `[RunAfterBuild]` procedure that calls `Startup.Main`, and build the project. +3. The DEBUG CONSOLE prints `Limit=0 Doubled=0 FromConfig=0 Box.Size=3`. + +**Expected behavior** +`Limit=5 Doubled=14 FromConfig=105 Box.Size=3`, which is what the built exe prints. Module-level initializers are a twinBASIC extension, so there is no VB6 behaviour to compare with. + +**Desktop:** + - OS: Windows 10 Pro 22H2 (build 19045) + - twinBASIC compiler version: BETA 1010 + +**Additional context** +Every module-level variable reads 0 as long as one initializer reads another module's variable. Each of these makes the IDE print `Limit=5 Doubled=14`: the same project without `Other`, `FromConfig = 100`, `FromConfig = Config.Base + 100` (another module's constant), and `FromConfig = Limit2 + 100`, where `Limit2` is a variable of `Other` itself. A class field initializer runs either way (`Box.Size=3`). In the exe, a module whose initializer reads a module that is set up after it reads 0 there; that is a matter of order, and the IDE gives 0 for every module, whatever the order. + +Severity: medium. A `[RunAfterBuild]` procedure that signs or post-processes the build sees wrong settings with no message, and code that runs correctly as an exe behaves differently in the IDE. + +Not tried: a run with F5 (only `[RunAfterBuild]` was observed), a variable of the module's own read with a qualified name (`Other.Limit2`), 64-bit, LLVM. + + diff --git a/bugs/module-init-reads-other-module-ide/module-init-reads-other-module-ide.twinproj b/bugs/filed/module-init-reads-other-module-ide/module-init-reads-other-module-ide.twinproj similarity index 100% rename from bugs/module-init-reads-other-module-ide/module-init-reads-other-module-ide.twinproj rename to bugs/filed/module-init-reads-other-module-ide/module-init-reads-other-module-ide.twinproj diff --git a/bugs/module-init-reads-other-module-ide/repro.json b/bugs/filed/module-init-reads-other-module-ide/repro.json similarity index 85% rename from bugs/module-init-reads-other-module-ide/repro.json rename to bugs/filed/module-init-reads-other-module-ide/repro.json index 46a3ddb5e..0f6f0169e 100644 --- a/bugs/module-init-reads-other-module-ide/repro.json +++ b/bugs/filed/module-init-reads-other-module-ide/repro.json @@ -5,5 +5,6 @@ "output": [ "^Limit=0 Doubled=0 FromConfig=0 Box\\.Size=3$" ] - } + }, + "issue": 2545 } diff --git a/bugs/module-init-reads-other-module-ide/src/Settings b/bugs/filed/module-init-reads-other-module-ide/src/Settings similarity index 100% rename from bugs/module-init-reads-other-module-ide/src/Settings rename to bugs/filed/module-init-reads-other-module-ide/src/Settings diff --git a/bugs/module-init-reads-other-module-ide/src/Sources/Startup.twin b/bugs/filed/module-init-reads-other-module-ide/src/Sources/Startup.twin similarity index 100% rename from bugs/module-init-reads-other-module-ide/src/Sources/Startup.twin rename to bugs/filed/module-init-reads-other-module-ide/src/Sources/Startup.twin diff --git a/bugs/filed/negative-zero-text-win64/REPORT.md b/bugs/filed/negative-zero-text-win64/REPORT.md new file mode 100644 index 000000000..c088b6699 --- /dev/null +++ b/bugs/filed/negative-zero-text-win64/REPORT.md @@ -0,0 +1,30 @@ +Filed as [twinbasic/twinbasic#2547](https://github.com/twinbasic/twinbasic/issues/2547). + +## A 64-bit build prints a negative zero as `-0` + +**Describe the bug** +A `Single` or `Double` that holds a negative zero (the sign bit set, every other bit clear) converts to the text `-0` in a 64-bit build: `CStr`, `Str$` (with no leading space), `Format$`, concatenation with `&` and `Debug.Print` all give it. A 32-bit twinBASIC build gives `0` (`Str$` gives ` 0`), and so does VB6. Both builds hold the negative zero: the sign bit (`&H80000000` in a `Single`, `&H8000000000000000` in a `Double`) is read back through `RtlMoveMemory`, and the value compares equal to 0. It was found on a page of the documentation whose sample prints a GDI+ matrix offset that is a negative zero, `0 5 -0` in a 64-bit build where the page says `0 5 0`. It was observed in IDE runs. + +**To Reproduce** +Steps to reproduce the behavior: +1. Open `negative-zero-text-win64.twinproj` (attached as `negative-zero-text-win64.zip`). Its module `NegativeZeroTextTests` has one `[TestCase]` for each conversion, asserting what VB6 gives, two cases that check the sign bit, and two controls. +2. Run the test cases as a 64-bit build (`tbrun --tests --arch win64`). The seven conversion cases fail, with `-0` as `Actual`; the sign-bit cases and the controls pass. As a 32-bit build all eleven pass. +3. The shortest form of it, in a 64-bit build: +```vb +Dim s As Single +s = 0! +s = -s +Debug.Print CStr(s), Str$(s), Format$(s), "x=" & s ' VB6 and 32-bit: 0 " 0" 0 x=0 ; 64-bit: -0 -0 -0 x=-0 +``` + +**Expected behavior** +The same text in a 64-bit build as in a 32-bit one and in VB6: `0` from `CStr`, `Format$` and `&`, and ` 0` from `Str$`. The reproducer's `vb6/` project prints them (attached as `negative-zero-text-win64-vb6.zip`). + +**Desktop:** + - OS: Windows 10 Pro 22H2 (build 19045) + - twinBASIC compiler version: BETA 1010 + +**Additional context** +The reproducer's `repro.json` names the 64-bit build (`"arch": "win64"`). A positive zero and an ordinary negative number (`-1.5`) print the same in both builds. Severity: low. A negative zero is the result of `-x` for a zero `x`, and a COM or API result can hold one, so a program meets it, and one that shows or compares the text behaves differently in the two builds. + + diff --git a/bugs/negative-zero-text-win64/negative-zero-text-win64.twinproj b/bugs/filed/negative-zero-text-win64/negative-zero-text-win64.twinproj similarity index 100% rename from bugs/negative-zero-text-win64/negative-zero-text-win64.twinproj rename to bugs/filed/negative-zero-text-win64/negative-zero-text-win64.twinproj diff --git a/bugs/negative-zero-text-win64/repro.json b/bugs/filed/negative-zero-text-win64/repro.json similarity index 97% rename from bugs/negative-zero-text-win64/repro.json rename to bugs/filed/negative-zero-text-win64/repro.json index 346122f12..c69b57b6f 100644 --- a/bugs/negative-zero-text-win64/repro.json +++ b/bugs/filed/negative-zero-text-win64/repro.json @@ -16,5 +16,6 @@ "^PASS NegativeZeroTextTests.CStrOfPositiveZero_Is0$", "^PASS NegativeZeroTextTests.CStrOfNegativeNumber_IsUnchanged$" ] - } + }, + "issue": 2547 } diff --git a/bugs/negative-zero-text-win64/src/Settings b/bugs/filed/negative-zero-text-win64/src/Settings similarity index 100% rename from bugs/negative-zero-text-win64/src/Settings rename to bugs/filed/negative-zero-text-win64/src/Settings diff --git a/bugs/negative-zero-text-win64/src/Sources/RegressionTests.twin b/bugs/filed/negative-zero-text-win64/src/Sources/RegressionTests.twin similarity index 100% rename from bugs/negative-zero-text-win64/src/Sources/RegressionTests.twin rename to bugs/filed/negative-zero-text-win64/src/Sources/RegressionTests.twin diff --git a/bugs/negative-zero-text-win64/src/Sources/Startup.twin b/bugs/filed/negative-zero-text-win64/src/Sources/Startup.twin similarity index 100% rename from bugs/negative-zero-text-win64/src/Sources/Startup.twin rename to bugs/filed/negative-zero-text-win64/src/Sources/Startup.twin diff --git a/bugs/negative-zero-text-win64/vb6/Module1.bas b/bugs/filed/negative-zero-text-win64/vb6/Module1.bas similarity index 100% rename from bugs/negative-zero-text-win64/vb6/Module1.bas rename to bugs/filed/negative-zero-text-win64/vb6/Module1.bas diff --git a/bugs/filed/negative-zero-text-win64/vb6/Probe.vbp b/bugs/filed/negative-zero-text-win64/vb6/Probe.vbp new file mode 100644 index 000000000..4d210b0e8 --- /dev/null +++ b/bugs/filed/negative-zero-text-win64/vb6/Probe.vbp @@ -0,0 +1,7 @@ +Type=Exe +Module=Module1; Module1.bas +Startup="Sub Main" +ExeName32="Probe.exe" +Name="Probe" +CompilationType=0 +OptimizationType=0 diff --git a/bugs/filed/redim-inverted-bounds-error/REPORT.md b/bugs/filed/redim-inverted-bounds-error/REPORT.md new file mode 100644 index 000000000..cc1ffcb67 --- /dev/null +++ b/bugs/filed/redim-inverted-bounds-error/REPORT.md @@ -0,0 +1,91 @@ +Filed as [twinbasic/twinbasic#2544](https://github.com/twinbasic/twinbasic/issues/2544). + +## `ReDim` with an upper bound less than its lower bound raises -2147467259, *Unspecified error*, where VB6 raises error 9 + +**Describe the bug** +`ReDim X(0 To -1)`, `ReDim X(-1)` and `ReDim X(5 To 4, 1)` on a dynamic array (`Dim X() As Long`), all of which give a dimension an upper bound below its lower bound, raise error -2147467259 (`&H80004005`), *Unspecified error*, where VB6 raises error 9, *Subscript out of range*. A handler that tests for 9 does not recognise it. Observed in the compiler's test mode, 32-bit and 64-bit, and in a probe run after a build. + +**To Reproduce** +Steps to reproduce the behavior: +1. Open `redim-inverted-bounds-error.twinproj` (attached as `redim-inverted-bounds-error.zip`). +2. Run its test cases, the module below. On BETA 1010 the three controls pass (`ReDim X(0 To 0)`, `ReDim X(-3 To -1)`, and a subscript above a fixed-size array's range, which raises 9) and the four cases on an inverted bound fail with `Actual: -2147467259`; `Err.Description` is *Unspecified error*. + +The module is meant to be adapted as a regression test. Each case asserts VB6's result, which the attached VB6 project (`redim-inverted-bounds-error-vb6.zip`) prints: 9, *Subscript out of range*, for each of the three statements. +```vb +' Regression tests: ReDim with an upper bound less than its lower bound raises error 9, +' "Subscript out of range". Expected numbers are VB6's (the VB6 project beside this one +' prints them). tbrun --tests runs each case and prints PASS or FAIL. +[TestFixture] +Module RegressionTests + + ' Controls: bounds in order, an empty-looking but valid range, and a subscript out of range. + [TestCase] + Public Sub ReDim_OneElement_Succeeds() + Dim X() As Long + ReDim X(0 To 0) + Assert.Strict.AreEqual 0&, UBound(X) + End Sub + + [TestCase] + Public Sub ReDim_NegativeBoundsInOrder_Succeeds() + Dim X() As Long + ReDim X(-3 To -1) + Assert.Strict.AreEqual -3&, LBound(X) + End Sub + + [TestCase] + Public Sub FixedArray_SubscriptAboveRange_Raises9() + Dim A(1 To 3) As Long + On Error Resume Next + A(4) = 1 + Assert.Strict.AreEqual 9&, Err.Number + End Sub + + [TestCase] + Public Sub ReDim_ZeroToMinusOne_Raises9() + Dim X() As Long + On Error Resume Next + ReDim X(0 To -1) + Assert.Strict.AreEqual 9&, Err.Number + End Sub + + [TestCase] + Public Sub ReDim_MinusOne_Raises9() + Dim X() As Long + On Error Resume Next + ReDim X(-1) + Assert.Strict.AreEqual 9&, Err.Number + End Sub + + [TestCase] + Public Sub ReDim_FiveToFour_SecondDimension_Raises9() + Dim X() As Long + On Error Resume Next + ReDim X(5 To 4, 1) + Assert.Strict.AreEqual 9&, Err.Number + End Sub + + [TestCase] + Public Sub ReDim_ErrorDescription() + Dim X() As Long + On Error Resume Next + ReDim X(0 To -1) + Assert.Strict.AreEqual "Subscript out of range", Err.Description + End Sub + +End Module +``` + +**Expected behavior** +Error 9, *Subscript out of range*, as VB6 raises, and as twinBASIC itself raises for a subscript outside a fixed-size array. + +**Desktop:** + - OS: Windows 10 Pro 22H2 (build 19045) + - twinBASIC compiler version: BETA 1010 + +**Additional context** +Severity: low. An error is raised wherever VB6 raises one, so no failure goes unreported, but a handler that tests for 9 does not recognise it, and *Unspecified error* does not say what is wrong. It is the same symptom as "An index out of range on a fixed-size array member of a UDT raises -2147467259, *Unspecified error*, where VB6 raises error 9": a bounds failure that reaches the program as *Unspecified error*, here from the allocation rather than from a subscript. + +What does not reproduce it: bounds in order (`0 To 0`, `-3 To -1`), and a subscript outside a fixed-size array that is not in a UDT. + + diff --git a/bugs/redim-inverted-bounds-error/redim-inverted-bounds-error.twinproj b/bugs/filed/redim-inverted-bounds-error/redim-inverted-bounds-error.twinproj similarity index 100% rename from bugs/redim-inverted-bounds-error/redim-inverted-bounds-error.twinproj rename to bugs/filed/redim-inverted-bounds-error/redim-inverted-bounds-error.twinproj diff --git a/bugs/redim-inverted-bounds-error/repro.json b/bugs/filed/redim-inverted-bounds-error/repro.json similarity index 96% rename from bugs/redim-inverted-bounds-error/repro.json rename to bugs/filed/redim-inverted-bounds-error/repro.json index 5f4a12400..020b28ffc 100644 --- a/bugs/redim-inverted-bounds-error/repro.json +++ b/bugs/filed/redim-inverted-bounds-error/repro.json @@ -11,5 +11,6 @@ "^FAIL RegressionTests.ReDim_FiveToFour_SecondDimension_Raises9: .*Actual: -2147467259", "^FAIL RegressionTests.ReDim_ErrorDescription: .*Actual: \"Unspecified error\"" ] - } + }, + "issue": 2544 } diff --git a/bugs/redim-inverted-bounds-error/src/Settings b/bugs/filed/redim-inverted-bounds-error/src/Settings similarity index 100% rename from bugs/redim-inverted-bounds-error/src/Settings rename to bugs/filed/redim-inverted-bounds-error/src/Settings diff --git a/bugs/redim-inverted-bounds-error/src/Sources/RegressionTests.twin b/bugs/filed/redim-inverted-bounds-error/src/Sources/RegressionTests.twin similarity index 100% rename from bugs/redim-inverted-bounds-error/src/Sources/RegressionTests.twin rename to bugs/filed/redim-inverted-bounds-error/src/Sources/RegressionTests.twin diff --git a/bugs/filed/redim-inverted-bounds-error/src/Sources/Startup.twin b/bugs/filed/redim-inverted-bounds-error/src/Sources/Startup.twin new file mode 100644 index 000000000..be8f5c8bc --- /dev/null +++ b/bugs/filed/redim-inverted-bounds-error/src/Sources/Startup.twin @@ -0,0 +1,6 @@ +Module Startup + + Public Sub Main() + End Sub + +End Module diff --git a/bugs/redim-inverted-bounds-error/vb6/Module1.bas b/bugs/filed/redim-inverted-bounds-error/vb6/Module1.bas similarity index 100% rename from bugs/redim-inverted-bounds-error/vb6/Module1.bas rename to bugs/filed/redim-inverted-bounds-error/vb6/Module1.bas diff --git a/bugs/filed/redim-inverted-bounds-error/vb6/Probe.vbp b/bugs/filed/redim-inverted-bounds-error/vb6/Probe.vbp new file mode 100644 index 000000000..4d210b0e8 --- /dev/null +++ b/bugs/filed/redim-inverted-bounds-error/vb6/Probe.vbp @@ -0,0 +1,7 @@ +Type=Exe +Module=Module1; Module1.bas +Startup="Sub Main" +ExeName32="Probe.exe" +Name="Probe" +CompilationType=0 +OptimizationType=0 diff --git a/bugs/filed/round-null-negative-digits/REPORT.md b/bugs/filed/round-null-negative-digits/REPORT.md new file mode 100644 index 000000000..5ad6f3487 --- /dev/null +++ b/bugs/filed/round-null-negative-digits/REPORT.md @@ -0,0 +1,69 @@ +Filed as [twinbasic/twinbasic#2540](https://github.com/twinbasic/twinbasic/issues/2540). + +## `Round` of `Null` with a negative *numdecimalplaces* returns `Null`, where VB6 raises error 5 + +**Describe the bug** +`Round` raises error 5, *Invalid procedure call or argument*, for a negative *numdecimalplaces*: `Round(1.5, -1)` and `Round(Empty, -1)` raise it in twinBASIC and VB6. When the *expression* is `Null`, VB6 still raises 5, and twinBASIC returns `Null`. Observed in the compiler's test mode, 32-bit and 64-bit, and in a built exe. + +**To Reproduce** +Steps to reproduce the behavior: +1. Open `round-null-negative-digits.twinproj` (attached as `round-null-negative-digits.zip`). +2. Run its test cases, the module below. On BETA 1010 the three controls pass and `Round_NullNegativeDigits_Raises5` fails with `Actual: 0`. + +The module is meant to be adapted as a regression test. Each case asserts VB6's result, which the attached VB6 project (`round-null-negative-digits-vb6.zip`) prints. +```vb +' Regression tests: Round raises error 5 for a negative numdecimalplaces when the +' expression is Null, as it does for any other expression, and as VB6 does (the VB6 +' project beside this one prints it). tbrun --tests runs each case and prints PASS or FAIL. +[TestFixture] +Module RegressionTests + + ' Controls: a negative numdecimalplaces raises 5 for a number and for Empty, and + ' Round of Null with a valid numdecimalplaces returns Null. + [TestCase] + Public Sub Round_NumberNegativeDigits_Raises5() + Dim V As Variant + On Error Resume Next + V = Round(1.5, -1) + Assert.Strict.AreEqual 5&, Err.Number + End Sub + + [TestCase] + Public Sub Round_EmptyNegativeDigits_Raises5() + Dim V As Variant + On Error Resume Next + V = Round(Empty, -1) + Assert.Strict.AreEqual 5&, Err.Number + End Sub + + [TestCase] + Public Sub Round_NullValidDigits_ReturnsNull() + Dim V As Variant = Round(Null, 2) + Assert.Strict.IsTrue IsNull(V) + End Sub + + ' The defect: Null with a negative numdecimalplaces returns Null. + [TestCase] + Public Sub Round_NullNegativeDigits_Raises5() + Dim V As Variant + On Error Resume Next + V = Round(Null, -1) + Assert.Strict.AreEqual 5&, Err.Number + End Sub + +End Module +``` + +**Expected behavior** +Error 5 for a negative *numdecimalplaces* whatever the *expression*, as VB6 does. + +**Desktop:** + - OS: Windows 10 Pro 22H2 (build 19045) + - twinBASIC compiler version: BETA 1010 + +**Additional context** +Severity: low. An invalid argument goes unreported only while the value is `Null`. + +What does not reproduce it: a *numdecimalplaces* above 22 with `Null`, which returns `Null` in VB6 too (the limit of 22 applies only to a `Single`, `Double`, `Date` or string); a `Null` *numdecimalplaces*, which raises 94 in both. + + diff --git a/bugs/filed/round-null-negative-digits/repro.json b/bugs/filed/round-null-negative-digits/repro.json new file mode 100644 index 000000000..4a2da722d --- /dev/null +++ b/bugs/filed/round-null-negative-digits/repro.json @@ -0,0 +1,13 @@ +{ + "mode": "test", + "expect": { + "exit": 7, + "output": [ + "^PASS RegressionTests.Round_NumberNegativeDigits_Raises5$", + "^PASS RegressionTests.Round_EmptyNegativeDigits_Raises5$", + "^PASS RegressionTests.Round_NullValidDigits_ReturnsNull$", + "^FAIL RegressionTests.Round_NullNegativeDigits_Raises5: .*Actual: 0 " + ] + }, + "issue": 2540 +} diff --git a/bugs/filed/round-null-negative-digits/round-null-negative-digits.twinproj b/bugs/filed/round-null-negative-digits/round-null-negative-digits.twinproj new file mode 100644 index 000000000..f94d3ef5a Binary files /dev/null and b/bugs/filed/round-null-negative-digits/round-null-negative-digits.twinproj differ diff --git a/bugs/filed/round-null-negative-digits/src/Settings b/bugs/filed/round-null-negative-digits/src/Settings new file mode 100644 index 000000000..f08ea8e5f --- /dev/null +++ b/bugs/filed/round-null-negative-digits/src/Settings @@ -0,0 +1,75 @@ +{ + "configuration.inherits": "Defaults", + "project.appTitle": "RoundNullNegativeDigits", + "project.buildPath": "${SourcePath}\\Build\\${ProjectName}_${Architecture}.${FileExtension}", + "project.buildType": "Standard EXE", + "project.description": "Reproduces: x", + "project.exportPathIsV2": true, + "project.id": "{4FCE0798-9BD5-4F1D-BED0-666E98B31077}", + "project.name": "RoundNullNegativeDigits", + "project.optionExplicit": true, + "project.references": [ + { + "id": "{00020430-0000-0000-C000-000000000046}", + "lcid": 0, + "name": "OLE Automation", + "path32": "C:\\Windows\\SysWOW64\\stdole2.tlb", + "path64": "C:\\Windows\\System32\\stdole2.tlb", + "symbolId": "stdole", + "versionMajor": 2, + "versionMinor": 0 + }, + { + "id": "{F50B82D0-DCAB-43FE-9631-11959D4A4728}", + "isCompilerPackage": true, + "licence": "MIT", + "name": "[COMPILER PACKAGE] twinBASIC - VB Compatibility Package (Forms)", + "path32": "", + "path64": "", + "publisher": "TWINBASIC-COMPILER", + "symbolId": "VB", + "versionBuild": 0, + "versionMajor": 0, + "versionMinor": 0, + "versionRevision": 31 + }, + { + "id": "{C192FB39-64CA-4D9B-B477-A5502F48EFCC}", + "isCompilerPackage": true, + "licence": "MIT", + "name": "[COMPILER PACKAGE] twinBASIC - App global class object", + "path32": "", + "path64": "", + "publisher": "TWINBASIC-COMPILER", + "symbolId": "AppGlobalClassProject", + "versionBuild": 0, + "versionMajor": 1, + "versionMinor": 0, + "versionRevision": 0 + }, + { + "id": "{9847A10D-8987-44B2-8B87-EA77DEF7F1D9}", + "isCompilerPackage": true, + "licence": "MIT", + "name": "[COMPILER PACKAGE] Assert", + "path32": "", + "path64": "", + "publisher": "TWINBASIC-COMPILER", + "symbolId": "Assert", + "versionBuild": 0, + "versionMajor": 0, + "versionMinor": 0, + "versionRevision": 24 + } + ], + "project.settingsVersion": 1, + "project.startupObject": "Sub Main", + "project.warnings": { + "errors": [], + "hints": [], + "ignored": [], + "info": [], + "warnings": [] + }, + "runtime.useUnicodeStandardLibrary": true +} diff --git a/bugs/filed/round-null-negative-digits/src/Sources/RegressionTests.twin b/bugs/filed/round-null-negative-digits/src/Sources/RegressionTests.twin new file mode 100644 index 000000000..c2dbb797a --- /dev/null +++ b/bugs/filed/round-null-negative-digits/src/Sources/RegressionTests.twin @@ -0,0 +1,40 @@ +' Regression tests: Round raises error 5 for a negative numdecimalplaces when the +' expression is Null, as it does for any other expression, and as VB6 does (the VB6 +' project beside this one prints it). tbrun --tests runs each case and prints PASS or FAIL. +[TestFixture] +Module RegressionTests + + ' Controls: a negative numdecimalplaces raises 5 for a number and for Empty, and + ' Round of Null with a valid numdecimalplaces returns Null. + [TestCase] + Public Sub Round_NumberNegativeDigits_Raises5() + Dim V As Variant + On Error Resume Next + V = Round(1.5, -1) + Assert.Strict.AreEqual 5&, Err.Number + End Sub + + [TestCase] + Public Sub Round_EmptyNegativeDigits_Raises5() + Dim V As Variant + On Error Resume Next + V = Round(Empty, -1) + Assert.Strict.AreEqual 5&, Err.Number + End Sub + + [TestCase] + Public Sub Round_NullValidDigits_ReturnsNull() + Dim V As Variant = Round(Null, 2) + Assert.Strict.IsTrue IsNull(V) + End Sub + + ' The defect: Null with a negative numdecimalplaces returns Null. + [TestCase] + Public Sub Round_NullNegativeDigits_Raises5() + Dim V As Variant + On Error Resume Next + V = Round(Null, -1) + Assert.Strict.AreEqual 5&, Err.Number + End Sub + +End Module diff --git a/bugs/filed/round-null-negative-digits/src/Sources/Startup.twin b/bugs/filed/round-null-negative-digits/src/Sources/Startup.twin new file mode 100644 index 000000000..be8f5c8bc --- /dev/null +++ b/bugs/filed/round-null-negative-digits/src/Sources/Startup.twin @@ -0,0 +1,6 @@ +Module Startup + + Public Sub Main() + End Sub + +End Module diff --git a/bugs/filed/round-null-negative-digits/vb6/Module1.bas b/bugs/filed/round-null-negative-digits/vb6/Module1.bas new file mode 100644 index 000000000..be47aef74 --- /dev/null +++ b/bugs/filed/round-null-negative-digits/vb6/Module1.bas @@ -0,0 +1,21 @@ +Attribute VB_Name = "Module1" +Option Explicit + +' What VB6 does with Round of Null and a negative numdecimalplaces: error 5. +' Build and run it with: node scripts/bug_repro.mjs vb6 round-null-negative-digits + +Private Sub Report(ByVal Label As String, ByVal Result As Variant) + Print #9, Label & ": " & Err.Number & " " & Err.Description & " (" & TypeName(Result) & ")" + Err.Clear +End Sub + +Sub Main() + Dim V As Variant + Open App.Path & "\out.txt" For Output As #9 + On Error Resume Next + V = Empty: V = Round(1.5, -1): Report "Round_NumberNegativeDigits", V + V = Empty: V = Round(Empty, -1): Report "Round_EmptyNegativeDigits", V + V = Empty: V = Round(Null, 2): Report "Round_NullValidDigits", V + V = Empty: V = Round(Null, -1): Report "Round_NullNegativeDigits", V + Close #9 +End Sub diff --git a/bugs/filed/round-null-negative-digits/vb6/Probe.vbp b/bugs/filed/round-null-negative-digits/vb6/Probe.vbp new file mode 100644 index 000000000..4d210b0e8 --- /dev/null +++ b/bugs/filed/round-null-negative-digits/vb6/Probe.vbp @@ -0,0 +1,7 @@ +Type=Exe +Module=Module1; Module1.bas +Startup="Sub Main" +ExeName32="Probe.exe" +Name="Probe" +CompilationType=0 +OptimizationType=0 diff --git a/bugs/filed/string-null-character/REPORT.md b/bugs/filed/string-null-character/REPORT.md new file mode 100644 index 000000000..57710825b --- /dev/null +++ b/bugs/filed/string-null-character/REPORT.md @@ -0,0 +1,89 @@ +Filed as [twinbasic/twinbasic#2543](https://github.com/twinbasic/twinbasic/issues/2543). + +## `String` with a *character* of `Null` raises error 94, where VB6 returns `Null` + +**Describe the bug** +`String(3, Null)`, the **Variant** form of `String`, raises error 94, *Invalid use of Null*. VB6 raises no error and returns **Null** (`VarType` 1), as it does for `String(0, Null)`. `String$(3, Null)` raises 94 in both, and so does a *number* of **Null** in either form, so only the **Variant** form with a **Null** *character* differs. Observed in the compiler's test mode, 32-bit and 64-bit. + +**To Reproduce** +Steps to reproduce the behavior: +1. Open `string-null-character.twinproj` (attached as `string-null-character.zip`). +2. Run its test cases, the module below. On BETA 1010 the four controls pass (`String(3, "a")`, a **Null** *number* in `String` and `String$`, and `String$(3, Null)`, which raise 94 in VB6 too) and the three cases on `String` with a **Null** *character* fail: `Actual: 94`, or `runtime error 94: Invalid use of Null`. + +The module is meant to be adapted as a regression test. Each case asserts VB6's result, which the attached VB6 project (`string-null-character-vb6.zip`) prints: no error and `VarType` 1 (**Null**) for `String(3, Null)` and `String(0, Null)`, and 94 for the controls. +```vb +' Regression tests: the Variant form String, given a character of Null, returns Null +' with no error; String$ raises error 94. Expected results are VB6's (the VB6 project +' beside this one prints them). tbrun --tests runs each case and prints PASS or FAIL. +[TestFixture] +Module RegressionTests + + ' Controls: a letter, a Null number in either form, and String$ with a Null character. + [TestCase] + Public Sub String_Letter_Repeats() + Assert.Strict.AreEqual CVar("aaa"), String(3, "a") + End Sub + + [TestCase] + Public Sub String_NullNumber_Raises94() + On Error Resume Next + Dim R As Variant + R = String(Null, "a") + Assert.Strict.AreEqual 94&, Err.Number + End Sub + + [TestCase] + Public Sub StringDollar_NullNumber_Raises94() + On Error Resume Next + Dim R As Variant + R = String$(Null, "a") + Assert.Strict.AreEqual 94&, Err.Number + End Sub + + [TestCase] + Public Sub StringDollar_NullCharacter_Raises94() + On Error Resume Next + Dim R As Variant + R = String$(3, Null) + Assert.Strict.AreEqual 94&, Err.Number + End Sub + + ' The defect: String with a Null character returns Null. + [TestCase] + Public Sub String_NullCharacter_NoError() + On Error Resume Next + Dim R As Variant + R = String(3, Null) + Assert.Strict.AreEqual 0&, Err.Number + End Sub + + [TestCase] + Public Sub String_NullCharacter_ReturnsNull() + Dim R As Variant + R = String(3, Null) + Assert.Strict.AreEqual vbNull, VarType(R) + End Sub + + [TestCase] + Public Sub String_ZeroNullCharacter_ReturnsNull() + Dim R As Variant + R = String(0, Null) + Assert.Strict.AreEqual vbNull, VarType(R) + End Sub + +End Module +``` + +**Expected behavior** +`String(number, Null)` returns **Null** with no error, as VB6 does, and as the **Variant** forms of other string functions propagate **Null**; `String$` keeps raising 94. + +**Desktop:** + - OS: Windows 10 Pro 22H2 (build 19045) + - twinBASIC compiler version: BETA 1010 + +**Additional context** +Severity: low. Code that relies on **Null** propagating through `String`, as VB6 code reading database fields can, stops with error 94. + +What does not reproduce it: `String$` with a **Null** *character*, and a **Null** *number* in either form, all of which raise 94 in VB6 as well. + + diff --git a/bugs/string-null-character/repro.json b/bugs/filed/string-null-character/repro.json similarity index 96% rename from bugs/string-null-character/repro.json rename to bugs/filed/string-null-character/repro.json index 9064e3703..9ff7ddd9b 100644 --- a/bugs/string-null-character/repro.json +++ b/bugs/filed/string-null-character/repro.json @@ -11,5 +11,6 @@ "^FAIL RegressionTests.String_NullCharacter_ReturnsNull: [(]runtime error 94: ", "^FAIL RegressionTests.String_ZeroNullCharacter_ReturnsNull: [(]runtime error 94: " ] - } + }, + "issue": 2543 } diff --git a/bugs/string-null-character/src/Settings b/bugs/filed/string-null-character/src/Settings similarity index 100% rename from bugs/string-null-character/src/Settings rename to bugs/filed/string-null-character/src/Settings diff --git a/bugs/string-null-character/src/Sources/RegressionTests.twin b/bugs/filed/string-null-character/src/Sources/RegressionTests.twin similarity index 100% rename from bugs/string-null-character/src/Sources/RegressionTests.twin rename to bugs/filed/string-null-character/src/Sources/RegressionTests.twin diff --git a/bugs/filed/string-null-character/src/Sources/Startup.twin b/bugs/filed/string-null-character/src/Sources/Startup.twin new file mode 100644 index 000000000..be8f5c8bc --- /dev/null +++ b/bugs/filed/string-null-character/src/Sources/Startup.twin @@ -0,0 +1,6 @@ +Module Startup + + Public Sub Main() + End Sub + +End Module diff --git a/bugs/string-null-character/string-null-character.twinproj b/bugs/filed/string-null-character/string-null-character.twinproj similarity index 100% rename from bugs/string-null-character/string-null-character.twinproj rename to bugs/filed/string-null-character/string-null-character.twinproj diff --git a/bugs/string-null-character/vb6/Module1.bas b/bugs/filed/string-null-character/vb6/Module1.bas similarity index 100% rename from bugs/string-null-character/vb6/Module1.bas rename to bugs/filed/string-null-character/vb6/Module1.bas diff --git a/bugs/filed/string-null-character/vb6/Probe.vbp b/bugs/filed/string-null-character/vb6/Probe.vbp new file mode 100644 index 000000000..4d210b0e8 --- /dev/null +++ b/bugs/filed/string-null-character/vb6/Probe.vbp @@ -0,0 +1,7 @@ +Type=Exe +Module=Module1; Module1.bas +Startup="Sub Main" +ExeName32="Probe.exe" +Name="Probe" +CompilationType=0 +OptimizationType=0 diff --git a/bugs/filed/trig-above-2-to-63/REPORT.md b/bugs/filed/trig-above-2-to-63/REPORT.md new file mode 100644 index 000000000..5f1bd8604 --- /dev/null +++ b/bugs/filed/trig-above-2-to-63/REPORT.md @@ -0,0 +1,99 @@ +Filed as [twinbasic/twinbasic#2539](https://github.com/twinbasic/twinbasic/issues/2539). + +## `Cos` and `Sin` return their argument, and `Tan` raises an automation error, for a number of 2^63 or more in magnitude, where VB6 raises error 5 + +**Describe the bug** +For a number of 2^63 (9,223,372,036,854,775,808) or more in magnitude, twinBASIC's `Cos` and `Sin` return the number itself: `Cos(1E+20)` and `Sin(1E+20)` are `1E+20`, outside the range -1 to 1, with no error. `Tan` raises -2147348207 (`&H80021111`), *Automation error*. VB6 raises error 5, *Invalid procedure call or argument*, for all three, from 9.223372E+18 up in magnitude (just below 2^63). Observed in the compiler's test mode, 32-bit and 64-bit, and in a built exe. + +**To Reproduce** +Steps to reproduce the behavior: +1. Open `trig-above-2-to-63.twinproj` (attached as `trig-above-2-to-63.zip`). +2. Run its test cases, the module below. On BETA 1010 the three controls pass (each function of 2^62 gives a value with no error) and the four cases fail: `Cos` and `Sin` with `Actual: 0`, `Tan` with `Actual: -2147348207`. + +The module is meant to be adapted as a regression test. Each case asserts VB6's result, which the attached VB6 project (`trig-above-2-to-63-vb6.zip`) prints: error 5 for each, and also for 9.223372E+18 itself, where 9.223372E+18 - 2048 gives a value. +```vb +' Regression tests: Cos, Sin and Tan of a number of 2^63 or more in magnitude raise +' error 5, as VB6 does (the VB6 project beside this one prints it), rather than +' returning the argument (Cos, Sin) or raising an automation error (Tan). +' tbrun --tests runs each case and prints PASS or FAIL. +[TestFixture] +Module RegressionTests + + ' Controls: below 2^62 each returns a value in its range. + [TestCase] + Public Sub Cos_2To62_InRange() + Dim X As Double = 2 ^ 62 + Assert.Strict.IsTrue Abs(Cos(X)) <= 1 + End Sub + + [TestCase] + Public Sub Sin_2To62_InRange() + Dim X As Double = 2 ^ 62 + Assert.Strict.IsTrue Abs(Sin(X)) <= 1 + End Sub + + [TestCase] + Public Sub Tan_2To62_NoError() + Dim X As Double = 2 ^ 62 + Dim R As Double + On Error Resume Next + R = Tan(X) + Assert.Strict.AreEqual 0&, Err.Number + End Sub + + ' The defect: Cos and Sin return the argument itself, with no error. + [TestCase] + Public Sub Cos_2To63_Raises5() + Dim X As Double = 2 ^ 63 + Dim R As Double + On Error Resume Next + R = Cos(X) + Assert.Strict.AreEqual 5&, Err.Number + End Sub + + [TestCase] + Public Sub Cos_1E300_Raises5() + Dim X As Double = 1E+300 + Dim R As Double + On Error Resume Next + R = Cos(X) + Assert.Strict.AreEqual 5&, Err.Number + End Sub + + [TestCase] + Public Sub Sin_Minus2To63_Raises5() + Dim X As Double = -(2 ^ 63) + Dim R As Double + On Error Resume Next + R = Sin(X) + Assert.Strict.AreEqual 5&, Err.Number + End Sub + + ' The defect: Tan raises -2147348207 (&H80021111), "Automation error". + [TestCase] + Public Sub Tan_2To63_Raises5() + Dim X As Double = 2 ^ 63 + Dim R As Double + On Error Resume Next + R = Tan(X) + Assert.Strict.AreEqual 5&, Err.Number + End Sub + +End Module +``` + +**Expected behavior** +Error 5, *Invalid procedure call or argument*, for each of the three, as VB6 does. (A correctly reduced result would serve as well, but would be a change from VB6.) + +**Desktop:** + - OS: Windows 10 Pro 22H2 (build 19045) + - twinBASIC compiler version: BETA 1010 + +**Additional context** +Severity: medium for `Cos` and `Sin`: the wrong value is returned silently, and is outside the range the functions promise. `Tan` raises an error, but not VB's. + +Between 9.223372E+18 and 2^63 in magnitude, twinBASIC returns a value where VB6 already raises 5; the regression tests above do not assert that range. + +What does not reproduce it: any number below 9.223372E+18 in magnitude. + + diff --git a/bugs/filed/trig-above-2-to-63/repro.json b/bugs/filed/trig-above-2-to-63/repro.json new file mode 100644 index 000000000..1a6120936 --- /dev/null +++ b/bugs/filed/trig-above-2-to-63/repro.json @@ -0,0 +1,16 @@ +{ + "mode": "test", + "expect": { + "exit": 7, + "output": [ + "^PASS RegressionTests.Cos_2To62_InRange$", + "^PASS RegressionTests.Sin_2To62_InRange$", + "^PASS RegressionTests.Tan_2To62_NoError$", + "^FAIL RegressionTests.Cos_2To63_Raises5: .*Actual: 0 ", + "^FAIL RegressionTests.Cos_1E300_Raises5: .*Actual: 0 ", + "^FAIL RegressionTests.Sin_Minus2To63_Raises5: .*Actual: 0 ", + "^FAIL RegressionTests.Tan_2To63_Raises5: .*Actual: -2147348207 " + ] + }, + "issue": 2539 +} diff --git a/bugs/filed/trig-above-2-to-63/src/Settings b/bugs/filed/trig-above-2-to-63/src/Settings new file mode 100644 index 000000000..483f777fe --- /dev/null +++ b/bugs/filed/trig-above-2-to-63/src/Settings @@ -0,0 +1,75 @@ +{ + "configuration.inherits": "Defaults", + "project.appTitle": "TrigAbove2To63", + "project.buildPath": "${SourcePath}\\Build\\${ProjectName}_${Architecture}.${FileExtension}", + "project.buildType": "Standard EXE", + "project.description": "Reproduces: x", + "project.exportPathIsV2": true, + "project.id": "{B73711C8-01F6-4E81-B2B3-B8A757DB523E}", + "project.name": "TrigAbove2To63", + "project.optionExplicit": true, + "project.references": [ + { + "id": "{00020430-0000-0000-C000-000000000046}", + "lcid": 0, + "name": "OLE Automation", + "path32": "C:\\Windows\\SysWOW64\\stdole2.tlb", + "path64": "C:\\Windows\\System32\\stdole2.tlb", + "symbolId": "stdole", + "versionMajor": 2, + "versionMinor": 0 + }, + { + "id": "{F50B82D0-DCAB-43FE-9631-11959D4A4728}", + "isCompilerPackage": true, + "licence": "MIT", + "name": "[COMPILER PACKAGE] twinBASIC - VB Compatibility Package (Forms)", + "path32": "", + "path64": "", + "publisher": "TWINBASIC-COMPILER", + "symbolId": "VB", + "versionBuild": 0, + "versionMajor": 0, + "versionMinor": 0, + "versionRevision": 31 + }, + { + "id": "{C192FB39-64CA-4D9B-B477-A5502F48EFCC}", + "isCompilerPackage": true, + "licence": "MIT", + "name": "[COMPILER PACKAGE] twinBASIC - App global class object", + "path32": "", + "path64": "", + "publisher": "TWINBASIC-COMPILER", + "symbolId": "AppGlobalClassProject", + "versionBuild": 0, + "versionMajor": 1, + "versionMinor": 0, + "versionRevision": 0 + }, + { + "id": "{9847A10D-8987-44B2-8B87-EA77DEF7F1D9}", + "isCompilerPackage": true, + "licence": "MIT", + "name": "[COMPILER PACKAGE] Assert", + "path32": "", + "path64": "", + "publisher": "TWINBASIC-COMPILER", + "symbolId": "Assert", + "versionBuild": 0, + "versionMajor": 0, + "versionMinor": 0, + "versionRevision": 24 + } + ], + "project.settingsVersion": 1, + "project.startupObject": "Sub Main", + "project.warnings": { + "errors": [], + "hints": [], + "ignored": [], + "info": [], + "warnings": [] + }, + "runtime.useUnicodeStandardLibrary": true +} diff --git a/bugs/filed/trig-above-2-to-63/src/Sources/RegressionTests.twin b/bugs/filed/trig-above-2-to-63/src/Sources/RegressionTests.twin new file mode 100644 index 000000000..a4d1ed5eb --- /dev/null +++ b/bugs/filed/trig-above-2-to-63/src/Sources/RegressionTests.twin @@ -0,0 +1,68 @@ +' Regression tests: Cos, Sin and Tan of a number of 2^63 or more in magnitude raise +' error 5, as VB6 does (the VB6 project beside this one prints it), rather than +' returning the argument (Cos, Sin) or raising an automation error (Tan). +' tbrun --tests runs each case and prints PASS or FAIL. +[TestFixture] +Module RegressionTests + + ' Controls: below 2^62 each returns a value in its range. + [TestCase] + Public Sub Cos_2To62_InRange() + Dim X As Double = 2 ^ 62 + Assert.Strict.IsTrue Abs(Cos(X)) <= 1 + End Sub + + [TestCase] + Public Sub Sin_2To62_InRange() + Dim X As Double = 2 ^ 62 + Assert.Strict.IsTrue Abs(Sin(X)) <= 1 + End Sub + + [TestCase] + Public Sub Tan_2To62_NoError() + Dim X As Double = 2 ^ 62 + Dim R As Double + On Error Resume Next + R = Tan(X) + Assert.Strict.AreEqual 0&, Err.Number + End Sub + + ' The defect: Cos and Sin return the argument itself, with no error. + [TestCase] + Public Sub Cos_2To63_Raises5() + Dim X As Double = 2 ^ 63 + Dim R As Double + On Error Resume Next + R = Cos(X) + Assert.Strict.AreEqual 5&, Err.Number + End Sub + + [TestCase] + Public Sub Cos_1E300_Raises5() + Dim X As Double = 1E+300 + Dim R As Double + On Error Resume Next + R = Cos(X) + Assert.Strict.AreEqual 5&, Err.Number + End Sub + + [TestCase] + Public Sub Sin_Minus2To63_Raises5() + Dim X As Double = -(2 ^ 63) + Dim R As Double + On Error Resume Next + R = Sin(X) + Assert.Strict.AreEqual 5&, Err.Number + End Sub + + ' The defect: Tan raises -2147348207 (&H80021111), "Automation error". + [TestCase] + Public Sub Tan_2To63_Raises5() + Dim X As Double = 2 ^ 63 + Dim R As Double + On Error Resume Next + R = Tan(X) + Assert.Strict.AreEqual 5&, Err.Number + End Sub + +End Module diff --git a/bugs/filed/trig-above-2-to-63/src/Sources/Startup.twin b/bugs/filed/trig-above-2-to-63/src/Sources/Startup.twin new file mode 100644 index 000000000..be8f5c8bc --- /dev/null +++ b/bugs/filed/trig-above-2-to-63/src/Sources/Startup.twin @@ -0,0 +1,6 @@ +Module Startup + + Public Sub Main() + End Sub + +End Module diff --git a/bugs/filed/trig-above-2-to-63/trig-above-2-to-63.twinproj b/bugs/filed/trig-above-2-to-63/trig-above-2-to-63.twinproj new file mode 100644 index 000000000..8f57fcaae Binary files /dev/null and b/bugs/filed/trig-above-2-to-63/trig-above-2-to-63.twinproj differ diff --git a/bugs/filed/trig-above-2-to-63/vb6/Module1.bas b/bugs/filed/trig-above-2-to-63/vb6/Module1.bas new file mode 100644 index 000000000..bfc7addc1 --- /dev/null +++ b/bugs/filed/trig-above-2-to-63/vb6/Module1.bas @@ -0,0 +1,27 @@ +Attribute VB_Name = "Module1" +Option Explicit + +' What VB6 does with Cos, Sin and Tan of a number of 2^63 or more in magnitude: +' error 5, from 9.223372E+18 up. +' Build and run it with: node scripts/bug_repro.mjs vb6 trig-above-2-to-63 + +Private Sub Report(ByVal Label As String, ByVal Result As Variant) + Print #9, Label & ": " & Err.Number & " " & Err.Description & " (" & Result & ")" + Err.Clear +End Sub + +Sub Main() + Dim X As Double, R As Variant + Open App.Path & "\out.txt" For Output As #9 + On Error Resume Next + X = 2 ^ 62: R = Empty: R = Cos(X): Report "Cos_2To62", R + X = 2 ^ 62: R = Empty: R = Sin(X): Report "Sin_2To62", R + X = 2 ^ 62: R = Empty: R = Tan(X): Report "Tan_2To62", R + X = 2 ^ 63: R = Empty: R = Cos(X): Report "Cos_2To63", R + X = 1E+300: R = Empty: R = Cos(X): Report "Cos_1E300", R + X = -(2 ^ 63): R = Empty: R = Sin(X): Report "Sin_Minus2To63", R + X = 2 ^ 63: R = Empty: R = Tan(X): Report "Tan_2To63", R + X = 9.223372E+18 - 2048: R = Empty: R = Cos(X): Report "Cos_JustBelow9.223372E18", R + X = 9.223372E+18: R = Empty: R = Cos(X): Report "Cos_9.223372E18", R + Close #9 +End Sub diff --git a/bugs/filed/trig-above-2-to-63/vb6/Probe.vbp b/bugs/filed/trig-above-2-to-63/vb6/Probe.vbp new file mode 100644 index 000000000..4d210b0e8 --- /dev/null +++ b/bugs/filed/trig-above-2-to-63/vb6/Probe.vbp @@ -0,0 +1,7 @@ +Type=Exe +Module=Module1; Module1.bas +Startup="Sub Main" +ExeName32="Probe.exe" +Name="Probe" +CompilationType=0 +OptimizationType=0 diff --git a/bugs/filed/val-suffix-overflow-no-error/REPORT.md b/bugs/filed/val-suffix-overflow-no-error/REPORT.md new file mode 100644 index 000000000..d1ba5fa0b --- /dev/null +++ b/bugs/filed/val-suffix-overflow-no-error/REPORT.md @@ -0,0 +1,88 @@ +Filed as [twinbasic/twinbasic#2542](https://github.com/twinbasic/twinbasic/issues/2542). + +## `Val` raises no error for a number followed by `%`, `&` or `@` that does not fit that type, where VB6 raises error 6 + +**Describe the bug** +`Val` reads a type-declaration character after a number: `Val("50%")` is 50, and `Val("50.5%")` raises error 13, *Type mismatch*, in both twinBASIC and VB6, because a fraction is not an **Integer**. When the number is whole but outside the range of the type the character names, VB6 raises error 6, *Overflow*: `Val("32768%")`, `Val("-32769%")`, `Val("2147483648&")`, `Val("1e15@")`. twinBASIC raises nothing and returns the number as a **Double** (`Val("70000%")` is 70000). Observed in the compiler's test mode, 32-bit and 64-bit, and in a built exe. + +**To Reproduce** +Steps to reproduce the behavior: +1. Open `val-suffix-overflow-no-error.twinproj` (attached as `val-suffix-overflow-no-error.zip`). +2. Run its test cases, the module below. On BETA 1010 the three controls pass (the largest **Integer** and **Long** with their characters, and `"50.5%"`, which raises 13) and the four cases past a type's range fail with `Actual: 0`. + +The module is meant to be adapted as a regression test. Each case asserts VB6's result, which the attached VB6 project (`val-suffix-overflow-no-error-vb6.zip`) prints: error 6 for the four, and 32767, 2147483647 and error 13 for the controls. +```vb +' Regression tests: Val of a number followed by a type-declaration character raises +' error 6 when the number does not fit that type. Expected results are VB6's (the VB6 +' project beside this one prints them). tbrun --tests runs each case and prints PASS or FAIL. +[TestFixture] +Module RegressionTests + + ' Controls: numbers that fit their type, and a fraction with % (error 13 in both). + [TestCase] + Public Sub Val_LargestInteger_Accepted() + Assert.Strict.AreEqual 32767#, Val("32767%") + End Sub + + [TestCase] + Public Sub Val_LargestLong_Accepted() + Assert.Strict.AreEqual 2147483647#, Val("2147483647&") + End Sub + + [TestCase] + Public Sub Val_FractionInteger_Raises13() + Dim R As Double + On Error Resume Next + R = Val("50.5%") + Assert.Strict.AreEqual 13&, Err.Number + End Sub + + ' The defect: each number is outside the range of its type, and twinBASIC returns it. + [TestCase] + Public Sub Val_IntegerPastLargest_Raises6() + Dim R As Double + On Error Resume Next + R = Val("32768%") + Assert.Strict.AreEqual 6&, Err.Number + End Sub + + [TestCase] + Public Sub Val_IntegerPastSmallest_Raises6() + Dim R As Double + On Error Resume Next + R = Val("-32769%") + Assert.Strict.AreEqual 6&, Err.Number + End Sub + + [TestCase] + Public Sub Val_LongPastLargest_Raises6() + Dim R As Double + On Error Resume Next + R = Val("2147483648&") + Assert.Strict.AreEqual 6&, Err.Number + End Sub + + [TestCase] + Public Sub Val_CurrencyPastLargest_Raises6() + Dim R As Double + On Error Resume Next + R = Val("1e15@") + Assert.Strict.AreEqual 6&, Err.Number + End Sub + +End Module +``` + +**Expected behavior** +Error 6, *Overflow*, for a number outside the range of the type its character names, as VB6 does, and as `Val` already raises error 13 for a fraction with `%` or `&`. + +**Desktop:** + - OS: Windows 10 Pro 22H2 (build 19045) + - twinBASIC compiler version: BETA 1010 + +**Additional context** +Severity: low. A program that counts on the error to reject text such as `"70000%"` accepts it. + +What does not reproduce it: a number that fits its type; `!` and `#` (`Val("1e39!")` raises nothing in VB6 either, and `Val("1e400#")` raises 6 in both); a number past the **Double** range with no character, which raises 6 in both. + + diff --git a/bugs/filed/val-suffix-overflow-no-error/repro.json b/bugs/filed/val-suffix-overflow-no-error/repro.json new file mode 100644 index 000000000..e89c15bcd --- /dev/null +++ b/bugs/filed/val-suffix-overflow-no-error/repro.json @@ -0,0 +1,16 @@ +{ + "mode": "test", + "expect": { + "exit": 7, + "output": [ + "^PASS RegressionTests.Val_LargestInteger_Accepted$", + "^PASS RegressionTests.Val_LargestLong_Accepted$", + "^PASS RegressionTests.Val_FractionInteger_Raises13$", + "^FAIL RegressionTests.Val_IntegerPastLargest_Raises6: .*Actual: 0 ", + "^FAIL RegressionTests.Val_IntegerPastSmallest_Raises6: .*Actual: 0 ", + "^FAIL RegressionTests.Val_LongPastLargest_Raises6: .*Actual: 0 ", + "^FAIL RegressionTests.Val_CurrencyPastLargest_Raises6: .*Actual: 0 " + ] + }, + "issue": 2542 +} diff --git a/bugs/filed/val-suffix-overflow-no-error/src/Settings b/bugs/filed/val-suffix-overflow-no-error/src/Settings new file mode 100644 index 000000000..fd1330785 --- /dev/null +++ b/bugs/filed/val-suffix-overflow-no-error/src/Settings @@ -0,0 +1,75 @@ +{ + "configuration.inherits": "Defaults", + "project.appTitle": "ValSuffixOverflowNoError", + "project.buildPath": "${SourcePath}\\Build\\${ProjectName}_${Architecture}.${FileExtension}", + "project.buildType": "Standard EXE", + "project.description": "Reproduces: Val with a type-declaration character raises no error when the number does not fit the type, where VB6 raises error 6", + "project.exportPathIsV2": true, + "project.id": "{FBBAB053-EB70-47F7-B4F8-D8FCBC50E172}", + "project.name": "ValSuffixOverflowNoError", + "project.optionExplicit": true, + "project.references": [ + { + "id": "{00020430-0000-0000-C000-000000000046}", + "lcid": 0, + "name": "OLE Automation", + "path32": "C:\\Windows\\SysWOW64\\stdole2.tlb", + "path64": "C:\\Windows\\System32\\stdole2.tlb", + "symbolId": "stdole", + "versionMajor": 2, + "versionMinor": 0 + }, + { + "id": "{F50B82D0-DCAB-43FE-9631-11959D4A4728}", + "isCompilerPackage": true, + "licence": "MIT", + "name": "[COMPILER PACKAGE] twinBASIC - VB Compatibility Package (Forms)", + "path32": "", + "path64": "", + "publisher": "TWINBASIC-COMPILER", + "symbolId": "VB", + "versionBuild": 0, + "versionMajor": 0, + "versionMinor": 0, + "versionRevision": 31 + }, + { + "id": "{C192FB39-64CA-4D9B-B477-A5502F48EFCC}", + "isCompilerPackage": true, + "licence": "MIT", + "name": "[COMPILER PACKAGE] twinBASIC - App global class object", + "path32": "", + "path64": "", + "publisher": "TWINBASIC-COMPILER", + "symbolId": "AppGlobalClassProject", + "versionBuild": 0, + "versionMajor": 1, + "versionMinor": 0, + "versionRevision": 0 + }, + { + "id": "{9847A10D-8987-44B2-8B87-EA77DEF7F1D9}", + "isCompilerPackage": true, + "licence": "MIT", + "name": "[COMPILER PACKAGE] Assert", + "path32": "", + "path64": "", + "publisher": "TWINBASIC-COMPILER", + "symbolId": "Assert", + "versionBuild": 0, + "versionMajor": 0, + "versionMinor": 0, + "versionRevision": 24 + } + ], + "project.settingsVersion": 1, + "project.startupObject": "Sub Main", + "project.warnings": { + "errors": [], + "hints": [], + "ignored": [], + "info": [], + "warnings": [] + }, + "runtime.useUnicodeStandardLibrary": true +} diff --git a/bugs/filed/val-suffix-overflow-no-error/src/Sources/RegressionTests.twin b/bugs/filed/val-suffix-overflow-no-error/src/Sources/RegressionTests.twin new file mode 100644 index 000000000..fa4b0507b --- /dev/null +++ b/bugs/filed/val-suffix-overflow-no-error/src/Sources/RegressionTests.twin @@ -0,0 +1,59 @@ +' Regression tests: Val of a number followed by a type-declaration character raises +' error 6 when the number does not fit that type. Expected results are VB6's (the VB6 +' project beside this one prints them). tbrun --tests runs each case and prints PASS or FAIL. +[TestFixture] +Module RegressionTests + + ' Controls: numbers that fit their type, and a fraction with % (error 13 in both). + [TestCase] + Public Sub Val_LargestInteger_Accepted() + Assert.Strict.AreEqual 32767#, Val("32767%") + End Sub + + [TestCase] + Public Sub Val_LargestLong_Accepted() + Assert.Strict.AreEqual 2147483647#, Val("2147483647&") + End Sub + + [TestCase] + Public Sub Val_FractionInteger_Raises13() + Dim R As Double + On Error Resume Next + R = Val("50.5%") + Assert.Strict.AreEqual 13&, Err.Number + End Sub + + ' The defect: each number is outside the range of its type, and twinBASIC returns it. + [TestCase] + Public Sub Val_IntegerPastLargest_Raises6() + Dim R As Double + On Error Resume Next + R = Val("32768%") + Assert.Strict.AreEqual 6&, Err.Number + End Sub + + [TestCase] + Public Sub Val_IntegerPastSmallest_Raises6() + Dim R As Double + On Error Resume Next + R = Val("-32769%") + Assert.Strict.AreEqual 6&, Err.Number + End Sub + + [TestCase] + Public Sub Val_LongPastLargest_Raises6() + Dim R As Double + On Error Resume Next + R = Val("2147483648&") + Assert.Strict.AreEqual 6&, Err.Number + End Sub + + [TestCase] + Public Sub Val_CurrencyPastLargest_Raises6() + Dim R As Double + On Error Resume Next + R = Val("1e15@") + Assert.Strict.AreEqual 6&, Err.Number + End Sub + +End Module diff --git a/bugs/filed/val-suffix-overflow-no-error/src/Sources/Startup.twin b/bugs/filed/val-suffix-overflow-no-error/src/Sources/Startup.twin new file mode 100644 index 000000000..be8f5c8bc --- /dev/null +++ b/bugs/filed/val-suffix-overflow-no-error/src/Sources/Startup.twin @@ -0,0 +1,6 @@ +Module Startup + + Public Sub Main() + End Sub + +End Module diff --git a/bugs/filed/val-suffix-overflow-no-error/val-suffix-overflow-no-error.twinproj b/bugs/filed/val-suffix-overflow-no-error/val-suffix-overflow-no-error.twinproj new file mode 100644 index 000000000..b83da0c8c Binary files /dev/null and b/bugs/filed/val-suffix-overflow-no-error/val-suffix-overflow-no-error.twinproj differ diff --git a/bugs/filed/val-suffix-overflow-no-error/vb6/Module1.bas b/bugs/filed/val-suffix-overflow-no-error/vb6/Module1.bas new file mode 100644 index 000000000..7974cf778 --- /dev/null +++ b/bugs/filed/val-suffix-overflow-no-error/vb6/Module1.bas @@ -0,0 +1,25 @@ +Attribute VB_Name = "Module1" +Option Explicit + +' What VB6 does with Val of a number followed by a type-declaration character that the +' number does not fit: error 6. +' Build and run it with: node scripts/bug_repro.mjs vb6 val-suffix-overflow-no-error + +Private Sub Report(ByVal Label As String, ByVal Result As Variant) + Print #9, Label & ": " & Err.Number & " " & Err.Description & " (" & Result & ")" + Err.Clear +End Sub + +Sub Main() + Dim R As Variant + Open App.Path & "\out.txt" For Output As #9 + On Error Resume Next + R = Empty: R = Val("32767%"): Report "Val_LargestInteger", R + R = Empty: R = Val("2147483647&"): Report "Val_LargestLong", R + R = Empty: R = Val("50.5%"): Report "Val_FractionInteger", R + R = Empty: R = Val("32768%"): Report "Val_IntegerPastLargest", R + R = Empty: R = Val("-32769%"): Report "Val_IntegerPastSmallest", R + R = Empty: R = Val("2147483648&"): Report "Val_LongPastLargest", R + R = Empty: R = Val("1e15@"): Report "Val_CurrencyPastLargest", R + Close #9 +End Sub diff --git a/bugs/filed/val-suffix-overflow-no-error/vb6/Probe.vbp b/bugs/filed/val-suffix-overflow-no-error/vb6/Probe.vbp new file mode 100644 index 000000000..4d210b0e8 --- /dev/null +++ b/bugs/filed/val-suffix-overflow-no-error/vb6/Probe.vbp @@ -0,0 +1,7 @@ +Type=Exe +Module=Module1; Module1.bas +Startup="Sub Main" +ExeName32="Probe.exe" +Name="Probe" +CompilationType=0 +OptimizationType=0 diff --git a/bugs/latebound-hresult-mapping/latebound-hresult-mapping.twinproj b/bugs/latebound-hresult-mapping/latebound-hresult-mapping.twinproj index a42335059..c9bbc0a42 100644 Binary files a/bugs/latebound-hresult-mapping/latebound-hresult-mapping.twinproj and b/bugs/latebound-hresult-mapping/latebound-hresult-mapping.twinproj differ diff --git a/bugs/latebound-hresult-mapping/repro.json b/bugs/latebound-hresult-mapping/repro.json index 22c85af28..7dd9fa72b 100644 --- a/bugs/latebound-hresult-mapping/repro.json +++ b/bugs/latebound-hresult-mapping/repro.json @@ -19,7 +19,9 @@ "^FAIL LateBoundErrorTests\\.ArrayIsLocked_Raises10: .*Actual: -2147352563 ", "^FAIL LateBoundErrorTests\\.NoInterface_Raises430: .*Actual: -2147467262 ", "^FAIL LateBoundErrorTests\\.AccessDenied_Raises70: .*Actual: -2147024891 ", - "^FAIL LateBoundErrorTests\\.Abort_Raises287: .*Actual: -2147467260 " + "^FAIL LateBoundErrorTests\\.Abort_Raises287: .*Actual: -2147467260 ", + "^FAIL LateBoundErrorTests\\.NotACollection_Raises451: .*Actual: -2147352559 ", + "^FAIL LateBoundErrorTests\\.NotACollection_Dictionary_Raises451: .*Actual: -2147352559 " ] } } diff --git a/bugs/latebound-hresult-mapping/src/Sources/LateBoundErrorTests.twin b/bugs/latebound-hresult-mapping/src/Sources/LateBoundErrorTests.twin index ca3356d4d..887ae159a 100644 --- a/bugs/latebound-hresult-mapping/src/Sources/LateBoundErrorTests.twin +++ b/bugs/latebound-hresult-mapping/src/Sources/LateBoundErrorTests.twin @@ -227,4 +227,21 @@ Module LateBoundErrorTests Assert.Strict.AreEqual 287, Err.Number ' VB6: 287, Application-defined or object-defined error End Sub + [TestCase] + Public Sub NotACollection_Raises451() + Dim o As Object = New HResultObject + On Error Resume Next + o.Hr80020011 ' DISP_E_NOTACOLLECTION + Assert.Strict.AreEqual 451, Err.Number ' VB6: 451, Property let procedure not defined and property get procedure did not return an object + End Sub + + [TestCase] + Public Sub NotACollection_Dictionary_Raises451() + Dim d As Object = CreateObject("Scripting.Dictionary") + Dim x As Variant + On Error Resume Next + x = d.Count(1) ' Count takes no index + Assert.Strict.AreEqual 451, Err.Number ' VB6: 451 + End Sub + End Module diff --git a/bugs/latebound-hresult-mapping/vb6/Module1.bas b/bugs/latebound-hresult-mapping/vb6/Module1.bas index 5703b6169..32ad51f52 100644 --- a/bugs/latebound-hresult-mapping/vb6/Module1.bas +++ b/bugs/latebound-hresult-mapping/vb6/Module1.bas @@ -1,247 +1,252 @@ -Attribute VB_Name = "Module1" -Option Explicit - -' The VB6 side of latebound-hresult-mapping: the error number VB6 raises when a -' late-bound call fails. Build and run it with: -' node scripts/bug_repro.mjs vb6 latebound-hresult-mapping -' A lightweight COM object whose IDispatch::Invoke returns a chosen HRESULT: the -' name Cn fails with code n of the table in InitCodes. -' Memory block gObj: element 0 = pointer to the vtable, element 1 = reference count. - -Private Declare Sub CopyMemory Lib "kernel32.dll" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long) -Private Declare Function lstrlenW Lib "kernel32.dll" (ByVal lpString As Long) As Long -Private Declare Function SysAllocString Lib "oleaut32.dll" (ByVal psz As Long) As Long - -Private Const NCODES As Long = 24 -Private gVtbl(0 To 6) As Long -Private gObj(0 To 1) As Long -Private gCodes(1 To NCODES) As Long -Private gNames(1 To NCODES) As String -Private gCalls(0 To NCODES) As Long -Private gFlags(0 To NCODES) As Long -Private gArgs(0 To NCODES) As Long -Private gHasExc(0 To NCODES) As Long -Private gGetIds As Long - -Sub Main() - On Error GoTo Fail - Open App.Path & "\out.txt" For Output As #9 - Cases - Close #9 - Exit Sub -Fail: - On Error Resume Next - Print #9, "unhandled error " & Err.Number & ": " & Err.Description - Close #9 -End Sub - -Private Sub SetCode(ByVal n As Long, ByVal code As Long, ByVal nm As String) - gCodes(n) = code - gNames(n) = nm -End Sub - -Private Sub InitCodes() - SetCode 1, &H80004001, "E_NOTIMPL" - SetCode 2, &H8007000E, "E_OUTOFMEMORY" - SetCode 3, &H80070057, "E_INVALIDARG" - SetCode 4, &H8002000E, "DISP_E_BADPARAMCOUNT" - SetCode 5, &H8002000F, "DISP_E_PARAMNOTOPTIONAL" - SetCode 6, &H8002000A, "DISP_E_OVERFLOW" - SetCode 7, &H80020005, "DISP_E_TYPEMISMATCH" - SetCode 8, &H8002000B, "DISP_E_BADINDEX" - SetCode 9, &H80020012, "DISP_E_DIVBYZERO" - SetCode 10, &H80020007, "DISP_E_NONAMEDARGS" - SetCode 11, &H80020008, "DISP_E_BADVARTYPE" - SetCode 12, &H80020004, "DISP_E_PARAMNOTFOUND" - SetCode 13, &H8002000D, "DISP_E_ARRAYISLOCKED" - SetCode 14, &H80004002, "E_NOINTERFACE" - SetCode 15, &H80070005, "E_ACCESSDENIED" - SetCode 16, &H80004003, "E_POINTER" - SetCode 17, &H80004004, "E_ABORT" - SetCode 18, &H80004005, "E_FAIL" - SetCode 19, &H8000FFFF, "E_UNEXPECTED" - SetCode 20, &H80020003, "DISP_E_MEMBERNOTFOUND" - SetCode 21, &H800A01A8, "800A01A8" - SetCode 22, &H8007000D, "HRESULT_FROM_WIN32(ERROR_INVALID_DATA) 8007000D" - SetCode 23, &H80020009, "DISP_E_EXCEPTION (excepinfo scode 80041234, wCode 0, description given)" - SetCode 24, &H80020009, "DISP_E_EXCEPTION (excepinfo scode 0, wCode 1234, description given)" -End Sub - -Private Function Fn(ByVal p As Long) As Long - Fn = p -End Function - -Private Sub InitObject() - gVtbl(0) = Fn(AddressOf Obj_QueryInterface) - gVtbl(1) = Fn(AddressOf Obj_AddRef) - gVtbl(2) = Fn(AddressOf Obj_Release) - gVtbl(3) = Fn(AddressOf Obj_GetTypeInfoCount) - gVtbl(4) = Fn(AddressOf Obj_GetTypeInfo) - gVtbl(5) = Fn(AddressOf Obj_GetIDsOfNames) - gVtbl(6) = Fn(AddressOf Obj_Invoke) - gObj(0) = VarPtr(gVtbl(0)) - gObj(1) = 1 -End Sub - -Public Function Obj_QueryInterface(ByVal pThis As Long, ByVal riid As Long, ByVal ppv As Long) As Long - Dim d1 As Long, d2 As Integer, d3 As Integer, b(0 To 7) As Byte - Dim isUnk As Boolean, isDisp As Boolean - If riid = 0 Or ppv = 0 Then - Obj_QueryInterface = &H80004003 - Exit Function - End If - CopyMemory d1, ByVal riid, 4 - CopyMemory d2, ByVal (riid + 4), 2 - CopyMemory d3, ByVal (riid + 6), 2 - CopyMemory b(0), ByVal (riid + 8), 8 - isUnk = (d1 = 0 And d2 = 0 And d3 = 0 And b(0) = &HC0 And b(7) = &H46 And b(1) = 0 And b(6) = 0) - isDisp = (d1 = &H20400 And d2 = 0 And d3 = 0 And b(0) = &HC0 And b(7) = &H46 And b(1) = 0 And b(6) = 0) - If isUnk Or isDisp Then - gObj(1) = gObj(1) + 1 - CopyMemory ByVal ppv, pThis, 4 - Obj_QueryInterface = 0 - Else - CopyMemory ByVal ppv, 0&, 4 - Obj_QueryInterface = &H80004002 - End If -End Function - -Public Function Obj_AddRef(ByVal pThis As Long) As Long - gObj(1) = gObj(1) + 1 - Obj_AddRef = gObj(1) -End Function - -Public Function Obj_Release(ByVal pThis As Long) As Long - gObj(1) = gObj(1) - 1 - Obj_Release = gObj(1) -End Function - -Public Function Obj_GetTypeInfoCount(ByVal pThis As Long, ByVal pctinfo As Long) As Long - If pctinfo <> 0 Then CopyMemory ByVal pctinfo, 0&, 4 - Obj_GetTypeInfoCount = 0 -End Function - -Public Function Obj_GetTypeInfo(ByVal pThis As Long, ByVal iTInfo As Long, ByVal lcid As Long, ByVal ppTInfo As Long) As Long - Obj_GetTypeInfo = &H80004001 -End Function - -Public Function Obj_GetIDsOfNames(ByVal pThis As Long, ByVal riid As Long, ByVal rgszNames As Long, ByVal cNames As Long, ByVal lcid As Long, ByVal rgDispId As Long) As Long - Dim pName As Long, n As Long, s As String, num As Long - gGetIds = gGetIds + 1 - CopyMemory pName, ByVal rgszNames, 4 - n = lstrlenW(pName) - s = String$(n, vbNullChar) - If n > 0 Then CopyMemory ByVal StrPtr(s), ByVal pName, n * 2 - If UCase$(Left$(s, 1)) = "C" And IsNumeric(Mid$(s, 2)) Then - num = CLng(Mid$(s, 2)) - CopyMemory ByVal rgDispId, num, 4 - Obj_GetIDsOfNames = 0 - Else - num = -1 - CopyMemory ByVal rgDispId, num, 4 - Obj_GetIDsOfNames = &H80020006 - End If -End Function - -Public Function Obj_Invoke(ByVal pThis As Long, ByVal dispIdMember As Long, ByVal riid As Long, ByVal lcid As Long, ByVal wFlags As Long, ByVal pDispParams As Long, ByVal pVarResult As Long, ByVal pExcepInfo As Long, ByVal puArgErr As Long) As Long - Dim cArgs As Long, bstr As Long, sc As Long, wc As Integer - If dispIdMember >= 1 And dispIdMember <= NCODES Then - gCalls(dispIdMember) = gCalls(dispIdMember) + 1 - gFlags(dispIdMember) = wFlags And &HFFFF& - If pDispParams <> 0 Then CopyMemory cArgs, ByVal (pDispParams + 8), 4 - gArgs(dispIdMember) = cArgs - gHasExc(dispIdMember) = IIf(pExcepInfo <> 0, 1, 0) - If gCodes(dispIdMember) = &H80020009 And pExcepInfo <> 0 Then - If dispIdMember = 23 Then - wc = 0: sc = &H80041234 - Else - wc = 1234: sc = 0 - End If - ' the caller need not have zeroed it: fill all 32 bytes - Dim z(0 To 31) As Byte - CopyMemory ByVal pExcepInfo, z(0), 32 - CopyMemory ByVal pExcepInfo, wc, 2 - bstr = SysAllocString(StrPtr("Source from excepinfo")) - CopyMemory ByVal (pExcepInfo + 4), bstr, 4 - bstr = SysAllocString(StrPtr("Description from excepinfo")) - CopyMemory ByVal (pExcepInfo + 8), bstr, 4 - CopyMemory ByVal (pExcepInfo + 28), sc, 4 - End If - Obj_Invoke = gCodes(dispIdMember) - Else - Obj_Invoke = &H80020003 - End If -End Function - -Private Sub Rec(ByVal n As Long, ByVal way As String, ByVal callsBefore As Long) - Dim num As Long, desc As String, src As String - num = Err.Number: desc = Err.Description: src = Err.Source - Print #9, gNames(n) & " (" & Hex$(gCodes(n)) & ") " & way & " -> " & num & " [" & desc & "] invokeCalls=" & (gCalls(n) - callsBefore) - Err.Clear -End Sub - -Private Sub Show(ByVal what As String) - Print #9, what & " -> " & Err.Number & " [" & Err.Description & "]" - Err.Clear -End Sub - -Sub Cases() - Dim o As Object, p As Long, n As Long, before As Long - InitCodes - InitObject - p = VarPtr(gObj(0)) - gObj(1) = gObj(1) + 1 - CopyMemory o, p, 4 - Print #9, "object ptr ok: " & (Not (o Is Nothing)) - - For n = 1 To NCODES - On Error Resume Next - Err.Clear - before = gCalls(n) - Select Case n - Case 1: o.C1 - Case 2: o.C2 - Case 3: o.C3 - Case 4: o.C4 - Case 5: o.C5 - Case 6: o.C6 - Case 7: o.C7 - Case 8: o.C8 - Case 9: o.C9 - Case 10: o.C10 - Case 11: o.C11 - Case 12: o.C12 - Case 13: o.C13 - Case 14: o.C14 - Case 15: o.C15 - Case 16: o.C16 - Case 17: o.C17 - Case 18: o.C18 - Case 19: o.C19 - Case 20: o.C20 - Case 21: o.C21 - Case 22: o.C22 - Case 23: o.C23 - Case 24: o.C24 - End Select - Rec n, "statement", before - On Error GoTo 0 - Next - - CopyMemory o, 0&, 4 - - ' Ordinary objects: a name they do not have, and a wrong number of arguments. - Dim w As Object, c As Object, x As Variant - Set w = New Widget - Set c = New Collection - On Error Resume Next - w.Nope - Show "Widget.Nope" - c.Nope - Show "Collection.Nope" - CallByName w, "Nope", VbMethod - Show "CallByName Widget, Nope" - x = c.Item(1, 2) - Show "x = Collection.Item(1, 2)" - On Error GoTo 0 -End Sub +Attribute VB_Name = "Module1" +Option Explicit + +' The VB6 side of latebound-hresult-mapping: the error number VB6 raises when a +' late-bound call fails. Build and run it with: +' node scripts/bug_repro.mjs vb6 latebound-hresult-mapping +' A lightweight COM object whose IDispatch::Invoke returns a chosen HRESULT: the +' name Cn fails with code n of the table in InitCodes. +' Memory block gObj: element 0 = pointer to the vtable, element 1 = reference count. + +Private Declare Sub CopyMemory Lib "kernel32.dll" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long) +Private Declare Function lstrlenW Lib "kernel32.dll" (ByVal lpString As Long) As Long +Private Declare Function SysAllocString Lib "oleaut32.dll" (ByVal psz As Long) As Long + +Private Const NCODES As Long = 25 +Private gVtbl(0 To 6) As Long +Private gObj(0 To 1) As Long +Private gCodes(1 To NCODES) As Long +Private gNames(1 To NCODES) As String +Private gCalls(0 To NCODES) As Long +Private gFlags(0 To NCODES) As Long +Private gArgs(0 To NCODES) As Long +Private gHasExc(0 To NCODES) As Long +Private gGetIds As Long + +Sub Main() + On Error GoTo Fail + Open App.Path & "\out.txt" For Output As #9 + Cases + Close #9 + Exit Sub +Fail: + On Error Resume Next + Print #9, "unhandled error " & Err.Number & ": " & Err.Description + Close #9 +End Sub + +Private Sub SetCode(ByVal n As Long, ByVal code As Long, ByVal nm As String) + gCodes(n) = code + gNames(n) = nm +End Sub + +Private Sub InitCodes() + SetCode 1, &H80004001, "E_NOTIMPL" + SetCode 2, &H8007000E, "E_OUTOFMEMORY" + SetCode 3, &H80070057, "E_INVALIDARG" + SetCode 4, &H8002000E, "DISP_E_BADPARAMCOUNT" + SetCode 5, &H8002000F, "DISP_E_PARAMNOTOPTIONAL" + SetCode 6, &H8002000A, "DISP_E_OVERFLOW" + SetCode 7, &H80020005, "DISP_E_TYPEMISMATCH" + SetCode 8, &H8002000B, "DISP_E_BADINDEX" + SetCode 9, &H80020012, "DISP_E_DIVBYZERO" + SetCode 10, &H80020007, "DISP_E_NONAMEDARGS" + SetCode 11, &H80020008, "DISP_E_BADVARTYPE" + SetCode 12, &H80020004, "DISP_E_PARAMNOTFOUND" + SetCode 13, &H8002000D, "DISP_E_ARRAYISLOCKED" + SetCode 14, &H80004002, "E_NOINTERFACE" + SetCode 15, &H80070005, "E_ACCESSDENIED" + SetCode 16, &H80004003, "E_POINTER" + SetCode 17, &H80004004, "E_ABORT" + SetCode 18, &H80004005, "E_FAIL" + SetCode 19, &H8000FFFF, "E_UNEXPECTED" + SetCode 20, &H80020003, "DISP_E_MEMBERNOTFOUND" + SetCode 21, &H800A01A8, "800A01A8" + SetCode 22, &H8007000D, "HRESULT_FROM_WIN32(ERROR_INVALID_DATA) 8007000D" + SetCode 23, &H80020009, "DISP_E_EXCEPTION (excepinfo scode 80041234, wCode 0, description given)" + SetCode 24, &H80020009, "DISP_E_EXCEPTION (excepinfo scode 0, wCode 1234, description given)" + SetCode 25, &H80020011, "DISP_E_NOTACOLLECTION" +End Sub + +Private Function Fn(ByVal p As Long) As Long + Fn = p +End Function + +Private Sub InitObject() + gVtbl(0) = Fn(AddressOf Obj_QueryInterface) + gVtbl(1) = Fn(AddressOf Obj_AddRef) + gVtbl(2) = Fn(AddressOf Obj_Release) + gVtbl(3) = Fn(AddressOf Obj_GetTypeInfoCount) + gVtbl(4) = Fn(AddressOf Obj_GetTypeInfo) + gVtbl(5) = Fn(AddressOf Obj_GetIDsOfNames) + gVtbl(6) = Fn(AddressOf Obj_Invoke) + gObj(0) = VarPtr(gVtbl(0)) + gObj(1) = 1 +End Sub + +Public Function Obj_QueryInterface(ByVal pThis As Long, ByVal riid As Long, ByVal ppv As Long) As Long + Dim d1 As Long, d2 As Integer, d3 As Integer, b(0 To 7) As Byte + Dim isUnk As Boolean, isDisp As Boolean + If riid = 0 Or ppv = 0 Then + Obj_QueryInterface = &H80004003 + Exit Function + End If + CopyMemory d1, ByVal riid, 4 + CopyMemory d2, ByVal (riid + 4), 2 + CopyMemory d3, ByVal (riid + 6), 2 + CopyMemory b(0), ByVal (riid + 8), 8 + isUnk = (d1 = 0 And d2 = 0 And d3 = 0 And b(0) = &HC0 And b(7) = &H46 And b(1) = 0 And b(6) = 0) + isDisp = (d1 = &H20400 And d2 = 0 And d3 = 0 And b(0) = &HC0 And b(7) = &H46 And b(1) = 0 And b(6) = 0) + If isUnk Or isDisp Then + gObj(1) = gObj(1) + 1 + CopyMemory ByVal ppv, pThis, 4 + Obj_QueryInterface = 0 + Else + CopyMemory ByVal ppv, 0&, 4 + Obj_QueryInterface = &H80004002 + End If +End Function + +Public Function Obj_AddRef(ByVal pThis As Long) As Long + gObj(1) = gObj(1) + 1 + Obj_AddRef = gObj(1) +End Function + +Public Function Obj_Release(ByVal pThis As Long) As Long + gObj(1) = gObj(1) - 1 + Obj_Release = gObj(1) +End Function + +Public Function Obj_GetTypeInfoCount(ByVal pThis As Long, ByVal pctinfo As Long) As Long + If pctinfo <> 0 Then CopyMemory ByVal pctinfo, 0&, 4 + Obj_GetTypeInfoCount = 0 +End Function + +Public Function Obj_GetTypeInfo(ByVal pThis As Long, ByVal iTInfo As Long, ByVal lcid As Long, ByVal ppTInfo As Long) As Long + Obj_GetTypeInfo = &H80004001 +End Function + +Public Function Obj_GetIDsOfNames(ByVal pThis As Long, ByVal riid As Long, ByVal rgszNames As Long, ByVal cNames As Long, ByVal lcid As Long, ByVal rgDispId As Long) As Long + Dim pName As Long, n As Long, s As String, num As Long + gGetIds = gGetIds + 1 + CopyMemory pName, ByVal rgszNames, 4 + n = lstrlenW(pName) + s = String$(n, vbNullChar) + If n > 0 Then CopyMemory ByVal StrPtr(s), ByVal pName, n * 2 + If UCase$(Left$(s, 1)) = "C" And IsNumeric(Mid$(s, 2)) Then + num = CLng(Mid$(s, 2)) + CopyMemory ByVal rgDispId, num, 4 + Obj_GetIDsOfNames = 0 + Else + num = -1 + CopyMemory ByVal rgDispId, num, 4 + Obj_GetIDsOfNames = &H80020006 + End If +End Function + +Public Function Obj_Invoke(ByVal pThis As Long, ByVal dispIdMember As Long, ByVal riid As Long, ByVal lcid As Long, ByVal wFlags As Long, ByVal pDispParams As Long, ByVal pVarResult As Long, ByVal pExcepInfo As Long, ByVal puArgErr As Long) As Long + Dim cArgs As Long, bstr As Long, sc As Long, wc As Integer + If dispIdMember >= 1 And dispIdMember <= NCODES Then + gCalls(dispIdMember) = gCalls(dispIdMember) + 1 + gFlags(dispIdMember) = wFlags And &HFFFF& + If pDispParams <> 0 Then CopyMemory cArgs, ByVal (pDispParams + 8), 4 + gArgs(dispIdMember) = cArgs + gHasExc(dispIdMember) = IIf(pExcepInfo <> 0, 1, 0) + If gCodes(dispIdMember) = &H80020009 And pExcepInfo <> 0 Then + If dispIdMember = 23 Then + wc = 0: sc = &H80041234 + Else + wc = 1234: sc = 0 + End If + ' the caller need not have zeroed it: fill all 32 bytes + Dim z(0 To 31) As Byte + CopyMemory ByVal pExcepInfo, z(0), 32 + CopyMemory ByVal pExcepInfo, wc, 2 + bstr = SysAllocString(StrPtr("Source from excepinfo")) + CopyMemory ByVal (pExcepInfo + 4), bstr, 4 + bstr = SysAllocString(StrPtr("Description from excepinfo")) + CopyMemory ByVal (pExcepInfo + 8), bstr, 4 + CopyMemory ByVal (pExcepInfo + 28), sc, 4 + End If + Obj_Invoke = gCodes(dispIdMember) + Else + Obj_Invoke = &H80020003 + End If +End Function + +Private Sub Rec(ByVal n As Long, ByVal way As String, ByVal callsBefore As Long) + Dim num As Long, desc As String, src As String + num = Err.Number: desc = Err.Description: src = Err.Source + Print #9, gNames(n) & " (" & Hex$(gCodes(n)) & ") " & way & " -> " & num & " [" & desc & "] invokeCalls=" & (gCalls(n) - callsBefore) + Err.Clear +End Sub + +Private Sub Show(ByVal what As String) + Print #9, what & " -> " & Err.Number & " [" & Err.Description & "]" + Err.Clear +End Sub + +Sub Cases() + Dim o As Object, p As Long, n As Long, before As Long + InitCodes + InitObject + p = VarPtr(gObj(0)) + gObj(1) = gObj(1) + 1 + CopyMemory o, p, 4 + Print #9, "object ptr ok: " & (Not (o Is Nothing)) + + For n = 1 To NCODES + On Error Resume Next + Err.Clear + before = gCalls(n) + Select Case n + Case 1: o.C1 + Case 2: o.C2 + Case 3: o.C3 + Case 4: o.C4 + Case 5: o.C5 + Case 6: o.C6 + Case 7: o.C7 + Case 8: o.C8 + Case 9: o.C9 + Case 10: o.C10 + Case 11: o.C11 + Case 12: o.C12 + Case 13: o.C13 + Case 14: o.C14 + Case 15: o.C15 + Case 16: o.C16 + Case 17: o.C17 + Case 18: o.C18 + Case 19: o.C19 + Case 20: o.C20 + Case 21: o.C21 + Case 22: o.C22 + Case 23: o.C23 + Case 24: o.C24 + Case 25: o.C25 + End Select + Rec n, "statement", before + On Error GoTo 0 + Next + + CopyMemory o, 0&, 4 + + ' Ordinary objects: a name they do not have, and a wrong number of arguments. + Dim w As Object, c As Object, x As Variant + Set w = New Widget + Set c = New Collection + On Error Resume Next + w.Nope + Show "Widget.Nope" + c.Nope + Show "Collection.Nope" + CallByName w, "Nope", VbMethod + Show "CallByName Widget, Nope" + x = c.Item(1, 2) + Show "x = Collection.Item(1, 2)" + Set c = CreateObject("Scripting.Dictionary") + x = c.Count(1) + Show "x = Dictionary.Count(1)" + On Error GoTo 0 +End Sub diff --git a/bugs/raiseeventbyname-no-events-crash/raiseeventbyname-no-events-crash.twinproj b/bugs/raiseeventbyname-no-events-crash/raiseeventbyname-no-events-crash.twinproj new file mode 100644 index 000000000..d6b3c6584 Binary files /dev/null and b/bugs/raiseeventbyname-no-events-crash/raiseeventbyname-no-events-crash.twinproj differ diff --git a/bugs/raiseeventbyname-no-events-crash/repro.json b/bugs/raiseeventbyname-no-events-crash/repro.json new file mode 100644 index 000000000..cc4c3b989 --- /dev/null +++ b/bugs/raiseeventbyname-no-events-crash/repro.json @@ -0,0 +1,18 @@ +{ + "mode": "test", + "expect": { + "exit": 7, + "output": [ + "^PASS RegressionTests.RaiseEventByName_Nothing_Raises91$", + "^PASS RegressionTests.RaiseEventByName_DeclaredEventWithHandler_CallsIt$", + "^PASS RegressionTests.RaiseEventByName2_DeclaredEventWithHandler_CallsIt$", + "^PASS RegressionTests.RaiseEvent_NoHandler_DoesNothing$", + "^PASS RegressionTests.RaiseEventByName_UndeclaredEventName_RaisesAnError$", + "^FAIL RegressionTests.RaiseEventByName_DeclaredEventNoHandler_Succeeds: .*Actual: -2147467259 ", + "^FAIL RegressionTests.RaiseEventByName2_DeclaredEventNoHandler_Succeeds: .*Actual: -2147467259 ", + "^FAIL RegressionTests.RaiseEventByName_Collection_RaisesAnError: .*ACCESS_VIOLATION", + "^FAIL RegressionTests.RaiseEventByName_Dictionary_RaisesAnError: .*ACCESS_VIOLATION", + "^FAIL RegressionTests.RaiseEventByName2_Collection_RaisesAnError: .*ACCESS_VIOLATION" + ] + } +} diff --git a/bugs/raiseeventbyname-no-events-crash/src/Settings b/bugs/raiseeventbyname-no-events-crash/src/Settings new file mode 100644 index 000000000..74e0f5c83 --- /dev/null +++ b/bugs/raiseeventbyname-no-events-crash/src/Settings @@ -0,0 +1,75 @@ +{ + "configuration.inherits": "Defaults", + "project.appTitle": "RaiseeventbynameNoEventsCrash", + "project.buildPath": "${SourcePath}\\Build\\${ProjectName}_${Architecture}.${FileExtension}", + "project.buildType": "Standard EXE", + "project.description": "Reproduces: RaiseEventByName and RaiseEventByName2 end the program with an access violation for an object that declares no events", + "project.exportPathIsV2": true, + "project.id": "{559F0C9F-9628-4E53-A5B5-3249473685D0}", + "project.name": "RaiseeventbynameNoEventsCrash", + "project.optionExplicit": true, + "project.references": [ + { + "id": "{00020430-0000-0000-C000-000000000046}", + "lcid": 0, + "name": "OLE Automation", + "path32": "C:\\Windows\\SysWOW64\\stdole2.tlb", + "path64": "C:\\Windows\\System32\\stdole2.tlb", + "symbolId": "stdole", + "versionMajor": 2, + "versionMinor": 0 + }, + { + "id": "{F50B82D0-DCAB-43FE-9631-11959D4A4728}", + "isCompilerPackage": true, + "licence": "MIT", + "name": "[COMPILER PACKAGE] twinBASIC - VB Compatibility Package (Forms)", + "path32": "", + "path64": "", + "publisher": "TWINBASIC-COMPILER", + "symbolId": "VB", + "versionBuild": 0, + "versionMajor": 0, + "versionMinor": 0, + "versionRevision": 31 + }, + { + "id": "{C192FB39-64CA-4D9B-B477-A5502F48EFCC}", + "isCompilerPackage": true, + "licence": "MIT", + "name": "[COMPILER PACKAGE] twinBASIC - App global class object", + "path32": "", + "path64": "", + "publisher": "TWINBASIC-COMPILER", + "symbolId": "AppGlobalClassProject", + "versionBuild": 0, + "versionMajor": 1, + "versionMinor": 0, + "versionRevision": 0 + }, + { + "id": "{9847A10D-8987-44B2-8B87-EA77DEF7F1D9}", + "isCompilerPackage": true, + "licence": "MIT", + "name": "[COMPILER PACKAGE] Assert", + "path32": "", + "path64": "", + "publisher": "TWINBASIC-COMPILER", + "symbolId": "Assert", + "versionBuild": 0, + "versionMajor": 0, + "versionMinor": 0, + "versionRevision": 24 + } + ], + "project.settingsVersion": 1, + "project.startupObject": "Sub Main", + "project.warnings": { + "errors": [], + "hints": [], + "ignored": [], + "info": [], + "warnings": [] + }, + "runtime.useUnicodeStandardLibrary": true +} diff --git a/bugs/raiseeventbyname-no-events-crash/src/Sources/EventSource.twin b/bugs/raiseeventbyname-no-events-crash/src/Sources/EventSource.twin new file mode 100644 index 000000000..8522d79d8 --- /dev/null +++ b/bugs/raiseeventbyname-no-events-crash/src/Sources/EventSource.twin @@ -0,0 +1,11 @@ +' A class that declares one event, for the controls and the no-handler cases. Raise is what +' the RaiseEvent statement does with the same event: with no handler attached, nothing. +Class EventSource + + Public Event Changed(ByVal Value As Long) + + Public Sub Raise(ByVal Value As Long) + RaiseEvent Changed(Value) + End Sub + +End Class diff --git a/bugs/raiseeventbyname-no-events-crash/src/Sources/Listener.twin b/bugs/raiseeventbyname-no-events-crash/src/Sources/Listener.twin new file mode 100644 index 000000000..c3f2495e1 --- /dev/null +++ b/bugs/raiseeventbyname-no-events-crash/src/Sources/Listener.twin @@ -0,0 +1,18 @@ +' Handles EventSource's event and counts the calls, for the control that raises it with a +' handler attached. +Class Listener + + Private WithEvents Source As EventSource + Public Calls As Long + Public LastValue As Long + + Public Sub Attach(ByVal S As EventSource) + Set Source = S + End Sub + + Private Sub Source_Changed(ByVal Value As Long) + Calls = Calls + 1 + LastValue = Value + End Sub + +End Class diff --git a/bugs/raiseeventbyname-no-events-crash/src/Sources/RegressionTests.twin b/bugs/raiseeventbyname-no-events-crash/src/Sources/RegressionTests.twin new file mode 100644 index 000000000..c7555172a --- /dev/null +++ b/bugs/raiseeventbyname-no-events-crash/src/Sources/RegressionTests.twin @@ -0,0 +1,109 @@ +' Regression tests: RaiseEventByName and RaiseEventByName2 raise an error, and do not end the +' program, for an object that declares no events; and an event with no handler attached is +' raised with no error, as the RaiseEvent statement raises it. tbrun --tests runs each case in +' the compiler's test mode, where an access violation ends the case and not the run, and prints +' PASS or FAIL. The crash cases assert only that some error is raised: VB6 has no +' RaiseEventByName to say which. +[TestFixture] +Module RegressionTests + + ' Controls: Nothing raises 91, a declared event with a handler attached calls it, and a name + ' the object does not declare raises an error. + [TestCase] + Public Sub RaiseEventByName_Nothing_Raises91() + Dim O As Object + On Error Resume Next + RaiseEventByName O, "Changed", Array(1) + Assert.Strict.AreEqual 91&, Err.Number + End Sub + + [TestCase] + Public Sub RaiseEventByName_DeclaredEventWithHandler_CallsIt() + Dim S As New EventSource + Dim L As New Listener + L.Attach S + On Error Resume Next + RaiseEventByName S, "Changed", Array(7) + Assert.Strict.AreEqual 0&, Err.Number + Assert.Strict.AreEqual 1&, L.Calls + Assert.Strict.AreEqual 7&, L.LastValue + End Sub + + [TestCase] + Public Sub RaiseEventByName2_DeclaredEventWithHandler_CallsIt() + Dim S As New EventSource + Dim L As New Listener + L.Attach S + On Error Resume Next + RaiseEventByName2 S, "Changed", 7 + Assert.Strict.AreEqual 0&, Err.Number + Assert.Strict.AreEqual 1&, L.Calls + End Sub + + [TestCase] + Public Sub RaiseEvent_NoHandler_DoesNothing() + Dim S As New EventSource + On Error Resume Next + S.Raise 1 + Assert.Strict.AreEqual 0&, Err.Number + End Sub + + [TestCase] + Public Sub RaiseEventByName_UndeclaredEventName_RaisesAnError() + Dim S As New EventSource + Dim L As New Listener + L.Attach S + On Error Resume Next + RaiseEventByName S, "Nope", Array(1) + Assert.Strict.AreNotEqual 0&, Err.Number + End Sub + + ' The defect: a declared event with no handler attached raises -2147467259, where + ' RaiseEvent Changed(1) inside the class does nothing. + [TestCase] + Public Sub RaiseEventByName_DeclaredEventNoHandler_Succeeds() + Dim O As Object + Set O = New EventSource + On Error Resume Next + RaiseEventByName O, "Changed", Array(1) + Assert.Strict.AreEqual 0&, Err.Number + End Sub + + [TestCase] + Public Sub RaiseEventByName2_DeclaredEventNoHandler_Succeeds() + Dim O As Object + Set O = New EventSource + On Error Resume Next + RaiseEventByName2 O, "Changed", 1 + Assert.Strict.AreEqual 0&, Err.Number + End Sub + + ' The defect: an object that declares no events ends the program. + [TestCase] + Public Sub RaiseEventByName_Collection_RaisesAnError() + Dim O As Object + Set O = New Collection + On Error Resume Next + RaiseEventByName O, "Changed", Array(1) + Assert.Strict.AreNotEqual 0&, Err.Number + End Sub + + [TestCase] + Public Sub RaiseEventByName_Dictionary_RaisesAnError() + Dim O As Object + Set O = CreateObject("Scripting.Dictionary") + On Error Resume Next + RaiseEventByName O, "Changed", Array(1) + Assert.Strict.AreNotEqual 0&, Err.Number + End Sub + + [TestCase] + Public Sub RaiseEventByName2_Collection_RaisesAnError() + Dim O As Object + Set O = New Collection + On Error Resume Next + RaiseEventByName2 O, "Changed", 1 + Assert.Strict.AreNotEqual 0&, Err.Number + End Sub + +End Module diff --git a/bugs/raiseeventbyname-no-events-crash/src/Sources/Startup.twin b/bugs/raiseeventbyname-no-events-crash/src/Sources/Startup.twin new file mode 100644 index 000000000..be8f5c8bc --- /dev/null +++ b/bugs/raiseeventbyname-no-events-crash/src/Sources/Startup.twin @@ -0,0 +1,6 @@ +Module Startup + + Public Sub Main() + End Sub + +End Module diff --git a/docs/Documentation/Tools-Defects.md b/docs/Documentation/Tools-Defects.md index 199209201..d20820529 100644 --- a/docs/Documentation/Tools-Defects.md +++ b/docs/Documentation/Tools-Defects.md @@ -152,21 +152,21 @@ Exit codes: **0** the sample ran, or, with `--docs`, no fence differs and none r ## errors.mjs {: #errors } - node scripts/errors.mjs run [...] [--only ] [--runtime tb|vb6|win64]... [--ide ] [--vb6 ] [--keep] + node scripts/errors.mjs run [...] [--only ] [--runtime tb|win64|vb6|vba64]... [--ide ] [--vb6 ] [--keep] node scripts/errors.mjs generate [...] [--only ] node scripts/errors.mjs write [...] - node scripts/errors.mjs messages [--runtime tb|vb6]... [--ide ] [--vb6 ] [--keep] + node scripts/errors.mjs messages [--runtime tb|win64|vb6|vba64]... [--ide ] [--vb6 ] [--keep] -Measures the run-time errors each documented procedure raises, and writes each reference page's Common Errors section from them. The errors are data, not prose: a YAML file per area under `errors/` lists, for each page, the cases that raise each error and the cases that must raise nothing, and `errors/messages.yml` gives each number's message: the one `Err.Description` gives once the error is raised, and the one `Error$(number)` gives, where the two differ. The format is in `WIP.Errors.md`. A file is named as it is in `errors/`; with none named, every file there is used. `--only ` keeps the pages whose path under `docs/` matches. +Measures the run-time errors each documented procedure raises, and writes each reference page's Common Errors section from them. The errors are data, not prose: a YAML file per area under `errors/` lists, for each page, the cases that raise each error and the cases that must raise nothing, and `errors/messages.yml` gives each number's message: the one `Err.Description` gives once the error is raised, and the one `Error$(number)` gives, where the two differ. The format is in `WIP.Errors.md`. Each twinBASIC build is compared with its counterpart: 32-bit twinBASIC with VB6, 64-bit twinBASIC with 64-bit VBA. A case can run under a *harness*, the code of `errors/harnesses/.bas`, one source for twinBASIC, VB6 and VBA, which the probe calls before and after the case: the `clock` harness replaces the system's clock setters in the probe's own process, so a case that sets `Date` or `Time` is measured, and the value it would set checked, without the clock being changed. A file is named as it is in `errors/`; with none named, every file there is used. `--only ` keeps the pages whose path under `docs/` matches. | Command | Effect | |---|---| -| `run` | Builds one probe of the cases for each runtime and runs it: 32-bit twinBASIC (`tb`) and VB6 by default, and 64-bit twinBASIC (`win64`) as well for a data file that says `win64: true`; `--runtime` names them instead, and may be repeated. Each case is a procedure of its own that runs under `On Error Resume Next` and reports `Err.Number` and `Err.Description`. The twinBASIC probe is a tree run by [`tbrun --exe`](Tools-Compiler#tbrun), so the cases run in the built exe and not in the IDE, and the VB6 probe is a `Probe.vbp` built and run by the code [`bug_repro.mjs vb6`](#bug-repro) uses, on a private desktop. A case a compiler refuses is reported with the compiler's message and left out, and the probe is built again without it, so one bad case costs a rebuild and not the run. Each case is printed with what each runtime raised, and a case is `FAIL` when a runtime raised another number than the data says, refused it, never reported, or raised a number whose message is not the one in `messages.yml`. `--keep` keeps the generated probes and prints where they are. | -| `generate` | Writes the probes to `/tb`, a tree for `tbrun --exe`, and `/vb6`, a `Probe.vbp` project, without running them. | +| `run` | Builds one probe of the cases for each runtime and runs it: 32-bit twinBASIC (`tb`), 64-bit twinBASIC (`win64`), VB6 (`vb6`) and 64-bit VBA (`vba64`), all four by default; `--runtime` names them instead, and may be repeated. Each case is a procedure of its own that runs under `On Error Resume Next` and reports `Err.Number` and `Err.Description`. The twinBASIC probe is a tree run by [`tbrun --exe`](Tools-Compiler#tbrun), so the cases run in the built exe and not in the IDE, and the VB6 probe is a `Probe.vbp` built and run by the code [`bug_repro.mjs vb6`](#bug-repro) uses, on a private desktop. The VBA probe is a module run in 64-bit Microsoft Access through automation, also on a private desktop: one Access serves the whole run, each probe in a new database, and an Access that shows a box (a compile error) or passes the time limit is ended and the next probe starts another. Access needs no change to its security settings for this. A case a compiler refuses is reported with the compiler's message and left out, and the probe is built again without it, so one bad case costs a rebuild and not the run. Each case is printed with what each runtime raised, and a case is `FAIL` when a runtime raised another number than the data says, refused it, never reported, or raised a number whose message is not the one in `messages.yml`. `--keep` keeps the generated probes and prints where they are. | +| `generate` | Writes the probes to `/tb`, a tree for `tbrun --exe`, `/vb6`, a `Probe.vbp` project, and `/vba`, a `Module1.bas` module, without running them. | | `write` | Writes each page's section from the data, between `` and ``: before the page's first Example heading, else before See Also, else at the end, at the level the page gives those headings. Rows of the data that raise the same number make one row of the table, their conditions one line each, in the order of the data. A page whose data lists only cases that raise nothing gets no section, and loses one it had. Each number in a section links to its row on the central page, [Run-time Errors](../../Reference/Run-time-Errors), and `write` also writes that page's index between `` and ``: a row for every number in `messages.yml`, with its anchor, its message and links to the pages whose sections list it. Writing again changes nothing. [`check_errors.mjs`](Tools-Toolchain-Tests#check-errors) is its gate. | -| `messages` | Builds one probe of the messages for each runtime, 32-bit twinBASIC and VB6 unless `--runtime` names one, runs it as `run` runs its probe, and compares it with `messages.yml`. The probe gives `Error$(n)` and, after `Err.Raise n`, `Err.Number` and `Err.Description`, for every number from 1 to 65535 whose `Error$` is not *Application-defined or object-defined error*, and for every other number the file lists. Every such number from 1 to 65535 must be in the file, and every number in it must give its messages: both in both runtimes for a number from 1 to 65535; for another number, both in twinBASIC and `Error$` in VB6, since VB6's `Err.Raise` turns many HRESULTs into its own numbers (`Err.Raise &H80070057` raises 5). Each disagreement is printed. | +| `messages` | Builds one probe of the messages for each runtime, all four unless `--runtime` names some, runs it as `run` runs its probe, and compares it with `messages.yml`. The probe gives `Error$(n)` and, after `Err.Raise n`, `Err.Number` and `Err.Description`, for every number from 1 to 65535 whose `Error$` is not *Application-defined or object-defined error*, and for every other number the file lists. Every such number from 1 to 65535 must be in the file, and every number in it must give its messages: both in every runtime for a number from 1 to 65535; for another number, both in twinBASIC and `Error$` in VB6 and VBA, since VB6's `Err.Raise` turns many HRESULTs into its own numbers (`Err.Raise &H80070057` raises 5). Each disagreement is printed. | -`run` and `messages` need twinBASIC and VB6, found as `tbrun` and [`vb6run.mjs`](#vb6run) find them, so they are outside every gate and outside CI, and are run by a person when a new BETA is installed. `generate` and `write` need neither. +`run` and `messages` need twinBASIC, found as `tbrun` finds it, VB6, found as [`vb6run.mjs`](#vb6run) finds it, and 64-bit Microsoft Access, so they are outside every gate and outside CI, and are run by a person when a new BETA is installed. `generate` and `write` need none of them. Exit codes: **0** `run`, `messages`: every runtime agrees with the data; `generate`, `write`: done; **1** `run`, `messages`: a runtime disagrees with the data; **2** a refused command line, data with a problem, a runtime that could not be run or a probe that ended early, or a crash. diff --git a/docs/Reference/Core/ReDim.md b/docs/Reference/Core/ReDim.md index 9bc714c02..2d464b090 100644 --- a/docs/Reference/Core/ReDim.md +++ b/docs/Reference/Core/ReDim.md @@ -65,7 +65,7 @@ The **ReDim** statement acts as a declarative statement if the variable it decla | [13 (`&HD`)](tb:Errors#13), *Type mismatch* | **Preserve** is used on a **Variant** that does not hold an array | > [!NOTE] BETA 1010 -> When an upper bound is less than its lower bound, VB6 raises error [9 (`&H9`)](tb:Errors#9), *Subscript out of range*, instead. +> When an upper bound is less than its lower bound, VB6 and 64-bit VBA raise error [9 (`&H9`)](tb:Errors#9), *Subscript out of range*, instead. diff --git a/docs/Reference/Default/VBA/Conversion/Bin.md b/docs/Reference/Default/VBA/Conversion/Bin.md index df77398c2..ccff58a95 100644 --- a/docs/Reference/Default/VBA/Conversion/Bin.md +++ b/docs/Reference/Default/VBA/Conversion/Bin.md @@ -28,14 +28,28 @@ The result holds only the digits `0` and `1`, with no leading zeros and no prefi | Zero or a positive number | The digits of its value | | A negative **Integer** | 16 characters | | A negative **Long** | 32 characters | -| A negative **LongLong** or **Double** | 64 characters | +| A negative **LongLong**, **Single**, **Double**, **Currency** or **Decimal**, or a string holding a negative number | 64 characters | | **Null** | **Null** (unsuffixed form only) | | **Empty** | `"0"` | -**Bin** accepts the same arguments as [**Hex**](Hex) and [**Oct**](Oct) and returns the same value in a different base. A string that holds a number, including one written with the `&H` or `&O` prefix, is converted as the number. A string that does not hold a number (an empty string, or one with the `&B` prefix, included) raises error 13, *Type mismatch*. A **Double** too large for a **LongLong** raises error 6, *Overflow*. `Bin$(Null)` raises error 94, *Invalid use of Null*. +**Bin** accepts the same arguments as [**Hex**](Hex) and [**Oct**](Oct) and returns the same value in a different base. A string that holds a number, including one written with the `&H` or `&O` prefix, is converted as the number. + +> [!NOTE] +> A **Single** or **Double** of 2^62 (4,611,686,018,427,387,904) or more, or less than -2^62, raises error 6, *Overflow*, though its value fits a **LongLong**, as it does in **Hex** and **Oct**. A **LongLong**, **Decimal** or string of the same value is converted; so is `Bin(CLngLng(x))`. For the opposite of **Bin**, precede a binary value with **&B**: `&B101` is the number 5 (see [Literals](../../../Features/Language/Literals)). + +### Common Errors + +| Error | When | +|---|---| +| [6 (`&H6`)](tb:Errors#6), *Overflow* | *number* is outside the range of a **LongLong**, or is a **Single** or **Double** less than -4,611,686,018,427,387,904 or greater than 4,611,686,018,427,387,903 | +| [13 (`&HD`)](tb:Errors#13), *Type mismatch* | *number* is a string that does not hold a number (a zero-length string, and one with the `&B` prefix, included) | +| [94 (`&H5E`)](tb:Errors#94), *Invalid use of Null* | **Bin$** is given a *number* that is **Null** | + + + ### Example This example uses the **Bin** function to return the binary value of numbers of several types. diff --git a/docs/Reference/Default/VBA/Conversion/CBool.md b/docs/Reference/Default/VBA/Conversion/CBool.md index a314c0208..794222900 100644 --- a/docs/Reference/Default/VBA/Conversion/CBool.md +++ b/docs/Reference/Default/VBA/Conversion/CBool.md @@ -18,10 +18,19 @@ Syntax: **CBool(** *expression* **)** The return type is **Boolean**. If *expression* evaluates to a nonzero value, **CBool** returns **True**; otherwise, it returns **False**. -If *expression* cannot be interpreted as a numeric value, a run-time error occurs. - The data-type conversion functions document code by showing that the result of some operation should be expressed as a particular data type rather than the default data type. + +### Common Errors + +| Error | When | +|---|---| +| [6 (`&H6`)](tb:Errors#6), *Overflow* | *expression* is a string that holds a number outside the range of a **Double** | +| [13 (`&HD`)](tb:Errors#13), *Type mismatch* | *expression* is a string that is not a number, **True** or **False** (a zero-length string included) | +| [94 (`&H5E`)](tb:Errors#94), *Invalid use of Null* | *expression* is **Null** | + + + ### Example This example uses the **CBool** function to convert an expression to a **Boolean**. diff --git a/docs/Reference/Default/VBA/Conversion/CByte.md b/docs/Reference/Default/VBA/Conversion/CByte.md index 2b57da818..9dadca608 100644 --- a/docs/Reference/Default/VBA/Conversion/CByte.md +++ b/docs/Reference/Default/VBA/Conversion/CByte.md @@ -16,7 +16,18 @@ Syntax: **CByte(** *expression* **)** *expression* : *required* Any valid string or numeric expression in the range `0` to `255`. -The return type is **Byte**. If *expression* is outside the range of a **Byte**, a run-time error occurs. Fractions are rounded --- when the fractional part is exactly `0.5`, **CByte** rounds to the nearest even number. +The return type is **Byte**. Fractions are rounded --- when the fractional part is exactly `0.5`, **CByte** rounds to the nearest even number. + + +### Common Errors + +| Error | When | +|---|---| +| [6 (`&H6`)](tb:Errors#6), *Overflow* | *expression* rounds to a number less than 0 or greater than 255 | +| [13 (`&HD`)](tb:Errors#13), *Type mismatch* | *expression* is a string that does not hold a number (a zero-length string included) | +| [94 (`&H5E`)](tb:Errors#94), *Invalid use of Null* | *expression* is **Null** | + + ### Example diff --git a/docs/Reference/Default/VBA/Conversion/CCur.md b/docs/Reference/Default/VBA/Conversion/CCur.md index b1684966d..dfb2dfbbf 100644 --- a/docs/Reference/Default/VBA/Conversion/CCur.md +++ b/docs/Reference/Default/VBA/Conversion/CCur.md @@ -16,12 +16,23 @@ Syntax: **CCur(** *expression* **)** *expression* : *required* Any valid string or numeric expression in the range `-922,337,203,685,477.5808` to `922,337,203,685,477.5807`. -The return type is **Currency**. If *expression* is outside that range, a run-time error occurs. +The return type is **Currency**. **CCur** forces currency arithmetic in cases where single-precision, double-precision, or integer arithmetic would normally occur. **CCur** is the internationally aware alternative to **Val** for converting between data types. **CCur** recognizes different decimal separators, different thousand separators, and various currency options properly, depending on the system's locale setting. + +### Common Errors + +| Error | When | +|---|---| +| [6 (`&H6`)](tb:Errors#6), *Overflow* | *expression* is less than -922,337,203,685,477.5808 or greater than 922,337,203,685,477.5807 | +| [13 (`&HD`)](tb:Errors#13), *Type mismatch* | *expression* is a string that does not hold a number (a zero-length string included) | +| [94 (`&H5E`)](tb:Errors#94), *Invalid use of Null* | *expression* is **Null** | + + + ### Example This example uses the **CCur** function to convert an expression to a **Currency**. diff --git a/docs/Reference/Default/VBA/Conversion/CDate.md b/docs/Reference/Default/VBA/Conversion/CDate.md index e10f2390e..8c60ad1c7 100644 --- a/docs/Reference/Default/VBA/Conversion/CDate.md +++ b/docs/Reference/Default/VBA/Conversion/CDate.md @@ -24,6 +24,31 @@ Use the **IsDate** function to determine whether *expression* can be converted t [**CVDate**](CVDate) is also provided for compatibility with previous versions of Visual Basic. The syntax of **CVDate** is identical to **CDate**; however, **CVDate** returns a **Variant** whose subtype is **Date** instead of an actual **Date** type. +A **Date** holds a day number from -657,434 (January 1, 100) to 2,958,465 (December 31, 9999), with the time of day as its fraction. + +> [!WARNING] BETA 1010 +> **CDate** of a number outside that range, or of a string that holds one, raises no error: `CDate(2958466)`, `CDate(-657435)`, `CDate(1E+300)` and `CDate("3000000")` each return a **Date** that holds the number, and **CStr**, **Format** and **Year** of it then raise error 5, *Invalid procedure call or argument*. VB6 raises error 6, *Overflow*, for the number and error 13, *Type mismatch*, for the string. Compare a day number with -657,434 and 2,958,465 before converting it, or use [**CVDate**](CVDate), which raises error 13 for a number outside the range. + + +### Common Errors + +| Error | When | +|---|---| +| [6 (`&H6`)](tb:Errors#6), *Overflow* | *expression* is a **Decimal** less than -657,434 or 2,958,466 or more | +| [13 (`&HD`)](tb:Errors#13), *Type mismatch* | *expression* is a string that is not a date, a time or a number (a zero-length string included), or a date after December 31, 9999 | +| [94 (`&H5E`)](tb:Errors#94), *Invalid use of Null* | *expression* is **Null** | + +> [!NOTE] BETA 1010 +> When *expression* is a **Decimal** less than -657,434 or 2,958,466 or more, VB6 and 64-bit VBA raise error [13 (`&HD`)](tb:Errors#13), *Type mismatch*, instead. + +> [!NOTE] BETA 1010 +> When **CDate** is given a number of another type than **Decimal** that is less than -657,434 or 2,958,466 or more, VB6 and 64-bit VBA raise error [6 (`&H6`)](tb:Errors#6), *Overflow*; twinBASIC raises none. + +> [!NOTE] BETA 1010 +> When **CDate** is given a string that holds a number less than -657,434 or 2,958,466 or more, VB6 and 64-bit VBA raise error [13 (`&HD`)](tb:Errors#13), *Type mismatch*; twinBASIC raises none. + + + ### Example This example uses the **CDate** function to convert a string to a **Date**. In general, hard-coding dates and times as strings (as shown in this example) is not recommended. Use date literals and time literals, such as `#2/12/1969#` and `#4:45:23 PM#`, instead. diff --git a/docs/Reference/Default/VBA/Conversion/CDbl.md b/docs/Reference/Default/VBA/Conversion/CDbl.md index e4df0b8c1..64528c0a4 100644 --- a/docs/Reference/Default/VBA/Conversion/CDbl.md +++ b/docs/Reference/Default/VBA/Conversion/CDbl.md @@ -16,10 +16,21 @@ Syntax: **CDbl(** *expression* **)** *expression* : *required* Any valid string or numeric expression in the **Double** range --- `-1.79769313486231E308` to `-4.94065645841247E-324` for negative values, and `4.94065645841247E-324` to `1.79769313486232E308` for positive values. -The return type is **Double**. If *expression* is outside the range of a **Double**, a run-time error occurs. +The return type is **Double**. **CDbl** is the internationally aware alternative to [**Val**](Val) for converting a string to a numeric type. **CDbl** recognizes different decimal separators and different thousand separators properly, depending on the system's locale setting. + +### Common Errors + +| Error | When | +|---|---| +| [6 (`&H6`)](tb:Errors#6), *Overflow* | *expression* is a string that holds a number outside the range of a **Double** | +| [13 (`&HD`)](tb:Errors#13), *Type mismatch* | *expression* is a string that does not hold a number (a zero-length string included) | +| [94 (`&H5E`)](tb:Errors#94), *Invalid use of Null* | *expression* is **Null** | + + + ### Example This example uses the **CDbl** function to convert an expression to a **Double**. diff --git a/docs/Reference/Default/VBA/Conversion/CDec.md b/docs/Reference/Default/VBA/Conversion/CDec.md index f710481f5..1e97bb363 100644 --- a/docs/Reference/Default/VBA/Conversion/CDec.md +++ b/docs/Reference/Default/VBA/Conversion/CDec.md @@ -23,6 +23,17 @@ The return type is **Decimal**. **CDec** is the internationally aware alternative to [**Val**](Val) for converting a string to a numeric type. + +### Common Errors + +| Error | When | +|---|---| +| [6 (`&H6`)](tb:Errors#6), *Overflow* | *expression* is less than -79,228,162,514,264,337,593,543,950,335 or greater than 79,228,162,514,264,337,593,543,950,335 | +| [13 (`&HD`)](tb:Errors#13), *Type mismatch* | *expression* is a string that does not hold a number (a zero-length string included) | +| [94 (`&H5E`)](tb:Errors#94), *Invalid use of Null* | *expression* is **Null** | + + + ### Example This example uses the **CDec** function to convert a numeric value to a **Decimal**. diff --git a/docs/Reference/Default/VBA/Conversion/CInt.md b/docs/Reference/Default/VBA/Conversion/CInt.md index 679d73071..f8e8595ee 100644 --- a/docs/Reference/Default/VBA/Conversion/CInt.md +++ b/docs/Reference/Default/VBA/Conversion/CInt.md @@ -16,12 +16,23 @@ Syntax: **CInt(** *expression* **)** *expression* : *required* Any valid string or numeric expression in the range `-32,768` to `32,767`. Fractions are rounded. -The return type is **Integer**. If *expression* is outside the range of an **Integer**, a run-time error occurs. +The return type is **Integer**. When the fractional part is exactly `0.5`, **CInt** always rounds it to the nearest even number. For example, `0.5` rounds to `0`, and `1.5` rounds to `2`. **CInt** differs from the [**Fix**](Fix) and [**Int**](Int) functions, which truncate, rather than round, the fractional part of a number. Also, **Fix** and **Int** always return a value of the same type as is passed in. **CInt** is the internationally aware alternative to [**Val**](Val) for converting a string to a numeric type. + +### Common Errors + +| Error | When | +|---|---| +| [6 (`&H6`)](tb:Errors#6), *Overflow* | *expression* rounds to a number less than -32,768 or greater than 32,767 | +| [13 (`&HD`)](tb:Errors#13), *Type mismatch* | *expression* is a string that does not hold a number (a zero-length string included) | +| [94 (`&H5E`)](tb:Errors#94), *Invalid use of Null* | *expression* is **Null** | + + + ### Example This example uses the **CInt** function to convert a value to an **Integer**. diff --git a/docs/Reference/Default/VBA/Conversion/CLng.md b/docs/Reference/Default/VBA/Conversion/CLng.md index 9a2c9b75a..ca32e501f 100644 --- a/docs/Reference/Default/VBA/Conversion/CLng.md +++ b/docs/Reference/Default/VBA/Conversion/CLng.md @@ -16,10 +16,21 @@ Syntax: **CLng(** *expression* **)** *expression* : *required* Any valid string or numeric expression in the range `-2,147,483,648` to `2,147,483,647`. Fractions are rounded. -The return type is **Long**. If *expression* is outside the range of a **Long**, a run-time error occurs. +The return type is **Long**. When the fractional part is exactly `0.5`, **CLng** always rounds it to the nearest even number. For example, `0.5` rounds to `0`, and `1.5` rounds to `2`. **CLng** differs from the [**Fix**](Fix) and [**Int**](Int) functions, which truncate, rather than round, the fractional part of a number. + +### Common Errors + +| Error | When | +|---|---| +| [6 (`&H6`)](tb:Errors#6), *Overflow* | *expression* rounds to a number less than -2,147,483,648 or greater than 2,147,483,647 | +| [13 (`&HD`)](tb:Errors#13), *Type mismatch* | *expression* is a string that does not hold a number (a zero-length string included) | +| [94 (`&H5E`)](tb:Errors#94), *Invalid use of Null* | *expression* is **Null** | + + + ### Example This example uses the **CLng** function to convert values to a **Long**. diff --git a/docs/Reference/Default/VBA/Conversion/CLngLng.md b/docs/Reference/Default/VBA/Conversion/CLngLng.md index 026e108b4..70a01ff2b 100644 --- a/docs/Reference/Default/VBA/Conversion/CLngLng.md +++ b/docs/Reference/Default/VBA/Conversion/CLngLng.md @@ -16,7 +16,7 @@ Syntax: **CLngLng(** *expression* **)** *expression* : *required* Any valid string or numeric expression in the range `-9,223,372,036,854,775,808` to `9,223,372,036,854,775,807`. Fractions are rounded. -The return type is **LongLong**. If *expression* is outside the range of a **LongLong**, a run-time error occurs. +The return type is **LongLong**. When the fractional part is exactly `0.5`, **CLngLng** always rounds it to the nearest even number. For example, `0.5` rounds to `0`, and `1.5` rounds to `2`. **CLngLng** differs from the [**Fix**](Fix) and [**Int**](Int) functions, which truncate, rather than round, the fractional part of a number. @@ -26,13 +26,27 @@ When the fractional part is exactly `0.5`, **CLngLng** always rounds it to the n > [!NOTE] > In VBA, **LongLong** (and therefore **CLngLng**) is restricted to 64-bit hosts. twinBASIC supports **LongLong** in both 32-bit and 64-bit modes --- see [Data Types](../../../Features/Language/Data-Types). +> [!WARNING] BETA 1010 +> The constant 2^63, one past the largest **LongLong**, converts to -9223372036854775808 with no error: `CLngLng(2 ^ 63)` and `CLngLng(9223372036854775808#)` do, and so does `CLngLng(9223372036854775807)`, because a whole-number literal without the `^` suffix is a **Double** that rounds to 2^63. A **Double** variable holding 2^63 raises error 6, as the table below says. Write the largest value as `9223372036854775807^`. 64-bit VBA raises error 6 for each of these constants. + + +### Common Errors + +| Error | When | +|---|---| +| [6 (`&H6`)](tb:Errors#6), *Overflow* | *expression* rounds to a number less than -9,223,372,036,854,775,808 or greater than 9,223,372,036,854,775,807 | +| [13 (`&HD`)](tb:Errors#13), *Type mismatch* | *expression* is a string that does not hold a number (a zero-length string included) | +| [94 (`&H5E`)](tb:Errors#94), *Invalid use of Null* | *expression* is **Null** | + + + ### Example This example uses the **CLngLng** function to convert an expression to a **LongLong**. ```tb check_build Dim someValue As Variant -someValue = 9223372036854775807 +someValue = 9223372036854775807^ Dim longLongValue As LongLong longLongValue = CLngLng(someValue) MsgBox "The LongLong representation is: " & longLongValue diff --git a/docs/Reference/Default/VBA/Conversion/CLngPtr.md b/docs/Reference/Default/VBA/Conversion/CLngPtr.md index 759ac2647..6ce4dd774 100644 --- a/docs/Reference/Default/VBA/Conversion/CLngPtr.md +++ b/docs/Reference/Default/VBA/Conversion/CLngPtr.md @@ -16,12 +16,23 @@ Syntax: **CLngPtr(** *expression* **)** *expression* : *required* Any valid string or numeric expression. The acceptable range is `-2,147,483,648` to `2,147,483,647` on 32-bit systems, and `-9,223,372,036,854,775,808` to `9,223,372,036,854,775,807` on 64-bit systems. Fractions are rounded. -The return type is **LongPtr**, which is a **Long** on 32-bit systems and a **LongLong** on 64-bit systems. If *expression* is outside the valid range, a run-time error occurs. +The return type is **LongPtr**, which is a **Long** on 32-bit systems and a **LongLong** on 64-bit systems. When the fractional part is exactly `0.5`, **CLngPtr** always rounds it to the nearest even number. **CLngPtr** differs from the [**Fix**](Fix) and [**Int**](Int) functions, which truncate, rather than round, the fractional part of a number. **LongPtr** is intended primarily for holding pointer values returned from API calls. See [Data Types](../../../Features/Language/Data-Types) for details. + +### Common Errors + +| Error | When | +|---|---| +| [6 (`&H6`)](tb:Errors#6), *Overflow* | in a 32-bit build, *expression* rounds to a number less than -2,147,483,648 or greater than 2,147,483,647
*expression* rounds to a number less than -9,223,372,036,854,775,808 or greater than 9,223,372,036,854,775,807 | +| [13 (`&HD`)](tb:Errors#13), *Type mismatch* | *expression* is a string that does not hold a number (a zero-length string included) | +| [94 (`&H5E`)](tb:Errors#94), *Invalid use of Null* | *expression* is **Null** | + + + ### Example This example uses the **CLngPtr** function to convert an expression to a **LongPtr**. diff --git a/docs/Reference/Default/VBA/Conversion/CSng.md b/docs/Reference/Default/VBA/Conversion/CSng.md index 67f875032..635760bb9 100644 --- a/docs/Reference/Default/VBA/Conversion/CSng.md +++ b/docs/Reference/Default/VBA/Conversion/CSng.md @@ -16,10 +16,21 @@ Syntax: **CSng(** *expression* **)** *expression* : *required* Any valid string or numeric expression in the **Single** range --- `-3.402823E38` to `-1.401298E-45` for negative values, and `1.401298E-45` to `3.402823E38` for positive values. -The return type is **Single**. If *expression* is outside the range of a **Single**, a run-time error occurs. +The return type is **Single**. **CSng** is the internationally aware alternative to [**Val**](Val) for converting a string to a numeric type. + +### Common Errors + +| Error | When | +|---|---| +| [6 (`&H6`)](tb:Errors#6), *Overflow* | *expression* is less than -3.402823E38 or greater than 3.402823E38 | +| [13 (`&HD`)](tb:Errors#13), *Type mismatch* | *expression* is a string that does not hold a number (a zero-length string included) | +| [94 (`&H5E`)](tb:Errors#94), *Invalid use of Null* | *expression* is **Null** | + + + ### Example This example uses the **CSng** function to convert values to a **Single**. diff --git a/docs/Reference/Default/VBA/Conversion/CStr.md b/docs/Reference/Default/VBA/Conversion/CStr.md index c12692f61..5153eebee 100644 --- a/docs/Reference/Default/VBA/Conversion/CStr.md +++ b/docs/Reference/Default/VBA/Conversion/CStr.md @@ -24,7 +24,6 @@ The return type is **String**. The result depends on the type of *expression*: | **Date** | A string containing a date in the system's short date format. | | **Empty** | A zero-length string (`""`). | | **Error** | A string containing the word `Error` followed by the error number. | -| **Null** | A run-time error. | | Other numeric | A string containing the number. | **CStr** is the internationally aware alternative to [**Str**](Str) for converting a number to a string. **CStr** recognizes different decimal separators properly, depending on the system's locale setting. @@ -32,6 +31,15 @@ The return type is **String**. The result depends on the type of *expression*: > [!WARNING] BETA 1010 > A 64-bit build converts a negative zero of type **Single** or **Double** (a value that equals 0 and has its sign bit set) to `"-0"`. A 32-bit build and VB6 give `"0"`. The same happens when such a value is joined to a string with `&`. + +### Common Errors + +| Error | When | +|---|---| +| [94 (`&H5E`)](tb:Errors#94), *Invalid use of Null* | *expression* is **Null** | + + + ### Example This example uses the **CStr** function to convert a numeric value to a **String**. diff --git a/docs/Reference/Default/VBA/Conversion/CType.md b/docs/Reference/Default/VBA/Conversion/CType.md index e520be8d4..64005b488 100644 --- a/docs/Reference/Default/VBA/Conversion/CType.md +++ b/docs/Reference/Default/VBA/Conversion/CType.md @@ -36,6 +36,17 @@ The return type matches *type*. In both roles **CType** is an operator-like form recognized by the compiler; it isn't called like a regular function and the unparameterized name `CType` cannot be assigned to a function reference. + +### Common Errors + +| Error | When | +|---|---| +| [6 (`&H6`)](tb:Errors#6), *Overflow* | *value* is a number outside the range of *type* | +| [13 (`&HD`)](tb:Errors#13), *Type mismatch* | *value* cannot be converted to *type*, such as a string that does not hold a number converted to a numeric type | +| [94 (`&H5E`)](tb:Errors#94), *Invalid use of Null* | *value* is **Null** and *type* is not **Variant** | + + + ### See Also - [Enhanced Pointer Functionality](../../../Features/Language/Pointers#ctypeof-type) diff --git a/docs/Reference/Default/VBA/Conversion/CVDate.md b/docs/Reference/Default/VBA/Conversion/CVDate.md index a997c5652..15dcc3443 100644 --- a/docs/Reference/Default/VBA/Conversion/CVDate.md +++ b/docs/Reference/Default/VBA/Conversion/CVDate.md @@ -16,10 +16,19 @@ Syntax: **CVDate(** *expression* **)** *expression* : *required* Any expression that can be converted to a date --- a date literal, a date/time string, or a number that falls within the range of acceptable dates. -The return type is **Variant** (**Date**). An error occurs if *expression* cannot be converted to a date. +The return type is **Variant** (**Date**). If *expression* is **Null**, **CVDate** returns **Null**. **CVDate** is provided for compatibility with previous versions of Visual Basic. The syntax of **CVDate** is identical to [**CDate**](CDate); however, **CVDate** returns a **Variant** whose subtype is **Date** instead of an actual **Date** type. Since **Date** is now an intrinsic type, there is no further need for **CVDate** in new code. The same effect can be achieved by converting an expression to a **Date** with [**CDate**](CDate) and then assigning it to a **Variant**. + +### Common Errors + +| Error | When | +|---|---| +| [13 (`&HD`)](tb:Errors#13), *Type mismatch* | *expression* is a string that is not a date, a time or a number (a zero-length string included), or a number outside the range of a **Date**: less than -657,434 (January 1, 100) or 2,958,466 (January 1, 10000) or more | + + + ### Example ```tb check_build diff --git a/docs/Reference/Default/VBA/Conversion/CVErr.md b/docs/Reference/Default/VBA/Conversion/CVErr.md index 79960dafa..6824bd979 100644 --- a/docs/Reference/Default/VBA/Conversion/CVErr.md +++ b/docs/Reference/Default/VBA/Conversion/CVErr.md @@ -20,6 +20,16 @@ Use the **CVErr** function to create user-defined errors in user-created procedu Note that implicit conversion of an **Error** is not allowed. For example, the return value of **CVErr** cannot be directly assigned to a variable that is not a **Variant**. An explicit conversion (using [**CInt**](CInt), [**CDbl**](CDbl), and so on) of the value returned by **CVErr** can be assigned to a variable of the appropriate data type. + +### Common Errors + +| Error | When | +|---|---| +| [5 (`&H5`)](tb:Errors#5), *Invalid procedure call or argument* | *errornumber* rounds to a number less than 0 or greater than 65,535 | +| [94 (`&H5E`)](tb:Errors#94), *Invalid use of Null* | *errornumber* is **Null** | + + + ### Example This example uses the **CVErr** function to return a **Variant** whose **VarType** is **vbError** (10). The user-defined function `CalculateDouble` returns an error if the argument passed to it isn't a number. Use **CVErr** to return user-defined errors from user-defined procedures or to defer handling of a run-time error. Use the **IsError** function to test whether the value represents an error. diff --git a/docs/Reference/Default/VBA/Conversion/Error.md b/docs/Reference/Default/VBA/Conversion/Error.md index b571a9c69..55cc06a64 100644 --- a/docs/Reference/Default/VBA/Conversion/Error.md +++ b/docs/Reference/Default/VBA/Conversion/Error.md @@ -15,7 +15,7 @@ Syntax: - **Error** [ **(** *errornumber* **)** ] *errornumber* -: *optional* Any valid error number. If *errornumber* is a valid error number but is not defined, **Error** returns the string `"Application-defined or object-defined error"`. If *errornumber* is not valid, an error occurs. If *errornumber* is omitted, the message corresponding to the most recent run-time error is returned. If no run-time error has occurred, or *errornumber* is `0`, **Error** returns a zero-length string (`""`). +: *optional* Any valid error number. If *errornumber* is a valid error number but is not defined, **Error** returns the string `"Application-defined or object-defined error"`. If *errornumber* is omitted, the message corresponding to the most recent run-time error is returned. If no run-time error has occurred, or *errornumber* is `0`, **Error** returns a zero-length string (`""`). The `$`-suffixed form returns a **String**; the unsuffixed form returns a **Variant** (**String**). @@ -24,6 +24,18 @@ The `$`-suffixed form returns a **String**; the unsuffixed form returns a **Vari Examine the property settings of the **Err** object to identify the most recent run-time error. The return value of the **Error** function corresponds to the **Description** property of the **Err** object. + +### Common Errors + +| Error | When | +|---|---| +| [94 (`&H5E`)](tb:Errors#94), *Invalid use of Null* | *errornumber* is **Null** | + +> [!NOTE] BETA 1010 +> When **Error** or **Error$** is given an *errornumber* that rounds to a number greater than 65,535, VB6 and 64-bit VBA raise error [6 (`&H6`)](tb:Errors#6), *Overflow*; twinBASIC raises none. + + + ### Example This example uses the **Error** function to print error messages that correspond to the specified error numbers. diff --git a/docs/Reference/Default/VBA/Conversion/Hex.md b/docs/Reference/Default/VBA/Conversion/Hex.md index 210b72c4f..0e8a79a13 100644 --- a/docs/Reference/Default/VBA/Conversion/Hex.md +++ b/docs/Reference/Default/VBA/Conversion/Hex.md @@ -21,14 +21,36 @@ Syntax: The `$`-suffixed form returns a **String**; the unsuffixed form returns a **Variant** (**String**). -| If *number* is | Hex returns | -|---------------------------------|---------------------------------| -| -2,147,483,648 to 2,147,483,647 | Up to eight hexadecimal characters | -| **Null** | **Null** (unsuffixed form only) | -| **Empty** | Zero (`"0"`) | +*number* can be any number in the range of a **LongLong**. The result has no leading zeros. A negative number gives its two's-complement form at the width of its data type: + +| If *number* is | Hex returns | +|-------------------------------------------------|---------------------------------| +| Zero or a positive number | The digits of its value, up to 16 characters | +| A negative **Integer** | 4 characters | +| A negative **Long** | 8 characters | +| A negative **LongLong**, **Single**, **Double**, **Currency** or **Decimal**, or a string holding a negative number | 16 characters | +| **Null** | **Null** (unsuffixed form only) | +| **Empty** | Zero (`"0"`) | + +> [!NOTE] +> VB6's **Hex** takes only a number in the range of a **Long**, and raises error 6, *Overflow*, for any other. It gives a negative **Single**, **Double**, **Currency** or **Decimal**, or a string holding a negative number, the eight characters of a **Long**: `Hex(-1#)` is `"FFFFFFFF"` in VB6 and `"FFFFFFFFFFFFFFFF"` in twinBASIC. 64-bit VBA takes the range of a **LongLong** and gives the 16 characters, as twinBASIC does. + +> [!NOTE] +> A **Single** or **Double** of 2^62 (4,611,686,018,427,387,904) or more, or less than -2^62, raises error 6, *Overflow*, though its value fits a **LongLong**, as in 64-bit VBA. A **LongLong**, **Decimal** or string of the same value is converted; so is `Hex(CLngLng(x))`. For the opposite of **Hex**, precede a hexadecimal value with **&H**. For example, `Hex(255)` returns the string `"FF"` and `&HFF` returns the number 255. + +### Common Errors + +| Error | When | +|---|---| +| [6 (`&H6`)](tb:Errors#6), *Overflow* | *number* is outside the range of a **LongLong**, or is a **Single** or **Double** less than -4,611,686,018,427,387,904 or greater than 4,611,686,018,427,387,903 | +| [13 (`&HD`)](tb:Errors#13), *Type mismatch* | *number* is a string that does not hold a number (a zero-length string included) | +| [94 (`&H5E`)](tb:Errors#94), *Invalid use of Null* | **Hex$** is given a *number* that is **Null** | + + + ### Example This example uses the **Hex** function to return the hexadecimal value of a number. diff --git a/docs/Reference/Default/VBA/Conversion/MacID.md b/docs/Reference/Default/VBA/Conversion/MacID.md index ac7ac60c0..49ba038b3 100644 --- a/docs/Reference/Default/VBA/Conversion/MacID.md +++ b/docs/Reference/Default/VBA/Conversion/MacID.md @@ -28,3 +28,12 @@ Dir("SomePath", MacID("TEXT")) > [!NOTE] > twinBASIC currently targets Windows. **MacID** is provided for source compatibility with VBA code originally written for the Macintosh; on Windows, the value it returns has no special meaning to the file-system or shell functions. + + +## Common Errors + +| Error | When | +|---|---| +| [94 (`&H5E`)](tb:Errors#94), *Invalid use of Null* | *constant* is **Null** | + + diff --git a/docs/Reference/Default/VBA/Conversion/Oct.md b/docs/Reference/Default/VBA/Conversion/Oct.md index e52b61de6..59e4a6f71 100644 --- a/docs/Reference/Default/VBA/Conversion/Oct.md +++ b/docs/Reference/Default/VBA/Conversion/Oct.md @@ -21,14 +21,36 @@ Syntax: The `$`-suffixed form returns a **String**; the unsuffixed form returns a **Variant** (**String**). -| If *number* is | Oct returns | -|----------------|---------------------------------| -| **Null** | **Null** (unsuffixed form only) | -| **Empty** | Zero (`"0"`) | -| Any other number | Up to 11 octal characters | +*number* can be any number in the range of a **LongLong**. The result has no leading zeros. A negative number gives its two's-complement form at the width of its data type: + +| If *number* is | Oct returns | +|-------------------------------------------------|---------------------------------| +| Zero or a positive number | The digits of its value, up to 21 characters | +| A negative **Integer** | 6 characters | +| A negative **Long** | 11 characters | +| A negative **LongLong**, **Single**, **Double**, **Currency** or **Decimal**, or a string holding a negative number | 22 characters | +| **Null** | **Null** (unsuffixed form only) | +| **Empty** | Zero (`"0"`) | + +> [!NOTE] +> VB6's **Oct** takes only a number in the range of a **Long**, and raises error 6, *Overflow*, for any other. It gives a negative **Single**, **Double**, **Currency** or **Decimal**, or a string holding a negative number, the 11 characters of a **Long**: `Oct(-1#)` is `"37777777777"` in VB6 and `"1777777777777777777777"` in twinBASIC. 64-bit VBA takes the range of a **LongLong** and gives the 22 characters, as twinBASIC does. + +> [!NOTE] +> A **Single** or **Double** of 2^62 (4,611,686,018,427,387,904) or more, or less than -2^62, raises error 6, *Overflow*, though its value fits a **LongLong**, as in 64-bit VBA. A **LongLong**, **Decimal** or string of the same value is converted; so is `Oct(CLngLng(x))`. Octal numbers can be represented directly by preceding numbers in the proper range with `&O`. For example, `&O10` is the octal notation for decimal 8. + +### Common Errors + +| Error | When | +|---|---| +| [6 (`&H6`)](tb:Errors#6), *Overflow* | *number* is outside the range of a **LongLong**, or is a **Single** or **Double** less than -4,611,686,018,427,387,904 or greater than 4,611,686,018,427,387,903 | +| [13 (`&HD`)](tb:Errors#13), *Type mismatch* | *number* is a string that does not hold a number (a zero-length string included) | +| [94 (`&H5E`)](tb:Errors#94), *Invalid use of Null* | **Oct$** is given a *number* that is **Null** | + + + ### Example This example uses the **Oct** function to return the octal value of a number. diff --git a/docs/Reference/Default/VBA/Conversion/Str.md b/docs/Reference/Default/VBA/Conversion/Str.md index 8e87e5de8..ec9f7bef7 100644 --- a/docs/Reference/Default/VBA/Conversion/Str.md +++ b/docs/Reference/Default/VBA/Conversion/Str.md @@ -31,6 +31,15 @@ Use the [**Format**](../Strings/Format) function to convert numeric values to be > [!NOTE] > The **Str** function recognizes only the period (`.`) as a valid decimal separator. When different decimal separators may be used (for example, in international applications), use [**CStr**](CStr) to convert a number to a string. + +### Common Errors + +| Error | When | +|---|---| +| [94 (`&H5E`)](tb:Errors#94), *Invalid use of Null* | **Str$** is given a *number* that is **Null** | + + + ### Example This example uses the **Str** function to return a string representation of a number. When a number is converted to a string, a leading space is always reserved for its sign. diff --git a/docs/Reference/Default/VBA/Conversion/Val.md b/docs/Reference/Default/VBA/Conversion/Val.md index 351ab5be1..f6cfb7fa6 100644 --- a/docs/Reference/Default/VBA/Conversion/Val.md +++ b/docs/Reference/Default/VBA/Conversion/Val.md @@ -38,7 +38,21 @@ Val("&HFFFF") > The **Val** function recognizes only the period (`.`) as a valid decimal separator. When different decimal separators are used, as in international applications, use [**CDbl**](CDbl) instead to convert a string to a number. > [!NOTE] -> The **Val** function recognizes deprecated data type suffixes prior to conversion and may result in a type-mismatch error. For example, fifty percent represented as the string `"50%"` will convert as expected to `50`, but `Val("50.5%")` will raise an error because the percent symbol is interpreted as a suffix declaring the data type as **Integer**, which it is not in this case. The full list of data type suffixes is **Single** (`!`), **Currency** (`@`), **Double** (`#`), **String** (`$`), **Integer** (`%`), **Long** (`&`), and **LongLong** (`^`) for 64-bit hosts. +> The **Val** function reads a data type suffix after a number as the number's type. Fifty percent written as `"50%"` converts to `50`, but the `%` of `"50.5%"` declares an **Integer**, which `50.5` is not (see Common Errors below). The full list of data type suffixes is **Single** (`!`), **Currency** (`@`), **Double** (`#`), **String** (`$`), **Integer** (`%`), **Long** (`&`), and **LongLong** (`^`) for 64-bit hosts. + + +### Common Errors + +| Error | When | +|---|---| +| [6 (`&H6`)](tb:Errors#6), *Overflow* | *string* holds a number outside the range of a **Double** | +| [13 (`&HD`)](tb:Errors#13), *Type mismatch* | *string* holds a number with a fractional part followed by the **Integer** (`%`) or **Long** (`&`) type-declaration character, such as `"50.5%"` | +| [94 (`&H5E`)](tb:Errors#94), *Invalid use of Null* | *string* is **Null** | + +> [!NOTE] BETA 1010 +> When **Val** is given a *string* that holds a number followed by the **Integer** (`%`), **Long** (`&`) or **Currency** (`@`) type-declaration character, and the number is outside the range of that type, VB6 and 64-bit VBA raise error [6 (`&H6`)](tb:Errors#6), *Overflow*; twinBASIC raises none. + + ### Example diff --git a/docs/Reference/Default/VBA/Conversion/ValDec.md b/docs/Reference/Default/VBA/Conversion/ValDec.md index 602156e56..7a42d26f7 100644 --- a/docs/Reference/Default/VBA/Conversion/ValDec.md +++ b/docs/Reference/Default/VBA/Conversion/ValDec.md @@ -26,6 +26,16 @@ The **ValDec** function follows the same parsing rules as [**Val**](Val): it sto > [!NOTE] > **ValDec** recognizes only the period (`.`) as a valid decimal separator. When different decimal separators are used, as in international applications, use [**CDec**](CDec) instead. + +### Common Errors + +| Error | When | +|---|---| +| [6 (`&H6`)](tb:Errors#6), *Overflow* | *string* holds a number outside the range of a **Decimal** | +| [94 (`&H5E`)](tb:Errors#94), *Invalid use of Null* | *string* is **Null** | + + + ### Example This example uses the **ValDec** function to parse a string into a **Decimal**. diff --git a/docs/Reference/Default/VBA/DateTime/Calendar.md b/docs/Reference/Default/VBA/DateTime/Calendar.md index 2109098c0..1e1ba7d2d 100644 --- a/docs/Reference/Default/VBA/DateTime/Calendar.md +++ b/docs/Reference/Default/VBA/DateTime/Calendar.md @@ -23,6 +23,19 @@ Syntax: **Calendar** [ **=** *calendartype* ] The **Calendar** property can only be set programmatically. The setting of **Calendar** affects the string returned by the [**Date$**](Date#date-1) property when the calendar is set to Hijri. +> [!NOTE] BETA 1010 +> twinBASIC has not implemented **Calendar** yet. The compiler warns TB0009 at each use, **Calendar** always reads as **vbCalGreg**, and setting it raises an error, **vbCalGreg** included. + + +### Common Errors + +| Error | When | +|---|---| +| [-2147467259 (`&H80004005`)](tb:Errors#-2147467259), *Unspecified error* | **Calendar** is set to any value other than **Null**, **vbCalGreg** included | +| [94 (`&H5E`)](tb:Errors#94), *Invalid use of Null* | *calendartype* is **Null** | + + + ### Example This example sets the calendar type to Hijri. diff --git a/docs/Reference/Default/VBA/DateTime/Date.md b/docs/Reference/Default/VBA/DateTime/Date.md index b2c4fb296..03aecd280 100644 --- a/docs/Reference/Default/VBA/DateTime/Date.md +++ b/docs/Reference/Default/VBA/DateTime/Date.md @@ -48,7 +48,10 @@ Syntax: **Date** **=** *date* > [!IMPORTANT] > -> In some versions of Microsoft Windows, including Windows 10 and 11, setting the system date is a privileged operation that requires the process to have relevant permissions. Without those permissions, assignment to **Date** results in a Permission Denied runtime error. +> In some versions of Microsoft Windows, including Windows 10 and 11, setting the system date is a privileged operation that requires the process to have relevant permissions. + +> [!NOTE] +> twinBASIC also takes a number, or a string that holds one, and reads it as [**CDate**](../Conversion/CDate) does: `Date = 36526` sets January 1, 2000, and **Empty** sets December 30, 1899. As for a **Date** value, only the date part is set, and the time of day is kept. VB6 and 64-bit VBA raise error 13, *Type mismatch*, for a number. #### Example @@ -93,7 +96,7 @@ Syntax: **Date$** **=** *date* > [!IMPORTANT] > -> In some versions of Microsoft Windows, including Windows 10 and 11, setting the system date is a privileged operation that requires the process to have relevant permissions. Without those permissions, assignment to **Date**$ results in a Permission Denied runtime error. +> In some versions of Microsoft Windows, including Windows 10 and 11, setting the system date is a privileged operation that requires the process to have relevant permissions. #### Example @@ -105,6 +108,29 @@ MyDate = "02-12-1985" ' Assign a date to a variable. Date$ = MyDate ' Change the system date. ``` + +### Common Errors + +| Error | When | +|---|---| +| [13 (`&HD`)](tb:Errors#13), *Type mismatch* | **Date** is given a string that is not a date, or a number outside the range of dates | +| [-2147467259 (`&H80004005`)](tb:Errors#-2147467259), *Unspecified error* | **Date$** is given a value that is not a date of the form *mm-dd-yyyy*
**Date** or **Date$** is given a date before January 1, 1601, which Windows cannot set
**Date$** is given a date in another form VB6 reads, such as *m-d-yyyy*, *m/d/yyyy* or *mm-dd-yy* | +| [94 (`&H5E`)](tb:Errors#94), *Invalid use of Null* | **Date$** is given **Null**
**Date** is given **Null** | + +> [!NOTE] BETA 1010 +> When **Date$** is given a value that is not a date of the form *mm-dd-yyyy*, VB6 and 64-bit VBA raise error [5 (`&H5`)](tb:Errors#5), *Invalid procedure call or argument*, instead. + +> [!NOTE] BETA 1010 +> When **Date** or **Date$** is given a date before January 1, 1601, which Windows cannot set, VB6 and 64-bit VBA raise error [5 (`&H5`)](tb:Errors#5), *Invalid procedure call or argument*, instead. + +> [!NOTE] BETA 1010 +> When **Date$** is given a date in another form VB6 reads, such as *m-d-yyyy*, *m/d/yyyy* or *mm-dd-yy*, VB6 and 64-bit VBA raise no error. + +> [!NOTE] BETA 1010 +> When **Date** is given **Null**, VB6 and 64-bit VBA raise error [13 (`&HD`)](tb:Errors#13), *Type mismatch*, instead. + + + ### See Also - [Time](Time) property diff --git a/docs/Reference/Default/VBA/DateTime/DateAdd.md b/docs/Reference/Default/VBA/DateTime/DateAdd.md index 8a3a86de1..546534ca1 100644 --- a/docs/Reference/Default/VBA/DateTime/DateAdd.md +++ b/docs/Reference/Default/VBA/DateTime/DateAdd.md @@ -53,7 +53,8 @@ In this case, **DateAdd** returns 28-Feb-95, not 31-Feb-95. If *date* is 31-Jan- > [!WARNING] BETA 1010 > **DateAdd** with the "m", "q" or "yyyy" interval does not stop at the last day of a shorter month: it counts the extra days into the next month and raises no error. `DateAdd("m", 1, "31-Jan-95")` returns 3-Mar-95, `DateAdd("m", -1, #3/31/2026#)` returns 3-Mar-2026, and `DateAdd("yyyy", 1, #2/29/2024#)` returns 1-Mar-2025. VB6 returns 28-Feb-95, 28-Feb-2026 and 28-Feb-2025. -If the calculated date would precede the year 100, an error occurs. +> [!WARNING] BETA 1010 +> **DateAdd** with the "yyyy", "q" or "m" interval raises no error when the result would be before January 1, 100, and returns a date in the 1900s or 2000s instead: `DateAdd("yyyy", -1, #1/1/100#)` returns January 1, 1999, and `DateAdd("m", -1, #1/1/100#)` returns December 1, 1999. VB6 raises error 5. If *number* isn't a **Long** value, it is rounded to the nearest whole number before being evaluated. @@ -61,6 +62,24 @@ The format of the return value is determined by **Control Panel** settings, not If the [**Calendar**](Calendar) property setting is Gregorian, the supplied date must be Gregorian. If the calendar is Hijri, the supplied date must be Hijri. + +### Common Errors + +| Error | When | +|---|---| +| [5 (`&H5`)](tb:Errors#5), *Invalid procedure call or argument* | *interval* is not one of the settings listed
the result would be after December 31, 9999, or before January 1, 100 for an *interval* other than "yyyy", "q" or "m" | +| [6 (`&H6`)](tb:Errors#6), *Overflow* | *date* is a number outside the range of dates | +| [13 (`&HD`)](tb:Errors#13), *Type mismatch* | *date* is a string that is not a date | +| [94 (`&H5E`)](tb:Errors#94), *Invalid use of Null* | *interval* or *number* is **Null** | + +> [!NOTE] BETA 1010 +> When the result with an *interval* of "yyyy", "q" or "m" would be before January 1, 100, VB6 and 64-bit VBA raise error [5 (`&H5`)](tb:Errors#5), *Invalid procedure call or argument*; twinBASIC raises none. + +> [!NOTE] BETA 1010 +> When *date* is a number outside the range of dates, VB6 and 64-bit VBA raise error [13 (`&HD`)](tb:Errors#13), *Type mismatch*, instead. + + + ### Example This example takes a date and, using the **DateAdd** function, displays a corresponding date a specified number of months in the future. diff --git a/docs/Reference/Default/VBA/DateTime/DateDiff.md b/docs/Reference/Default/VBA/DateTime/DateDiff.md index fb855d01a..14030f20e 100644 --- a/docs/Reference/Default/VBA/DateTime/DateDiff.md +++ b/docs/Reference/Default/VBA/DateTime/DateDiff.md @@ -66,6 +66,9 @@ To calculate the number of days between *date1* and *date2*, use either Day of Y If *interval* is Week ("ww"), however, **DateDiff** returns the number of calendar weeks between the two dates. It counts the number of Sundays between *date1* and *date2*. **DateDiff** counts *date2* if it falls on a Sunday, but it doesn't count *date1*, even if it does fall on a Sunday. +> [!WARNING] BETA 1010 +> With the "ww" interval, **DateDiff** raises no error for a *firstdayofweek* outside 0 to 7, and what it returns follows no rule: a negative value acts as if taken modulo 7, 8 to 13 act as **vbSunday**, and a larger value matches no setting. VB6 raises error 5. + If *date1* refers to a later point in time than *date2*, the function returns a negative number. The *firstdayofweek* argument affects calculations that use the "w" and "ww" interval symbols. If *date1* or *date2* is a date literal, the specified year becomes a permanent part of that date. If *date1* or *date2* is enclosed in double quotation marks and the year is omitted, the current year is inserted each time the expression is evaluated. @@ -74,6 +77,24 @@ When comparing December 31 to January 1 of the immediately succeeding year, **Da If the [**Calendar**](Calendar) property setting is Gregorian, the supplied date must be Gregorian. If the calendar is Hijri, the supplied date must be Hijri. + +### Common Errors + +| Error | When | +|---|---| +| [5 (`&H5`)](tb:Errors#5), *Invalid procedure call or argument* | *interval* is not one of the settings listed | +| [6 (`&H6`)](tb:Errors#6), *Overflow* | *date1* or *date2* is a number outside the range of dates | +| [13 (`&HD`)](tb:Errors#13), *Type mismatch* | *date1* or *date2* is a string that is not a date | +| [94 (`&H5E`)](tb:Errors#94), *Invalid use of Null* | *interval*, *firstdayofweek* or *firstweekofyear* is **Null** | + +> [!NOTE] BETA 1010 +> When *interval* is "ww" and *firstdayofweek* is outside 0 to 7, VB6 and 64-bit VBA raise error [5 (`&H5`)](tb:Errors#5), *Invalid procedure call or argument*; twinBASIC raises none. + +> [!NOTE] BETA 1010 +> When *date1* or *date2* is a number outside the range of dates, VB6 and 64-bit VBA raise error [13 (`&HD`)](tb:Errors#13), *Type mismatch*, instead. + + + ### Example This example uses the **DateDiff** function to display the number of days between a given date and today. diff --git a/docs/Reference/Default/VBA/DateTime/DatePart.md b/docs/Reference/Default/VBA/DateTime/DatePart.md index 7f20dfbe0..5d152c3b8 100644 --- a/docs/Reference/Default/VBA/DateTime/DatePart.md +++ b/docs/Reference/Default/VBA/DateTime/DatePart.md @@ -64,10 +64,36 @@ Syntax: **DatePart** ( *interval*, *date* [, *firstdayofweek* [, *firstweekofyea The *firstdayofweek* argument affects calculations that use the "w" and "ww" interval symbols. +> [!WARNING] BETA 1010 +> With the "w" or "ww" interval, **DatePart** raises no error for a *firstdayofweek* outside 0 to 7, and what it returns follows no rule: a negative value acts as if taken modulo 7, 8 acts as **vbSunday**, and a larger value matches no setting. `DatePart("w", #1/1/2000#, 255)` returns -2. VB6 raises error 5. + +> [!WARNING] BETA 1010 +> With the "ww" interval, **DatePart** goes wrong for a date in the first days of year 100 whose week belongs to year 99. With **vbFirstFullWeek** it never returns, and the program stops responding: `DatePart("ww", #1/1/100#, vbSunday, vbFirstFullWeek)` is one such call. With **vbFirstFourDays** it returns a wrong number: -98980 for January 1, 100. VB6 returns 52 for both. + If *date* is a date literal, the specified year becomes a permanent part of that date. If *date* is enclosed in double quotation marks and the year is omitted, the current year is inserted each time the expression is evaluated. If the [**Calendar**](Calendar) property setting is Gregorian, the supplied date must be Gregorian. If the calendar is Hijri, the supplied date must be Hijri. The returned date part is in the time period units of the current calendar. + +### Common Errors + +| Error | When | +|---|---| +| [5 (`&H5`)](tb:Errors#5), *Invalid procedure call or argument* | *interval* is not one of the settings listed and *date* is not **Null**
*interval* is not one of the settings listed and *date* is **Null** | +| [13 (`&HD`)](tb:Errors#13), *Type mismatch* | *date* is a string that is not a date, or a number outside the range of dates | +| [94 (`&H5E`)](tb:Errors#94), *Invalid use of Null* | *interval*, *firstdayofweek* or *firstweekofyear* is **Null** | + +> [!NOTE] BETA 1010 +> When *interval* is not one of the settings listed and *date* is **Null**, VB6 and 64-bit VBA raise no error. + +> [!NOTE] BETA 1010 +> When *interval* is "w" or "ww" and *firstdayofweek* is outside 0 to 7, VB6 and 64-bit VBA raise error [5 (`&H5`)](tb:Errors#5), *Invalid procedure call or argument*; twinBASIC raises none. + +> [!NOTE] BETA 1010 +> When *interval* is "ww" and *firstweekofyear* is outside 0 to 3, VB6 and 64-bit VBA raise error [5 (`&H5`)](tb:Errors#5), *Invalid procedure call or argument*; twinBASIC raises none. + + + ### Example This example takes a date and, using the **DatePart** function, displays the quarter of the year in which it occurs. diff --git a/docs/Reference/Default/VBA/DateTime/DateSerial.md b/docs/Reference/Default/VBA/DateTime/DateSerial.md index 2f844ad2e..29b38d56e 100644 --- a/docs/Reference/Default/VBA/DateTime/DateSerial.md +++ b/docs/Reference/Default/VBA/DateTime/DateSerial.md @@ -26,12 +26,22 @@ To specify a date, such as December 31, 1991, the range of numbers for each **Da The following example uses numeric expressions instead of absolute date numbers. The **DateSerial** function returns a date that is the day before the first day (`1 - 1`), two months before August (`8 - 2`), 10 years before 1990 (`1990 - 10`) --- in other words, May 31, 1980. -When any argument exceeds the accepted range, it increments to the next larger unit as appropriate. For example, 35 days is evaluated as one month and some number of days, depending on where in the year it is applied. If any single argument is outside the range -32,768 to 32,767, an error occurs. If the date specified by the three arguments falls outside the acceptable range of dates, an error occurs. +When any argument exceeds the accepted range, it increments to the next larger unit as appropriate. For example, 35 days is evaluated as one month and some number of days, depending on where in the year it is applied. If any single argument is outside the range -32,768 to 32,767, an error occurs. Two-digit years for the *year* argument are interpreted based on user-defined machine settings. The default settings are that values between 0 and 29 are interpreted as the years 2000--2029, and values between 30 and 99 are interpreted as the years 1930--1999. For all other *year* arguments, use a four-digit year. If the [**Calendar**](Calendar) property setting is Gregorian, the supplied value is assumed to be Gregorian. If the setting is Hijri, the supplied value is assumed to be Hijri, and two-digit *year* values between 0 and 99 are interpreted as the years 1400--1499. + +### Common Errors + +| Error | When | +|---|---| +| [5 (`&H5`)](tb:Errors#5), *Invalid procedure call or argument* | the date is before January 1, 100 or after December 31, 9999 | +| [94 (`&H5E`)](tb:Errors#94), *Invalid use of Null* | *year*, *month* or *day* is **Null** | + + + ### Example This example uses the **DateSerial** function to return the date for the specified year, month, and day. diff --git a/docs/Reference/Default/VBA/DateTime/DateValue.md b/docs/Reference/Default/VBA/DateTime/DateValue.md index b9162d8ad..f3b49ea1f 100644 --- a/docs/Reference/Default/VBA/DateTime/DateValue.md +++ b/docs/Reference/Default/VBA/DateTime/DateValue.md @@ -20,10 +20,20 @@ If *date* is a string that includes only numbers separated by valid date separat If the year part of *date* is omitted, **DateValue** uses the current year from the system date. -If the *date* argument includes time information, **DateValue** doesn't return it. However, if *date* includes invalid time information (such as "89:98"), an error occurs. +If the *date* argument includes time information, **DateValue** doesn't return it. If the [**Calendar**](Calendar) property setting is Gregorian, the supplied date must be Gregorian. If the calendar is Hijri, the supplied date must be Hijri. + +### Common Errors + +| Error | When | +|---|---| +| [13 (`&HD`)](tb:Errors#13), *Type mismatch* | *date* is not a date: a string that is not one or holds an invalid time, a date after December 31, 9999, a number with a whole part, **Empty**, **True**, **False** or an **Error** value | +| [94 (`&H5E`)](tb:Errors#94), *Invalid use of Null* | *date* is **Null** | + + + ### Example This example uses the **DateValue** function to convert a string to a date. You can also use date literals to directly assign a date to a **Variant** or **Date** variable (for example, `MyDate = #2/12/69#`). diff --git a/docs/Reference/Default/VBA/DateTime/Day.md b/docs/Reference/Default/VBA/DateTime/Day.md index deeb89b44..a14b21176 100644 --- a/docs/Reference/Default/VBA/DateTime/Day.md +++ b/docs/Reference/Default/VBA/DateTime/Day.md @@ -19,6 +19,15 @@ Syntax: **Day** ( *date* ) > [!NOTE] > If the [**Calendar**](Calendar) property setting is Gregorian, the returned integer represents the Gregorian day of the month. If the calendar is Hijri, the returned integer represents the Hijri day of the month. + +### Common Errors + +| Error | When | +|---|---| +| [13 (`&HD`)](tb:Errors#13), *Type mismatch* | *date* is a string that is not a date, a number outside the range of dates, or an **Error** value | + + + ### Example This example uses the **Day** function to obtain the day of the month from a specified date. diff --git a/docs/Reference/Default/VBA/DateTime/Hour.md b/docs/Reference/Default/VBA/DateTime/Hour.md index 9220b0a1e..d7b2452fe 100644 --- a/docs/Reference/Default/VBA/DateTime/Hour.md +++ b/docs/Reference/Default/VBA/DateTime/Hour.md @@ -16,6 +16,15 @@ Syntax: **Hour** ( *time* ) *time* : *required* Any **Variant**, numeric expression, string expression, or any combination that can represent a time. If *time* contains **Null**, **Null** is returned. + +### Common Errors + +| Error | When | +|---|---| +| [13 (`&HD`)](tb:Errors#13), *Type mismatch* | *time* is a string that is not a time, a number outside the range of dates, or an **Error** value | + + + ### Example This example uses the **Hour** function to obtain the hour from a specified time. diff --git a/docs/Reference/Default/VBA/DateTime/Minute.md b/docs/Reference/Default/VBA/DateTime/Minute.md index 5c739c302..71693726a 100644 --- a/docs/Reference/Default/VBA/DateTime/Minute.md +++ b/docs/Reference/Default/VBA/DateTime/Minute.md @@ -16,6 +16,15 @@ Syntax: **Minute** ( *time* ) *time* : *required* Any **Variant**, numeric expression, string expression, or any combination that can represent a time. If *time* contains **Null**, **Null** is returned. + +### Common Errors + +| Error | When | +|---|---| +| [13 (`&HD`)](tb:Errors#13), *Type mismatch* | *time* is a string that is not a time, a number outside the range of dates, or an **Error** value | + + + ### Example This example uses the **Minute** function to obtain the minute of the hour from a specified time. diff --git a/docs/Reference/Default/VBA/DateTime/Month.md b/docs/Reference/Default/VBA/DateTime/Month.md index 6aa551744..d8ad48862 100644 --- a/docs/Reference/Default/VBA/DateTime/Month.md +++ b/docs/Reference/Default/VBA/DateTime/Month.md @@ -19,6 +19,15 @@ Syntax: **Month** ( *date* ) > [!NOTE] > If the [**Calendar**](Calendar) property setting is Gregorian, the returned integer represents the Gregorian month. If the calendar is Hijri, the returned integer represents the Hijri month. For Hijri dates, the argument can be any numeric expression that represents a date and/or time from 1/1/100 (Gregorian Aug 2, 718) through 4/3/9666 (Gregorian Dec 31, 9999). + +### Common Errors + +| Error | When | +|---|---| +| [13 (`&HD`)](tb:Errors#13), *Type mismatch* | *date* is a string that is not a date, a number outside the range of dates, or an **Error** value | + + + ### Example This example uses the **Month** function to obtain the month from a specified date. diff --git a/docs/Reference/Default/VBA/DateTime/Second.md b/docs/Reference/Default/VBA/DateTime/Second.md index c4df21b17..84c53c8ed 100644 --- a/docs/Reference/Default/VBA/DateTime/Second.md +++ b/docs/Reference/Default/VBA/DateTime/Second.md @@ -16,6 +16,15 @@ Syntax: **Second** ( *time* ) *time* : *required* Any **Variant**, numeric expression, string expression, or any combination that can represent a time. If *time* contains **Null**, **Null** is returned. + +### Common Errors + +| Error | When | +|---|---| +| [13 (`&HD`)](tb:Errors#13), *Type mismatch* | *time* is a string that is not a time, a number outside the range of dates, or an **Error** value | + + + ### Example This example uses the **Second** function to obtain the second of the minute from a specified time. diff --git a/docs/Reference/Default/VBA/DateTime/Time.md b/docs/Reference/Default/VBA/DateTime/Time.md index 710f7fa70..f51ba4fbc 100644 --- a/docs/Reference/Default/VBA/DateTime/Time.md +++ b/docs/Reference/Default/VBA/DateTime/Time.md @@ -45,11 +45,14 @@ Syntax: **Time** **=** *time* : *required* Any numeric expression, string expression, or any combination that can represent a time. -If *time* is a string, **Time** attempts to convert it to a time by using the time separators specified by the system. If it cannot be converted to a valid time, an error occurs. +If *time* is a string, **Time** attempts to convert it to a time by using the time separators specified by the system. > [!NOTE] > -> In some versions of Microsoft Windows, including Windows 10 and 11, setting the system time is a privileged operation that requires the process to have relevant permissions. Without those permissions, assignment to **Time** results in a Permission Denied runtime error. +> In some versions of Microsoft Windows, including Windows 10 and 11, setting the system time is a privileged operation that requires the process to have relevant permissions. + +> [!NOTE] +> twinBASIC takes a number as [**CDate**](../Conversion/CDate) converts it, and sets the time part of the result: `Time = 0.25` sets 6:00:00 AM, and so does `Time = 1.25`. VB6 and 64-bit VBA raise error 13, *Type mismatch*, for a number, though they take the same value as a **Date**, such as `CDate(0.25)`. #### Example @@ -93,7 +96,7 @@ Syntax: **Time$** **=** *time* > [!NOTE] > -> In some versions of Microsoft Windows, including Windows 10 and 11, setting the system time is a privileged operation that requires the process to have relevant permissions. Without those permissions, assignment to **Time$** results in a Permission Denied runtime error. +> In some versions of Microsoft Windows, including Windows 10 and 11, setting the system time is a privileged operation that requires the process to have relevant permissions. #### Example @@ -105,6 +108,26 @@ MyTime = "4:35:17 PM" ' Assign a time. Time$ = MyTime ' Set system time. ``` + +### Common Errors + +| Error | When | +|---|---| +| [13 (`&HD`)](tb:Errors#13), *Type mismatch* | **Time** is given a string that is not a time, or a number outside the range of dates | +| [-2147467259 (`&H80004005`)](tb:Errors#-2147467259), *Unspecified error* | **Time$** is given a value that is not a string of the form *hh:mm:ss*, or one outside 00:00:00 to 23:59:59
**Time$** is given a time in another form VB6 reads, such as *hh:mm* or *hh* | +| [94 (`&H5E`)](tb:Errors#94), *Invalid use of Null* | **Time$** is given **Null**
**Time** is given **Null** | + +> [!NOTE] BETA 1010 +> When **Time$** is given a value that is not a string of the form *hh:mm:ss*, or one outside 00:00:00 to 23:59:59, VB6 and 64-bit VBA raise error [5 (`&H5`)](tb:Errors#5), *Invalid procedure call or argument*, instead. + +> [!NOTE] BETA 1010 +> When **Time$** is given a time in another form VB6 reads, such as *hh:mm* or *hh*, VB6 and 64-bit VBA raise no error. + +> [!NOTE] BETA 1010 +> When **Time** is given **Null**, VB6 and 64-bit VBA raise error [13 (`&HD`)](tb:Errors#13), *Type mismatch*, instead. + + + ### See Also - [Date](Date) property diff --git a/docs/Reference/Default/VBA/DateTime/TimeSerial.md b/docs/Reference/Default/VBA/DateTime/TimeSerial.md index ce14d0355..039ad67ba 100644 --- a/docs/Reference/Default/VBA/DateTime/TimeSerial.md +++ b/docs/Reference/Default/VBA/DateTime/TimeSerial.md @@ -30,7 +30,20 @@ The following example uses expressions instead of absolute time numbers. The **T TimeSerial(12 - 6, -15, 0) ``` -When any argument exceeds the normal range for that argument, it increments to the next larger unit as appropriate. For example, 75 minutes is evaluated as one hour and 15 minutes. If any single argument is outside the range -32,768 to 32,767, an error occurs. If the time specified by the three arguments causes the date to fall outside the acceptable range of dates, an error occurs. +When any argument exceeds the normal range for that argument, it increments to the next larger unit as appropriate. For example, 75 minutes is evaluated as one hour and 15 minutes. If any single argument is outside the range -32,768 to 32,767, an error occurs. + + + + +### Common Errors + +| Error | When | +|---|---| +| [94 (`&H5E`)](tb:Errors#94), *Invalid use of Null* | *hour*, *minute* or *second* is **Null** | + + ### Example diff --git a/docs/Reference/Default/VBA/DateTime/TimeValue.md b/docs/Reference/Default/VBA/DateTime/TimeValue.md index 33b174091..346a2f873 100644 --- a/docs/Reference/Default/VBA/DateTime/TimeValue.md +++ b/docs/Reference/Default/VBA/DateTime/TimeValue.md @@ -14,11 +14,24 @@ Returns a **Variant** (**Date**) containing the time. Syntax: **TimeValue** ( *time* ) *time* -: *required* Normally a string expression representing a time from 0:00:00 (12:00:00 A.M.) to 23:59:59 (11:59:59 P.M.), inclusive. However, *time* can also be any expression that represents a time in that range. If *time* contains **Null**, **Null** is returned. +: *required* Normally a string expression representing a time from 0:00:00 (12:00:00 A.M.) to 23:59:59 (11:59:59 P.M.), inclusive. However, *time* can also be any expression that represents a time in that range. + Valid times can be entered using a 12-hour or 24-hour clock. For example, `"2:24PM"` and `"14:24"` are both valid *time* arguments. -If the *time* argument contains date information, **TimeValue** doesn't return it. However, if *time* includes invalid date information, an error occurs. +If the *time* argument contains date information, **TimeValue** doesn't return it. + + +### Common Errors + +| Error | When | +|---|---| +| [13 (`&HD`)](tb:Errors#13), *Type mismatch* | *time* is not a time: a string that is not one, a time outside 0:00:00 to 23:59:59, a string with an invalid date, a number outside the range of dates, **Empty**, **True** or an **Error** value | +| [94 (`&H5E`)](tb:Errors#94), *Invalid use of Null* | *time* is **Null** | + + ### Example diff --git a/docs/Reference/Default/VBA/DateTime/Weekday.md b/docs/Reference/Default/VBA/DateTime/Weekday.md index df943534d..d086b3fd1 100644 --- a/docs/Reference/Default/VBA/DateTime/Weekday.md +++ b/docs/Reference/Default/VBA/DateTime/Weekday.md @@ -46,6 +46,17 @@ The **Weekday** function can return any of these values: If the [**Calendar**](Calendar) property setting is Gregorian, the returned integer represents the Gregorian day of the week for the date argument. If the calendar is Hijri, the returned integer represents the Hijri day of the week for the date argument. + +### Common Errors + +| Error | When | +|---|---| +| [5 (`&H5`)](tb:Errors#5), *Invalid procedure call or argument* | *firstdayofweek* is outside 0 to 7 | +| [13 (`&HD`)](tb:Errors#13), *Type mismatch* | *date* is a string that is not a date, a number outside the range of dates, or an **Error** value | +| [94 (`&H5E`)](tb:Errors#94), *Invalid use of Null* | *firstdayofweek* is **Null** | + + + ### Example This example uses the **Weekday** function to obtain the day of the week from a specified date. diff --git a/docs/Reference/Default/VBA/DateTime/Year.md b/docs/Reference/Default/VBA/DateTime/Year.md index dc5e58a98..32853e914 100644 --- a/docs/Reference/Default/VBA/DateTime/Year.md +++ b/docs/Reference/Default/VBA/DateTime/Year.md @@ -19,6 +19,15 @@ Syntax: **Year** ( *date* ) > [!NOTE] > If the [**Calendar**](Calendar) property setting is Gregorian, the returned integer represents the Gregorian year for the date argument. If the calendar is Hijri, the returned integer represents the Hijri year for the date argument. + +### Common Errors + +| Error | When | +|---|---| +| [13 (`&HD`)](tb:Errors#13), *Type mismatch* | *date* is a string that is not a date, a number outside the range of dates, or an **Error** value | + + + ### Example This example uses the **Year** function to obtain the year from a specified date. diff --git a/docs/Reference/Default/VBA/DateTime/index.md b/docs/Reference/Default/VBA/DateTime/index.md index 51b4f8da1..5844bbd25 100644 --- a/docs/Reference/Default/VBA/DateTime/index.md +++ b/docs/Reference/Default/VBA/DateTime/index.md @@ -65,6 +65,9 @@ Debug.Print DateDiff("d", #1/1/2026#, #5/9/2026#) ' 128 The [**Calendar**](Calendar) property selects the calendar --- **vbCalGreg** (Gregorian, the default) or **vbCalHijri** (Hijri) --- used by the rest of the module. The setting controls how **Date$** formats the system date, how arguments to **DateSerial**, **DateValue**, **DateAdd**, and **DateDiff** are interpreted, and how the parts returned by **DatePart**, **Year**, **Month**, **Day**, and **Weekday** are reported. +> [!NOTE] BETA 1010 +> twinBASIC has not implemented **Calendar** yet: it always reads as **vbCalGreg**, and setting it raises an error. + ## Members - [Calendar](Calendar) -- returns or sets the calendar type (Gregorian or Hijri) diff --git a/docs/Reference/Default/VBA/Information/Array.md b/docs/Reference/Default/VBA/Information/Array.md index 4bf1d6b09..4407e416c 100644 --- a/docs/Reference/Default/VBA/Information/Array.md +++ b/docs/Reference/Default/VBA/Information/Array.md @@ -23,7 +23,7 @@ Syntax: : *required* (destructuring form) The variables to receive successive elements of *RhsArray*. Pass `_` to skip an element. *RhsArray* -: *required* (destructuring form) An array; non-array values raise an error. +: *required* (destructuring form) An array. The lower bound of an array created with **Array** is determined by the **Option Base** statement at the component scope, defaulting to `0`. @@ -52,6 +52,16 @@ Array(a, , b) = Array(1, 2, 3) > [!NOTE] > A **Variant** that is not declared as an array can still contain an array, and a **Variant** array can hold values of any type except fixed-length strings and user-defined types. Although a **Variant** containing an array is conceptually different from an array of **Variant** elements, indexing works the same way for both. + +### Common Errors + +| Error | When | +|---|---| +| [5 (`&H5`)](tb:Errors#5), *expected an array* | the destructuring form is given a right side that is not an array, **Null** included | +| [9 (`&H9`)](tb:Errors#9), *Subscript out of range* | the destructuring form is given an array with fewer elements than variables, or one not yet sized | + + + ### Example This example uses the **Array** function to return a **Variant** containing an array. diff --git a/docs/Reference/Default/VBA/Information/LBound.md b/docs/Reference/Default/VBA/Information/LBound.md index 51c89f6e9..0d11d52af 100644 --- a/docs/Reference/Default/VBA/Information/LBound.md +++ b/docs/Reference/Default/VBA/Information/LBound.md @@ -21,8 +21,8 @@ Syntax: **LBound(** *arrayname* [ **,** *dimension* ] **)** > [!IMPORTANT] > On a dynamic array that has never been sized --- one declared `Dim a() As Long` and -> not yet **ReDim**'d, or one **Erase** has released --- this raises run-time error 9, -> *Subscript out of range*, rather than returning anything. Test with +> not yet **ReDim**'d, or one **Erase** has released --- this raises an error (see +> Common Errors) rather than returning anything. Test with > [**IsArrayInitialized**](IsArrayInitialized) first. A **ParamArray** is always > initialized, so it needs no guard: with no arguments passed it reports bounds of 0 > and -1. @@ -39,6 +39,24 @@ For an array `Dim A(1 To 100, 0 To 3, -3 To 4)`, **LBound** returns: The default lower bound for any dimension is either 0 or 1, depending on the **Option Base** setting. Arrays created with the [**Array**](../../Core/Array) function are zero-based regardless of **Option Base**. Arrays whose dimensions are set with the **To** clause in **Dim**, **Private**, **Public**, **ReDim**, or **Static** can have any integer lower bound. + +### Common Errors + +| Error | When | +|---|---| +| [9 (`&H9`)](tb:Errors#9), *Subscript out of range* | *arrayname* is a dynamic array that has not been sized with **ReDim**, or that **Erase** has released | +| [-2147467259 (`&H80004005`)](tb:Errors#-2147467259), *Unspecified error* | *dimension* is less than 1 or greater than the number of dimensions of *arrayname*, after rounding
*dimension* is greater than 32,767 | +| [13 (`&HD`)](tb:Errors#13), *Type mismatch* | *arrayname* is a **Variant** that holds no array, **Empty** and **Null** included | +| [94 (`&H5E`)](tb:Errors#94), *Invalid use of Null* | *dimension* is **Null** | + +> [!NOTE] BETA 1010 +> When *dimension* is less than 1 or greater than the number of dimensions of *arrayname*, after rounding, VB6 and 64-bit VBA raise error [9 (`&H9`)](tb:Errors#9), *Subscript out of range*, instead. + +> [!NOTE] BETA 1010 +> When *dimension* is greater than 32,767, VB6 and 64-bit VBA raise error [6 (`&H6`)](tb:Errors#6), *Overflow*, instead. + + + ### Example This example uses **LBound** to return the smallest available subscript for the indicated dimension of an array. diff --git a/docs/Reference/Default/VBA/Information/QBColor.md b/docs/Reference/Default/VBA/Information/QBColor.md index 331077dc0..25297ca80 100644 --- a/docs/Reference/Default/VBA/Information/QBColor.md +++ b/docs/Reference/Default/VBA/Information/QBColor.md @@ -14,7 +14,7 @@ Returns a **Long** representing the RGB colour code corresponding to the specifi Syntax: **QBColor(** *color* **)** *color* -: *required* A whole number in the range 0--15. +: *required* An **Integer** in the range 0--15. The *color* argument has these settings: @@ -31,6 +31,16 @@ The *color* argument has these settings: The *color* argument represents colour values used by earlier versions of Basic --- Microsoft Visual Basic for MS-DOS and the QuickBASIC compiler. Starting with the least-significant byte, the returned value specifies the red, green, and blue components used to set the corresponding colour in the RGB system, exactly as if [**RGB**](RGB) had been called with those components. + +### Common Errors + +| Error | When | +|---|---| +| [5 (`&H5`)](tb:Errors#5), *Invalid procedure call or argument* | *color* is outside 0 to 15, after rounding | +| [94 (`&H5E`)](tb:Errors#94), *Invalid use of Null* | *color* is **Null** | + + + ### Example This example uses **QBColor** to set a form's background colour from a numeric colour code. diff --git a/docs/Reference/Default/VBA/Information/RGB.md b/docs/Reference/Default/VBA/Information/RGB.md index 3f2314de5..f043043b0 100644 --- a/docs/Reference/Default/VBA/Information/RGB.md +++ b/docs/Reference/Default/VBA/Information/RGB.md @@ -14,13 +14,13 @@ Returns a **Long** representing an RGB colour value. Syntax: **RGB(** *red* **,** *green* **,** *blue* **)** *red* -: *required* A number in the range 0--255 representing the red component of the colour. +: *required* An **Integer** in the range 0--255 representing the red component of the colour. *green* -: *required* A number in the range 0--255 representing the green component of the colour. +: *required* An **Integer** in the range 0--255 representing the green component of the colour. *blue* -: *required* A number in the range 0--255 representing the blue component of the colour. +: *required* An **Integer** in the range 0--255 representing the blue component of the colour. Application methods and properties that accept a colour specification expect a number representing an RGB colour value: the relative intensity of red, green, and blue that produces a particular shade. The value of any argument that exceeds 255 is treated as 255. @@ -37,6 +37,16 @@ The following table lists some standard colours and the red, green, and blue val | Yellow | 255 | 255 | 0 | | White | 255 | 255 | 255 | + +### Common Errors + +| Error | When | +|---|---| +| [5 (`&H5`)](tb:Errors#5), *Invalid procedure call or argument* | *red*, *green* or *blue* is negative, after rounding | +| [94 (`&H5E`)](tb:Errors#94), *Invalid use of Null* | *red*, *green* or *blue* is **Null** | + + + ### Example This example uses **RGB** to construct several colour values. diff --git a/docs/Reference/Default/VBA/Information/RGBA.md b/docs/Reference/Default/VBA/Information/RGBA.md index 154cf12c9..400612252 100644 --- a/docs/Reference/Default/VBA/Information/RGBA.md +++ b/docs/Reference/Default/VBA/Information/RGBA.md @@ -24,6 +24,16 @@ Syntax: **RGBA(** *red* **,** *green* **,** *blue* **,** *alpha* **)** **RGBA** extends [**RGB**](RGB) with an alpha channel for use with APIs that accept transparency. The component values can be read back with [**RGB_R**](RGB_R), [**RGB_G**](RGB_G), [**RGB_B**](RGB_B), and [**RGBA_A**](RGBA_A). + +### Common Errors + +| Error | When | +|---|---| +| [5 (`&H5`)](tb:Errors#5), *Invalid procedure call or argument* | *red*, *green*, *blue* or *alpha* is negative | +| [94 (`&H5E`)](tb:Errors#94), *Invalid use of Null* | *red*, *green*, *blue* or *alpha* is **Null** | + + + ### Example This example uses **RGBA** to build a semi-transparent red. diff --git a/docs/Reference/Default/VBA/Information/RGBA_A.md b/docs/Reference/Default/VBA/Information/RGBA_A.md index c2416b92c..112ff1cd1 100644 --- a/docs/Reference/Default/VBA/Information/RGBA_A.md +++ b/docs/Reference/Default/VBA/Information/RGBA_A.md @@ -15,6 +15,15 @@ Syntax: **RGBA_A(** *RGBA* **)** The return value is the alpha (opacity) component in the range 0--255: **0** is fully transparent and **255** is fully opaque. + +### Common Errors + +| Error | When | +|---|---| +| [94 (`&H5E`)](tb:Errors#94), *Invalid use of Null* | *RGBA* is **Null** | + + + ### Example This example extracts the alpha component from a colour built with **RGBA**. diff --git a/docs/Reference/Default/VBA/Information/RGB_B.md b/docs/Reference/Default/VBA/Information/RGB_B.md index 5c2ad86e7..6ea776a0e 100644 --- a/docs/Reference/Default/VBA/Information/RGB_B.md +++ b/docs/Reference/Default/VBA/Information/RGB_B.md @@ -15,6 +15,15 @@ Syntax: **RGB_B(** *RGBA* **)** The return value is the blue component in the range 0--255. + +### Common Errors + +| Error | When | +|---|---| +| [94 (`&H5E`)](tb:Errors#94), *Invalid use of Null* | *RGBA* is **Null** | + + + ### Example This example extracts the blue component from a colour built with **RGB**. diff --git a/docs/Reference/Default/VBA/Information/RGB_G.md b/docs/Reference/Default/VBA/Information/RGB_G.md index 986fea152..a56524f78 100644 --- a/docs/Reference/Default/VBA/Information/RGB_G.md +++ b/docs/Reference/Default/VBA/Information/RGB_G.md @@ -15,6 +15,15 @@ Syntax: **RGB_G(** *RGBA* **)** The return value is the green component in the range 0--255. + +### Common Errors + +| Error | When | +|---|---| +| [94 (`&H5E`)](tb:Errors#94), *Invalid use of Null* | *RGBA* is **Null** | + + + ### Example This example extracts the green component from a colour built with **RGB**. diff --git a/docs/Reference/Default/VBA/Information/RGB_R.md b/docs/Reference/Default/VBA/Information/RGB_R.md index 86ba5fbbb..847a2e7f0 100644 --- a/docs/Reference/Default/VBA/Information/RGB_R.md +++ b/docs/Reference/Default/VBA/Information/RGB_R.md @@ -15,6 +15,15 @@ Syntax: **RGB_R(** *RGBA* **)** The return value is the red component in the range 0--255. + +### Common Errors + +| Error | When | +|---|---| +| [94 (`&H5E`)](tb:Errors#94), *Invalid use of Null* | *RGBA* is **Null** | + + + ### Example This example extracts the red component from a colour built with **RGB**. diff --git a/docs/Reference/Default/VBA/Information/TranslateColor.md b/docs/Reference/Default/VBA/Information/TranslateColor.md index 296a223d5..b8f86f6bb 100644 --- a/docs/Reference/Default/VBA/Information/TranslateColor.md +++ b/docs/Reference/Default/VBA/Information/TranslateColor.md @@ -18,6 +18,16 @@ Syntax: **TranslateColor(** *ColorValue* [ **,** *hPalettePtr* ] **)** The OLE colour format encodes either a literal RGB value or an index into the Windows system palette (such as `&H80000012`, the colour of button text). **TranslateColor** returns the corresponding plain RGB **Long**, suitable for passing to APIs and properties that expect a true colour value rather than a system-palette reference. + +### Common Errors + +| Error | When | +|---|---| +| [5 (`&H5`)](tb:Errors#5), *Invalid procedure call or argument* | the high byte of *ColorValue* is not &H00, &H01, &H02 or &H80
*ColorValue* is a system colour (&H80000000 and up) with an index above 30 (&H1E) | +| [94 (`&H5E`)](tb:Errors#94), *Invalid use of Null* | *ColorValue* is **Null** | + + + ### Example This example translates an OLE system colour to its current RGB value. diff --git a/docs/Reference/Default/VBA/Information/UBound.md b/docs/Reference/Default/VBA/Information/UBound.md index db7e0bd2f..bd13b8fc4 100644 --- a/docs/Reference/Default/VBA/Information/UBound.md +++ b/docs/Reference/Default/VBA/Information/UBound.md @@ -23,8 +23,8 @@ Syntax: **UBound(** *arrayname* [ **,** *dimension* ] **)** > [!IMPORTANT] > On a dynamic array that has never been sized --- one declared `Dim a() As Long` and -> not yet **ReDim**'d, or one **Erase** has released --- this raises run-time error 9, -> *Subscript out of range*, rather than returning anything. Test with +> not yet **ReDim**'d, or one **Erase** has released --- this raises an error (see +> Common Errors) rather than returning anything. Test with > [**IsArrayInitialized**](IsArrayInitialized) first. A **ParamArray** is always > initialized, so it needs no guard: with no arguments passed it reports bounds of 0 > and -1. @@ -37,6 +37,24 @@ For an array `Dim A(1 To 100, 0 To 3, -3 To 4)`, **UBound** returns: | `UBound(A, 2)` | 3 | | `UBound(A, 3)` | 4 | + +### Common Errors + +| Error | When | +|---|---| +| [9 (`&H9`)](tb:Errors#9), *Subscript out of range* | *arrayname* is a dynamic array that has not been sized with **ReDim**, or that **Erase** has released | +| [-2147467259 (`&H80004005`)](tb:Errors#-2147467259), *Unspecified error* | *dimension* is less than 1 or greater than the number of dimensions of *arrayname*, after rounding
*dimension* is greater than 32,767 | +| [13 (`&HD`)](tb:Errors#13), *Type mismatch* | *arrayname* is a **Variant** that holds no array, **Empty** and **Null** included | +| [94 (`&H5E`)](tb:Errors#94), *Invalid use of Null* | *dimension* is **Null** | + +> [!NOTE] BETA 1010 +> When *dimension* is less than 1 or greater than the number of dimensions of *arrayname*, after rounding, VB6 and 64-bit VBA raise error [9 (`&H9`)](tb:Errors#9), *Subscript out of range*, instead. + +> [!NOTE] BETA 1010 +> When *dimension* is greater than 32,767, VB6 and 64-bit VBA raise error [6 (`&H6`)](tb:Errors#6), *Overflow*, instead. + + + ### Example This example uses **UBound** to return the largest available subscript for the indicated dimension of an array. diff --git a/docs/Reference/Default/VBA/Interaction/CallByDispId.md b/docs/Reference/Default/VBA/Interaction/CallByDispId.md index da09bd699..70d8bad9f 100644 --- a/docs/Reference/Default/VBA/Interaction/CallByDispId.md +++ b/docs/Reference/Default/VBA/Interaction/CallByDispId.md @@ -26,6 +26,18 @@ The return value is a **Variant** containing whatever the call returned. For met **CallByDispId** skips the name lookup that [**CallByName**](CallByName) performs, which is useful in two situations: when the dispatch ID is already known and the cost of a `GetIDsOfNames` round trip should be avoided, and when the target member is not exposed by name (e.g. a default member with `DISPID_VALUE = 0`, an explicit-DISPID extension, or a hidden/restricted member). + +### Common Errors + +| Error | When | +|---|---| +| [438 (`&H1B6`)](tb:Errors#438), *Object doesn't support this property or method* | *object* has no member with the dispatch ID *dispid* | +| [-2147467259 (`&H80004005`)](tb:Errors#-2147467259), *Unspecified error* | *calltype* is not one of the **VbCallType** values, or *object* is **Nothing** | +| [-2147352562 (`&H8002000E`)](tb:Errors#-2147352562), *Invalid number of parameters.* | the number of *args* is not the number the member takes | +| [94 (`&H5E`)](tb:Errors#94), *Invalid use of Null* | *calltype* is **Null** | + + + ### Example This example invokes the default member of an object --- `DISPID_VALUE`, defined as 0 --- by dispatch ID. diff --git a/docs/Reference/Default/VBA/Interaction/CallByName.md b/docs/Reference/Default/VBA/Interaction/CallByName.md index f9ce652b4..4cd382ae3 100644 --- a/docs/Reference/Default/VBA/Interaction/CallByName.md +++ b/docs/Reference/Default/VBA/Interaction/CallByName.md @@ -30,6 +30,41 @@ The return value is a **Variant** containing whatever the call returned. For met > [!NOTE] BETA 1010 > When the member called raises an error itself with a constant number, as in `Err.Raise 380`, or with the [**Error**](../../Core/Error) statement, **CallByName** raises error -2147352567 (`&H80020009`), *Exception occurred.*, not that number. VB6 raises the member's own number. A number the member passes in a variable, as in `Err.Raise Number`, and a run-time error such as a division by zero arrive unchanged. + +### Common Errors + +| Error | When | +|---|---| +| [438 (`&H1B6`)](tb:Errors#438), *Object doesn't support this property or method* | *object* has no member named *procname*, or none of the kind *calltype* names
*procname* is a zero-length string | +| [-2147467259 (`&H80004005`)](tb:Errors#-2147467259), *Unspecified error* | *calltype* is not one of the **VbCallType** values (3, **vbMethod** + **vbGet**, is accepted)
*object* is **Nothing** | +| [-2147352562 (`&H8002000E`)](tb:Errors#-2147352562), *Invalid number of parameters.* | the number of *args* is not the number the member takes, or a read-only property is assigned | +| [-2147352559 (`&H80020011`)](tb:Errors#-2147352559), *Does not support a collection.* | *args* are given to a property that takes none | +| [-2147352567 (`&H80020009`)](tb:Errors#-2147352567), *Exception occurred.* | the member raises an error of its own, such as **Add** of a **Dictionary** given a key it holds
the member raises an error of its own, such as a **Dictionary**'s **CompareMode** set when it has items | +| [94 (`&H5E`)](tb:Errors#94), *Invalid use of Null* | *procname* or *calltype* is **Null** | + +> [!NOTE] BETA 1010 +> When *procname* is a zero-length string, VB6 and 64-bit VBA raise error [450 (`&H1C2`)](tb:Errors#450), *Wrong number of arguments or invalid property assignment*, instead. + +> [!NOTE] BETA 1010 +> When *calltype* is not one of the **VbCallType** values (3, **vbMethod** + **vbGet**, is accepted), VB6 and 64-bit VBA raise error [438 (`&H1B6`)](tb:Errors#438), *Object doesn't support this property or method*, instead. + +> [!NOTE] BETA 1010 +> When *object* is **Nothing**, VB6 and 64-bit VBA raise error [5 (`&H5`)](tb:Errors#5), *Invalid procedure call or argument*, instead. + +> [!NOTE] BETA 1010 +> When the number of *args* is not the number the member takes, or a read-only property is assigned, VB6 and 64-bit VBA raise error [450 (`&H1C2`)](tb:Errors#450), *Wrong number of arguments or invalid property assignment*, instead. + +> [!NOTE] BETA 1010 +> When *args* are given to a property that takes none, VB6 and 64-bit VBA raise error [451 (`&H1C3`)](tb:Errors#451), *Property let procedure not defined and property get procedure did not return an object*, instead. + +> [!NOTE] BETA 1010 +> When the member raises an error of its own, such as **Add** of a **Dictionary** given a key it holds, VB6 and 64-bit VBA raise error [457 (`&H1C9`)](tb:Errors#457), *This key is already associated with an element of this collection*, instead. + +> [!NOTE] BETA 1010 +> When the member raises an error of its own, such as a **Dictionary**'s **CompareMode** set when it has items, VB6 and 64-bit VBA raise error [5 (`&H5`)](tb:Errors#5), *Invalid procedure call or argument*, instead. + + + ### Example These three calls use **CallByName** to operate on a control by name. The first sets its **MousePointer** property to the crosshair cursor, the second reads the same property back out, and the third invokes the **Move** method to reposition the control. diff --git a/docs/Reference/Default/VBA/Interaction/Choose.md b/docs/Reference/Default/VBA/Interaction/Choose.md index 2bf9020eb..e616844be 100644 --- a/docs/Reference/Default/VBA/Interaction/Choose.md +++ b/docs/Reference/Default/VBA/Interaction/Choose.md @@ -21,9 +21,22 @@ Syntax: **Choose(** *index* **,** *choice-1* [ **,** *choice-2* **, ...** [ **,* If *index* is 1, **Choose** returns *choice-1*; if *index* is 2, it returns *choice-2*; and so on. If *index* is less than 1 or greater than the number of choices listed, **Choose** returns **Null**. Non-integer values of *index* are rounded to the nearest whole number before being evaluated. +> [!NOTE] +> An *index* outside the range of a **Single** raises error 6, *Overflow*, in twinBASIC, as in 64-bit VBA. VB6 returns **Null** for it. + > [!NOTE] > **Choose** evaluates *every* choice in the list, not only the one it returns. Watch for side effects: a [**MsgBox**](MsgBox) call inside any of the choices is invoked once per choice, not just for the selected one. To avoid this --- for example, when one of the branches would error out --- use the short-circuiting [**If**](If) function instead. + +### Common Errors + +| Error | When | +|---|---| +| [94 (`&H5E`)](tb:Errors#94), *Invalid use of Null* | *index* is **Null** | +| [6 (`&H6`)](tb:Errors#6), *Overflow* | *index* is outside the range of a **Single** | + + + ### Example This example uses **Choose** to map a 1-based option index to a name. diff --git a/docs/Reference/Default/VBA/Interaction/CreateObject.md b/docs/Reference/Default/VBA/Interaction/CreateObject.md index 716b6b27a..67de0caa5 100644 --- a/docs/Reference/Default/VBA/Interaction/CreateObject.md +++ b/docs/Reference/Default/VBA/Interaction/CreateObject.md @@ -28,11 +28,37 @@ Set ExcelApp = CreateObject("Excel.Application") ExcelApp.Visible = True ``` -If a remote *servername* is supplied but the remote machine doesn't exist or is unreachable, a run-time error occurs. If an object has registered itself as single-instance, only one instance is ever created, no matter how many times **CreateObject** is invoked. +If an object has registered itself as single-instance, only one instance is ever created, no matter how many times **CreateObject** is invoked. > [!NOTE] > **CreateObject** obtains a new instance of the object. [**GetObject**](GetObject) attaches to an *already-running* instance --- or starts the object's application with a particular file loaded. + +### Common Errors + +| Error | When | +|---|---| +| [-2147221020 (`&H800401E4`)](tb:Errors#-2147221020), *Invalid syntax* | *class* is not a registered class name, such as a number, or a CLSID in braces without `new:` | +| [-2147221164 (`&H80040154`)](tb:Errors#-2147221164), *Class not registered* | *class* is `new:` with a CLSID that is not registered | +| [-2147221005 (`&H800401F3`)](tb:Errors#-2147221005), *Invalid class string* | *class* is `new:` followed by text that is not a CLSID | +| [5 (`&H5`)](tb:Errors#5), *Invalid procedure call or argument* | *class* is a zero-length string | +| [-2147023174 (`&H800706BA`)](tb:Errors#-2147023174), *The RPC server is unavailable.* | *servername* names a computer that cannot be reached | +| [94 (`&H5E`)](tb:Errors#94), *Invalid use of Null* | *class* or *servername* is **Null** | + +> [!NOTE] BETA 1010 +> When *class* is not a registered class name, such as a number, or a CLSID in braces without `new:`, VB6 and 64-bit VBA raise error [429 (`&H1AD`)](tb:Errors#429), *ActiveX component can't create object*, instead. + +> [!NOTE] BETA 1010 +> When *class* is `new:` with a CLSID that is not registered, VB6 and 64-bit VBA raise error [429 (`&H1AD`)](tb:Errors#429), *ActiveX component can't create object*, instead. + +> [!NOTE] BETA 1010 +> When *class* is `new:` followed by text that is not a CLSID, VB6 and 64-bit VBA raise error [429 (`&H1AD`)](tb:Errors#429), *ActiveX component can't create object*, instead. + +> [!NOTE] BETA 1010 +> When *class* is a zero-length string, VB6 and 64-bit VBA raise error [429 (`&H1AD`)](tb:Errors#429), *ActiveX component can't create object*, instead. + + + ### Example This example creates a Microsoft Excel **Application** object, makes it visible, and then closes it via **Quit**, releasing the reference at the end. diff --git a/docs/Reference/Default/VBA/Interaction/DeleteSetting.md b/docs/Reference/Default/VBA/Interaction/DeleteSetting.md index 2f84c4d83..6993bbb7a 100644 --- a/docs/Reference/Default/VBA/Interaction/DeleteSetting.md +++ b/docs/Reference/Default/VBA/Interaction/DeleteSetting.md @@ -25,10 +25,22 @@ Syntax: **DeleteSetting** *appname*, *section*, [ *key* ] : *optional* The name of the key to delete within the specified *section*. If omitted, the entire **section** and all keys within are deleted. -A run-time error occurs when **DeleteSetting** is called for a non-existent *appname*, *section*, or *key*. The root of these registry settings is: `Computer\HKEY_CURRENT_USER\Software\VB and VBA Program Settings`. + +## Common Errors + +| Error | When | +|---|---| +| [-2147467259 (`&H80004005`)](tb:Errors#-2147467259), *Unspecified error* | the application, section or key named does not exist, or *appname* or *section* is a zero-length string | +| [94 (`&H5E`)](tb:Errors#94), *Invalid use of Null* | an argument is **Null** | + +> [!NOTE] BETA 1010 +> When the application, section or key named does not exist, or *appname* or *section* is a zero-length string, VB6 and 64-bit VBA raise error [5 (`&H5`)](tb:Errors#5), *Invalid procedure call or argument*, instead. + + + ## Example The following example first uses the [**SaveSetting**](SaveSetting) statement to make entries in the Windows registry for the application, and then uses the **DeleteSetting** statement to remove them. Because no *key* argument is specified, the whole section is deleted, including the section name and all its keys. diff --git a/docs/Reference/Default/VBA/Interaction/Environ.md b/docs/Reference/Default/VBA/Interaction/Environ.md index 5acb796f0..bb0ba75ec 100644 --- a/docs/Reference/Default/VBA/Interaction/Environ.md +++ b/docs/Reference/Default/VBA/Interaction/Environ.md @@ -26,7 +26,25 @@ The `$`-suffixed forms return a **String**; the unsuffixed forms return a **Vari When called with *envstring*, **Environ** returns the value assigned to that environment variable --- that is, the text following the equal sign (`=`) in the environment-string table for that variable. If *envstring* can't be found in the table, a zero-length string (`""`) is returned. -When called with *number*, **Environ** returns the entire entry at that position, including the variable name, the equal sign, and the value (e.g. `"PATH=C:\Windows;C:\Windows\System32"`). If there is no entry at that position, a zero-length string is returned. +When called with *number*, **Environ** returns the entire entry at that position, including the variable name, the equal sign, and the value (e.g. `"PATH=C:\Windows;C:\Windows\System32"`). + + + + +### Common Errors + +| Error | When | +|---|---| +| [-2147467259 (`&H80004005`)](tb:Errors#-2147467259), *Unspecified error* | *number* is less than 1 after rounding (**Empty** and **True** included), no entry has that position, or *envstring* is a zero-length string
*number* is greater than 32,767 | +| [94 (`&H5E`)](tb:Errors#94), *Invalid use of Null* | *envstring* or *number* is **Null** | + +> [!NOTE] BETA 1010 +> When *number* is less than 1 after rounding (**Empty** and **True** included), no entry has that position, or *envstring* is a zero-length string, VB6 and 64-bit VBA raise error [5 (`&H5`)](tb:Errors#5), *Invalid procedure call or argument*, instead. + +> [!NOTE] BETA 1010 +> When *number* is greater than 32,767, VB6 and 64-bit VBA raise error [6 (`&H6`)](tb:Errors#6), *Overflow*, instead. + + ### Example diff --git a/docs/Reference/Default/VBA/Interaction/GetAllSettings.md b/docs/Reference/Default/VBA/Interaction/GetAllSettings.md index eab213647..2fc65696d 100644 --- a/docs/Reference/Default/VBA/Interaction/GetAllSettings.md +++ b/docs/Reference/Default/VBA/Interaction/GetAllSettings.md @@ -23,6 +23,19 @@ Returns a **Variant** whose contents are a two-dimensional array of strings: eac The root of these registry settings is: `Computer\HKEY_CURRENT_USER\Software\VB and VBA Program Settings`. + +### Common Errors + +| Error | When | +|---|---| +| [-2147467259 (`&H80004005`)](tb:Errors#-2147467259), *Unspecified error* | *appname* or *section* is a zero-length string | +| [94 (`&H5E`)](tb:Errors#94), *Invalid use of Null* | *appname* or *section* is **Null** | + +> [!NOTE] BETA 1010 +> When *appname* or *section* is a zero-length string, VB6 and 64-bit VBA raise error [5 (`&H5`)](tb:Errors#5), *Invalid procedure call or argument*, instead. + + + ### Example This example first uses [**SaveSetting**](SaveSetting) to make entries in the Windows registry for the application, then uses **GetAllSettings** to display every key/value in a section, and finally uses [**DeleteSetting**](DeleteSetting) to remove the application's entries. Note that the *appname* and *section* names themselves are not retrieved. diff --git a/docs/Reference/Default/VBA/Interaction/GetObject.md b/docs/Reference/Default/VBA/Interaction/GetObject.md index 86f075baf..d9fa62ce6 100644 --- a/docs/Reference/Default/VBA/Interaction/GetObject.md +++ b/docs/Reference/Default/VBA/Interaction/GetObject.md @@ -28,7 +28,7 @@ Set CADObject = GetObject("C:\CAD\SCHEMA.CAD") When the call is made with a *pathname*, the application registered for that file is started (if it isn't already running), and the object inside the file is activated. -If *pathname* is a zero-length string (`""`), **GetObject** returns a *new* instance of the type named by *class*. If *pathname* is omitted altogether, **GetObject** attempts to attach to a *currently running* instance of the type named by *class*; if no such instance exists, a run-time error occurs. +If *pathname* is a zero-length string (`""`), **GetObject** returns a *new* instance of the type named by *class*. If *pathname* is omitted altogether, **GetObject** attempts to attach to a *currently running* instance of the type named by *class*. Some applications support activating a *part* of a file. Append `!` and an application-specific identifier to the file name --- for example, the third layer of a CAD drawing: @@ -49,6 +49,37 @@ Set MyObject = GetObject("C:\Drawings\Sample.drw", "Figment.Drawing") For an object registered as single-instance, **GetObject** with the zero-length-string syntax always returns the same instance, and the form with *pathname* omitted causes an error. + +### Common Errors + +| Error | When | +|---|---| +| [-2147221014 (`&H800401EA`)](tb:Errors#-2147221014), *Moniker cannot open file* | *pathname* names a file that does not exist, and *class* is omitted | +| [-2147467262 (`&H80004002`)](tb:Errors#-2147467262), *No such interface supported* | *pathname* names a file that does not exist, and *class* is given | +| [-2147221005 (`&H800401F3`)](tb:Errors#-2147221005), *Invalid class string* | *class* is not a registered class name | +| [-2147221021 (`&H800401E3`)](tb:Errors#-2147221021), *Operation unavailable* | *pathname* is omitted and no instance of *class* is running | +| [-2147467259 (`&H80004005`)](tb:Errors#-2147467259), *Unspecified error* | both arguments are omitted | +| [5 (`&H5`)](tb:Errors#5), *Invalid procedure call or argument* | *pathname* is a zero-length string and *class* is omitted | +| [-2147221020 (`&H800401E4`)](tb:Errors#-2147221020), *Invalid syntax* | *pathname* is neither a file name nor a name Windows can bind to an object | +| [94 (`&H5E`)](tb:Errors#94), *Invalid use of Null* | *pathname* or *class* is **Null** | + +> [!NOTE] BETA 1010 +> When *pathname* names a file that does not exist, and *class* is omitted, VB6 and 64-bit VBA raise error [432 (`&H1B0`)](tb:Errors#432), *File name or class name not found during Automation operation*, instead. + +> [!NOTE] BETA 1010 +> When *pathname* names a file that does not exist, and *class* is given, VB6 and 64-bit VBA raise error [432 (`&H1B0`)](tb:Errors#432), *File name or class name not found during Automation operation*, instead. + +> [!NOTE] BETA 1010 +> When *class* is not a registered class name, VB6 and 64-bit VBA raise error [429 (`&H1AD`)](tb:Errors#429), *ActiveX component can't create object*, instead. + +> [!NOTE] BETA 1010 +> When *pathname* is omitted and no instance of *class* is running, VB6 and 64-bit VBA raise error [429 (`&H1AD`)](tb:Errors#429), *ActiveX component can't create object*, instead. + +> [!NOTE] BETA 1010 +> When both arguments are omitted, VB6 and 64-bit VBA raise error [13 (`&HD`)](tb:Errors#13), *Type mismatch*, instead. + + + ### Example This example uses **GetObject** to attach to a Microsoft Excel **Worksheet** opened from a file. The first call (without *pathname*) tries to attach to a running Excel; the second call opens the file. If Excel was not already running when the script started, it is closed at the end via **Application.Quit**. diff --git a/docs/Reference/Default/VBA/Interaction/GetSetting.md b/docs/Reference/Default/VBA/Interaction/GetSetting.md index cba8ddfc6..1f456d6dd 100644 --- a/docs/Reference/Default/VBA/Interaction/GetSetting.md +++ b/docs/Reference/Default/VBA/Interaction/GetSetting.md @@ -34,6 +34,19 @@ If any of the items named in the **GetSetting** arguments don't exist, **GetSett The root of these registry settings is: `Computer\HKEY_CURRENT_USER\Software\VB and VBA Program Settings`. + +### Common Errors + +| Error | When | +|---|---| +| [-2147467259 (`&H80004005`)](tb:Errors#-2147467259), *Unspecified error* | *appname*, *section* or *key* is a zero-length string | +| [94 (`&H5E`)](tb:Errors#94), *Invalid use of Null* | an argument is **Null** | + +> [!NOTE] BETA 1010 +> When *appname*, *section* or *key* is a zero-length string, VB6 and 64-bit VBA raise error [5 (`&H5`)](tb:Errors#5), *Invalid procedure call or argument*, instead. + + + ### Example This example first uses the [**SaveSetting**](SaveSetting) statement to make entries in the Windows registry for the application specified as *appname*, and then uses the **GetSetting** function to display one of the settings. Because the *default* argument is specified, some value is guaranteed to be returned. Note that *section* names can't be retrieved with **GetSetting**. Finally, the [**DeleteSetting**](DeleteSetting) statement removes all the application's entries. diff --git a/docs/Reference/Default/VBA/Interaction/IIf.md b/docs/Reference/Default/VBA/Interaction/IIf.md index d580b00cd..2f7d28602 100644 --- a/docs/Reference/Default/VBA/Interaction/IIf.md +++ b/docs/Reference/Default/VBA/Interaction/IIf.md @@ -25,6 +25,15 @@ Syntax: **IIf(** *expr* **,** *truepart* **,** *falsepart* **)** > [!IMPORTANT] > **IIf** always evaluates both *truepart* and *falsepart*, even though it returns only one of them. Watch for side effects: if the unused branch would raise an error (for example, division by zero), the error still occurs. Use the short-circuiting [**If**](If) function --- a twinBASIC addition --- to guard against errors in the unused branch. + +### Common Errors + +| Error | When | +|---|---| +| [13 (`&HD`)](tb:Errors#13), *Type mismatch* | *expr* is a string that is not a **Boolean** or a number | + + + ### Example This example uses **IIf** to return the word "Large" if the amount is greater than 1000, and "Small" otherwise. diff --git a/docs/Reference/Default/VBA/Interaction/If.md b/docs/Reference/Default/VBA/Interaction/If.md index b0b50a110..80b3d5fc1 100644 --- a/docs/Reference/Default/VBA/Interaction/If.md +++ b/docs/Reference/Default/VBA/Interaction/If.md @@ -32,6 +32,15 @@ The three-argument form is the inline conditional: it returns *truepart* when *e > [!NOTE] > **If** uses special internal bindings in the compiler and may not behave exactly like a regular function --- in particular, `Application.Run "If", ...` and other reflective callers will not invoke it. + +### Common Errors + +| Error | When | +|---|---| +| [13 (`&HD`)](tb:Errors#13), *Type mismatch* | *expression* is a string that is not a **Boolean** or a number | + + + ### Example ```tb check_build diff --git a/docs/Reference/Default/VBA/Interaction/Partition.md b/docs/Reference/Default/VBA/Interaction/Partition.md index c233a576f..5cba9b3d4 100644 --- a/docs/Reference/Default/VBA/Interaction/Partition.md +++ b/docs/Reference/Default/VBA/Interaction/Partition.md @@ -43,6 +43,27 @@ If *interval* is 1, the range collapses to *number:number*, regardless of *start Any argument may be a decimal value, but is rounded to the nearest even integer before processing. If any argument is **Null**, **Partition** returns **Null**. +> [!NOTE] +> twinBASIC takes any argument in the range of a **LongLong**, as 64-bit VBA does. VB6 raises error 6, *Overflow*, for an argument outside the range of a **Long**. + + +### Common Errors + +| Error | When | +|---|---| +| [-2147467259 (`&H80004005`)](tb:Errors#-2147467259), *Unspecified error* | *start* is less than 0, *stop* is not greater than *start*, or *interval* is less than 1, after rounding
an argument is a string that is not a number
an argument is outside the range of a **LongLong** | + +> [!NOTE] BETA 1010 +> When *start* is less than 0, *stop* is not greater than *start*, or *interval* is less than 1, after rounding, VB6 and 64-bit VBA raise error [5 (`&H5`)](tb:Errors#5), *Invalid procedure call or argument*, instead. + +> [!NOTE] BETA 1010 +> When an argument is a string that is not a number, VB6 and 64-bit VBA raise error [13 (`&HD`)](tb:Errors#13), *Type mismatch*, instead. + +> [!NOTE] BETA 1010 +> When an argument is outside the range of a **LongLong**, VB6 and 64-bit VBA raise error [6 (`&H6`)](tb:Errors#6), *Overflow*, instead. + + + ### Example This example passes a series of freight costs to **Partition** with *start* = 0, *stop* = 500, and *interval* = 50, and prints the range each one falls into. The first range is `" 0: 49"`, and so on up to 500. Note that 12.5 is rounded before the range is chosen, and that a value below *start* or above *stop* gets one of the open-ended forms. diff --git a/docs/Reference/Default/VBA/Interaction/RaiseEventByName.md b/docs/Reference/Default/VBA/Interaction/RaiseEventByName.md index 5eeb91ff1..6644ab18c 100644 --- a/docs/Reference/Default/VBA/Interaction/RaiseEventByName.md +++ b/docs/Reference/Default/VBA/Interaction/RaiseEventByName.md @@ -26,6 +26,22 @@ Returns a **Variant**. The returned value is the value left in the last `ByRef` If *procname* doesn't match any event declared on *object*, or the supplied arity doesn't match the event declaration, a run-time error occurs. +> [!WARNING] BETA 1010 +> An *object* that declares no events, such as a **Collection** or a **Scripting.Dictionary**, ends the program with an access violation, with no error to handle. Unsaved data is lost. + + +> [!NOTE] BETA 1010 +> An event that no handler is attached to raises error -2147467259 (`&H80004005`), *Unspecified error*. The **RaiseEvent** statement raises nothing for it. + + +### Common Errors + +| Error | When | +|---|---| +| [91 (`&H5B`)](tb:Errors#91), *Object variable or With block variable not set* | *object* is **Nothing** | + + + ### Example ```tb check_build diff --git a/docs/Reference/Default/VBA/Interaction/RaiseEventByName2.md b/docs/Reference/Default/VBA/Interaction/RaiseEventByName2.md index b3d19cc01..8ab7be53d 100644 --- a/docs/Reference/Default/VBA/Interaction/RaiseEventByName2.md +++ b/docs/Reference/Default/VBA/Interaction/RaiseEventByName2.md @@ -26,6 +26,22 @@ Returns a **Variant**. The returned value is the value left in the last `ByRef` If *procname* doesn't match any event declared on *object*, or the supplied arity doesn't match the event declaration, a run-time error occurs. +> [!WARNING] BETA 1010 +> An *object* that declares no events, such as a **Collection**, ends the program with an access violation, with no error to handle. Unsaved data is lost. + + +> [!NOTE] BETA 1010 +> An event that no handler is attached to raises error -2147467259 (`&H80004005`), *Unspecified error*. The **RaiseEvent** statement raises nothing for it. + + +### Common Errors + +| Error | When | +|---|---| +| [91 (`&H5B`)](tb:Errors#91), *Object variable or With block variable not set* | *object* is **Nothing** | + + + ### Example ```tb check_build diff --git a/docs/Reference/Default/VBA/Interaction/SaveSetting.md b/docs/Reference/Default/VBA/Interaction/SaveSetting.md index e3b39df30..24aff5b57 100644 --- a/docs/Reference/Default/VBA/Interaction/SaveSetting.md +++ b/docs/Reference/Default/VBA/Interaction/SaveSetting.md @@ -30,10 +30,22 @@ Syntax: **SaveSetting** *appname*, *section*, *key*, *setting* : String expression containing the value that key is being set to. -An error occurs if the key setting can't be saved for any reason. The root of these registry settings is: `Computer\HKEY_CURRENT_USER\Software\VB and VBA Program Settings`. + +### Common Errors + +| Error | When | +|---|---| +| [-2147467259 (`&H80004005`)](tb:Errors#-2147467259), *Unspecified error* | *appname*, *section* or *key* is a zero-length string | +| [94 (`&H5E`)](tb:Errors#94), *Invalid use of Null* | an argument is **Null** | + +> [!NOTE] BETA 1010 +> When *appname*, *section* or *key* is a zero-length string, VB6 and 64-bit VBA raise error [5 (`&H5`)](tb:Errors#5), *Invalid procedure call or argument*, instead. + + + ### Example The following example first uses the **SaveSetting** statement to make entries in the Windows registry for the application, and then uses the [**DeleteSetting**](DeleteSetting) statement to remove them. diff --git a/docs/Reference/Default/VBA/Interaction/Switch.md b/docs/Reference/Default/VBA/Interaction/Switch.md index 0eb647a46..3158825b6 100644 --- a/docs/Reference/Default/VBA/Interaction/Switch.md +++ b/docs/Reference/Default/VBA/Interaction/Switch.md @@ -19,13 +19,26 @@ Syntax: **Switch(** *expr-1* **,** *value-1* [ **,** *expr-2* **,** *value-2* ** *value* : *required* Value or expression to be returned if the corresponding *expr* is **True**. -The argument list consists of pairs of conditions and values. Conditions are evaluated from left to right, and the value associated with the first condition that evaluates to **True** is returned. If the parts aren't properly paired (i.e. an odd number of arguments is supplied) a run-time error occurs. +The argument list consists of pairs of conditions and values. Conditions are evaluated from left to right, and the value associated with the first condition that evaluates to **True** is returned. **Switch** returns **Null** if none of the conditions is **True**, or if the first **True** condition has a corresponding *value* of **Null**. > [!NOTE] > **Switch** evaluates *every* condition expression and *every* value, not only the ones it ends up using. Watch for side effects: if any of the value expressions would raise an error (for example, division by zero), the error occurs even when **Switch** would not have returned that value. + +### Common Errors + +| Error | When | +|---|---| +| [-2147467259 (`&H80004005`)](tb:Errors#-2147467259), *Unspecified error* | the arguments are not in pairs: their number is odd | +| [13 (`&HD`)](tb:Errors#13), *Type mismatch* | an *expr* is a string that is not a **Boolean** or a number | + +> [!NOTE] BETA 1010 +> When the arguments are not in pairs: their number is odd, VB6 and 64-bit VBA raise error [5 (`&H5`)](tb:Errors#5), *Invalid procedure call or argument*, instead. + + + ### Example This example uses **Switch** to return the language associated with a city name. diff --git a/docs/Reference/Default/VBA/Math/Abs.md b/docs/Reference/Default/VBA/Math/Abs.md index 4fbd9e2e6..6e234ee49 100644 --- a/docs/Reference/Default/VBA/Math/Abs.md +++ b/docs/Reference/Default/VBA/Math/Abs.md @@ -16,6 +16,18 @@ Syntax: **Abs(** *number* **)** The absolute value of a number is its unsigned magnitude. For example, `Abs(-1)` and `Abs(1)` both return `1`. + +### Common Errors + +| Error | When | +|---|---| +| [6 (`&H6`)](tb:Errors#6), *Overflow* | *number* is a **Currency** of -922,337,203,685,477.5808 | + +> [!NOTE] BETA 1010 +> When *number* is an **Integer** of -32,768 or a **Long** of -2,147,483,648 (twinBASIC returns a **Long** or a **Double**), VB6 and 64-bit VBA raise error [6 (`&H6`)](tb:Errors#6), *Overflow*; twinBASIC raises none. + + + ### Example This example uses the **Abs** function to compute the absolute value of a number. diff --git a/docs/Reference/Default/VBA/Math/Atn.md b/docs/Reference/Default/VBA/Math/Atn.md index 78fd7b056..5639e84c7 100644 --- a/docs/Reference/Default/VBA/Math/Atn.md +++ b/docs/Reference/Default/VBA/Math/Atn.md @@ -21,6 +21,15 @@ The range of the result is -pi/2 to pi/2 radians. To convert degrees to radians, > [!NOTE] > **Atn** is the inverse trigonometric function of [**Tan**](Tan), which takes an angle as its argument and returns the ratio of two sides of a right triangle. Do not confuse **Atn** with the cotangent, which is the simple inverse of a tangent (1/tangent). + +### Common Errors + +| Error | When | +|---|---| +| [94 (`&H5E`)](tb:Errors#94), *Invalid use of Null* | *number* is **Null** | + + + ### Example This example uses **Atn** to derive an approximation of pi. diff --git a/docs/Reference/Default/VBA/Math/Cos.md b/docs/Reference/Default/VBA/Math/Cos.md index 9c0a96417..ebd446f79 100644 --- a/docs/Reference/Default/VBA/Math/Cos.md +++ b/docs/Reference/Default/VBA/Math/Cos.md @@ -18,6 +18,21 @@ The **Cos** function takes an angle and returns the ratio of two sides of a righ To convert degrees to radians, multiply degrees by pi/180. To convert radians to degrees, multiply radians by 180/pi. +> [!WARNING] BETA 1010 +> **Cos** of a number of 9,223,372,036,854,775,808 (2^63) or more in magnitude returns the number itself, with no error: `Cos(1E+20)` is `1E+20`, outside the range -1 to 1. Check the magnitude of *number* before the call. + + +### Common Errors + +| Error | When | +|---|---| +| [94 (`&H5E`)](tb:Errors#94), *Invalid use of Null* | *number* is **Null** | + +> [!NOTE] BETA 1010 +> When the magnitude of *number* is 9,223,372,000,000,000,000 or more, VB6 and 64-bit VBA raise error [5 (`&H5`)](tb:Errors#5), *Invalid procedure call or argument*; twinBASIC raises none. + + + ### Example This example uses the **Cos** function to return the cosine of an angle. diff --git a/docs/Reference/Default/VBA/Math/Exp.md b/docs/Reference/Default/VBA/Math/Exp.md index 9dd4d602a..7a0db1343 100644 --- a/docs/Reference/Default/VBA/Math/Exp.md +++ b/docs/Reference/Default/VBA/Math/Exp.md @@ -14,11 +14,21 @@ Syntax: **Exp(** *number* **)** *number* : *required* A **Double** or any valid numeric expression. -If the value of *number* exceeds 709.782712893, an error occurs. The constant *e* is approximately 2.718282. +The constant *e* is approximately 2.718282. > [!NOTE] > The **Exp** function complements the action of the [**Log**](Log) function and is sometimes referred to as the antilogarithm. + +### Common Errors + +| Error | When | +|---|---| +| [6 (`&H6`)](tb:Errors#6), *Overflow* | *number* is greater than 709.782712893384 | +| [94 (`&H5E`)](tb:Errors#94), *Invalid use of Null* | *number* is **Null** | + + + ### Example This example uses the **Exp** function to return *e* raised to a power. diff --git a/docs/Reference/Default/VBA/Math/Log.md b/docs/Reference/Default/VBA/Math/Log.md index ccfe4b2d8..78be82d26 100644 --- a/docs/Reference/Default/VBA/Math/Log.md +++ b/docs/Reference/Default/VBA/Math/Log.md @@ -28,6 +28,16 @@ Static Function Log10(X) End Function ``` + +### Common Errors + +| Error | When | +|---|---| +| [5 (`&H5`)](tb:Errors#5), *Invalid procedure call or argument* | *number* is zero or negative (**Empty** included) | +| [94 (`&H5E`)](tb:Errors#94), *Invalid use of Null* | *number* is **Null** | + + + ### Example This example uses the **Log** function to return the natural logarithm of a number. diff --git a/docs/Reference/Default/VBA/Math/Randomize.md b/docs/Reference/Default/VBA/Math/Randomize.md index 47f75799a..f28c06df2 100644 --- a/docs/Reference/Default/VBA/Math/Randomize.md +++ b/docs/Reference/Default/VBA/Math/Randomize.md @@ -21,6 +21,15 @@ If **Randomize** is not used, the **Rnd** function (with no arguments) uses the > [!NOTE] > To repeat sequences of random numbers, call **Rnd** with a negative argument immediately before using **Randomize** with a numeric argument. Using **Randomize** with the same value for *number* does not repeat the previous sequence. + +### Common Errors + +| Error | When | +|---|---| +| [94 (`&H5E`)](tb:Errors#94), *Invalid use of Null* | *number* is **Null** | + + + ### Example This example uses the **Randomize** statement to initialize the random-number generator. Because the *number* argument has been omitted, **Randomize** uses the return value from the [**Timer**](../DateTime/Timer) function as the new seed value. diff --git a/docs/Reference/Default/VBA/Math/Rnd.md b/docs/Reference/Default/VBA/Math/Rnd.md index 614ff60c3..376e1a91e 100644 --- a/docs/Reference/Default/VBA/Math/Rnd.md +++ b/docs/Reference/Default/VBA/Math/Rnd.md @@ -40,6 +40,15 @@ Here, *upperbound* is the highest number in the range, and *lowerbound* is the l > [!NOTE] > To repeat sequences of random numbers, call **Rnd** with a negative argument immediately before using **Randomize** with a numeric argument. Using **Randomize** with the same value for *number* does not repeat the previous sequence. + +### Common Errors + +| Error | When | +|---|---| +| [94 (`&H5E`)](tb:Errors#94), *Invalid use of Null* | *number* is **Null** | + + + ### Example This example uses the **Rnd** function to generate a random integer value from 1 to 6. diff --git a/docs/Reference/Default/VBA/Math/Round.md b/docs/Reference/Default/VBA/Math/Round.md index 370aaa06d..cfdf72e16 100644 --- a/docs/Reference/Default/VBA/Math/Round.md +++ b/docs/Reference/Default/VBA/Math/Round.md @@ -20,6 +20,19 @@ Syntax: **Round(** *expression* [ **,** *numdecimalplaces* ] **)** > [!NOTE] > **Round** uses banker's rounding (round half to even): when the value is exactly half-way between two possible rounded results, it rounds toward the nearest even digit. For example, `Round(0.5, 0)` returns `0`, and `Round(1.5, 0)` returns `2`. + +### Common Errors + +| Error | When | +|---|---| +| [5 (`&H5`)](tb:Errors#5), *Invalid procedure call or argument* | *numdecimalplaces* rounds to a negative number
*numdecimalplaces* is greater than 22 and *expression* is a **Single**, **Double** or **Date**, or a string | +| [94 (`&H5E`)](tb:Errors#94), *Invalid use of Null* | *numdecimalplaces* is **Null** | + +> [!NOTE] BETA 1010 +> When *expression* is **Null** and *numdecimalplaces* is negative, VB6 and 64-bit VBA raise error [5 (`&H5`)](tb:Errors#5), *Invalid procedure call or argument*; twinBASIC raises none. + + + ### Example ```tb check_build diff --git a/docs/Reference/Default/VBA/Math/Sgn.md b/docs/Reference/Default/VBA/Math/Sgn.md index 80802692e..47d73af18 100644 --- a/docs/Reference/Default/VBA/Math/Sgn.md +++ b/docs/Reference/Default/VBA/Math/Sgn.md @@ -22,6 +22,15 @@ The sign of the *number* argument determines the return value of the **Sgn** fun | Equal to zero | 0 | | Less than zero | -1 | + +### Common Errors + +| Error | When | +|---|---| +| [94 (`&H5E`)](tb:Errors#94), *Invalid use of Null* | *number* is **Null** | + + + ### Example This example uses the **Sgn** function to determine the sign of a number. diff --git a/docs/Reference/Default/VBA/Math/ShiftRotateLeft.md b/docs/Reference/Default/VBA/Math/ShiftRotateLeft.md index 1f67fb5d5..9ec36d182 100644 --- a/docs/Reference/Default/VBA/Math/ShiftRotateLeft.md +++ b/docs/Reference/Default/VBA/Math/ShiftRotateLeft.md @@ -18,7 +18,7 @@ Syntax: **ShiftRotateLeft(** *number*, *shiftamount* **)** The result has the type of *number*. The rotation works on all the bits of that type: 8 for a **Byte**, 16 for an **Integer** or a **Boolean**, 32 for a **Long**, 64 for a **LongLong** or a **Currency**, and 32 or 64 for a **LongPtr**, as the build is 32-bit or 64-bit. A **Currency** is rotated as the 64-bit integer that stores it, which is its value multiplied by 10,000. -The rotation count is taken modulo the number of bits, so rotating a **Long** by 32 gives the value unchanged, and by 33 the same as by 1. A negative *shiftamount* rotates right: rotating a **Long** left by -1 is the same as rotating it left by 31. No value of *shiftamount* raises an error. +The rotation count is taken modulo the number of bits, so rotating a **Long** by 32 gives the value unchanged, and by 33 the same as by 1. A negative *shiftamount* rotates right: rotating a **Long** left by -1 is the same as rotating it left by 31. No **Long** value of *shiftamount* raises an error. Unlike the [**\<<**](../../Core/LeftShift) operator, which discards the bits shifted out and fills with zeros, **ShiftRotateLeft** loses no bits: `&H12345678 << 4` is `&H23456780`, and `ShiftRotateLeft(&H12345678, 4)` is `&H23456781`. @@ -30,6 +30,15 @@ With constant arguments, the call is evaluated when the code is compiled, so it > [!NOTE] > **ShiftRotateLeft** is a twinBASIC extension. VBA and VB6 have no function of this name: a call to it is the compile error *Sub or Function not defined*. + +### Common Errors + +| Error | When | +|---|---| +| [94 (`&H5E`)](tb:Errors#94), *Invalid use of Null* | *shiftamount* is **Null** | + + + ### Example ```tb check_run diff --git a/docs/Reference/Default/VBA/Math/ShiftRotateRight.md b/docs/Reference/Default/VBA/Math/ShiftRotateRight.md index c0bf11ce6..608686e70 100644 --- a/docs/Reference/Default/VBA/Math/ShiftRotateRight.md +++ b/docs/Reference/Default/VBA/Math/ShiftRotateRight.md @@ -18,7 +18,7 @@ Syntax: **ShiftRotateRight(** *number*, *shiftamount* **)** The result has the type of *number*. The rotation works on all the bits of that type: 8 for a **Byte**, 16 for an **Integer** or a **Boolean**, 32 for a **Long**, 64 for a **LongLong** or a **Currency**, and 32 or 64 for a **LongPtr**, as the build is 32-bit or 64-bit. A **Currency** is rotated as the 64-bit integer that stores it, which is its value multiplied by 10,000. -The rotation count is taken modulo the number of bits, so rotating a **Long** by 32 gives the value unchanged, and by 33 the same as by 1. A negative *shiftamount* rotates left. No value of *shiftamount* raises an error. +The rotation count is taken modulo the number of bits, so rotating a **Long** by 32 gives the value unchanged, and by 33 the same as by 1. A negative *shiftamount* rotates left. No **Long** value of *shiftamount* raises an error. Unlike the [**\>>**](../../Core/RightShift) operator, which discards the bits shifted out, **ShiftRotateRight** loses no bits, and it never copies the sign bit: `&H12345678 >> 4` is `&H1234567`, and `ShiftRotateRight(&H12345678, 4)` is `&H81234567`. @@ -30,6 +30,15 @@ With constant arguments, the call is evaluated when the code is compiled, so it > [!NOTE] > **ShiftRotateRight** is a twinBASIC extension. VBA and VB6 have no function of this name: a call to it is the compile error *Sub or Function not defined*. + +### Common Errors + +| Error | When | +|---|---| +| [94 (`&H5E`)](tb:Errors#94), *Invalid use of Null* | *shiftamount* is **Null** | + + + ### Example ```tb check_run diff --git a/docs/Reference/Default/VBA/Math/ShiftUnsignedRight.md b/docs/Reference/Default/VBA/Math/ShiftUnsignedRight.md index 00e30c292..cbc430786 100644 --- a/docs/Reference/Default/VBA/Math/ShiftUnsignedRight.md +++ b/docs/Reference/Default/VBA/Math/ShiftUnsignedRight.md @@ -30,6 +30,15 @@ With constant arguments, the call is evaluated when the code is compiled, so it > [!NOTE] > **ShiftUnsignedRight** is a twinBASIC extension. VBA and VB6 have no function of this name: a call to it is the compile error *Sub or Function not defined*. + +### Common Errors + +| Error | When | +|---|---| +| [94 (`&H5E`)](tb:Errors#94), *Invalid use of Null* | *shiftamount* is **Null** | + + + ### Example ```tb check_run diff --git a/docs/Reference/Default/VBA/Math/Sin.md b/docs/Reference/Default/VBA/Math/Sin.md index 2a233f2cc..bb670465b 100644 --- a/docs/Reference/Default/VBA/Math/Sin.md +++ b/docs/Reference/Default/VBA/Math/Sin.md @@ -18,6 +18,21 @@ The **Sin** function takes an angle and returns the ratio of two sides of a righ To convert degrees to radians, multiply degrees by pi/180. To convert radians to degrees, multiply radians by 180/pi. +> [!WARNING] BETA 1010 +> **Sin** of a number of 9,223,372,036,854,775,808 (2^63) or more in magnitude returns the number itself, with no error: `Sin(1E+20)` is `1E+20`, outside the range -1 to 1. Check the magnitude of *number* before the call. + + +### Common Errors + +| Error | When | +|---|---| +| [94 (`&H5E`)](tb:Errors#94), *Invalid use of Null* | *number* is **Null** | + +> [!NOTE] BETA 1010 +> When the magnitude of *number* is 9,223,372,000,000,000,000 or more, VB6 and 64-bit VBA raise error [5 (`&H5`)](tb:Errors#5), *Invalid procedure call or argument*; twinBASIC raises none. + + + ### Example This example uses the **Sin** function to return the sine of an angle. diff --git a/docs/Reference/Default/VBA/Math/Sqr.md b/docs/Reference/Default/VBA/Math/Sqr.md index 30ef73012..ae6dddc4c 100644 --- a/docs/Reference/Default/VBA/Math/Sqr.md +++ b/docs/Reference/Default/VBA/Math/Sqr.md @@ -14,6 +14,16 @@ Syntax: **Sqr(** *number* **)** *number* : *required* A **Double** or any valid numeric expression greater than or equal to zero. + +### Common Errors + +| Error | When | +|---|---| +| [5 (`&H5`)](tb:Errors#5), *Invalid procedure call or argument* | *number* is negative | +| [94 (`&H5E`)](tb:Errors#94), *Invalid use of Null* | *number* is **Null** | + + + ### Example This example uses the **Sqr** function to calculate the square root of a number. diff --git a/docs/Reference/Default/VBA/Math/Tan.md b/docs/Reference/Default/VBA/Math/Tan.md index 5f85e6595..b59e8967e 100644 --- a/docs/Reference/Default/VBA/Math/Tan.md +++ b/docs/Reference/Default/VBA/Math/Tan.md @@ -18,6 +18,22 @@ Syntax: **Tan(** *number* **)** To convert degrees to radians, multiply degrees by pi/180. To convert radians to degrees, multiply radians by 180/pi. + +### Common Errors + +| Error | When | +|---|---| +| [94 (`&H5E`)](tb:Errors#94), *Invalid use of Null* | *number* is **Null** | +| [-2147348207 (`&H80021111`)](tb:Errors#-2147348207), *Automation error* | the magnitude of *number* is 9,223,372,036,854,775,808 (2^63) or more | + +> [!NOTE] BETA 1010 +> When the magnitude of *number* is 9,223,372,036,854,775,808 (2^63) or more, VB6 and 64-bit VBA raise error [5 (`&H5`)](tb:Errors#5), *Invalid procedure call or argument*, instead. + +> [!NOTE] BETA 1010 +> When the magnitude of *number* is 9,223,372,000,000,000,000 or more but less than 2^63, VB6 and 64-bit VBA raise error [5 (`&H5`)](tb:Errors#5), *Invalid procedure call or argument*; twinBASIC raises none. + + + ### Example This example uses the **Tan** function to return the tangent of an angle. diff --git a/docs/Reference/Default/VBA/Math/index.md b/docs/Reference/Default/VBA/Math/index.md index c04fd7873..1d8311c26 100644 --- a/docs/Reference/Default/VBA/Math/index.md +++ b/docs/Reference/Default/VBA/Math/index.md @@ -41,7 +41,7 @@ Debug.Print Log(100) / Log(10) ' 2 -- base-10 log of 100 ## Square root -[**Sqr**](Sqr) returns the square root of a non-negative number as a **Double**. Passing a negative value raises a run-time error rather than returning a complex or **NaN** result; if a calculation may legitimately produce a negative input, guard the call with [**Sgn**](Sgn) or a comparison to zero. +[**Sqr**](Sqr) returns the square root of a non-negative number as a **Double**. Passing a negative value raises error 5, *Invalid procedure call or argument*, rather than returning a complex or **NaN** result; if a calculation may legitimately produce a negative input, guard the call with [**Sgn**](Sgn) or a comparison to zero. ## Rounding diff --git a/docs/Reference/Default/VBA/Strings/Filter.md b/docs/Reference/Default/VBA/Strings/Filter.md index d37994c8b..443961cb5 100644 --- a/docs/Reference/Default/VBA/Strings/Filter.md +++ b/docs/Reference/Default/VBA/Strings/Filter.md @@ -15,7 +15,7 @@ Syntax: **Filter(** *sourcearray*, *match* [ **,** *include* [ **,** *compare* ] : *required* One-dimensional array of strings to be searched. > [!NOTE] -> twinBASIC also takes a *sourcearray* whose elements are of a type other than **String** or **Variant**, such as **Long** or **Double**. For a **Long** array VB6 raises error 13, *Type mismatch*. +> twinBASIC also takes a *sourcearray* whose elements are of a type other than **String** or **Variant**, such as **Long** or **Double**. For a **Long** array VB6 and 64-bit VBA raise error 13, *Type mismatch*. *match* : *required* String to search for. diff --git a/docs/Reference/Default/VBA/Strings/InStr.md b/docs/Reference/Default/VBA/Strings/InStr.md index 7b230541f..a03718fb7 100644 --- a/docs/Reference/Default/VBA/Strings/InStr.md +++ b/docs/Reference/Default/VBA/Strings/InStr.md @@ -60,7 +60,7 @@ The **InStrB** function is used with byte data contained in a string. Instead of | [94 (`&H5E`)](tb:Errors#94), *Invalid use of Null* | *start* or *compare* is **Null** | > [!NOTE] BETA 1010 -> When **InStr** is given a *compare* that is not a valid locale identifier, and *string1* or *string2* is zero-length, VB6 raises no error. +> When **InStr** is given a *compare* that is not a valid locale identifier, and *string1* or *string2* is zero-length, VB6 and 64-bit VBA raise no error. > [!NOTE] BETA 1010 > When **InStr** is given a *compare* of 2 (**vbDatabaseCompare**), VB6 raises error [5 (`&H5`)](tb:Errors#5), *Invalid procedure call or argument*; twinBASIC raises none. diff --git a/docs/Reference/Default/VBA/Strings/Join.md b/docs/Reference/Default/VBA/Strings/Join.md index 7e5791e73..cc50f3ed0 100644 --- a/docs/Reference/Default/VBA/Strings/Join.md +++ b/docs/Reference/Default/VBA/Strings/Join.md @@ -18,7 +18,7 @@ Syntax: **Join(** *sourcearray* [ **,** *delimiter* ] **)** : *optional* String character used to separate the substrings in the returned string. If omitted, the space character (`" "`) is used. If *delimiter* is a zero-length string (`""`), all items in the list are concatenated with no delimiters. > [!NOTE] -> twinBASIC also takes a *sourcearray* whose elements are of a type other than **String** or **Variant**, such as **Long** or **Double**. For a **Long** array VB6 raises error 5, *Invalid procedure call or argument*. +> twinBASIC also takes a *sourcearray* whose elements are of a type other than **String** or **Variant**, such as **Long** or **Double**. For a **Long** array VB6 and 64-bit VBA raise error 5, *Invalid procedure call or argument*. ### Common Errors diff --git a/docs/Reference/Default/VBA/Strings/StrConv.md b/docs/Reference/Default/VBA/Strings/StrConv.md index 93a25f2db..4f8147892 100644 --- a/docs/Reference/Default/VBA/Strings/StrConv.md +++ b/docs/Reference/Default/VBA/Strings/StrConv.md @@ -58,7 +58,7 @@ When converting from a **Byte** array in ANSI format to a string, use the **StrC | [94 (`&H5E`)](tb:Errors#94), *Invalid use of Null* | *conversion* or *LCID* is **Null** | > [!NOTE] BETA 1010 -> When *conversion* includes **vbWide**, **vbNarrow**, **vbKatakana** or **vbHiragana** and *LCID* names an East Asian locale, on a system whose locale is not East Asian, VB6 raises no error. +> When *conversion* includes **vbWide**, **vbNarrow**, **vbKatakana** or **vbHiragana** and *LCID* names an East Asian locale, on a system whose locale is not East Asian, VB6 and 64-bit VBA raise no error. diff --git a/docs/Reference/Default/VBA/Strings/String.md b/docs/Reference/Default/VBA/Strings/String.md index 448dda40d..200bb2c98 100644 --- a/docs/Reference/Default/VBA/Strings/String.md +++ b/docs/Reference/Default/VBA/Strings/String.md @@ -30,7 +30,7 @@ When *character* is a number greater than 255, **String** converts the number to | [94 (`&H5E`)](tb:Errors#94), *Invalid use of Null* | *number* is **Null**, or **String$** is given a *character* that is **Null**
**String** is given a *character* that is **Null** | > [!NOTE] BETA 1010 -> When **String** is given a *character* that is **Null**, VB6 raises no error. +> When **String** is given a *character* that is **Null**, VB6 and 64-bit VBA raise no error. diff --git a/docs/Reference/Run-time-Errors.md b/docs/Reference/Run-time-Errors.md index bb65cf91c..7941a9837 100644 --- a/docs/Reference/Run-time-Errors.md +++ b/docs/Reference/Run-time-Errors.md @@ -44,13 +44,13 @@ The messages are those of twinBASIC 32-bit and VB6, which agree for every number | Error | Message | Listed by | |---|---|---| | **3**{: #3 } (`&H3`) | *Return without GoSub* | --- | -| **5**{: #5 } (`&H5`) | *Invalid procedure call or argument* | [22 pages](#listed-5) | -| **6**{: #6 } (`&H6`) | *Overflow* | [1 page](#listed-6) | +| **5**{: #5 } (`&H5`) | *Invalid procedure call or argument* | [38 pages](#listed-5) | +| **6**{: #6 } (`&H6`) | *Overflow* | [23 pages](#listed-6) | | **7**{: #7 } (`&H7`) | *Out of memory* | --- | -| **9**{: #9 } (`&H9`) | *Subscript out of range* | [1 page](#listed-9) | +| **9**{: #9 } (`&H9`) | *Subscript out of range* | [4 pages](#listed-9) | | **10**{: #10 } (`&HA`) | *This array is fixed or temporarily locked* | --- | | **11**{: #11 } (`&HB`) | *Division by zero* | --- | -| **13**{: #13 } (`&HD`) | *Type mismatch* | [3 pages](#listed-13) | +| **13**{: #13 } (`&HD`) | *Type mismatch* | [39 pages](#listed-13) | | **14**{: #14 } (`&HE`) | *Out of string space* | --- | | **16**{: #16 } (`&H10`) | *Expression too complex* | --- | | **17**{: #17 } (`&H11`) | *Can't perform requested operation* | --- | @@ -79,10 +79,10 @@ The messages are those of twinBASIC 32-bit and VB6, which agree for every number | **74**{: #74 } (`&H4A`) | *Can't rename with different drive* | --- | | **75**{: #75 } (`&H4B`) | *Path/File access error* | --- | | **76**{: #76 } (`&H4C`) | *Path not found* | --- | -| **91**{: #91 } (`&H5B`) | *Object variable or With block variable not set* | --- | +| **91**{: #91 } (`&H5B`) | *Object variable or With block variable not set* | [2 pages](#listed-91) | | **92**{: #92 } (`&H5C`) | *For loop not initialized* | --- | | **93**{: #93 } (`&H5D`) | *Invalid pattern string* | --- | -| **94**{: #94 } (`&H5E`) | *Invalid use of Null* | [28 pages](#listed-94) | +| **94**{: #94 } (`&H5E`) | *Invalid use of Null* | [95 pages](#listed-94) | | **96**{: #96 } (`&H60`) | *Unable to sink events of object because the object is already firing events to the maximum number of event receivers that it supports* | --- | | **97**{: #97 } (`&H61`) | *Can not call friend function on object which is not an instance of defining class* | --- | | **98**{: #98 } (`&H62`) | *A property or method call cannot include a reference to a private object, either as an argument or as a return value* | --- | @@ -103,7 +103,7 @@ The messages are those of twinBASIC 32-bit and VB6, which agree for every number | **429**{: #429 } (`&H1AD`) | *ActiveX component can't create object* | --- | | **430**{: #430 } (`&H1AE`) | *Class does not support Automation or does not support expected interface* | --- | | **432**{: #432 } (`&H1B0`) | *File name or class name not found during Automation operation* | --- | -| **438**{: #438 } (`&H1B6`) | *Object doesn't support this property or method* | --- | +| **438**{: #438 } (`&H1B6`) | *Object doesn't support this property or method* | [2 pages](#listed-438) | | **440**{: #440 } (`&H1B8`) | *Automation error* | --- | | **442**{: #442 } (`&H1BA`) | *Connection to type library or object library for remote process has been lost. Press OK for dialog to remove reference.* | --- | | **443**{: #443 } (`&H1BB`) | *Automation object does not have a default value* | --- | @@ -130,21 +130,58 @@ The messages are those of twinBASIC 32-bit and VB6, which agree for every number | **735**{: #735 } (`&H2DF`) | *Can't save file to TEMP* | --- | | **744**{: #744 } (`&H2E8`) | *Search text not found* | --- | | **746**{: #746 } (`&H2EA`) | *Replacements too long* | --- | -| **-2147467259**{: #-2147467259 } (`&H80004005`) | *Unspecified error*; **Error** gives *Application-defined or object-defined error* | [1 page](#listed--2147467259) | +| **-2147467262**{: #-2147467262 } (`&H80004002`) | *No such interface supported*; **Error** gives *Application-defined or object-defined error* | [1 page](#listed--2147467262) | +| **-2147467259**{: #-2147467259 } (`&H80004005`) | *Unspecified error*; **Error** gives *Application-defined or object-defined error* | [16 pages](#listed--2147467259) | +| **-2147352567**{: #-2147352567 } (`&H80020009`) | *Exception occurred.*; **Error** gives *Application-defined or object-defined error* | [1 page](#listed--2147352567) | +| **-2147352562**{: #-2147352562 } (`&H8002000E`) | *Invalid number of parameters.*; **Error** gives *Application-defined or object-defined error* | [2 pages](#listed--2147352562) | +| **-2147352559**{: #-2147352559 } (`&H80020011`) | *Does not support a collection.*; **Error** gives *Application-defined or object-defined error* | [1 page](#listed--2147352559) | +| **-2147348207**{: #-2147348207 } (`&H80021111`) | *Automation error*; **Error** gives *Application-defined or object-defined error* | [1 page](#listed--2147348207) | +| **-2147221164**{: #-2147221164 } (`&H80040154`) | *Class not registered*; **Error** gives *Application-defined or object-defined error* | [1 page](#listed--2147221164) | +| **-2147221021**{: #-2147221021 } (`&H800401E3`) | *Operation unavailable*; **Error** gives *Application-defined or object-defined error* | [1 page](#listed--2147221021) | +| **-2147221020**{: #-2147221020 } (`&H800401E4`) | *Invalid syntax*; **Error** gives *Application-defined or object-defined error* | [2 pages](#listed--2147221020) | +| **-2147221014**{: #-2147221014 } (`&H800401EA`) | *Moniker cannot open file*; **Error** gives *Application-defined or object-defined error* | [1 page](#listed--2147221014) | +| **-2147221005**{: #-2147221005 } (`&H800401F3`) | *Invalid class string*; **Error** gives *Application-defined or object-defined error* | [2 pages](#listed--2147221005) | +| **-2147023174**{: #-2147023174 } (`&H800706BA`) | *The RPC server is unavailable.*; **Error** gives *Application-defined or object-defined error* | [1 page](#listed--2147023174) | ## Pages that list each error -**5**{: #listed-5 } (`&H5`): [Asc](../tB/Modules/Strings/Asc), [Chr](../tB/Modules/Strings/Chr), [Filter](../tB/Modules/Strings/Filter), [Format](../tB/Modules/Strings/Format), [FormatCurrency](../tB/Modules/Strings/FormatCurrency), [FormatDateTime](../tB/Modules/Strings/FormatDateTime), [FormatNumber](../tB/Modules/Strings/FormatNumber), [FormatPercent](../tB/Modules/Strings/FormatPercent), [InStr](../tB/Modules/Strings/InStr), [InStrRev](../tB/Modules/Strings/InStrRev), [Join](../tB/Modules/Strings/Join), [Left](../tB/Modules/Strings/Left), [Mid](../tB/Modules/Strings/Mid), [MonthName](../tB/Modules/Strings/MonthName), [Replace](../tB/Modules/Strings/Replace), [Right](../tB/Modules/Strings/Right), [Space](../tB/Modules/Strings/Space), [Split](../tB/Modules/Strings/Split), [StrComp](../tB/Modules/Strings/StrComp), [StrConv](../tB/Modules/Strings/StrConv), [String](../tB/Modules/Strings/String), [WeekdayName](../tB/Modules/Strings/WeekdayName) +**5**{: #listed-5 } (`&H5`): [Array](../tB/Modules/Information/Array), [Asc](../tB/Modules/Strings/Asc), [Chr](../tB/Modules/Strings/Chr), [CreateObject](../tB/Modules/Interaction/CreateObject), [CVErr](../tB/Modules/Conversion/CVErr), [DateAdd](../tB/Modules/DateTime/DateAdd), [DateDiff](../tB/Modules/DateTime/DateDiff), [DatePart](../tB/Modules/DateTime/DatePart), [DateSerial](../tB/Modules/DateTime/DateSerial), [Filter](../tB/Modules/Strings/Filter), [Format](../tB/Modules/Strings/Format), [FormatCurrency](../tB/Modules/Strings/FormatCurrency), [FormatDateTime](../tB/Modules/Strings/FormatDateTime), [FormatNumber](../tB/Modules/Strings/FormatNumber), [FormatPercent](../tB/Modules/Strings/FormatPercent), [GetObject](../tB/Modules/Interaction/GetObject), [InStr](../tB/Modules/Strings/InStr), [InStrRev](../tB/Modules/Strings/InStrRev), [Join](../tB/Modules/Strings/Join), [Left](../tB/Modules/Strings/Left), [Log](../tB/Modules/Math/Log), [Mid](../tB/Modules/Strings/Mid), [MonthName](../tB/Modules/Strings/MonthName), [QBColor](../tB/Modules/Information/QBColor), [Replace](../tB/Modules/Strings/Replace), [RGB](../tB/Modules/Information/RGB), [RGBA](../tB/Modules/Information/RGBA), [Right](../tB/Modules/Strings/Right), [Round](../tB/Modules/Math/Round), [Space](../tB/Modules/Strings/Space), [Split](../tB/Modules/Strings/Split), [Sqr](../tB/Modules/Math/Sqr), [StrComp](../tB/Modules/Strings/StrComp), [StrConv](../tB/Modules/Strings/StrConv), [String](../tB/Modules/Strings/String), [TranslateColor](../tB/Modules/Information/TranslateColor), [Weekday](../tB/Modules/DateTime/Weekday), [WeekdayName](../tB/Modules/Strings/WeekdayName) -**6**{: #listed-6 } (`&H6`): [Chr](../tB/Modules/Strings/Chr) +**6**{: #listed-6 } (`&H6`): [Abs](../tB/Modules/Math/Abs), [Bin](../tB/Modules/Conversion/Bin), [CBool](../tB/Modules/Conversion/CBool), [CByte](../tB/Modules/Conversion/CByte), [CCur](../tB/Modules/Conversion/CCur), [CDate](../tB/Modules/Conversion/CDate), [CDbl](../tB/Modules/Conversion/CDbl), [CDec](../tB/Modules/Conversion/CDec), [Choose](../tB/Modules/Interaction/Choose), [Chr](../tB/Modules/Strings/Chr), [CInt](../tB/Modules/Conversion/CInt), [CLng](../tB/Modules/Conversion/CLng), [CLngLng](../tB/Modules/Conversion/CLngLng), [CLngPtr](../tB/Modules/Conversion/CLngPtr), [CSng](../tB/Modules/Conversion/CSng), [CType](../tB/Modules/Conversion/CType), [DateAdd](../tB/Modules/DateTime/DateAdd), [DateDiff](../tB/Modules/DateTime/DateDiff), [Exp](../tB/Modules/Math/Exp), [Hex](../tB/Modules/Conversion/Hex), [Oct](../tB/Modules/Conversion/Oct), [Val](../tB/Modules/Conversion/Val), [ValDec](../tB/Modules/Conversion/ValDec) -**9**{: #listed-9 } (`&H9`): [ReDim](../tB/Core/ReDim) +**9**{: #listed-9 } (`&H9`): [Array](../tB/Modules/Information/Array), [LBound](../tB/Modules/Information/LBound), [ReDim](../tB/Core/ReDim), [UBound](../tB/Modules/Information/UBound) -**13**{: #listed-13 } (`&HD`): [Filter](../tB/Modules/Strings/Filter), [Join](../tB/Modules/Strings/Join), [ReDim](../tB/Core/ReDim) +**13**{: #listed-13 } (`&HD`): [Bin](../tB/Modules/Conversion/Bin), [CBool](../tB/Modules/Conversion/CBool), [CByte](../tB/Modules/Conversion/CByte), [CCur](../tB/Modules/Conversion/CCur), [CDate](../tB/Modules/Conversion/CDate), [CDbl](../tB/Modules/Conversion/CDbl), [CDec](../tB/Modules/Conversion/CDec), [CInt](../tB/Modules/Conversion/CInt), [CLng](../tB/Modules/Conversion/CLng), [CLngLng](../tB/Modules/Conversion/CLngLng), [CLngPtr](../tB/Modules/Conversion/CLngPtr), [CSng](../tB/Modules/Conversion/CSng), [CType](../tB/Modules/Conversion/CType), [CVDate](../tB/Modules/Conversion/CVDate), [Date](../tB/Modules/DateTime/Date), [DateAdd](../tB/Modules/DateTime/DateAdd), [DateDiff](../tB/Modules/DateTime/DateDiff), [DatePart](../tB/Modules/DateTime/DatePart), [DateValue](../tB/Modules/DateTime/DateValue), [Day](../tB/Modules/DateTime/Day), [Filter](../tB/Modules/Strings/Filter), [Hex](../tB/Modules/Conversion/Hex), [Hour](../tB/Modules/DateTime/Hour), [If](../tB/Modules/Interaction/If), [IIf](../tB/Modules/Interaction/IIf), [Join](../tB/Modules/Strings/Join), [LBound](../tB/Modules/Information/LBound), [Minute](../tB/Modules/DateTime/Minute), [Month](../tB/Modules/DateTime/Month), [Oct](../tB/Modules/Conversion/Oct), [ReDim](../tB/Core/ReDim), [Second](../tB/Modules/DateTime/Second), [Switch](../tB/Modules/Interaction/Switch), [Time](../tB/Modules/DateTime/Time), [TimeValue](../tB/Modules/DateTime/TimeValue), [UBound](../tB/Modules/Information/UBound), [Val](../tB/Modules/Conversion/Val), [Weekday](../tB/Modules/DateTime/Weekday), [Year](../tB/Modules/DateTime/Year) -**94**{: #listed-94 } (`&H5E`): [Asc](../tB/Modules/Strings/Asc), [Chr](../tB/Modules/Strings/Chr), [Filter](../tB/Modules/Strings/Filter), [Format](../tB/Modules/Strings/Format), [FormatCurrency](../tB/Modules/Strings/FormatCurrency), [FormatDateTime](../tB/Modules/Strings/FormatDateTime), [FormatNumber](../tB/Modules/Strings/FormatNumber), [FormatPercent](../tB/Modules/Strings/FormatPercent), [InStr](../tB/Modules/Strings/InStr), [InStrRev](../tB/Modules/Strings/InStrRev), [Join](../tB/Modules/Strings/Join), [LCase](../tB/Modules/Strings/LCase), [Left](../tB/Modules/Strings/Left), [LTrim](../tB/Modules/Strings/LTrim), [Mid](../tB/Modules/Strings/Mid), [MonthName](../tB/Modules/Strings/MonthName), [Replace](../tB/Modules/Strings/Replace), [Right](../tB/Modules/Strings/Right), [RTrim](../tB/Modules/Strings/RTrim), [Space](../tB/Modules/Strings/Space), [Split](../tB/Modules/Strings/Split), [StrComp](../tB/Modules/Strings/StrComp), [StrConv](../tB/Modules/Strings/StrConv), [String](../tB/Modules/Strings/String), [StrReverse](../tB/Modules/Strings/StrReverse), [Trim](../tB/Modules/Strings/Trim), [UCase](../tB/Modules/Strings/UCase), [WeekdayName](../tB/Modules/Strings/WeekdayName) +**91**{: #listed-91 } (`&H5B`): [RaiseEventByName](../tB/Modules/Interaction/RaiseEventByName), [RaiseEventByName2](../tB/Modules/Interaction/RaiseEventByName2) -**-2147467259**{: #listed--2147467259 } (`&H80004005`): [ReDim](../tB/Core/ReDim) +**94**{: #listed-94 } (`&H5E`): [Asc](../tB/Modules/Strings/Asc), [Atn](../tB/Modules/Math/Atn), [Bin](../tB/Modules/Conversion/Bin), [Calendar](../tB/Modules/DateTime/Calendar), [CallByDispId](../tB/Modules/Interaction/CallByDispId), [CallByName](../tB/Modules/Interaction/CallByName), [CBool](../tB/Modules/Conversion/CBool), [CByte](../tB/Modules/Conversion/CByte), [CCur](../tB/Modules/Conversion/CCur), [CDate](../tB/Modules/Conversion/CDate), [CDbl](../tB/Modules/Conversion/CDbl), [CDec](../tB/Modules/Conversion/CDec), [Choose](../tB/Modules/Interaction/Choose), [Chr](../tB/Modules/Strings/Chr), [CInt](../tB/Modules/Conversion/CInt), [CLng](../tB/Modules/Conversion/CLng), [CLngLng](../tB/Modules/Conversion/CLngLng), [CLngPtr](../tB/Modules/Conversion/CLngPtr), [Cos](../tB/Modules/Math/Cos), [CreateObject](../tB/Modules/Interaction/CreateObject), [CSng](../tB/Modules/Conversion/CSng), [CStr](../tB/Modules/Conversion/CStr), [CType](../tB/Modules/Conversion/CType), [CVErr](../tB/Modules/Conversion/CVErr), [Date](../tB/Modules/DateTime/Date), [DateAdd](../tB/Modules/DateTime/DateAdd), [DateDiff](../tB/Modules/DateTime/DateDiff), [DatePart](../tB/Modules/DateTime/DatePart), [DateSerial](../tB/Modules/DateTime/DateSerial), [DateValue](../tB/Modules/DateTime/DateValue), [DeleteSetting](../tB/Modules/Interaction/DeleteSetting), [Environ](../tB/Modules/Interaction/Environ), [Error function](../tB/Modules/Conversion/Error), [Exp](../tB/Modules/Math/Exp), [Filter](../tB/Modules/Strings/Filter), [Format](../tB/Modules/Strings/Format), [FormatCurrency](../tB/Modules/Strings/FormatCurrency), [FormatDateTime](../tB/Modules/Strings/FormatDateTime), [FormatNumber](../tB/Modules/Strings/FormatNumber), [FormatPercent](../tB/Modules/Strings/FormatPercent), [GetAllSettings](../tB/Modules/Interaction/GetAllSettings), [GetObject](../tB/Modules/Interaction/GetObject), [GetSetting](../tB/Modules/Interaction/GetSetting), [Hex](../tB/Modules/Conversion/Hex), [InStr](../tB/Modules/Strings/InStr), [InStrRev](../tB/Modules/Strings/InStrRev), [Join](../tB/Modules/Strings/Join), [LBound](../tB/Modules/Information/LBound), [LCase](../tB/Modules/Strings/LCase), [Left](../tB/Modules/Strings/Left), [Log](../tB/Modules/Math/Log), [LTrim](../tB/Modules/Strings/LTrim), [MacID](../tB/Modules/Conversion/MacID), [Mid](../tB/Modules/Strings/Mid), [MonthName](../tB/Modules/Strings/MonthName), [Oct](../tB/Modules/Conversion/Oct), [QBColor](../tB/Modules/Information/QBColor), [Randomize](../tB/Modules/Math/Randomize), [Replace](../tB/Modules/Strings/Replace), [RGB](../tB/Modules/Information/RGB), [RGB_B](../tB/Modules/Information/RGB_B), [RGB_G](../tB/Modules/Information/RGB_G), [RGB_R](../tB/Modules/Information/RGB_R), [RGBA](../tB/Modules/Information/RGBA), [RGBA_A](../tB/Modules/Information/RGBA_A), [Right](../tB/Modules/Strings/Right), [Rnd](../tB/Modules/Math/Rnd), [Round](../tB/Modules/Math/Round), [RTrim](../tB/Modules/Strings/RTrim), [SaveSetting](../tB/Modules/Interaction/SaveSetting), [Sgn](../tB/Modules/Math/Sgn), [ShiftRotateLeft](../tB/Modules/Math/ShiftRotateLeft), [ShiftRotateRight](../tB/Modules/Math/ShiftRotateRight), [ShiftUnsignedRight](../tB/Modules/Math/ShiftUnsignedRight), [Sin](../tB/Modules/Math/Sin), [Space](../tB/Modules/Strings/Space), [Split](../tB/Modules/Strings/Split), [Sqr](../tB/Modules/Math/Sqr), [Str](../tB/Modules/Conversion/Str), [StrComp](../tB/Modules/Strings/StrComp), [StrConv](../tB/Modules/Strings/StrConv), [String](../tB/Modules/Strings/String), [StrReverse](../tB/Modules/Strings/StrReverse), [Tan](../tB/Modules/Math/Tan), [Time](../tB/Modules/DateTime/Time), [TimeSerial](../tB/Modules/DateTime/TimeSerial), [TimeValue](../tB/Modules/DateTime/TimeValue), [TranslateColor](../tB/Modules/Information/TranslateColor), [Trim](../tB/Modules/Strings/Trim), [UBound](../tB/Modules/Information/UBound), [UCase](../tB/Modules/Strings/UCase), [Val](../tB/Modules/Conversion/Val), [ValDec](../tB/Modules/Conversion/ValDec), [Weekday](../tB/Modules/DateTime/Weekday), [WeekdayName](../tB/Modules/Strings/WeekdayName) + +**438**{: #listed-438 } (`&H1B6`): [CallByDispId](../tB/Modules/Interaction/CallByDispId), [CallByName](../tB/Modules/Interaction/CallByName) + +**-2147467262**{: #listed--2147467262 } (`&H80004002`): [GetObject](../tB/Modules/Interaction/GetObject) + +**-2147467259**{: #listed--2147467259 } (`&H80004005`): [Calendar](../tB/Modules/DateTime/Calendar), [CallByDispId](../tB/Modules/Interaction/CallByDispId), [CallByName](../tB/Modules/Interaction/CallByName), [Date](../tB/Modules/DateTime/Date), [DeleteSetting](../tB/Modules/Interaction/DeleteSetting), [Environ](../tB/Modules/Interaction/Environ), [GetAllSettings](../tB/Modules/Interaction/GetAllSettings), [GetObject](../tB/Modules/Interaction/GetObject), [GetSetting](../tB/Modules/Interaction/GetSetting), [LBound](../tB/Modules/Information/LBound), [Partition](../tB/Modules/Interaction/Partition), [ReDim](../tB/Core/ReDim), [SaveSetting](../tB/Modules/Interaction/SaveSetting), [Switch](../tB/Modules/Interaction/Switch), [Time](../tB/Modules/DateTime/Time), [UBound](../tB/Modules/Information/UBound) + +**-2147352567**{: #listed--2147352567 } (`&H80020009`): [CallByName](../tB/Modules/Interaction/CallByName) + +**-2147352562**{: #listed--2147352562 } (`&H8002000E`): [CallByDispId](../tB/Modules/Interaction/CallByDispId), [CallByName](../tB/Modules/Interaction/CallByName) + +**-2147352559**{: #listed--2147352559 } (`&H80020011`): [CallByName](../tB/Modules/Interaction/CallByName) + +**-2147348207**{: #listed--2147348207 } (`&H80021111`): [Tan](../tB/Modules/Math/Tan) + +**-2147221164**{: #listed--2147221164 } (`&H80040154`): [CreateObject](../tB/Modules/Interaction/CreateObject) + +**-2147221021**{: #listed--2147221021 } (`&H800401E3`): [GetObject](../tB/Modules/Interaction/GetObject) + +**-2147221020**{: #listed--2147221020 } (`&H800401E4`): [CreateObject](../tB/Modules/Interaction/CreateObject), [GetObject](../tB/Modules/Interaction/GetObject) + +**-2147221014**{: #listed--2147221014 } (`&H800401EA`): [GetObject](../tB/Modules/Interaction/GetObject) + +**-2147221005**{: #listed--2147221005 } (`&H800401F3`): [CreateObject](../tB/Modules/Interaction/CreateObject), [GetObject](../tB/Modules/Interaction/GetObject) + +**-2147023174**{: #listed--2147023174 } (`&H800706BA`): [CreateObject](../tB/Modules/Interaction/CreateObject) diff --git a/errors/VBA.Conversion.yml b/errors/VBA.Conversion.yml new file mode 100644 index 000000000..0c0754eef --- /dev/null +++ b/errors/VBA.Conversion.yml @@ -0,0 +1,550 @@ +# The VBA Conversion module. Format: WIP.Errors.md. +beta: 1010 +pages: + Reference/Default/VBA/Conversion/Bin.md: + rows: + - error: 6 + when: "*number* is outside the range of a **LongLong**, or is a **Single** or **Double** less than -4,611,686,018,427,387,904 or greater than 4,611,686,018,427,387,903" + cases: + - { expr: Bin(1E+20), only: tb } + - { expr: 'Bin("9223372036854775808")', only: tb } + - { decl: Dim D As Variant, setup: 'D = CDec("9223372036854775808")', expr: Bin(D), only: tb } + - { decl: Dim X As Double, setup: X = 2 ^ 62, expr: Bin(X), only: tb } + - { decl: Dim X As Double, setup: X = -(2 ^ 62) - 1024, expr: Bin(X), only: tb } + - { decl: Dim S As Single, setup: S = 4.7E+18, expr: Bin(S), only: tb } + - error: 13 + when: "*number* is a string that does not hold a number (a zero-length string, and one with the `&B` prefix, included)" + cases: + - { expr: 'Bin("abc")', only: tb } + - { expr: 'Bin("")', only: tb } + - { expr: 'Bin("&B101")', only: tb } + - error: 94 + when: "**Bin$** is given a *number* that is **Null**" + cases: + - { expr: Bin$(Null), only: tb } + quiet: + - { expr: Bin(Null), only: tb } + - { expr: Bin(Empty), only: tb } + - { expr: 'Bin("&HFF")', only: tb } + - { expr: 'Bin("9223372036854775807")', only: tb } + - { decl: Dim D As Variant, setup: 'D = CDec("9223372036854775807")', expr: Bin(D), only: tb } + - { decl: Dim L As LongLong, setup: L = 9223372036854775807^, expr: Bin(L), only: tb } + - { decl: Dim X As Double, setup: X = 2 ^ 62 - 1024, expr: Bin(X), only: tb } + - { decl: Dim X As Double, setup: X = -(2 ^ 62), expr: Bin(X), only: tb } + Reference/Default/VBA/Conversion/CBool.md: + rows: + - error: 6 + when: "*expression* is a string that holds a number outside the range of a **Double**" + cases: + - CBool("1e400") + - error: 13 + when: "*expression* is a string that is not a number, **True** or **False** (a zero-length string included)" + cases: + - CBool("abc") + - CBool("yes") + - CBool("") + - error: 94 + when: "*expression* is **Null**" + cases: [CBool(Null)] + quiet: + - CBool("True") + - CBool("False") + - CBool("1") + - CBool(Empty) + - CBool(1E+300) + - CBool(CVErr(5)) + Reference/Default/VBA/Conversion/CByte.md: + rows: + - error: 6 + when: "*expression* rounds to a number less than 0 or greater than 255" + cases: + - CByte(256) + - CByte(-1) + - CByte(255.5) + - CByte(-0.6) + - CByte("256") + - error: 13 + when: "*expression* is a string that does not hold a number (a zero-length string included)" + cases: + - CByte("abc") + - CByte("") + - error: 94 + when: "*expression* is **Null**" + cases: [CByte(Null)] + quiet: + - CByte(255.4) + - CByte(-0.5) + - CByte(True) + - CByte(CVErr(5)) + Reference/Default/VBA/Conversion/CCur.md: + rows: + - error: 6 + when: "*expression* is less than -922,337,203,685,477.5808 or greater than 922,337,203,685,477.5807" + cases: + - CCur(1E+15) + - CCur(-1E+15) + - CCur("922337203685477.5808") + - CCur("-922337203685477.5809") + - error: 13 + when: "*expression* is a string that does not hold a number (a zero-length string included)" + cases: + - CCur("abc") + - CCur("") + - error: 94 + when: "*expression* is **Null**" + cases: [CCur(Null)] + quiet: + - CCur(922337203685477#) + - CCur("922337203685477.5807") + - CCur("-922337203685477.5808") + Reference/Default/VBA/Conversion/CDate.md: + rows: + - error: 6 + when: "*expression* is a **Decimal** less than -657,434 or 2,958,466 or more" + vb6: 13 + bug: cdate-string-decimal-out-of-range + cases: + - CDate(CDec(2958466)) + - CDate(CDec(-657435)) + - error: 13 + when: "*expression* is a string that is not a date, a time or a number (a zero-length string included), or a date after December 31, 9999" + cases: + - CDate("abc") + - CDate("") + - CDate("13/13/2020") + - CDate("1/1/10000") + - error: 94 + when: "*expression* is **Null**" + cases: [CDate(Null)] + - error: 0 + when: "**CDate** is given a number of another type than **Decimal** that is less than -657,434 or 2,958,466 or more" + vb6: 6 + bug: date-out-of-range-no-error + cases: + - CDate(2958466) + - CDate(2958466.5) + - CDate(-657435) + - CDate(1E+300) + - CDate(-1E+300) + - CDate(CLng(3000000)) + - CDate(CSng(3000000)) + - CDate(CCur(2958466)) + - error: 0 + when: "**CDate** is given a string that holds a number less than -657,434 or 2,958,466 or more" + vb6: 13 + bug: cdate-string-decimal-out-of-range + cases: + - CDate("2958466") + - CDate("-657435") + - CDate("3000000") + - CDate("1e10") + quiet: + - CDate(2958465) + - CDate(2958465.9999) + - CDate(-657434) + - CDate(-657434.5) + - CDate(True) + - CDate(CDec(5)) + - CDate(CCur(5)) + - CDate("5") + - CDate("2958465") + - CDate("-657434") + - CDate("12/31/9999") + - CDate("100-01-01") + Reference/Default/VBA/Conversion/CDbl.md: + rows: + - error: 6 + when: "*expression* is a string that holds a number outside the range of a **Double**" + cases: + - CDbl("1e309") + - CDbl("-1e309") + - error: 13 + when: "*expression* is a string that does not hold a number (a zero-length string included)" + cases: + - CDbl("abc") + - CDbl("") + - error: 94 + when: "*expression* is **Null**" + cases: [CDbl(Null)] + quiet: + - CDbl("1.7e308") + - CDbl("1e-400") + - CDbl(CDec("79228162514264337593543950335")) + - CDbl(CVErr(5)) + Reference/Default/VBA/Conversion/CDec.md: + rows: + - error: 6 + when: "*expression* is less than -79,228,162,514,264,337,593,543,950,335 or greater than 79,228,162,514,264,337,593,543,950,335" + cases: + - CDec(1E+29) + - CDec(-1E+29) + - CDec("79228162514264337593543950336") + - error: 13 + when: "*expression* is a string that does not hold a number (a zero-length string included)" + cases: + - CDec("abc") + - CDec("") + - error: 94 + when: "*expression* is **Null**" + cases: [CDec(Null)] + quiet: + - CDec(7.9E+28) + - CDec("79228162514264337593543950335") + - CDec(1E-30) + Reference/Default/VBA/Conversion/CInt.md: + rows: + - error: 6 + when: "*expression* rounds to a number less than -32,768 or greater than 32,767" + cases: + - CInt(32768) + - CInt(32767.5) + - CInt(-32769) + - CInt(-32768.6) + - CInt(2147483648#) + - CInt("1e5") + - CInt("&H10000") + - error: 13 + when: "*expression* is a string that does not hold a number (a zero-length string included)" + cases: + - CInt("abc") + - CInt("") + - error: 94 + when: "*expression* is **Null**" + cases: [CInt(Null)] + quiet: + - CInt(32767.4) + - CInt(-32768.5) + - CInt("&H8000") + - CInt("&HFFFF") + - CInt(CVErr(5)) + Reference/Default/VBA/Conversion/CLng.md: + rows: + - error: 6 + when: "*expression* rounds to a number less than -2,147,483,648 or greater than 2,147,483,647" + cases: + - CLng(2147483648#) + - CLng(2147483647.5) + - CLng(-2147483649#) + - CLng(-2147483648.6) + - CLng("2147483648") + - CLng("&H100000000") + - error: 13 + when: "*expression* is a string that does not hold a number (a zero-length string included)" + cases: + - CLng("abc") + - CLng("") + - error: 94 + when: "*expression* is **Null**" + cases: [CLng(Null)] + quiet: + - CLng(2147483647.4) + - CLng(-2147483648.5) + - CLng("&HFFFFFFFF") + - CLng(CVErr(5)) + Reference/Default/VBA/Conversion/CLngLng.md: + rows: + - error: 6 + when: "*expression* rounds to a number less than -9,223,372,036,854,775,808 or greater than 9,223,372,036,854,775,807" + cases: + - { decl: Dim X As Double, setup: X = 2 ^ 63, expr: CLngLng(X), only: [tb, vba64] } + - { expr: CLngLng(1E+19), only: [tb, vba64] } + - { expr: CLngLng(-1E+19), only: [tb, vba64] } + - { expr: 'CLngLng("9223372036854775808")', only: [tb, vba64] } + - { expr: 'CLngLng("9223372036854775807.5")', only: [tb, vba64] } + - { decl: Dim D As Variant, setup: 'D = CDec("9223372036854775808")', expr: CLngLng(D), only: [tb, vba64] } + - error: 13 + when: "*expression* is a string that does not hold a number (a zero-length string included)" + cases: + - { expr: 'CLngLng("abc")', only: [tb, vba64] } + - { expr: 'CLngLng("")', only: [tb, vba64] } + - error: 94 + when: "*expression* is **Null**" + cases: + - { expr: CLngLng(Null), only: [tb, vba64] } + quiet: + - { expr: 'CLngLng("9223372036854775807")', only: [tb, vba64] } + - { expr: 'CLngLng("-9223372036854775808.5")', only: [tb, vba64] } + - { expr: CLngLng(9.2233720368547748E+18), only: [tb, vba64] } + - { decl: Dim C As Currency, setup: C = 922337203685477@, expr: CLngLng(C), only: [tb, vba64] } + Reference/Default/VBA/Conversion/CLngPtr.md: + rows: + - error: 6 + when: "in a 32-bit build, *expression* rounds to a number less than -2,147,483,648 or greater than 2,147,483,647" + win64: 0 + cases: + - { expr: CLngPtr(2147483648#), only: [tb, vba64] } + - { expr: CLngPtr(2147483647.5), only: [tb, vba64] } + - { expr: CLngPtr(-2147483649#), only: [tb, vba64] } + - error: 6 + when: "*expression* rounds to a number less than -9,223,372,036,854,775,808 or greater than 9,223,372,036,854,775,807" + cases: + - { expr: CLngPtr(1E+19), only: [tb, vba64] } + - { expr: CLngPtr(-1E+19), only: [tb, vba64] } + - error: 13 + when: "*expression* is a string that does not hold a number (a zero-length string included)" + cases: + - { expr: 'CLngPtr("abc")', only: [tb, vba64] } + - { expr: 'CLngPtr("")', only: [tb, vba64] } + - error: 94 + when: "*expression* is **Null**" + cases: + - { expr: CLngPtr(Null), only: [tb, vba64] } + quiet: + - { expr: CLngPtr(2147483647.4), only: [tb, vba64] } + - { expr: CLngPtr(-2147483648.5), only: [tb, vba64] } + Reference/Default/VBA/Conversion/CSng.md: + rows: + - error: 6 + when: "*expression* is less than -3.402823E38 or greater than 3.402823E38" + cases: + - CSng(3.5E+38) + - CSng(-3.5E+38) + - CSng("1e39") + - error: 13 + when: "*expression* is a string that does not hold a number (a zero-length string included)" + cases: + - CSng("abc") + - CSng("") + - error: 94 + when: "*expression* is **Null**" + cases: [CSng(Null)] + quiet: + - CSng(3.4E+38) + - CSng(1E-50) + Reference/Default/VBA/Conversion/CStr.md: + rows: + - error: 94 + when: "*expression* is **Null**" + cases: [CStr(Null)] + quiet: + - CStr(Empty) + - CStr(CVErr(5)) + - CStr(1E+300) + Reference/Default/VBA/Conversion/CType.md: + rows: + - error: 6 + when: "*value* is a number outside the range of *type*" + cases: + - { expr: CType(Of Integer)(40000), only: tb } + - { expr: CType(Of Byte)(-1), only: tb } + - error: 13 + when: "*value* cannot be converted to *type*, such as a string that does not hold a number converted to a numeric type" + cases: + - { expr: 'CType(Of Long)("abc")', only: tb } + - { expr: 'CType(Of Date)("abc")', only: tb } + - error: 94 + when: "*value* is **Null** and *type* is not **Variant**" + cases: + - { expr: CType(Of Long)(Null), only: tb } + - { expr: CType(Of String)(Null), only: tb } + quiet: + - { expr: CType(Of Variant)(Null), only: tb } + - { expr: 'CType(Of Long)("5")', only: tb } + Reference/Default/VBA/Conversion/CVDate.md: + rows: + - error: 13 + when: "*expression* is a string that is not a date, a time or a number (a zero-length string included), or a number outside the range of a **Date**: less than -657,434 (January 1, 100) or 2,958,466 (January 1, 10000) or more" + cases: + - CVDate("abc") + - CVDate("") + - CVDate("1/1/10000") + - CVDate(2958466) + - CVDate(-657436) + - CVDate(1E+300) + - CVDate(CDec(3000000)) + - CVDate(CCur(2958466)) + quiet: + - CVDate(Null) + - CVDate(2958465) + - CVDate(2958465.9999) + - CVDate(-657434) + - CVDate(CDec(5)) + Reference/Default/VBA/Conversion/CVErr.md: + rows: + - error: 5 + when: "*errornumber* rounds to a number less than 0 or greater than 65,535" + cases: + - CVErr(-1) + - CVErr(-0.6) + - CVErr(65536) + - CVErr(65535.5) + - CVErr(-2147467259) + - CVErr(-2147483648#) + - error: 94 + when: "*errornumber* is **Null**" + cases: [CVErr(Null)] + quiet: + - CVErr(0) + - CVErr(-0.5) + - CVErr(65535) + - CVErr("5") + Reference/Default/VBA/Conversion/Error.md: + rows: + - error: 94 + when: "*errornumber* is **Null**" + cases: [Error(Null), Error$(Null)] + - error: 0 + when: "**Error** or **Error$** is given an *errornumber* that rounds to a number greater than 65,535" + vb6: 6 + bug: error-function-above-65535 + cases: + - Error(65536) + - Error(65535.5) + - Error(100000) + - Error(2147483647) + - Error$(65536) + - Error$(100000) + quiet: + - Error(0) + - Error(5) + - Error(65535) + - Error(65535.4) + - Error(-1) + - Error$(-1) + - Error(-65536) + - Error(-2147467259) + - Error(-2147483648#) + Reference/Default/VBA/Conversion/Hex.md: + rows: + - error: 6 + when: "*number* is outside the range of a **LongLong**, or is a **Single** or **Double** less than -4,611,686,018,427,387,904 or greater than 4,611,686,018,427,387,903" + cases: + - Hex(1E+20) + - Hex("9223372036854775808") + - { decl: Dim D As Variant, setup: 'D = CDec("1E+20")', expr: Hex(D) } + - { decl: Dim X As Double, setup: X = 2 ^ 62, expr: Hex(X) } + - { decl: Dim X As Double, setup: X = -(2 ^ 62) - 1024, expr: Hex(X) } + - { decl: Dim S As Single, setup: S = 4.7E+18, expr: Hex(S) } + - error: 13 + when: "*number* is a string that does not hold a number (a zero-length string included)" + cases: + - Hex("abc") + - Hex("") + - error: 94 + when: "**Hex$** is given a *number* that is **Null**" + cases: [Hex$(Null)] + quiet: + - Hex(Null) + - Hex(Empty) + - Hex(2147483647) + - Hex(-2147483648#) + - Hex("&HFF") + - { expr: Hex(2147483648#), only: [tb, vba64] } + - { expr: Hex(-2147483649#), only: [tb, vba64] } + - { expr: Hex(2147483647.5), only: [tb, vba64] } + - { expr: 'Hex("9223372036854775807")', only: [tb, vba64] } + - { decl: Dim L As LongLong, setup: L = 9223372036854775807^, expr: Hex(L), only: [tb, vba64] } + - { decl: Dim D As Variant, setup: 'D = CDec("9223372036854775807")', expr: Hex(D), only: [tb, vba64] } + - { decl: Dim C As Currency, setup: C = 922337203685477@, expr: Hex(C), only: [tb, vba64] } + - { decl: Dim X As Double, setup: X = 2 ^ 62 - 1024, expr: Hex(X), only: [tb, vba64] } + - { decl: Dim X As Double, setup: X = -(2 ^ 62), expr: Hex(X), only: [tb, vba64] } + Reference/Default/VBA/Conversion/MacID.md: + rows: + - error: 94 + when: "*constant* is **Null**" + cases: [MacID(Null)] + quiet: + - MacID("TEXT") + - MacID("ABC") + - MacID("") + Reference/Default/VBA/Conversion/Oct.md: + rows: + - error: 6 + when: "*number* is outside the range of a **LongLong**, or is a **Single** or **Double** less than -4,611,686,018,427,387,904 or greater than 4,611,686,018,427,387,903" + cases: + - Oct(1E+20) + - Oct("9223372036854775808") + - { decl: Dim X As Double, setup: X = 2 ^ 62, expr: Oct(X) } + - { decl: Dim X As Double, setup: X = -(2 ^ 62) - 1024, expr: Oct(X) } + - error: 13 + when: "*number* is a string that does not hold a number (a zero-length string included)" + cases: + - Oct("abc") + - Oct("") + - error: 94 + when: "**Oct$** is given a *number* that is **Null**" + cases: [Oct$(Null)] + quiet: + - Oct(Null) + - Oct(Empty) + - Oct(2147483647) + - { expr: Oct(2147483648#), only: [tb, vba64] } + - { expr: Oct(-2147483649#), only: [tb, vba64] } + - { expr: Oct(2147483647.5), only: [tb, vba64] } + - { decl: Dim L As LongLong, setup: L = 9223372036854775807^, expr: Oct(L), only: [tb, vba64] } + - { decl: Dim D As Variant, setup: 'D = CDec("9223372036854775807")', expr: Oct(D), only: [tb, vba64] } + - { decl: Dim X As Double, setup: X = 2 ^ 62 - 1024, expr: Oct(X), only: [tb, vba64] } + Reference/Default/VBA/Conversion/Str.md: + rows: + - error: 94 + when: "**Str$** is given a *number* that is **Null**" + cases: [Str$(Null)] + quiet: + - Str(Null) + - Str(Empty) + - Str("1") + - Str(1E+300) + Reference/Default/VBA/Conversion/Val.md: + rows: + - error: 6 + when: "*string* holds a number outside the range of a **Double**" + cases: + - Val("1e400") + - Val("-1e400") + - Val("1d400") + - Val("1e400#") + - Val("&HFFFFFFFFFFFFFFFFFFFF") + - error: 13 + when: "*string* holds a number with a fractional part followed by the **Integer** (`%`) or **Long** (`&`) type-declaration character, such as `\"50.5%\"`" + cases: + - Val("50.5%") + - Val("50.5&") + - error: 94 + when: "*string* is **Null**" + cases: [Val(Null)] + - error: 0 + when: "**Val** is given a *string* that holds a number followed by the **Integer** (`%`), **Long** (`&`) or **Currency** (`@`) type-declaration character, and the number is outside the range of that type" + vb6: 6 + bug: val-suffix-overflow-no-error + cases: + - Val("32768%") + - Val("-32769%") + - Val("70000%") + - Val("2147483648&") + - Val("1e15@") + quiet: + - Val("") + - Val("abc") + - Val("50%") + - Val("32767%") + - Val("-32768%") + - Val("2147483647&") + - Val("50.5!") + - Val("50.5#") + - Val("50.5@") + - Val("1e39!") + - Val("1e308") + - Val("&H") + - Val("&HFFFFFFFFF") + - Val("&O777777777777") + - Val(5) + Reference/Default/VBA/Conversion/ValDec.md: + rows: + - error: 6 + when: "*string* holds a number outside the range of a **Decimal**" + cases: + - { expr: 'ValDec("1e29")', only: tb } + - { expr: 'ValDec("1e400")', only: tb } + - { expr: 'ValDec("79228162514264337593543950336")', only: tb } + - { expr: 'ValDec("-79228162514264337593543950336")', only: tb } + - error: 94 + when: "*string* is **Null**" + cases: + - { expr: ValDec(Null), only: tb } + quiet: + - { expr: 'ValDec("")', only: tb } + - { expr: 'ValDec("abc")', only: tb } + - { expr: 'ValDec("1e28")', only: tb } + - { expr: 'ValDec("79228162514264337593543950335")', only: tb } + - { expr: 'ValDec("50.5%")', only: tb } + - { expr: 'ValDec("70000%")', only: tb } diff --git a/errors/VBA.DateTime.yml b/errors/VBA.DateTime.yml new file mode 100644 index 000000000..c066e0e3c --- /dev/null +++ b/errors/VBA.DateTime.yml @@ -0,0 +1,561 @@ +# The VBA DateTime module. Format: WIP.Errors.md. +# +# Date and Time run under the clock harness (errors/harnesses/clock.bas): the clock +# setters are replaced for the length of each case, so a value that is accepted raises +# nothing, the real clock is never set, and clock_Expect checks what would have been set +# (the date part for Date and Date$, the time part for Time and Time$). A number is an +# extension of twinBASIC's (owner, 2026-10-22): VB6 raises 13 for one, so those cases are +# only: tb. +# +# Calendar is not implemented in twinBASIC (TB0009), and the owner chose not to report +# it, so its cases are twinBASIC-only and have no VB6 NOTE. In VB6 vbCalHijri succeeds +# and stays: a VB6 case that sets it must set vbCalGreg again. +beta: 1010 +pages: + Reference/Default/VBA/DateTime/Calendar.md: + rows: + - error: -2147467259 + when: "**Calendar** is set to any value other than **Null**, **vbCalGreg** included" + cases: + - { run: Calendar = vbCalGreg, only: tb } + - { run: Calendar = vbCalHijri, only: tb } + - { run: 'Calendar = "1"', only: tb } + - { run: Calendar = 2, only: tb } + - { run: Calendar = -1, only: tb } + - error: 94 + when: "*calendartype* is **Null**" + cases: + - { run: Calendar = Null } + quiet: + - Calendar + Reference/Default/VBA/DateTime/Date.md: + harness: clock + rows: + - error: 13 + when: "**Date** is given a string that is not a date, or a number outside the range of dates" + cases: + - { run: 'Date = "abc"' } + - { run: 'Date = "February 30, 2020"' } + - { run: 'Date = "1/1/10000"' } + - { run: Date = 2958466 } + - { run: Date = -657435 } + - { run: Date = 3000000 } + - { run: Date = 1E+300 } + - { run: Date = CVErr(5) } + - error: -2147467259 + when: "**Date$** is given a value that is not a date of the form *mm-dd-yyyy*" + vb6: 5 + bug: date-time-set-errors + cases: + - { run: 'Date$ = "abc"' } + - { run: 'Date$ = ""' } + - { run: 'Date$ = "2000-01-01"' } + - { run: 'Date$ = "January 1, 2000"' } + - { run: Date$ = 36526 } + - { run: Date$ = Empty } + - { run: 'Date$ = "02-30-2000"' } + - { run: 'Date$ = "13-01-2000"' } + - error: -2147467259 + when: "**Date** or **Date$** is given a date before January 1, 1601, which Windows cannot set" + vb6: 5 + bug: date-time-set-errors + cases: + - { run: 'Date = #12/31/1600#' } + - { run: 'Date = #1/1/100#' } + - { run: 'Date = "1/1/1000"' } + - { run: 'Date$ = "12-31-1600"' } + - { run: Date = -657434, only: tb } + - error: -2147467259 + when: "**Date$** is given a date in another form VB6 reads, such as *m-d-yyyy*, *m/d/yyyy* or *mm-dd-yy*" + vb6: 0 + bug: date-time-set-errors + cases: + - { run: "Date$ = \"1-2-2000\"\nclock_Expect #1/2/2000#, Null" } + - { run: "Date$ = \"1/2/2000\"\nclock_Expect #1/2/2000#, Null" } + - { run: "Date$ = \"01-02-00\"\nclock_Expect #1/2/2000#, Null" } + - error: 94 + when: "**Date$** is given **Null**" + cases: + - { run: Date$ = Null } + - error: 94 + when: "**Date** is given **Null**" + vb6: 13 + bug: date-time-set-errors + cases: + - { run: Date = Null } + quiet: + - Date + - Date$ + - { run: "Date = Date\nclock_Expect Date, Null" } + - { run: "Date = #1/1/2000#\nclock_Expect #1/1/2000#, Null" } + - { run: "Date = #1/1/2000 6:00:00 PM#\nclock_Expect #1/1/2000#, Null" } + - { run: "Date = #12/31/9999#\nclock_Expect #12/31/9999#, Null" } + - { run: "Date = #1/1/1601#\nclock_Expect #1/1/1601#, Null" } + - { run: "Date$ = \"01-01-1601\"\nclock_Expect #1/1/1601#, Null" } + - { run: "Date = \"1/1/2000\"\nclock_Expect #1/1/2000#, Null" } + - { run: "Date = \"January 1, 2000\"\nclock_Expect #1/1/2000#, Null" } + - { run: "Date = CDate(36526.75)\nclock_Expect #1/1/2000#, Null" } + - { run: "Date$ = \"01-02-2000\"\nclock_Expect #1/2/2000#, Null" } + - { run: "Date$ = Format$(Date, \"mm-dd-yyyy\")\nclock_Expect Date, Null" } + - { run: "Date = 36526\nclock_Expect #1/1/2000#, Null", only: tb } + - { run: "Date = 36526.75\nclock_Expect #1/1/2000#, Null", only: tb } + - { run: "Date = 0\nclock_Expect #12/30/1899#, Null", only: tb } + - { run: "Date = 0.5\nclock_Expect #12/30/1899#, Null", only: tb } + - { run: "Date = -1\nclock_Expect #12/29/1899#, Null", only: tb } + - { run: "Date = -0.5\nclock_Expect #12/30/1899#, Null", only: tb } + - { run: "Date = -1.5\nclock_Expect #12/29/1899#, Null", only: tb } + - { run: "Date = 2958465\nclock_Expect #12/31/9999#, Null", only: tb } + - { run: "Date = 2958465.99999\nclock_Expect #12/31/9999#, Null", only: tb } + - { run: "Date = Empty\nclock_Expect #12/30/1899#, Null", only: tb } + - { run: "Date = False\nclock_Expect #12/30/1899#, Null", only: tb } + - { run: "Date = True\nclock_Expect #12/29/1899#, Null", only: tb } + - { run: "Date = CByte(5)\nclock_Expect #1/4/1900#, Null", only: tb } + - { run: "Date = CLng(36526)\nclock_Expect #1/1/2000#, Null", only: tb } + - { run: "Date = CSng(36526.5)\nclock_Expect #1/1/2000#, Null", only: tb } + - { run: "Date = CCur(36526.5)\nclock_Expect #1/1/2000#, Null", only: tb } + - { run: "Date = CDec(36526.25)\nclock_Expect #1/1/2000#, Null", only: tb } + - { run: "Date = \"36526\"\nclock_Expect #1/1/2000#, Null", only: tb } + Reference/Default/VBA/DateTime/Time.md: + harness: clock + rows: + - error: 13 + when: "**Time** is given a string that is not a time, or a number outside the range of dates" + cases: + - { run: 'Time = "abc"' } + - { run: 'Time = "25:00"' } + - { run: Time = 2958466 } + - { run: Time = -657435 } + - { run: Time = 3000000 } + - { run: Time = CVErr(5) } + - error: -2147467259 + when: "**Time$** is given a value that is not a string of the form *hh:mm:ss*, or one outside 00:00:00 to 23:59:59" + vb6: 5 + bug: date-time-set-errors + cases: + - { run: 'Time$ = "abc"' } + - { run: 'Time$ = ""' } + - { run: 'Time$ = "25:00"' } + - { run: 'Time$ = "24:00:00"' } + - { run: 'Time$ = "10:60"' } + - { run: 'Time$ = "10:00 PM"' } + - { run: Time$ = 0.5 } + - { run: Time$ = Empty } + - error: -2147467259 + when: "**Time$** is given a time in another form VB6 reads, such as *hh:mm* or *hh*" + vb6: 0 + bug: date-time-set-errors + cases: + - { run: "Time$ = \"10:20\"\nclock_Expect Null, #10:20:00 AM#" } + - { run: "Time$ = \"10\"\nclock_Expect Null, #10:00:00 AM#" } + - error: 94 + when: "**Time$** is given **Null**" + cases: + - { run: Time$ = Null } + - error: 94 + when: "**Time** is given **Null**" + vb6: 13 + bug: date-time-set-errors + cases: + - { run: Time = Null } + quiet: + - Time + - Time$ + - { run: "Time = Time\nclock_Expect Null, TimeSerial(Hour(Time), Minute(Time), Second(Time))" } + - { run: "Time = #6:00:00 PM#\nclock_Expect Null, #6:00:00 PM#" } + - { run: "Time = #1/1/2000 6:00:00 PM#\nclock_Expect Null, #6:00:00 PM#" } + - { run: "Time = \"10:00\"\nclock_Expect Null, #10:00:00 AM#" } + - { run: "Time = \"0.25\"\nclock_Expect Null, #12:25:00 AM#" } + - { run: "Time = \"1.5\"\nclock_Expect Null, #1:05:00 AM#" } + - { run: "Time = CDate(0.25)\nclock_Expect Null, #6:00:00 AM#" } + - { run: "Time = CDate(1.25)\nclock_Expect Null, #6:00:00 AM#" } + - { run: "Time = CDate(-1.25)\nclock_Expect Null, #6:00:00 AM#" } + - { run: "Time = CDate(0.999999)\nclock_Expect Null, #12:00:00 AM#" } + - { run: "Time$ = \"10:20:30\"\nclock_Expect Null, #10:20:30 AM#" } + - { run: "Time = 0\nclock_Expect Null, #12:00:00 AM#", only: tb } + - { run: "Time = 0.25\nclock_Expect Null, #6:00:00 AM#", only: tb } + - { run: "Time = 0.5\nclock_Expect Null, #12:00:00 PM#", only: tb } + - { run: "Time = 1 / 86400\nclock_Expect Null, #12:00:01 AM#", only: tb } + - { run: "Time = 0.999999\nclock_Expect Null, #12:00:00 AM#", only: tb } + - { run: "Time = 1.25\nclock_Expect Null, #6:00:00 AM#", only: tb } + - { run: "Time = 36526.75\nclock_Expect Null, #6:00:00 PM#", only: tb } + - { run: "Time = -0.25\nclock_Expect Null, #6:00:00 AM#", only: tb } + - { run: "Time = -1.25\nclock_Expect Null, #6:00:00 AM#", only: tb } + - { run: "Time = 2958465.5\nclock_Expect Null, #12:00:00 PM#", only: tb } + - { run: "Time = Empty\nclock_Expect Null, #12:00:00 AM#", only: tb } + - { run: "Time = False\nclock_Expect Null, #12:00:00 AM#", only: tb } + - { run: "Time = True\nclock_Expect Null, #12:00:00 AM#", only: tb } + - { run: "Time = CCur(0.5)\nclock_Expect Null, #12:00:00 PM#", only: tb } + - { run: "Time = CDec(0.75)\nclock_Expect Null, #6:00:00 PM#", only: tb } + - { run: "Time = CSng(0.25)\nclock_Expect Null, #6:00:00 AM#", only: tb } + Reference/Default/VBA/DateTime/DateAdd.md: + rows: + - error: 5 + when: "*interval* is not one of the settings listed" + cases: + - 'DateAdd("x", 1, #1/1/2000#)' + - 'DateAdd("", 1, #1/1/2000#)' + - 'DateAdd("dd", 1, #1/1/2000#)' + - 'DateAdd(" d", 1, #1/1/2000#)' + - 'DateAdd(1, 1, #1/1/2000#)' + - error: 5 + when: 'the result would be after December 31, 9999, or before January 1, 100 for an *interval* other than "yyyy", "q" or "m"' + cases: + - 'DateAdd("d", 1, #12/31/9999#)' + - 'DateAdd("s", 1, #12/31/9999 11:59:59 PM#)' + - 'DateAdd("n", 1, #12/31/9999 11:59:00 PM#)' + - 'DateAdd("h", 1, #12/31/9999 11:00:00 PM#)' + - 'DateAdd("y", 1, #12/31/9999#)' + - 'DateAdd("w", 1, #12/31/9999#)' + - 'DateAdd("ww", 1, #12/30/9999#)' + - 'DateAdd("m", 1, #12/1/9999#)' + - 'DateAdd("q", 1, #11/1/9999#)' + - 'DateAdd("yyyy", 1, #1/1/9999#)' + - 'DateAdd("d", 2147483647, #1/1/2000#)' + - 'DateAdd("d", -1, #1/1/100#)' + - 'DateAdd("h", -1, #1/1/100#)' + - 'DateAdd("ww", -1, #1/5/100#)' + - 'DateAdd("yyyy", -10000, #1/1/2000#)' + - error: 0 + when: 'the result with an *interval* of "yyyy", "q" or "m" would be before January 1, 100' + vb6: 5 + bug: dateadd-year-below-100 + cases: + - 'DateAdd("yyyy", -1, #1/1/100#)' + - 'DateAdd("yyyy", -1901, #1/1/2000#)' + - 'DateAdd("q", -1, #1/1/100#)' + - 'DateAdd("m", -1, #1/1/100#)' + - error: 6 + when: "*date* is a number outside the range of dates" + vb6: 13 + bug: dateadd-date-out-of-range-overflow + cases: + - 'DateAdd("d", 1, 3000000)' + - 'DateAdd("d", 0, 2958466)' + - 'DateAdd("d", 0, -657435)' + - error: 13 + when: "*date* is a string that is not a date" + cases: + - 'DateAdd("d", 1, "abc")' + - error: 94 + when: "*interval* or *number* is **Null**" + cases: + - 'DateAdd(Null, 1, #1/1/2000#)' + - 'DateAdd("d", Null, #1/1/2000#)' + - 'DateAdd("d", Null, Null)' + quiet: + - 'DateAdd("D", 1, #1/1/2000#)' + - 'DateAdd("d", 1, Null)' + - 'DateAdd("x", 1, Null)' + - 'DateAdd("d", 1, Empty)' + - 'DateAdd("d", 1, "1/1/2000")' + - 'DateAdd("d", 1.5, #1/1/2000#)' + - 'DateAdd("s", 2147483647, #1/1/2000#)' + - 'DateAdd("yyyy", -1900, #1/1/2000#)' + - 'DateAdd("d", 0, -657434)' + - 'DateAdd("d", 1, 2958464)' + Reference/Default/VBA/DateTime/DateDiff.md: + rows: + - error: 5 + when: "*interval* is not one of the settings listed" + cases: + - 'DateDiff("x", #1/1/2000#, #1/1/2001#)' + - 'DateDiff("", #1/1/2000#, #1/1/2001#)' + - error: 0 + when: '*interval* is "ww" and *firstdayofweek* is outside 0 to 7' + vb6: 5 + bug: datepart-firstdayofweek-out-of-range + cases: + - 'DateDiff("ww", #1/1/2000#, #1/1/2001#, 8)' + - 'DateDiff("ww", #1/1/2000#, #1/1/2001#, -1)' + - 'DateDiff("ww", #1/1/2000#, #1/1/2001#, 255)' + - error: 6 + when: "*date1* or *date2* is a number outside the range of dates" + vb6: 13 + bug: dateadd-date-out-of-range-overflow + cases: + - 'DateDiff("d", #1/1/2000#, 3000000)' + - 'DateDiff("d", -700000, #1/1/2001#)' + - 'DateDiff("d", 2958466, #1/1/2001#)' + - error: 13 + when: "*date1* or *date2* is a string that is not a date" + cases: + - 'DateDiff("d", "abc", #1/1/2001#)' + - error: 94 + when: "*interval*, *firstdayofweek* or *firstweekofyear* is **Null**" + cases: + - 'DateDiff(Null, #1/1/2000#, #1/1/2001#)' + - 'DateDiff("d", #1/1/2000#, #1/1/2001#, Null)' + - 'DateDiff("d", #1/1/2000#, #1/1/2001#, 1, Null)' + quiet: + - 'DateDiff("d", Null, #1/1/2001#)' + - 'DateDiff("d", #1/1/2000#, Null)' + - 'DateDiff("x", Null, #1/1/2001#)' + - 'DateDiff("ww", Null, #1/1/2001#, 8)' + - 'DateDiff("d", #1/1/2000#, #1/1/2001#, 8)' + - 'DateDiff("w", #1/1/2000#, #1/1/2001#, 8)' + - 'DateDiff("ww", #1/1/2000#, #1/1/2001#, 0)' + - 'DateDiff("ww", #1/1/2000#, #1/1/2001#, 7)' + - 'DateDiff("ww", #1/1/2000#, #1/1/2001#, 1, 4)' + - 'DateDiff("ww", #1/1/2000#, #1/1/2001#, 1, -1)' + - 'DateDiff("s", #1/1/100#, #12/31/9999#)' + - 'DateDiff("s", #12/31/9999#, #1/1/100#)' + Reference/Default/VBA/DateTime/DatePart.md: + rows: + - error: 5 + when: "*interval* is not one of the settings listed and *date* is not **Null**" + cases: + - 'DatePart("x", #1/1/2000#)' + - 'DatePart("", #1/1/2000#)' + - error: 5 + when: "*interval* is not one of the settings listed and *date* is **Null**" + vb6: 0 + bug: datepart-null-checks-interval + cases: + - 'DatePart("x", Null)' + - error: 0 + when: '*interval* is "w" or "ww" and *firstdayofweek* is outside 0 to 7' + vb6: 5 + bug: datepart-firstdayofweek-out-of-range + cases: + - 'DatePart("w", #1/1/2000#, 8)' + - 'DatePart("w", #1/1/2000#, -1)' + - 'DatePart("w", #1/1/2000#, 255)' + - 'DatePart("ww", #1/1/2000#, 8)' + - 'DatePart("ww", #1/1/2000#, -1)' + - error: 0 + when: '*interval* is "ww" and *firstweekofyear* is outside 0 to 3' + vb6: 5 + bug: datepart-firstweekofyear-out-of-range + cases: + - 'DatePart("ww", #1/1/2000#, 1, 4)' + - 'DatePart("ww", #1/1/2000#, 1, -1)' + - error: 13 + when: "*date* is a string that is not a date, or a number outside the range of dates" + cases: + - 'DatePart("d", "abc")' + - 'DatePart("d", 3000000)' + - 'DatePart("d", -700000)' + - error: 94 + when: "*interval*, *firstdayofweek* or *firstweekofyear* is **Null**" + cases: + - 'DatePart(Null, #1/1/2000#)' + - 'DatePart("d", #1/1/2000#, Null)' + - 'DatePart("d", #1/1/2000#, 1, Null)' + quiet: + - 'DatePart("d", Null)' + - 'DatePart("ww", Null, 8)' + - 'DatePart("d", #1/1/2000#, 8)' + - 'DatePart("yyyy", #1/1/2000#, 8)' + - 'DatePart("ww", #1/1/2000#, 0)' + - 'DatePart("w", #1/1/2000#, 7)' + - 'DatePart("d", #1/1/2000#, 1, 4)' + - 'DatePart("w", #1/1/2000#, 1, 4)' + - 'DatePart("ww", #1/1/2000#, 1, 0)' + - 'DatePart("ww", #1/1/2000#, 1, 3)' + - 'DatePart("ww", #12/31/9999#, 1, 2)' + - 'DatePart("ww", #12/31/9999#, 1, 3)' + - 'DatePart("ww", #1/1/100#, 1, 2)' + - 'DatePart("ww", #1/8/100#, 1, 3)' + Reference/Default/VBA/DateTime/DateSerial.md: + rows: + - error: 5 + when: "the date is before January 1, 100 or after December 31, 9999" + cases: + - DateSerial(10000, 1, 1) + - DateSerial(9999, 12, 32) + - DateSerial(9999, 13, 1) + - DateSerial(9999, 1, 366) + - DateSerial(32767, 1, 1) + - DateSerial(100, 1, 0) + - DateSerial(-1901, 1, 1) + - DateSerial(-32768, 1, 1) + - error: 94 + when: "*year*, *month* or *day* is **Null**" + cases: + - DateSerial(Null, 1, 1) + - DateSerial(2000, Null, 1) + - DateSerial(2000, 1, Null) + quiet: + - DateSerial(9999, 12, 31) + - DateSerial(9999, 1, 365) + - DateSerial(100, 1, 1) + - DateSerial(100, 0, 1) + - DateSerial(0, 1, 1) + - DateSerial(99, 1, 1) + - DateSerial(-1900, 1, 1) + - DateSerial(2000, 32767, 32767) + - DateSerial(2000, -32768, -32768) + - DateSerial(101, -13, 1) + - DateSerial(2000.5, 1, 1) + Reference/Default/VBA/DateTime/DateValue.md: + rows: + - error: 13 + when: "*date* is not a date: a string that is not one or holds an invalid time, a date after December 31, 9999, a number with a whole part, **Empty**, **True**, **False** or an **Error** value" + cases: + - DateValue("abc") + - DateValue("") + - DateValue("2/30/2000") + - 'DateValue("1/1/2000 89:98")' + - DateValue("1/1/10000") + - DateValue("36526") + - DateValue(36526) + - DateValue(0) + - DateValue(36526.5) + - DateValue(3000000) + - DateValue(Empty) + - DateValue(True) + - DateValue(False) + - DateValue(CVErr(5)) + - error: 94 + when: "*date* is **Null**" + cases: + - DateValue(Null) + quiet: + - DateValue("12/31/9999") + - DateValue("1/1/100") + - DateValue("1/1/99") + - 'DateValue("1/1/2000 10:00")' + - DateValue("10:00") + - 'DateValue(#1/1/2000#)' + - DateValue(CDate(36526.5)) + - DateValue(0.5) + Reference/Default/VBA/DateTime/TimeSerial.md: + rows: + - error: 94 + when: "*hour*, *minute* or *second* is **Null**" + cases: + - TimeSerial(Null, 0, 0) + - TimeSerial(0, Null, 0) + - TimeSerial(0, 0, Null) + quiet: + - TimeSerial(32767, 32767, 32767) + - TimeSerial(-32768, -32768, -32768) + - TimeSerial(24, 0, 0) + - TimeSerial(-1, 0, 0) + Reference/Default/VBA/DateTime/TimeValue.md: + rows: + - error: 13 + when: "*time* is not a time: a string that is not one, a time outside 0:00:00 to 23:59:59, a string with an invalid date, a number outside the range of dates, **Empty**, **True** or an **Error** value" + cases: + - TimeValue("abc") + - TimeValue("") + - TimeValue("25:00") + - TimeValue("24:00") + - TimeValue("10:60") + - 'TimeValue("13/45/2000 10:00")' + - TimeValue(3000000) + - TimeValue(-700000) + - TimeValue(Empty) + - TimeValue(True) + - TimeValue(CVErr(5)) + - error: 94 + when: "*time* is **Null**" + cases: + - TimeValue(Null) + quiet: + - TimeValue("23:59:59") + - TimeValue("2:24PM") + - 'TimeValue("1/1/2000 10:00")' + - TimeValue(0.5) + - TimeValue(1.5) + Reference/Default/VBA/DateTime/Day.md: + rows: + - error: 13 + when: "*date* is a string that is not a date, a number outside the range of dates, or an **Error** value" + cases: + - Day("abc") + - Day("") + - Day("1/1/10000") + - Day(2958466) + - Day(-657435) + - Day(1E+300) + - Day(CVErr(5)) + quiet: + - Day(Null) + - Day(Empty) + - Day(True) + - Day("10:00") + - Day(2958465) + - Day(-657434) + Reference/Default/VBA/DateTime/Month.md: + rows: + - error: 13 + when: "*date* is a string that is not a date, a number outside the range of dates, or an **Error** value" + cases: + - Month("abc") + - Month(3000000) + - Month(-700000) + - Month(CVErr(5)) + quiet: + - Month(Null) + Reference/Default/VBA/DateTime/Year.md: + rows: + - error: 13 + when: "*date* is a string that is not a date, a number outside the range of dates, or an **Error** value" + cases: + - Year("abc") + - Year(3000000) + - Year(-700000) + - Year(CVErr(5)) + quiet: + - Year(Null) + Reference/Default/VBA/DateTime/Weekday.md: + rows: + - error: 5 + when: "*firstdayofweek* is outside 0 to 7" + cases: + - 'Weekday(#1/1/2000#, 8)' + - 'Weekday(#1/1/2000#, -1)' + - Weekday(Empty, 8) + - error: 13 + when: "*date* is a string that is not a date, a number outside the range of dates, or an **Error** value" + cases: + - Weekday("abc") + - Weekday(3000000) + - Weekday(-700000) + - Weekday(CVErr(5)) + - error: 94 + when: "*firstdayofweek* is **Null**" + cases: + - 'Weekday(#1/1/2000#, Null)' + - Weekday(Null, Null) + quiet: + - Weekday(Null) + - Weekday(Null, 8) + - 'Weekday(#1/1/2000#, 0)' + - 'Weekday(#1/1/2000#, 7)' + Reference/Default/VBA/DateTime/Hour.md: + rows: + - error: 13 + when: "*time* is a string that is not a time, a number outside the range of dates, or an **Error** value" + cases: + - Hour("abc") + - Hour("25:00") + - Hour(3000000) + - Hour(-700000) + - Hour(1E+300) + - Hour(CVErr(5)) + quiet: + - Hour(Null) + Reference/Default/VBA/DateTime/Minute.md: + rows: + - error: 13 + when: "*time* is a string that is not a time, a number outside the range of dates, or an **Error** value" + cases: + - Minute("abc") + - Minute(3000000) + - Minute(-700000) + - Minute(CVErr(5)) + quiet: + - Minute(Null) + Reference/Default/VBA/DateTime/Second.md: + rows: + - error: 13 + when: "*time* is a string that is not a time, a number outside the range of dates, or an **Error** value" + cases: + - Second("abc") + - Second(3000000) + - Second(-700000) + - Second(CVErr(5)) + quiet: + - Second(Null) diff --git a/errors/VBA.Information.yml b/errors/VBA.Information.yml new file mode 100644 index 000000000..aeab00d2a --- /dev/null +++ b/errors/VBA.Information.yml @@ -0,0 +1,218 @@ +# The VBA Information module. Format: WIP.Errors.md. +beta: 1010 +pages: + Reference/Default/VBA/Information/LBound.md: + rows: + - error: 9 + when: "*arrayname* is a dynamic array that has not been sized with **ReDim**, or that **Erase** has released" + cases: + - { decl: Dim A() As Long, expr: LBound(A) } + - { decl: Dim A() As Long, setup: "ReDim A(1)\nErase A", expr: LBound(A) } + - error: -2147467259 + when: "*dimension* is less than 1 or greater than the number of dimensions of *arrayname*, after rounding" + vb6: 9 + bug: argument-errors-unspecified-error + cases: + - { decl: Dim A(1 To 3) As Long, expr: 'LBound(A, 0)' } + - { decl: Dim A(1 To 3) As Long, expr: 'LBound(A, -1)' } + - { decl: Dim A(1 To 3) As Long, expr: 'LBound(A, 2)' } + - { decl: Dim A(1 To 3) As Long, expr: 'LBound(A, 1.5)' } + - { decl: Dim A(1 To 3) As Long, expr: 'LBound(A, 0.5)' } + - { decl: Dim A(1 To 3) As Long, expr: 'LBound(A, Empty)' } + - { decl: "Dim A(1 To 2, 3 To 4) As Long", expr: 'LBound(A, 3)' } + - { decl: Dim V As Variant, setup: 'V = Array(1, 2)', expr: 'LBound(V, 2)' } + - error: -2147467259 + when: "*dimension* is greater than 32,767" + vb6: 6 + bug: argument-errors-unspecified-error + cases: + - { decl: Dim A(1 To 3) As Long, expr: 'LBound(A, 32768)' } + - error: 13 + when: "*arrayname* is a **Variant** that holds no array, **Empty** and **Null** included" + cases: + - { decl: Dim V As Variant, setup: V = 5, expr: LBound(V) } + - { decl: Dim V As Variant, expr: LBound(V) } + - { decl: Dim V As Variant, setup: V = Null, expr: LBound(V) } + - { decl: Dim V As Variant, setup: V = "abc", expr: LBound(V) } + - { decl: Dim V As Variant, setup: Set V = Nothing, expr: LBound(V) } + - error: 94 + when: "*dimension* is **Null**" + cases: + - { decl: Dim A(1 To 3) As Long, expr: 'LBound(A, Null)' } + quiet: + - { decl: Dim A(1 To 3) As Long, expr: 'LBound(A, 1)' } + - { decl: Dim A(1 To 3) As Long, expr: 'LBound(A, 1.4)' } + - { decl: Dim A(1 To 3) As Long, expr: 'LBound(A, "1")' } + - { decl: "Dim A(1 To 2, 3 To 4) As Long", expr: 'LBound(A, 2)' } + - { decl: Dim V As Variant, setup: V = Array(), expr: LBound(V) } + Reference/Default/VBA/Information/UBound.md: + rows: + - error: 9 + when: "*arrayname* is a dynamic array that has not been sized with **ReDim**, or that **Erase** has released" + cases: + - { decl: Dim A() As Long, expr: UBound(A) } + - { decl: Dim A() As Long, setup: "ReDim A(1)\nErase A", expr: UBound(A) } + - { decl: Dim S() As String, expr: UBound(S) } + - error: -2147467259 + when: "*dimension* is less than 1 or greater than the number of dimensions of *arrayname*, after rounding" + vb6: 9 + bug: argument-errors-unspecified-error + cases: + - { decl: Dim A(1 To 3) As Long, expr: 'UBound(A, 0)' } + - { decl: Dim A(1 To 3) As Long, expr: 'UBound(A, 2)' } + - { decl: Dim A(1 To 3) As Long, expr: 'UBound(A, 1.5)' } + - error: -2147467259 + when: "*dimension* is greater than 32,767" + vb6: 6 + bug: argument-errors-unspecified-error + cases: + - { decl: Dim A(1 To 3) As Long, expr: 'UBound(A, 32768)' } + - error: 13 + when: "*arrayname* is a **Variant** that holds no array, **Empty** and **Null** included" + cases: + - { decl: Dim V As Variant, setup: V = 5, expr: UBound(V) } + - { decl: Dim V As Variant, expr: UBound(V) } + - { decl: Dim V As Variant, setup: V = Null, expr: UBound(V) } + - error: 94 + when: "*dimension* is **Null**" + cases: + - { decl: Dim A(1 To 3) As Long, expr: 'UBound(A, Null)' } + quiet: + - { decl: Dim A(1 To 3) As Long, expr: 'UBound(A, 1)' } + - { decl: Dim V As Variant, setup: V = Array(), expr: UBound(V) } + Reference/Default/VBA/Information/QBColor.md: + rows: + - error: 5 + when: "*color* is outside 0 to 15, after rounding" + cases: [QBColor(-1), QBColor(16), QBColor(15.5), QBColor(-0.6)] + - error: 94 + when: "*color* is **Null**" + cases: [QBColor(Null)] + quiet: [QBColor(0), QBColor(15), QBColor(15.4), QBColor(-0.5), QBColor("7"), QBColor(Empty)] + Reference/Default/VBA/Information/RGB.md: + rows: + - error: 5 + when: "*red*, *green* or *blue* is negative, after rounding" + cases: + - RGB(-1, 0, 0) + - RGB(0, -1, 0) + - RGB(0, 0, -1) + - RGB(-0.6, 0, 0) + - error: 94 + when: "*red*, *green* or *blue* is **Null**" + cases: + - RGB(Null, 0, 0) + - RGB(0, Null, 0) + - RGB(0, 0, Null) + quiet: + - RGB(255, 255, 255) + - RGB(256, 0, 0) + - RGB(0, 0, 256) + - RGB(32767, 0, 0) + - RGB(255.5, 0, 0) + - RGB(-0.4, 0, 0) + - RGB("1", 0, 0) + - RGB(Empty, 0, 0) + Reference/Default/VBA/Information/RGBA.md: + rows: + - error: 5 + when: "*red*, *green*, *blue* or *alpha* is negative" + cases: + - { expr: 'RGBA(-1, 0, 0, 0)', only: tb } + - { expr: 'RGBA(0, 0, 0, -1)', only: tb } + - error: 94 + when: "*red*, *green*, *blue* or *alpha* is **Null**" + cases: + - { expr: 'RGBA(Null, 0, 0, 0)', only: tb } + - { expr: 'RGBA(0, 0, 0, Null)', only: tb } + quiet: + - { expr: 'RGBA(255, 255, 255, 255)', only: tb } + - { expr: 'RGBA(256, 0, 0, 0)', only: tb } + - { expr: 'RGBA(0, 0, 0, 256)', only: tb } + Reference/Default/VBA/Information/RGB_R.md: + rows: + - error: 94 + when: "*RGBA* is **Null**" + cases: [{ expr: RGB_R(Null), only: tb }] + quiet: + - { expr: RGB_R(-1), only: tb } + - { expr: RGB_R(&HFFFFFF), only: tb } + Reference/Default/VBA/Information/RGB_G.md: + rows: + - error: 94 + when: "*RGBA* is **Null**" + cases: [{ expr: RGB_G(Null), only: tb }] + quiet: [{ expr: RGB_G(-1), only: tb }] + Reference/Default/VBA/Information/RGB_B.md: + rows: + - error: 94 + when: "*RGBA* is **Null**" + cases: [{ expr: RGB_B(Null), only: tb }] + quiet: [{ expr: RGB_B(-1), only: tb }] + Reference/Default/VBA/Information/RGBA_A.md: + rows: + - error: 94 + when: "*RGBA* is **Null**" + cases: [{ expr: RGBA_A(Null), only: tb }] + quiet: [{ expr: RGBA_A(-1), only: tb }] + Reference/Default/VBA/Information/TranslateColor.md: + rows: + - error: 5 + when: "the high byte of *ColorValue* is not &H00, &H01, &H02 or &H80" + cases: + - { expr: TranslateColor(&H3000000), only: tb } + - { expr: TranslateColor(&H7F000000), only: tb } + - { expr: TranslateColor(&H81000000), only: tb } + - { expr: TranslateColor(-1), only: tb } + - error: 5 + when: "*ColorValue* is a system colour (&H80000000 and up) with an index above 30 (&H1E)" + cases: + - { expr: TranslateColor(&H8000001F), only: tb } + - { expr: TranslateColor(&H80000020), only: tb } + - { expr: TranslateColor(&H80000099), only: tb } + - { expr: TranslateColor(&H800000FF), only: tb } + - error: 94 + when: "*ColorValue* is **Null**" + cases: [{ expr: TranslateColor(Null), only: tb }] + quiet: + - { expr: TranslateColor(&HFFFFFF), only: tb } + - { expr: TranslateColor(&H80000000), only: tb } + - { expr: TranslateColor(&H80000012), only: tb } + - { expr: TranslateColor(&H8000001E), only: tb } + - { expr: TranslateColor(&H1000000), only: tb } + - { expr: TranslateColor(&H10000FF), only: tb } + - { expr: TranslateColor(&H1FFFFFF), only: tb } + - { expr: TranslateColor(&H2000000), only: tb } + - { expr: 'TranslateColor(&H80000012, -1)', only: tb } + Reference/Default/VBA/Information/Array.md: + rows: + - error: 5 + when: "the destructuring form is given a right side that is not an array, **Null** included" + message: expected an array + cases: + - { decl: "Dim X As Variant, Y As Variant", run: 'Array(X, Y) = 5', only: tb } + - { decl: "Dim X As Variant, Y As Variant", run: 'Array(X, Y) = Null', only: tb } + - error: 9 + when: "the destructuring form is given an array with fewer elements than variables, or one not yet sized" + cases: + - { decl: "Dim X As Variant, Y As Variant", run: 'Array(X, Y) = Array(1)', only: tb } + - { decl: "Dim X As Variant, Y As Variant, A() As Long", run: 'Array(X, Y) = A', only: tb } + quiet: + - Array() + - { decl: "Dim X As Variant, Y As Variant", run: 'Array(X, Y) = Array(1, 2, 3)', only: tb } + Reference/Default/VBA/Information/IsArrayInitialized.md: + quiet: + - { expr: IsArrayInitialized(Null), only: tb } + - { decl: Dim A() As Long, expr: IsArrayInitialized(A), only: tb } + Reference/Default/VBA/Information/IsDate.md: + quiet: [IsDate(Null), IsDate(Empty)] + Reference/Default/VBA/Information/IsNumeric.md: + quiet: [IsNumeric(Null), IsNumeric(Empty)] + Reference/Default/VBA/Information/TypeName.md: + quiet: + - TypeName(Null) + - { decl: Dim O As Object, expr: TypeName(O) } + Reference/Default/VBA/Information/VarType.md: + quiet: + - VarType(Null) + - { decl: Dim O As Object, expr: VarType(O) } diff --git a/errors/VBA.Interaction.yml b/errors/VBA.Interaction.yml new file mode 100644 index 000000000..ae6f10185 --- /dev/null +++ b/errors/VBA.Interaction.yml @@ -0,0 +1,447 @@ +# The VBA Interaction module, its non-interactive procedures (batch 6a). Format: WIP.Errors.md. +beta: 1010 +pages: + Reference/Default/VBA/Interaction/Choose.md: + rows: + - error: 94 + when: "*index* is **Null**" + cases: + - Choose(Null, "a", "b") + # An extension (owner, 2026-10-22): VB6 returns Null; 64-bit VBA raises 6 too. + - error: 6 + when: "*index* is outside the range of a **Single**" + cases: + - { expr: 'Choose(3.5E+38, "a")', only: [tb, vba64] } + - { expr: 'Choose(1E+300, "a")', only: [tb, vba64] } + - { expr: 'Choose(-1E+300, "a")', only: [tb, vba64] } + quiet: + - Choose(1, "a", "b") + - Choose(0, "a", "b") + - Choose(3, "a", "b") + - Choose(-1, "a") + - Choose(1.5, "a", "b") + - Choose(0.5, "a", "b") + - Choose("1", "a", "b") + - Choose(Empty, "a") + - Choose(32768, "a") + - Choose(2147483648#, "a") + - Choose(1E+20, "a") + - Choose(3.4E+38, "a") + - Choose(CDec("1E+20"), "a") + Reference/Default/VBA/Interaction/Switch.md: + rows: + - error: -2147467259 + when: "the arguments are not in pairs: their number is odd" + vb6: 5 + bug: argument-errors-unspecified-error + cases: + - Switch(True) + - Switch(True, 1, False) + - Switch(Null, 1, False) + - error: 13 + when: "an *expr* is a string that is not a **Boolean** or a number" + cases: + - Switch("abc", 1) + quiet: + - Switch() + - Switch(Null, 1) + - Switch(Null, 1, True, 2) + - Switch("True", 1) + - Switch(5, 1) + - Switch(False, 1) + - Switch(True, Null) + - Switch(Empty, 1) + Reference/Default/VBA/Interaction/IIf.md: + rows: + - error: 13 + when: "*expr* is a string that is not a **Boolean** or a number" + cases: + - IIf("abc", 1, 2) + quiet: + - IIf(Null, 1, 2) + - IIf("True", 1, 2) + - IIf(Empty, 1, 2) + - IIf(5, 1, 2) + Reference/Default/VBA/Interaction/If.md: + rows: + - error: 13 + when: "*expression* is a string that is not a **Boolean** or a number" + cases: + - { expr: 'If("abc", 1, 2)', only: tb } + quiet: + - { expr: 'If(Null, 1, 2)', only: tb } + - { expr: 'If(Null, 2)', only: tb } + - { expr: 'If(Empty, 2)', only: tb } + - { expr: 'If(True, 1, 1 / 0)', only: tb } + Reference/Default/VBA/Interaction/Partition.md: + rows: + - error: -2147467259 + when: "*start* is less than 0, *stop* is not greater than *start*, or *interval* is less than 1, after rounding" + vb6: 5 + bug: argument-errors-unspecified-error + cases: + - Partition(5, -1, 10, 1) + - Partition(5, -0.6, 10, 1) + - Partition(5, 0, 0, 1) + - Partition(5, 10, 5, 1) + - Partition(5, 0, 10, 0) + - Partition(5, 0, 10, -1) + - Partition(5, 0, 10, 0.5) + - error: -2147467259 + when: "an argument is a string that is not a number" + vb6: 13 + bug: argument-errors-unspecified-error + cases: + - Partition("abc", 0, 10, 1) + - Partition(5, 0, 10, "abc") + - error: -2147467259 + when: "an argument is outside the range of a **LongLong**" + vb6: 6 + bug: argument-errors-unspecified-error + cases: + - Partition(1E+20, 0, 10, 1) + - Partition(5, 0, 1E+20, 1) + - Partition(5, 0, 10, 1E+20) + quiet: + # An extension (owner, 2026-10-22): the LongLong range, as in 64-bit VBA; VB6 raises 6. + - { expr: 'Partition(2147483648#, 0, 10, 1)', only: [tb, vba64] } + - { expr: 'Partition(-2147483649#, 0, 10, 1)', only: [tb, vba64] } + - { expr: 'Partition(5, 0, 2147483648#, 1)', only: [tb, vba64] } + - Partition(5, 0, 10, 1) + - Partition(-5, 0, 10, 1) + - Partition(50, 0, 10, 1) + - Partition(5, 0, 1, 1) + - Partition(5, -0.4, 10, 1) + - Partition(5, 0, 10, 0.6) + - Partition(5, 0, 10, 20) + - Partition(5, 0, 2147483647, 1) + - Partition(5, 0, 10, 2147483647) + - Partition(5, 0, 2147483647, 2147483647) + - Partition(Null, 0, 10, 1) + - Partition(5, Null, 10, 1) + - Partition(5, 0, Null, 1) + - Partition(5, 0, 10, Null) + - Partition(Empty, 0, 10, 1) + Reference/Default/VBA/Interaction/Environ.md: + rows: + - error: -2147467259 + when: "*number* is less than 1 after rounding (**Empty** and **True** included), no entry has that position, or *envstring* is a zero-length string" + vb6: 5 + bug: argument-errors-unspecified-error + cases: + - Environ(0) + - Environ(-1) + - Environ(0.4) + - Environ(0.5) + - Environ(Empty) + - Environ(True) + - Environ$(0) + - Environ(1000) + - Environ(32767) + - Environ("") + - error: -2147467259 + when: "*number* is greater than 32,767" + vb6: 6 + bug: argument-errors-unspecified-error + cases: + - Environ(32768) + - Environ(100000) + - Environ(2147483647) + - error: 94 + when: "*envstring* or *number* is **Null**" + cases: + - Environ(Null) + - Environ$(Null) + quiet: + - Environ(1) + - Environ(0.6) + - Environ(1.5) + - Environ("1") + - Environ("PATH") + - Environ("NO_SUCH_VARIABLE_TBDOCS") + - Environ("A=B") + Reference/Default/VBA/Interaction/Command.md: + quiet: + - Command() + - Command$() + Reference/Default/VBA/Interaction/CreateObject.md: + rows: + - error: -2147221020 + when: "*class* is not a registered class name, such as a number, or a CLSID in braces without `new:`" + vb6: 429 + bug: createobject-getobject-raw-hresults + cases: + - { decl: Dim O As Object, run: 'Set O = CreateObject("No.Such.Class")' } + - { decl: Dim O As Object, run: 'Set O = CreateObject("Scripting")' } + - { decl: Dim O As Object, run: 'Set O = CreateObject("{EE09B103-97E0-11CF-978F-00A02463E06F}")' } + - { decl: Dim O As Object, run: 'Set O = CreateObject(5)' } + - error: -2147221164 + when: "*class* is `new:` with a CLSID that is not registered" + vb6: 429 + bug: createobject-getobject-raw-hresults + cases: + - { decl: Dim O As Object, run: 'Set O = CreateObject("new:{00000000-0000-0000-0000-000000000000}")' } + - error: -2147221005 + when: "*class* is `new:` followed by text that is not a CLSID" + vb6: 429 + bug: createobject-getobject-raw-hresults + cases: + - { decl: Dim O As Object, run: 'Set O = CreateObject("new:not-a-guid")' } + - error: 5 + when: "*class* is a zero-length string" + vb6: 429 + bug: createobject-getobject-raw-hresults + cases: + - { decl: Dim O As Object, run: 'Set O = CreateObject("")' } + - error: -2147023174 + when: "*servername* names a computer that cannot be reached" + cases: + - { decl: Dim O As Object, run: 'Set O = CreateObject("Scripting.Dictionary", "no-such-server.invalid")' } + - error: 94 + when: "*class* or *servername* is **Null**" + cases: + - { decl: Dim O As Object, run: 'Set O = CreateObject(Null)' } + - { decl: Dim O As Object, run: 'Set O = CreateObject("Scripting.Dictionary", Null)' } + quiet: + - { decl: Dim O As Object, run: 'Set O = CreateObject("Scripting.Dictionary")' } + - { decl: Dim O As Object, run: 'Set O = CreateObject("Scripting.Dictionary", "")' } + - { decl: Dim O As Object, run: 'Set O = CreateObject("new:{EE09B103-97E0-11CF-978F-00A02463E06F}")' } + Reference/Default/VBA/Interaction/GetObject.md: + rows: + - error: -2147221014 + when: "*pathname* names a file that does not exist, and *class* is omitted" + vb6: 432 + bug: createobject-getobject-raw-hresults + cases: + - { decl: Dim O As Object, run: 'Set O = GetObject("C:\no\such\file.xyz")' } + - error: -2147467262 + when: "*pathname* names a file that does not exist, and *class* is given" + vb6: 432 + bug: createobject-getobject-raw-hresults + cases: + - { decl: Dim O As Object, run: 'Set O = GetObject("C:\no\such\file.xyz", "Scripting.Dictionary")' } + - error: -2147221005 + when: "*class* is not a registered class name" + vb6: 429 + bug: createobject-getobject-raw-hresults + cases: + - { decl: Dim O As Object, run: 'Set O = GetObject(, "No.Such.Class")' } + - { decl: Dim O As Object, run: 'Set O = GetObject("", "No.Such.Class")' } + - error: -2147221021 + when: "*pathname* is omitted and no instance of *class* is running" + vb6: 429 + bug: createobject-getobject-raw-hresults + cases: + - { decl: Dim O As Object, run: 'Set O = GetObject(, "Scripting.Dictionary")' } + - error: -2147467259 + when: "both arguments are omitted" + vb6: 13 + bug: argument-errors-unspecified-error + cases: + - { decl: Dim O As Object, run: Set O = GetObject() } + - error: 5 + when: "*pathname* is a zero-length string and *class* is omitted" + cases: + - { decl: Dim O As Object, run: 'Set O = GetObject("")' } + - error: -2147221020 + when: "*pathname* is neither a file name nor a name Windows can bind to an object" + cases: + - { decl: Dim O As Object, run: 'Set O = GetObject("notaprogid:xyz")' } + - error: 94 + when: "*pathname* or *class* is **Null**" + cases: + - { decl: Dim O As Object, run: Set O = GetObject(Null) } + - { decl: Dim O As Object, run: 'Set O = GetObject(, Null)' } + quiet: + - { decl: Dim O As Object, run: 'Set O = GetObject("", "Scripting.Dictionary")' } + Reference/Default/VBA/Interaction/CallByName.md: + rows: + - error: 438 + when: "*object* has no member named *procname*, or none of the kind *calltype* names" + cases: + - { decl: Dim D As Object, setup: 'Set D = CreateObject("Scripting.Dictionary")', expr: 'CallByName(D, "Nope", vbGet)' } + - { decl: Dim D As Object, setup: 'Set D = CreateObject("Scripting.Dictionary")', expr: 'CallByName(D, "Count", vbMethod)' } + - { decl: Dim D As Object, setup: 'Set D = CreateObject("Scripting.Dictionary")', expr: 'CallByName(D, "Exists", vbGet, "x")' } + - error: 438 + when: "*procname* is a zero-length string" + vb6: 450 + bug: callbyname-dispatch-hresults + cases: + - { decl: Dim D As Object, setup: 'Set D = CreateObject("Scripting.Dictionary")', expr: 'CallByName(D, "", vbGet)' } + - error: -2147467259 + when: "*calltype* is not one of the **VbCallType** values (3, **vbMethod** + **vbGet**, is accepted)" + vb6: 438 + bug: argument-errors-unspecified-error + cases: + - { decl: Dim D As Object, setup: 'Set D = CreateObject("Scripting.Dictionary")', expr: 'CallByName(D, "Count", 0)' } + - { decl: Dim D As Object, setup: 'Set D = CreateObject("Scripting.Dictionary")', expr: 'CallByName(D, "Count", 5)' } + - { decl: Dim D As Object, setup: 'Set D = CreateObject("Scripting.Dictionary")', expr: 'CallByName(D, "Count", 16)' } + - { decl: Dim D As Object, setup: 'Set D = CreateObject("Scripting.Dictionary")', expr: 'CallByName(D, "Count", 99)' } + - error: -2147467259 + when: "*object* is **Nothing**" + vb6: 5 + bug: argument-errors-unspecified-error + cases: + - { decl: Dim D As Object, expr: 'CallByName(D, "Count", vbGet)' } + - error: -2147352562 + when: "the number of *args* is not the number the member takes, or a read-only property is assigned" + vb6: 450 + bug: latebound-hresult-mapping + cases: + - { decl: Dim D As Object, setup: 'Set D = CreateObject("Scripting.Dictionary")', run: 'CallByName D, "Add", vbMethod' } + - { decl: Dim D As Object, setup: 'Set D = CreateObject("Scripting.Dictionary")', run: 'CallByName D, "Add", vbMethod, "x"' } + - { decl: Dim D As Object, setup: 'Set D = CreateObject("Scripting.Dictionary")', run: 'CallByName D, "Add", vbMethod, "x", 1, 2' } + - { decl: Dim D As Object, setup: 'Set D = CreateObject("Scripting.Dictionary")', run: 'CallByName D, "Count", vbLet, 5' } + - error: -2147352559 + when: "*args* are given to a property that takes none" + vb6: 451 + bug: latebound-hresult-mapping + cases: + - { decl: Dim D As Object, setup: 'Set D = CreateObject("Scripting.Dictionary")', expr: 'CallByName(D, "Count", vbGet, 1)' } + - error: -2147352567 + when: "the member raises an error of its own, such as **Add** of a **Dictionary** given a key it holds" + vb6: 457 + bug: callbyname-dispatch-hresults + cases: + - { decl: Dim D As Object, setup: "Set D = CreateObject(\"Scripting.Dictionary\")\nD.Add \"x\", 1", run: 'CallByName D, "Add", vbMethod, "x", 2' } + - error: -2147352567 + when: "the member raises an error of its own, such as a **Dictionary**'s **CompareMode** set when it has items" + vb6: 5 + bug: callbyname-dispatch-hresults + cases: + - { decl: Dim D As Object, setup: "Set D = CreateObject(\"Scripting.Dictionary\")\nD.Add \"x\", 1", run: 'CallByName D, "CompareMode", vbLet, 1' } + - error: 94 + when: "*procname* or *calltype* is **Null**" + cases: + - { decl: Dim D As Object, setup: 'Set D = CreateObject("Scripting.Dictionary")', expr: 'CallByName(D, Null, vbGet)' } + - { decl: Dim D As Object, setup: 'Set D = CreateObject("Scripting.Dictionary")', expr: 'CallByName(D, "Count", Null)' } + quiet: + - { decl: Dim D As Object, setup: 'Set D = CreateObject("Scripting.Dictionary")', expr: 'CallByName(D, "Count", vbGet)' } + - { decl: Dim D As Object, setup: 'Set D = CreateObject("Scripting.Dictionary")', expr: 'CallByName(D, "Count", 3)' } + - { decl: Dim D As Object, setup: 'Set D = CreateObject("Scripting.Dictionary")', run: 'CallByName D, "Add", vbMethod, "x", 1' } + - { decl: Dim D As Object, setup: 'Set D = CreateObject("Scripting.Dictionary")', run: 'CallByName D, "CompareMode", vbLet, 99' } + - { decl: Dim D As Object, setup: 'Set D = CreateObject("Scripting.Dictionary")', expr: 'CallByName(D, "Keys", vbMethod)' } + - { decl: Dim D As Object, setup: 'Set D = CreateObject("Scripting.Dictionary")', expr: 'CallByName(D, "Item", vbGet, "x")' } + - { decl: Dim D As Object, setup: 'Set D = CreateObject("Scripting.Dictionary")', run: 'CallByName D, "Item", vbSet, "x", Nothing' } + Reference/Default/VBA/Interaction/CallByDispId.md: + rows: + - error: 438 + when: "*object* has no member with the dispatch ID *dispid*" + cases: + - { decl: Dim D As Object, setup: 'Set D = CreateObject("Scripting.Dictionary")', expr: 'CallByDispId(D, 12345, vbGet)', only: tb } + - error: -2147467259 + when: "*calltype* is not one of the **VbCallType** values, or *object* is **Nothing**" + cases: + - { decl: Dim D As Object, setup: 'Set D = CreateObject("Scripting.Dictionary")', expr: 'CallByDispId(D, 2, 99)', only: tb } + - { decl: Dim D As Object, expr: 'CallByDispId(D, 2, vbGet)', only: tb } + - error: -2147352562 + when: "the number of *args* is not the number the member takes" + cases: + - { decl: Dim D As Object, setup: 'Set D = CreateObject("Scripting.Dictionary")', run: 'CallByDispId D, 1, vbMethod', only: tb } + - error: 94 + when: "*calltype* is **Null**" + cases: + - { decl: Dim D As Object, setup: 'Set D = CreateObject("Scripting.Dictionary")', expr: 'CallByDispId(D, 2, Null)', only: tb } + quiet: + - { decl: Dim D As Object, setup: 'Set D = CreateObject("Scripting.Dictionary")', expr: 'CallByDispId(D, 2, vbGet)', only: tb } + - { decl: Dim D As Object, setup: 'Set D = CreateObject("Scripting.Dictionary")', expr: 'CallByDispId(D, 0, vbGet, "x")', only: tb } + Reference/Default/VBA/Interaction/RaiseEventByName.md: + rows: + - error: 91 + when: "*object* is **Nothing**" + cases: + - { decl: Dim C As Object, expr: 'RaiseEventByName(C, "Foo")', only: tb } + Reference/Default/VBA/Interaction/RaiseEventByName2.md: + rows: + - error: 91 + when: "*object* is **Nothing**" + cases: + - { decl: Dim C As Object, expr: 'RaiseEventByName2(C, "Foo", 1)', only: tb } + Reference/Default/VBA/Interaction/SaveSetting.md: + harness: registry + rows: + - error: -2147467259 + when: "*appname*, *section* or *key* is a zero-length string" + vb6: 5 + bug: argument-errors-unspecified-error + cases: + - { run: 'SaveSetting "", "S", "K", "V"' } + - { run: 'SaveSetting registry_App, "", "K", "V"' } + - { run: 'SaveSetting registry_App, "S", "", "V"' } + - error: 94 + when: "an argument is **Null**" + cases: + - { run: 'SaveSetting Null, "S", "K", "V"' } + - { run: 'SaveSetting registry_App, Null, "K", "V"' } + - { run: 'SaveSetting registry_App, "S", Null, "V"' } + - { run: 'SaveSetting registry_App, "S", "K", Null' } + quiet: + - { run: 'SaveSetting registry_App, "S", "K", "V"' } + - { run: 'SaveSetting registry_App, "S", "K", ""' } + - { run: 'SaveSetting registry_App, "S", "K", 5' } + - { run: 'SaveSetting registry_App, "S\T", "K", "V"' } + - { run: 'SaveSetting registry_App, "S", "K\L", "V"' } + Reference/Default/VBA/Interaction/GetSetting.md: + harness: registry + rows: + - error: -2147467259 + when: "*appname*, *section* or *key* is a zero-length string" + vb6: 5 + bug: argument-errors-unspecified-error + cases: + - 'GetSetting("", "S", "K")' + - 'GetSetting(registry_App, "", "K")' + - 'GetSetting(registry_App, "S", "")' + - error: 94 + when: "an argument is **Null**" + cases: + - 'GetSetting(Null, "S", "K")' + - 'GetSetting(registry_App, Null, "K")' + - 'GetSetting(registry_App, "S", Null)' + - 'GetSetting(registry_App, "S", "K", Null)' + quiet: + - 'GetSetting(registry_App, "S", "K")' + - { setup: 'SaveSetting registry_App, "S", "K", "V"', expr: 'GetSetting(registry_App, "S", "K")' } + Reference/Default/VBA/Interaction/GetAllSettings.md: + harness: registry + rows: + - error: -2147467259 + when: "*appname* or *section* is a zero-length string" + vb6: 5 + bug: argument-errors-unspecified-error + cases: + - 'GetAllSettings("", "S")' + - 'GetAllSettings(registry_App, "")' + - error: 94 + when: "*appname* or *section* is **Null**" + cases: + - 'GetAllSettings(Null, "S")' + - 'GetAllSettings(registry_App, Null)' + quiet: + - 'GetAllSettings(registry_App, "S")' + - { setup: 'SaveSetting registry_App, "S", "K", "V"', expr: 'GetAllSettings(registry_App, "S")' } + Reference/Default/VBA/Interaction/DeleteSetting.md: + harness: registry + rows: + - error: -2147467259 + when: "the application, section or key named does not exist, or *appname* or *section* is a zero-length string" + vb6: 5 + bug: argument-errors-unspecified-error + cases: + - { run: DeleteSetting registry_App } + - { run: 'DeleteSetting registry_App, "S"' } + - { run: 'DeleteSetting registry_App, "S", "K"' } + - { setup: 'SaveSetting registry_App, "S", "K", "V"', run: 'DeleteSetting registry_App, "S", "Other"' } + - { setup: 'SaveSetting registry_App, "S", "K", "V"', run: 'DeleteSetting registry_App, "T"' } + - { run: 'DeleteSetting ""' } + - { setup: 'SaveSetting registry_App, "S", "K", "V"', run: 'DeleteSetting registry_App, ""' } + - error: 94 + when: "an argument is **Null**" + cases: + - { run: DeleteSetting Null } + - { setup: 'SaveSetting registry_App, "S", "K", "V"', run: 'DeleteSetting registry_App, Null' } + - { setup: 'SaveSetting registry_App, "S", "K", "V"', run: 'DeleteSetting registry_App, "S", Null' } + quiet: + - { setup: 'SaveSetting registry_App, "S", "K", "V"', run: 'DeleteSetting registry_App, "S", "K"' } + - { setup: 'SaveSetting registry_App, "S", "K", "V"', run: 'DeleteSetting registry_App, "S"' } + - { setup: 'SaveSetting registry_App, "S", "K", "V"', run: DeleteSetting registry_App } diff --git a/errors/VBA.Math.yml b/errors/VBA.Math.yml new file mode 100644 index 000000000..47f208b64 --- /dev/null +++ b/errors/VBA.Math.yml @@ -0,0 +1,261 @@ +# The VBA Math module. Format: WIP.Errors.md. +beta: 1010 +pages: + Reference/Default/VBA/Math/Abs.md: + rows: + - error: 6 + when: "*number* is a **Currency** of -922,337,203,685,477.5808" + cases: + - { decl: Dim C As Currency, setup: C = -922337203685477.5807@ - 0.0001@, expr: Abs(C) } + - { decl: Dim V As Variant, setup: V = -922337203685477.5807@ - 0.0001@, expr: Abs(V) } + - error: 0 + when: "*number* is an **Integer** of -32,768 or a **Long** of -2,147,483,648 (twinBASIC returns a **Long** or a **Double**)" + vb6: 6 + bug: abs-minimum-widens-type + cases: + - { decl: Dim I As Integer, setup: I = -32768, expr: Abs(I) } + - { decl: Dim L As Long, setup: L = -2147483647 - 1, expr: Abs(L) } + quiet: + - Abs(Null) + - Abs(Empty) + - Abs(True) + - Abs(-1.7976931348623157E+308) + - { decl: Dim I As Integer, setup: I = -32767, expr: Abs(I) } + - { decl: Dim L As Long, setup: L = -2147483647, expr: Abs(L) } + - { decl: Dim V As Variant, setup: V = CInt(-32768), expr: Abs(V) } + - { decl: Dim V As Variant, setup: V = CLng(-2147483648#), expr: Abs(V) } + - { decl: Dim C As Currency, setup: C = -922337203685477.5807@, expr: Abs(C) } + - { decl: Dim D As Variant, setup: 'D = CDec("-79228162514264337593543950335")', expr: Abs(D) } + Reference/Default/VBA/Math/Atn.md: + rows: + - error: 94 + when: "*number* is **Null**" + cases: [Atn(Null)] + quiet: + - Atn(Empty) + - Atn(1E+308) + - Atn(-1E+308) + Reference/Default/VBA/Math/Cos.md: + rows: + - error: 94 + when: "*number* is **Null**" + cases: [Cos(Null)] + - error: 0 + when: "the magnitude of *number* is 9,223,372,000,000,000,000 or more" + vb6: 5 + bug: trig-above-2-to-63 + cases: + - Cos(9.223372E+18) + - { decl: Dim X As Double, setup: X = 2 ^ 63, expr: Cos(X) } + - { decl: Dim X As Double, setup: X = -(2 ^ 63), expr: Cos(X) } + - Cos(1E+300) + quiet: + - Cos(Empty) + - Cos(1E+18) + - Cos(-1E+18) + Reference/Default/VBA/Math/Sin.md: + rows: + - error: 94 + when: "*number* is **Null**" + cases: [Sin(Null)] + - error: 0 + when: "the magnitude of *number* is 9,223,372,000,000,000,000 or more" + vb6: 5 + bug: trig-above-2-to-63 + cases: + - Sin(9.223372E+18) + - { decl: Dim X As Double, setup: X = 2 ^ 63, expr: Sin(X) } + - { decl: Dim X As Double, setup: X = -(2 ^ 63), expr: Sin(X) } + - Sin(1E+300) + quiet: + - Sin(Empty) + - Sin(1E+18) + - Sin(-1E+18) + Reference/Default/VBA/Math/Tan.md: + rows: + - error: 94 + when: "*number* is **Null**" + cases: [Tan(Null)] + - error: -2147348207 + when: "the magnitude of *number* is 9,223,372,036,854,775,808 (2^63) or more" + vb6: 5 + bug: trig-above-2-to-63 + cases: + - { decl: Dim X As Double, setup: X = 2 ^ 63, expr: Tan(X) } + - { decl: Dim X As Double, setup: X = -(2 ^ 63), expr: Tan(X) } + - Tan(1E+20) + - Tan(1E+300) + - error: 0 + when: "the magnitude of *number* is 9,223,372,000,000,000,000 or more but less than 2^63" + vb6: 5 + bug: trig-above-2-to-63 + cases: + - Tan(9.223372E+18) + - { decl: Dim X As Double, setup: X = 2 ^ 63 - 1024, expr: Tan(X) } + - { decl: Dim X As Double, setup: X = -(2 ^ 63) + 1024, expr: Tan(X) } + quiet: + - Tan(Empty) + - Tan(1E+18) + - Tan(-1E+18) + - Tan(1.5707963267949) + Reference/Default/VBA/Math/Exp.md: + rows: + - error: 6 + when: "*number* is greater than 709.782712893384" + cases: + - Exp(709.782712894) + - Exp(710) + - Exp(1E+300) + - error: 94 + when: "*number* is **Null**" + cases: [Exp(Null)] + quiet: + - Exp(Empty) + - Exp(709.782712893) + - Exp(-1000) + - Exp(-1E+300) + Reference/Default/VBA/Math/Log.md: + rows: + - error: 5 + when: "*number* is zero or negative (**Empty** included)" + cases: + - Log(0) + - Log(-1) + - Log(-1E-300) + - Log(Empty) + - { decl: Dim X As Double, setup: X = -0#, expr: Log(X) } + - error: 94 + when: "*number* is **Null**" + cases: [Log(Null)] + quiet: + - Log(4.94065645841247E-324) + - Log(1E+308) + Reference/Default/VBA/Math/Sqr.md: + rows: + - error: 5 + when: "*number* is negative" + cases: + - Sqr(-1) + - Sqr(-1E-300) + - error: 94 + when: "*number* is **Null**" + cases: [Sqr(Null)] + quiet: + - Sqr(Empty) + - Sqr(0) + - Sqr(1E+308) + - { decl: Dim X As Double, setup: X = -0#, expr: Sqr(X) } + Reference/Default/VBA/Math/Randomize.md: + rows: + - error: 94 + when: "*number* is **Null**" + cases: + - { run: Randomize Null } + quiet: + - { run: Randomize } + - { run: Randomize Empty } + - { run: Randomize 1E+300 } + - { run: Randomize -1E+300 } + - { run: 'Randomize CDec("79228162514264337593543950335")' } + Reference/Default/VBA/Math/Rnd.md: + rows: + - error: 94 + when: "*number* is **Null**" + cases: [Rnd(Null)] + quiet: + - Rnd + - Rnd(Empty) + - Rnd(0) + - Rnd(-1) + - Rnd(3.4E+38) + Reference/Default/VBA/Math/Round.md: + rows: + - error: 5 + when: "*numdecimalplaces* rounds to a negative number" + cases: + - Round(1.5, -1) + - Round(1.5, -0.6) + - Round(Empty, -1) + - { decl: Dim C As Currency, setup: C = 1.5, expr: 'Round(C, -1)' } + - { decl: Dim D As Variant, setup: 'D = CDec("1.5")', expr: 'Round(D, -1)' } + - error: 5 + when: "*numdecimalplaces* is greater than 22 and *expression* is a **Single**, **Double** or **Date**, or a string" + cases: + - Round(1.5, 23) + - Round(1.5, 2147483647) + - Round(1.7976931348623157E+308, 300) + - { decl: Dim S As Single, setup: S = 1.5, expr: 'Round(S, 23)' } + - { decl: Dim T As Date, setup: 'T = #1/1/2000#', expr: 'Round(T, 23)' } + - Round("1.5", 23) + - error: 94 + when: "*numdecimalplaces* is **Null**" + cases: + - Round(1.5, Null) + - Round(Null, Null) + - error: 0 + when: "*expression* is **Null** and *numdecimalplaces* is negative" + vb6: 5 + bug: round-null-negative-digits + cases: + - Round(Null, -1) + quiet: + - Round(Null) + - Round(Null, 23) + - Round(Empty) + - Round("2.5") + - Round(1.5, 0) + - Round(1.5, 22) + - Round(1.5, -0.4) + - Round(1.5, Empty) + - Round(1.7976931348623157E+308, 1) + - { decl: Dim S As Single, setup: S = 1.5, expr: 'Round(S, 22)' } + - { decl: Dim C As Currency, setup: C = 1.5, expr: 'Round(C, 2147483647)' } + - { decl: Dim I As Integer, setup: I = 1, expr: 'Round(I, 2147483647)' } + - { decl: Dim L As Long, setup: L = 1, expr: 'Round(L, 2147483647)' } + - { decl: Dim D As Variant, setup: 'D = CDec("1.5")', expr: 'Round(D, 2147483647)' } + - Round(True, 2147483647) + - Round(Empty, 2147483647) + - { decl: Dim L As LongLong, setup: L = 9223372036854775807^, expr: 'Round(L, 2147483647)', only: [tb, vba64] } + Reference/Default/VBA/Math/Sgn.md: + rows: + - error: 94 + when: "*number* is **Null**" + cases: [Sgn(Null)] + quiet: + - Sgn(Empty) + - Sgn("-3") + - Sgn(-1E+308) + Reference/Default/VBA/Math/ShiftRotateLeft.md: + rows: + - error: 94 + when: "*shiftamount* is **Null**" + cases: + - { decl: "Dim L As Long\nDim V As Variant", setup: "L = 1\nV = Null", expr: 'ShiftRotateLeft(L, V)', only: tb } + quiet: + - { decl: Dim L As Long, setup: L = 1, expr: 'ShiftRotateLeft(L, -1)', only: tb } + - { decl: Dim L As Long, setup: L = 1, expr: 'ShiftRotateLeft(L, 2147483647)', only: tb } + - { decl: Dim L As Long, setup: L = 1, expr: 'ShiftRotateLeft(L, -2147483647 - 1)', only: tb } + - { decl: "Dim L As Long\nDim V As Variant", setup: "L = 1\nV = Empty", expr: 'ShiftRotateLeft(L, V)', only: tb } + - { decl: Dim B As Byte, setup: B = 1, expr: 'ShiftRotateLeft(B, 9)', only: tb } + - { decl: Dim C As Currency, setup: C = 1, expr: 'ShiftRotateLeft(C, 63)', only: tb } + Reference/Default/VBA/Math/ShiftRotateRight.md: + rows: + - error: 94 + when: "*shiftamount* is **Null**" + cases: + - { decl: "Dim L As Long\nDim V As Variant", setup: "L = 1\nV = Null", expr: 'ShiftRotateRight(L, V)', only: tb } + quiet: + - { decl: Dim L As Long, setup: L = 1, expr: 'ShiftRotateRight(L, -1)', only: tb } + - { decl: Dim L As Long, setup: L = 1, expr: 'ShiftRotateRight(L, 2147483647)', only: tb } + - { decl: "Dim L As Long\nDim V As Variant", setup: "L = 1\nV = Empty", expr: 'ShiftRotateRight(L, V)', only: tb } + Reference/Default/VBA/Math/ShiftUnsignedRight.md: + rows: + - error: 94 + when: "*shiftamount* is **Null**" + cases: + - { decl: "Dim L As Long\nDim V As Variant", setup: "L = 1\nV = Null", expr: 'ShiftUnsignedRight(L, V)', only: tb } + quiet: + - { decl: Dim L As Long, setup: L = -1, expr: 'ShiftUnsignedRight(L, -1)', only: tb } + - { decl: Dim L As Long, setup: L = -1, expr: 'ShiftUnsignedRight(L, 32)', only: tb } + - { decl: Dim L As Long, setup: L = -1, expr: 'ShiftUnsignedRight(L, 2147483647)', only: tb } + - { decl: "Dim L As Long\nDim V As Variant", setup: "L = 1\nV = Empty", expr: 'ShiftUnsignedRight(L, V)', only: tb } diff --git a/errors/VBA.Strings.yml b/errors/VBA.Strings.yml index 21f1186b8..613b0682f 100644 --- a/errors/VBA.Strings.yml +++ b/errors/VBA.Strings.yml @@ -83,7 +83,7 @@ pages: vb6: 5 bug: database-compare-no-error cases: - - InStr(1, "abc", "a", 2) + - { expr: 'InStr(1, "abc", "a", 2)', only: [tb, vb6] } # Access gives 2 a meaning of its own - error: 94 when: "*start* or *compare* is **Null**" cases: @@ -242,7 +242,7 @@ pages: vb6: 5 bug: database-compare-no-error cases: - - Filter(Array("ab", "c"), "a", True, 2) + - { expr: 'Filter(Array("ab", "c"), "a", True, 2)', only: [tb, vb6] } # Access gives 2 a meaning of its own - error: 13 when: "*sourcearray* is **Null**, or has more than one dimension" cases: @@ -518,7 +518,7 @@ pages: vb6: 5 bug: database-compare-no-error cases: - - StrComp("a", "b", 2) + - { expr: 'StrComp("a", "b", 2)', only: [tb, vb6] } # Access gives 2 a meaning of its own - error: 94 when: "*compare* is **Null**" cases: diff --git a/errors/harnesses/clock.bas b/errors/harnesses/clock.bas new file mode 100644 index 000000000..a3a0680de --- /dev/null +++ b/errors/harnesses/clock.bas @@ -0,0 +1,151 @@ +' The clock harness, for twinBASIC, VB6 and VBA alike: a case that sets Date, Date$, Time or +' Time$ runs with the system's clock setters replaced, so that what the runtime would set is +' recorded and the real clock is never changed. clock_Begin patches SetLocalTime and +' SetSystemTime (kernelbase and kernel32) with a jump to clock_FakeSet, which records the +' SYSTEMTIME and succeeds where the real setter would, and AdjustTokenPrivileges (kernelbase and advapi32), which +' VB6 calls first and twinBASIC does not, with one to clock_FakeAdjust, which succeeds. +' clock_End puts the original bytes back. The patch is of the probe's own process (the exe, +' or Access for VBA), and only while a case runs. clock_Expect raises 1001 when the clock was +' not set to what the case expects. VBA7 is true in twinBASIC and in VBA 7; VB6 compiles only +' the #Else branches, so they hold what it cannot parse. + +Private Type clock_SYSTEMTIME + wYear As Integer + wMonth As Integer + wDayOfWeek As Integer + wDay As Integer + wHour As Integer + wMinute As Integer + wSecond As Integer + wMilliseconds As Integer +End Type + +#If VBA7 Then +Private Declare PtrSafe Function clock_GetModuleHandleA Lib "kernel32" Alias "GetModuleHandleA" (ByVal Name As String) As LongPtr +Private Declare PtrSafe Function clock_LoadLibraryA Lib "kernel32" Alias "LoadLibraryA" (ByVal Name As String) As LongPtr +Private Declare PtrSafe Function clock_GetProcAddress Lib "kernel32" Alias "GetProcAddress" (ByVal H As LongPtr, ByVal Name As String) As LongPtr +Private Declare PtrSafe Function clock_VirtualProtect Lib "kernel32" Alias "VirtualProtect" (ByVal A As LongPtr, ByVal S As LongPtr, ByVal P As Long, ByRef Old As Long) As Long +Private Declare PtrSafe Sub clock_CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (ByRef D As Any, ByRef S As Any, ByVal N As LongPtr) +Private Declare PtrSafe Sub clock_SetLastError Lib "kernel32" Alias "SetLastError" (ByVal E As Long) +Private Declare PtrSafe Function clock_SystemTimeToFileTime Lib "kernel32" Alias "SystemTimeToFileTime" (ByRef St As clock_SYSTEMTIME, ByRef Ft As Currency) As Long +#Else +Private Declare Function clock_GetModuleHandleA Lib "kernel32" Alias "GetModuleHandleA" (ByVal Name As String) As Long +Private Declare Function clock_LoadLibraryA Lib "kernel32" Alias "LoadLibraryA" (ByVal Name As String) As Long +Private Declare Function clock_GetProcAddress Lib "kernel32" Alias "GetProcAddress" (ByVal H As Long, ByVal Name As String) As Long +Private Declare Function clock_VirtualProtect Lib "kernel32" Alias "VirtualProtect" (ByVal A As Long, ByVal S As Long, ByVal P As Long, ByRef Old As Long) As Long +Private Declare Sub clock_CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (ByRef D As Any, ByRef S As Any, ByVal N As Long) +Private Declare Sub clock_SetLastError Lib "kernel32" Alias "SetLastError" (ByVal E As Long) +Private Declare Function clock_SystemTimeToFileTime Lib "kernel32" Alias "SystemTimeToFileTime" (ByRef St As clock_SYSTEMTIME, ByRef Ft As Currency) As Long +#End If + +' Each address is kept in a Variant: a LongPtr in twinBASIC and VBA 7, a Long in VB6. +Private clock_Targets(5) As Variant +Private clock_Saved(71) As Byte +Private clock_Count As Long +Private clock_Hits As Long +Private clock_Last As clock_SYSTEMTIME + +' Refuses what the real setters refuse: a time SystemTimeToFileTime cannot convert (a year +' outside 1601 to 30827, hour 24, February 30, ...) fails with ERROR_INVALID_PARAMETER. +Private Function clock_FakeSet(ByRef St As clock_SYSTEMTIME) As Long + Dim Ft As Currency + clock_Hits = clock_Hits + 1 + clock_Last = St + If clock_SystemTimeToFileTime(St, Ft) = 0 Then + clock_SetLastError 87 + Exit Function + End If + clock_FakeSet = 1 +End Function + +' AdjustTokenPrivileges' six parameters, read as Long: in 64-bit code each still has a slot of its own. +Private Function clock_FakeAdjust(ByVal Token As Long, ByVal DisableAll As Long, ByVal NewState As Long, ByVal BufferLength As Long, ByVal PreviousState As Long, ByVal ReturnLength As Long) As Long + clock_SetLastError 0 + clock_FakeAdjust = 1 +End Function + +#If VBA7 Then +Private Function clock_Address(ByVal A As LongPtr) As Variant + clock_Address = A +End Function +#Else +Private Function clock_Address(ByVal A As Long) As Variant + clock_Address = A +End Function +#End If + +' The jump to Fn: mov eax, Fn; jmp eax, or in 64-bit code mov rax, Fn; jmp rax. +Private Sub clock_Patch(ByVal Target As Variant, ByVal Fn As Variant) + Dim B(11) As Byte, N As Long, Old As Long, I As Long + If Target = 0 Then Exit Sub + For I = 0 To clock_Count - 1 + If clock_Targets(I) = Target Then Exit Sub + Next + #If Win64 Then + Dim F As LongPtr, P As LongPtr + F = Fn + B(0) = &H48: B(1) = &HB8 + clock_CopyMemory B(2), F, 8 + B(10) = &HFF: B(11) = &HE0 + N = 12 + #Else + Dim F As Long, P As Long + F = Fn + B(0) = &HB8 + clock_CopyMemory B(1), F, 4 + B(5) = &HFF: B(6) = &HE0 + N = 7 + #End If + P = Target + clock_VirtualProtect P, N, &H40, Old + clock_CopyMemory clock_Saved(clock_Count * 12), ByVal P, N + clock_CopyMemory ByVal P, B(0), N + clock_Targets(clock_Count) = Target + clock_Count = clock_Count + 1 +End Sub + +Private Sub clock_Begin() + Dim KB As Variant, K32 As Variant, Adv As Variant + KB = clock_GetModuleHandleA("kernelbase") + K32 = clock_GetModuleHandleA("kernel32") + Adv = clock_LoadLibraryA("advapi32") + clock_Hits = 0 + clock_Patch clock_GetProcAddress(KB, "SetLocalTime"), clock_Address(AddressOf clock_FakeSet) + clock_Patch clock_GetProcAddress(KB, "SetSystemTime"), clock_Address(AddressOf clock_FakeSet) + clock_Patch clock_GetProcAddress(K32, "SetLocalTime"), clock_Address(AddressOf clock_FakeSet) + clock_Patch clock_GetProcAddress(K32, "SetSystemTime"), clock_Address(AddressOf clock_FakeSet) + clock_Patch clock_GetProcAddress(KB, "AdjustTokenPrivileges"), clock_Address(AddressOf clock_FakeAdjust) + clock_Patch clock_GetProcAddress(Adv, "AdjustTokenPrivileges"), clock_Address(AddressOf clock_FakeAdjust) +End Sub + +Private Sub clock_End() + Dim I As Long, N As Long + #If Win64 Then + Dim P As LongPtr + N = 12 + #Else + Dim P As Long + N = 7 + #End If + For I = 0 To clock_Count - 1 + P = clock_Targets(I) + clock_CopyMemory ByVal P, clock_Saved(I * 12), N + Next + clock_Count = 0 +End Sub + +' Raises 1001 when the clock was not set, or its date part is not Day, or its time part is +' not Clock (each skipped when Null). Does nothing when the case has already raised. +Private Sub clock_Expect(ByVal Day As Variant, ByVal Clock As Variant) + Dim D As Date, T As Date + If Err.Number <> 0 Then Exit Sub + If clock_Hits = 0 Then Err.Raise 1001, , "the clock was not set" + D = DateSerial(clock_Last.wYear, clock_Last.wMonth, clock_Last.wDay) + T = TimeSerial(clock_Last.wHour, clock_Last.wMinute, clock_Last.wSecond) + If Not IsNull(Day) Then + If D <> CDate(Day) Then Err.Raise 1001, , "the date was set to " & Format$(D, "yyyy-mm-dd") + End If + If Not IsNull(Clock) Then + If T <> CDate(Clock) Then Err.Raise 1001, , "the time was set to " & Format$(T, "hh:nn:ss") + End If +End Sub diff --git a/errors/harnesses/registry.bas b/errors/harnesses/registry.bas new file mode 100644 index 000000000..42fac3cfd --- /dev/null +++ b/errors/harnesses/registry.bas @@ -0,0 +1,45 @@ +' The registry harness, for twinBASIC, VB6 and VBA alike: a case that calls SaveSetting, +' GetSetting, GetAllSettings or DeleteSetting names the application registry_App +' (tbdocsErrorsProbe and the process id), and +' registry_Begin and registry_End delete that application's whole key, with every section and +' subkey under it, from HKEY_CURRENT_USER\Software\VB and VBA Program Settings, so nothing a +' case writes stays in the registry. DeleteSetting cannot do it: VB6 and VBA raise error 5 for +' an application or section that has a subkey. SHDeleteKeyW deletes the tree. The error the +' case raised is kept: registry_End raises it again, with its description, once the key is +' gone. A case's setup may save the settings it reads. VBA7 is true in twinBASIC and in VBA 7; +' VB6 compiles only the #Else branch. + +#If VBA7 Then +Private Declare PtrSafe Function registry_SHDeleteKey Lib "shlwapi" Alias "SHDeleteKeyW" (ByVal hKey As LongPtr, ByVal SubKey As LongPtr) As Long +Private Declare PtrSafe Function registry_GetCurrentProcessId Lib "kernel32" Alias "GetCurrentProcessId" () As Long +#Else +Private Declare Function registry_SHDeleteKey Lib "shlwapi" Alias "SHDeleteKeyW" (ByVal hKey As Long, ByVal SubKey As Long) As Long +Private Declare Function registry_GetCurrentProcessId Lib "kernel32" Alias "GetCurrentProcessId" () As Long +#End If + +' The application's name ends in the process id: errors.mjs run probes every runtime at once, +' and a runtime's cleanup must never delete what another runtime's case has just saved. +Private Function registry_App() As String + registry_App = "tbdocsErrorsProbe" & registry_GetCurrentProcessId() +End Function + +' HKEY_CURRENT_USER, &H80000001, sign-extended in a 64-bit build as Windows defines it. +Private Sub registry_Clear() + Dim Path As String + Path = "Software\VB and VBA Program Settings\" & registry_App + registry_SHDeleteKey &H80000001, StrPtr(Path) +End Sub + +Private Sub registry_Begin() + Dim N As Long, D As String + N = Err.Number: D = Err.Description + registry_Clear + If N <> 0 Then Err.Raise N, , D +End Sub + +Private Sub registry_End() + Dim N As Long, D As String + N = Err.Number: D = Err.Description + registry_Clear + If N <> 0 Then Err.Raise N, , D +End Sub diff --git a/errors/messages.yml b/errors/messages.yml index 27e7bf0da..74872955e 100644 --- a/errors/messages.yml +++ b/errors/messages.yml @@ -95,3 +95,36 @@ -2147467259: raised: Unspecified error error: Application-defined or object-defined error +-2147348207: + raised: Automation error + error: Application-defined or object-defined error +-2147467262: + raised: No such interface supported + error: Application-defined or object-defined error +-2147352567: + raised: Exception occurred. + error: Application-defined or object-defined error +-2147352562: + raised: Invalid number of parameters. + error: Application-defined or object-defined error +-2147352559: + raised: Does not support a collection. + error: Application-defined or object-defined error +-2147221021: + raised: Operation unavailable + error: Application-defined or object-defined error +-2147221020: + raised: Invalid syntax + error: Application-defined or object-defined error +-2147221014: + raised: Moniker cannot open file + error: Application-defined or object-defined error +-2147221005: + raised: Invalid class string + error: Application-defined or object-defined error +-2147221164: + raised: Class not registered + error: Application-defined or object-defined error +-2147023174: + raised: The RPC server is unavailable. + error: Application-defined or object-defined error diff --git a/scripts/check_errors.mjs b/scripts/check_errors.mjs index 68febe136..f1b87cbbb 100644 --- a/scripts/check_errors.mjs +++ b/scripts/check_errors.mjs @@ -24,7 +24,8 @@ // 1 a probe or the corpus failed // 2 the gate could not run: a refused command line, or a crash -import { existsSync, readFileSync } from "node:fs"; +import { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs"; +import os from "node:os"; import path from "node:path"; import yaml from "js-yaml"; import { exitOnCrash, parseCli, printHelpAndExit, withUsageError } from "../lib/cli.mjs"; @@ -35,6 +36,7 @@ import { CENTRAL_SHORTCUT, caseAtLine, compareMessages, + expectedDescription, dataFiles, findSection, loadDataFile, @@ -154,11 +156,11 @@ const section = renderSection(page, { permalink: "/tB/Modules/Strings/Mid", }); check( - "the section has the table, its numbers linked to the central page by its shortcut, and the VB6 note", + "the section has the table, its numbers linked to the central page by its shortcut, and the note for VB6 and 64-bit VBA", section .join("\n") .includes( - "| [5 (`&H5`)](tb:Errors#5), *Invalid procedure call or argument* | *x* is negative |\n| [9 (`&H9`)](tb:Errors#9), *Subscript out of range* | the index is out |\n\n> [!NOTE] BETA 1010\n> When the index is out, VB6 raises error [5 (`&H5`)](tb:Errors#5), *Invalid procedure call or argument*, instead.", + "| [5 (`&H5`)](tb:Errors#5), *Invalid procedure call or argument* | *x* is negative |\n| [9 (`&H9`)](tb:Errors#9), *Subscript out of range* | the index is out |\n\n> [!NOTE] BETA 1010\n> When the index is out, VB6 and 64-bit VBA raise error [5 (`&H5`)](tb:Errors#5), *Invalid procedure call or argument*, instead.", ), ); const merged = renderSection( @@ -179,17 +181,97 @@ check( ); check("a page with only quiet cases gets no section", renderSection({ rows: [], quiet: [1] }, { messages }) === null); const vb6Only = renderSection( - { rows: [{ error: 0, when: "*x* is 2", vb6: 5, bug: "pending", cases: [] }], quiet: [] }, + { rows: [{ error: 0, when: "*x* is 2", vb6: 5, bug: "pending", cases: [{}] }], quiet: [] }, { name: "probe.yml", beta: 1010, messages, level: "##", permalink: "/A" }, ).join("\n"); check( - "a row of error 0 has its VB6 note and no table", + "a row of error 0 has its note and no table", !vb6Only.includes("| Error |") && vb6Only.includes( - "## Common Errors\n\n> [!NOTE] BETA 1010\n> When *x* is 2, VB6 raises error [5 (`&H5`)](tb:Errors#5), *Invalid procedure call or argument*; twinBASIC raises none.\n\n", + "## Common Errors\n\n> [!NOTE] BETA 1010\n> When *x* is 2, VB6 and 64-bit VBA raise error [5 (`&H5`)](tb:Errors#5), *Invalid procedure call or argument*; twinBASIC raises none.\n\n", ), vb6Only, ); +const noteOf = (row) => + renderSection( + { rows: [{ when: "*x* is 2", cases: [{}], ...row }], quiet: [] }, + { name: "probe.yml", beta: 1010, messages, level: "##", permalink: "/A" }, + ).find((l) => l.startsWith("> When")); +const messageRows = renderSection( + { + rows: [ + { error: 5, when: "*x* is 2", cases: [{}] }, + { error: 5, when: "*y* is 3", message: "expected an array", cases: [{}] }, + ], + quiet: [], + }, + { name: "probe.yml", beta: 1010, messages, level: "##", permalink: "/A" }, +).filter((l) => l.startsWith("| [")); +check( + "a row's message: is its text in the table, and rows of one number with two texts stay apart", + messageRows.length === 2 && messageRows[1].includes("*expected an array* | *y* is 3 |"), + messageRows.join("\n"), +); +check( + "the description run checks: a row's message: in twinBASIC, none for an HRESULT in VB6 or VBA", + expectedDescription({ error: 5, message: "expected an array" }, "win64", 5, messages) === "expected an array" && + expectedDescription({ error: 5, message: "expected an array" }, "vb6", 5, messages) === + "Invalid procedure call or argument" && + expectedDescription(null, "vba64", -2147467259, messages) === null && + expectedDescription(null, "tb", -2147467259, messages) === "Unspecified error", +); +check( + "64-bit VBA's own difference has a sentence of its own, compared with twinBASIC 64-bit", + noteOf({ error: 5, vba64: 9, bug: "pending" }) === + "> When *x* is 2, 64-bit VBA raises error [9 (`&H9`)](tb:Errors#9), *Subscript out of range*, instead." && + noteOf({ error: 5, vb6: 9, vba64: 0, bug: "pending" }) === + "> When *x* is 2, VB6 raises error [9 (`&H9`)](tb:Errors#9), *Subscript out of range*, instead; 64-bit VBA raises no error." && + noteOf({ error: 5, win64: 0, vba64: 9, bug: "pending" }) === + "> When *x* is 2, 64-bit VBA raises error [9 (`&H9`)](tb:Errors#9), *Subscript out of range*; twinBASIC raises none." && + noteOf({ error: 6, win64: 0 }) === undefined, +); +check( + "a row none of whose cases runs in 64-bit VBA says nothing of it", + noteOf({ error: 0, vb6: 5, bug: "x", cases: [{ only: ["tb", "vb6"] }] }) === + "> When *x* is 2, VB6 raises error [5 (`&H5`)](tb:Errors#5), *Invalid procedure call or argument*; twinBASIC raises none.", +); +const onlies = data(` +beta: 1 +pages: + A.md: + quiet: + - { expr: a, only: tb } + - { expr: b, only: [tb, vba64] } + - { expr: c, only: [tb, vb6] } + - d +`); +check( + "only: says which runtimes run a case, tb meaning both twinBASIC builds", + onlies.problems.length === 0 && + ["tb", "win64", "vb6", "vba64"] + .map((rt) => + probeCases([onlies], rt) + .map((c) => c.label) + .join(""), + ) + .join() === "abcd,abcd,cd,bd", + onlies.problems.join("; "), +); +check( + "only: refuses a list without tb, an unknown runtime and all three", + ["[vb6]", "[tb, win64]", "[tb, vb6, vba64]", "vba64"].every( + (v) => data(`beta: 1\npages:\n A.md:\n quiet:\n - { expr: a, only: ${v} }\n`).problems.length === 1, + ), +); +check( + "a vba64: that is its default is refused, and one that differs from twinBASIC 64-bit needs a bug:", + data("beta: 1\npages:\n A.md:\n rows:\n - { error: 5, vb6: 9, vba64: 9, bug: x, when: x, cases: [x] }\n") + .problems.length === 1 && + data("beta: 1\npages:\n A.md:\n rows:\n - { error: 5, vba64: 9, when: x, cases: [x] }\n").problems + .length === 1 && + data("beta: 1\npages:\n A.md:\n rows:\n - { error: 0, vba64: 9, bug: x, when: x, cases: [x] }\n").problems + .length === 0, +); check( "error 0 is refused without a vb6: number", data("beta: 1\npages:\n A.md:\n rows:\n - { error: 0, when: x, cases: [x] }\n").problems.length === 1, @@ -211,14 +293,21 @@ check( "the level follows the page's Example heading", pageLevel(doc) === "###" && pageLevel("# T\n## See Also\n") === "##", ); +check( + "a page with an Example under each property takes its See Also's level", + pageLevel("# T\n## P\n#### Example\n## Q\n#### Example\n### See Also\n") === "###", +); const cases = probeCases([sample], "tb"); const vb6Cases = probeCases([sample], "vb6"); check( "cases carry what each runtime expects", - cases.map((c) => c.expect).join() === "5,5,9,0" && vb6Cases[2].expect === 5, + cases.map((c) => c.expect).join() === "5,5,9,0" && + vb6Cases[2].expect === 5 && + probeCases([sample], "vba64")[2].expect === 5 && + probeCases([sample], "win64")[2].expect === 9, ); -for (const dialect of ["tb", "vb6"]) { +for (const dialect of ["tb", "vb6", "vba"]) { const mod = probeModule(cases, dialect); const lines = mod.text.split("\r\n"); check( @@ -227,6 +316,80 @@ for (const dialect of ["tb", "vb6"]) { caseAtLine(mod.spans, mod.spans[1].from + 3) === 2, ); } +const harnessed = data(` +beta: 1010 +pages: + A.md: + harness: one + rows: + - { error: 5, when: x, cases: [F(1), { expr: F(2), harness: two }] } + - { error: 6, when: y, harness: two, cases: [F(3)] } + quiet: [F(4)] + B.md: + quiet: [F(5)] +`); +check( + "a case takes its own harness, else its row's, else its page's", + harnessed.problems.length === 0 && + probeCases([harnessed], "tb") + .map((c) => c.harness ?? "-") + .join() === "one,two,two,one,-", + harnessed.problems.join("; "), +); +check( + "a harness is a lowercase name", + data("beta: 1\npages:\n A.md:\n harness: Clock\n quiet: [x]\n").problems.length === 1, +); +for (const dialect of ["tb", "vb6", "vba"]) { + const hc = probeCases([harnessed], dialect); + const code = new Map([ + ["one", "' one's code"], + ["two", "' two's code\r\n"], + ]); + const mod = probeModule(hc, dialect, code); + const lines = mod.text.split("\r\n"); + const c1 = lines.slice(mod.spans[0].from - 1, mod.spans[0].to); + const c5 = lines.slice(mod.spans[4].from - 1, mod.spans[4].to); + check( + `${dialect}: each harness's code comes once, before Report, and its case runs between _Begin and _End`, + lines.filter((l) => l === "' one's code").length === 1 && + lines.indexOf("' two's code") < lines.indexOf("Private Sub Report(ByVal S As String)") && + c1.indexOf(" one_Begin") > c1.indexOf(" On Error Resume Next") && + c1.indexOf(" one_End") === c1.indexOf(" Result = F(1)") + 1 && + c1.some((l) => l.startsWith(" If Err.Number <> 0 Then") && l.endsWith(": one_End: Exit Sub")) && + !c5.some((l) => l.includes("_Begin")), + ); +} +check( + "a harness without its file under errors/harnesses/ is reported", + (() => { + const dir = mkdtempSync(path.join(os.tmpdir(), "check-errors-")); + try { + writeFileSync( + path.join(dir, "x.yml"), + "beta: 1\npages:\n A.md:\n harness: one\n quiet: [F(1), { expr: F(2), harness: two }]\n", + ); + mkdirSync(path.join(dir, "harnesses")); + writeFileSync(path.join(dir, "harnesses", "one.bas"), "' one\n"); + const { problems } = loadDataFile("x.yml", dir); + return problems.length === 1 && problems[0].includes("harness two"); + } finally { + rmSync(dir, { recursive: true, force: true }); + } + })(), +); +check( + "a module whose cases name a harness it has no code for is refused", + (() => { + try { + probeModule(probeCases([harnessed], "tb"), "tb", new Map()); + return false; + } catch { + return true; + } + })(), +); + check( "links are relative to the permalinks", relativeLink("/Reference/Run-time-Errors", "/tB/Core/ReDim") === "../tB/Core/ReDim" && @@ -289,14 +452,14 @@ check( }).length === 1, ); -for (const dialect of ["tb", "vb6"]) { +for (const dialect of ["tb", "vb6", "vba"]) { const lines = messageProbeModule([-2147467259], dialect).split("\r\n"); check( `${dialect}: the messages' probe runs every VB number, then the others, then ends`, lines.includes(" For N = 1 To 65535") && lines.includes(" One -2147467259, True") && lines.filter((l) => l === "Public Sub Main()").length === 1 && - (dialect === "vb6" || lines.includes("End Module")), + (dialect !== "tb" || lines.includes("End Module")), ); } diff --git a/scripts/errors.mjs b/scripts/errors.mjs index e8ebe86e5..c5a487f24 100644 --- a/scripts/errors.mjs +++ b/scripts/errors.mjs @@ -2,10 +2,10 @@ // The run-time errors each documented procedure raises: measuring them, and // writing each page's Common Errors section from the data under errors/. // -// node scripts/errors.mjs run [...] [--only ] [--runtime tb|vb6|win64]... +// node scripts/errors.mjs run [...] [--only ] [--runtime tb|win64|vb6|vba64]... // node scripts/errors.mjs generate [...] [--only ] // node scripts/errors.mjs write [...] -// node scripts/errors.mjs messages [--runtime tb|vb6]... +// node scripts/errors.mjs messages [--runtime tb|win64|vb6|vba64]... // // Exit codes: // 0 run, messages: every runtime agrees with the data; generate, write: done @@ -20,10 +20,12 @@ // raise them, errors/*.yml, whose format WIP.Errors.md describes -- and the // section, the probe and the check are generated from it. `run` builds one // probe from the data for each runtime (a twinBASIC exe through scripts/tbrun.mjs, -// a VB6 exe through lib/vb6.mjs) and compares what each case raised with what -// the data says; `messages` does the same for errors/messages.yml, the message -// of every number. They need twinBASIC and VB6, so they are run by a person when -// a BETA is installed, never by CI. `write` -- each page's section, and the +// 32-bit and 64-bit, a VB6 exe through lib/vb6.mjs, and a module run in 64-bit +// Access through lib/vba.mjs: twinBASIC 32-bit answers to VB6, 64-bit to 64-bit VBA) +// and compares what each case raised with what the data says; `messages` does the +// same for errors/messages.yml, the message of every number. They need twinBASIC, +// VB6 and 64-bit Office, so they are run by a person when a BETA is installed, +// never by CI. `write` -- each page's section, and the // index of the numbers on the central page -- needs neither; check_errors.mjs is // its gate. @@ -37,6 +39,7 @@ import { CENTRAL_PAGE, caseAtLine, compareMessages, + expectedDescription, dataFiles, findSection, loadDataFile, @@ -47,7 +50,9 @@ import { pagePath, probeCases, probeModule, + harnessCode, readMessageOutput, + RUNTIMES, readProbeOutput, renderIndex, renderSection, @@ -56,6 +61,8 @@ import { VB_NUMBERS, } from "./lib/errors-data.mjs"; import { encodeAnsi, findVb6, NO_VB6, parseMakeLog, runRepro } from "./lib/vb6.mjs"; +import { faultText } from "./lib/win-fault.mjs"; +import { findAccess64, NO_VBA64, VBA_MODULE, VbaSession } from "./lib/vba.mjs"; exitOnCrash(); @@ -71,8 +78,8 @@ named, every file there is used. run builds a probe of the cases for each runtime, runs it, and compares what each case raised with the data. A case the compiler refuses is reported and left out, and the probe built again without it. - generate writes the probes to /tb (a tree for scripts/tbrun.mjs --exe) - and /vb6 (Probe.vbp), without running them + generate writes the probes to /tb (a tree for scripts/tbrun.mjs --exe), + /vb6 (Probe.vbp) and /vba (Module1.bas), without running them write writes each page's Common Errors section from the data, and the index of the numbers on the central page, docs/Reference/Run-time-Errors.md messages builds a probe of the messages for each runtime, runs it, and @@ -81,9 +88,8 @@ named, every file there is used. other number the file lists --only only the pages whose path under docs/ matches - --runtime R tb (32-bit twinBASIC), vb6 or win64 (64-bit twinBASIC); may be - repeated. By default tb and vb6, and win64 for a file that says - win64: true; messages measures tb and vb6 only + --runtime R tb (32-bit twinBASIC), win64 (64-bit twinBASIC), vb6, or vba64 + (64-bit VBA, in Access); may be repeated. By default all four --ide P the twinBASIC IDE, as for tbrun (default $TB_IDE, or the newest on the Desktop) --vb6 P VB6.EXE (default $VB6_EXE, or VB6's own install folder) @@ -95,7 +101,6 @@ Exit codes: 1 run, messages: a runtime disagrees with the data 2 a refused command line, bad data, or a runtime that could not be run`; -const RUNTIMES = ["tb", "vb6", "win64"]; const COMMANDS = ["run", "generate", "write", "messages"]; const TBRUN = path.join(REPO_ROOT, "scripts", "tbrun.mjs"); const TB_TEMPLATE = path.join(REPO_ROOT, "test", "example-projects", "console", "Settings"); @@ -137,7 +142,6 @@ if (command !== "run" && command !== "messages") { if ((command === "write" || command === "messages") && only) usage("--only is for run and generate"); if (command === "messages") { if (rest.length) usage("messages takes no data file: it measures errors/messages.yml"); - if (values.runtime.includes("win64")) usage("--runtime: messages measures tb and vb6"); } // ------------------------------------------------------------------ the data @@ -203,7 +207,7 @@ if (command === "write") { function writeTbTree(dir, cases, text) { mkdirSync(path.join(dir, "Sources"), { recursive: true }); cpSync(TB_TEMPLATE, path.join(dir, "Settings")); - const mod = text === undefined ? probeModule(cases, "tb") : { text, spans: [] }; + const mod = text === undefined ? probeModule(cases, "tb", harnessCode(cases)) : { text, spans: [] }; writeFileSync(path.join(dir, "Sources", "Probe.twin"), mod.text); return mod; } @@ -211,17 +215,24 @@ function writeTbTree(dir, cases, text) { function writeVb6Project(dir, cases, text) { mkdirSync(dir, { recursive: true }); cpSync(VB6_PROJECT, path.join(dir, "Probe.vbp")); - const mod = text === undefined ? probeModule(cases, "vb6") : { text, spans: [] }; + const mod = text === undefined ? probeModule(cases, "vb6", harnessCode(cases)) : { text, spans: [] }; writeFileSync(path.join(dir, "Module1.bas"), encodeAnsi(mod.text)); return mod; } +const vbaModule = (cases) => probeModule(cases, "vba", harnessCode(cases)); + if (command === "generate") { const tb = probeCases(files, "tb", { only }); const vb6 = probeCases(files, "vb6", { only }); + const vba = probeCases(files, "vba64", { only }); writeTbTree(path.join(outDir, "tb"), tb); writeVb6Project(path.join(outDir, "vb6"), vb6); - console.log(`errors: ${tb.length} cases in ${path.join(outDir, "tb")}, ${vb6.length} in ${path.join(outDir, "vb6")}`); + mkdirSync(path.join(outDir, "vba"), { recursive: true }); + writeFileSync(path.join(outDir, "vba", VBA_MODULE), encodeAnsi(vbaModule(vba).text)); + console.log( + `errors: ${tb.length} cases in ${path.join(outDir, "tb")}, ${vb6.length} in ${path.join(outDir, "vb6")}, ${vba.length} in ${path.join(outDir, "vba")}`, + ); process.exit(0); } @@ -290,7 +301,7 @@ async function runTb(dir, cases, arch, text) { if (!json?.exeRun) return { harness: `tbrun exit ${r.code}: ${(r.stderr || r.stdout).trim().split("\n").slice(-3).join(" / ")}` }; const e = json.exeRun; - const note = [e.fault, e.timedOut && "timed out", ...e.dialogs.map((d) => `a box: ${d.text}`)] + const note = [e.fault && faultText(e.fault), e.timedOut && "timed out", ...e.dialogs.map((d) => `a box: ${d.text}`)] .filter(Boolean) .join("; "); return { lines: e.lines, note }; @@ -312,21 +323,72 @@ async function runVb6(dir, cases, vb6, text) { if (id === null) return { harness: `VB6 did not build the probe: ${message}` }; return { refused: new Map([[id, message]]) }; } - const note = [r.fault, r.timedOut && "timed out", ...r.dialogs.map((d) => `a box: ${d.text}`)] + const note = [r.fault && faultText(r.fault), r.timedOut && "timed out", ...r.dialogs.map((d) => `a box: ${d.text}`)] + .filter(Boolean) + .join("; "); + return { lines: r.lines, note }; +} + +// One Access for the whole run (lib/vba.mjs, 4), started by the first probe that needs it. +let vbaSession = null; + +async function runVba64(dir, cases, text) { + vbaSession ??= new VbaSession(); + const mod = text === undefined ? vbaModule(cases) : { text, spans: [] }; + let r; + try { + r = await vbaSession.runModule(dir, mod.text, { timeoutMs: text === undefined ? 120000 : 600000 }); + } catch (e) { + return { harness: e.message.split("\n")[0] }; + } + const compile = r.boxes.find((b) => /^Compile error/i.test(b.text)); + if (compile) { + // VBA compiles a case's Sub as Main first calls it, and Main calls them in order: the + // refused one is the first that did not report. + if (!r.lines.includes("start")) return { harness: `an error in the module's declarations: ${compile.text}` }; + const { seen } = readProbeOutput(r.lines); + const id = cases.find((c) => !seen.has(c.id))?.id; + if (id === undefined) return { harness: `a compile error after every case: ${compile.text}` }; + return { refused: new Map([[id, compile.text.replace(/^Compile error:\s*/i, "")]]) }; + } + const note = [ + r.timedOut && "timed out", + ...r.boxes.map((b) => `a box: ${b.text}`), + r.result && r.result !== "ran" && r.result, + !r.result && !r.timedOut && "Access ended", + ] .filter(Boolean) .join("; "); return { lines: r.lines, note }; } +// The runtimes a command measures, and what each needs found first. +function runtimesWanted() { + const runtimes = values.runtime.length ? RUNTIMES.filter((r) => values.runtime.includes(r)) : RUNTIMES; + let vb6 = null; + if (runtimes.includes("vb6")) { + vb6 = findVb6(values.vb6); + if (!vb6) die(2, values.vb6 ? `no such file: ${values.vb6}\n${NO_VB6}` : NO_VB6); + } + if (runtimes.includes("vba64") && !findAccess64()) die(2, NO_VBA64); + return { runtimes, vb6 }; +} + +async function finish() { + await vbaSession?.close(); + if (values.keep) console.log(`errors: the probes are in ${work}`); + else rmSync(work, { recursive: true, force: true }); +} + if (command === "messages") { // The numbers past VB's own that the list gives are measured as well. const others = [...messages.keys()].filter((n) => n < VB_NUMBERS.from || n > VB_NUMBERS.to).sort((a, b) => a - b); - const runtimes = values.runtime.length ? [...new Set(values.runtime)] : ["tb", "vb6"]; - const vb6 = runtimes.includes("vb6") ? findVb6(values.vb6) : null; - if (runtimes.includes("vb6") && !vb6) die(2, values.vb6 ? `no such file: ${values.vb6}\n${NO_VB6}` : NO_VB6); + const { runtimes, vb6 } = runtimesWanted(); const run = { tb: (d) => runTb(d, [], "win32", messageProbeModule(others, "tb")), + win64: (d) => runTb(d, [], "win64", messageProbeModule(others, "tb")), vb6: (d) => runVb6(d, [], vb6, messageProbeModule(others, "vb6")), + vba64: (d) => runVba64(d, [], messageProbeModule(others, "vba")), }; const seen = {}; const broken = []; @@ -340,8 +402,7 @@ if (command === "messages") { else broken.push(`${rt}: the probe stopped before it ended${got.note ? `: ${got.note}` : ""}`); }), ); - if (values.keep) console.log(`errors: the probes are in ${work}`); - else rmSync(work, { recursive: true, force: true }); + await finish(); if (broken.length) die(2, broken.join("\n")); const wrong = compareMessages(messages, seen); for (const w of wrong) console.log(` ${w}`); @@ -352,31 +413,20 @@ if (command === "messages") { process.exit(wrong.length ? 1 : 0); } -const wanted = values.runtime.length ? values.runtime : null; -const plan = []; -const want = (rt) => (wanted ? wanted.includes(rt) : rt !== "win64"); -if (want("tb")) plan.push(["tb", (d, c) => runTb(d, c, "win32")]); -if (wanted ? wanted.includes("win64") : files.some((f) => f.win64)) { - plan.push(["win64", (d, c) => runTb(d, c, "win64")]); -} -if (want("vb6")) { - const vb6 = findVb6(values.vb6); - if (!vb6) die(2, values.vb6 ? `no such file: ${values.vb6}\n${NO_VB6}` : NO_VB6); - plan.push(["vb6", (d, c) => runVb6(d, c, vb6)]); -} +const { runtimes, vb6 } = runtimesWanted(); +const runners = { + tb: (d, c) => runTb(d, c, "win32"), + win64: (d, c) => runTb(d, c, "win64"), + vb6: (d, c) => runVb6(d, c, vb6), + vba64: (d, c) => runVba64(d, c), +}; +const plan = runtimes.map((rt) => [rt, runners[rt]]); -const caseLists = Object.fromEntries( - plan.map(([rt]) => { - // win64 is measured for the files that ask for it, or for all when --runtime names it. - const from = rt === "win64" && !wanted?.includes("win64") ? files.filter((f) => f.win64) : files; - return [rt, probeCases(from, rt, { only })]; - }), -); +const caseLists = Object.fromEntries(plan.map(([rt]) => [rt, probeCases(files, rt, { only })])); const measured = Object.fromEntries( await Promise.all(plan.map(async ([rt, run]) => [rt, await measure(rt, caseLists[rt], run)])), ); -if (values.keep) console.log(`errors: the probes are in ${work}`); -else rmSync(work, { recursive: true, force: true }); +await finish(); // ------------------------------------------------------------------ the report @@ -410,10 +460,11 @@ for (const c of [...all.values()].sort((a, b) => a.id - b.id)) { else if (r.missing) wrong.push(`${rt} reported nothing`); else if (r.setup) wrong.push(`${rt}: its setup raised ${r.number}, ${r.description}`); else if (r.number !== mine.expect) wrong.push(`${rt} raised ${r.number}, the data says ${mine.expect}`); - else if (r.number !== 0 && r.description !== messages.get(r.number)?.raised) { - wrong.push( - `${rt}'s message is "${r.description}", errors/messages.yml has "${messages.get(r.number)?.raised ?? "nothing"}"`, - ); + else if (r.number !== 0) { + const want = expectedDescription(mine.row, rt, r.number, messages); + if (want !== null && r.description !== want) { + wrong.push(`${rt}'s message is "${r.description}", the data and errors/messages.yml say "${want}"`); + } } } console.log( diff --git a/scripts/lib/cli-cases.mjs b/scripts/lib/cli-cases.mjs index f07833129..33419be83 100644 --- a/scripts/lib/cli-cases.mjs +++ b/scripts/lib/cli-cases.mjs @@ -127,12 +127,11 @@ const CASES = [ { tool: "scripts/errors.mjs", args: ["measure"], exit: 2, stderr: /^unknown command: measure\nusage: node scripts\/errors\.mjs / }, { tool: "scripts/errors.mjs", args: ["run", "--help", "--bogus"], exit: 0, stdout: /^usage: node scripts\/errors\.mjs / }, { tool: "scripts/errors.mjs", args: ["generate"], exit: 2, stderr: /^generate needs the folder to write to\nusage: node scripts\/errors\.mjs / }, - { tool: "scripts/errors.mjs", args: ["run", "--runtime", "vb5"], exit: 2, stderr: /^--runtime: one of tb, vb6, win64\nusage: node scripts\/errors\.mjs / }, + { tool: "scripts/errors.mjs", args: ["run", "--runtime", "vb5"], exit: 2, stderr: /^--runtime: one of tb, win64, vb6, vba64\nusage: node scripts\/errors\.mjs / }, { tool: "scripts/errors.mjs", args: ["write", "--only", "Mid"], exit: 2, stderr: /^--only is for run and generate\nusage: node scripts\/errors\.mjs / }, { tool: "scripts/errors.mjs", args: ["write", "--keep"], exit: 2, stderr: /^--keep is for run and messages\nusage: node scripts\/errors\.mjs / }, { tool: "scripts/errors.mjs", args: ["generate", "x", "--runtime", "tb"], exit: 2, stderr: /^--runtime is for run and messages\nusage: node scripts\/errors\.mjs / }, { tool: "scripts/errors.mjs", args: ["messages", "Core.yml"], exit: 2, stderr: /^messages takes no data file: it measures errors\/messages\.yml\nusage: node scripts\/errors\.mjs / }, - { tool: "scripts/errors.mjs", args: ["messages", "--runtime", "win64"], exit: 2, stderr: /^--runtime: messages measures tb and vb6\nusage: node scripts\/errors\.mjs / }, { tool: "scripts/errors.mjs", args: ["messages", "--only", "Mid"], exit: 2, stderr: /^--only is for run and generate\nusage: node scripts\/errors\.mjs / }, { tool: "scripts/errors.mjs", args: ["write", "no-such.yml"], exit: 2, stderr: "no such data file: errors/no-such.yml\n" }, { tool: "scripts/addin_test.mjs", args: ["--help"], exit: 0, stdout: /^usage: node scripts\/addin_test\.mjs / }, diff --git a/scripts/lib/errors-data.mjs b/scripts/lib/errors-data.mjs index 736f948e3..caf02428d 100644 --- a/scripts/lib/errors-data.mjs +++ b/scripts/lib/errors-data.mjs @@ -12,11 +12,14 @@ import { DOCS_DIR, REPO_ROOT } from "../../lib/repo-paths.mjs"; export const ERRORS_DIR = path.join(REPO_ROOT, "errors"); export const MESSAGES_FILE = "messages.yml"; +/** The harnesses, errors/harnesses/.bas: module-level code a case can run under (WIP.Errors.md). */ +export const HARNESS_DIR = "harnesses"; +const HARNESS_NAME = /^[a-z][a-z0-9]*$/; -const ROW_KEYS = new Set(["error", "when", "cases", "vb6", "win64", "bug"]); -const CASE_KEYS = new Set(["expr", "run", "decl", "setup", "only"]); -const PAGE_KEYS = new Set(["rows", "quiet", "none"]); -const FILE_KEYS = new Set(["beta", "win64", "pages"]); +const ROW_KEYS = new Set(["error", "when", "cases", "vb6", "win64", "vba64", "bug", "message", "harness"]); +const CASE_KEYS = new Set(["expr", "run", "decl", "setup", "only", "harness"]); +const PAGE_KEYS = new Set(["rows", "quiet", "none", "harness"]); +const FILE_KEYS = new Set(["beta", "pages"]); // ------------------------------------------------------------------ the data @@ -61,31 +64,63 @@ export function loadMessages(dir = ERRORS_DIR) { const isInt = (v) => Number.isInteger(v); +// What `only:` may name: twinBASIC (both builds), VB6 and 64-bit VBA. `tb` alone is written +// as a word, any other choice as a list, and every list names tb. +const ONLY = ["tb", "vb6", "vba64"]; +function readOnly(v, where, problems) { + if (v === undefined) return undefined; + const list = v === "tb" ? ["tb"] : Array.isArray(v) ? v : null; + if ( + !list || + !list.includes("tb") || + list.some((x) => !ONLY.includes(x)) || + new Set(list).size !== list.length || + list.length === ONLY.length + ) { + problems.push(`${where}: only: tb, or a list of tb and vb6 or vba64`); + return undefined; + } + return list; +} + +/** Whether case `c` runs in `runtime` (one of RUNTIMES), as its only: says. */ +export const runsIn = (c, runtime) => !c.only || c.only.includes(runtime === "win64" ? "tb" : runtime); + // A case as the data writes it, a string (an expression) or a mapping, made one shape: -// { expr?, run?, decl: [], setup: [], only? } -function normaliseCase(c, where, problems) { - if (typeof c === "string") return { expr: c, decl: [], setup: [] }; +// { expr?, run?, decl: [], setup: [], only?, harness? }, `only` a list as readOnly makes it; a +// case without a harness of its own takes its row's, else its page's (`inherited`). +function normaliseCase(c, where, problems, inherited) { + if (typeof c === "string") return { expr: c, decl: [], setup: [], harness: inherited }; if (!c || typeof c !== "object" || Array.isArray(c)) { problems.push(`${where}: a case is an expression or a mapping`); return null; } for (const k of Object.keys(c)) if (!CASE_KEYS.has(k)) problems.push(`${where}: unknown key ${k}`); if ((c.expr === undefined) === (c.run === undefined)) problems.push(`${where}: give one of expr and run`); - if (c.only !== undefined && c.only !== "tb") problems.push(`${where}: only: can only be tb`); + const only = readOnly(c.only, where, problems); + checkHarness(c.harness, where, problems); const lines = (v) => (v === undefined ? [] : String(v).replace(/\r/g, "").replace(/\n$/, "").split("\n")); return { expr: c.expr === undefined ? undefined : String(c.expr), run: c.run === undefined ? undefined : lines(c.run), decl: lines(c.decl), setup: lines(c.setup), - only: c.only, + only, + harness: c.harness ?? inherited, }; } +function checkHarness(name, where, problems) { + if (name !== undefined && (typeof name !== "string" || !HARNESS_NAME.test(name))) + problems.push( + `${where}: harness: a name of lowercase letters and digits, the file errors/${HARNESS_DIR}/.bas`, + ); +} + /** * One data file, read and checked. `name` is relative to `dir`. - * @returns {{name, beta, win64, pages: Array<{page, rows, quiet, none}>, problems: string[]}} - * a row is { error, when, vb6?, win64?, bug?, cases }; `page` is relative to docs/ + * @returns {{name, beta, pages: Array<{page, rows, quiet, none}>, problems: string[]}} + * a row is { error, when, vb6?, win64?, vba64?, bug?, message?, cases }; `page` is relative to docs/ */ export function loadDataFile(name, dir = ERRORS_DIR) { const problems = []; @@ -95,14 +130,22 @@ export function loadDataFile(name, dir = ERRORS_DIR) { } catch (e) { return { name, pages: [], problems: [`${name}: ${e.message.split("\n")[0]}`] }; } - return { name, ...readData(raw, name, problems), problems }; + const data = readData(raw, name, problems); + const names = new Set( + data.pages.flatMap((p) => [...p.rows.flatMap((r) => r.cases), ...p.quiet].map((c) => c.harness)), + ); + for (const h of names) { + if (h !== undefined && HARNESS_NAME.test(h) && !existsSync(path.join(dir, HARNESS_DIR, `${h}.bas`))) + problems.push(`${name}: harness ${h}: no file errors/${HARNESS_DIR}/${h}.bas`); + } + return { name, ...data, problems }; } /** The checking half of loadDataFile, over an already parsed document. */ export function readData(raw, name, problems) { if (!raw || typeof raw !== "object") { problems.push(`${name}: not a mapping`); - return { beta: null, win64: false, pages: [] }; + return { beta: null, pages: [] }; } for (const k of Object.keys(raw)) if (!FILE_KEYS.has(k)) problems.push(`${name}: unknown key ${k}`); if (!isInt(raw.beta)) problems.push(`${name}: beta: the BETA the data was measured on, a number`); @@ -116,6 +159,7 @@ export function readData(raw, name, problems) { continue; } for (const k of Object.keys(entry)) if (!PAGE_KEYS.has(k)) problems.push(`${at}: unknown key ${k}`); + checkHarness(entry.harness, at, problems); const rows = []; (entry.rows ?? []).forEach((r, i) => { const w = `${at}: row ${i + 1}`; @@ -124,28 +168,52 @@ export function readData(raw, name, problems) { return; } for (const k of Object.keys(r)) if (!ROW_KEYS.has(k)) problems.push(`${w}: unknown key ${k}`); - // error: 0 is a condition where twinBASIC raises nothing and VB6 does: a NOTE, no table row. - if (!isInt(r.error) || (r.error === 0 && !r.vb6)) { - problems.push(`${w}: error: a number other than 0, or 0 with a vb6: other than 0`); + // error: 0 is a condition where twinBASIC raises nothing and VB6 or 64-bit VBA does: a + // NOTE, no table row. + if (!isInt(r.error) || (r.error === 0 && !r.vb6 && !r.vba64)) { + problems.push(`${w}: error: a number other than 0, or 0 with a vb6: or vba64: other than 0`); } if (typeof r.when !== "string" || !r.when.trim()) problems.push(`${w}: when: the condition, in the reader's terms`); else if (/\||\n/.test(r.when.trim())) problems.push(`${w}: when: one line, without |`); if (r.vb6 !== undefined && !isInt(r.vb6)) problems.push(`${w}: vb6: a number`); if (r.vb6 !== undefined && r.vb6 === r.error) problems.push(`${w}: vb6: the same as error; leave it out`); - if (r.vb6 !== undefined && typeof r.bug !== "string") { - problems.push(`${w}: bug: a row that differs from VB6 names its bugs/ slug, or pending`); - } - if (r.bug !== undefined && r.vb6 === undefined) problems.push(`${w}: bug: only on a row with vb6:`); if (r.win64 !== undefined && !isInt(r.win64)) problems.push(`${w}: win64: a number`); + if (r.win64 !== undefined && r.win64 === r.error) problems.push(`${w}: win64: the same as error; leave it out`); + if (r.vba64 !== undefined && !isInt(r.vba64)) problems.push(`${w}: vba64: a number`); + if (r.vba64 !== undefined && r.vba64 === vba64Default(r)) { + problems.push(`${w}: vba64: what it would be without it (vb6:, else win64:, else error:); leave it out`); + } + const differs = r.vb6 !== undefined || vba64Differs(r); + if (differs && typeof r.bug !== "string") { + problems.push(`${w}: bug: a row that differs from VB6 or 64-bit VBA names its bugs/ slug, or pending`); + } + if (r.bug !== undefined && !differs) { + problems.push(`${w}: bug: only on a row with vb6:, or with a vba64: that differs from twinBASIC 64-bit`); + } + // message: twinBASIC's own Err.Description for the row's number, where it is not the + // number's message in messages.yml. + if (r.message !== undefined && (typeof r.message !== "string" || !r.message.trim() || /\n/.test(r.message))) + problems.push(`${w}: message: twinBASIC's Err.Description, one line`); + if (r.message !== undefined && r.error === 0) problems.push(`${w}: message: only on a row that raises`); + checkHarness(r.harness, w, problems); if (!Array.isArray(r.cases) || !r.cases.length) problems.push(`${w}: cases: at least one`); const cases = (Array.isArray(r.cases) ? r.cases : []) - .map((c, j) => normaliseCase(c, `${w} case ${j + 1}`, problems)) + .map((c, j) => normaliseCase(c, `${w} case ${j + 1}`, problems, r.harness ?? entry.harness)) .filter(Boolean); - rows.push({ error: r.error, when: String(r.when ?? "").trim(), vb6: r.vb6, win64: r.win64, bug: r.bug, cases }); + rows.push({ + error: r.error, + when: String(r.when ?? "").trim(), + vb6: r.vb6, + win64: r.win64, + vba64: r.vba64, + bug: r.bug, + message: r.message, + cases, + }); }); const quiet = (entry.quiet ?? []) - .map((c, j) => normaliseCase(c, `${at}: quiet ${j + 1}`, problems)) + .map((c, j) => normaliseCase(c, `${at}: quiet ${j + 1}`, problems, entry.harness)) .filter(Boolean); if (entry.none !== undefined && (typeof entry.none !== "string" || rows.length)) { problems.push(`${at}: none: a sentence, on a page without rows`); @@ -153,13 +221,45 @@ export function readData(raw, name, problems) { if (!rows.length && !quiet.length) problems.push(`${at}: neither rows nor quiet cases`); pages.push({ page, rows, quiet, none: entry.none }); } - return { beta: raw.beta ?? null, win64: raw.win64 === true, pages }; + return { beta: raw.beta ?? null, pages }; } // ------------------------------------------------------------------ the probes +// What each runtime is expected to raise for a row. twinBASIC 64-bit raises what 32-bit does +// unless the row says win64:. 64-bit VBA raises what VB6 does where the row says vb6: (a +// difference of twinBASIC's from the VB family), else what twinBASIC 64-bit does, unless the +// row says vba64:. +const vba64Default = (r) => r.vb6 ?? r.win64 ?? r.error; +/** What `runtime` ("tb", "win64", "vb6" or "vba64") is expected to raise for `row`. */ +export function expected(row, runtime) { + if (runtime === "win64") return row.win64 ?? row.error; + if (runtime === "vb6") return row.vb6 ?? row.error; + if (runtime === "vba64") return row.vba64 ?? vba64Default(row); + return row.error; +} +/** Whether 64-bit VBA raises something other than twinBASIC 64-bit for `row`. */ +export const vba64Differs = (row) => expected(row, "vba64") !== expected(row, "win64"); + +/** The runtimes `run` measures: twinBASIC 32-bit and 64-bit, VB6 and 64-bit VBA. */ +export const RUNTIMES = ["tb", "win64", "vb6", "vba64"]; +/** The runtimes of the VB family, whose Err.Raise turns HRESULTs into VB numbers. */ +const VB_FAMILY = new Set(["vb6", "vba64"]); + /** - * The cases of `files` to run in `runtime` ("tb", "vb6" or "win64"), each with + * The Err.Description `runtime` must give when a case of `row` (null for a quiet case) raises + * `number`, or null when it is not checked: VB6 and VBA describe an HRESULT as "Automation + * error" with Windows' text on a line of its own, which the probe's one line cannot hold. A row's + * `message:` is twinBASIC's own text for its number, where twinBASIC gives one. + */ +export function expectedDescription(row, runtime, number, messages) { + if (VB_FAMILY.has(runtime) && (number < VB_NUMBERS.from || number > VB_NUMBERS.to)) return null; + if (row?.message !== undefined && !VB_FAMILY.has(runtime) && number === expected(row, runtime)) return row.message; + return messages.get(number)?.raised ?? "nothing"; +} + +/** + * The cases of `files` to run in `runtime` (one of RUNTIMES), each with * what it expects. Ids are 1-based and stable for one list of files. */ export function probeCases(files, runtime, { only } = {}) { @@ -170,10 +270,8 @@ export function probeCases(files, runtime, { only } = {}) { const take = !only || only.test(p.page); const push = (c, row, label) => { id++; - if (!take || (runtime === "vb6" && c.only === "tb")) return; - let expect = row ? row.error : 0; - if (row && runtime === "vb6" && row.vb6 !== undefined) expect = row.vb6; - if (row && runtime === "win64" && row.win64 !== undefined) expect = row.win64; + if (!take || !runsIn(c, runtime)) return; + const expect = row ? expected(row, runtime) : 0; out.push({ id, file: f.name, page: p.page, label, row, expect, ...c }); }; for (const row of p.rows) for (const c of row.cases) push(c, row, c.expr ?? c.run.join(" : ")); @@ -191,9 +289,13 @@ function caseLines(c, report) { ind("Dim Result As Variant"), ...c.decl.map(ind), ind("On Error Resume Next"), + ...(c.harness ? [ind(`${c.harness}_Begin`)] : []), ...c.setup.map(ind), - ind(`If Err.Number <> 0 Then ${report} "setup ${c.id} " & Err.Number & " " & Err.Description: Exit Sub`), + ind( + `If Err.Number <> 0 Then ${report} "setup ${c.id} " & Err.Number & " " & Err.Description: ${c.harness ? `${c.harness}_End: ` : ""}Exit Sub`, + ), ...(c.expr !== undefined ? [ind(`Result = ${c.expr}`)] : c.run.map(ind)), + ...(c.harness ? [ind(`${c.harness}_End`)] : []), ind(`${report} "case ${c.id} " & Err.Number & " " & Err.Description`), "End Sub", ]; @@ -201,17 +303,27 @@ function caseLines(c, report) { /** * The probe module for `cases`: the twinBASIC module (dialect "tb", a .twin file run as - * an exe by tbrun) or the VB6 one (dialect "vb6", Module1.bas of Probe.vbp). Each case - * is a Sub of its own, run under On Error Resume Next, that reports + * an exe by tbrun), the VB6 one (dialect "vb6", Module1.bas of Probe.vbp) or the VBA one + * (dialect "vba", Module1.bas run in Access by lib/vba.mjs, which reports `start` first). + * Each case is a Sub of its own, run under On Error Resume Next, that reports * `case `; Main reports `done` last. + * The code of each harness the cases use comes first, from `harnesses` (name -> text, as + * harnessCode reads it): one file serves every dialect. * @returns {{text: string, spans: Array<{id, from, to}>}} spans are 1-based file lines */ -export function probeModule(cases, dialect) { +export function probeModule(cases, dialect, harnesses = new Map()) { + const used = [...new Set(cases.map((c) => c.harness).filter(Boolean))].sort(); + const harnessLines = used.flatMap((h) => { + if (!harnesses.has(h)) throw new Error(`no code for harness ${h}`); + const text = harnesses.get(h).replace(/\r/g, "").replace(/\n+$/, ""); + return [`' ---- harness ${h}: errors/${HARNESS_DIR}/${h}.bas`, ...text.split("\n")]; + }); const head = dialect === "tb" ? [ "' Generated by scripts/errors.mjs from errors/*.yml; do not edit.", "Module Probe", + ...harnessLines, "Private Sub Report(ByVal S As String)", " TbRun.Out S", "End Sub", @@ -225,15 +337,26 @@ export function probeModule(cases, dialect) { 'Attribute VB_Name = "Module1"', "' Generated by scripts/errors.mjs from errors/*.yml; do not edit.", "Option Explicit", + ...harnessLines, "Private Sub Report(ByVal S As String)", " Dim F As Integer", " F = FreeFile", - ' Open App.Path & "\\out.txt" For Append As #F', + // VBA has no App object: the module runs in Access (lib/vba.mjs), beside its database. + ` Open ${dialect === "vba" ? "CurrentProject.Path" : "App.Path"} & "\\out.txt" For Append As #F`, " Print #F, S", " Close #F", "End Sub", ]; - const lines = [...head, "Public Sub Main()", ...cases.map((c) => ` C${c.id}`), ' Report "done"', "End Sub"]; + // VBA compiles a procedure when it is first called; `start` tells an error in the module's + // declarations, which stops it before Main runs, from one in a case (lib/vba.mjs). + const lines = [ + ...head, + "Public Sub Main()", + ...(dialect === "vba" ? [' Report "start"'] : []), + ...cases.map((c) => ` C${c.id}`), + ' Report "done"', + "End Sub", + ]; const spans = []; for (const c of cases) { const from = lines.length + 1; @@ -244,6 +367,14 @@ export function probeModule(cases, dialect) { return { text: `${lines.join("\r\n")}\r\n`, spans }; } +/** The code of each harness `cases` use, read from `dir`: Map name -> text. */ +export function harnessCode(cases, dir = ERRORS_DIR) { + const out = new Map(); + for (const h of new Set(cases.map((c) => c.harness).filter(Boolean))) + out.set(h, readFileSync(path.join(dir, HARNESS_DIR, `${h}.bas`), "utf8")); + return out; +} + // ------------------------------------------------------------------ the messages /** The numbers whose message is VB's own: Error$ is defined for these. */ @@ -306,7 +437,8 @@ export function readMessageOutput(lines) { * runtime must be listed; each listed number's Error$ must be its `error` in both * runtimes, and the description of the error raised its `raised`: in both runtimes * for a number from 1 to 65535, in twinBASIC only for another (VB6's Err.Raise turns - * many HRESULTs into its own numbers). `seen` maps a runtime to readMessageOutput's map. + * many HRESULTs into its own numbers, and so may VBA's). `seen` maps a runtime to + * readMessageOutput's map. */ export function compareMessages(messages, seen) { const out = []; @@ -320,7 +452,7 @@ export function compareMessages(messages, seen) { continue; } if (g.error !== m.error) out.push(`${n}: ${rt}'s Error$ is "${g.error}", the list has "${m.error}"`); - if (rt !== "vb6" || vbs(n)) { + if (!VB_FAMILY.has(rt) || vbs(n)) { if (g.number !== n) out.push(`${n}: ${rt}'s Err.Raise ${n} raised ${g.number}`); else if (g.description !== m.raised) out.push(`${n}: ${rt}'s description is "${g.description}", the list has "${m.raised}"`); @@ -396,13 +528,18 @@ export function findSection(text) { return { section: found, problems }; } -/** The heading level the page uses for Example and See Also: "##" or "###"; "##" when it has neither. */ +/** + * The heading level the page uses for Example and See Also: "##" or "###"; "##" when it + * has neither. The last such heading decides, so a page whose properties each have an + * Example a level down (Date, Time) takes its See Also's level. + */ export function pageLevel(text) { + let level = "##"; for (const { text: t, fenced } of scan(text.split("\n"))) { const m = !fenced && ANCHOR.exec(t.replace(/\r$/, "")); - if (m) return m[1]; + if (m) level = m[1]; } - return "##"; + return level; } /** An error number in decimal and in VB hexadecimal, as a 32-bit Long: -2147467259 (`&H80004005`). */ @@ -455,22 +592,40 @@ export function renderSection(p, { name, beta, messages, level }) { ]; if (p.none !== undefined) out.push(p.none.trim()); else { - // Rows that raise the same number share one row, their conditions a line each. - // A row of error 0 raises nothing in twinBASIC: it has its NOTE and no table row. + // Rows that raise the same number with the same message share one row, their conditions a + // line each. A row of error 0 raises nothing in twinBASIC: it has its NOTE and no table row. const byError = new Map(); - for (const r of p.rows.filter((x) => x.error !== 0)) - byError.set(r.error, [...(byError.get(r.error) ?? []), escapeCell(r.when)]); + for (const r of p.rows.filter((x) => x.error !== 0)) { + const text = r.message ?? msg(r.error); + const key = `${r.error}\n${text}`; + const entry = byError.get(key) ?? { n: r.error, text, whens: [] }; + entry.whens.push(escapeCell(r.when)); + byError.set(key, entry); + } if (byError.size) out.push("| Error | When |", "|---|---|"); - for (const [n, whens] of byError) out.push(`| ${num(n)}, *${msg(n)}* | ${whens.join("
")} |`); - for (const r of p.rows.filter((x) => x.vb6 !== undefined)) { - const what = - r.vb6 === 0 - ? "raises no error" - : r.error === 0 - ? `raises error ${num(r.vb6)}, *${msg(r.vb6)}*; twinBASIC raises none` - : `raises error ${num(r.vb6)}, *${msg(r.vb6)}*, instead`; + for (const { n, text, whens } of byError.values()) + out.push(`| ${num(n)}, *${escapeCell(text)}* | ${whens.join("
")} |`); + // A row where VB6 or 64-bit VBA raises something else gets a NOTE: one sentence when the + // two agree, one each when they do not. `mine` is what twinBASIC raises where they differ. + const what = (n, mine, plural) => + n === 0 + ? `raise${plural ? "" : "s"} no error` + : `raise${plural ? "" : "s"} error ${num(n)}, *${msg(n)}*${mine === 0 ? "; twinBASIC raises none" : ", instead"}`; + for (const r of p.rows) { + // A row none of whose cases runs in 64-bit VBA says nothing of it. + const vb6 = r.vb6 !== undefined; + const vba = vba64Differs(r) && r.cases.some((c) => runsIn(c, "vba64")); + if (!vb6 && !vba) continue; + const vbaN = expected(r, "vba64"); + const sentences = + vb6 && vba && vbaN === r.vb6 + ? [`VB6 and 64-bit VBA ${what(r.vb6, r.error, true)}`] + : [ + ...(vb6 ? [`VB6 ${what(r.vb6, r.error, false)}`] : []), + ...(vba ? [`64-bit VBA ${what(vbaN, expected(r, "win64"), false)}`] : []), + ]; if (out.at(-1) !== "") out.push(""); - out.push(`> [!NOTE] BETA ${beta}`, `> When ${r.when}, VB6 ${what}.`); + out.push(`> [!NOTE] BETA ${beta}`, `> When ${r.when}, ${sentences.join("; ")}.`); } } out.push("", ""); diff --git a/scripts/lib/vba.mjs b/scripts/lib/vba.mjs new file mode 100644 index 000000000..8f07c89aa --- /dev/null +++ b/scripts/lib/vba.mjs @@ -0,0 +1,307 @@ +// Running VBA modules in 64-bit Office VBA, for scripts/errors.mjs: the runtime that +// twinBASIC's 64-bit build is compared with, as VB6 is the one its 32-bit build is. +// +// ----------------------------------------------------- what had to be learned +// +// 1. VBA HAS NO HOST BUT AN OFFICE APPLICATION. VBE7.DLL holds the compiler and the +// runtime, but only a host starts it, and the SDK that lets a program be one is +// licensed to vendors only. Its exported runtime functions called directly would skip +// what is being compared: the compiled code's own conversions, how a Variant is +// passed, how an error reaches Err. So the module runs in Access. +// 2. ACCESS, NOT EXCEL. Excel imports a module only with "Trust access to the VBA project +// object model" switched on, a security setting that is the user's. Access has no such +// setting: through automation it creates a database, its VBE imports the module, and +// Application.Run runs `Main`. Automation runs code with its AutomationSecurity low, so +// no Trust Center setting or trusted location comes into it either. +// 3. A POWERSHELL DRIVER, STARTED ON A PRIVATE DESKTOP. Node cannot call COM. The driver +// travels as -EncodedCommand, so no script file runs and the execution policy never +// comes into it, and is started by launchOnDesktop: Access, a local server DCOM starts +// for it, opens its windows on the driver's desktop, never on the user's. Access is not +// in the launcher's job, so the driver waits for it to end before it ends itself, and the +// desktop is not given back with Access still on it; Node ends Access by the pid the +// driver writes first whenever it gives up on it. +// 4. ONE ACCESS FOR A WHOLE RUN. Access.Application starts a new MSACCESS.EXE for every +// CreateObject, so COM keeps nothing running between probes: the session does. Its driver +// holds one Access and takes jobs from a file, job.txt, naming a folder; each job is a new +// database in that folder, closed when Main returns. A session that has lost its Access +// (a box it could not get past, a time limit) is started again for the next job. +// 5. A COMPILE ERROR IS A BOX. VBA compiles a procedure when it is first called, so the +// cases before the one it refuses have run and reported; then the VBE opens its +// "Compile error:" box and Run waits on it. The box is read from the desktop's windows +// (it belongs to Access's pid), and the refused case is the first one that did not report. +// Run does not return once the box is answered, since the VBE stays in break mode waiting +// for a person, so that Access is ended and the next job starts another. An error in the module's declarations stops it before +// Main's first line, which the probe reports as `start`, so the two cannot be confused. +// 6. THE MODULE IS READ IN THE ANSI CODE PAGE, as VB6 reads its own, and out.txt is +// written in it. + +import { execFileSync } from "node:child_process"; +import { + closeSync, + existsSync, + mkdirSync, + mkdtempSync, + openSync, + readFileSync, + readSync, + renameSync, + rmSync, + writeFileSync, +} from "node:fs"; +import { tmpdir } from "node:os"; +import path from "node:path"; +import { desktopWindows, killTree, launchOnDesktop, sleep } from "./tb-ide.mjs"; +import { decodeAnsi, encodeAnsi, splitLines } from "./vb6.mjs"; + +export const NO_VBA64 = + "errors: no 64-bit Access: the vba64 runtime runs its probe in 64-bit Microsoft Access, through automation"; + +/** The module and its output in a job's folder. */ +export const VBA_MODULE = "Module1.bas"; +export const VBA_OUT = "out.txt"; +const PID_FILE = "access.pid"; +const LOG_FILE = "driver.log"; +const JOB_FILE = "job.txt"; +const QUIT_FILE = "quit.txt"; +const RESULT_FILE = "result.txt"; + +// The machine field of a PE file's header: 0x8664 for x64. +function peMachine(file) { + const fd = openSync(file, "r"); + try { + const head = Buffer.alloc(4096); + readSync(fd, head, 0, head.length, 0); + const pe = head.readUInt32LE(0x3c); + return head.toString("latin1", pe, pe + 4) === "PE\0\0" ? head.readUInt16LE(pe + 4) : null; + } finally { + closeSync(fd); + } +} + +const regDefault = (key) => { + try { + const out = execFileSync("reg", ["query", key, "/ve"], { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }); + return /REG_(?:EXPAND_)?SZ\s+(.*)$/m.exec(out)?.[1].trim() ?? null; + } catch { + return null; + } +}; + +/** + * The 64-bit MSACCESS.EXE that automation starts, or null: Access.Application's local server + * as the registry gives it, when that file is an x64 program. + */ +export function findAccess64() { + const clsid = regDefault("HKCR\\Access.Application\\CLSID"); + if (!clsid) return null; + const server = regDefault(`HKCR\\CLSID\\${clsid}\\LocalServer32`); + // The path may be quoted, or not though it has spaces, and may be followed by switches. + const exe = server && (/^"([^"]+)"/.exec(server)?.[1] ?? /^(.*?\.exe)(?:\s|$)/i.exec(server)?.[1]); + if (!exe || !existsSync(exe)) return null; + try { + return peMachine(exe) === 0x8664 ? exe : null; + } catch { + return null; + } +} + +// The driver (3, 4). It writes Access's pid, then takes each job.txt as it appears: the job's +// folder, where it writes result.txt once the database is closed again (`ran`, or what went +// wrong). quit.txt ends it. Every step is a line of driver.log; it ends only once Access has. +const DRIVER = String.raw` +$ErrorActionPreference = 'Stop' +$dir = $env:ERRORS_VBA_DIR +$log = Join-Path $dir '${LOG_FILE}' +$jobFile = Join-Path $dir '${JOB_FILE}' +$quitFile = Join-Path $dir '${QUIT_FILE}' +function L($s) { Add-Content -LiteralPath $log -Value $s } +function Msg($e) { ($e.Exception.Message -replace '\s+', ' ').Trim() } +Add-Type -Namespace ErrorsVba -Name U -MemberDefinition '[DllImport("user32.dll")] public static extern uint GetWindowThreadProcessId(System.IntPtr h, out uint pid);' +$app = $null; $accessPid = 0 +try { + $app = New-Object -ComObject Access.Application + [void][ErrorsVba.U]::GetWindowThreadProcessId([IntPtr]$app.hWndAccessApp(), [ref]$accessPid) + Set-Content -LiteralPath (Join-Path $dir '${PID_FILE}') -Value $accessPid + L "access $accessPid" + $app.AutomationSecurity = 1 + while (-not (Test-Path -LiteralPath $quitFile)) { + if (-not (Test-Path -LiteralPath $jobFile)) { Start-Sleep -Milliseconds 50; continue } + $job = (Get-Content -LiteralPath $jobFile -Raw).Trim() + Remove-Item -LiteralPath $jobFile + L "job $job" + $result = 'ran' + $comp = $null + try { + $app.NewCurrentDatabase((Join-Path $job 'Probe.accdb')) + $comp = $app.VBE.ActiveVBProject.VBComponents.Import((Join-Path $job '${VBA_MODULE}')) + $app.Run('Main') + } catch { $result = "run: $(Msg $_)" } + # A module left in the database makes Access ask whether to save it as the database closes. + try { if ($comp) { $app.VBE.ActiveVBProject.VBComponents.Remove($comp) } } catch { $result = "$result; remove: $(Msg $_)" } + try { $app.CloseCurrentDatabase() } catch { $result = "$result; close: $(Msg $_)" } + L $result + Set-Content -LiteralPath (Join-Path $job '${RESULT_FILE}') -Value $result + } + $app.Quit(2) +} catch { + L "error: $(Msg $_)" +} +if ($app) { try { [void][Runtime.InteropServices.Marshal]::FinalReleaseComObject($app) } catch {} } +$app = $null +[GC]::Collect(); [GC]::WaitForPendingFinalizers() +if ($accessPid) { + $p = Get-Process -Id $accessPid -ErrorAction SilentlyContinue + if ($p -and -not $p.WaitForExit(15000)) { Stop-Process -Id $accessPid -Force -ErrorAction SilentlyContinue; L "ended access" } +} +`; + +const POWERSHELL = path.join( + process.env.SystemRoot ?? "C:\\Windows", + "System32", + "WindowsPowerShell", + "v1.0", + "powershell.exe", +); + +// The boxes Access shows on `desktop`: each visible #32770 window of process `pid`, with the +// texts of its Static children joined. +async function accessBoxes(desktop, pid) { + const w = await desktopWindows(desktop, { children: true }); + const boxes = []; + for (let i = 0; i < w.length; i++) { + if (w[i].depth !== 0 || w[i].pid !== pid || !w[i].visible || w[i].class !== "#32770") continue; + const texts = []; + for (let j = i + 1; j < w.length && w[j].depth === 1; j++) { + if (w[j].class === "Static" && w[j].title) texts.push(w[j].title); + } + boxes.push({ title: w[i].title, text: texts.join(" ").replace(/\s+/g, " ").trim() }); + } + return boxes; +} + +/** + * One 64-bit Access, started on a private desktop when the first job needs it, that runs one + * module after another (4). `close` ends it; a run that ends without it leaves nothing behind + * but what the launcher's lease ends with it. + */ +export class VbaSession { + constructor({ pollMs = 1000 } = {}) { + this.pollMs = pollMs; + this.run = null; + this.dir = null; + this.jobs = 0; + } + + get accessPid() { + try { + return Number(readFileSync(path.join(this.dir, PID_FILE), "utf8").trim()) || null; + } catch { + return null; + } + } + + async start() { + this.dir = mkdtempSync(path.join(tmpdir(), "errors-vba-")); + this.run = await launchOnDesktop({ + exe: POWERSHELL, + args: ["-NoProfile", "-NonInteractive", "-EncodedCommand", Buffer.from(DRIVER, "utf16le").toString("base64")], + tag: "errors vba64", + env: { ...process.env, ERRORS_VBA_DIR: this.dir }, + }); + this.exited = false; + this.run.exited.then(() => { + this.exited = true; + }); + } + + // Ends the session: politely when `polite`, else by Access's pid and the launcher's. + async stop(polite = true) { + const run = this.run; + if (!run) return; + this.run = null; + // The driver goes on waiting for jobs after its Access is ended, until quit.txt appears. + if (!this.exited) writeFileSync(path.join(this.dir, QUIT_FILE), ""); + if (!polite) { + const pid = this.accessPid; + if (pid) killTree(pid); + } + let timer; + const ended = await Promise.race([ + run.finished.then(() => true), + new Promise((r) => { + timer = setTimeout(() => r(false), 30000); + }), + ]); + clearTimeout(timer); + if (!ended) { + const pid = this.accessPid; + if (pid) killTree(pid); + killTree(run.pid); + run.launcher.kill(); + await run.finished; + } + rmSync(this.dir, { recursive: true, force: true }); + } + + close() { + return this.stop(true); + } + + /** + * Runs `moduleText` (a VB module whose `Main` reports with a `Report` that appends to out.txt + * beside the database) in folder `dir`. + * + * @returns {Promise<{lines: string[], boxes: {title: string, text: string}[], timedOut: boolean, result: string|null}>} + * `lines` is what out.txt held, `boxes` what Access showed (Access is ended at the first), + * `result` the driver's word on the job, null when Access was lost + */ + async runModule(dir, moduleText, { timeoutMs = 120000 } = {}) { + if (this.run && this.exited) await this.stop(false); + if (!this.run) await this.start(); + mkdirSync(dir, { recursive: true }); + writeFileSync(path.join(dir, VBA_MODULE), encodeAnsi(moduleText)); + const jobTmp = path.join(this.dir, `job-${++this.jobs}.tmp`); + writeFileSync(jobTmp, dir); + renameSync(jobTmp, path.join(this.dir, JOB_FILE)); + const resultFile = path.join(dir, RESULT_FILE); + const boxes = []; + const start = Date.now(); + let timedOut = false; + let lost = false; + while (!existsSync(resultFile)) { + await Promise.race([this.run.exited, sleep(this.pollMs)]); + if (existsSync(resultFile)) break; + if (this.exited) { + lost = true; + break; + } + if (Date.now() - start > timeoutMs) { + timedOut = true; + break; + } + const pid = this.accessPid; + if (!pid) continue; + let seen = []; + try { + seen = await accessBoxes(this.run.desktop, pid); + } catch { + // the desktop is listed again at the next poll + } + // Run does not return after a compile error's box is answered: the VBE stays in break + // mode, waiting for a person. So any box ends this Access, and the next job starts one. + if (seen.length) { + boxes.push(...seen.map((b) => ({ title: b.title, text: b.text }))); + lost = true; + break; + } + } + if (timedOut || lost) await this.stop(false); + const out = existsSync(path.join(dir, VBA_OUT)) ? readFileSync(path.join(dir, VBA_OUT)) : null; + return { + lines: out ? splitLines(decodeAnsi(out)) : [], + boxes, + timedOut, + result: existsSync(resultFile) ? readFileSync(resultFile, "utf8").trim() : null, + }; + } +}