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:

The Compiler Warnings list of the Project Settings dialog, showing warnings TB0001 to TB0016. Each row has a drop-down at its left, set to WARNING except for TB0015, which is set to HINT, then the bold code and the text of the warning

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