DevAnalyzer


Overview

DevAnalyzer can be applied just like the normal Component Pascal compiler for checking a program's syntactic and semantic correctness (it even uses the base modules of the Component Pascal compiler). In the examples below you can, for instance, remove a semicolon somewhere and DevAnalyzer will report the resulting syntax error.

Click on any of the underlined blue warning messages to jump directly to the corresponding tutorial section.


Warning message 	Number	Explanation: item is ...	Option

neverused	900	never referenced in program	default
neverset	901	never assigned to	default
usedbeforeset	902	accessed before assigned to	default
setbutneverused	903	assigned to but never accessed	default
usedasvarpar,possiblynotset	904	passed as a varpar before assigned to	VAR Par
alsodeclaredinouterscope	905	also declared in outer scope	Levels
access/assignmenttointermediate	906	referenced in different scope	Intermed.
statementafterRETURN/EXIT	909	superfluous statement	default
forloopvariableset	910	changed, although being a loop variable	default
evaluationsequenceofparams	913	function call inside procedure call	Side Effects
superfluoussemicolon	930	semicolon before END, etc.	Semicolons


Introduction

During the development of a program, many design iterations are made. The source text is changed repeatedly and enhanced, errors are corrected, output statements are inserted and removed again later. The programmer changes procedure bodies and thereby inserts variables that are later not needed anymore. Modules are imported  for temporary purposes  but later never used.

To clean up such modules, which have evolved over time, DevAnalyzer has been developed. It is a "normal" Component Pascal compiler, which does a full syntactic and semantic analysis with error checking of the program at hand, but does not generate a symbol file or object code. Instead, it examines the program and reports problems in the code. The analysis takes place per module. Inter-module dependencies are not taken into account.


Tutorial

In the following sections, the different warnings generated by DevAnalyzer are explained in detail. A short source code example is given for illustration. To try out an example, simply click inside the embedded text view containing the example and select Analyze Module from the Info menu. You may also use the Analyzer Options... dialog. The Analyze button in the dialog has the same effect as Info->Analyze Module, i.e., both operate on the focus view.


Definitions

A program item (or item for short) denotes a construct of Component Pascal occurring in a declaration sequence. These are in particular: constants, types, variables, receivers, parameters, procedures, and modules. The terms variable, field, and parameter are used with their normal meaning.
	

Default Warnings, Always Enabled

Never Used

An item declared in the source code but never referenced again is flagged as never used. Frequent examples of such items are variables in declarations that were copied over from other declarations, or modules. Except for modules, the occurrence of such an item can be deleted.
Caution: Some modules in BlackBox Component Builder are imported because of a side effect in the module body, but are never used in the code. Be careful when removing module names from an import list. Typical examples are modules installing a new directory object in some base module.

When analyzing the next example, you will see that the following items are marked as never used: StdLog, Size, the y field of record Rec, SameRec, Dont, and j in procedure Dont.

MODULE NeverUsed;

	IMPORT StdLog;
	
	CONST N = 16; Size = 2*N;
	
	TYPE
		Rec = RECORD x, y: INTEGER END;
		SameRec = Rec;
	
	PROCEDURE Do;
		VAR r: Rec;
	BEGIN
		r.x := N; r.x := 2*r.x
	END Do;
	
	PROCEDURE Dont;
		VAR i, j, k: INTEGER;
	BEGIN
		k := 0;
		FOR i := 0 TO N-1 DO k := k+i END
	END Dont;

BEGIN Do
END NeverUsed.


Set But Never Used

An item that is initialized but never used again is marked as set but never used. Examples of such items are parameters of a procedure, which are set by definition but may never be used in the body of the procedure, because the programmer changed its implementation. Other examples are variables that were meant to be used in a loop or dummy variables to which a return value is assigned. Sometimes, these items are superfluous and can be deleted.

In the following example, the y field of Rec, the k parameter, and the val variable of Proc are set but never used.

MODULE SetButNeverUsed;

	TYPE Rec = RECORD x, y: INTEGER END;

	PROCEDURE Log2(u: INTEGER): INTEGER;
		VAR i: INTEGER;
	BEGIN
		ASSERT(u > 0, 20);
		i := 0;
		WHILE u > 0 DO u := u DIV 2; INC(i) END;
		RETURN i
	END Log2;

	PROCEDURE Proc(i, j, k: INTEGER);
		VAR r: Rec; val: INTEGER;
	BEGIN
		r.x := i; r.y := j;
		val := Log2(r.x)
	END Proc;
	
BEGIN Proc(5, 5, 5)
END SetButNeverUsed.


Used Before Set

