unit TCGenerics; {$mode objfpc}{$H+} interface uses Classes, SysUtils, fpcunit, pastree, testregistry, pscanner, tctypeparser; Type { TTestGenerics - for resolver see unit tcresolvegenerics } TTestGenerics = Class(TBaseTestTypeParser) Published // generic types Procedure TestObjectGenerics; Procedure TestRecordGenerics; Procedure TestArrayGenerics; Procedure TestArrayGenericsDelphi; Procedure TestProcTypeGenerics; Procedure TestDeclarationDelphi; Procedure TestDeclarationFPC; Procedure TestDeclarationFPCNoSpaces; Procedure TestMethodImplementation; // generic constraints Procedure TestGenericConstraint; Procedure TestGenericInterfaceConstraint; Procedure TestDeclarationConstraint; // specialize type Procedure TestSpecializationDelphi; Procedure TestDeclarationDelphiSpecialize; Procedure TestInlineSpecializationInArgument; Procedure TestSpecializeNested; Procedure TestInlineSpecializeInStatement; Procedure TestInlineSpecializeInStatementDelphi; // generic functions Procedure TestGenericFunction_Program; Procedure TestGenericFunction_Unit; // generic method Procedure TestGenericMethod_Program; Procedure TestGenericMethod_OverloadDelphi; end; implementation procedure TTestGenerics.TestObjectGenerics; begin Add([ 'Type', 'Generic TSomeClass = Object', ' b : T;', 'end;', '']); ParseDeclarations; end; procedure TTestGenerics.TestRecordGenerics; begin Add([ 'Type', ' Generic TSome = Record', ' b : T;', ' end;', '']); ParseDeclarations; end; procedure TTestGenerics.TestArrayGenerics; begin Add([ 'Type', ' Generic TSome = array of T;', ' Generic TStatic = array[R] of T;', '']); ParseDeclarations; end; procedure TTestGenerics.TestArrayGenericsDelphi; begin Add([ '{$mode delphi}', 'Type', ' TSome = array of T;', ' TStatic = array[R] of T;', '']); ParseDeclarations; end; procedure TTestGenerics.TestProcTypeGenerics; begin Add([ 'Type', ' Generic TSome = procedure(v: T);', ' Generic TFunc = function(b: R): T;', '']); ParseDeclarations; end; procedure TTestGenerics.TestDeclarationDelphi; Var T : TPasClassType; begin Scanner.CurrentModeSwitches:=[msDelphi]+Scanner.CurrentModeSwitches ; Source.Add('Type'); Source.Add(' TSomeClass = Class(TObject)'); Source.Add(' b : T;'); Source.Add(' b2 : T2;'); Source.Add(' FItems: ^TArray;'); Source.Add(' type'); Source.Add(' TDictionaryEnumerator = TDictionary.TKeyEnumerator;'); Source.Add(' end;'); ParseDeclarations; AssertNotNull('have generic definition',Declarations.Classes); AssertEquals('have generic definition',1,Declarations.Classes.Count); AssertEquals('Pascal class',TPasClassType,TObject(Declarations.Classes[0]).ClassType); T:=TPasClassType(Declarations.Classes[0]); AssertNotNull('have generic templates',T.GenericTemplateTypes); AssertEquals('2 template types',2,T.GenericTemplateTypes.Count); AssertSame('Parent 0 is class',T,TPasElement(T.GenericTemplateTypes[0]).Parent); AssertSame('Parent 1 is class',T,TPasElement(T.GenericTemplateTypes[1]).Parent); end; procedure TTestGenerics.TestDeclarationFPC; Var T : TPasClassType; begin Scanner.CurrentModeSwitches:=[msDelphi]+Scanner.CurrentModeSwitches; Source.Add('Type'); Source.Add(' TSomeClass = Class(TObject)'); Source.Add(' b : T;'); Source.Add(' b2 : T2;'); Source.Add(' end;'); ParseDeclarations; AssertNotNull('have generic definition',Declarations.Classes); AssertEquals('have generic definition',1,Declarations.Classes.Count); AssertEquals('Pascal class',TPasClassType,TObject(Declarations.Classes[0]).ClassType); T:=TPasClassType(Declarations.Classes[0]); AssertNotNull('have generic templates',T.GenericTemplateTypes); AssertEquals('2 template types',2,T.GenericTemplateTypes.Count); AssertSame('Parent 0 is class',T,TPasElement(T.GenericTemplateTypes[0]).Parent); AssertSame('Parent 1 is class',T,TPasElement(T.GenericTemplateTypes[1]).Parent); end; procedure TTestGenerics.TestDeclarationFPCNoSpaces; Var T : TPasClassType; begin Scanner.CurrentModeSwitches:=[msDelphi]+Scanner.CurrentModeSwitches; Source.Add('Type'); Source.Add(' TSomeClass=Class(TObject)'); Source.Add(' b : T;'); Source.Add(' b2 : T2;'); Source.Add(' end;'); ParseDeclarations; AssertNotNull('have generic definition',Declarations.Classes); AssertEquals('have generic definition',1,Declarations.Classes.Count); AssertEquals('Pascal class',TPasClassType,TObject(Declarations.Classes[0]).ClassType); T:=TPasClassType(Declarations.Classes[0]); AssertNotNull('have generic templates',T.GenericTemplateTypes); AssertEquals('2 template types',2,T.GenericTemplateTypes.Count); AssertSame('Parent 0 is class',T,TPasElement(T.GenericTemplateTypes[0]).Parent); AssertSame('Parent 1 is class',T,TPasElement(T.GenericTemplateTypes[1]).Parent); end; procedure TTestGenerics.TestMethodImplementation; begin With source do begin Add('unit afile;'); Add('{$MODE DELPHI}'); Add('interface'); Add('type'); Add(' TTest = object'); Add(' procedure foo(v:T);'); Add(' procedure bar(v:T);'); Add(' type'); Add(' TSub = class'); Add(' procedure DoIt(v:T);'); Add(' end;'); Add(' end;'); Add('implementation'); Add('procedure TTest.foo;'); Add('begin'); Add('end;'); Add('procedure TTest.bar;'); Add('begin'); Add('end;'); Add('procedure TTest.TSub.DoIt;'); Add('begin'); Add('end;'); end; ParseModule; end; procedure TTestGenerics.TestGenericConstraint; begin Add([ 'Type', 'Generic TSomeClass = class', ' b : T;', 'end;', 'Generic TBird = class', ' c : specialize TBird;', 'end;', 'Generic TEagle = class', 'end;', 'Generic TEagle = class', 'end;', '']); ParseDeclarations; end; procedure TTestGenerics.TestGenericInterfaceConstraint; begin Add([ 'Type', 'TIntfA = interface end;', 'TIntfB = interface end;', 'TBird = class(TInterfacedObject,TIntfA,TIntfB) end;', 'Generic TAnt = class', ' b: T;', ' c: specialize TAnt;', 'end;', 'Generic TFly = class', ' b: S;', ' c: specialize TFly;', 'end;', '']); ParseDeclarations; end; procedure TTestGenerics.TestDeclarationConstraint; Var T : TPasClassType; begin Scanner.CurrentModeSwitches:=[msDelphi]+Scanner.CurrentModeSwitches ; Source.Add('Type'); Source.Add(' TSomeClass = Class(TObject)'); Source.Add(' b : T;'); Source.Add(' end;'); ParseDeclarations; AssertNotNull('have generic definition',Declarations.Classes); AssertEquals('have generic definition',1,Declarations.Classes.Count); AssertEquals('Pascal class',TPasClassType,TObject(Declarations.Classes[0]).ClassType); T:=TPasClassType(Declarations.Classes[0]); AssertNotNull('have generic templates',T.GenericTemplateTypes); AssertEquals('1 template types',1,T.GenericTemplateTypes.Count); AssertSame('Parent 0 is class',T,TPasElement(T.GenericTemplateTypes[0]).Parent); AssertEquals('Type constraint is recorded','T2',TPasGenericTemplateType(T.GenericTemplateTypes[0]).TypeConstraint); end; procedure TTestGenerics.TestSpecializationDelphi; begin Add('{$mode delphi}'); ParseType('TFPGList',TPasSpecializeType,''); end; procedure TTestGenerics.TestDeclarationDelphiSpecialize; Var T : TPasClassType; begin Scanner.CurrentModeSwitches:=[msDelphi]+Scanner.CurrentModeSwitches ; Source.Add('Type'); Source.Add(' TSomeClass = Class(TSomeGeneric)'); Source.Add(' b : T;'); Source.Add(' b2 : T2;'); Source.Add(' end;'); ParseDeclarations; AssertNotNull('have generic definition',Declarations.Classes); AssertEquals('have generic definition',1,Declarations.Classes.Count); AssertEquals('Pascal class',TPasClassType,TObject(Declarations.Classes[0]).ClassType); T:=TPasClassType(Declarations.Classes[0]); AssertEquals('Name is correct','TSomeClass',T.Name); AssertNotNull('have generic templates',T.GenericTemplateTypes); AssertEquals('2 template types',2,T.GenericTemplateTypes.Count); AssertSame('Parent 0 is class',T,TPasElement(T.GenericTemplateTypes[0]).Parent); AssertSame('Parent 1 is class',T,TPasElement(T.GenericTemplateTypes[1]).Parent); end; procedure TTestGenerics.TestInlineSpecializationInArgument; begin With source do begin Add('unit afile;'); Add('{$MODE DELPHI}'); Add('interface'); Add('type'); Add(' TFoo=class'); Add(' procedure foo(var Node:TSomeGeneric;const index:Integer);'); Add(' end;'); Add('implementation'); end; ParseModule; end; procedure TTestGenerics.TestSpecializeNested; begin Add([ 'Type', ' generic TSomeClass = class(specialize TOther>) end;', '']); ParseDeclarations; end; procedure TTestGenerics.TestInlineSpecializeInStatement; begin Add([ '{$mode objfpc}', 'begin', ' vec:=specialize TVector.create;', ' t:=specialize a;', //' t:=specialize a>;', //' t:=a.specialize b;', ' t:=specialize a.c;', '']); ParseModule; end; procedure TTestGenerics.TestInlineSpecializeInStatementDelphi; begin Add([ '{$mode delphi}', 'begin', ' vec:=TVector.create;', ' b:=a>;', ' t:=a.b;', ' t:=a.c;', // forbidden:' t:=a.d>;', '']); ParseModule; end; procedure TTestGenerics.TestGenericFunction_Program; begin Add([ 'generic function IfThen(val:boolean;const iftrue:T; const iffalse:T) :T; inline; overload;', 'begin', 'end;', 'begin', ' specialize IfThen(true,2,3);', '']); ParseModule; end; procedure TTestGenerics.TestGenericFunction_Unit; begin Add([ 'unit afile;', 'interface', 'generic function Get(val: T) :T;', 'implementation', 'generic function Get(val: T) :T;', 'begin', 'end;', 'initialization', ' specialize GetIt(2);', '']); ParseModule; end; procedure TTestGenerics.TestGenericMethod_Program; begin Add([ '{$mode objfpc}', 'type', ' TObject = class', ' generic function Get(val: T) :T;', ' type TBird = word;', ' generic procedure Fly;', ' const C = 1;', ' generic procedure Run;', ' end;', 'generic function TObject.Get(val: T) :T;', 'begin', 'end;', 'begin', ' TObject.specialize GetIt(2);', '']); ParseModule; end; procedure TTestGenerics.TestGenericMethod_OverloadDelphi; begin Add([ '{$mode delphi}', 'type', ' TObject = class', ' procedure Fly; overload;', ' procedure Fly(val: T); overload;', ' end;', 'procedure TObject.Fly;', 'begin', 'end;', 'procedure TObject.Fly(val: word);', 'begin', 'end;', 'var o : TObject;', 'begin', ' o.Fly;', ' o.Fly();', ' o.Fly(3);', ' with o do begin', ' Fly;', ' Fly();', ' Fly(13);', ' end;', '']); ParseModule; end; initialization RegisterTest(TTestGenerics); end.