Compiler Warnings
twinBASIC reports a warning, as the code is written, for a common bad practice or a likely oversight: code that compiles, but probably does not do what was meant, or uses a feature kept only for compatibility with VBA and VB6. Each warning has a code from TB0001 to TB0037, and the Diagnostics panel lists them with the line they are on.
Adjusting Warnings
Each warning has a level: WARNING, HINT, INFO, IGNORE or ERROR. A warning at IGNORE is not reported, and one at ERROR is reported as an error, so the project does not build. In a new project the Compiler Warnings list of Project Settings shows TB0015, TB0030 and TB0034 at HINT, TB0018 to TB0021, TB0024, TB0025 and TB0029 at IGNORE, and every other warning at WARNING. The compiler reports TB0029, TB0034 and TB0035 at other levels: see TB0029, TB0034 and TB0035.
The levels for the whole project are in the Compiler Warnings group of Project Settings. Each row of its list has a drop-down for the level, then the code and the text of the warning. The Filter warnings… box above the list narrows it to the rows whose code or text holds the words typed:
Three attributes change the level in one class, module or procedure: IgnoreWarnings turns the named warnings off, EnforceWarnings reports them as warnings, also one that is at IGNORE in the project, and EnforceErrors reports them as errors. Each takes one or more codes, written in full with the leading zeroes and not quoted:
[IgnoreWarnings(TB0001)]
Module LegacyMath
DefLng A-Z
End Module
Strict Mode
Four warnings report implicit conversions that VBA makes silently, in the way .NET’s Option Strict does: TB0018, TB0019, TB0020 and TB0021. They are at IGNORE by default. Set them to WARNING or ERROR in Project Settings, or name them in an EnforceWarnings or EnforceErrors attribute, to have the compiler insist that each such conversion is written out. Each can be turned on or off on its own.
The Warnings
TB0001: Use of Legacy DefType
A Deftype statement, such as DefLng, sets the type of every variable whose name starts with one of its letters. The type of a variable then depends on its name rather than on its declaration, which makes the code hard to read and its errors hard to find.
DefLng A-Z
Function Twice(n)
Twice = n * 2
End Function
TB0002: Compiler-Generated Variable
In a file with Option Explicit Off, a name that has not been declared is declared by the compiler, as a Variant. Each such variable draws TB0002, This variable has been auto-declared by the compiler due to Option Explicit being OFF, so a misspelt name does not go unnoticed.
Option Explicit Off
Module Tally
Sub Count()
hits = hits + 1
Debug.Print hits
End Sub
End Module
TB0003: Implicit Variant Data Type
A variable, parameter or function declared without As type is a Variant. TB0003, Datatype has not been explicitly declared so will default to Variant, says so. Write As Variant where a Variant is meant.
Dim total
total = 1
Debug.Print total
TB0004: Wrong Exit Statement
Exit Function or Exit Sub inside a Property Get procedure leaves the property, as VBA allows, but the statement that belongs there is Exit Property.
Private m_Count As Long
Public Property Get Count() As Long
If m_Count < 0 Then Exit Function
Count = m_Count
End Property
TB0005: Warning Directive
A #Warning directive reports its text as warning TB0005, as a reminder in the code.
#Warning "Remove this before the release build"
Debug.Print "testing"
Note BETA 1042
The text of a #Warning or #Error directive stops at its first |: #Warning "before|after" reports only "before.
TB0006: Record Length Is Ignored in Binary Mode
A file opened For Binary is read and written byte by byte, so the Len clause of its Open statement has no effect.
Dim f As Integer
f = FreeFile
Open "data.bin" For Binary As #f Len = 512
Close #f
TB0007: Mismatched Data Type Hint
A type-declaration character on a name that is declared as a different type, such as count$ for a Long.
Dim count As Long
count = 5
Debug.Print count$
TB0008: Suspicious Call
A call to a class’s own Class_Initialize event procedure, Suspicious call to event Class_Initialize. The class’s initialization runs again on an object that already exists. Move the code into a procedure that Class_Initialize and the other caller both call.
Private m_Items As Collection
Private Sub Class_Initialize()
Set m_Items = New Collection
End Sub
Public Sub Clear()
Class_Initialize
End Sub
TB0009: Unimplemented
A call to a procedure marked Unimplemented, [ExportToPdf] has not yet been implemented. The warning is at the call, not at the procedure. Some members of the built-in packages that twinBASIC does not implement yet are marked this way.
[Unimplemented]
Public Sub ExportToPdf()
End Sub
Public Sub SaveReport()
ExportToPdf
End Sub
TB0010: Duplicate DLL Declaration
Reports a DLL entry point that is declared more than once: Duplicate declaration of DLL entry point.
TB0011: Use of Legacy Date/Time Property Let
Reports an assignment to the legacy date and time properties, such as the Date statement, which sets the system date: Use of legacy date/time property-let assignments are strongly discouraged.
TB0012: Possible Hex Literal Misuse
A hexadecimal literal without a type character takes the smallest type it fits in. So &H8000 is the Integer -32,768, and stored in a Long it stays -32,768 rather than becoming the positive 32,768 that was most likely meant, which needs &H8000&. TB0012 reports a literal from &H8000 to &HFFFF converted to a Long or LongLong, and one from &H80000000 to &HFFFFFFFF, a Long, converted to a LongLong. Its message names the literal to write instead: &H8000& for a Long, &H80000000^ for a LongLong.
Dim mask As Long
mask = &H8000
Debug.Print mask ' -32768
mask = &H8000&
Debug.Print mask ' 32768
TB0013: Missing InterfaceId Attribute
A class marked COMControl without an InterfaceId attribute, and again without an EventInterfaceId attribute. A host may cache the identifiers of a control’s interfaces, so they should be fixed in the source rather than made anew by each build.
[COMControl]
Class Gauge
End Class
TB0014: Inline ByVal on a UDT
ByVal written in front of a user-defined type in a call to a Declare procedure has no effect. The structure is passed by reference all the same, and its strings are not converted to ANSI, as in VBA and VB6.
Private Type CursorPoint
X As Long
Y As Long
End Type
Private Declare PtrSafe Function GetCursorPos Lib "user32" (ByRef lpPoint As Any) As Long
Public Sub ShowCursor()
Dim pt As CursorPoint
GetCursorPos ByVal pt
Debug.Print pt.X, pt.Y
End Sub
TB0015: Declare Redirected to twinBASIC
A Declare of a function that twinBASIC implements itself, such as GetMem4 from msvbvm60.dll: This Declare statement has been redirected to an internal twinBASIC implementation, supporting both 32-bit and 64-bit. The call runs twinBASIC’s own code, not the DLL’s, in a 32-bit and a 64-bit build alike. The FAQ lists the functions. It is at HINT by default.
Private Declare PtrSafe Sub GetMem4 Lib "msvbvm60" (ByVal Src As LongPtr, ByVal Dst As LongPtr)
A Declare of a function from a legacy DLL that twinBASIC calls in another DLL instead is TB0034.
TB0016: Implicit ReDim Variable
ReDim of a name that has not been declared declares the array, as VBA allows. Declare it first, with Dim and empty parentheses.
ReDim scores(1 To 10) As Long
scores(1) = 95
TB0017: Inexact Array Alias Argument
Reports an array passed to an array parameter whose element type is a different alias of the same type, which twinBASIC accepts for compatibility with VBA and VB6: array argument does not precisely match the expected type as the alias subtypes are different, but this is allowed for VBx compatibility.
TB0018: Implicit Narrowing Conversion
A value converted to a smaller type without a conversion function, such as a Long assigned to an Integer. The conversion raises an overflow error when the value does not fit. The message names the function to use, here CInt. An enumeration value assigned to an Integer or a Byte draws it too. It is at IGNORE by default.
[EnforceWarnings(TB0018)]
Public Sub Narrow()
Dim i As Integer, l As Long
l = 1000
i = l ' TB0018
i = CInt(l) ' no warning
End Sub
TB0019: Implicit Enumeration Conversion
A value of one enumeration assigned to a variable of another, or a Double, Single, Currency or Variant assigned to a variable of an enumeration. CType(Of type) makes the conversion explicit. It is at IGNORE by default.
[EnforceWarnings(TB0019)]
Public Sub Convert()
Dim answer As VbMsgBoxResult, day As VbDayOfWeek
answer = vbYes
day = answer ' TB0019
day = CType(Of VbDayOfWeek)(answer) ' no warning
End Sub
Note BETA 1042
A member of another enumeration, written as a constant (day = vbOK), converts without a warning.
TB0020: Suspicious Interface Conversion
An object assigned to a variable of an interface or class that its type is not declared to support, suspicious conversion from type StdPicture to type Font. The assignment asks the object for that interface at run time, and fails if it does not have it. CType(Of type) makes the conversion explicit. It is at IGNORE by default.
[EnforceWarnings(TB0020)]
Public Sub Assign()
Dim myPic As StdPicture
Dim myFont As StdFont
Set myFont = myPic ' TB0020
Set myFont = CType(Of StdFont)(myPic) ' no warning
End Sub
TB0021: Implicit Enumeration Conversion to/from Numeric
Reports an implicit conversion between an enumeration and a numeric type. It is at IGNORE by default.
Note BETA 1042
None of these conversions draws TB0021. A whole number assigned to a variable of an enumeration, or an enumeration value assigned to a Long, converts without a warning. A Double, Single, Currency or Variant assigned to a variable of an enumeration draws TB0019, and an enumeration value assigned to an Integer, Byte or Single draws TB0018.
TB0022: Suspicious Assignment
An array assigned to an array of another element type, suspicious array assignment; subtypes do not match.
Note BETA 1042
The message reads only Suspicious array assignment: the IDE cuts the text that en-gb.lang gives it at its semicolon. See Language packs.
Dim counts() As Long
Dim small() As Integer
ReDim small(3)
counts = small
TB0023: Unusual Use of an Implicit Implements Field
In a class, Implements name also declares a field called name, which is Nothing. Code that uses the field draws TB0023, Unusual use of implicit field generated from an Implements statement. To refer to the object as that interface, use Me.
Interface IPrintable
Sub PrintOut()
End Interface
Class Invoice
Implements IPrintable
Private Sub IPrintable_PrintOut() Implements IPrintable.PrintOut
End Sub
Public Function AsPrintable() As IPrintable
Set AsPrintable = IPrintable ' TB0023: the field is Nothing
End Function
End Class
TB0024: Unused Variable
A local variable that is declared but never used, Unused local variable ‘unused’. It is at IGNORE by default.
[EnforceWarnings(TB0024)]
Public Sub Report()
Dim total As Long
Dim unused As String
total = 1
Debug.Print total
End Sub
TB0025: Unused Parameter
A parameter that the procedure never uses, Unused procedure parameter ‘title’. It is at IGNORE by default.
[EnforceWarnings(TB0025)]
Public Sub Greet(name As String, title As String)
Debug.Print "Hello, " & name
End Sub
TB0026: Mismatched Delegate Types
AddressOf of a procedure whose parameters or return type do not match the Delegate type it is assigned or passed to. The program still builds, and the procedure then receives its arguments in a form it does not expect. CType(Of type) makes the conversion explicit, and [EnforceErrors(TB0026)] makes the mismatch an error. See Delegates.
Private Delegate Function LongTransform(ByVal x As Long) As Long
Private Function Half(ByVal x As Integer) As Long
Return x \ 2
End Function
Public Sub UseIt()
Dim t As LongTransform = AddressOf Half ' TB0026
Debug.Print t(10)
End Sub
TB0027: Return Without GoSub
Reports a Return that is not preceded by a GoSub.
TB0028: Control Overshadowed
Reports a control on a form that has the name of a method or property of the form: control [name] is overshadowed by base form method or property of the same name.
TB0029: Missing PtrSafe
A Declare statement without PtrSafe, which 64-bit VBA 7 requires, Declare needs PtrSafe attribute on this build configuration for VBA7 compatibility. It is reported only in a 64-bit build. The Compiler Warnings list shows it at IGNORE by default.
Note BETA 1042
TB0029 is reported as a warning whatever its level: at IGNORE, HINT, INFO or ERROR in the Compiler Warnings list, and under IgnoreWarnings or EnforceErrors on a module or on the Declare. Adding PtrSafe to the Declare removes it.
[EnforceWarnings(TB0029)]
Module Win32Calls
Private Declare Function GetTickCount Lib "kernel32" () As Long
End Module
TB0030: Declare Alias Not Needed
An Alias clause on a Declare statement that names the same entry point as the procedure’s own name. It is at HINT by default.
Private Declare PtrSafe Function GetTickCount Lib "kernel32" Alias "GetTickCount" () As Long
TB0031: Suspicious Use of Erase
Erase of the array a function returns. The function returns a copy of its array, which is freed on its own, so the statement erases nothing that remains.
Private Function Squares() As Long()
Dim r(3) As Long
Return r
End Function
Public Sub Clear()
Erase Squares()
End Sub
TB0032: Unused Line Label
A line label that no GoTo, GoSub, On Error or Resume statement names, Line label is not used.
Dim i As Long
Retry:
i = i + 1
Debug.Print i
TB0033: Duplicate Definition
Reports a duplicate definition as a warning: Duplicate definition, consider changing the name to be unique. A name declared twice in one module, procedure, Enum or Type is error TB5134 instead.
TB0034: Legacy DLL Redirected
A Declare of a function from a legacy system DLL that twinBASIC calls in its modern replacement instead, such as OleCreatePictureIndirect, which olepro32.dll exported and oleaut32.dll now does: Use of legacy DLL olepro32 has been redirected to oleaut32. Up to BETA 1033 the compiler reported it as TB0015.
The Compiler Warnings list shows it at HINT by default, and its level there applies as any other warning’s does, as do IgnoreWarnings and EnforceErrors.
Private Declare PtrSafe Function OleCreatePictureIndirect Lib "olepro32" ( _
ByRef PicDesc As Any, ByRef RefIID As Any, _
ByVal fPictureOwnsHandle As Long, ByRef IPic As Any) As Long
TB0035: Late-Bound Conversion
A value whose type is known only at run time, an Object or a Variant, assigned or passed to a variable or parameter of a class or interface type: Conversion from type Variant to type Font might fail at runtime. Use CType(Of Font)(expression) to avoid this message. An item of a Collection is a Variant, and the result of a function declared As Object is an Object. When the value is not an object of that type, the conversion raises an error at run time, such as error 424, Object required, for a string. CType(Of type) makes the conversion explicit. A conversion from a known type that is not declared to support the target is TB0020 instead. The Compiler Warnings list shows TB0035 at WARNING by default.
Note BETA 1042
In a project whose settings give TB0035 no level, as a new project’s do, TB0035 is not reported, although the Compiler Warnings list shows it at WARNING. Name it in an EnforceWarnings attribute, as below, to have it reported.
[EnforceWarnings(TB0035)]
Public Sub UseFirst(ByVal fonts As Collection)
Dim f As StdFont
Set f = fonts(1) ' TB0035
Set f = CType(Of StdFont)(fonts(1)) ' no warning
End Sub
TB0036: Assignment to an Object With No Default Property
An assignment without Set to a variable of a class that has no default property to take the value, Assignment to class that does not seem to offer a default property-put member. At run time the assignment raises error 438, Object doesn’t support this property or method. Assign to one of the object’s properties, or use Set to assign another object.
Class Counter
Public Total As Long
End Class
Module Usage
Public Sub Reset()
Dim c As New Counter
c = 0 ' TB0036: Counter has no default property
c.Total = 0 ' no warning
End Sub
End Module
TB0037: Implicit Conversion to a Delegate Type
A value that is not a delegate, such as a LongPtr that holds a procedure’s address, a Long or a Variant, assigned or passed to a Delegate type: Implicit conversion to delegate type. Use CType(Of ) to explicitly convert the delegate to a different type. The compiler cannot check that the value is the address of a procedure that matches the delegate. AddressOf of a procedure assigns without this warning, and TB0026 checks the procedure instead. CType(Of type) makes the conversion explicit.
Private Delegate Function LongTransform(ByVal x As Long) As Long
Private Function Quarter(ByVal x As Long) As Long
Return x \ 4
End Function
Public Sub UseIt()
Dim p As LongPtr = AddressOf Quarter
Dim t As LongTransform = p ' TB0037
Dim u As LongTransform = CType(Of LongTransform)(p) ' no warning
Debug.Print t(10), u(10)
End Sub