Thanks to everyone for all the responses!

I put the macro at the end of this posting.

Cannon Smith inquired:

RE> I don?t have a macro for you, but I?m curious to know why asking the 
compiler to check the syntax isn?t enough?

1. It may not be a syntactical problem; I may have simply forgotten to 
declare the variable.  While I use the 'All Variables are Typed' option, I 
find that 4D doesn't bark very loud if it can discern the typing.

2. The main reason is that I'm changing my coding style (a hard thing to 
do) from putting all my declarations on the first few lines in the method 
to putting them throughout the method, wherever the variable is used, so 
there's more opportunity for missed and double declarations.

3. I used to clear arrays by doing yet another array declaration, such as 
ARRAY TEXT($aText;0), which really isn't a best practice IMHO because way 
back when we had ARRAY STRING it was possible to change a string length in 
one place and not change it in another place - if you want to declare an 
array, then declare it, if you want to resize it, then resize it. Separate 
tools for separate operations.  Hence, I look for "double declarations" 
where I may have declared a variable twice. 

4. I've found that while I'm meticulous about declaring my variables, when 
I flub, it is often because of a mis-spelling. 
C_TEXT($MyVeryLongVariableName) works fine, but later in the thick and 
heat and smoke of a thundering coding session I might use 
$MyVaryLongVariableName:=2 - syntactically this works, the compiler 
probably won't bark, but I have a bug and an easy way to find it is to see 
if I've declared it.

Thanks *VERY* much to Lee Hinde for pointing out that it is a lot easier 
to examine what valid characters IN a local variable vs trying to examine 
characters that might fall AFTER a local variable.

++++++
Here's the macro, set up to run when the method is saved:

<macro name="Check Local Vars" method_event="on_save" type_ahead="false" 
version="2">
<text><method>CheckLocalDeclarations("<method_path/>")</method></text>
</macro>

+++++++
Here's the 4D code, CheckLocalDeclarations, that does the check.  Provide 
your own dialog to display vMsgText, vMsgText1, and vMsgText2 - or if you 
are using v16, don't do this, just pass the values as parameters to the 
form.


//Bob Miller04/27/18CheckLocalDeclarations - called by macro to check that 
all local variables are declared.
//Method name is passed by the macro as $1
//This method calls itself in a new process if only one parm is sent (eg. 
when it is called by the macro).

C_TEXT($1;$2;$MethodName;$AllCodeButDeclarations;$LineDeclarationBlock;$ArrayDeclarationBlock;$NewProcessFlag)
C_TEXT($MethodCode;$CR)
C_TEXT($RegEx;$CodeLine)
C_LONGINT($N;$NumLines;$ProcNo;$L;$NumDeclarations;$WhereCommentBegins)
C_POINTER($DummyPtr)
$CR:=Char(Carriage return)//I know I should use a constant, yes...still 
haven't got around to this.

Case of 
: (Count parameters=1)//if called with only a single parm, then this 
method re-opens itself in its own process
//Note below on New Process: I chose to leave the * off so I can run it 
again if I want, a new window is opened
$ProcNo:=New process(Current method name;256*1024;Current method 
name;$1;"NewProcess")
BRING TO FRONT($ProcNo)


: (Count parameters=2)//cool technique I learned from Tim PENNER at 4D
$MethodName:=$1
$NewProcessFlag:=$2//existence of $2 simply means we are in a new process, 
that's all it does

METHOD GET CODE($MethodName;$MethodCode)

ARRAY TEXT($aMethodCodeArr;0)
TextToArrayList (->$MethodCode;->$aMethodCodeArr;"";"";"NoSort")//this 
takes text and puts each line as an array element

$NumLines:=Size of array($aMethodCodeArr)

For ($N;1;$NumLines)//go through all the lines of the method, separate 
into 4 pieces: variable declarations, array declarations, code, and 
comments.
//We don't keep any comments.I don't know why I want to keep variable 
declarations separate from array declarations; it makes me feel better.

$CodeLine:=$aMethodCodeArr{$N}//this is one line of code.We examine it.

