[fpc-pascal] Codetools find references
Ryan Joseph
genericptr at gmail.com
Fri Jun 26 11:00:46 CEST 2020
I'm finally revising this problem I had over a month ago with find references not working. I'm going to just post this code here and see if you see anything strange (most of it is from your example code).
What happens is that the unit graph seems to be incomplete if the units are in another directory and I use -Fu via FPCOptions (if supply no FPCOptions then it fails completely). If I take the units out of the directory the graph is complete and all references are found. Strangely it DOES find the first unit but none after that. Bugs or misuse?
============
FPC$ /Users/ryanjoseph/Developer/Projects/FPC/TestCodeTools/TestCodeTools
main program: /Users/ryanjoseph/Developer/Projects/FPC/TestCodeTools/test/test.pas
Found identifier: TSomeType
4 in unit graph
searching /Users/ryanjoseph/Developer/Projects/FPC/TestCodeTools/test/units/utypes.pas...
searching /Users/ryanjoseph/Developer/Projects/FPC/TestCodeTools/test/units/utypes.pas...
searching /Users/ryanjoseph/Developer/Projects/FPC/TestCodeTools/test/test.pas...
searching /Users/ryanjoseph/Developer/Projects/FPC/TestCodeTools/test/units/utypes.pas...
found: /Users/ryanjoseph/Developer/Projects/FPC/TestCodeTools/test/units/utypes.pas @ 10,3
found: /Users/ryanjoseph/Developer/Projects/FPC/TestCodeTools/test/units/utypes.pas @ 12,24
found: /Users/ryanjoseph/Developer/Projects/FPC/TestCodeTools/test/units/utypes.pas @ 16,24
found: /Users/ryanjoseph/Developer/Projects/FPC/TestCodeTools/test/test.pas @ 11,6
============
{$mode objfpc}
program test;
uses
ugenconst, uother, utypes;
{$include hidden.inc}
var
c: TSomeClass;
t: TSomeType = HIDDEN_VALUE;
begin
t := TSomeValue;
DoThis(t);
PrintType(t);
end.
============
{$mode objfpc}{$H+}
program TestCodeTools;
uses
SysUtils, Classes, DateUtils,
fpjson, fpjsonrtti,
CodeToolManager, CodeToolsConfig, CodeCache, IdentCompletionTool,
BasicCodeTools, CodeTree, FindDeclarationTool, PascalParserTool,
KeywordFuncLists, DefineTemplates, LazFileUtils, Laz_AVL_Tree,CTUnitGraph;
procedure InitCodeTools(Switches: array of string);
var
CodeToolsOptions: TCodeToolsOptions;
Switch: String;
begin
CodeToolsOptions := TCodeToolsOptions.Create;
with CodeToolsOptions do
begin
//FPCSrcDir := '/usr/local/share/fpcsrc';
//FPCPath := '/usr/local/lib/fpc/3.0.4/ppcx64';
InitWithEnvironmentVariables;
for Switch in Switches do
FPCOptions := FPCOptions + switch + ' ';
end;
with CodeToolBoss do
begin
Init(CodeToolsOptions);
IdentifierList.SortForHistory := True;
IdentifierList.SortForScope := True;
end;
end;
procedure CheckSyntax(Code: TCodeBuffer);
var
Tool: TCodeTool;
begin
if not CodeToolBoss.Explore(Code,Tool,true) then
if CodeToolBoss.ErrorCode <> nil then
writeln('Syntax Error -> '+CodeToolBoss.ErrorCode.FileName+': "'+CodeToolBoss.ErrorMessage+'" @ '+IntToStr(CodeToolBoss.ErrorLine)+':'+IntToStr(CodeToolBoss.ErrorColumn))
else
writeln('Syntax Error -> '+CodeToolBoss.ErrorMessage);
end;
procedure FindReferences(Filename, MainFilename: String; Y, X: Integer);
var
DeclCode, StartSrcCode, Code: TCodeBuffer;
ListOfPCodeXYPosition: TFPList;
DeclX, DeclY, DeclTopLine, i: Integer;
Identifier: string;
Graph: TUsesGraph;
Cache: TFindIdentifierReferenceCache;
TreeOfPCodeXYPosition: TAVLTree;
ANode, Node: TAVLTreeNode;
CodePos: PCodeXYPosition;
Files: TStringList;
Completed: boolean;
UGUnit: TUGUnit;
begin
// Step 1: load the file
StartSrcCode:=CodeToolBoss.LoadFile(Filename,false,false);
CheckSyntax(StartSrcCode);
// Step 2: find the main declaration
if not CodeToolBoss.FindMainDeclaration(StartSrcCode,
X,Y,
DeclCode,DeclX,DeclY,DeclTopLine) then
begin
writeln('FindMainDeclaration failed in '+StartSrcCode.FileName+' at '+IntToStr(Y)+':'+IntToStr(X));
ExitCode:=-1;
exit;
end;
CheckSyntax(DeclCode);
// Step 3: get identifier
CodeToolBoss.GetIdentifierAt(DeclCode,DeclX,DeclY,Identifier);
writeln('Found identifier: ',Identifier);
// Step 4: collect all modules of program
Files:=TStringList.Create;
ListOfPCodeXYPosition:=nil;
TreeOfPCodeXYPosition:=nil;
Cache:=nil;
try
Files.Add(DeclCode.Filename);
if CompareFilenames(DeclCode.Filename,StartSrcCode.Filename)<>0 then
Files.Add(DeclCode.Filename);
// parse all used units
Graph:=CodeToolBoss.CreateUsesGraph;
try
Graph.AddStartUnit(MainFilename);
Graph.AddTargetUnit(DeclCode.Filename);
Graph.Parse(true,Completed);
Node:=Graph.FilesTree.FindLowest;
while Node<>nil do begin
UGUnit:=TUGUnit(Node.Data);
Files.Add(UGUnit.Filename);
Node:=Node.Successor;
end;
finally
Graph.Free;
end;
// Step 5: find references in all files
writeln(Files.count, ' in unit graph');
for i:=0 to Files.Count-1 do begin
writeln('searching ', Files[i], '...');
Code:=CodeToolBoss.LoadFile(Files[i],true,false);
if Code=nil then begin
writeln('unable to load "',Files[i],'"');
continue;
end;
// check Syntax
CheckSyntax(Code);
// search references
CodeToolBoss.FreeListOfPCodeXYPosition(ListOfPCodeXYPosition);
if not CodeToolBoss.FindReferences(
DeclCode,DeclX,DeclY,
Code, true, ListOfPCodeXYPosition, Cache) then
begin
writeln('FindReferences failed in "'+Code.Filename+'"');
continue;
end;
if ListOfPCodeXYPosition=nil then continue;
// In order to show all references after any parser error, they are
// collected in a tree
if TreeOfPCodeXYPosition=nil then
TreeOfPCodeXYPosition:=CodeToolBoss.CreateTreeOfPCodeXYPosition;
CodeToolBoss.AddListToTreeOfPCodeXYPosition(ListOfPCodeXYPosition,
TreeOfPCodeXYPosition,true,false);
end;
// Step 6: show references
if TreeOfPCodeXYPosition=nil then begin
// No references found
exit;
end;
ANode:=TreeOfPCodeXYPosition.FindHighest;
while ANode<>nil do begin
CodePos:=PCodeXYPosition(ANode.Data);
writeln(' found: ', CodePos^.Code.Filename, ' @ ', CodePos^.Y, ',',CodePos^.X);
ANode:=TreeOfPCodeXYPosition.FindPrecessor(ANode);
end;
finally
Files.Free;
CodeToolBoss.FreeListOfPCodeXYPosition(ListOfPCodeXYPosition);
CodeToolBoss.FreeTreeOfPCodeXYPosition(TreeOfPCodeXYPosition);
Cache.Free;
end;
end;
var
Path, ProgramPath: String;
begin
ChDir('/Users/ryanjoseph/Developer/Projects/FPC/TestCodeTools');
InitCodeTools([
'-Fu'+ExpandFileName('./test/units'),
'-Fi'+ExpandFileName('./test/include')
]);
ProgramPath := ExpandFileName('./test/test.pas');
Path := ProgramPath;
writeln('main program: ', ProgramPath);
FindReferences(Path, ProgramPath, 11, 13);
end.
Regards,
Ryan Joseph
More information about the fpc-pascal
mailing list