codetools: test RenameIdentifier

git-svn-id: trunk@64978 -
This commit is contained in:
mattias 2021-04-12 18:49:28 +00:00
parent d54508d7b7
commit 01bf54a2cc
3 changed files with 251 additions and 22 deletions

View File

@ -539,6 +539,10 @@ type
function FindUsedUnitReferences(Code: TCodeBuffer; X, Y: integer;
SkipComments: boolean; out UsedUnitFilename: string;
var ListOfPCodeXYPosition: TFPList): boolean;
function FindReferencesInFiles(Files: TStringList;
DeclarationCode: TCodeBuffer; const DeclarationCaretXY: TPoint;
SearchInComments: boolean;
var TreeOfPCodeXYPosition: TAVLTree): boolean;
function RenameIdentifier(TreeOfPCodeXYPosition: TAVLTree;
const OldIdentifier, NewIdentifier: string;
DeclarationCode: TCodeBuffer = nil; DeclarationCaretXY: PPoint = nil): boolean;
@ -2775,6 +2779,66 @@ begin
{$ENDIF}
end;
function TCodeToolManager.FindReferencesInFiles(Files: TStringList;
DeclarationCode: TCodeBuffer; const DeclarationCaretXY: TPoint;
SearchInComments: boolean; var TreeOfPCodeXYPosition: TAVLTree): boolean;
var
i, j: Integer;
Code: TCodeBuffer;
ListOfPCodeXYPosition: TFPList;
Cache: TFindIdentifierReferenceCache;
Filename: String;
begin
Result:=false;
ListOfPCodeXYPosition:=nil;
TreeOfPCodeXYPosition:=nil;
Cache:=nil;
try
// search in every file
for i:=0 to Files.Count-1 do begin
Filename:=Files[i];
if ExtractFileNameOnly(Filename)='' then
continue; // invalid filename
//debugln(['TCodeToolManager.FindReferencesInFiles ',Filename]);
j:=i-1;
while (j>=0) and (CompareFilenames(Filename,Files[j])<>0) do dec(j);
if j>=0 then continue; // skip duplicate
Code:=LoadFile(Filename,true,false);
if Code=nil then begin
debugln('TCodeToolManager.FindReferencesInFiles unable to load "',Filename,'"');
exit;
end;
// search references
FreeListOfPCodeXYPosition(ListOfPCodeXYPosition);
if not FindReferences(
DeclarationCode,DeclarationCaretXY.X,DeclarationCaretXY.Y,
Code, not SearchInComments, ListOfPCodeXYPosition, Cache) then
begin
debugln('TCodeToolManager.FindReferencesInFiles unable to FindReferences in "',Code.Filename,'"');
exit;
end;
//debugln('TCodeToolManager.FindReferencesInFiles FindReferences in "',Code.Filename,'" ',dbgs(ListOfPCodeXYPosition<>nil));
// add to tree
if ListOfPCodeXYPosition<>nil then begin
if TreeOfPCodeXYPosition=nil then
TreeOfPCodeXYPosition:=CreateTreeOfPCodeXYPosition;
AddListToTreeOfPCodeXYPosition(ListOfPCodeXYPosition,
TreeOfPCodeXYPosition,true,false);
end;
end;
Result:=true;
finally
CodeToolBoss.FreeListOfPCodeXYPosition(ListOfPCodeXYPosition);
if not Result then
CodeToolBoss.FreeTreeOfPCodeXYPosition(TreeOfPCodeXYPosition);
Cache.Free;
end;
end;
function TCodeToolManager.RenameIdentifier(TreeOfPCodeXYPosition: TAVLTree;
const OldIdentifier, NewIdentifier: string; DeclarationCode: TCodeBuffer;
DeclarationCaretXY: PPoint): boolean;

View File

