mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-08-10 20:56:14 +02:00
CodeTools: improve generics support (also nested generics types). Issue #18373, patch from Anton
git-svn-id: trunk@32016 -
This commit is contained in:
parent
db121af7a1
commit
52a0ff3f37
@ -121,6 +121,9 @@ ResourceString
|
|||||||
ctsUnitNotFound = 'unit not found: %s';
|
ctsUnitNotFound = 'unit not found: %s';
|
||||||
ctsSourceNotFoundUnit = 'source not found: unit %s';
|
ctsSourceNotFoundUnit = 'source not found: unit %s';
|
||||||
ctsIdentifierNotFound = 'identifier not found: %s';
|
ctsIdentifierNotFound = 'identifier not found: %s';
|
||||||
|
ctsNotEnoughGenParams = 'Not enough actual generic parameters';
|
||||||
|
ctsTooManyGenParams = 'Too many actual generic parameters';
|
||||||
|
ctsSyntaxErrorInGeneric = 'Syntax error in generic type declaration';
|
||||||
ctsNoContextNodeFoundAtCursor = 'no context node found at cursor';
|
ctsNoContextNodeFoundAtCursor = 'no context node found at cursor';
|
||||||
ctsInheritedKeywordOnlyAllowedInMethods =
|
ctsInheritedKeywordOnlyAllowedInMethods =
|
||||||
'inherited keyword only allowed in methods';
|
'inherited keyword only allowed in methods';
|
||||||
|
@ -438,6 +438,25 @@ type
|
|||||||
TOnGetDirectoryCache = function(const ADirectory: string
|
TOnGetDirectoryCache = function(const ADirectory: string
|
||||||
): TCTDirectoryCache of object;
|
): TCTDirectoryCache of object;
|
||||||
|
|
||||||
|
{ TGenericParams }
|
||||||
|
|
||||||
|
TGenericParams = class
|
||||||
|
private
|
||||||
|
FParamValuesTool: TFindDeclarationTool;
|
||||||
|
FSpecializeParamsNode: TCodeTreeNode;
|
||||||
|
FParams: TStringList; // "ParamName" => PChar("real type")
|
||||||
|
procedure RaiseParamsMissmatch(const AMessage: string);
|
||||||
|
public
|
||||||
|
constructor Create;
|
||||||
|
destructor Destroy; override;
|
||||||
|
procedure SetParamNames(CT: TFindDeclarationTool;
|
||||||
|
GenericParamsNode: TCodeTreeNode);
|
||||||
|
procedure SetParamValues(CT: TFindDeclarationTool;
|
||||||
|
SpecializeParamsNode: TCodeTreeNode);
|
||||||
|
function FindRealType(GenParam: PChar): PChar;
|
||||||
|
function ParamExists(ParamName: PChar): Boolean;
|
||||||
|
end;
|
||||||
|
|
||||||
TFindDeclarationInput = record
|
TFindDeclarationInput = record
|
||||||
Flags: TFindDeclarationFlags;
|
Flags: TFindDeclarationFlags;
|
||||||
Identifier: PChar;
|
Identifier: PChar;
|
||||||
@ -472,6 +491,7 @@ type
|
|||||||
private
|
private
|
||||||
FirstFoundProc: PFoundProc;//list of all saved PFoundProc
|
FirstFoundProc: PFoundProc;//list of all saved PFoundProc
|
||||||
LastFoundProc: PFoundProc;
|
LastFoundProc: PFoundProc;
|
||||||
|
FGenParams: TGenericParams;
|
||||||
procedure FreeFoundProc(aFoundProc: PFoundProc; FreeNext: boolean);
|
procedure FreeFoundProc(aFoundProc: PFoundProc; FreeNext: boolean);
|
||||||
procedure RemoveFoundProcFromList(aFoundProc: PFoundProc);
|
procedure RemoveFoundProcFromList(aFoundProc: PFoundProc);
|
||||||
public
|
public
|
||||||
@ -506,6 +526,11 @@ type
|
|||||||
procedure SetIdentifier(NewIdentifierTool: TFindDeclarationTool;
|
procedure SetIdentifier(NewIdentifierTool: TFindDeclarationTool;
|
||||||
NewIdentifier: PChar; NewOnIdentifierFound: TOnIdentifierFound);
|
NewIdentifier: PChar; NewOnIdentifierFound: TOnIdentifierFound);
|
||||||
procedure SetFirstFoundProc(const ProcContext: TFindContext);
|
procedure SetFirstFoundProc(const ProcContext: TFindContext);
|
||||||
|
procedure SetGenericParamNames(GenericType: TFindContext);
|
||||||
|
procedure SetGenericParamValues(SpecializeParamsTool: TFindDeclarationTool;
|
||||||
|
SpecializeNode: TCodeTreeNode);
|
||||||
|
function ExistsGenericParam(ParamName: PChar): Boolean;
|
||||||
|
procedure SetForGenericParam(ParamName: PChar; FindParams: TFindDeclarationParams);
|
||||||
procedure ChangeFoundProc(const ProcContext: TFindContext;
|
procedure ChangeFoundProc(const ProcContext: TFindContext;
|
||||||
ProcCompatibility: TTypeCompatibility;
|
ProcCompatibility: TTypeCompatibility;
|
||||||
ParamCompatibilityList: TTypeCompatibilityList);
|
ParamCompatibilityList: TTypeCompatibilityList);
|
||||||
@ -1199,6 +1224,92 @@ begin
|
|||||||
ListOfPFindContext:=nil;
|
ListOfPFindContext:=nil;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
{ TGenericParams }
|
||||||
|
|
||||||
|
procedure TGenericParams.RaiseParamsMissmatch(const AMessage: string);
|
||||||
|
begin
|
||||||
|
FParamValuesTool.MoveCursorToNodeStart(FSpecializeParamsNode);
|
||||||
|
FParamValuesTool.RaiseException(AMessage);
|
||||||
|
end;
|
||||||
|
|
||||||
|
constructor TGenericParams.Create;
|
||||||
|
begin
|
||||||
|
FParams := TStringList.Create;
|
||||||
|
end;
|
||||||
|
|
||||||
|
destructor TGenericParams.Destroy;
|
||||||
|
begin
|
||||||
|
FParams.Free;
|
||||||
|
inherited Destroy;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TGenericParams.SetParamNames(CT: TFindDeclarationTool;
|
||||||
|
GenericParamsNode: TCodeTreeNode);
|
||||||
|
var p: TCodeTreeNode; n: Integer;
|
||||||
|
begin
|
||||||
|
if not Assigned(CT) then Exit;
|
||||||
|
p := GenericParamsNode.FirstChild;
|
||||||
|
n := 0;
|
||||||
|
while (p <> nil) do
|
||||||
|
begin
|
||||||
|
if n >= FParams.Count then
|
||||||
|
RaiseParamsMissmatch(ctsNotEnoughGenParams);
|
||||||
|
CT.MoveCursorToNodeStart(p);
|
||||||
|
CT.ReadNextAtom;
|
||||||
|
FParams[n] := CT.GetAtom;
|
||||||
|
Inc(n);
|
||||||
|
p := p.NextBrother;
|
||||||
|
end;
|
||||||
|
if n < FParams.Count then
|
||||||
|
RaiseParamsMissmatch(ctsTooManyGenParams);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TGenericParams.SetParamValues(CT: TFindDeclarationTool;
|
||||||
|
SpecializeParamsNode: TCodeTreeNode);
|
||||||
|
begin
|
||||||
|
FParams.Clear;
|
||||||
|
FParamValuesTool := CT;
|
||||||
|
FSpecializeParamsNode := SpecializeParamsNode;
|
||||||
|
if not Assigned(CT) then Exit;
|
||||||
|
with FParamValuesTool do
|
||||||
|
begin
|
||||||
|
MoveCursorToNodeStart(SpecializeParamsNode);
|
||||||
|
if not ReadNextAtomIsChar('<') then
|
||||||
|
FParamValuesTool.RaiseException('"<" expected');
|
||||||
|
ReadNextAtom;
|
||||||
|
while CurPos.Flag = cafWord do
|
||||||
|
begin
|
||||||
|
FParams.AddObject('', TObject(@Src[CurPos.StartPos]));
|
||||||
|
ReadNextAtom;
|
||||||
|
if CurPos.Flag = cafComma then ReadNextAtom;
|
||||||
|
end;
|
||||||
|
if not AtomIsChar('>') then
|
||||||
|
FParamValuesTool.RaiseException('">" expected');
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TGenericParams.FindRealType(GenParam: PChar): PChar;
|
||||||
|
var i: Integer; s: string;
|
||||||
|
begin
|
||||||
|
Result := nil;
|
||||||
|
for i := 0 to FParams.Count - 1 do
|
||||||
|
begin
|
||||||
|
s := FParams[i];
|
||||||
|
if CompareIdentifiers(GenParam, PChar(s)) = 0 then
|
||||||
|
Exit(PChar(FParams.Objects[i]));
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TGenericParams.ParamExists(ParamName: PChar): Boolean;
|
||||||
|
var i: Integer; s: string;
|
||||||
|
begin
|
||||||
|
for i := 0 to FParams.Count - 1 do
|
||||||
|
begin
|
||||||
|
s := FParams[i];
|
||||||
|
if CompareIdentifiers(PChar(s), ParamName) = 0 then Exit(True);
|
||||||
|
end;
|
||||||
|
Result := False;
|
||||||
|
end;
|
||||||
|
|
||||||
{ TFindDeclarationTool }
|
{ TFindDeclarationTool }
|
||||||
|
|
||||||
@ -3407,10 +3518,14 @@ var
|
|||||||
{$IFDEF ShowTriedBaseContexts}
|
{$IFDEF ShowTriedBaseContexts}
|
||||||
debugln(['TFindDeclarationTool.FindBaseTypeOfNode.SearchIdentifier Identifier=',GetIdentifier(@Src[IdentStart])]);
|
debugln(['TFindDeclarationTool.FindBaseTypeOfNode.SearchIdentifier Identifier=',GetIdentifier(@Src[IdentStart])]);
|
||||||
{$ENDIF}
|
{$ENDIF}
|
||||||
SubParams.SetIdentifier(Self,@Src[IdentStart],nil);
|
|
||||||
SubParams.Flags:=[fdfSearchInParentNodes,fdfExceptionOnNotFound]
|
SubParams.Flags:=[fdfSearchInParentNodes,fdfExceptionOnNotFound]
|
||||||
+(fdfGlobals*SubParams.Flags);
|
+(fdfGlobals*SubParams.Flags);
|
||||||
|
if Params.ExistsGenericParam(@Src[IdentStart]) then
|
||||||
|
Params.SetForGenericParam(@Src[IdentStart], SubParams)
|
||||||
|
else begin
|
||||||
|
SubParams.SetIdentifier(Self,@Src[IdentStart],nil);
|
||||||
SubParams.ContextNode:=StartNode.Parent;
|
SubParams.ContextNode:=StartNode.Parent;
|
||||||
|
end;
|
||||||
if (SubParams.ContextNode.Desc in AllIdentifierDefinitions) then begin
|
if (SubParams.ContextNode.Desc in AllIdentifierDefinitions) then begin
|
||||||
// pascal allows things like 'var a: a;' -> skip var definition
|
// pascal allows things like 'var a: a;' -> skip var definition
|
||||||
Include(SubParams.Flags,fdfIgnoreCurContextNode);
|
Include(SubParams.Flags,fdfIgnoreCurContextNode);
|
||||||
@ -3457,7 +3572,7 @@ var
|
|||||||
// only types allowed here
|
// only types allowed here
|
||||||
TestContext.Tool:=SubParams.NewCodeTool;
|
TestContext.Tool:=SubParams.NewCodeTool;
|
||||||
TestContext.Node:=SubParams.NewNode;
|
TestContext.Node:=SubParams.NewNode;
|
||||||
if not (TestContext.Node.Desc in [ctnTypeDefinition,ctnGenericParameter]) then
|
if not (TestContext.Node.Desc in [ctnTypeDefinition,ctnGenericType,ctnGenericParameter]) then
|
||||||
begin
|
begin
|
||||||
// not a type
|
// not a type
|
||||||
{$IFDEF ShowTriedBaseContexts}
|
{$IFDEF ShowTriedBaseContexts}
|
||||||
@ -3758,6 +3873,7 @@ begin
|
|||||||
NameNode:=SpecializeNode.FirstChild;
|
NameNode:=SpecializeNode.FirstChild;
|
||||||
Result.Node:=NameNode;
|
Result.Node:=NameNode;
|
||||||
if Result.Node=nil then break;
|
if Result.Node=nil then break;
|
||||||
|
Params.SetGenericParamValues(Self, SpecializeNode);
|
||||||
SearchIdentifier(SpecializeNode,NameNode.StartPos,IsPredefined,Result);
|
SearchIdentifier(SpecializeNode,NameNode.StartPos,IsPredefined,Result);
|
||||||
if (Result.Node=nil) or (Result.Node.Desc<>ctnGenericType) then begin
|
if (Result.Node=nil) or (Result.Node.Desc<>ctnGenericType) then begin
|
||||||
// not a generic
|
// not a generic
|
||||||
@ -3766,6 +3882,7 @@ begin
|
|||||||
RaiseExceptionFmt(ctsStrExpectedButAtomFound,
|
RaiseExceptionFmt(ctsStrExpectedButAtomFound,
|
||||||
[ctsGenericIdentifier,GetAtom]);
|
[ctsGenericIdentifier,GetAtom]);
|
||||||
end;
|
end;
|
||||||
|
Params.SetGenericParamNames(Result);
|
||||||
end else
|
end else
|
||||||
break;
|
break;
|
||||||
end;
|
end;
|
||||||
@ -3774,6 +3891,8 @@ begin
|
|||||||
finally
|
finally
|
||||||
if NodeStack=@MyNodeStack then begin
|
if NodeStack=@MyNodeStack then begin
|
||||||
// cache the result in all nodes
|
// cache the result in all nodes
|
||||||
|
// do not cache the result of generic type
|
||||||
|
if not Assigned(Params.FGenParams) then
|
||||||
CreateBaseTypeCaches(NodeStack,Result);
|
CreateBaseTypeCaches(NodeStack,Result);
|
||||||
// free node stack
|
// free node stack
|
||||||
FinalizeNodeStack(NodeStack);
|
FinalizeNodeStack(NodeStack);
|
||||||
@ -10469,6 +10588,7 @@ end;
|
|||||||
|
|
||||||
destructor TFindDeclarationParams.Destroy;
|
destructor TFindDeclarationParams.Destroy;
|
||||||
begin
|
begin
|
||||||
|
FGenParams.Free;
|
||||||
Clear;
|
Clear;
|
||||||
FreeFoundProc(FirstFoundProc,true);
|
FreeFoundProc(FirstFoundProc,true);
|
||||||
inherited Destroy;
|
inherited Destroy;
|
||||||
@ -10672,6 +10792,53 @@ begin
|
|||||||
FoundProc^.Context:=ProcContext;
|
FoundProc^.Context:=ProcContext;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
procedure TFindDeclarationParams.SetGenericParamNames(
|
||||||
|
GenericType: TFindContext);
|
||||||
|
var GenericTypeNode: TCodeTreeNode;
|
||||||
|
begin
|
||||||
|
if not Assigned(FGenParams) then
|
||||||
|
IdentifierTool.RaiseException(ctsSyntaxErrorInGeneric);
|
||||||
|
GenericTypeNode := GenericType.Node;
|
||||||
|
if not Assigned(GenericTypeNode)
|
||||||
|
or not Assigned(GenericTypeNode.FirstChild)
|
||||||
|
or not Assigned(GenericTypeNode.FirstChild.NextBrother)
|
||||||
|
then
|
||||||
|
GenericType.Tool.RaiseException(ctsSyntaxErrorInGeneric);
|
||||||
|
FGenParams.SetParamNames(GenericType.Tool,
|
||||||
|
GenericTypeNode.FirstChild.NextBrother);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFindDeclarationParams.SetGenericParamValues(
|
||||||
|
SpecializeParamsTool: TFindDeclarationTool;
|
||||||
|
SpecializeNode: TCodeTreeNode);
|
||||||
|
begin
|
||||||
|
if not Assigned(FGenParams) then
|
||||||
|
FGenParams := TGenericParams.Create;
|
||||||
|
if not Assigned(SpecializeNode)
|
||||||
|
or not Assigned(SpecializeNode.FirstChild)
|
||||||
|
or not Assigned(SpecializeNode.FirstChild.NextBrother)
|
||||||
|
then
|
||||||
|
SpecializeParamsTool.RaiseException('error in specialize syntax');
|
||||||
|
FGenParams.SetParamValues(SpecializeParamsTool,
|
||||||
|
SpecializeNode.FirstChild.NextBrother);
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TFindDeclarationParams.ExistsGenericParam(ParamName: PChar): Boolean;
|
||||||
|
begin
|
||||||
|
if not Assigned(FGenParams) then
|
||||||
|
Result := False
|
||||||
|
else
|
||||||
|
Result := FGenParams.ParamExists(ParamName);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFindDeclarationParams.SetForGenericParam(ParamName: PChar;
|
||||||
|
FindParams: TFindDeclarationParams);
|
||||||
|
begin
|
||||||
|
FindParams.SetIdentifier(FGenParams.FParamValuesTool,
|
||||||
|
FGenParams.FindRealType(ParamName), nil);
|
||||||
|
FindParams.ContextNode := FGenParams.FSpecializeParamsNode;
|
||||||
|
end;
|
||||||
|
|
||||||
procedure TFindDeclarationParams.ChangeFoundProc(
|
procedure TFindDeclarationParams.ChangeFoundProc(
|
||||||
const ProcContext: TFindContext;
|
const ProcContext: TFindContext;
|
||||||
ProcCompatibility: TTypeCompatibility;
|
ProcCompatibility: TTypeCompatibility;
|
||||||
|
Loading…
Reference in New Issue
Block a user