Dev/Docu/ru/Analyzer.odc

DevAnalyzer

ENGLISH
Stefan H.-M. Ludwig


Обзор

Щелкните на одной из синих ссылок, чтобы перейти прямо к соответствующему разделу руководства. Для технической поддержки перейдите в раздел  контактнаяинформация. 


Предупреждение	Номер	Объяснение: элемент ...	ОпцияInfo>Analyzeroptions...
neverused	900	нигде не  используется	по умолчанию
neverset	901	нигде не присваивается значение	по умолчанию
usedbeforeset	902	используется до инициализации	по умолчанию
setbutneverused	903	присвоенное значение не используется	по умолчанию
usedasvarpar,possiblynotset	904	пар-р-ссылка передается до иниц-ии	VAR Par
alsodeclaredinouterscope	905	также объявлен во внешней области	Levels
access/assignmenttointermediate	906	используется из другой области	Intermed.
statementafterRETURN/EXIT	909	излишний оператор	по умолчанию
forloopvariableset	910	изменен, хотя явл. перем. цикла	по умолчанию
evaluationsequenceofparams	913	вызов функции внутри выз. процедуры	Side Effects
superfluoussemicolon	930	точка с запятой перед END, и т. п.	Semicolons

Введение

В процессе разработки программы делается много итераций проектирования. Исходный текст вновь и вновь изменяется и расширяется, исправляются ошибки, операторы вывода вставляются и удаляются снова и снова. Программист изменяет тела процедур и при этом вставляет переменные, которые потом становятся больше не нужны. Для временных задач импортируются модули, которы позже никогда не используются.

Для очистки таких, постоянно изменяющихся, модулей был разработан DevAnalyzer. Это "обычный" компилятор Компонентного Паскаля, который выполняет полный синтаксический и семантический анализ с проверкой на ошибки, но не создаёт символьного файла или объектного кода. Вместо этого он иследует программу на наличие проблем в исходном коде. Анализ выполняется помодульно. Межмодульные зависимости в рассчёт не принимаются.

DevAnalyzer может быть использован как обычный компилятор Компонентного Паскаля для проверки синтаксической и семантической корректности программы (он даже использует при работе основные модули компилятора Компонентного Паскаля). В привёденных ниже фрагментах кода, Вы, например, можете убрать где-нибудь точку с запятой, и DevAnalyzer сообщит о появившейся ошибке.


Руководство

В последующиех разделах подробно рассказывается о различных предупреждениях, выдаваемых анализатором. В качестве демонстрации приводятся небольшие модули-примеры. Чтобы проверить работу анализатора достаточно щёлкнуть по внедрённому тексту с примером и выбрать AnalyzeЏModule из меню Info. Также можно использовать диалог AnalyzerЏOptions... Кнопка Analyze диалога равносильна пункту меню Info>AnalyzeModule; в обоих случаях выполняется анализ сфокусированного текста.


Определения

Обозначим за элемент программы (просто элемент для краткости) любую конструкцию Компонентного Паскаля, встречающуюся в последовательности описаний. Это, в частности: константы, типы, переменные, принимающие параметры, параметры, процедуры, и модули. Термины переменная, поле, и параметр используются в их обычном значении.


Предупреждения по умолчанию, выдаются всегда

Never Used

Элемент, который только описан в исходном коде и нигде не используется, помечается как "never used". Чаще всего такими элементами являются переменные в описаниях, которые скопированы из других описаний, или модули. Если элемент не является модулем, он может быть удалён.
Внимание: Некоторые модули в BlackBox Component Builder импортируются только ради выполнения их секций инициализации, и в коде не используются. Будьте внимательны, когда удаляете имена модулей из списка импорта. Типичные примеры - модули, устанавливающие новый фабричный объект в некоторый базовый модуль.

При анализе последующего примера, Вы увидите следующие элементы, помеченные "never used": StdLog, Size, поле y записи Rec, SameRec, Dont, и j в процедуре 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

Элемент, который инициализирован и далее не используется, помечается как "set but never used". Примерами таких элементов являются параметры процедуры, которые инициализированы по определению, но могут быть не использованы в теле процедуры, т. к. программист изменил её реализацию. Другие примеры - переменные, использование которых планировалось в цикле или незначащие переменные, которым присваиваются значения процедур-функций. Иногда эти элементы избыточны и могут быть удалены.

В следующем примере, поле y записи Rec, параметр k и переменная val процедуры Proc помечаются "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

Переменные и поля записи, которые используются до присвоения им зачения, сигнализируют о возможных ошибках в программе и помечаются "used before set". Более того, помечается место первого использования таких переменных (за исключением записей и массивов). Не все предупреждения указывают на ошибки, однако: Переменная, которая задаётся в одной из веток оператора IF внутри цикла и используется до этого присваивания, помечается анализатором, хотя код может быть правильным.

В следующем примере, переменные r, i и k помечаются "used before set" (для i и k - их первое использование). В зависимости от начального значения j использование и инициализация k могут быть корректны. DevAnalyzer не в состоянии отслеживать эти зависимости, но помечает сомнительные части программы, чтобы программист более внимательно взглянул на них.

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

Переменная или поле записи, которое никогда не оказывалось с левой стороны оператора присваивания, помечается как "never set". Это предупреждение о реальной ошибке в программе. Оно значит, что используется неопределённое (т.Џк. не было присваивания) значение переменной или поля записи.

В следующем примере, поле y записи Rec и переменная i процедуры Proc будут помечены "never set". К тому же, первое использование переменной i будет помечено "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