@ -1024,7 +1024,7 @@ begin
DefAWideChar:=succ(AWideChar);
Enum:=TEnum(0);
DefEnum:=succ(Enum);
EnumRg:=TEnumRg(0);
EnumRg:={%H-}TEnumRg(0);
DefEnumRg:=succ(EnumRg);
SetOfEnum:=[];
DefSetOfEnum:=[red];
@ -1032,11 +1032,11 @@ begin
DefSetOfEnumRg:=[red];
SetOfBool:=[];
DefSetOfBool:=[true];
MyInt:=TMyInt(0);
MyInt:={%H-}TMyInt(0);
DefMyInt:=MyInt+1;
SetOfMyInt:=[];
DefSetOfMyInt:=[2];
MyChar:=TMyChar(0);
MyChar:={%H-}TMyChar(0);
DefMyChar:=succ(MyChar);
SetOfMyChar:=[];
DefSetOfMyChar:=[#4];
@ -1356,7 +1356,7 @@ begin
V2:=low(ShortInt);
V3:=high(Word);
V4:=low(SmallInt);
V5:=high(LongWord);
V5:=high(LongWord){%H-};
V6:=low(LongInt);
V7:=high(QWord);
V8:=low(int64);

View File

@ -10,24 +10,165 @@ unit TestRefactoring;
interface
uses
Classes, SysUtils, CodeToolManager, CodeCache, CodeTree,
BasicCodeTools, LazLogger, LazFileUtils, fpcunit, testregistry,
Classes, SysUtils, CodeToolManager, CodeCache, CodeTree, BasicCodeTools,
CTUnitGraph, LazLogger, LazFileUtils, Laz_AVL_Tree, fpcunit, testregistry,
TestFinddeclaration;
const
ExplodeWithMarker = 'explodewith:';
type
{ TCustomTestRefactoring }
TCustomTestRefactoring = class(TCustomTestFindDeclaration)
protected
procedure RenameReferences(NewIdentifier: string);
procedure CheckDiff(CurCode: TCodeBuffer; const ExpLines: array of string);
end;
{ TTestRefactoring }
TTestRefactoring = class(TTestCase)
TTestRefactoring = class(TCustomTestRefactoring)
private
published
procedure TestExplodeWith;
procedure TestRenameReferences;
end;
implementation
{ TCustomTestRefactoring }
procedure TCustomTestRefactoring.RenameReferences(NewIdentifier: string);
var
Marker: TFDMarker;
Tool: TCodeTool;
DeclX, DeclY, DeclTopLine: integer;
DeclCode: TCodeBuffer;
Files: TStringList;
Graph: TUsesGraph;
Completed: boolean;
Node: TAVLTreeNode;
UGUnit: TUGUnit;
DeclarationCaretXY: TPoint;
PascalReferences: TAVLTree;
OldIdentifier: string;
begin
if not IsValidIdent(NewIdentifier) then
Fail('TCustomTestRefactoring.RenameReferences invalid NewName="'+NewIdentifier+'"');
// find marker #Rename
ParseSimpleMarkers(Code);
if MarkerCount<1 then
Fail('missing marker');
if MarkerCount>1 then
Fail('too many markers');
Marker:=Markers[0];
if Marker.Kind<>'#' then
Fail('expected # marker, but found '+Marker.Kind);
if not SameText(Marker.Name,'Rename') then
Fail('expected marker #Rename, but found #'+Marker.Name);
// find the main declaration
if not CodeToolBoss.Explore(Code,Tool,true,false) then
Fail('CodeToolBoss.Explore failed');
Code.AbsoluteToLineCol(Marker.NameStartPos,DeclarationCaretXY.Y,DeclarationCaretXY.X);
if not CodeToolBoss.FindMainDeclaration(Code,
DeclarationCaretXY.X,DeclarationCaretXY.Y,
DeclCode,DeclX,DeclY,DeclTopLine) then
begin
Fail('CodeToolBoss.FindMainDeclaration failed '+dbgs(DeclarationCaretXY)+' File='+Code.Filename);
end;
DeclarationCaretXY:=Point(DeclX,DeclY);
CodeToolBoss.GetIdentifierAt(DeclCode,DeclarationCaretXY.X,DeclarationCaretXY.Y,OldIdentifier);
// create the file list
Files:=TStringList.Create;
Graph:=nil;
PascalReferences:=nil;
try
Files.Add(DeclCode.Filename);
if CompareFilenames(DeclCode.Filename,Code.Filename)<>0 then
Files.Add(DeclCode.Filename);
Graph:=CodeToolBoss.CreateUsesGraph;
Graph.AddStartUnit(Code.Filename);
Graph.AddTargetUnit(DeclCode.Filename);
Graph.Parse(true,Completed);
Node:=Graph.FilesTree.FindLowest;
Files.Clear;
while Node<>nil do begin
UGUnit:=TUGUnit(Node.Data);
Files.Add(UGUnit.Filename);
Node:=Node.Successor;
end;
// search pascal source references
if not CodeToolBoss.FindReferencesInFiles(Files,DeclCode,
DeclarationCaretXY,true,PascalReferences) then begin
Fail('CodeToolBoss.FindReferencesInFiles failed at '+dbgs(DeclarationCaretXY)+' File='+Code.Filename);
end;
if not CodeToolBoss.RenameIdentifier(PascalReferences,
OldIdentifier, NewIdentifier, DeclCode, @DeclarationCaretXY)
then begin
Fail('CodeToolBoss.RenameIdentifier failed');
end;
finally
CodeToolBoss.FreeTreeOfPCodeXYPosition(PascalReferences);
Graph.Free;
Files.Free;
end;
end;
procedure TCustomTestRefactoring.CheckDiff(CurCode: TCodeBuffer;
const ExpLines: array of string);
var
CurLine: String;
i: Integer;
Differ: Boolean;
begin
//debugln(['TCustomTestRefactoring.CheckDiff ',CurCode.Filename,' ',length(ExpLines)]);
if High(ExpLines)=CurCode.LineCount-1 then begin
Differ:=false;
for i:=0 to High(ExpLines) do begin
if ExpLines[i]<>CurCode.GetLine(i,false) then
Differ:=true;
end;
if not Differ then exit;
end;
debugln('TCustomTestRefactoring.CheckDiff Expected=');
for i:=0 to High(ExpLines) do
debugln(' ',ExpLines[i]);
debugln('TCustomTestRefactoring.CheckDiff Found=');
for i:=0 to CurCode.LineCount-1 do
debugln(' ',CurCode.GetLine(i,false));
debugln('TCustomTestRefactoring.CheckDiff Diff=');
for i:=0 to High(ExpLines) do begin
if i>=CurCode.LineCount then begin
debugln(' Expec: ',ExpLines[i]);
debugln(' Found: ');
end else begin
CurLine:=CurCode.GetLine(i,false);
if ExpLines[i]<>CurLine then begin
debugln(' Expec: ',ExpLines[i]);
debugln(' Found: ',CurLine);
end else begin
debugln(' : ',ExpLines[i]);
end;
end;
end;
for i:=High(ExpLines)+1 to CurCode.LineCount-1 do begin
debugln('>>Expec: ');
debugln('<<Found: ',CurCode.GetLine(i,false));
end;
Fail('TCustomTestRefactoring.CheckDiff ');
end;
{ TTestRefactoring }
procedure TTestRefactoring.TestExplodeWith;
@ -40,7 +181,7 @@ type
end;
PWithBlock = ^TWithBlock;
var
Code: TCodeBuffer;
CurCode: TCodeBuffer;
Tool: TCodeTool;
Node, StatementNode: TCodeTreeNode;
CodeXYPos: TCodeXYPosition;
@ -50,9 +191,9 @@ var
aWith: PWithBlock;
begin
Filename:=ExpandFileNameUTF8('moduletests/rt_explodewith.pas');
Code:=CodeToolBoss.LoadFile(Filename,true,false);
AssertEquals('Load file error: '+Filename,true,Code<>nil);
if not CodeToolBoss.Explore(Code,Tool,true) then
CurCode:=CodeToolBoss.LoadFile(Filename,true,false);
AssertEquals('Load file error: '+Filename,true,CurCode<>nil);
if not CodeToolBoss.Explore(CurCode,Tool,true) then
AssertEquals('Parse error: ','',CodeToolBoss.ErrorMessage);
// collect all With-Blocks
Node:=Tool.Tree.Root;
@ -66,7 +207,7 @@ begin
aWith:=@ListOfWiths[High(ListOfWiths)];
aWith^.CodeXYPos:=CodeXYPos;
aWith^.WithExpr:=Tool.ExtractWithBlockExpression(Node,[]);
aWith^.StatementStartPos:=FindPrevNonSpace(Code.Source,StatementNode.StartPos);
aWith^.StatementStartPos:=FindPrevNonSpace(CurCode.Source,StatementNode.StartPos);
aWith^.StatementEndPos:=StatementNode.EndPos;
end;
end;
@ -77,26 +218,26 @@ begin
aWith:=@ListOfWiths[i];
CodeXYPos:=aWith^.CodeXYPos;
//debugln(['TTestRefactoring.TestExplodeWith ',dbgs(CodeXYPos)]);
OldSource:=Code.Source;
OldSource:=CurCode.Source;
try
if CodeToolBoss.RemoveWithBlock(Code,CodeXYPos.X,CodeXYPos.Y) then begin
if CodeToolBoss.RemoveWithBlock(CurCode,CodeXYPos.X,CodeXYPos.Y) then begin
// success
// => check changes
// get new bounds
NewStartPos:=aWith^.StatementStartPos;
NewEndPos:=aWith^.StatementEndPos;
Code.AdjustPosition(NewStartPos);
Code.AdjustPosition(NewEndPos);
if (NewStartPos<1) or (NewStartPos>Code.SourceLength)
or (NewEndPos<1) or (NewEndPos>Code.SourceLength)
CurCode.AdjustPosition(NewStartPos);
CurCode.AdjustPosition(NewEndPos);
if (NewStartPos<1) or (NewStartPos>CurCode.SourceLength)
or (NewEndPos<1) or (NewEndPos>CurCode.SourceLength)
or (NewEndPos<NewStartPos)
then begin
debugln(['TTestRefactoring.TestExplodeWith WrongCode: ']);
debugln(Code.Source);
debugln(CurCode.Source);
Fail('CodeToolBoss.RemoveWithBlock failed at '+dbgs(CodeXYPos));
end;
// check each marker
Src:=Code.Source;
Src:=CurCode.Source;
//debugln(['TTestRefactoring.TestExplodeWith NewBlock=',copy(Src,NewStartPos,NewEndPos-NewStartPos)]);
p:=NewStartPos;
repeat
@ -116,7 +257,7 @@ begin
then begin
Fail('CodeToolBoss.RemoveWithBlock failed at '+dbgs(CodeXYPos)
+': Expected insertion "'+ExpectedInsertion+'"'
+' at '+Code.AbsoluteToLineColStr(CommentEndPos)
+' at '+CurCode.AbsoluteToLineColStr(CommentEndPos)
+', but found "'+dbgstr(Src,CommentStartPos,20)+'"');
end;
end;
@ -130,11 +271,35 @@ begin
Fail('CodeToolBoss.RemoveWithBlock failed at '+dbgs(CodeXYPos)+': '+CodeToolBoss.ErrorMessage);
end;
finally
Code.Source:=OldSource;
CurCode.Source:=OldSource;
end;
end;
end;
procedure TTestRefactoring.TestRenameReferences;
begin
StartProgram;
Add([
'var Cow: longint;',
'begin',
' cow{#Rename}:=3;',
' test1.cow:=4;',
'end.',
'']);
RenameReferences('Bird');
CheckDiff(Code,[
'program test1;',
'',
'{$mode objfpc}{$H+}',
'',
'var Bird: longint;',
'begin',
' Bird{#Rename}:=3;',
' test1.Bird:=4;',
'end.',
'']);
end;
initialization
RegisterTests([TTestRefactoring]);
end.