mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-09-24 14:19:11 +02:00
codetools: started function to disable unreachable IFDEF blocks
git-svn-id: trunk@11791 -
This commit is contained in:
parent
ece5c99e5b
commit
ff1ef707c1
@ -130,6 +130,8 @@ type
|
||||
var Changed: boolean);
|
||||
procedure DisableAllUnusedDefines(var Changed: boolean);
|
||||
procedure MoveIfNotThenDefsUp(var Changed: boolean);
|
||||
procedure DisableUnreachableBlocks(Undefines, Defines: TStrings;
|
||||
var Changed: boolean);
|
||||
procedure DisableDefineNode(Node: TCodeTreeNode; var Changed: boolean);
|
||||
procedure DisableIfNode(Node: TCodeTreeNode; var Changed: boolean);
|
||||
procedure DisableNode(Node: TCodeTreeNode; var Changed: boolean);
|
||||
@ -237,6 +239,31 @@ procedure AdjustPositionAfterInsert(var p: integer; IsStart: boolean;
|
||||
|
||||
implementation
|
||||
|
||||
type
|
||||
TDefineStatus = (
|
||||
dsUnknown,
|
||||
dsDefined,
|
||||
dsNotDefined
|
||||
);
|
||||
|
||||
TDefineValue = class
|
||||
Name: string;
|
||||
Status: TDefineStatus;
|
||||
Value: string;
|
||||
end;
|
||||
|
||||
function CompareDefineValues(Data1, Data2: Pointer): integer;
|
||||
begin
|
||||
Result:=CompareIdentifierPtrs(Pointer(TDefineValue(Data1).Name),
|
||||
Pointer(TDefineValue(Data2).Name));
|
||||
end;
|
||||
|
||||
function ComparePCharWithDefineValue(Name, DefValue: Pointer): integer;
|
||||
begin
|
||||
Result:=CompareIdentifierPtrs(Name,
|
||||
Pointer(TDefineValue(DefValue).Name));
|
||||
end;
|
||||
|
||||
function CompareCompilerMacroStats(Data1, Data2: Pointer): integer;
|
||||
begin
|
||||
Result:=CompareIdentifierPtrs(Pointer(TCompilerMacroStats(Data1).Name),
|
||||
@ -694,6 +721,32 @@ procedure TCompilerDirectivesTree.MoveIfNotThenDefsUp(var Changed: boolean
|
||||
|
||||
And move the define behind the IF block
|
||||
*)
|
||||
|
||||
function IdentifierIsReadAfterNode(Identifier: PChar;
|
||||
StartNode: TCodeTreeNode): boolean;
|
||||
var
|
||||
Node: TCodeTreeNode;
|
||||
ParentNode: TCodeTreeNode;
|
||||
begin
|
||||
Node:=StartNode;
|
||||
while Node<>nil do begin
|
||||
case Node.Desc of
|
||||
cdnIf,cdnElseIf:
|
||||
if FindNameInIfExpression(Node,Identifier)>0 then exit(true);
|
||||
cdnDefine:
|
||||
if DefineUsesName(Node,Identifier) then begin
|
||||
ParentNode:=StartNode;
|
||||
while (ParentNode<>nil) do begin
|
||||
if ParentNode=Node.Parent then exit(false);
|
||||
ParentNode:=ParentNode.Parent;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
Node:=Node.Next;
|
||||
end;
|
||||
Result:=false;
|
||||
end;
|
||||
|
||||
var
|
||||
Node: TCodeTreeNode;
|
||||
NextNode: TCodeTreeNode;
|
||||
@ -724,7 +777,6 @@ begin
|
||||
cdnIf, cdnElseIf:
|
||||
if FindNameInIfExpression(SubNode,@Src[NameStart])>0 then begin
|
||||
// this sub IF block uses the macro
|
||||
|
||||
LastIFNode:=SubNode;
|
||||
end;
|
||||
|
||||
@ -767,12 +819,12 @@ begin
|
||||
SubNode:=NextSubNode;
|
||||
end;
|
||||
|
||||
if (LastDefineNode<>nil) and (LastIFNode=nil) then begin
|
||||
if (LastChildDefineNode<>nil) then begin
|
||||
(* this is
|
||||
{$IFNDEF Name}
|
||||
...
|
||||
{$DEFINE Name}
|
||||
... Name not used ...
|
||||
... Name only read ...
|
||||
{$ENDIF}
|
||||
|
||||
or IFDEF and UNDEF
|
||||
@ -781,12 +833,17 @@ begin
|
||||
EndNode:=Node;
|
||||
while (EndNode<>nil) and (EndNode.Desc<>cdnEnd) do
|
||||
EndNode:=EndNode.NextBrother;
|
||||
if EndNode<>nil then begin
|
||||
if (EndNode<>nil)
|
||||
and IdentifierIsReadAfterNode(@Src[NameStart],EndNode) then begin
|
||||
InsertPos:=FindLineEndOrCodeAfterPosition(Src,EndNode.EndPos,SrcLen,
|
||||
NestedComments);
|
||||
NewSrc:=LineEnding+GetDirective(LastDefineNode);
|
||||
InsertDefine(InsertPos,NewSrc,LastDefineNode.SubDesc);
|
||||
DisableDefineNode(LastDefineNode,Changed);
|
||||
if LastDefineNode=nil then begin
|
||||
// the name was not read after it was set -> disable the define
|
||||
// in the block
|
||||
DisableDefineNode(LastDefineNode,Changed);
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
@ -794,6 +851,257 @@ begin
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TCompilerDirectivesTree.DisableUnreachableBlocks(Undefines,
|
||||
Defines: TStrings; var Changed: boolean);
|
||||
type
|
||||
|
||||
PDefineChange = ^TDefineChange;
|
||||
TDefineChange = record
|
||||
Name: string;
|
||||
OldStatus: TDefineStatus;
|
||||
Next: PDefineChange;
|
||||
end;
|
||||
|
||||
var
|
||||
CurDefines: TAVLTree;
|
||||
Stack: array of PDefineChange;// stack of lists of PDefineChange
|
||||
StackPointer: integer;
|
||||
|
||||
procedure InitStack;
|
||||
begin
|
||||
SetLength(Stack,1);
|
||||
StackPointer:=0;
|
||||
Stack[0]:=nil;
|
||||
end;
|
||||
|
||||
procedure FreeStack;
|
||||
var
|
||||
i: Integer;
|
||||
Item: PDefineChange;
|
||||
DeleteItem: PDefineChange;
|
||||
begin
|
||||
for i:=0 to StackPointer do begin
|
||||
Item:=Stack[i];
|
||||
while Item<>nil do begin
|
||||
DeleteItem:=Item;
|
||||
Item:=DeleteItem^.Next;
|
||||
Dispose(DeleteItem);
|
||||
end;
|
||||
end;
|
||||
Setlength(Stack,0);
|
||||
end;
|
||||
|
||||
procedure AddStackChange(const MacroName: string; OldStatus: TDefineStatus);
|
||||
var
|
||||
Change: PDefineChange;
|
||||
begin
|
||||
New(Change);
|
||||
FillChar(Change^,SizeOf(TDefineChange),0);
|
||||
Change^.Name:=MacroName;
|
||||
Change^.OldStatus:=OldStatus;
|
||||
Change^.Next:=Stack[StackPointer];
|
||||
Stack[StackPointer]:=Change;
|
||||
end;
|
||||
|
||||
function GetStatus(Identifier: PChar): TDefineStatus;
|
||||
var
|
||||
AVLNode: TAVLTreeNode;
|
||||
begin
|
||||
AVLNode:=CurDefines.FindKey(Identifier,@ComparePCharWithDefineValue);
|
||||
if AVLNode<>nil then
|
||||
Result:=TDefineValue(AVLNode.Data).Status
|
||||
else
|
||||
Result:=dsUnknown;
|
||||
end;
|
||||
|
||||
procedure SetStatus(Identifier: PChar; NewStatus: TDefineStatus;
|
||||
SaveOnStack: boolean = true);
|
||||
var
|
||||
AVLNode: TAVLTreeNode;
|
||||
DefValue: TDefineValue;
|
||||
begin
|
||||
AVLNode:=CurDefines.FindKey(Identifier,@ComparePCharWithDefineValue);
|
||||
if AVLNode=nil then begin
|
||||
if NewStatus<>dsUnknown then begin
|
||||
DefValue:=TDefineValue.Create;
|
||||
DefValue.Name:=GetIdentifier(Identifier);
|
||||
DefValue.Status:=NewStatus;
|
||||
AddStackChange(DefValue.Name,dsUnknown);
|
||||
end else begin
|
||||
// no change
|
||||
end;
|
||||
end else begin
|
||||
DefValue:=TDefineValue(AVLNode.Data);
|
||||
if NewStatus<>dsUnknown then begin
|
||||
if NewStatus<>DefValue.Status then begin
|
||||
AddStackChange(DefValue.Name,DefValue.Status);
|
||||
DefValue.Status:=NewStatus;
|
||||
end;
|
||||
end else begin
|
||||
AddStackChange(DefValue.Name,DefValue.Status);
|
||||
CurDefines.Delete(AVLNode);
|
||||
DefValue.Free;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure InitDefines;
|
||||
var
|
||||
i: Integer;
|
||||
CurName: string;
|
||||
begin
|
||||
CurDefines:=TAVLTree.Create(@CompareDefineValues);
|
||||
if Undefines<>nil then begin
|
||||
for i:=0 to Undefines.Count-1 do
|
||||
if Undefines[i]<>'' then
|
||||
SetStatus(PChar(Undefines[i]),dsNotDefined);
|
||||
end;
|
||||
if Defines<>nil then begin
|
||||
for i:=0 to Defines.Count-1 do begin
|
||||
CurName:=Defines.Names[i];
|
||||
if CurName='' then continue;
|
||||
SetStatus(PChar(CurName),dsDefined);
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure FreeDefines;
|
||||
begin
|
||||
if CurDefines=nil then exit;
|
||||
CurDefines.FreeAndClear;
|
||||
FreeAndNil(CurDefines);
|
||||
end;
|
||||
|
||||
procedure Push;
|
||||
begin
|
||||
inc(StackPointer);
|
||||
if StackPointer=length(Stack) then
|
||||
SetLength(Stack,length(Stack)*2+10);
|
||||
Stack[StackPointer]:=nil;
|
||||
end;
|
||||
|
||||
procedure Pop;
|
||||
var
|
||||
Change: PDefineChange;
|
||||
begin
|
||||
if StackPointer=0 then
|
||||
raise Exception.Create('TCompilerDirectivesTree.DisableUnreachableBlocks.Pop');
|
||||
// undo all changes
|
||||
while Stack[StackPointer]<>nil do begin
|
||||
Change:=Stack[StackPointer];
|
||||
SetStatus(PChar(Change^.Name),Change^.OldStatus,false);
|
||||
Stack[StackPointer]:=Change^.Next;
|
||||
Dispose(Change);
|
||||
end;
|
||||
dec(StackPointer);
|
||||
end;
|
||||
|
||||
var
|
||||
Node: TCodeTreeNode;
|
||||
NextNode: TCodeTreeNode;
|
||||
NameStart: integer;
|
||||
NewStatus: TDefineStatus;
|
||||
Identifier: PChar;
|
||||
OldStatus: TDefineStatus;
|
||||
HasValue: boolean;
|
||||
ValueStart: integer;
|
||||
ExprNode: TCodeTreeNode;
|
||||
IsIfBlock: Boolean;
|
||||
begin
|
||||
InitDefines;
|
||||
InitStack;
|
||||
try
|
||||
Node:=Tree.Root;
|
||||
while Node<>nil do begin
|
||||
NextNode:=Node.Next;
|
||||
case Node.Desc of
|
||||
cdnIf, cdnElse:
|
||||
begin
|
||||
if Node.Desc=cdnIf then begin
|
||||
IsIfBlock:=true;
|
||||
end else begin
|
||||
// close prior block
|
||||
IsIfBlock:=false;// it is an Else-block
|
||||
Pop;
|
||||
end;
|
||||
// start new block
|
||||
Push;
|
||||
|
||||
if IsIfBlock then begin
|
||||
ExprNode:=Node;
|
||||
end else begin
|
||||
if Node.PriorBrother.Desc=cdnIf then begin
|
||||
ExprNode:=Node.PriorBrother;
|
||||
end else begin
|
||||
ExprNode:=nil;
|
||||
end;
|
||||
end;
|
||||
|
||||
if (ExprNode<>nil) and IsIfExpressionSimple(ExprNode,NameStart) then
|
||||
begin
|
||||
// a simple expression
|
||||
Identifier:=@Src[NameStart];
|
||||
if (Node.SubDesc=cdnsUndef)=IsIfBlock then
|
||||
NewStatus:=dsNotDefined
|
||||
else
|
||||
NewStatus:=dsDefined;
|
||||
OldStatus:=GetStatus(Identifier);
|
||||
if (OldStatus=dsUnknown) or (OldStatus=NewStatus) then begin
|
||||
// this block is reachable
|
||||
SetStatus(Identifier,NewStatus);
|
||||
end else begin
|
||||
// this block is unreachable
|
||||
NextNode:=Node.NextBrother;
|
||||
if (NextNode<>nil) and (NextNode.Desc=cdnEnd) then begin
|
||||
// the end node will be disabled too, so do the Pop here
|
||||
NextNode:=Node.NextSkipChilds;
|
||||
Pop;
|
||||
end;
|
||||
// ToDo: disable content
|
||||
//DisableIfNode(Node,Changed);
|
||||
end;
|
||||
end else begin
|
||||
// a complex expression (If, ElseIf, Else)
|
||||
// assume: it is reachable
|
||||
end;
|
||||
end;
|
||||
|
||||
cdnElseIf:
|
||||
begin
|
||||
// if there is an ElseIf block, then there must be an IF block in front
|
||||
// And the IF block in front must be reachable,
|
||||
// otherwise it would be disabled
|
||||
Pop;
|
||||
// If+ElseIf gives a complex expression
|
||||
// assume: it is reachable
|
||||
Push;
|
||||
end;
|
||||
|
||||
cdnEnd:
|
||||
begin
|
||||
Pop;
|
||||
end;
|
||||
|
||||
cdnDefine:
|
||||
if ((Node.SubDesc=cdnsDefine) or (Node.SubDesc=cdnsUndef)
|
||||
or (Node.SubDesc=cdnsSetC))
|
||||
and GetDefineNameAndValue(Node,NameStart,HasValue,ValueStart) then begin
|
||||
if Node.SubDesc=cdnsDefine then
|
||||
NewStatus:=dsDefined
|
||||
else
|
||||
NewStatus:=dsNotDefined;
|
||||
// TODO: set status not only for stack level, but all levels
|
||||
SetStatus(@Src[NameStart],NewStatus);
|
||||
end;
|
||||
end;
|
||||
Node:=NextNode;
|
||||
end;
|
||||
finally
|
||||
FreeStack;
|
||||
FreeDefines;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TCompilerDirectivesTree.DisableDefineNode(Node: TCodeTreeNode;
|
||||
var Changed: boolean);
|
||||
var
|
||||
@ -1230,6 +1538,8 @@ begin
|
||||
|
||||
MoveIfNotThenDefsUp(Changed);
|
||||
|
||||
//DisableUnreachableBlocks(Undefines,Defines,Changed);
|
||||
|
||||
RemoveEmptyNodes(Changed);
|
||||
finally
|
||||
ClearMacros;
|
||||
|
Loading…
Reference in New Issue
Block a user