Variables and record fields, which are used before they are set denote possible errors in the program and are marked as used before set. Furthermore, the first usage of such a variable is marked (except for records and arrays). Not all warnings are errors, though: A variable, which is set in one branch of an IF statement inside a loop and used before this assignment is marked by DevAnalyzer, although the code might be correct.

In the following example, variables r, i, and k and the first use of i and k are marked used before set. The usage and initialization of k might be correct, depending on the initial value of j. DevAnalyzer is not capable of tracking these dependencies, but flags dubious program parts so that the programmer takes a closer look at it.

MODULE UsedBeforeSet;

	TYPE Rec = RECORD x, y: INTEGER END;

	PROCEDURE Proc;
		VAR r: Rec; i, j, k: INTEGER;
	BEGIN
		r.x := r.y; j := i+1; r.y := i*r.x;
		LOOP
			IF j > 10 THEN i := 2*k; DEC(j)
			ELSIF j > 5 THEN i := 3*k; DEC(j)
			ELSIF j = 0 THEN k := 1
			ELSE EXIT
			END
		END
	END Proc;
	
BEGIN Proc
END UsedBeforeSet.


Never Set

A variable or a field of a record that never occurs on the left side of an assignment operator is marked as never set. This warning signals a real error in the program. It means that the variable or field is read but never gets a value assigned to.

In the following example, the y field of Rec and the variable i of Proc are never set. Furthermore, the first use of i is flagged as used before set.

MODULE NeverSet;

	TYPE Rec = RECORD x, y: INTEGER END;

	PROCEDURE Proc;
		VAR r: Rec; i, j: INTEGER;
	BEGIN
		r.x := i; j := r.x+r.y; j := 2*j
	END Proc;
	
BEGIN Proc
END NeverSet.


For Loop Variable Set

In a FOR loop, the loop variable should be read only, but never set. When a loop variable is set or passed to a procedure treating it as a VAR parameter (which might change the value of the parameter), the warning for loop variable set is issued. The first case usually is a programming error, whereas the second case needs to be looked at more closely.

In the following example, the local variable i is changed by the INC statement, while the local variable j is passed to a procedure treating it as a VAR parameter.

MODULE LoopVariables;

	IMPORT StdLog;

	PROCEDURE Init(VAR k: INTEGER);
	BEGIN k := 0
	END Init;

	PROCEDURE Do;
		VAR i, j: INTEGER;
	BEGIN
		FOR i := 0 TO 9 DO StdLog.Int(i); INC(i) END;
		StdLog.Ln;
		FOR j := 0 TO 9 DO Init(j) END
	END Do;

BEGIN Do
END LoopVariables.


Statement After RETURN/EXIT

Statements following an EXIT or a RETURN statement are never executed and may therefore be removed. This is an obvious error, but sometimes such code can be the result of various design iterations, where many changes were made. The superfluous statements are marked as statement after RETURN/EXIT.

In the following example, x := 0 in Sgn and i := i DIV 2 in Do are superfluous.

MODULE ReturnExit;

	PROCEDURE Sgn(x: INTEGER): INTEGER;
	BEGIN
		IF x < 0 THEN RETURN -1; x := 0 ELSIF x = 0 THEN RETURN 0 ELSE RETURN 1 END
	END Sgn;

	PROCEDURE Do;
		VAR i: INTEGER;
	BEGIN
		i := 15;
		LOOP
			IF Sgn(i) = -1 THEN EXIT; i := i DIV 2
			ELSIF ODD(i) THEN i := 3*i+1
			ELSE i := 2*i; EXIT
			END
		END
	END Do;

BEGIN Do
END ReturnExit.


Warnings Enabled Using The Analyze... Dialog

The following warnings are only generated, if the corresponding option in the Analyze... dialog is enabled. These options may be saved, reset to the default, or loaded with the various buttons in the dialog. When starting up, DevAnalyzer initializes itself with the last saved options.

Additionally, DevAnalyzer counts the number of statements in a module and displays it in the dialog. This gives a very good measure for the complexity of a module, as it is independent of the formatting style. One statement is counted for one of the possible alternatives in the Statement production of the Component Pascal report.




Used as Varpar, Possibly Not Set (VAR Parameter Option)

An item that before being initialized is passed as an actual variable parameter to a procedure, might never be initialized. I.e., it cannot be guaranteed from outside the procedure that the item is set before used inside the procedure. Since this is not always an error but merely a fact of insufficient analysis, this warning is only issued when enabled through the VAR parameter option in the Analyze... dialog. The generated warning is used as varpar, possibly not set.

In the following example, j is marked as used as varpar, possibly not set, while i is not marked since it is initialized in Do.

