Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Scanning VBScript for Unused Variables

541 views
Skip to first unread message

Joseph Morales

unread,
Sep 8, 2006, 3:35:28 PM9/8/06
to
Is there an easy/free way to scan a large VBScript for unused variables?

Visual Studio 2005 is supposed to have an autocorrect feature, but I can't
find how to turn it on. Either that, or its always on and it just doesn't
display anything for scripts.

Thanks!
--
Joseph Morales


Richard Mueller

unread,
Sep 11, 2006, 12:24:55 AM9/11/06
to
Joseph Morales wrote:

> Is there an easy/free way to scan a large VBScript for unused variables?
>
> Visual Studio 2005 is supposed to have an autocorrect feature, but I can't
> find how to turn it on. Either that, or its always on and it just doesn't
> display anything for scripts.
>

Hi,

I don't know of any tool or script for this. It would not be easy. However,
I would find it useful myself, so I've attempted to develop a VBScript
program to find unused variables.

I always use Option Explicit and declare all variables and user defined
constants. The VBScript below tracks all variables declared in Dim
statements, all constants declared in Const statements, and all variables
defined in Sub and Function statements. It also attempts to keep track of
the scope of these. The program parses each line and keeps track of the
number of times each variable/constant is referenced in the program (other
than when it is declared/defined). The program outputs each
variable/constant with it's scope and the number of references found. The
form of the output is:

<scope>\<variable>: <number of references>

The "global" scope is identified as "%%global", in case there is a method
called "global". The program requires a filename as argument. The program
makes a lot of assumptions. For example, it assumes many delimiters.
However, it seems to have worked on several scripts of mine. If a variable
is declared, but never used, the number of references would be zero.

Please feel free to comment or point out flaws and shortcomings. I didn't
spend a lot of time on this and I'm sure it could be improved.
=============
' ParseVars.vbs
' Program to determine how many times each variable and user
' defined constant is referenced in a VBScript program.
' Keeps track of the scope of each.
Option Explicit

Dim objFSO, strFileName, objFile, strLine
Dim objList, k
Dim arrVariables, strVariable
Dim arrLines, strStatement
Dim strScope

' Check required argument.
If (Wscript.Arguments.Count <> 1) Then
Wscript.Echo "Missing argument, file name"
Wscript.Quit
End If

' Retrieve file name.
strFileName = Wscript.Arguments(0)

' Setup dictionary object of variables and constants.
Set objList = CreateObject("Scripting.Dictionary")
objList.CompareMode = vbTextCompare

' Open the VBScript file.
Set objFSO = CreateObject("Scripting.FileSystemObject")
On Error Resume Next
Set objFile = objFSO.OpenTextFile(strFileName, 1)
If (Err.Number <> 0) Then
On Error GoTo 0
Wscript.Echo "File not found: " & strFileName
Wscript.Quit
End If
On Error GoTo 0

' Start with global scope.
strScope = "%%global"

' Read each line of the VBScript file and process.
Do Until objFile.AtEndOfStream

strLine = LCase(Trim(objFile.ReadLine))
' Remove all quoted strings.
strLine = RemoveStrings(strLine)

' Break up into statements, if more than one per line.
arrLines = Split(strLine, ":")
' Process each statement.
For Each strStatement In arrLines

strStatement = Trim(strStatement)

If (Left(strStatement, 4) = "dim ") Then
' Retrieve declared variables.
Call GetVars(Trim(Mid(strStatement, 5)))
ElseIf (Left(strStatement, 6) = "const ") Then
' Retrieve user defined constants.
Call GetConst(Trim(Mid(strStatement, 7)))
ElseIf (Left(strStatement, 4) = "sub ") Then
' Retrieve variables in Sub statement.
Call GetSub(Trim(Mid(strStatement, 5)))
ElseIf (Left(strStatement, 9) = "function ") Then
' Retrieve variables in Function statement.
Call GetSub(Trim(Mid(strStatement, 10)))
ElseIf (strStatement = "end sub") Then
' Return to global scope.
strScope = "%%global"
ElseIf (strStatement = "end function") Then
' Return to global scope.
strScope = "%%global"
Else
' Parse all other lines for variables and constants.
Call ParseLine(strStatement)
End If
Next
Loop
objFile.Close

' List variables and constants, with their scope,
' and the number of times each is referenced.
arrVariables = objList.Keys
For Each strVariable In arrVariables
Wscript.Echo strVariable & ": " & objList(strVariable)
Next

Sub GetVars(strValue)
' Retrieve variables from a Dim statement.
' Keep track of the scope of each.
Dim arrVars, j, strVariable

arrVars = Split(strValue, ",")
For j = 0 To UBound(arrVars)
strVariable = strScope & "\" & Trim(arrVars(j))
If Not objList.Exists(strVariable) Then
objList(strVariable) = 0
End If
Next
End Sub