Значение управляющей переменной цикла FOR не должно изменяться другими операторами. Если управляющая переменая изменяется или передаётся в качестве VAR-параметра процедуры (что означает возможное изменения значения параметра), выдаётся предупреждение "for loop variable set". Первый случай, как правило, говорит об ошибке в программе. Второй - требует внимательного изучения.

В следующем примере, локальная переменная i изменяется процедурой INC, а локальная переменная j передаётся в качестве VAR-параметра процедуры.

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

Операторы, идущие за EXIT или RETURN, никогда не выполнятся, и, поэтому, могут быть удалены. Это явная ошибка, но и она может быть не замечена при больших объёмах изменений после многочисленных итераций проектирования. Избыточные операторы помечаются "statement after RETURN/EXIT".

В следующем примере, операторы x := 0 в процедуре Sgn и i := i DIV 2 в Do излишни.

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.


Выборочные предупреждения, диалог AnalyzeЏOptions...

Последующие предупреждения выдаются, если соответствующий переключатель в диалоге AnalyzeЏOptions... включён. Кнопки диалога позволяют сохранить (Save Options) и загрузить (Load Options) настройки, а также сбросить их к значениям по-умолчанию (Reset Options). При запуске DevAnalyzer использует последние сохранённые настройке.

Дополнительно в диалоге показывается количество операторов в модуле, которое считает DevAnalyzer. Это очень хорошая мера сложности модуля, т.Џк. она не зависит от стиля оформления. Один оператор - одна из возможных альтернатив продукции "оператор" [Statement] грамматики Компонентного Паскаля (см. сообщение о языке).




Used as Varpar, Possibly Not Set (переключатель VAR Parameter)

Элемент, который до инициализации передан в качестве фактического VAR-параметра процедуре, может оказаться вообще не инициализирован. Т.Џе., ничто вне процедуры не может гарантировать, что внутри процедуры элемент получит осмысленное значение до его использования. Поскольку подробный анализ (внутри процедуры) не выполняется, предупреждение не всегда говорит об ошибке и выдаётся только когда включён переключатель VAR parameter диалога AnalyzeЏOptions... Текст предупреждения - "used as varpar, possibly not set".

В следующем примере, j помечается "used as varpar, possibly not set", в то время как i, получающая в 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.


Анализ экспортированных и промежуточных элементов (переключатели Exported Items и Levels)

Экспортированные элементы исключаются из анализа (предупреждения "used before set", "never used" и т.Џд.). Если их нужно подвергнуть рассмотрению, включите переключатель Exported items.

Локальные элементы, описанные в другой области видимости также исключаются из анализа. Например, не возможно эффективно определить факт инициализации элемента в локальной процедуре. Включите переключатель Levels диалога AnalyzeЏOptions... чтобы анализ таких элементов выполнялся.

Если эти переключатели выключены, при анализе следующего примера предупреждения не будут выданы. Однако, при их включении, экспортированная процедура Do помечается как "never used", локальная переменная k как "never set", и её использование в Local как "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)

Повторное использование имён элементов, описанных вне текущей области видимости, ведёт к замешательству при чтении кода, особенно по прошествии некоторого времени. При обнаружении, такие элементы помечаются как "also declared in outer scope".

Также, присвоения или использование элементов из другой области видимости может быть причиной ошибок, т.Џк. может быть воспринято, как изменение/использование глобального элемента. Поэтому, такие присваивания/использования помечаются как "access/assignment to intermediate".

Выдачу этих предупреждений  определяет переключатель Intermediate items диалога AnalyzeЏOptions...

В следующем примере, локальная переменная i, локальная процедура Local, и параметр i процедуры Local помечаются как "also declared in outer scope". Присвоение j и использование k в Local помечаются как "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)

Последовательность вычисления параметров при вызове процедуры в Компонентном Паскале не определена. Т.Џо. вызов функций с побочными эффектами (изменяющих глобальное состояние) в списке параметров проблематичен. Поэтому (и по другим причнам), следует избегать функций с побочными эффектами (как минимум - их вызовов в списке параметров).

Например, если фактическими параметрами выступают некая глобальная переменная и значение функции, побочный эффект которой заключается в изменении этой переменной, то итоговое значение фактических параметров сильно зависит от последовательности их вычисления. DevAnalyzer отмечает такой проблемный код пометкой  "evaluation sequence of params".

Для выполнения этого анализа нужно включить переключатель Side Effects диалога AnalyzeЏOptions...

В примере функция F изменяет глобальную переменную x. Значение F и x формируют фактические параметры процедуры, печатающей их. Попробуйте выполнить этот код на разных машинных архитектурах или с использованием разных компиляторов Комонентного Паскаля и сравните результаты.

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)

Это предупреждение для программистов-эстетов (или просто придирчивых людей). В коде помечаются лишние точки с запятой - предшествующие END или другим терминальным лексемам, которые могут идти после нетерминала оператор [Statement]. Пометка - "superfluous semicolon".

Включите переключатель Semicolons диалога AnalyzeЏOptions....

Попробуйте и убедитесь лично.

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.

Контактная информация

Вы можете связаться со мной напрямую или через Oberon microsystems. Если Вы обнаружите ошибки или захотите увидеть новые возможности в новой версии DevAnalyzer, дайте мне знать. Также, если пожелаете, сообщите о Вашем (позитивном или негативном) опыте использования данного продукта (support@oberon.ch).

		Stefan H.-M. Ludwig	24 марта 1998