MODULE VarPar;

	PROCEDURE Varpars(VAR i: INTEGER; VAR j: INTEGER; k: INTEGER);
	BEGIN
		i := 5; j := 6; k := 7*k
	END Varpars;
	
	PROCEDURE Do;
		VAR i, j, k: INTEGER;
	BEGIN
		i := 1; k := 2;
		Varpars(i, j, k)
	END Do;

BEGIN Do
END VarPar.


Analysis of Exported and Intermediate Items (Exported Items and Levels Option)

Items, which are exported are not subjected to analysis (used before set, never used etc.). If these items should be included in the analysis, enable the Exported items option.
	Local items, which are declared in a different scope are excluded from the analysis as well. It cannot be determined efficiently, if an item is initialized in a local procedure, for instance. Enable the Levels option in the Analyze... dialog to include such items in the analysis.

In the following example, analyzing without these options enabled will give no warnings. When you enable the options, however, the exported procedure Do is marked as never used, the local variable k is marked as never set, and its use in Local is marked as used before set.

MODULE ExportLevel;

	CONST Local = 5;
	VAR i: LONGINT;
	
	PROCEDURE Do*;
		VAR i, j, k: INTEGER;
		PROCEDURE Local(i: INTEGER);
		BEGIN j := i+k
		END Local;
	BEGIN
		j := 0;
		FOR i := 0 TO 7 DO j := j+i END;
		Local(j)
	END Do;

BEGIN i := 0; i := i+Local
END ExportLevel.

Also Declared in Outer Scope, Access/Assignment to Intermediate (Intermediate Items Option)

Reusing names of items declared outside the current scope can lead to confusion, especially when looking at the code after some time. To mark these items, the warning also declared in outer scope is issued when encountering such an item.
	Also, assigning to or using an item from a different scope can be erroneous, as one might think that a global item is changed or accessed. Therefore, such assignments or uses are marked as access/assignment to intermediate.
	The Intermediate items option in the Analyze... dialog controls this feature.

In the following example, the local variable i, the local procedure Local, and the parameter i of Local are marked as also declared in outer scope. The assignment to j and the use of k in Local are marked as access/assignment to intermediate.

MODULE Intermediates;

	CONST Local = 5;
	VAR i: LONGINT;
	
	PROCEDURE Do;
		VAR i, j, k: INTEGER;
		PROCEDURE Local(i: INTEGER);
		BEGIN j := i+k
		END Local;
	BEGIN
		j := 0;
		FOR i := 0 TO 7 DO j := j+i END;
		Local(j)
	END Do;

BEGIN i := 0; i := i+Local; Do
END Intermediates.


Evaluation Sequence of Params. (Side Effects Option)

As the evaluation sequence of parameters in a procedure call is not defined in Component Pascal, function calls in parameter lists are problematic, if the functions have side effects on the global state. Therefore (and for other reasons), functions with side effects should be avoided (at least calling them in parameter lists).
	E.g., a function call returning a value, which in turn is passed as a parameter to a procedure, and which might change the global state of which some variable is used again in the parameter list, depends heavily on the evaluation sequence. DevAnalyzer marks this problematic code with evaluation sequence of params..
	Enable the Side Effects option in the Analyze Options... dialog for this analysis.

In the example, function F changes the global variable x. The result of F and x are used in the procedure call writing out the values. Try this code on two different machine architectures or using two different Component Pascal compilers, and compare the results.

MODULE Evaluation;
	IMPORT StdLog;
	VAR x: INTEGER;

	PROCEDURE F(): INTEGER;
	BEGIN
		INC(x); RETURN x
	END F;
	
	PROCEDURE Write(a, b: INTEGER);
	BEGIN
		StdLog.Int(a); StdLog.Int(b); StdLog.Ln
	END Write;

BEGIN x := 5; Write(x, F())
END Evaluation.


Superfluous Semicolon (Semicolons Option)

This warning is for the programmer with aesthetic ambitions (or simply for picky people). It marks superfluous semicolons in the code, i.e. semicolons before END or other statement-ending symbols. These semicolons are marked with superfluous semicolon.
	Enable it with the Semicolons option in the Analyze Options... dialog.

Try it out and see for yourself.

MODULE Semicolons;

	TYPE R = RECORD x: INTEGER; END;
	
	PROCEDURE Do(i: INTEGER);
		VAR j: INTEGER; r: R;
	BEGIN
		IF i < 0 THEN j := -1; ELSIF i = 0 THEN j := 0; ELSE j := 1; END;
		REPEAT DEC(i); UNTIL i = 0;
		CASE j OF
		| -1: r.x := 17;
		| 0: r.x := 3; r.x := 2*r.x;
		ELSE i := 0
		END; 
	END Do;

BEGIN Do(28);
END Semicolons.





Stefan H.-M. Ludwig	24 March 1998