$WhereCommentBegins:=Position("//";$CodeLine)//find and remove all 
comments; they may contain local variable references, such as unused code
If ($WhereCommentBegins>=1)
$CodeLine:=Substring($CodeLine;1;$WhereCommentBegins-1)
End if 

$CodeLine:=Replace string($CodeLine;Char(Tab);"")//remove leading TAB's, 
we want what we're looking for to be at position 1
$CodeLine:=DeLBlank ($CodeLine)//remove leading spaces from the code line, 
so we're assured what we are looking for will be at position 1

Case of 
: (Position("C_";$CodeLine)=1)//this is a C_BOOLEAN or C_TEXT or 
C_Something declaration command and now will be at position 1 (no tabs, no 
spaces)
$LineDeclarationBlock:=$LineDeclarationBlock+$CodeLine+$CR//this is all 
the code containing declarations


: (Position("ARRAY ";$CodeLine)=1)//this is an ARRAY BOOLEAN or ARRAY TEXT 
or ARRAY Something command
$ArrayDeclarationBlock:=$ArrayDeclarationBlock+$CodeLine+$CR//this is all 
the code containing array declarations

Else 
// this is not a declaration line
If ($CodeLine#"")//make sure it is not a blank line; build up the code 
WITHOUT the declarations; we'll
//later examine this to get all the variables that are in use
$AllCodeButDeclarations:=$AllCodeButDeclarations+$CodeLine+$CR
End if //$CodeLine#""

End case 
End for //$N;1;$NumLines


$RegEx:="\\$[a-zA-Z0-9]*"//many thanks to Lee Hinde for this, horray!

// ++++++++++++Pull all declared local variables out
ARRAY TEXT($aDeclaredLocals;0)// array of all locals pulled out of our 
code containing declaractions; these are "Declared Locals"
ARRAY TEXT($aUniqueDeclaredLocals;0)// Same as above, reduced to a single 
instance for each local var
ParseRegexToArray ($LineDeclarationBlock;$RegEx;->$aDeclaredLocals)//Takes 
the text $1, uses the RegEx $2, puts all matches in array $3
ARR_Distinct (->$aDeclaredLocals;->$aUniqueDeclaredLocals)//pulls out any 
duplicates, so only 1 instance is in $2

ARRAY TEXT($aLocalDeclaredMultiple;0)
ARRAY LONGINT($aLocalDeclaredMultipleCount;0)
ARRAY LONGINT($aIndex;0)

If (Size of array($aDeclaredLocals)#Size of 
array($aUniqueDeclaredLocals))//check if any var is declared twice
ARR_SizeArrays (0;->$aIndex)
For ($L;1;Size of array($aUniqueDeclaredLocals))
$NumDeclarations:=ARR_FindInArray_All 
(->$aUniqueDeclaredLocals{$L};->$aDeclaredLocals;->$aIndex)//Takes the 
text $1,
//looks for it in array $2, returns the number of times it finds it in 
$0.If we found it more than once, it was declared more than once.
If ($NumDeclarations>1)
APPEND TO ARRAY($aLocalDeclaredMultiple;$aUniqueDeclaredLocals{$L})
APPEND TO ARRAY($aLocalDeclaredMultipleCount;$NumDeclarations)
End if 
End for 
End if //Size of array($aDeclaredLocals)#Size of 
array($aUniqueDeclaredLocals)


// ++++++++++++Pull all declared local arrays out
ARRAY TEXT($aDeclaredArrays;0)//Get array of all declared local arrays, 
just like above
ARRAY TEXT($aUniqueDeclaredArrays;0)
ParseRegexToArray ($ArrayDeclarationBlock;$RegEx;->$aDeclaredArrays)
ARR_Distinct (->$aDeclaredArrays;->$aUniqueDeclaredArrays)

If (Size of array($aDeclaredArrays)#Size of 
array($aUniqueDeclaredArrays))//check if any var is declared twice
ARR_SizeArrays (0;->$aIndex)
For ($L;1;Size of array($aUniqueDeclaredArrays))
$NumDeclarations:=ARR_FindInArray_All 
(->$aUniqueDeclaredArrays{$L};->$aDeclaredArrays;->$aIndex)
If ($NumDeclarations>1)
APPEND TO ARRAY($aLocalDeclaredMultiple;$aUniqueDeclaredArrays{$L})
APPEND TO ARRAY($aLocalDeclaredMultipleCount;$NumDeclarations)
End if 
End for 
End if //Size of array($aDeclaredArrays)#Size of 
array($aUniqueDeclaredArrays)


// ++++++++++++Pull all the local variables used in the code
ARRAY TEXT($aVarsUsed;0)//Get array of all the local vars used in the code 
(not declared, but actually used)
ARRAY TEXT($aUniqueVarsUsed;0)
ParseRegexToArray ($AllCodeButDeclarations;$RegEx;->$aVarsUsed)
ARR_Distinct (->$aVarsUsed;->$aUniqueVarsUsed)


// ++++++++++++Compare the set of declared locals with the set of "locals 
used in code"
//Note: ARR_Union, ARR_Subtract are part of a tech note by Charlie Vaas 
TN10-05 ArrayUtilities
ARRAY TEXT($aDeclaredAllVars;0)
ARR_Union 
(->$aUniqueDeclaredLocals;->$aUniqueDeclaredArrays;->$aDeclaredAllVars)//combine
 
the declared vars with the declared arrays

ARRAY TEXT($aUsedNotDeclared;0)
ARR_Subtract 
(->$aUniqueVarsUsed;->$aDeclaredAllVars;->$aUsedNotDeclared)//do some 
array DIFFERENCE to get vars that are not declared

ARRAY TEXT($aDeclaredNotUsed;0)
ARR_Subtract 
(->$aDeclaredAllVars;->$aUniqueVarsUsed;->$aDeclaredNotUsed)//do some 
array DIFFERENCE to get vars that are declared not used


// ++++++++++++We have our results in arrays, prepare text to display on a 
dialog
C_TEXT(vMsgText;vMsgText1)//for CheckLocalVarsDlog - replace when we can 
send parms directly to forms in v16 (declaration is here because we are in 
our own process)
vMsgText:=ArrayToText (->$aUsedNotDeclared;"; ")//this takes an array and 
converts the elements to a long text string with elements delimited by 
semicolon
vMsgText1:=ArrayToText (->$aDeclaredNotUsed;$CR)//ditto, delimited by 
carriage return

vMsgText2:=""
For ($L;1;Size of array($aLocalDeclaredMultiple))//build up text string 
showing any vars that were declared more than once, along with the number 
of times it was declared
vMsgText2:=vMsgText2+$aLocalDeclaredMultiple{$L}+" 
("+String($aLocalDeclaredMultipleCount{$L})+"x)"+$CR
End for 

If (vMsgText+vMsgText1+vMsgText2="")
//do nothing, no need to open window if everything is OK (but YOU could 
say, "Everything seems to be OK" in fine 4D form if you wanted to)
Else 
// I fully recognize it is now bad form to use process vars to communicate 
with a form, but hey, I'm still in v15...
MyOpenFormWindow ($DummyPtr;"CheckLocalVarsDlog";"Check Local 
Variables";0;*)//$DummyPtr tells the method this is a project form, not a 
table form.
`The form is CheckLocalVarsDlog, the next parm is the window title, then 
the window type, then star keeps the window open while this method 
completes.
End if 


End case 
//




Bob Miller
Chomerics, a division of Parker Hannifin Corporation


llllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllll
"PLEASE NOTE: The preceding information may be confidential or privileged. It 
only should be used or disseminated for the purpose of conducting business with 
Parker. If you are not an intended recipient, please notify the sender by 
replying to this message and then delete the information from your system. 
Thank you for your cooperation."
**********************************************************************
4D Internet Users Group (4D iNUG)
FAQ:  http://lists.4d.com/faqnug.html
Archive:  http://lists.4d.com/archives.html
Options: https://lists.4d.com/mailman/options/4d_tech
Unsub:  mailto:[email protected]
**********************************************************************

Reply via email to