Sub GetConst(strValue)
' Retrieve user defined constants from a Const statement.
' Keep track of the scope of each.
Dim intIndex, strResult

intIndex = InStr(strValue, "=")
If (intIndex > 0) Then
strResult = strScope & "\" & Trim(Mid(strValue, 1, intIndex - 1))
If Not objList.Exists(strResult) Then
objList(strResult) = 0
End If
End If

End Sub

Sub ParseLine(strLine)
' Parse each statement and look for declared variables/constants.
Dim arrValues, strValue, intCount

' Skip comments.
If Left(strLine, 1) = "'" Then
Exit Sub
End If
If Left(strLine, 4) = "rem " Then
Exit Sub
End If

' Replace parentheses with spaces.
strLine = Replace(strLine, "(", " ")
strLine = Replace(strLine, ")", " ")

' Replace delimiters with spaces.
strLine = Replace(strLine, ",", " ")
strLine = Replace(strLine, ".", " ")
strLine = Replace(strLine, "=", " ")
strLine = Replace(strLine, "<", " ")
strLine = Replace(strLine, ">", " ")
strLine = Replace(strLine, "&", " ")

' Break up each statement into words.
arrValues = Split(strLine)
' Check each word.
For Each strValue In arrValues
' Check local scope first.
If (strScope <> "%%global") Then
' If the word is found in the list of variables/constants,
' increment the number of references.
If objList.Exists(strScope & "\" & strValue) Then
intCount = objList(strScope & "\" & strValue) + 1
objList(strScope & "\" & strValue) = intCount
End If
End If
' Check global scope.
' If the word is found in the list of variables/constants,
' increment the number of references.
If objList.Exists("%%global\" & strValue) Then
intCount = objList("%%global\" & strValue) + 1
objList("%%global\" & strValue) = intCount
End If
Next

End Sub

Function RemoveStrings(strValue)
' Remove all quoted strings.
Dim j, blnQuoted, strChar

blnQuoted = False
RemoveStrings = ""
For j = 1 To Len(strValue)
strChar = Mid(strValue, j, 1)
If (strChar = """") Then
If (blnQuoted = False) Then
blnQuoted = True
Else
blnQuoted = False
End If
Else
If (blnQuoted = False) Then
RemoveStrings = RemoveStrings & strChar
End If
End If
Next

End Function

Sub GetSub(strCall)
' Process subroutine and function statements.
' Keep track of the scope.
' Parse for variables.
Dim intIndex, strLine

intIndex = InStr(strCall, "(")
If (intIndex = 0) Then
strScope = strCall
Else
strScope = Trim(Mid(strCall, 1, intIndex - 1))
strLine = Mid(strCall, intIndex + 1)
strLine = Left(strLine, Len(strLine) - 1)
strLine = Replace(strLine, "byval", "")
strLine = Replace(strLine, "byref", "")
Call GetVars(strLine)
End If

End Sub

--
Richard
Microsoft MVP Scripting and ADSI
Hilltop Lab - http://www.rlmueller.net


Richard Mueller

unread,
Sep 11, 2006, 1:09:27 AM9/11/06
to
Corrections already.

I ran the script on itself and discovered that I declared the variable "k"
in the main body of the script but never used it. Shows how easily this can
happen.

I also noticed a flaw where I parse the statements and search for declared
variables/constants in the appropriate scope. I first check if each "word"
is declared in the local scope. If so, I increment the count of references.
Then I check if the "word" is declared in the global scope. This is a
mistake. A variable declared with local scope in a method overrides any
declaration of the same variable with global scope. I modified the program
so that if the "word" is found to have been declared in the local scope, the
global scope is not checked. If the "word" is not found to have been
declared in the local scope, then the program checks the global scope. The
corrected Sub ParseLine is included below. The rest of the program I posted
before is ok, except I should not have Dim'ed "k".

Note, this program will not detect variables that you fail to declare. The
WSH host does that for you if you use Option Explicit (and it would be too
difficult to include this functionality in the script). Note also that the
program only keeps track of references to variables after you declare them.
This is proper. If you use a variable before you Dim it, this should raise
an error when you run the script. The corrected Sub ParseLine follows (watch
for line wrapping):
============


Sub ParseLine(strLine)
' Parse each statement and look for declared variables/constants.

Dim arrValues, strValue, intCount, blnFound

' Skip comments.
If Left(strLine, 1) = "'" Then
Exit Sub
End If
If Left(strLine, 4) = "rem " Then
Exit Sub
End If

' Replace parentheses with spaces.
strLine = Replace(strLine, "(", " ")
strLine = Replace(strLine, ")", " ")

' Replace delimiters with spaces.
strLine = Replace(strLine, ",", " ")
strLine = Replace(strLine, ".", " ")
strLine = Replace(strLine, "=", " ")
strLine = Replace(strLine, "<", " ")
strLine = Replace(strLine, ">", " ")
strLine = Replace(strLine, "&", " ")

' Break up each statement into words.
arrValues = Split(strLine)
' Check each word.
For Each strValue In arrValues
' Check local scope first.

blnFound = False


If (strScope <> "%%global") Then
' If the word is found in the list of variables/constants,
' increment the number of references.
If objList.Exists(strScope & "\" & strValue) Then
intCount = objList(strScope & "\" & strValue) + 1
objList(strScope & "\" & strValue) = intCount

' If the word is found in the local scope,
' do not check the global scope.
' Variables declared in local scope override global.
blnFound = True
End If
End If
If (blnFound = False) Then


' Check global scope.
' If the word is found in the list of variables/constants,
' increment the number of references.
If objList.Exists("%%global\" & strValue) Then
intCount = objList("%%global\" & strValue) + 1
objList("%%global\" & strValue) = intCount
End If

End If
Next

End Sub

--

Bob Barrows [MVP]

unread,
Sep 11, 2006, 7:02:34 AM9/11/06
to
Richard Mueller wrote:
> script). Note also that the program only keeps track of references to
> variables after you declare them. This is proper. If you use a
> variable before you Dim it, this should raise an error when you run
> the script.

Are you sure? I thought variable declarations were "hoisted" at compile
time. Let me give it a try ...
Yes, this code runs fine:

option explicit
str="TheQuickBrownFoxJumpedOve.rTheTwoLazyDogs"
msgbox str
dim str

Of course, if the variable declaration is out of scope, that's a different
story.

--
Microsoft MVP - ASP/ASP.NET
Please reply to the newsgroup. This email account is my spam trap so I
don't check it very often. If you must reply off-line, then remove the
"NO SPAM"


Richard Mueller

unread,
Sep 11, 2006, 8:43:37 AM9/11/06
to

"Bob Barrows [MVP]" <reb0...@NOyahoo.SPAMcom> wrote in message
news:Op09GHZ1...@TK2MSFTNGP03.phx.gbl...

> Richard Mueller wrote:
>> script). Note also that the program only keeps track of references to
>> variables after you declare them. This is proper. If you use a
>> variable before you Dim it, this should raise an error when you run
>> the script.
>
> Are you sure? I thought variable declarations were "hoisted" at compile
> time. Let me give it a try ...
> Yes, this code runs fine:
>
> option explicit
> str="TheQuickBrownFoxJumpedOve.rTheTwoLazyDogs"
> msgbox str
> dim str
>
> Of course, if the variable declaration is out of scope, that's a different
> story.

Yikes, that means I would have to read the file twice, or assume all
declarations are at the top of each procedures.

Richard


McKirahan

unread,
Sep 11, 2006, 9:55:56 AM9/11/06
to
"Joseph Morales" <joseph....@unisys.com> wrote in message
news:edsgm0$gfo$1...@si05.rsvl.unisys.com...

> Is there an easy/free way to scan a large VBScript for unused variables?
>
> Visual Studio 2005 is supposed to have an autocorrect feature, but I can't
> find how to turn it on. Either that, or its always on and it just doesn't
> display anything for scripts.

Does the script use "Option Explicit"?

If so then a script could be written to add all "Dim" variables to a
dictionary and then scan all non-"Dim" lines for their usage and
updating the dictionary and then report the non-referenced items.

Just an idea...

I wrote the above 2 days ago and was working on creating it
but before I finished I saw that Richard had posted a solution.

I found that his solution does not allow for the following:
Dim i j k

as the following statement requires comma separated variables:


arrVars = Split(strValue, ",")

I got around by converting all spaces to commas then ignoring all
non-blank "variables" in the array made from the Split() statement.

Here's an update to the subroutine:

Sub GetVars(strValue)
' Retrieve variables from a Dim statement.
' Keep track of the scope of each.
Dim arrVars, j, strVariable

strValue = Replace(strValue," ",",")


arrVars = Split(strValue, ",")
For j = 0 To UBound(arrVars)

If Trim(arrVars(j)) <> "" Then


strVariable = strScope & "\" & Trim(arrVars(j))
If Not objList.Exists(strVariable) Then
objList(strVariable) = 0

Justin Piper

unread,
Sep 11, 2006, 10:20:42 AM9/11/06
to
On Mon, 11 Sep 2006 08:55:56 -0500, McKirahan <Ne...@McKirahan.com> wrote:

> I found that his solution does not allow for the following:
> Dim i j k

I've never seen this before. In what context is this syntax valid?

--
Justin Piper
Bizco Technologies
http://www.bizco.com/

McKirahan

unread,
Sep 11, 2006, 10:36:33 AM9/11/06
to
"Justin Piper" <jpi...@bizco.com> wrote in message
news:op.tfpwu...@luxembourg.psg.bizcotech.com...

> On Mon, 11 Sep 2006 08:55:56 -0500, McKirahan <Ne...@McKirahan.com> wrote:
>
> > I found that his solution does not allow for the following:
> > Dim i j k
>
> I've never seen this before. In what context is this syntax valid?

Apparently it's not -- sorry.

"You declare multiple variables by separating
each variable name with a comma."

per the Visual Basic Script Language Reference


Joseph Morales

unread,
Sep 11, 2006, 7:52:11 PM9/11/06
to
>>"Richard Mueller" <rlmuelle...@ameritech.NOSPAM.net> wrote:
>>I've attempted to develop a VBScript program to find unused variables.

Thanks! I ran ParseVars.vbs against an 8,000 line script and it found dozens
of unused variables. Pretty cool.

Small issues:
The ParseVars.vbs script seems to be confused by tab characters. So, soon
after the line

Do Until objFile.AtEndOfStream

I've added

strLine = Replace(strLine, vbTab, " ")

For array declarations, the ParseVars.vbs seems to include the (upperbound)
clause as part of the variable name. I added some code to strip that out.

ParseVars.vbs currently doesn't treat Redim statements as declarations, but
sometimes they operate that way. I haven't fixed that yet.

I made some other changes to display the variable names in their original
casing, since they're easier for me to read that way. This has the side
effect that a variable could be marked as unused if all the uses are cased
differently than the declaration.

I prefer to declare variables before using them, so I don't care that
VBScript actually permits them to be declared afterward.

Joseph Morales


Richard Mueller

unread,
Sep 12, 2006, 1:36:02 AM9/12/06
to

"Joseph Morales" <joseph....@unisys.com> wrote in message
news:ee4src$2g1j$1...@si05.rsvl.unisys.com...

I knew if this worked at all, it would be an iterative process. You make
good points. I modified the program to handle tabs, array bounds, and ReDim
statements. I left all variable names lower case to make comparisons case
insensitive.

Also, when I parse the lines for "words" to compare with variable names, I
wanted to include object references. So I included periods as delimiters.
This allows the code to parse "objFile.Open" and recognize "objFile" as a
variable. However, it also meant that object methods and properties, like
"Open" in my example, are interpreted as candidate variables. Someone could
have a variable "Open" and the program would improperly think this variable
was referenced.

I fixed this by replacing periods with a space followed by a period. Then
when I consider the "words", I can ignore any words that begin with a
period. The string "objFile.Open" becomes "objfile .open", so "objfile" is
compared to the list of variables, but ".open" is ignored.

I also modifed the program to read the VBScript file twice. The first time
through the file it retrieves variable names and puts them in the dictionary
object. The second time through it compares the "words" in each statement to
the dictionary object of variable names. This allows the Dim and ReDim
statements to be anywhere.

The program is getting longer, but here is my current version. Watch line
wrapping.
==============
' ParseVars.vbs
' Version 1.0 - September 11, 2006


' Program to determine how many times each variable and user
' defined constant is referenced in a VBScript program.
' Keeps track of the scope of each.
Option Explicit

Dim objFSO, strFileName, objFile, strLine
Dim objList

Dim arrVariables, strVariable
Dim arrLines, strStatement

Dim strScope, intIndex

' Check required argument.
If (Wscript.Arguments.Count <> 1) Then
Wscript.Echo "Missing argument, file name"

Call SyntaxHelp
Wscript.Quit
Wscript.Quit
End If

' Retrieve file name.
strFileName = Wscript.Arguments(0)

' Check for syntax request.
Select Case LCase(strfileName)
Case "/h"
Call SyntaxHelp
Wscript.Quit
Case "/?"
Call SyntaxHelp
Wscript.Quit
Case "?"
Call SyntaxHelp
Wscript.Quit
Case "-?"
Call SyntaxHelp
Wscript.Quit
Case "-h"
Call SyntaxHelp
Wscript.Quit
Case "/help"
Call SyntaxHelp
Wscript.Quit
Case "-help"
Call SyntaxHelp
Wscript.Quit
End Select

' Open the VBScript file.
Set objFSO = CreateObject("Scripting.FileSystemObject")
On Error Resume Next
Set objFile = objFSO.OpenTextFile(strFileName, 1)
If (Err.Number <> 0) Then
On Error GoTo 0
Wscript.Echo "File not found: " & strFileName

Set objFSO = Nothing


Wscript.Quit
End If
On Error GoTo 0

' Setup dictionary object of variables and constants.


Set objList = CreateObject("Scripting.Dictionary")
objList.CompareMode = vbTextCompare

' Start with global scope.
strScope = "%%global"

' Read each line of the VBScript file and retrieve
' all variables and constants declared, with their scope.


Do Until objFile.AtEndOfStream
strLine = LCase(Trim(objFile.ReadLine))

strLine = Replace(strLine, vbTab, " ")

' Remove all quoted strings.
strLine = RemoveStrings(strLine)

' Break up into statements, if more than one per line.
arrLines = Split(strLine, ":")
' Process each statement.
For Each strStatement In arrLines
strStatement = Trim(strStatement)

If (Left(strStatement, 4) = "dim ") Then
' Retrieve declared variables.
Call GetVars(Trim(Mid(strStatement, 5)))
ElseIf (Left(strStatement, 6) = "const ") Then
' Retrieve user defined constants.
Call GetConst(Trim(Mid(strStatement, 7)))

ElseIf (Left(strStatement, 6) = "redim ") Then
' Retrieve declared variables.
Call GetVars(Trim(Mid(strStatement, 7)))


ElseIf (Left(strStatement, 4) = "sub ") Then
' Retrieve variables in Sub statement.
Call GetSub(Trim(Mid(strStatement, 5)))
ElseIf (Left(strStatement, 9) = "function ") Then
' Retrieve variables in Function statement.
Call GetSub(Trim(Mid(strStatement, 10)))
ElseIf (strStatement = "end sub") Then
' Return to global scope.
strScope = "%%global"
ElseIf (strStatement = "end function") Then
' Return to global scope.
strScope = "%%global"
Else

' Skip all other lines.


End If
Next
Loop
objFile.Close

Set objFile = Nothing

' Read the file again.


Set objFile = objFSO.OpenTextFile(strFileName, 1)

' Start with global scope.
strScope = "%%global"

' Read each line of the VBScript file and count number of
' times each variable and constant is referenced.


Do Until objFile.AtEndOfStream
strLine = LCase(Trim(objFile.ReadLine))

strLine = Replace(strLine, vbTab, " ")

' Remove all quoted strings.
strLine = RemoveStrings(strLine)

' Break up into statements, if more than one per line.
arrLines = Split(strLine, ":")
' Process each statement.
For Each strStatement In arrLines
strStatement = Trim(strStatement)

If (Left(strStatement, 4) = "dim ") Then

' Skip.


ElseIf (Left(strStatement, 6) = "const ") Then

' Skip.
ElseIf (Left(strStatement, 6) = "redim ") Then
' Skip.


ElseIf (Left(strStatement, 4) = "sub ") Then

' Determine scope.
strStatement = Trim(Mid(strStatement, 5))
intIndex = InStr(strStatement, "(")


If (intIndex = 0) Then

strScope = strStatement
Else
strScope = Trim(Mid(strStatement, 1, intIndex - 1))
End If


ElseIf (Left(strStatement, 9) = "function ") Then

' Determine scope.
strStatement = Trim(Mid(strStatement, 10))
intIndex = InStr(strStatement, "(")


If (intIndex = 0) Then

strScope = strStatement
Else
strScope = Trim(Mid(strStatement, 1, intIndex - 1))
End If


ElseIf (strStatement = "end sub") Then
' Return to global scope.
strScope = "%%global"
ElseIf (strStatement = "end function") Then
' Return to global scope.
strScope = "%%global"
Else
' Parse all other lines for variables and constants.
Call ParseLine(strStatement)
End If
Next
Loop
objFile.Close

' List variables and constants, with their scope,
' and the number of times each is referenced.

' Output in the form:
' <scope>\<variable>: <number of references>


arrVariables = objList.Keys
For Each strVariable In arrVariables
Wscript.Echo strVariable & ": " & objList(strVariable)
Next

Sub GetVars(strValue)
' Retrieve variables from a comma delimited list,
' such as from a Dim statement or Sub or Function statement.


' Keep track of the scope of each.

Dim arrVars, j, strVariable, intIndex

arrVars = Split(strValue, ",")
For j = 0 To UBound(arrVars)

strVariable = Trim(arrVars(j))
' Strip off any bounds.
intIndex = InStr(strVariable, "(")
If (intIndex > 0) Then
strVariable = Left(strVariable, intIndex - 1)
End If
strVariable = strScope & "\" & strVariable


If Not objList.Exists(strVariable) Then
objList(strVariable) = 0

End If
Next
End Sub

Sub GetConst(strValue)


' Retrieve user defined constants from a Const statement.
' Keep track of the scope of each.
Dim intIndex, strResult

intIndex = InStr(strValue, "=")
If (intIndex > 0) Then

strResult = strScope & "\" _


& Trim(Mid(strValue, 1, intIndex - 1))
If Not objList.Exists(strResult) Then
objList(strResult) = 0
End If
End If

End Sub

Sub ParseLine(strLine)


' Parse each statement and look for
' declared variables/constants.
Dim arrValues, strValue, intCount, blnFound

' Skip comments.
If Left(strLine, 1) = "'" Then
Exit Sub
End If
If Left(strLine, 4) = "rem " Then
Exit Sub
End If

' Replace delimiters with spaces.


strLine = Replace(strLine, "(", " ")
strLine = Replace(strLine, ")", " ")
strLine = Replace(strLine, ",", " ")

strLine = Replace(strLine, "=", " ")
strLine = Replace(strLine, "<", " ")
strLine = Replace(strLine, ">", " ")
strLine = Replace(strLine, "&", " ")

' Replace periods with a space followed by period (.),
' so we can recognize the word that comes before
' the period as an object reference, but ignore
' the word that comes after a period, since it
' would be a property or method of the object.
strLine = Replace(strLine, ".", " .")

' Break up each statement into words.
arrValues = Split(strLine)
' Check each word.
For Each strValue In arrValues

' Ignore words that start with a period, as they are
' properties or methods of objects.
If (Left(strValue, 1) <> ".") Then

End Sub

Function RemoveStrings(strValue)


' Remove all quoted strings.
Dim j, blnQuoted, strChar

blnQuoted = False
RemoveStrings = ""
For j = 1 To Len(strValue)
strChar = Mid(strValue, j, 1)
If (strChar = """") Then
If (blnQuoted = False) Then
blnQuoted = True
Else
blnQuoted = False
End If
Else
If (blnQuoted = False) Then
RemoveStrings = RemoveStrings & strChar

End If
End If
Next

End Function

Sub GetSub(strCall)
' Process subroutine and function statements.
' Keep track of the scope.
' Parse for variables.
Dim intIndex, strLine

intIndex = InStr(strCall, "(")
If (intIndex = 0) Then
strScope = strCall
Else
strScope = Trim(Mid(strCall, 1, intIndex - 1))
strLine = Mid(strCall, intIndex + 1)
strLine = Left(strLine, Len(strLine) - 1)
strLine = Replace(strLine, "byval", "")
strLine = Replace(strLine, "byref", "")
Call GetVars(strLine)
End If

End Sub

Sub SyntaxHelp
' Subroutine to output syntax help.
Wscript.Echo "ParseVars.vbs"
Wscript.Echo "Program to determine how many times each variable"
Wscript.Echo "and user defined constant is referenced in a"
Wscript.Echo "VBScript program. Used to find variables declared"
Wscript.Echo "but never used. Use of Option Explicit is assumed."
Wscript.Echo "Syntax:"
Wscript.Echo " ParseVars.vbs <VBScript file name>"
Wscript.Echo "For example:"
Wscript.Echo _
" cscript //nologo ParseVars.vbs ""c:\scripts\MyTest.vbs"""
Wscript.Echo "Output in form:"
Wscript.Echo _
" <scope>\<variable or constant>: <number of references>"

McKirahan

unread,
Sep 12, 2006, 2:00:42 AM9/12/06
to
"Richard Mueller" <rlmuelle...@ameritech.NOSPAM.net> wrote in message
news:O4MfX1i1...@TK2MSFTNGP04.phx.gbl...

[snip]

> I knew if this worked at all, it would be an iterative process.

[snip]

Here's a construct that you may not be testing for:
a Dim on a continuation line:

Option Explicit
Dim a, _
b
a = "A"
b = "B"
WScript.Echo a & b

Don't think its use is very likely but it is possible...


Bill James

unread,
Sep 12, 2006, 9:12:09 AM9/12/06
to
"Richard Mueller" <rlmuelle...@ameritech.NOSPAM.net> wrote in message news:O4MfX1i1...@TK2MSFTNGP04.phx.gbl...
>

This shows a lot of promise. I know myself when I have a large script that I have edited/revised over time, I'm usually feeling like I left some unused variables but don't want to take the time to search them out, so this script can be handy.

I have one minor complaint. Where you have error checking for opening the file to be parsed, you are forcing the error message to be "File not found" without checking to see if that is actually the error. The error could be permissions, for example.

--

Bill James
Microsoft MVP - Shell/User

Windows VBScript Utilities » www.billsway.com/vbspage/
Windows Tweaks & Tips » www.billsway.com/notes_public/

Richard Mueller

unread,
Sep 12, 2006, 1:37:22 PM9/12/06
to

"McKirahan" <Ne...@McKirahan.com> wrote in message
news:FNydnev_p-Np0ZvY...@comcast.com...

I actually considered handling line continuations, but decided I could get
by without it. You have pointed out that this is not true for Dim
statements. Thinking about it, Sub and Function statements are also
problems.

I think I could modify the script to detect a trailing "_", and if found,
read the next line and append it. This would have to be a recursive thing,
to handle any number of line continuations. Perhaps:

Do Until objFile.AtEndOfStream
strLine = LCase(Trim(objFile.ReadLine))

strLine = GetLine(strLine)
'....
Loop

Function GetLine(strLine)
' Recursive function to handle line continuations.
Dim strNextLine
' Check for line continuation character.
If (Right(strLine, 1) = "_") Then
' Remove line continuation character and append space.
strLine = Left(strLine, Len(strLine) - 1) & " "
' Read the next line. objFile has global scope.
strNextLine = LCase(Trim(objFile.ReadLIne))
' Append the next line.
' Call this function recursively.
strLine = strLine & GetLine(strNextLine)
End If
GetLine = strLine
End Function

Richard Mueller

unread,
Sep 12, 2006, 2:06:21 PM9/12/06
to
This shows a lot of promise. I know myself when I have a large script that
I have edited/revised over time, I'm usually feeling like I left some unused
variables but don't want to take the time to search them out, so this script
can be handy.

I have one minor complaint. Where you have error checking for opening the
file to be parsed, you are forcing the error message to be "File not found"
without checking to see if that is actually the error. The error could be
permissions, for example.

--

Bill James
Microsoft MVP - Shell/User

Windows VBScript Utilities » www.billsway.com/vbspage/
Windows Tweaks & Tips » www.billsway.com/notes_public/

--------------------
I've spent lots of time myself manually searching for all the variables in
my scripts. I've also posted scripts in the newsgroups with "dead"
variables. In the back of my mind I'm thinking this can be modified to
handle VB6 source code. Would have to ignore data typing in Dim, ReDim, Sub,
and Function statements - look for string " as " and ignore.

I could check the error number to see which condition raised the error. Just
takes a bit of work to determine the numbers. If the file does not exist,
Err.Number = 53. If the user lacks permissions. Err.Number = 70. If the path
doesn't exist, Err.Number = 52. If the server (in a UNC path) does not
exist, Err.Number = 76. I'm sure there are other possibilities.

Justin Piper

unread,
Sep 12, 2006, 3:01:46 PM9/12/06
to
On Tue, 12 Sep 2006 00:36:02 -0500, Richard Mueller
<rlmuelle...@ameritech.NOSPAM.net> wrote:

> I modified the program to handle tabs, array bounds, and ReDim
> statements. I left all variable names lower case to make comparisons
> case insensitive.

I suppose you would prefer I not mention that identifiers can be quoted
with [ and ]. :)

Option Explicit
Dim [I am a variable], [I, for one, like commas], [[[[[[[[[[], []
[I am a variable] = 1
[I, for one, like commas] = 2
[[[[[[[[[[] = 3
[] = 4

Richard Mueller

unread,
Sep 12, 2006, 4:43:36 PM9/12/06
to

"Justin Piper" <jpi...@bizco.com> wrote in message
news:op.tfr4f...@luxembourg.psg.bizcotech.com...

It would be best to handle that, but it would take a bit of work. Cannot
Split the list of variables using the comma delimiter. The problem is
similar to reading a comma delimited file with VBScript, where commas
embedded in quoted strings require special treatment. I solved that problem
some time ago. When I get I chance, I'll look up my solution to that and see
if I can use it here, taking everything within the brackets literally.

I also need to check out [] and [[[[[[[[[[]. Never seen that before.

Justin Piper

unread,
Sep 12, 2006, 5:03:11 PM9/12/06
to
On Tue, 12 Sep 2006 15:43:36 -0500, Richard Mueller
<rlmuelle...@ameritech.NOSPAM.net> wrote:

>
> "Justin Piper" <jpi...@bizco.com> wrote in message
> news:op.tfr4f...@luxembourg.psg.bizcotech.com...
>> On Tue, 12 Sep 2006 00:36:02 -0500, Richard Mueller
>> <rlmuelle...@ameritech.NOSPAM.net> wrote:
>>
>>> I modified the program to handle tabs, array bounds, and ReDim
>>> statements. I left all variable names lower case to make comparisons
>>> case insensitive.
>>
>> I suppose you would prefer I not mention that identifiers can be quoted
>> with [ and ]. :)
>>
>> Option Explicit
>> Dim [I am a variable], [I, for one, like commas], [[[[[[[[[[], []
>> [I am a variable] = 1
>> [I, for one, like commas] = 2
>> [[[[[[[[[[] = 3
>> [] = 4
>

> It would be best to handle that, but it would take a bit of work. Cannot
> Split the list of variables using the comma delimiter. The problem is
> similar to reading a comma delimited file with VBScript, where commas
> embedded in quoted strings require special treatment.

I suspect you will need to revise ParseLine's treatment of
non-identifier tokens as well. Right now you're unilaterally replacing
(),=<>& with space, which would be a problem if they appeared inside a
quoted identifier.

> I also need to check out [] and [[[[[[[[[[]. Never seen that before.

They're legal. Dumb, but legal. These aren't, though, which might
complicate handling line continuations.

Dim [
]
Dim [ _
]

By the time you get done with all this, you might find you have
implemented VBScript in VBScript. :)

Joseph Morales

unread,
Sep 12, 2006, 8:33:47 PM9/12/06
to
In my script, I sometimes initialize a variable immediately after declaring
it:

Dim strMyVal : strMyVal = "Some value"

This is not a Const because there is a possibility that we'll change the
value later.

Anyway, this is an "uninteresting" use of a variable. If the initializing
statement is the only place that the variable gets assigned, then it's
effectively unused. It would be nice to be able to detect these.

I suspect that ParseVars.vbs can't/shouldn't be coded to handle all possible
situations, unless you're planning to actually market it. In practice, it's
useful if it detects reasonably common constructs that appear in your own
code.

Thanks,
Joseph Morales


Bill James

unread,
Sep 12, 2006, 9:28:21 PM9/12/06
to
> I've spent lots of time myself manually searching for all the variables in
> my scripts. I've also posted scripts in the newsgroups with "dead"
> variables. In the back of my mind I'm thinking this can be modified to
> handle VB6 source code. Would have to ignore data typing in Dim, ReDim, Sub,
> and Function statements - look for string " as " and ignore.
>
> I could check the error number to see which condition raised the error. Just
> takes a bit of work to determine the numbers. If the file does not exist,
> Err.Number = 53. If the user lacks permissions. Err.Number = 70. If the path
> doesn't exist, Err.Number = 52. If the server (in a UNC path) does not
> exist, Err.Number = 76. I'm sure there are other possibilities.
>
> --
> Richard
> Microsoft MVP Scripting and ADSI
> Hilltop Lab - http://www.rlmueller.net
>
>

I've notice another problem, if the Dim statement includes a comment the variable is not read in correctly, but thinks the space and comment are part of the variable name.

Dim sSql 'As String
readaddr\ssql 'as string: 0

By the way, the script work for .asp pages too, sort of. It falls down where there is client-side javascript code included in the file, but that is no huge problem IMO.

I have added a shortcut to the script to my SendTo folder, with the shortcut edited to call it with cmd cscript.

Dr John Stockton

unread,
Sep 13, 2006, 9:04:50 AM9/13/06
to
JRS: In article <u6gY9oV1...@TK2MSFTNGP02.phx.gbl>, dated Sun, 10
Sep 2006 23:24:55 remote, seen in news:microsoft.public.scripting.vbscri
pt, Richard Mueller <rlmuelle...@ameritech.NOSPAM.net> posted :

> If (blnQuoted = False) Then
> blnQuoted = True
> Else
> blnQuoted = False
> End If

IMHO, anyone writing that rather than

binQuoted = not binQuoted

Is either lamentably ignorant or paid by the line.

--
© John Stockton, Surrey, UK. ???@merlyn.demon.co.uk Turnpike v4.00 MIME. ©
Web <URL:http://www.merlyn.demon.co.uk/> - FAQish topics, acronyms, & links.
In MS OE, choose Tools, Options, Send; select Plain Text for News and E-mail.
Don't quote more than is needed, and respond after each quoted part.

Justin Piper

unread,
Sep 13, 2006, 1:43:36 PM9/13/06
to
On Wed, 13 Sep 2006 08:04:50 -0500, Dr John Stockton
<j...@merlyn.demon.co.uk> wrote:

> JRS: In article <u6gY9oV1...@TK2MSFTNGP02.phx.gbl>, dated Sun, 10
> Sep 2006 23:24:55 remote, seen in news:microsoft.public.scripting.vbscri
> pt, Richard Mueller <rlmuelle...@ameritech.NOSPAM.net> posted :
>
>> If (blnQuoted = False) Then
>> blnQuoted = True
>> Else
>> blnQuoted = False
>> End If
>
> IMHO, anyone writing that rather than
>
> binQuoted = not binQuoted
>
> Is either lamentably ignorant or paid by the line.
>

Perhaps he's just paranoid.

Option Explicit
Dim binQuoted: binQuoted = 10
WScript.Echo binQuoted, CBool(binQuoted)

binQuoted = Not binQuoted
WScript.Echo binQuoted, CBool(binQuoted)

If (binQuoted = False) Then
binQuoted = True
Else
binQuoted = False
End If
WScript.Echo binQuoted, CBool(binQuoted)

Dr John Stockton

unread,
Sep 14, 2006, 9:58:32 AM9/14/06
to
JRS: In article <op.tftvk...@luxembourg.psg.bizcotech.com>, dated
Wed, 13 Sep 2006 17:43:36 remote, seen in news:microsoft.public.scriptin
g.vbscript, Justin Piper <jpi...@bizco.com> posted :


If you look, you will see that it is local to the routine, named
(bln...) as Boolean, and only set to Boolean values.

If it has acquired a non-Boolean value, the situation is too bad for a
simple palliative that makes it into a Boolean.

--
© John Stockton, Surrey, UK. ?@merlyn.demon.co.uk DOS 3.3, 6.20; Win98. ©
Web <URL:http://www.merlyn.demon.co.uk/> - FAQqish topics, acronyms & links.
PAS EXE TXT ZIP via <URL:http://www.merlyn.demon.co.uk/programs/00index.htm>
My DOS <URL:http://www.merlyn.demon.co.uk/batfiles.htm> - also batprogs.htm.

0 new messages