lazarus/components/codetools/finddeclarationtool.pas
lazarus c30b72500e MG: find declaration of inherited keyword
git-svn-id: trunk@614 -
2002-01-21 20:41:53 +00:00

2251 lines
78 KiB
ObjectPascal

{
***************************************************************************
* *
* This source is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This code is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* General Public License for more details. *
* *
* A copy of the GNU General Public License is available on the World *
* Wide Web at <http://www.gnu.org/copyleft/gpl.html>. You can also *
* obtain it by writing to the Free Software Foundation, *
* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
* *
***************************************************************************
Author: Mattias Gaertner
Abstract:
TFindDeclarationTool enhances the TPascalParserTool with the ability
to find the source position or code tree node of a declaration.
ToDo:
- many things, search for 'ToDo'
- Difficulties:
1. Searching recursively
- ParentNodes
- Ancestor Classes/Objects/Interfaces
- with statements
- operators: '.', '()', 'A()', '^', 'inherited'
2. Searching enums must be searched in sub nodes
-> all classes node trees must be built
3. Searching in used units (interface USES and implementation USES)
4. Searching forward for pointer types e.g. ^Tralala
5. Mass Search: searching a compatible proc will result
in searching every parameter type of every reachable proc
(implementation section + interface section
+ used interface sections + class and ancestor methods)
How can this be achieved in good time?
-> Caching
- Caching:
Where:
For each section node (Interface, Implementation, ...)
For each BeginBlock
Entries: (What, Declaration Pos)
What: Identifier -> Ansistring (to reduce memory usage,
maintain a list of all identifier ansistrings)
Pos: Code+SrcPos
1. Source: TCodeTreeNode
2. PPU, PPW, DCU, ...
}
unit FindDeclarationTool;
{$ifdef FPC}{$mode objfpc}{$endif}{$H+}
interface
{$I codetools.inc}
{ $DEFINE CTDEBUG}
{ $DEFINE ShowTriedFiles}
{ $DEFINE ShowTriedContexts}
uses
{$IFDEF MEM_CHECK}
MemCheck,
{$ENDIF}
Classes, SysUtils, CodeTree, CodeAtom, CustomCodeTool, SourceLog,
KeywordFuncLists, BasicCodeTools, LinkScanner, CodeCache, AVL_Tree, TypInfo,
PascalParserTool, FileProcs, DefineTemplates;
type
TFindDeclarationTool = class;
// searchpath delimiter is semicolon
TOnGetSearchPath = function(Sender: TObject): string of object;
TOnGetCodeToolForBuffer = function(Sender: TObject;
Code: TCodeBuffer): TFindDeclarationTool of object;
TFindDeclarationFlag = (
fdfSearchInParentNodes, // if identifier not found in current context,
// proceed in prior nodes on same lvl and parents
fdfSearchInAncestors, // if context is a class, search also in
// ancestors/interfaces
fdfIgnoreCurContextNode,// skip context and proceed in prior/parent context
fdfExceptionOnNotFound, // raise exception if identifier not found
fdfIgnoreUsedUnits, // stay in current source
fdfSearchForward, // instead of searching in prior nodes, search in
// next nodes (successors)
fdfIgnoreClassVisibility,//find inaccessible private+protected fields
fdfClassPublished,fdfClassPublic,fdfClassProtected,fdfClassPrivate);
TFindDeclarationFlags = set of TFindDeclarationFlag;
TFindDeclarationInput = record
Flags: TFindDeclarationFlags;
Identifier: PChar;
ContextNode: TCodeTreeNode;
end;
TFindDeclarationParams = class;
TFindContext = record
Node: TCodeTreeNode;
Tool: TFindDeclarationTool;
end;
TFindDeclarationParams = class(TObject)
public
Flags: TFindDeclarationFlags;
Identifier: PChar;
ContextNode: TCodeTreeNode;
NewNode: TCodeTreeNode;
NewCleanPos: integer;
NewCodeTool: TFindDeclarationTool;
NewPos: TCodeXYPosition;
NewTopLine: integer;
constructor Create;
procedure Clear;
procedure Save(var Input: TFindDeclarationInput);
procedure Load(var Input: TFindDeclarationInput);
procedure SetResult(AFindContext: TFindContext);
procedure SetResult(ANewCodeTool: TFindDeclarationTool;
ANewNode: TCodeTreeNode);
procedure SetResult(ANewCodeTool: TFindDeclarationTool;
ANewNode: TCodeTreeNode; ANewCleanPos: integer);
procedure ConvertResultCleanPosToCaretPos;
procedure ClearResult;
procedure ClearInput;
end;
TFindDeclarationTool = class(TPascalParserTool)
private
FOnGetUnitSourceSearchPath: TOnGetSearchPath;
FOnGetCodeToolForBuffer: TOnGetCodeToolForBuffer;
{$IFDEF CTDEBUG}
DebugPrefix: string;
procedure IncPrefix;
procedure DecPrefix;
{$ENDIF}
function FindDeclarationInUsesSection(UsesNode: TCodeTreeNode;
CleanPos: integer;
var NewPos: TCodeXYPosition; var NewTopLine: integer): boolean;
function IsIncludeDirectiveAtPos(CleanPos, CleanCodePosInFront: integer;
var IncludeCode: TCodeBuffer): boolean;
function FindEnumInContext(Params: TFindDeclarationParams): boolean;
// sub methods for FindIdentifierInContext
function FindIdentifierInProcContext(ProcContextNode: TCodeTreeNode;
Params: TFindDeclarationParams): boolean;
function FindIdentifierInClassOfMethod(ProcContextNode: TCodeTreeNode;
Params: TFindDeclarationParams): boolean;
function FindIdentifierInWithVarContext(WithVarNode: TCodeTreeNode;
Params: TFindDeclarationParams): boolean;
function FindIdentifierInAncestors(ClassNode: TCodeTreeNode;
Params: TFindDeclarationParams): boolean;
function FindIdentifierInUsesSection(UsesNode: TCodeTreeNode;
Params: TFindDeclarationParams): boolean;
function FindIdentifierInHiddenUsedUnits(
Params: TFindDeclarationParams): boolean;
function FindIdentifierInUsedUnit(const AnUnitName: string;
Params: TFindDeclarationParams): boolean;
protected
function FindDeclarationOfIdentifier(
Params: TFindDeclarationParams): boolean;
function FindContextNodeAtCursor(
Params: TFindDeclarationParams): TFindContext;
function FindIdentifierInContext(Params: TFindDeclarationParams): boolean;
function FindBaseTypeOfNode(Params: TFindDeclarationParams;
Node: TCodeTreeNode): TFindContext;
function FindClassOfMethod(ProcNode: TCodeTreeNode;
Params: TFindDeclarationParams; FindClassContext: boolean): boolean;
function FindAncestorOfClass(ClassNode: TCodeTreeNode;
Params: TFindDeclarationParams; FindClassContext: boolean): boolean;
function FindForwardIdentifier(Params: TFindDeclarationParams;
var IsForward: boolean): boolean;
function FindExpressionResultType(Params: TFindDeclarationParams;
StartPos, EndPos: integer): TFindContext;
function FindCodeToolForUsedUnit(UnitNameAtom,
UnitInFileAtom: TAtomPosition;
ExceptionOnNotFound: boolean): TFindDeclarationTool;
function FindIdentifierInInterface(AskingTool: TFindDeclarationTool;
Params: TFindDeclarationParams): boolean;
function CompareNodeIdentifier(Node: TCodeTreeNode;
Params: TFindDeclarationParams): boolean;
function GetInterfaceNode: TCodeTreeNode;
public
function FindDeclaration(CursorPos: TCodeXYPosition;
var NewPos: TCodeXYPosition; var NewTopLine: integer): boolean;
function FindUnitSource(const AnUnitName,
AnUnitInFilename: string): TCodeBuffer;
property OnGetUnitSourceSearchPath: TOnGetSearchPath
read FOnGetUnitSourceSearchPath write FOnGetUnitSourceSearchPath;
property OnGetCodeToolForBuffer: TOnGetCodeToolForBuffer
read FOnGetCodeToolForBuffer write FOnGetCodeToolForBuffer;
end;
implementation
const
fdfAllClassVisibilities = [fdfClassPublished,fdfClassPublic,fdfClassProtected,
fdfClassPrivate];
fdfGlobals = [fdfExceptionOnNotFound, fdfIgnoreUsedUnits];
{ TFindContext }
function CreateFindContext(NewTool: TFindDeclarationTool;
NewNode: TCodeTreeNode): TFindContext;
begin
Result.Node:=NewNode;
Result.Tool:=NewTool;
end;
function CreateFindContext(Params: TFindDeclarationParams): TFindContext;
begin
Result.Node:=Params.NewNode;
Result.Tool:=TFindDeclarationTool(Params.NewCodeTool);
end;
{ TFindDeclarationTool }
function TFindDeclarationTool.FindDeclaration(CursorPos: TCodeXYPosition;
var NewPos: TCodeXYPosition; var NewTopLine: integer): boolean;
var CleanCursorPos: integer;
CursorNode, ClassNode: TCodeTreeNode;
Params: TFindDeclarationParams;
begin
Result:=false;
// build code tree
{$IFDEF CTDEBUG}
writeln(DebugPrefix,'TFindDeclarationTool.FindDeclaration A CursorPos=',CursorPos.X,',',CursorPos.Y);
{$ENDIF}
BuildTreeAndGetCleanPos(false,CursorPos,CleanCursorPos);
{$IFDEF CTDEBUG}
writeln(DebugPrefix,'TFindDeclarationTool.FindDeclaration C CleanCursorPos=',CleanCursorPos);
{$ENDIF}
// find CodeTreeNode at cursor
CursorNode:=FindDeepestNodeAtPos(CleanCursorPos,true);
if IsIncludeDirectiveAtPos(CleanCursorPos,CursorNode.StartPos,NewPos.Code)
then begin
NewPos.X:=1;
NewPos.Y:=1;
NewTopLine:=1;
Result:=true;
exit;
end;
{$IFDEF CTDEBUG}
writeln('TFindDeclarationTool.FindDeclaration D CursorNode=',NodeDescriptionAsString(CursorNode.Desc));
{$ENDIF}
if CursorNode.Desc=ctnUsesSection then begin
// find used unit
Result:=FindDeclarationInUsesSection(CursorNode,CleanCursorPos,
NewPos,NewTopLine);
end else begin
// first test if in a class
ClassNode:=CursorNode;
while (ClassNode<>nil) and (ClassNode.Desc<>ctnClass) do
ClassNode:=ClassNode.Parent;
if ClassNode<>nil then begin
// cursor is in class/object definition
if (ClassNode.SubDesc and ctnsForwardDeclaration)=0 then begin
// parse class and build CodeTreeNodes for all properties/methods
BuildSubTreeForClass(ClassNode);
CursorNode:=FindDeepestNodeAtPos(CleanCursorPos,true);
end;
end;
if CursorNode.Desc=ctnBeginBlock then begin
BuildSubTreeForBeginBlock(CursorNode);
CursorNode:=FindDeepestNodeAtPos(CleanCursorPos,true);
end;
MoveCursorToCleanPos(CleanCursorPos);
while (CurPos.StartPos>1) and (IsIdentChar[Src[CurPos.StartPos-1]]) do
dec(CurPos.StartPos);
if (CurPos.StartPos>=1) and (IsIdentStartChar[Src[CurPos.StartPos]]) then
begin
CurPos.EndPos:=CurPos.StartPos;
while (CurPos.EndPos<=SrcLen) and IsIdentChar[Src[CurPos.EndPos]] do
inc(CurPos.EndPos);
// find declaration of identifier
Params:=TFindDeclarationParams.Create;
try
Params.ContextNode:=CursorNode;
Params.Identifier:=@Src[CurPos.StartPos];
Params.Flags:=[fdfSearchInAncestors,fdfSearchInParentNodes,
fdfExceptionOnNotFound];
Result:=FindDeclarationOfIdentifier(Params);
if Result then begin
Params.ConvertResultCleanPosToCaretPos;
NewPos:=Params.NewPos;
NewTopLine:=Params.NewTopLine;
end;
finally
Params.Free;
end;
end else begin
// find declaration of not identifier
end;
end;
end;
function TFindDeclarationTool.FindDeclarationInUsesSection(
UsesNode: TCodeTreeNode; CleanPos: integer;
var NewPos: TCodeXYPosition; var NewTopLine: integer): boolean;
var UnitName, UnitInFilename: string;
UnitNamePos, UnitInFilePos: TAtomPosition;
begin
Result:=false;
{$IFDEF CTDEBUG}
writeln('TFindDeclarationTool.FindDeclarationInUsesSection A');
{$ENDIF}
// reparse uses section
MoveCursorToNodeStart(UsesNode);
ReadNextAtom;
if not UpAtomIs('USES') then
RaiseException('expected uses, but '+GetAtom+' found');
repeat
ReadNextAtom; // read name
if CurPos.StartPos>CleanPos then break;
if AtomIsChar(';') then break;
AtomIsIdentifier(true);
UnitNamePos:=CurPos;
ReadNextAtom;
if UpAtomIs('IN') then begin
ReadNextAtom;
if not AtomIsStringConstant then
RaiseException(
'string constant expected, but '+GetAtom+' found');
UnitInFilePos:=CurPos;
ReadNextAtom;
end else
UnitInFilePos.StartPos:=-1;
if CleanPos<UnitNamePos.EndPos then begin
// cursor is on a unitname -> try to locate it
UnitName:=copy(Src,UnitNamePos.StartPos,
UnitNamePos.EndPos-UnitNamePos.StartPos);
if UnitInFilePos.StartPos>=1 then
UnitInFilename:=copy(Src,UnitInFilePos.StartPos,
UnitInFilePos.EndPos-UnitInFilePos.StartPos)
else
UnitInFilename:='';
NewPos.Code:=FindUnitSource(UnitName,UnitInFilename);
if NewPos.Code=nil then
RaiseException('unit not found: '+UnitName);
NewPos.X:=1;
NewPos.Y:=1;
NewTopLine:=1;
Result:=true;
exit;
end;
if AtomIsChar(';') then break;
if not AtomIsChar(',') then
RaiseException('; expected, but '+GetAtom+' found')
until (CurPos.StartPos>SrcLen);
{$IFDEF CTDEBUG}
writeln('TFindDeclarationTool.FindDeclarationInUsesSection END cursor not on unitname');
{$ENDIF}
end;
function TFindDeclarationTool.FindUnitSource(const AnUnitName,
AnUnitInFilename: string): TCodeBuffer;
function LoadFile(const ExpandedFilename: string;
var NewCode: TCodeBuffer): boolean;
begin
{$IFDEF ShowTriedFiles}
writeln('TFindDeclarationTool.FindUnitSource.LoadFile ',ExpandedFilename);
{$ENDIF}
NewCode:=TCodeBuffer(Scanner.OnLoadSource(Self,ExpandedFilename));
Result:=NewCode<>nil;
end;
function SearchUnitFileInDir(const ADir, AnUnitName: string): TCodeBuffer;
var APath: string;
begin
APath:=ADir;
if (APath<>'') and (APath[length(APath)]<>PathDelim) then
APath:=APath+PathDelim;
{$IFNDEF win32}
if LoadFile(ADir+lowercase(AnUnitName)+'.pp',Result) then exit;
if LoadFile(ADir+lowercase(AnUnitName)+'.pas',Result) then exit;
{$ENDIF}
if LoadFile(ADir+AnUnitName+'.pp',Result) then exit;
if LoadFile(ADir+AnUnitName+'.pas',Result) then exit;
Result:=nil;
end;
function SearchUnitFileInPath(const APath, TheUnitName: string): TCodeBuffer;
var PathStart, PathEnd: integer;
ADir: string;
begin
PathStart:=1;
while PathStart<=length(APath) do begin
PathEnd:=PathStart;
while (PathEnd<=length(APath)) and (APath[PathEnd]<>';') do inc(PathEnd);
if PathEnd>PathStart then begin
ADir:=copy(APath,PathStart,PathEnd-PathStart);
if (ADir<>'') and (ADir[length(ADir)]<>PathDelim) then
ADir:=ADir+PathDelim;
if not FilenameIsAbsolute(ADir) then
ADir:=ExtractFilePath(TCodeBuffer(Scanner.MainCode).Filename)+ADir;
Result:=SearchUnitFileInDir(ADir,TheUnitName);
if Result<>nil then exit;
end;
PathStart:=PathEnd+1;
end;
Result:=nil;
end;
function SearchFileInPath(const APath, RelativeFilename: string): TCodeBuffer;
var PathStart, PathEnd: integer;
ADir: string;
begin
PathStart:=1;
while PathStart<=length(APath) do begin
PathEnd:=PathStart;
while (PathEnd<=length(APath)) and (APath[PathEnd]<>';') do inc(PathEnd);
if PathEnd>PathStart then begin
ADir:=copy(APath,PathStart,PathEnd-PathStart);
if (ADir<>'') and (ADir[length(ADir)]<>PathDelim) then
ADir:=ADir+PathDelim;
if not FilenameIsAbsolute(ADir) then
ADir:=ExtractFilePath(TCodeBuffer(Scanner.MainCode).Filename)+ADir;
if LoadFile(ADir+RelativeFilename,Result) then exit;
end;
PathStart:=PathEnd+1;
end;
Result:=nil;
end;
function SearchUnitInUnitLinks(const TheUnitName: string): TCodeBuffer;
var UnitLinks, CurFilename: string;
UnitLinkStart, UnitLinkEnd: integer;
begin
Result:=nil;
UnitLinks:=Scanner.Values[ExternalMacroStart+'UnitLinks'];
{$IFDEF ShowTriedFiles}
//writeln('TFindDeclarationTool.FindUnitSource.SearchUnitInUnitLinks');
{$ENDIF}
UnitLinkStart:=1;
while UnitLinkStart<=length(UnitLinks) do begin
while (UnitLinkStart<=length(UnitLinks))
and (UnitLinks[UnitLinkStart] in [#10,#13]) do
inc(UnitLinkStart);
UnitLinkEnd:=UnitLinkStart;
while (UnitLinkEnd<=length(UnitLinks)) and (UnitLinks[UnitLinkEnd]<>' ')
do
inc(UnitLinkEnd);
if UnitLinkEnd>UnitLinkStart then begin
{$IFDEF ShowTriedFiles}
//writeln(' unit "',copy(UnitLinks,UnitLinkStart,UnitLinkEnd-UnitLinkStart),'"');
{$ENDIF}
if AnsiCompareText(TheUnitName,
copy(UnitLinks,UnitLinkStart,UnitLinkEnd-UnitLinkStart))=0
then begin
// unit found -> parse filename
UnitLinkStart:=UnitLinkEnd+1;
UnitLinkEnd:=UnitLinkStart;
while (UnitLinkEnd<=length(UnitLinks))
and (not (UnitLinks[UnitLinkEnd] in [#10,#13])) do
inc(UnitLinkEnd);
if UnitLinkEnd>UnitLinkStart then begin
CurFilename:=copy(UnitLinks,UnitLinkStart,UnitLinkEnd-UnitLinkStart);
LoadFile(CurFilename,Result);
exit;
end;
end else begin
UnitLinkStart:=UnitLinkEnd+1;
while (UnitLinkStart<=length(UnitLinks))
and (not (UnitLinks[UnitLinkStart] in [#10,#13])) do
inc(UnitLinkStart);
end;
end else
break;
end;
end;
var CurDir, UnitSrcSearchPath: string;
MainCodeIsVirtual: boolean;
begin
{$IFDEF CTDEBUG}
writeln('TFindDeclarationTool.FindUnitSource A AnUnitName=',AnUnitName,' AnUnitInFilename=',AnUnitInFilename);
{$ENDIF}
Result:=nil;
if (AnUnitName='') or (Scanner=nil) or (Scanner.MainCode=nil)
or (not (TObject(Scanner.MainCode) is TCodeBuffer))
or (Scanner.OnLoadSource=nil) then
exit;
if Assigned(OnGetUnitSourceSearchPath) then
UnitSrcSearchPath:=OnGetUnitSourceSearchPath(Self)
else
UnitSrcSearchPath:=Scanner.Values[ExternalMacroStart+'SrcPath'];
{$IFDEF CTDEBUG}
writeln('TFindDeclarationTool.FindUnitSource UnitSrcSearchPath=',UnitSrcSearchPath);
{$ENDIF}
//writeln('>>>>>',Scanner.Values.AsString,'<<<<<');
if AnUnitInFilename<>'' then begin
// unitname in 'filename'
if FilenameIsAbsolute(AnUnitInFilename) then begin
Result:=TCodeBuffer(Scanner.OnLoadSource(Self,AnUnitInFilename));
end else begin
// search AnUnitInFilename in searchpath
Result:=SearchFileInPath(UnitSrcSearchPath,AnUnitInFilename);
end;
end else begin
// normal unit name -> search as the compiler would search
// first search in current directory (= where the maincode is)
MainCodeIsVirtual:=TCodeBuffer(Scanner.MainCode).IsVirtual;
if not MainCodeIsVirtual then begin
CurDir:=ExtractFilePath(TCodeBuffer(Scanner.MainCode).Filename);
end else begin
CurDir:='';
end;
{$IFDEF CTDEBUG}
writeln('TFindDeclarationTool.FindUnitSource Search in current dir=',CurDir);
{$ENDIF}
Result:=SearchUnitFileInDir(CurDir,AnUnitName);
if Result=nil then begin
// search in search path
{$IFDEF CTDEBUG}
writeln('TFindDeclarationTool.FindUnitSource Search in search path=',UnitSrcSearchPath);
{$ENDIF}
Result:=SearchUnitFileInPath(UnitSrcSearchPath,AnUnitName);
if Result=nil then begin
// search in FPC source directory
Result:=SearchUnitInUnitLinks(AnUnitName);
end;
end;
end;
end;
function TFindDeclarationTool.IsIncludeDirectiveAtPos(CleanPos,
CleanCodePosInFront: integer; var IncludeCode: TCodeBuffer): boolean;
var LinkIndex, CommentStart, CommentEnd: integer;
SrcLink: TSourceLink;
begin
Result:=false;
if (Scanner=nil) then exit;
LinkIndex:=Scanner.LinkIndexAtCleanPos(CleanPos);
if (LinkIndex<0) or (LinkIndex>=Scanner.LinkCount-1) then exit;
SrcLink:=Scanner.Links[LinkIndex+1];
if (SrcLink.Code=nil) or (SrcLink.Code=Scanner.Links[LinkIndex].Code) then
exit;
if CleanPosIsInComment(CleanPos,CleanCodePosInFront,CommentStart,CommentEnd)
and (CommentEnd=SrcLink.CleanedPos) then begin
IncludeCode:=TCodeBuffer(SrcLink.Code);
Result:=true;
exit;
end;
end;
function TFindDeclarationTool.FindDeclarationOfIdentifier(
Params: TFindDeclarationParams): boolean;
{ searches an identifier in clean code, parses code in front and after the
identifier
Params:
Identifier in clean source
ContextNode // = DeepestNode at Cursor
Result:
true, if NewPos+NewTopLine valid
For example:
A^.B().C[].Identifier
}
var OldContextNode: TCodeTreeNode;
NewContext: TFindContext;
begin
{$IFDEF CTDEBUG}
writeln('[TFindDeclarationTool.FindDeclarationOfIdentifier] Identifier=',
GetIdentifier(Params.Identifier),
' ContextNode=',NodeDescriptionAsString(Params.ContextNode.Desc));
{$ENDIF}
Result:=false;
MoveCursorToCleanPos(Params.Identifier);
OldContextNode:=Params.ContextNode;
NewContext:=FindContextNodeAtCursor(Params);
Params.Flags:=[fdfSearchInAncestors]
+fdfAllClassVisibilities+(fdfGlobals*Params.Flags);
if NewContext.Node=OldContextNode then begin
Params.Flags:=Params.Flags+[fdfSearchInParentNodes,fdfIgnoreCurContextNode];
end;
if NewContext.Tool<>Self then begin
// search in used unit
Exclude(Params.Flags,fdfClassPrivate);
if NewContext.Node.Desc=ctnClass then begin
// ToDo: if context node is not the class of the method the
// search started, remove fdfClassProtected from Flags
end;
end;
if (OldContextNode.Desc=ctnTypeDefinition)
and (OldContextNode.FirstChild<>nil)
and (OldContextNode.FirstChild.Desc=ctnClass)
and ((OldContextNode.FirstChild.SubDesc and ctnsForwardDeclaration)>0)
then
Include(Params.Flags,fdfSearchForward);
Params.ContextNode:=NewContext.Node;
Result:=NewContext.Tool.FindIdentifierInContext(Params);
end;
function TFindDeclarationTool.FindIdentifierInContext(
Params: TFindDeclarationParams): boolean;
{ searches an identifier in context node
It does not care about code in front of the identifier like 'a.Identifer'.
Params:
Identifier
ContextNode // = DeepestNode at Cursor
Result:
true, if NewPos+NewTopLine valid
}
var LastContextNode, StartContextNode, ContextNode: TCodeTreeNode;
IsForward: boolean;
begin
ContextNode:=Params.ContextNode;
StartContextNode:=ContextNode;
Result:=false;
if (fdfSearchForward in Params.Flags) then begin
// ToDo: check for circles
end;
if ContextNode<>nil then begin
repeat
{$IFDEF ShowTriedContexts}
writeln('[TFindDeclarationTool.FindIdentifierInContext] A Ident=',
GetIdentifier(Params.Identifier),
' Context=',ContextNode.DescAsString,' "',copy(Src,ContextNode.StartPos,8),'"',
' P=',fdfSearchInParentNodes in Params.Flags,
' A=',fdfSearchInAncestors in Params.Flags,
' IUU=',fdfIgnoreUsedUnits in Params.Flags
);
if (ContextNode.Desc=ctnClass) then
writeln(' ContextNode.LastChild=',ContextNode.LastChild<>nil);
{$ENDIF}
LastContextNode:=ContextNode;
if not (fdfIgnoreCurContextNode in Params.Flags) then begin
case ContextNode.Desc of
ctnTypeSection, ctnVarSection, ctnConstSection, ctnResStrSection,
ctnInterface, ctnImplementation,
ctnClassPublic, ctnClassPrivate, ctnClassProtected, ctnClassPublished,
ctnClass,
ctnRecordType, ctnRecordCase, ctnRecordVariant,
ctnParameterList:
begin
if ContextNode.Desc=ctnClass then begin
// just-in-time parsing for class node
BuildSubTreeForClass(ContextNode);
end;
if (ContextNode.LastChild<>nil) then begin
if not (fdfSearchForward in Params.Flags) then
ContextNode:=ContextNode.LastChild
else
ContextNode:=ContextNode.FirstChild;
end;
end;
ctnTypeDefinition, ctnVarDefinition, ctnConstDefinition, ctnEnumType:
begin
if CompareSrcIdentifiers(ContextNode.StartPos,Params.Identifier)
then begin
{$IFDEF ShowTriedContexts}
writeln(' Definition Identifier found=',GetIdentifier(Params.Identifier));
{$ENDIF}
// identifier found
Result:=true;
Params.SetResult(Self,ContextNode);
exit;
end;
// search for enums
Params.ContextNode:=ContextNode;
Result:=FindEnumInContext(Params);
if Result then exit;
end;
ctnProcedure:
begin
Result:=FindIdentifierInProcContext(ContextNode,Params);
if Result then exit;
end;
ctnProcedureHead:
begin
BuildSubTreeForProcHead(ContextNode);
if ContextNode.FirstChild<>nil then
ContextNode:=ContextNode.FirstChild;
end;
ctnProgram, ctnPackage, ctnLibrary, ctnUnit:
begin
MoveCursorToNodeStart(ContextNode);
ReadNextAtom; // read keyword
ReadNextAtom; // read name
if CompareSrcIdentifiers(CurPos.StartPos,Params.Identifier) then
begin
// identifier found
{$IFDEF ShowTriedContexts}
writeln(' Source Name Identifier found=',GetIdentifier(Params.Identifier));
{$ENDIF}
Result:=true;
Params.SetResult(Self,ContextNode,CurPos.StartPos);
exit;
end;
Result:=FindIdentifierInHiddenUsedUnits(Params);
if Result then exit;
end;
ctnProperty:
begin
if (Params.Identifier[0]<>'[') then begin
MoveCursorToNodeStart(ContextNode);
ReadNextAtom; // read keyword 'property'
ReadNextAtom; // read name
if CompareSrcIdentifiers(CurPos.StartPos,Params.Identifier) then
begin
// identifier found
// ToDo: identifiers after 'read', 'write' are procs with
// special parameter lists
{$IFDEF ShowTriedContexts}
writeln(' Property Identifier found=',GetIdentifier(Params.Identifier));
{$ENDIF}
Result:=true;
Params.SetResult(Self,ContextNode,CurPos.StartPos);
exit;
end;
end else begin
// the default property is searched
Result:=PropertyIsDefault(ContextNode);
if Result then exit;
end;
end;
ctnUsesSection:
begin
Result:=FindIdentifierInUsesSection(ContextNode,Params);
if Result then exit;
end;
ctnWithVariable:
begin
Result:=FindIdentifierInWithVarContext(ContextNode,Params);
if Result then exit;
end;
ctnPointerType:
begin
// pointer types can be forward definitions
Params.ContextNode:=ContextNode.Parent;
Result:=FindForwardIdentifier(Params,IsForward);
exit;
end;
end;
end else begin
Exclude(Params.Flags,fdfIgnoreCurContextNode);
{$IFDEF ShowTriedContexts}
writeln('[TFindDeclarationTool.FindIdentifierInContext] IgnoreCurContext');
{$ENDIF}
end;
if LastContextNode=ContextNode then begin
// same context -> search in prior context
if (not ContextNode.HasAsParent(StartContextNode)) then begin
// searching in a prior node, will leave the start context
if (not (fdfSearchInParentNodes in Params.Flags)) then begin
// searching in any parent context is not permitted
if not ((fdfSearchInAncestors in Params.Flags)
and (ContextNode.Desc=ctnClass)) then begin
// even searching in ancestors contexts is not permitted
// -> there is no prior context accessible any more
// -> identifier not found
{$IFDEF ShowTriedContexts}
writeln('[TFindDeclarationTool.FindIdentifierInContext] no prior node accessible ContextNode=',ContextNode.DescAsString);
{$ENDIF}
exit;
end;
end;
end;
repeat
// search for prior node
{$IFDEF ShowTriedContexts}
//writeln('[TFindDeclarationTool.FindIdentifierInContext] Searching prior node of ',ContextNode.DescAsString);
{$ENDIF}
if (ContextNode.Desc=ctnClass)
and (fdfSearchInAncestors in Params.Flags) then
begin
Result:=FindIdentifierInAncestors(ContextNode,Params);
if Result then exit;
end;
if ((not (fdfSearchForward in Params.Flags))
and (ContextNode.PriorBrother<>nil))
or ((fdfSearchForward in Params.Flags)
and (ContextNode.NextBrother<>nil)
and (ContextNode.NextBrother.Desc<>ctnImplementation)) then
begin
if not (fdfSearchForward in Params.Flags) then
ContextNode:=ContextNode.PriorBrother
else
ContextNode:=ContextNode.NextBrother;
{$IFDEF ShowTriedContexts}
writeln('[TFindDeclarationTool.FindIdentifierInContext] Searching in PriorBrother ContextNode=',ContextNode.DescAsString);
{$ENDIF}
// it is not always allowed to search in every node on the same lvl:
// -> test if class visibility valid
case ContextNode.Desc of
ctnClassPublished: if (fdfClassPublished in Params.Flags) then break;
ctnClassPublic: if (fdfClassPublic in Params.Flags) then break;
ctnClassProtected: if (fdfClassProtected in Params.Flags) then break;
ctnClassPrivate: if (fdfClassPrivate in Params.Flags) then break;
else
break;
end;
end else if ContextNode.Parent<>nil then begin
ContextNode:=ContextNode.Parent;
{$IFDEF ShowTriedContexts}
writeln('[TFindDeclarationTool.FindIdentifierInContext] Searching in Parent ContextNode=',ContextNode.DescAsString);
{$ENDIF}
case ContextNode.Desc of
ctnTypeSection, ctnVarSection, ctnConstSection, ctnResStrSection,
ctnInterface, ctnImplementation,
ctnClassPublished,ctnClassPublic,ctnClassProtected, ctnClassPrivate,
ctnRecordCase, ctnRecordVariant,
ctnProcedureHead, ctnParameterList:
// these codetreenodes build a parent-child-relationship, but
// for pascal it is only a range, hence after searching in the
// childs of the last node, it must be searched next in the childs
// of the prior node
;
ctnClass, ctnRecordType:
// do not search again in this node, go on ...
;
ctnProcedure:
begin
Result:=FindIdentifierInClassOfMethod(ContextNode,Params);
if Result then exit;
end;
else
break;
end;
end else begin
ContextNode:=nil;
break;
end;
until false;
end;
until ContextNode=nil;
end else begin
// DeepestNode=nil -> ignore
end;
if fdfExceptionOnNotFound in Params.Flags then begin
if IsPCharInSrc(Params.Identifier) then
MoveCursorToCleanPos(Params.Identifier);
RaiseException('Identifier not found '+GetIdentifier(Params.Identifier));
end;
end;
function TFindDeclarationTool.FindEnumInContext(
Params: TFindDeclarationParams): boolean;
{ search all subnodes for ctnEnumType
Params:
Identifier
ContextNode // = DeepestNode at Cursor
Result:
true, if NewPos+NewTopLine valid
}
var OldContextNode: TCodeTreeNode;
begin
Result:=false;
if Params.ContextNode=nil then exit;
OldContextNode:=Params.ContextNode;
try
if Params.ContextNode.Desc=ctnClass then
BuildSubTreeForClass(Params.ContextNode);
Params.ContextNode:=Params.ContextNode.FirstChild;
while Params.ContextNode<>nil do begin
if (Params.ContextNode.Desc in [ctnEnumType])
and CompareSrcIdentifiers(Params.ContextNode.StartPos,Params.Identifier)
then begin
// identifier found
Result:=true;
Params.SetResult(Self,Params.ContextNode);
exit;
end;
Result:=FindEnumInContext(Params);
if Result then exit;
Params.ContextNode:=Params.ContextNode.NextBrother;
end;
finally
Params.ContextNode:=OldContextNode;
end;
end;
function TFindDeclarationTool.FindContextNodeAtCursor(
Params: TFindDeclarationParams): TFindContext;
{ searches for the context node for a specific cursor pos
Params.Context should contain the deepest node at cursor
if there is no special context, then result is equal to Params.Context
Examples:
1. A.B - CleanPos points to B: if A is a class, the context node will be
the class node (ctnRecordType).
2. A().B - same as above
3. inherited A - CleanPos points to A: if in a method, the context node will
be the class node (ctnClass) of the current method.
4. A[]. - CleanPos points to '.': if A is an array, the context node will
be the array type node (ctnArrayType).
5. A[].B - CleanPos points to B: if A is an array of record, the context
node will be the record type node (ctnRecordType).
6. A^. - CleanPos points to '.': if A is a pointer of record, the context
node will be the record type node (ctnRecordType).
7. (A). - CleanPos points to '.': if A is a class, the context node will be
the class node (ctnClass).
8. (A as B) - CleanPos points to ')': if B is a classtype, the context node
will be the class node (ctnClass)
}
type
TAtomType = (atNone, atSpace, atIdentifier, atPoint, atAS, atINHERITED, atUp,
atRoundBracketOpen, atRoundBracketClose,
atEdgedBracketOpen, atEdgedBracketClose,
atRead, atWrite);
const
AtomTypeNames: array[TAtomType] of string =
('<None>','Space','Ident','Point','AS','INHERITED','Up^',
'Bracket(','Bracket)','Bracket[','Bracket]','READ','WRITE');
function GetCurrentAtomType: TAtomType;
begin
if (CurPos.StartPos=CurPos.EndPos) then
Result:=atSpace
else if UpAtomIs('READ') then
Result:=atRead
else if UpAtomIs('WRITE') then
Result:=atWrite
else if AtomIsIdentifier(false) then
Result:=atIdentifier
else if (CurPos.StartPos>=1) and (CurPos.StartPos<=SrcLen)
and (CurPos.StartPos=CurPos.EndPos-1) then begin
case Src[CurPos.StartPos] of
'.': Result:=atPoint;
'^': Result:=atUp;
'(': Result:=atRoundBracketOpen;
')': Result:=atRoundBracketClose;
'[': Result:=atEdgedBracketOpen;
']': Result:=atEdgedBracketClose;
else Result:=atNone;
end;
end
else if UpAtomIs('INHERITED') then
Result:=atINHERITED
else if UpAtomIs('AS') then
Result:=atAS
else
Result:=atNone;
end;
var CurAtom, NextAtom: TAtomPosition;
OldInput: TFindDeclarationInput;
NextAtomType, CurAtomType: TAtomType;
ProcNode: TCodeTreeNode;
begin
// start parsing the expression from right to left
NextAtom:=CurPos;
NextAtomType:=GetCurrentAtomType;
ReadPriorAtom;
CurAtom:=CurPos;
CurAtomType:=GetCurrentAtomType;
write('[TFindDeclarationTool.FindContextNodeAtCursor] A ',
' Context=',Params.ContextNode.DescAsString,
' CurAtom=',AtomTypeNames[CurAtomType],
' "',copy(Src,CurAtom.StartPos,CurAtom.EndPos-CurAtom.StartPos),'"',
' NextAtom=',AtomTypeNames[NextAtomType]
);
writeln('');
if not (CurAtomType in [atIdentifier,atPoint,atUp,atAs,atEdgedBracketClose,
atRoundBracketClose,atRead,atWrite,atINHERITED])
then begin
// no special context found -> the context node is the deepest node at
// cursor, and this should already be in Params.ContextNode
if (not (NextAtomType in [atSpace,atIdentifier,atRoundBracketOpen,
atEdgedBracketOpen])) then
begin
MoveCursorToCleanPos(NextAtom.StartPos);
ReadNextAtom;
RaiseException('identifier expected, but '
+GetAtom+' found');
end;
Result:=CreateFindContext(Self,Params.ContextNode);
exit;
end;
if (CurAtomType in [atRoundBracketClose,atEdgedBracketClose]) then begin
ReadBackTilBracketClose(true);
CurAtom.StartPos:=CurPos.StartPos;
end;
if (not (CurAtomType in [atAS,atRead,atWrite,atINHERITED]))
and ((CurAtomType<>atIdentifier) or (NextAtomType<>atIdentifier)) then
Result:=FindContextNodeAtCursor(Params)
else
Result:=CreateFindContext(Self,Params.ContextNode);
if Result.Node=nil then exit;
// the left side has been parsed and
// now the parsing goes from left to right
{$IFDEF CTDEBUG}
write('[TFindDeclarationTool.FindContextNodeAtCursor] B ',
' Context=',Params.ContextNode.DescAsString,
' CurAtom=',AtomTypeNames[CurAtomType],
' "',copy(Src,CurAtom.StartPos,CurAtom.EndPos-CurAtom.StartPos),'"',
' NextAtom=',AtomTypeNames[NextAtomType],
' Result=');
if Result.Node<>nil then write(Result.Node.DescAsString) else write('NIL');
writeln('');
{$ENDIF}
case CurAtomType of
atIdentifier:
begin
// for example 'AnObject[3]'
if not (NextAtomType in [atSpace,atPoint,atUp,atAS,atRoundBracketOpen,
atRoundBracketClose,atEdgedBracketOpen,atEdgedBracketClose]) then
begin
MoveCursorToCleanPos(NextAtom.StartPos);
ReadNextAtom;
RaiseException('illegal qualifier "'+GetAtom+'" found');
end;
if (Result.Node=Params.ContextNode) then begin
if CompareSrcIdentifier(CurAtom.StartPos,'SELF') then begin
// SELF in a method is the object itself
// -> check if in a proc
ProcNode:=Params.ContextNode;
while (ProcNode<>nil) do begin
if (ProcNode.Desc=ctnProcedure) then begin
// in a proc -> find the class context
if Result.Tool.FindClassOfMethod(ProcNode,Params,true) then begin
Result:=CreateFindContext(Params);
exit;
end;
end;
ProcNode:=ProcNode.Parent;
end;
end else if CompareSrcIdentifier(CurAtom.StartPos,'RESULT') then begin
// RESULT has a special meaning in a function
// -> check if in a function
ProcNode:=Params.ContextNode;
while (ProcNode<>nil) do begin
if (ProcNode.Desc=ctnProcedure) then begin
Result:=Result.Tool.FindBaseTypeOfNode(Params,ProcNode);
exit;
end;
ProcNode:=ProcNode.Parent;
end;
end;
end;
// find identifier
Params.Save(OldInput);
try
Params.Flags:=[fdfSearchInAncestors,fdfExceptionOnNotFound]
+fdfAllClassVisibilities
+(fdfGlobals*Params.Flags);
//writeln(' AAA ',Result.Node=Params.ContextNode,' ',Result.Node.DescAsString,',',Params.ContextNode.DescAsString);
if Result.Node=Params.ContextNode then begin
// there is no special context -> also search in parent contexts
Params.Flags:=Params.Flags
+[fdfSearchInParentNodes,fdfIgnoreCurContextNode];
end else
// special context
Params.ContextNode:=Result.Node;
Params.Identifier:=@Src[CurAtom.StartPos];
Result.Tool.FindIdentifierInContext(Params);
Result:=CreateFindContext(Params);
finally
Params.Load(OldInput);
end;
Result:=Result.Tool.FindBaseTypeOfNode(Params,Result.Node);
end;
atPoint:
begin
// for example 'A.B'
if Result.Node=Params.ContextNode then begin
MoveCursorToCleanPos(CurAtom.StartPos);
RaiseException('identifier expected, but . found');
end;
if (not (NextAtomType in [atSpace,atIdentifier])) then begin
MoveCursorToCleanPos(NextAtom.StartPos);
ReadNextAtom;
RaiseException('identifier expected, but '+GetAtom+' found');
end;
if (Result.Node.Desc in AllUsableSoureTypes) then begin
// identifier in front of the point is a unit name
if Result.Tool<>Self then begin
Result.Node:=Result.Tool.GetInterfaceNode;
end else begin
Result:=CreateFindContext(Self,Params.ContextNode);
end;
end;
// there is no special left to do, since Result already points to
// the type context node.
end;
atAS:
begin
// for example 'A as B'
if (not (NextAtomType in [atSpace,atIdentifier])) then begin
MoveCursorToCleanPos(NextAtom.StartPos);
ReadNextAtom;
RaiseException('identifier expected, but '+GetAtom+' found');
end;
// 'as' is a type cast, so the left side is irrelevant and was already
// ignored in the code at the start of this proc
// -> context is default context
end;
atUP:
begin
// for example:
// 1. 'PInt = ^integer' pointer type
// 2. a^ dereferencing
if not (NextAtomType in [atSpace,atPoint,atUp,atAS,atEdgedBracketClose,
atEdgedBracketOpen,atRoundBracketClose]) then
begin
MoveCursorToCleanPos(NextAtom.StartPos);
ReadNextAtom;
RaiseException('illegal qualifier "'+GetAtom+'" found');
end;
if Result.Node<>Params.ContextNode then begin
// left side of expression has defined a special context
// => this '^' is a dereference
if (not (NextAtomType in [atSpace,atPoint,atAS,atUP])) then begin
MoveCursorToCleanPos(NextAtom.StartPos);
ReadNextAtom;
RaiseException('. expected, but '+GetAtom+' found');
end;
if Result.Node.Desc<>ctnPointerType then begin
MoveCursorToCleanPos(CurAtom.StartPos);
RaiseException('illegal qualifier ^');
end;
Result:=Result.Tool.FindBaseTypeOfNode(Params,Result.Node.FirstChild);
end else if NodeHasParentOfType(Result.Node,ctnPointerType) then begin
// this is a pointer type definition
// -> the default context is ok
end;
end;
atEdgedBracketClose:
begin
// for example: a[]
// this could be:
// 1. ranged array
// 2. dynamic array
// 3. indexed pointer
// 4. default property
if not (NextAtomType in [atSpace,atPoint,atAs,atUp,atRoundBracketClose,
atRoundBracketOpen,atEdgedBracketClose,atEdgedBracketOpen]) then
begin
MoveCursorToCleanPos(NextAtom.StartPos);
ReadNextAtom;
RaiseException('illegal qualifier');
end;
if Result.Node<>Params.ContextNode then begin
case Result.Node.Desc of
ctnArrayType:
// the array type is the last child node
Result:=Result.Tool.FindBaseTypeOfNode(Params,Result.Node.LastChild);
ctnPointerType:
// the pointer type is the only child node
Result:=Result.Tool.FindBaseTypeOfNode(Params,Result.Node.FirstChild);
ctnClass:
begin
// search default property in class
Params.Save(OldInput);
Params.Flags:=[fdfSearchInAncestors,fdfExceptionOnNotFound]
+fdfGlobals*Params.Flags;
Params.Identifier:='['; // special identifier for default property
Params.ContextNode:=Result.Node;
Result.Tool.FindIdentifierInContext(Params);
Result:=Params.NewCodeTool.FindBaseTypeOfNode(Params,Params.NewNode);
Params.Load(OldInput);
end;
// ToDo string, ansistring, widestring, shortstring
else
MoveCursorToCleanPos(CurAtom.StartPos);
RaiseException('illegal qualifier');
end;
end;
end;
atRoundBracketClose:
begin
{ for example:
(a+b) expression bracket: the type is the result type of the
expression.
a() typecast or function
}
if not (NextAtomType in [atSpace,atPoint,atAs,atUp,atRoundBracketClose,
atRoundBracketOpen,atEdgedBracketClose,atEdgedBracketOpen]) then
begin
MoveCursorToCleanPos(NextAtom.StartPos);
ReadNextAtom;
RaiseException('illegal qualifier');
end;
if Result.Node<>Params.ContextNode then begin
// typecast or function
// ToDo: proc overloading, if parameter types incompatible search next
end else begin
// expression
Result:=FindExpressionResultType(Params,CurAtom.StartPos+1,
CurAtom.EndPos-1);
end;
end;
atINHERITED:
begin
// for example: inherited A;
if not (NextAtomType in [atSpace,atIdentifier]) then begin
MoveCursorToCleanPos(NextAtom.StartPos);
ReadNextAtom;
RaiseException('identifier expected, but '+GetAtom+' found');
end;
// find ancestor of class of method
ProcNode:=Result.Node;
while (ProcNode<>nil) do begin
if not (ProcNode.Desc in [ctnProcedure,ctnProcedureHead,ctnBeginBlock,
ctnAsmBlock,ctnWithVariable,ctnWithStatement,ctnCaseBlock,
ctnCaseVariable,ctnCaseStatement]) then
begin
break;
end;
if ProcNode.Desc=ctnProcedure then begin
Result.Tool.FindClassOfMethod(ProcNode,Params,true);
// find class ancestor
Params.NewCodeTool.FindAncestorOfClass(Params.NewNode,Params,true);
Result:=CreateFindContext(Params);
exit;
end;
ProcNode:=ProcNode.Parent;
end;
MoveCursorToCleanPos(CurAtom.StartPos);
RaiseException('inherited keyword only allowed in methods');
end;
else
// expression start found
begin
if (not (NextAtomType in [atSpace,atIdentifier,atRoundBracketOpen,
atEdgedBracketOpen])) then
begin
MoveCursorToCleanPos(NextAtom.StartPos);
ReadNextAtom;
RaiseException('identifier expected, but '+GetAtom+' found');
end;
end;
end;
{$IFDEF CTDEBUG}
write('[TFindDeclarationTool.FindContextNodeAtCursor] END ',
Params.ContextNode.DescAsString,' CurAtom=',AtomTypeNames[CurAtomType],
' NextAtom=',AtomTypeNames[NextAtomType],' Result=');
if Result.Node<>nil then write(Result.Node.DescAsString) else write('NIL');
writeln('');
{$ENDIF}
end;
function TFindDeclarationTool.FindBaseTypeOfNode(Params: TFindDeclarationParams;
Node: TCodeTreeNode): TFindContext;
var OldInput: TFindDeclarationInput;
ClassIdentNode: TCodeTreeNode;
begin
Result.Node:=Node;
Result.Tool:=Self;
while (Result.Node<>nil) do begin
// ToDo: check for circles
{$IFDEF ShowTriedContexts}
writeln('[TFindDeclarationTool.FindBaseTypeOfNode] A Result=',Result.Node.DescAsString);
{$ENDIF}
if (Result.Node.Desc in AllIdentifierDefinitions) then begin
// instead of variable/const/type definition, return the type
Result.Node:=FindTypeNodeOfDefinition(Result.Node);
end else
if (Result.Node.Desc=ctnClass)
and ((Result.Node.SubDesc and ctnsForwardDeclaration)>0) then
begin
// search the real class
ClassIdentNode:=Result.Node.Parent;
if (ClassIdentNode=nil) or (not (ClassIdentNode.Desc=ctnTypeDefinition))
then begin
MoveCursorToCleanPos(Result.Node.StartPos);
RaiseException('[TFindDeclarationTool.FindBaseTypeOfNode] '
+'forward class node without name');
end;
Params.Save(OldInput);
try
Params.Identifier:=@Src[ClassIdentNode.StartPos];
Params.Flags:=[fdfSearchInParentNodes,fdfSearchForward,
fdfIgnoreUsedUnits,fdfExceptionOnNotFound]
+(fdfGlobals*Params.Flags);
Params.ContextNode:=ClassIdentNode;
FindIdentifierInContext(Params);
if (Params.NewNode.Desc<>ctnTypeDefinition)
or (Params.NewCodeTool<>Self) then begin
MoveCursorToCleanPos(Result.Node.StartPos);
RaiseException('Forward class definition not resolved: '
+copy(Src,ClassIdentNode.StartPos,
ClassIdentNode.EndPos-ClassIdentNode.StartPos));
end;
Result:=Params.NewCodeTool.FindBaseTypeOfNode(Params,Params.NewNode);
exit;
finally
Params.Load(OldInput);
end;
end else
if (Result.Node.Desc=ctnIdentifier) then begin
// this type is just an alias for another type
// -> search the basic type
if Result.Node.Parent=nil then
break;
Params.Save(OldInput);
try
Params.Identifier:=@Src[Result.Node.StartPos];
Params.Flags:=[fdfSearchInParentNodes,fdfExceptionOnNotFound]
+(fdfGlobals*Params.Flags);
Params.ContextNode:=Result.Node.Parent;
if Params.ContextNode.Desc=ctnParameterList then
Params.ContextNode:=Params.ContextNode.Parent;
if Params.ContextNode.Desc=ctnProcedureHead then
Params.ContextNode:=Params.ContextNode.Parent;
FindIdentifierInContext(Params);
Result:=Params.NewCodeTool.FindBaseTypeOfNode(Params,Params.NewNode);
exit;
finally
Params.Load(OldInput);
end;
end else
if (Result.Node.Desc=ctnProperty) then begin
// this is a property -> search the type definition of the property
ReadTilTypeOfProperty(Result.Node);
Params.Save(OldInput);
try
Params.Identifier:=@Src[CurPos.StartPos];
Params.Flags:=[fdfSearchInParentNodes,fdfExceptionOnNotFound]
+(fdfGlobals*Params.Flags);
Params.ContextNode:=Result.Node.Parent;
FindIdentifierInContext(Params);
if Result.Node.HasAsParent(Params.NewNode) then
break;
Result:=Params.NewCodeTool.FindBaseTypeOfNode(Params,Params.NewNode);
exit;
finally
Params.Load(OldInput);
end;
end else
if (Result.Node.Desc in [ctnProcedure,ctnProcedureHead]) then begin
// a proc -> if this is a function return the result type
if Result.Node.Desc=ctnProcedureHead then
Result.Node:=Result.Node.Parent;
MoveCursorToNodeStart(Result.Node);
ReadNextAtom;
if UpAtomIs('CLASS') then ReadNextAtom;
if UpAtomIs('FUNCTION') then begin
// in a function -> find the result type
// build nodes for parameter list and result type
BuildSubTreeForProcHead(Result.Node);
// a proc node contains has as FirstChild a proc-head node
// and a proc-head node has as childs the parameterlist and the result
Result.Node:=Result.Node.FirstChild.FirstChild;
if Result.Node.Desc=ctnParameterList then
Result.Node:=Result.Node.NextBrother;
end else
break;
end else
if (Result.Node.Desc=ctnTypeType) then begin
// a TypeType is for example 'MyInt = type integer;'
// the context is not the 'type' keyword, but the identifier after it.
Result.Node:=Result.Node.FirstChild;
end else
break;
end;
if (Result.Node=nil) and (fdfExceptionOnNotFound in Params.Flags) then begin
MoveCursorToCleanPos(Params.Identifier);
RaiseException('base type not found');
end;
{$IFDEF CTDEBUG}
write('[TFindDeclarationTool.FindBaseTypeOfNode] END Node=');
if Node<>nil then write(Node.DescAsString) else write('NIL');
write(' Result=');
if Result.Node<>nil then write(Result.Node.DescAsString) else write('NIL');
writeln('');
{$ENDIF}
end;
function TFindDeclarationTool.FindIdentifierInProcContext(
ProcContextNode: TCodeTreeNode; Params: TFindDeclarationParams): boolean;
{ this function is internally used by FindIdentifierInContext
}
var
NameAtom: TAtomPosition;
begin
Result:=false;
// if proc is a method, search in class
// -> find class name
MoveCursorToNodeStart(ProcContextNode);
ReadNextAtom; // read keyword
ReadNextAtom; // read name
NameAtom:=CurPos;
ReadNextAtom;
if AtomIsChar('.') then begin
// proc is a method
// -> proceed the search normally ...
end else begin
// proc is not a method
if CompareSrcIdentifiers(NameAtom.StartPos,Params.Identifier) then
begin
// proc identifier found
{$IFDEF CTDEBUG}
writeln('[TFindDeclarationTool.FindIdentifierInProcContext] Proc Identifier found=',GetIdentifier(Params.Identifier));
{$ENDIF}
Result:=true;
Params.SetResult(Self,ProcContextNode,NameAtom.StartPos);
exit;
end else begin
// proceed the search normally ...
end;
end;
end;
function TFindDeclarationTool.FindIdentifierInClassOfMethod(
ProcContextNode: TCodeTreeNode; Params: TFindDeclarationParams): boolean;
{ this function is internally used by FindIdentifierInContext
}
var
ClassNameAtom: TAtomPosition;
OldInput: TFindDeclarationInput;
ClassContext: TFindContext;
begin
Result:=false;
// if proc is a method, search in class
// -> find class name
MoveCursorToNodeStart(ProcContextNode);
ReadNextAtom; // read keyword
ReadNextAtom; // read classname
ClassNameAtom:=CurPos;
ReadNextAtom;
if AtomIsChar('.') then begin
// proc is a method
if CompareSrcIdentifiers(ClassNameAtom.StartPos,Params.Identifier) then
begin
// the class itself is searched
// -> proceed the search normally ...
end else begin
// search the identifier in the class first
// 1. search the class
Params.Save(OldInput);
try
Params.Flags:=[fdfIgnoreCurContextNode,fdfSearchInParentNodes]
+(fdfGlobals*Params.Flags)
+[fdfExceptionOnNotFound,fdfIgnoreUsedUnits];
Params.ContextNode:=ProcContextNode;
Params.Identifier:=@Src[ClassNameAtom.StartPos];
{$IFDEF CTDEBUG}
writeln('[TFindDeclarationTool.FindIdentifierInProcContext] Proc="',copy(src,ProcContextNode.StartPos,30),'" searching class of method class="',GetIdentifier(ClassNameAtom.StartPos),'"');
{$ENDIF}
FindIdentifierInContext(Params);
ClassContext:=Params.NewCodeTool.FindBaseTypeOfNode(
Params,Params.NewNode);
if (ClassContext.Node=nil)
or (ClassContext.Node.Desc<>ctnClass) then begin
MoveCursorToCleanPos(ClassNameAtom.StartPos);
RaiseException('class identifier expected');
end;
// class context found
// 2. -> search identifier in class
Params.Load(OldInput);
Params.Flags:=[fdfSearchInAncestors]+fdfAllClassVisibilities
+(fdfGlobals*Params.Flags)
-[fdfExceptionOnNotFound];
Params.ContextNode:=ClassContext.Node;
{$IFDEF CTDEBUG}
writeln('[TFindDeclarationTool.FindIdentifierInProcContext] searching identifier in class of method');
{$ENDIF}
Result:=ClassContext.Tool.FindIdentifierInContext(Params);
if Result then exit;
finally
Params.Load(OldInput);
end;
end;
end else begin
// proc is not a method
if CompareSrcIdentifiers(ClassNameAtom.StartPos,Params.Identifier) then
begin
// proc identifier found
{$IFDEF CTDEBUG}
writeln('[TFindDeclarationTool.FindIdentifierInProcContext] Proc Identifier found=',GetIdentifier(Params.Identifier));
{$ENDIF}
Result:=true;
Params.SetResult(Self,ProcContextNode,ClassNameAtom.StartPos);
exit;
end else begin
// proceed the search normally ...
end;
end;
end;
function TFindDeclarationTool.FindClassOfMethod(ProcNode: TCodeTreeNode;
Params: TFindDeclarationParams; FindClassContext: boolean): boolean;
var
ClassNameAtom: TAtomPosition;
OldInput: TFindDeclarationInput;
ClassContext: TFindContext;
begin
{$IFDEF CTDEBUG}
writeln('[TFindDeclarationTool.FindClassOfMethod] A ');
{$ENDIF}
Result:=false;
MoveCursorToNodeStart(ProcNode);
ReadNextAtom; // read keyword
ReadNextAtom; // read classname
ClassNameAtom:=CurPos;
ReadNextAtom;
if AtomIsChar('.') then begin
// proc is a method
// -> search the class
Params.Save(OldInput);
try
Params.Flags:=[fdfIgnoreCurContextNode,fdfSearchInParentNodes,
fdfExceptionOnNotFound,fdfIgnoreUsedUnits]
+(fdfGlobals*Params.Flags);
Params.ContextNode:=ProcNode;
Params.Identifier:=@Src[ClassNameAtom.StartPos];
{$IFDEF CTDEBUG}
writeln('[TFindDeclarationTool.FindClassOfMethod] searching class of method class="',GetIdentifier(ClassNameAtom.StartPos),'"');
{$ENDIF}
FindIdentifierInContext(Params);
if FindClassContext then begin
// parse class and return class node
ClassContext:=FindBaseTypeOfNode(Params,Params.NewNode);
if (ClassContext.Node=nil)
or (ClassContext.Node.Desc<>ctnClass) then begin
MoveCursorToCleanPos(ClassNameAtom.StartPos);
RaiseException('class identifier expected');
end;
// class of method found
Params.SetResult(ClassContext);
// parse class and return class node
// ToDo: do no JIT parsing for PPU, PPW, DCU files
ClassContext.Tool.BuildSubTreeForClass(ClassContext.Node);
end;
Result:=true;
finally
Params.Load(OldInput);
end;
end else begin
// proc is not a method
end;
end;
function TFindDeclarationTool.FindAncestorOfClass(ClassNode: TCodeTreeNode;
Params: TFindDeclarationParams; FindClassContext: boolean): boolean;
var AncestorAtom: TAtomPosition;
OldInput: TFindDeclarationInput;
AncestorNode, ClassIdentNode: TCodeTreeNode;
SearchTObject: boolean;
AncestorContext: TFindContext;
begin
if (ClassNode=nil) or (ClassNode.Desc<>ctnClass) then
RaiseException('[TFindDeclarationTool.FindAncestorOfClass] '
+' invalid classnode');
Result:=false;
// search the ancestor name
MoveCursorToNodeStart(ClassNode);
ReadNextAtom; // read keyword 'class', 'object', 'interface', 'dispinterface'
if UpAtomIs('PACKED') then ReadNextAtom;
ReadNextAtom;
if not AtomIsChar('(') then begin
// no ancestor class specified
// check class name
ClassIdentNode:=ClassNode.Parent;
if (ClassIdentNode=nil) or (ClassIdentNode.Desc<>ctnTypeDefinition) then
begin
MoveCursorToNodeStart(ClassNode);
RaiseException('class without name');
end;
// if this class is not TObject, TObject is class ancestor
SearchTObject:=not CompareSrcIdentifier(ClassIdentNode.StartPos,'TObject');
if not SearchTObject then exit;
end else begin
ReadNextAtom;
if not AtomIsIdentifier(false) then exit;
// ancestor name found
AncestorAtom:=CurPos;
SearchTObject:=false;
end;
{$IFDEF CTDEBUG}
writeln('[TFindDeclarationTool.FindAncestorOfClass] ',
' search ancestor class = ',GetAtom);
{$ENDIF}
// search ancestor class context
CurPos.StartPos:=CurPos.EndPos;
Params.Save(OldInput);
try
Params.Flags:=[fdfSearchInParentNodes,fdfIgnoreCurContextNode,
fdfExceptionOnNotFound]
+(fdfGlobals*Params.Flags);
if not SearchTObject then
Params.Identifier:=@Src[AncestorAtom.StartPos]
else begin
Params.Identifier:='TObject';
Exclude(Params.Flags,fdfExceptionOnNotFound);
end;
Params.ContextNode:=ClassNode;
if not FindIdentifierInContext(Params) then begin
MoveCursorToNodeStart(ClassNode);
//writeln(' AQ2*** ',TCodeBuffer(Scanner.MainCode).Filename,' ',CurPos.StartPos);
RaiseException('default class ancestor TObject not found');
end;
if FindClassContext then begin
AncestorNode:=Params.NewNode;
AncestorContext:=Params.NewCodeTool.FindBaseTypeOfNode(Params,
AncestorNode);
Params.SetResult(AncestorContext);
end;
finally
Params.Load(OldInput);
end;
end;
function TFindDeclarationTool.FindForwardIdentifier(
Params: TFindDeclarationParams; var IsForward: boolean): boolean;
{ first search the identifier in the normal way via FindIdentifierInContext
then search the other direction }
var
OldInput: TFindDeclarationInput;
begin
Params.Save(OldInput);
Exclude(Params.Flags,fdfExceptionOnNotFound);
Result:=FindIdentifierInContext(Params);
if not Result then begin
Params.Load(OldInput);
Include(Params.Flags,fdfSearchForward);
Result:=FindIdentifierInContext(Params);
IsForward:=true;
end else begin
IsForward:=false;
Params.Load(OldInput);
end;
end;
function TFindDeclarationTool.FindIdentifierInWithVarContext(
WithVarNode: TCodeTreeNode; Params: TFindDeclarationParams): boolean;
{ this function is internally used by FindIdentifierInContext
}
var
WithVarContext: TFindContext;
OldInput: TFindDeclarationInput;
begin
{$IFDEF CTDEBUG}
writeln('[TFindDeclarationTool.FindIdentifierInWithVarContext] ',
GetIdentifier(Params.Identifier)
);
{$ENDIF}
Result:=false;
// find the base type of the with variable
// move cursor to end of with-expression
if (WithVarNode.FirstChild<>nil) then begin
// this is the last with-variable
MoveCursorToCleanPos(WithVarNode.FirstChild.StartPos);
ReadPriorAtom; // read 'do'
CurPos.EndPos:=CurPos.StartPos; // make the 'do' unread,
// because 'do' is not part of the expr
end else begin
// this is not the last with variable, so the expr end is equal to node end
MoveCursorToCleanPos(WithVarNode.EndPos);
end;
Params.Save(OldInput);
Params.ContextNode:=WithVarNode;
Include(Params.Flags,fdfExceptionOnNotFound);
WithVarContext:=FindContextNodeAtCursor(Params);
if (WithVarContext.Node=nil) or (WithVarContext.Node=OldInput.ContextNode)
or (not (WithVarContext.Node.Desc in [ctnClass,ctnRecordType])) then begin
MoveCursorToCleanPos(WithVarNode.StartPos);
RaiseException('expression type must be class or record type');
end;
// search identifier in with context
Params.Load(OldInput);
Exclude(Params.Flags,fdfExceptionOnNotFound);
Params.ContextNode:=WithVarContext.Node;
if WithVarContext.Tool.FindIdentifierInContext(Params) then begin
// identifier found in with context
Result:=true;
end else
Params.Load(OldInput);
end;
function TFindDeclarationTool.FindIdentifierInAncestors(
ClassNode: TCodeTreeNode; Params: TFindDeclarationParams): boolean;
{ this function is internally used by FindIdentifierInContext
}
var AncestorAtom: TAtomPosition;
OldInput: TFindDeclarationInput;
AncestorNode, ClassIdentNode: TCodeTreeNode;
SearchTObject: boolean;
AncestorContext: TFindContext;
begin
if (ClassNode=nil) or (ClassNode.Desc<>ctnClass) then
RaiseException('[TFindDeclarationTool.FindIdentifierInAncestors] '
+' invalid classnode');
Result:=false;
if not (fdfSearchInAncestors in Params.Flags) then exit;
// search the ancestor name
MoveCursorToNodeStart(ClassNode);
ReadNextAtom; // read keyword 'class', 'object', 'interface', 'dispinterface'
if UpAtomIs('PACKED') then ReadNextAtom;
ReadNextAtom;
if not AtomIsChar('(') then begin
// no ancestor class specified
// check class name
ClassIdentNode:=ClassNode.Parent;
if (ClassIdentNode=nil) or (ClassIdentNode.Desc<>ctnTypeDefinition) then
begin
MoveCursorToNodeStart(ClassNode);
RaiseException('class without name');
end;
// if this class is not TObject, TObject is class ancestor
SearchTObject:=not CompareSrcIdentifier(ClassIdentNode.StartPos,'TObject');
if not SearchTObject then exit;
end else begin
ReadNextAtom;
if not AtomIsIdentifier(false) then exit;
// ancestor name found
AncestorAtom:=CurPos;
SearchTObject:=false;
end;
{$IFDEF CTDEBUG}
writeln('[TFindDeclarationTool.FindIdentifierInAncestors] ',
' Ident=',GetIdentifier(Params.Identifier),
' search ancestor class = ',GetAtom);
{$ENDIF}
// search ancestor class context
CurPos.StartPos:=CurPos.EndPos;
Params.Save(OldInput);
try
Params.Flags:=[fdfSearchInParentNodes,fdfIgnoreCurContextNode,
fdfExceptionOnNotFound]
+(fdfGlobals*Params.Flags);
if not SearchTObject then
Params.Identifier:=@Src[AncestorAtom.StartPos]
else begin
Params.Identifier:='TObject';
Exclude(Params.Flags,fdfExceptionOnNotFound);
end;
Params.ContextNode:=ClassNode;
if not FindIdentifierInContext(Params) then begin
MoveCursorToNodeStart(ClassNode);
//writeln(' AQ*** ',TCodeBuffer(Scanner.MainCode).Filename,' ',CurPos.StartPos);
RaiseException('default class ancestor TObject not found');
end;
AncestorNode:=Params.NewNode;
AncestorContext:=Params.NewCodeTool.FindBaseTypeOfNode(Params,AncestorNode);
Params.Load(OldInput);
Exclude(Params.Flags,fdfExceptionOnNotFound);
Params.ContextNode:=AncestorContext.Node;
if (AncestorContext.Tool<>Self)
and (not (fdfIgnoreClassVisibility in Params.Flags)) then
Params.Flags:=Params.Flags-[fdfClassPrivate];
Result:=AncestorContext.Tool.FindIdentifierInContext(Params);
finally
Params.Load(OldInput);
end;
end;
{$IFDEF CTDEBUG}
procedure TFindDeclarationTool.DecPrefix;
begin
DebugPrefix:=copy(DebugPrefix,1,length(DebugPrefix)-2);
end;
procedure TFindDeclarationTool.IncPrefix;
begin
DebugPrefix:=DebugPrefix+' ';
end;
{$ENDIF}
function TFindDeclarationTool.FindExpressionResultType(
Params: TFindDeclarationParams; StartPos, EndPos: integer): TFindContext;
begin
// ToDo: operators
// ToDo: operator overloading
// ToDo: internal types. e.g. String[] is of type char
// ToDo: constant types: e.g. 1 is constnumber, #1 is constchar,
// '1' is conststring, 1.0 is constreal
// ToDo: set types: [], A * B
// This is a quick hack: Just return the type of the last variable.
MoveCursorToCleanPos(EndPos);
Result:=FindContextNodeAtCursor(Params);
end;
function TFindDeclarationTool.FindIdentifierInUsesSection(
UsesNode: TCodeTreeNode; Params: TFindDeclarationParams): boolean;
{ this function is internally used by FindIdentifierInContext
search backwards through the uses section
compare first the unit name, then load the unit and search there
}
var InAtom, UnitNameAtom: TAtomPosition;
NewCodeTool: TFindDeclarationTool;
OldInput: TFindDeclarationInput;
begin
Result:=false;
if (UsesNode=nil) or (UsesNode.Desc<>ctnUsesSection) then
RaiseException('[TFindDeclarationTool.FindIdentifierInUsesSection] '
+'internal error: invalid UsesNode');
// search backwards through the uses section
MoveCursorToCleanPos(UsesNode.EndPos);
ReadPriorAtom; // read ';'
if not AtomIsChar(';') then
RaiseException('; expected, but '+GetAtom+' found');
repeat
ReadPriorAtom; // read unitname
if AtomIsStringConstant then begin
InAtom:=CurPos;
ReadPriorAtom; // read 'in'
if not UpAtomIs('IN') then
RaiseException('keyword "in" expected, but '+GetAtom+' found');
ReadPriorAtom; // read unitname
end else
InAtom.StartPos:=-1;
AtomIsIdentifier(true);
UnitNameAtom:=CurPos;
if (fdfIgnoreUsedUnits in Params.Flags) then begin
if CompareSrcIdentifiers(UnitNameAtom.StartPos,Params.Identifier) then
begin
// the searched identifier was a uses unitname, but since the unit should
// not be opened, point to identifier in the uses section
Result:=true;
Params.SetResult(Self,UsesNode,UnitNameAtom.StartPos);
exit;
end else begin
// identifier not found
end;
end else begin
// open the unit and search the identifier in the interface
NewCodeTool:=FindCodeToolForUsedUnit(UnitNameAtom,InAtom,false);
if NewCodeTool=nil then begin
MoveCursorToCleanPos(UnitNameAtom.StartPos);
RaiseException('unit not found: '+copy(Src,UnitNameAtom.StartPos,
UnitNameAtom.EndPos-UnitNameAtom.StartPos));
end else if NewCodeTool=Self then begin
MoveCursorToCleanPos(UnitNameAtom.StartPos);
RaiseException('illegal circle using unit: '+copy(Src,
UnitNameAtom.StartPos,UnitNameAtom.EndPos-UnitNameAtom.StartPos));
end;
// search the identifier in the interface of the used unit
Params.Save(OldInput);
Params.Flags:=[fdfIgnoreUsedUnits]+(fdfGlobals*Params.Flags)
-[fdfExceptionOnNotFound];
Result:=NewCodeTool.FindIdentifierInInterface(Self,Params);
if Result then exit;
Params.Load(OldInput);
// restore the cursor
MoveCursorToCleanPos(UnitNameAtom.StartPos);
end;
ReadPriorAtom; // read keyword 'uses' or comma
until not AtomIsChar(',');
end;
function TFindDeclarationTool.FindCodeToolForUsedUnit(UnitNameAtom,
UnitInFileAtom: TAtomPosition;
ExceptionOnNotFound: boolean): TFindDeclarationTool;
var AnUnitName, AnUnitInFilename: string;
NewCode: TCodeBuffer;
begin
Result:=nil;
if (UnitNameAtom.StartPos<1) or (UnitNameAtom.EndPos<=UnitNameAtom.StartPos)
or (UnitNameAtom.EndPos>SrcLen+1) then
RaiseException('[TFindDeclarationTool.FindCodeToolForUsedUnit] '
+'internal error: invalid UnitNameAtom');
AnUnitName:=copy(Src,UnitNameAtom.StartPos,
UnitNameAtom.EndPos-UnitNameAtom.StartPos);
if UnitInFileAtom.StartPos>=1 then begin
if (UnitInFileAtom.StartPos<1)
or (UnitInFileAtom.EndPos<=UnitInFileAtom.StartPos)
or (UnitInFileAtom.EndPos>SrcLen+1) then
RaiseException('[TFindDeclarationTool.FindCodeToolForUsedUnit] '
+'internal error: invalid UnitInFileAtom');
AnUnitInFilename:=copy(Src,UnitInFileAtom.StartPos,
UnitInFileAtom.EndPos-UnitInFileAtom.StartPos);
end else
AnUnitInFilename:='';
NewCode:=FindUnitSource(AnUnitName,AnUnitInFilename);
if (NewCode=nil) then begin
// no source found
if ExceptionOnNotFound then
RaiseException('unit '+AnUnitName+' not found');
end else begin
// source found -> get codetool for it
{$IFDEF CTDEBUG}
writeln('[TFindDeclarationTool.FindCodeToolForUsedUnit] ',
' This source is=',TCodeBuffer(Scanner.MainCode).Filename,
' NewCode=',NewCode.Filename);
{$ENDIF}
if Assigned(FOnGetCodeToolForBuffer) then
Result:=FOnGetCodeToolForBuffer(Self,NewCode)
else if NewCode=TCodeBuffer(Scanner.MainCode) then
Result:=Self;
end;
end;
function TFindDeclarationTool.FindIdentifierInInterface(
AskingTool: TFindDeclarationTool; Params: TFindDeclarationParams): boolean;
var InterfaceNode: TCodeTreeNode;
SrcIsUsable: boolean;
OldInput: TFindDeclarationInput;
begin
Result:=false;
// build code tree
{$IFDEF CTDEBUG}
writeln(DebugPrefix,'TFindDeclarationTool.FindIdentifierInInterface',
' Ident=',GetIdentifier(Params.Identifier),
' IgnoreUsedUnits=',fdfIgnoreUsedUnits in Params.Flags,
' Self=',TCodeBuffer(Scanner.MainCode).Filename
);
{$ENDIF}
// ToDo: build codetree for ppu, ppw, dcu files
// build tree for pascal source
BuildTree(true);
// check source name
MoveCursorToNodeStart(Tree.Root);
ReadNextAtom; // read keyword for source type, e.g. 'unit'
SrcIsUsable:=UpAtomIs('UNIT');
if not SrcIsUsable then
RaiseException('source is not unit');
ReadNextAtom; // read source name
if CompareSrcIdentifiers(CurPos.StartPos,Params.Identifier) then begin
// identifier is source name
Params.SetResult(Self,Tree.Root,CurPos.StartPos);
Result:=true;
exit;
end;
// search identifier in interface
InterfaceNode:=FindInterfaceNode;
if InterfaceNode=nil then
RaiseException('interface section not found');
Params.Save(OldInput);
try
Params.Flags:=(fdfGlobals*Params.Flags)
-[fdfExceptionOnNotFound,fdfSearchInParentNodes];
Params.ContextNode:=InterfaceNode;
Result:=FindIdentifierInContext(Params);
finally
Params.Load(OldInput);
end;
end;
function TFindDeclarationTool.CompareNodeIdentifier(Node: TCodeTreeNode;
Params: TFindDeclarationParams): boolean;
begin
Result:=false;
if Node=nil then exit;
if Node.Desc in AllSourceTypes then begin
MoveCursorToNodeStart(Node);
ReadNextAtom;
ReadNextAtom;
Result:=CompareSrcIdentifiers(CurPos.StartPos,Params.Identifier);
end else if (Node.Desc in AllIdentifierDefinitions)
or (Node.Desc=ctnIdentifier) then begin
Result:=CompareSrcIdentifiers(Node.StartPos,Params.Identifier);
end;
end;
function TFindDeclarationTool.GetInterfaceNode: TCodeTreeNode;
begin
Result:=Tree.Root;
if Result=nil then begin
CurPos.StartPos:=-1;
RaiseException('[TFindDeclarationTool.GetInterfaceNode] no code tree found');
end;
if not (Tree.Root.Desc in AllUsableSoureTypes) then begin
CurPos.StartPos:=-1;
RaiseException('used unit is not an pascal unit');
end;
Result:=FindInterfaceNode;
if Result=nil then begin
CurPos.StartPos:=-1;
RaiseException('no interface section found');
end;
end;
function TFindDeclarationTool.FindIdentifierInUsedUnit(
const AnUnitName: string; Params: TFindDeclarationParams): boolean;
{ this function is internally used by FindIdentifierInUsesSection
for hidden used units, like the system unit or the objpas unit
}
var
NewCode: TCodeBuffer;
NewCodeTool: TFindDeclarationTool;
OldInput: TFindDeclarationInput;
begin
Result:=false;
// open the unit and search the identifier in the interface
NewCode:=FindUnitSource(AnUnitName,'');
if (NewCode=nil) then begin
// no source found
CurPos.StartPos:=-1;
RaiseException('unit '+AnUnitName+' not found');
end else begin
// source found -> get codetool for it
{$IFDEF CTDEBUG}
writeln('[TFindDeclarationTool.FindIdentifierInUsedUnit] ',
' This source is=',TCodeBuffer(Scanner.MainCode).Filename,
' NewCode=',NewCode.Filename,' IgnoreUsedUnits=',fdfIgnoreUsedUnits in Params.Flags);
{$ENDIF}
if Assigned(FOnGetCodeToolForBuffer) then begin
NewCodeTool:=FOnGetCodeToolForBuffer(Self,NewCode);
if NewCodeTool=nil then begin
CurPos.StartPos:=-1;
RaiseException('unit '+AnUnitName+' not found');
end;
end else if NewCode=TCodeBuffer(Scanner.MainCode) then begin
NewCodeTool:=Self;
CurPos.StartPos:=-1;
RaiseException('illegal circle using unit: '+AnUnitName);
end;
// search the identifier in the interface of the used unit
Params.Save(OldInput);
Params.Flags:=[fdfIgnoreUsedUnits]+(fdfGlobals*Params.Flags)
-[fdfExceptionOnNotFound];
Result:=NewCodeTool.FindIdentifierInInterface(Self,Params);
if Result then exit;
Params.Load(OldInput);
end;
end;
function TFindDeclarationTool.FindIdentifierInHiddenUsedUnits(
Params: TFindDeclarationParams): boolean;
const
sutSystem = 1;
sutObjPas = 2;
sutLineInfo = 3;
sutHeapTrc = 4;
sutNone = 5;
var
OldInput: TFindDeclarationInput;
SystemUnitName: string;
SpecialUnitType: integer;
begin
Result:=false;
{$IFDEF CTDEBUG}
writeln('[TFindDeclarationTool.FindIdentifierInHiddenUsedUnits] ',
GetIdentifier(Params.Identifier),' IgnoreUsedUnits=',fdfIgnoreUsedUnits in Params.Flags);
{$ENDIF}
if (Tree.Root<>nil) and (not (fdfIgnoreUsedUnits in Params.Flags)) then begin
// check, if this is a special unit
MoveCursorToNodeStart(Tree.Root);
ReadNextAtom;
ReadNextAtom;
if Scanner.InitialValues.IsDefined('LINUX') then
SystemUnitName:='SYSLINUX'
else
// ToDo: other OS than linux
SystemUnitName:='SYSTEM';
if UpAtomIs(SystemUnitName) then
SpecialUnitType:=sutSystem
else if UpAtomIs('OBJPAS') then
SpecialUnitType:=sutObjPas
else if UpAtomIs('LINEINFO') then
SpecialUnitType:=sutLineInfo
else if UpAtomIs('HEAPTRC') then
SpecialUnitType:=sutHeapTrc
else
SpecialUnitType:=sutNone;
// try hidden units
if (SpecialUnitType>sutHeapTrc)
and Scanner.InitialValues.IsDefined(ExternalMacroStart+'UseHeapTrcUnit')
then begin
// try hidden used unit 'heaptrc'
Result:=FindIdentifierInUsedUnit('HeapTrc',Params);
if Result then exit;
end;
if (SpecialUnitType>sutLineInfo)
and Scanner.InitialValues.IsDefined(ExternalMacroStart+'UseLineInfo')
then begin
// try hidden used unit 'lineinfo'
Result:=FindIdentifierInUsedUnit('LineInfo',Params);
if Result then exit;
end;
if (SpecialUnitType>sutObjPas)
and (Scanner.CompilerMode in [cmDELPHI,cmOBJFPC]) then begin
// try hidden used unit 'objpas'
Result:=FindIdentifierInUsedUnit('ObjPas',Params);
if Result then exit;
end;
// try hidden used unit 'system'
if (SpecialUnitType>sutSystem)
and CompareSrcIdentifiers(Params.Identifier,PChar(SystemUnitName)) then begin
// the system unit name itself is searched -> rename searched identifier
Params.Save(OldInput);
Params.Identifier:=PChar(SystemUnitName);
Result:=FindIdentifierInUsedUnit(SystemUnitName,Params);
Params.Load(OldInput);
end else
Result:=FindIdentifierInUsedUnit(SystemUnitName,Params);
if Result then exit;
end;
end;
{ TFindDeclarationParams }
constructor TFindDeclarationParams.Create;
begin
inherited Create;
Clear;
end;
procedure TFindDeclarationParams.Clear;
begin
ClearInput;
ClearResult;
end;
procedure TFindDeclarationParams.Load(var Input: TFindDeclarationInput);
begin
Flags:=Input.Flags;
Identifier:=Input.Identifier;
ContextNode:=Input.ContextNode;
end;
procedure TFindDeclarationParams.Save(var Input: TFindDeclarationInput);
begin
Input.Flags:=Flags;
Input.Identifier:=Identifier;
Input.ContextNode:=ContextNode;
end;
procedure TFindDeclarationParams.ClearResult;
begin
NewPos.Code:=nil;
NewPos.X:=-1;
NewPos.Y:=-1;
NewTopLine:=-1;
NewNode:=nil;
NewCleanPos:=-1;
NewCodeTool:=nil;
end;
procedure TFindDeclarationParams.SetResult(ANewCodeTool: TFindDeclarationTool;
ANewNode: TCodeTreeNode);
begin
ClearResult;
NewCodeTool:=ANewCodeTool;
NewNode:=ANewNode;
end;
procedure TFindDeclarationParams.SetResult(ANewCodeTool: TFindDeclarationTool;
ANewNode: TCodeTreeNode; ANewCleanPos: integer);
begin
ClearResult;
NewCodeTool:=ANewCodeTool;
NewNode:=ANewNode;
NewCleanPos:=ANewCleanPos;
end;
procedure TFindDeclarationParams.ConvertResultCleanPosToCaretPos;
begin
NewPos.Code:=nil;
if NewCodeTool<>nil then begin
if (NewCleanPos>=1) then
NewCodeTool.CleanPosToCaretAndTopLine(NewCleanPos,
NewPos,NewTopLine)
else if (NewNode<>nil) then
NewCodeTool.CleanPosToCaretAndTopLine(NewNode.StartPos,
NewPos,NewTopLine);
end;
end;
procedure TFindDeclarationParams.ClearInput;
begin
Flags:=[];
Identifier:=nil;
ContextNode:=nil;
end;
procedure TFindDeclarationParams.SetResult(AFindContext: TFindContext);
begin
ClearResult;
NewCodeTool:=AFindContext.Tool;
NewNode:=AFindContext.Node;
end;
end.