fcl-passrc: added some advanced record tests

git-svn-id: trunk@40639 -
This commit is contained in:
Mattias Gaertner 2018-12-25 07:19:34 +00:00
parent 5c5a524cd4
commit 18d4e36361
4 changed files with 174 additions and 36 deletions

View File

@ -5904,6 +5904,7 @@ end;
procedure TPasResolver.FinishProperty(PropEl: TPasProperty); procedure TPasResolver.FinishProperty(PropEl: TPasProperty);
var var
PropType: TPasType; PropType: TPasType;
ClassOrRecScope: TPasClassOrRecordScope;
ClassScope: TPasClassScope; ClassScope: TPasClassScope;
AncestorProp: TPasProperty; AncestorProp: TPasProperty;
IndexExpr: TPasExpr; IndexExpr: TPasExpr;
@ -5914,7 +5915,7 @@ var
begin begin
if PropType<>nil then exit; if PropType<>nil then exit;
AncEl:=nil; AncEl:=nil;
if ClassScope.AncestorScope<>nil then if (ClassScope<>nil) and (ClassScope.AncestorScope<>nil) then
AncEl:=ClassScope.AncestorScope.FindElement(PropEl.Name); AncEl:=ClassScope.AncestorScope.FindElement(PropEl.Name);
if AncEl is TPasProperty then if AncEl is TPasProperty then
begin begin
@ -5943,7 +5944,7 @@ var
// get inherited type // get inherited type
PropType:=GetPasPropertyType(AncestorProp); PropType:=GetPasPropertyType(AncestorProp);
// update DefaultProperty // update DefaultProperty
if (ClassScope.DefaultProperty=AncestorProp) then if ClassScope.DefaultProperty=AncestorProp then
ClassScope.DefaultProperty:=PropEl; ClassScope.DefaultProperty:=PropEl;
end; end;
end; end;
@ -6232,7 +6233,7 @@ var
var var
ResultType: TPasType; ResultType: TPasType;
CurClassType: TPasClassType; MembersType: TPasMembersType;
AccEl: TPasElement; AccEl: TPasElement;
Proc: TPasProcedure; Proc: TPasProcedure;
Arg: TPasArgument; Arg: TPasArgument;
@ -6253,8 +6254,12 @@ begin
['published property','"'+VariableModifierNames[m]+'"'],PropEl); ['published property','"'+VariableModifierNames[m]+'"'],PropEl);
PropType:=nil; PropType:=nil;
CurClassType:=PropEl.Parent as TPasClassType; MembersType:=PropEl.Parent as TPasMembersType;
ClassScope:=NoNil(CurClassType.CustomData) as TPasClassScope; ClassOrRecScope:=NoNil(MembersType.CustomData) as TPasClassOrRecordScope;
if ClassOrRecScope is TPasClassScope then
ClassScope:=TPasClassScope(ClassOrRecScope)
else
ClassScope:=nil;
AncestorProp:=nil; AncestorProp:=nil;
GetPropType; GetPropType;
IndexVal:=nil; IndexVal:=nil;
@ -6461,10 +6466,10 @@ begin
if PropEl.IsDefault then if PropEl.IsDefault then
begin begin
// set default array property // set default array property
if (ClassScope.DefaultProperty<>nil) if (ClassOrRecScope.DefaultProperty<>nil)
and (ClassScope.DefaultProperty.Parent=PropEl.Parent) then and (ClassOrRecScope.DefaultProperty.Parent=PropEl.Parent) then
RaiseMsg(20170216151938,nOnlyOneDefaultPropertyIsAllowed,sOnlyOneDefaultPropertyIsAllowed,[],PropEl); RaiseMsg(20170216151938,nOnlyOneDefaultPropertyIsAllowed,sOnlyOneDefaultPropertyIsAllowed,[],PropEl);
ClassScope.DefaultProperty:=PropEl; ClassOrRecScope.DefaultProperty:=PropEl;
end; end;
EmitTypeHints(PropEl,PropEl.VarType); EmitTypeHints(PropEl,PropEl.VarType);
finally finally
@ -14759,7 +14764,7 @@ procedure TPasResolver.CheckFoundElement(
var var
Proc: TPasProcedure; Proc: TPasProcedure;
Context: TPasElement; Context: TPasElement;
FoundContext: TPasClassType; FoundContext: TPasMembersType;
StartScope: TPasScope; StartScope: TPasScope;
OnlyTypeMembers, IsClassOf: Boolean; OnlyTypeMembers, IsClassOf: Boolean;
TypeEl: TPasType; TypeEl: TPasType;
@ -14956,7 +14961,7 @@ begin
if FindData.Found.Visibility in [visPrivate,visProtected,visStrictPrivate,visStrictProtected] then if FindData.Found.Visibility in [visPrivate,visProtected,visStrictPrivate,visStrictProtected] then
begin begin
Context:=GetVisibilityContext; Context:=GetVisibilityContext;
FoundContext:=FindData.Found.Parent as TPasClassType; FoundContext:=FindData.Found.Parent as TPasMembersType;
case FindData.Found.Visibility of case FindData.Found.Visibility of
visPrivate: visPrivate:
// private members can only be accessed in same module // private members can only be accessed in same module

View File

@ -253,6 +253,8 @@ procedure TTestClassType.StartClass(AncestorName: String; InterfaceList: String)
Var Var
S : String; S : String;
begin begin
if FStarted then
Fail('TTestClassType.StartClass already started');
FStarted:=True; FStarted:=True;
S:='TMyClass = Class'; S:='TMyClass = Class';
if (AncestorName<>'') then if (AncestorName<>'') then
@ -426,7 +428,7 @@ end;
procedure TTestClassType.SetUp; procedure TTestClassType.SetUp;
begin begin
inherited SetUp; inherited SetUp;
FDecl:=TstringList.Create; FDecl:=TStringList.Create;
FClass:=Nil; FClass:=Nil;
FParent:=''; FParent:='';
FStarted:=False; FStarted:=False;

View File

@ -488,10 +488,11 @@ type
// advanced record // advanced record
Procedure TestAdvRecord; Procedure TestAdvRecord;
Procedure TestAdvRecord_Private; // ToDo Procedure TestAdvRecord_Private;
// ToDO: Procedure TestAdvRecord_PropertyWithoutTypeFail;
// Todo: Procedure TestAdvRecord_ForwardFail // Todo: Procedure TestAdvRecord_ForwardFail
// ToDo: public, private, strict private // ToDo: public, private, strict private
// ToDo: TestAdvRecordPublsihedFail // ToDo: TestAdvRecordPublishedFail
// ToDo: TestAdvRecord_VirtualFail // ToDo: TestAdvRecord_VirtualFail
// ToDo: TestAdvRecord_OverrideFail // ToDo: TestAdvRecord_OverrideFail
// ToDo: constructor, destructor // ToDo: constructor, destructor
@ -7840,8 +7841,6 @@ end;
procedure TTestResolver.TestAdvRecord_Private; procedure TTestResolver.TestAdvRecord_Private;
begin begin
exit;
StartProgram(false); StartProgram(false);
Add([ Add([
'{$modeswitch advancedrecords}', '{$modeswitch advancedrecords}',

View File

@ -171,16 +171,30 @@ type
{ TTestRecordTypeParser } { TTestRecordTypeParser }
TTestRecordTypeParser= Class(TBaseTestTypeParser) TTestRecordTypeParser = Class(TBaseTestTypeParser)
private private
FDecl : TStrings;
FAdvanced,
FEnded,
FStarted: boolean;
FRecord: TPasRecordType;
FMember1: TPasElement;
function GetC(AIndex: Integer): TPasConst; function GetC(AIndex: Integer): TPasConst;
Function GetField(AIndex : Integer; R : TPasRecordType) : TPasVariable; Function GetField(AIndex : Integer; R : TPasRecordType) : TPasVariable;
Function GetField(AIndex : Integer; R : TPasVariant) : TPasVariable; Function GetField(AIndex : Integer; R : TPasVariant) : TPasVariable;
function GetF(AIndex: Integer): TPasVariable; function GetF(AIndex: Integer): TPasVariable;
function GetR: TPasRecordType; function GetM(AIndex : Integer): TPasElement;
Function GetVariant(AIndex : Integer; R : TPasRecordType) : TPasVariant; Function GetVariant(AIndex : Integer; R : TPasRecordType) : TPasVariant;
function GetV(AIndex: Integer): TPasVariant; function GetV(AIndex: Integer): TPasVariant;
Protected Protected
procedure SetUp; override;
procedure TearDown; override;
Procedure StartRecord(Advanced: boolean = false);
Procedure EndRecord(AEnd : String = 'end');
Procedure AddMember(S : String);
Procedure ParseRecord;
Procedure ParseRecordFail(Msg: string; MsgNumber: integer);
Procedure DoParseRecord;
Procedure TestFields(Const Fields : Array of string; AHint : String; HaveVariant : Boolean = False); Procedure TestFields(Const Fields : Array of string; AHint : String; HaveVariant : Boolean = False);
procedure AssertVariantSelector(AName, AType: string); procedure AssertVariantSelector(AName, AType: string);
procedure AssertConst1(Hints: TPasMemberHints); procedure AssertConst1(Hints: TPasMemberHints);
@ -216,12 +230,15 @@ type
procedure DoTestVariantNestedVariantFirstDeprecated(Const AHint : string); procedure DoTestVariantNestedVariantFirstDeprecated(Const AHint : string);
procedure DoTestVariantNestedVariantSecondDeprecated(const AHint: string); procedure DoTestVariantNestedVariantSecondDeprecated(const AHint: string);
procedure DoTestVariantNestedVariantBothDeprecated(const AHint: string); procedure DoTestVariantNestedVariantBothDeprecated(const AHint: string);
Property TheRecord : TPasRecordType Read GetR; Property TheRecord : TPasRecordType Read FRecord;
Property Advanced: boolean read FAdvanced;
Property Const1 : TPasConst Index 0 Read GetC; Property Const1 : TPasConst Index 0 Read GetC;
Property Field1 : TPasVariable Index 0 Read GetF; Property Field1 : TPasVariable Index 0 Read GetF;
Property Field2 : TPasVariable Index 1 Read GetF; Property Field2 : TPasVariable Index 1 Read GetF;
Property Variant1 : TPasVariant Index 0 Read GetV; Property Variant1 : TPasVariant Index 0 Read GetV;
Property Variant2 : TPasVariant Index 1 Read GetV; Property Variant2 : TPasVariant Index 1 Read GetV;
Property Members[AIndex : Integer] : TPasElement Read GetM;
Property Member1 : TPasElement Read FMember1;
Published Published
Procedure TestEmpty; Procedure TestEmpty;
Procedure TestEmptyComment; Procedure TestEmptyComment;
@ -333,6 +350,9 @@ type
Procedure TestVariantNestedVariantBothDeprecatedDeprecated; Procedure TestVariantNestedVariantBothDeprecatedDeprecated;
Procedure TestVariantNestedVariantBothDeprecatedPlatform; Procedure TestVariantNestedVariantBothDeprecatedPlatform;
Procedure TestOperatorField; Procedure TestOperatorField;
Procedure TestPropertyFail;
Procedure TestAdvRec_Property;
Procedure TestAdvRec_PropertyImplementsFail;
end; end;
{ TTestProcedureTypeParser } { TTestProcedureTypeParser }
@ -1148,7 +1168,7 @@ end;
function TTestRecordTypeParser.GetC(AIndex: Integer): TPasConst; function TTestRecordTypeParser.GetC(AIndex: Integer): TPasConst;
begin begin
Result:=TObject(GetR.Members[AIndex]) as TPasConst; Result:=TObject(TheRecord.Members[AIndex]) as TPasConst;
end; end;
function TTestRecordTypeParser.GetField(AIndex: Integer; R: TPasRecordType function TTestRecordTypeParser.GetField(AIndex: Integer; R: TPasRecordType
@ -1174,12 +1194,18 @@ end;
function TTestRecordTypeParser.GetF(AIndex: Integer): TPasVariable; function TTestRecordTypeParser.GetF(AIndex: Integer): TPasVariable;
begin begin
Result:=GetField(AIndex,GetR); Result:=GetField(AIndex,TheRecord);
end; end;
function TTestRecordTypeParser.GetR: TPasRecordType; function TTestRecordTypeParser.GetM(AIndex : Integer): TPasElement;
begin begin
Result:=TheType as TPasRecordType; AssertNotNull('Have Record',TheRecord);
if (AIndex>=TheRecord.Members.Count) then
Fail('No member '+IntToStr(AIndex));
AssertNotNull('Have member'+IntToStr(AIndex),TheRecord.Members[AIndex]);
If Not (TObject(TheRecord.Members[AIndex]) is TPasElement) then
Fail('Member '+IntTostr(AIndex)+' is not a TPasElement');
Result:=TPasElement(TheRecord.Members[AIndex])
end; end;
function TTestRecordTypeParser.GetVariant(AIndex: Integer; R: TPasRecordType function TTestRecordTypeParser.GetVariant(AIndex: Integer; R: TPasRecordType
@ -1194,7 +1220,94 @@ end;
function TTestRecordTypeParser.GetV(AIndex: Integer): TPasVariant; function TTestRecordTypeParser.GetV(AIndex: Integer): TPasVariant;
begin begin
Result:=GetVariant(AIndex,GetR); Result:=GetVariant(AIndex,TheRecord);
end;
procedure TTestRecordTypeParser.SetUp;
begin
inherited SetUp;
FDecl:=TStringList.Create;
FStarted:=false;
FEnded:=false;
end;
procedure TTestRecordTypeParser.TearDown;
begin
FreeAndNil(FDecl);
inherited TearDown;
end;
procedure TTestRecordTypeParser.StartRecord(Advanced: boolean);
var
S: String;
begin
if FStarted then
Fail('TTestRecordTypeParser.StartRecord already started');
FStarted:=True;
S:='TMyRecord = record';
if Advanced then
S:='{$modeswitch advancedrecords}'+sLineBreak+S;
FDecl.Add(S);
end;
procedure TTestRecordTypeParser.EndRecord(AEnd: String);
begin
if FEnded then exit;
if not FStarted then
StartRecord;
FEnded:=True;
if (AEnd<>'') then
FDecl.Add(' '+AEnd);
end;
procedure TTestRecordTypeParser.AddMember(S: String);
begin
if Not FStarted then
StartRecord;
FDecl.Add(' '+S);
end;
procedure TTestRecordTypeParser.ParseRecord;
begin
DoParseRecord;
end;
procedure TTestRecordTypeParser.ParseRecordFail(Msg: string; MsgNumber: integer
);
var
ok: Boolean;
begin
ok:=false;
try
ParseRecord;
except
on E: EParserError do
begin
AssertEquals('Expected {'+Msg+'}, but got msg {'+Parser.LastMsg+'}',MsgNumber,Parser.LastMsgNumber);
ok:=true;
end;
end;
AssertEquals('Missing parser error {'+Msg+'} ('+IntToStr(MsgNumber)+')',true,ok);
end;
procedure TTestRecordTypeParser.DoParseRecord;
begin
EndRecord;
Add('Type');
if AddComment then
begin
Add('// A comment');
Engine.NeedComments:=True;
end;
Add(' '+TrimRight(FDecl.Text)+';');
ParseDeclarations;
AssertEquals('One record type definition',1,Declarations.Types.Count);
AssertEquals('First declaration is type definition.',TPasRecordType,TObject(Declarations.Types[0]).ClassType);
FRecord:=TObject(Declarations.Types[0]) as TPasRecordType;
TheType:=FRecord; // needed by AssertComment
Definition:=TheType; // needed by CheckHint
if TheRecord.Members.Count>0 then
FMember1:=TObject(TheRecord.Members[0]) as TPasElement;
end; end;
procedure TTestRecordTypeParser.TestFields(const Fields: array of string; procedure TTestRecordTypeParser.TestFields(const Fields: array of string;
@ -1205,17 +1318,14 @@ Var
I : integer; I : integer;
begin begin
S:=''; StartRecord;
For I:=Low(Fields) to High(Fields) do For I:=Low(Fields) to High(Fields) do
begin AddMember(Fields[i]);
if (S<>'') then S:='end';
S:=S+sLineBreak; if AHint<>'' then
S:=S+' '+Fields[i]; S:=S+' '+AHint;
end; EndRecord(S);
if (S<>'') then ParseRecord;
S:=S+sLineBreak;
S:='record'+sLineBreak+s+' end';
ParseType(S,TPasRecordType,AHint);
if HaveVariant then if HaveVariant then
begin begin
AssertNotNull('Have variants',TheRecord.Variants); AssertNotNull('Have variants',TheRecord.Variants);
@ -1228,6 +1338,8 @@ begin
end; end;
if AddComment then if AddComment then
AssertComment; AssertComment;
if (AHint<>'') then
CheckHint(TPasMemberHint(GetEnumValue(TypeInfo(TPasMemberHint),'h'+AHint)));
end; end;
procedure TTestRecordTypeParser.AssertVariantSelector(AName,AType : string); procedure TTestRecordTypeParser.AssertVariantSelector(AName,AType : string);
@ -2411,6 +2523,26 @@ begin
AssertEquals('Field 1 name','operator',Field1.Name); AssertEquals('Field 1 name','operator',Field1.Name);
end; end;
procedure TTestRecordTypeParser.TestPropertyFail;
begin
AddMember('Property Something');
ParseRecordFail(SErrRecordPropertiesNotAllowed,nErrRecordPropertiesNotAllowed);
end;
procedure TTestRecordTypeParser.TestAdvRec_Property;
begin
StartRecord(true);
AddMember('Property Something: word');
ParseRecord;
end;
procedure TTestRecordTypeParser.TestAdvRec_PropertyImplementsFail;
begin
StartRecord(true);
AddMember('Property Something: word implements ISome;');
ParseRecordFail('Expected ";"',nParserExpectTokenError);
end;
{ TBaseTestTypeParser } { TBaseTestTypeParser }
Function TBaseTestTypeParser.ParseType(ASource: String; ATypeClass: TClass; Function TBaseTestTypeParser.ParseType(ASource: String; ATypeClass: TClass;
@ -2437,9 +2569,9 @@ begin
AssertEquals('One type definition',1,Declarations.Classes.Count) AssertEquals('One type definition',1,Declarations.Classes.Count)
else else
AssertEquals('One type definition',1,Declarations.Types.Count); AssertEquals('One type definition',1,Declarations.Types.Count);
If (AtypeClass<>Nil) then If ATypeClass<>Nil then
begin begin
if ATypeClass.InHeritsFrom(TPasClassType) then if ATypeClass.InheritsFrom(TPasClassType) then
Result:=TPasType(Declarations.Classes[0]) Result:=TPasType(Declarations.Classes[0])
else else
Result:=TPasType(Declarations.Types[0]); Result:=TPasType(Declarations.Types[0]);
@ -2449,7 +2581,7 @@ begin
FType:=Result; FType:=Result;
Definition:=Result; Definition:=Result;
if (Hint<>'') then if (Hint<>'') then
CheckHint(TPasMemberHint(Getenumvalue(typeinfo(TPasMemberHint),'h'+Hint))); CheckHint(TPasMemberHint(GetEnumValue(TypeInfo(TPasMemberHint),'h'+Hint)));
end; end;
Procedure TBaseTestTypeParser.AssertParseTypeError(ASource: String); Procedure TBaseTestTypeParser.AssertParseTypeError(ASource: String);