|
@@ -38,6 +38,7 @@ type
|
|
Row: cardinal;
|
|
Row: cardinal;
|
|
StartCol, EndCol: integer; // token start, end column
|
|
StartCol, EndCol: integer; // token start, end column
|
|
Identifier: string;
|
|
Identifier: string;
|
|
|
|
+ Param: string;
|
|
Next: PSrcMarker;
|
|
Next: PSrcMarker;
|
|
end;
|
|
end;
|
|
|
|
|
|
@@ -154,6 +155,9 @@ type
|
|
procedure CheckAccessMarkers; virtual;
|
|
procedure CheckAccessMarkers; virtual;
|
|
procedure CheckParamsExpr_pkSet_Markers; virtual;
|
|
procedure CheckParamsExpr_pkSet_Markers; virtual;
|
|
procedure CheckAttributeMarkers; virtual;
|
|
procedure CheckAttributeMarkers; virtual;
|
|
|
|
+ procedure CheckRTTIVisibility(aMarker: PSrcMarker; El: TPasMembersType; Explicit: boolean;
|
|
|
|
+ const ExpectedFields, ExpectedMethods, ExpectedProperties: TPasMembersType.TRTTIVisibilitySections); virtual;
|
|
|
|
+ procedure CheckRTTIVisibilityMarkers; virtual;
|
|
procedure GetSrc(Index: integer; out SrcLines: TStringList; out aFilename: string);
|
|
procedure GetSrc(Index: integer; out SrcLines: TStringList; out aFilename: string);
|
|
function FindElementsAt(aFilename: string; aLine, aStartCol, aEndCol: integer): TFPList;// list of TPasElement
|
|
function FindElementsAt(aFilename: string; aLine, aStartCol, aEndCol: integer): TFPList;// list of TPasElement
|
|
function FindElementsAt(aMarker: PSrcMarker; ErrorOnNoElements: boolean = true): TFPList;// list of TPasElement
|
|
function FindElementsAt(aMarker: PSrcMarker; ErrorOnNoElements: boolean = true): TFPList;// list of TPasElement
|
|
@@ -665,6 +669,7 @@ type
|
|
Procedure TestClass_TypeAlias;
|
|
Procedure TestClass_TypeAlias;
|
|
Procedure TestClass_Message;
|
|
Procedure TestClass_Message;
|
|
Procedure TestClass_Message_MissingParamFail;
|
|
Procedure TestClass_Message_MissingParamFail;
|
|
|
|
+ Procedure TestClass_ExtRTTI_Explicit;
|
|
|
|
|
|
// published
|
|
// published
|
|
Procedure TestClass_PublishedClassVarFail;
|
|
Procedure TestClass_PublishedClassVarFail;
|
|
@@ -1317,7 +1322,7 @@ var
|
|
end;
|
|
end;
|
|
|
|
|
|
function AddMarker(Kind: TSrcMarkerKind; const aFilename: string;
|
|
function AddMarker(Kind: TSrcMarkerKind; const aFilename: string;
|
|
- aLine, aStartCol, aEndCol: integer; const Identifier: string): PSrcMarker;
|
|
|
|
|
|
+ aLine, aStartCol, aEndCol: integer; const Identifier, Param: string): PSrcMarker;
|
|
begin
|
|
begin
|
|
New(Result);
|
|
New(Result);
|
|
Result^.Kind:=Kind;
|
|
Result^.Kind:=Kind;
|
|
@@ -1326,20 +1331,21 @@ var
|
|
Result^.StartCol:=aStartCol;
|
|
Result^.StartCol:=aStartCol;
|
|
Result^.EndCol:=aEndCol;
|
|
Result^.EndCol:=aEndCol;
|
|
Result^.Identifier:=Identifier;
|
|
Result^.Identifier:=Identifier;
|
|
|
|
+ Result^.Param:=Param;
|
|
Result^.Next:=nil;
|
|
Result^.Next:=nil;
|
|
//writeln('AddMarker Line="',SrcLine,'" Identifier=',Identifier,' Col=',aStartCol,'-',aEndCol,' "',copy(SrcLine,aStartCol,aEndCol-aStartCol),'"');
|
|
//writeln('AddMarker Line="',SrcLine,'" Identifier=',Identifier,' Col=',aStartCol,'-',aEndCol,' "',copy(SrcLine,aStartCol,aEndCol-aStartCol),'"');
|
|
AddMarker(Result);
|
|
AddMarker(Result);
|
|
end;
|
|
end;
|
|
|
|
|
|
function AddMarkerForTokenBehindComment(Kind: TSrcMarkerKind;
|
|
function AddMarkerForTokenBehindComment(Kind: TSrcMarkerKind;
|
|
- const Identifier: string): PSrcMarker;
|
|
|
|
|
|
+ const Identifier, Param: string): PSrcMarker;
|
|
var
|
|
var
|
|
TokenStart, p: PChar;
|
|
TokenStart, p: PChar;
|
|
begin
|
|
begin
|
|
p:=CommentEndP;
|
|
p:=CommentEndP;
|
|
ReadNextPascalToken(p,TokenStart,false,false);
|
|
ReadNextPascalToken(p,TokenStart,false,false);
|
|
Result:=AddMarker(Kind,Filename,LineNumber,
|
|
Result:=AddMarker(Kind,Filename,LineNumber,
|
|
- CommentEndP-PChar(SrcLine)+1,p-PChar(SrcLine)+1,Identifier);
|
|
|
|
|
|
+ CommentEndP-PChar(SrcLine)+1,p-PChar(SrcLine)+1,Identifier,Param);
|
|
end;
|
|
end;
|
|
|
|
|
|
function ReadIdentifier(var p: PChar): string;
|
|
function ReadIdentifier(var p: PChar): string;
|
|
@@ -1356,9 +1362,17 @@ var
|
|
Move(StartP^,Result[1],length(Result)*SizeOf(Char));
|
|
Move(StartP^,Result[1],length(Result)*SizeOf(Char));
|
|
end;
|
|
end;
|
|
|
|
|
|
|
|
+ function ReadParam(p: PChar): string;
|
|
|
|
+ begin
|
|
|
|
+ while p^ in [' ',#9,#10,#13] do inc(p);
|
|
|
|
+ SetLength(Result{%H-},CommentEndP-p-1);
|
|
|
|
+ if Result>'' then
|
|
|
|
+ Move(p^,Result[1],length(Result)*SizeOf(Char));
|
|
|
|
+ end;
|
|
|
|
+
|
|
procedure AddLabel;
|
|
procedure AddLabel;
|
|
var
|
|
var
|
|
- Identifier: String;
|
|
|
|
|
|
+ Identifier, Param: String;
|
|
p: PChar;
|
|
p: PChar;
|
|
begin
|
|
begin
|
|
p:=CommentStartP+2;
|
|
p:=CommentStartP+2;
|
|
@@ -1366,7 +1380,7 @@ var
|
|
//writeln('TTestResolver.CheckReferenceDirectives.AddLabel ',Identifier);
|
|
//writeln('TTestResolver.CheckReferenceDirectives.AddLabel ',Identifier);
|
|
if FindSrcLabel(Identifier)<>nil then
|
|
if FindSrcLabel(Identifier)<>nil then
|
|
RaiseError('duplicate label "'+Identifier+'"',p);
|
|
RaiseError('duplicate label "'+Identifier+'"',p);
|
|
- AddMarkerForTokenBehindComment(mkLabel,Identifier);
|
|
|
|
|
|
+ AddMarkerForTokenBehindComment(mkLabel,Identifier,ReadParam(p));
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure AddResolverReference;
|
|
procedure AddResolverReference;
|
|
@@ -1377,7 +1391,7 @@ var
|
|
p:=CommentStartP+2;
|
|
p:=CommentStartP+2;
|
|
Identifier:=ReadIdentifier(p);
|
|
Identifier:=ReadIdentifier(p);
|
|
//writeln('TTestResolver.CheckReferenceDirectives.AddReference ',Identifier);
|
|
//writeln('TTestResolver.CheckReferenceDirectives.AddReference ',Identifier);
|
|
- AddMarkerForTokenBehindComment(mkResolverReference,Identifier);
|
|
|
|
|
|
+ AddMarkerForTokenBehindComment(mkResolverReference,Identifier,ReadParam(p));
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure AddDirectReference;
|
|
procedure AddDirectReference;
|
|
@@ -1388,12 +1402,12 @@ var
|
|
p:=CommentStartP+2;
|
|
p:=CommentStartP+2;
|
|
Identifier:=ReadIdentifier(p);
|
|
Identifier:=ReadIdentifier(p);
|
|
//writeln('TTestResolver.CheckReferenceDirectives.AddDirectReference ',Identifier);
|
|
//writeln('TTestResolver.CheckReferenceDirectives.AddDirectReference ',Identifier);
|
|
- AddMarkerForTokenBehindComment(mkDirectReference,Identifier);
|
|
|
|
|
|
+ AddMarkerForTokenBehindComment(mkDirectReference,Identifier,ReadParam(p));
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure ParseCode(SrcLines: TStringList; aFilename: string);
|
|
procedure ParseCode(SrcLines: TStringList; aFilename: string);
|
|
var
|
|
var
|
|
- p,pstart,pend: PChar;
|
|
|
|
|
|
+ p,StartP,EndP: PChar;
|
|
IsDirective: Boolean;
|
|
IsDirective: Boolean;
|
|
begin
|
|
begin
|
|
//writeln('TTestResolver.CheckReferenceDirectives.ParseCode File=',aFilename);
|
|
//writeln('TTestResolver.CheckReferenceDirectives.ParseCode File=',aFilename);
|
|
@@ -1407,13 +1421,13 @@ var
|
|
if SrcLine='' then continue;
|
|
if SrcLine='' then continue;
|
|
//writeln('TTestResolver.CheckReferenceDirectives Line=',SrcLine);
|
|
//writeln('TTestResolver.CheckReferenceDirectives Line=',SrcLine);
|
|
|
|
|
|
- pstart:=PChar(SrcLine);
|
|
|
|
- pend:=pstart;
|
|
|
|
- inc(PEnd,length(SrcLine));
|
|
|
|
- p:=pstart;
|
|
|
|
|
|
+ StartP:=PChar(SrcLine);
|
|
|
|
+ EndP:=StartP;
|
|
|
|
+ inc(EndP,length(SrcLine));
|
|
|
|
+ p:=StartP;
|
|
repeat
|
|
repeat
|
|
case p^ of
|
|
case p^ of
|
|
- #0: if (p>=pend) then break;
|
|
|
|
|
|
+ #0: if (p>=EndP) then break;
|
|
'{':
|
|
'{':
|
|
begin
|
|
begin
|
|
CommentStartP:=p;
|
|
CommentStartP:=p;
|
|
@@ -1424,7 +1438,7 @@ var
|
|
repeat
|
|
repeat
|
|
case p^ of
|
|
case p^ of
|
|
#0:
|
|
#0:
|
|
- if (p>=pend) then
|
|
|
|
|
|
+ if (p>=EndP) then
|
|
begin
|
|
begin
|
|
// multi line comment
|
|
// multi line comment
|
|
if IsDirective then
|
|
if IsDirective then
|
|
@@ -1435,10 +1449,10 @@ var
|
|
SrcLine:=SrcLines[LineNumber-1];
|
|
SrcLine:=SrcLines[LineNumber-1];
|
|
//writeln('TTestResolver.CheckReferenceDirectives Comment Line=',SrcLine);
|
|
//writeln('TTestResolver.CheckReferenceDirectives Comment Line=',SrcLine);
|
|
until SrcLine<>'';
|
|
until SrcLine<>'';
|
|
- pstart:=PChar(SrcLine);
|
|
|
|
- pend:=pstart;
|
|
|
|
- inc(PEnd,length(SrcLine));
|
|
|
|
- p:=pstart;
|
|
|
|
|
|
+ StartP:=PChar(SrcLine);
|
|
|
|
+ EndP:=StartP;
|
|
|
|
+ inc(EndP,length(SrcLine));
|
|
|
|
+ p:=StartP;
|
|
continue;
|
|
continue;
|
|
end;
|
|
end;
|
|
'}':
|
|
'}':
|
|
@@ -2036,6 +2050,60 @@ begin
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
|
|
|
|
+procedure TCustomTestResolver.CheckRTTIVisibility(aMarker: PSrcMarker; El: TPasMembersType;
|
|
|
|
+ Explicit: boolean; const ExpectedFields, ExpectedMethods, ExpectedProperties: TPasMembersType.
|
|
|
|
+ TRTTIVisibilitySections);
|
|
|
|
+
|
|
|
|
+ procedure Check(const Types: string; const Expected, Actual: TPasMembersType.TRTTIVisibilitySections);
|
|
|
|
+ begin
|
|
|
|
+ if Expected=Actual then exit;
|
|
|
|
+ RaiseErrorAtSrcMarker(Types+' visibility expected '+dbgs(Expected)+', but found '+dbgs(Actual),aMarker);
|
|
|
|
+ end;
|
|
|
|
+
|
|
|
|
+begin
|
|
|
|
+ if Explicit<>El.RTTIVisibility.Explicit then
|
|
|
|
+ if Explicit then
|
|
|
|
+ RaiseErrorAtSrcMarker('rtti visibility explicit expected',aMarker)
|
|
|
|
+ else
|
|
|
|
+ RaiseErrorAtSrcMarker('rtti visibility inherit expected',aMarker);
|
|
|
|
+ Check('Fields',El.RTTIVisibility.Fields,ExpectedFields);
|
|
|
|
+ Check('Methods',El.RTTIVisibility.Methods,ExpectedMethods);
|
|
|
|
+ Check('Properties',El.RTTIVisibility.Properties,ExpectedProperties);
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+procedure TCustomTestResolver.CheckRTTIVisibilityMarkers;
|
|
|
|
+var
|
|
|
|
+ aMarker: PSrcMarker;
|
|
|
|
+ Elements: TFPList;
|
|
|
|
+ i: Integer;
|
|
|
|
+ Visibility: TPasMembersType.TRTTIVisibility;
|
|
|
|
+ MemberEl: TPasMembersType;
|
|
|
|
+begin
|
|
|
|
+ aMarker:=FirstSrcMarker;
|
|
|
|
+ while aMarker<>nil do
|
|
|
|
+ begin
|
|
|
|
+ if lowercase(LeftStr(aMarker^.Identifier,5))='rtti_' then
|
|
|
|
+ begin
|
|
|
|
+ //writeln('TTestResolver.CheckRTTIVisibilityMarkers ',aMarker^.Identifier,' "',aMarker^.Param,'" ',aMarker^.StartCol,' ',aMarker^.EndCol);
|
|
|
|
+ if not Parser.ParseRTTIDirective(aMarker^.Param,Visibility) then
|
|
|
|
+ RaiseErrorAtSrcMarker('invalid rtti marker',aMarker);
|
|
|
|
+ Elements:=FindElementsAt(aMarker);
|
|
|
|
+ try
|
|
|
|
+ i:=Elements.Count-1;
|
|
|
|
+ while (i>=0) and not (TPasElement(Elements[i]) is TPasMembersType) do dec(i);
|
|
|
|
+ if i<0 then
|
|
|
|
+ RaiseErrorAtSrcMarker('rtti marker not at membertype',aMarker);
|
|
|
|
+ MemberEl:=TPasMembersType(Elements[i]);
|
|
|
|
+ CheckRTTIVisibility(aMarker,MemberEl,Visibility.Explicit,
|
|
|
|
+ Visibility.Fields,Visibility.Methods,Visibility.Properties);
|
|
|
|
+ finally
|
|
|
|
+ Elements.Free;
|
|
|
|
+ end;
|
|
|
|
+ end;
|
|
|
|
+ aMarker:=aMarker^.Next;
|
|
|
|
+ end;
|
|
|
|
+end;
|
|
|
|
+
|
|
procedure TCustomTestResolver.GetSrc(Index: integer; out SrcLines: TStringList; out
|
|
procedure TCustomTestResolver.GetSrc(Index: integer; out SrcLines: TStringList; out
|
|
aFilename: string);
|
|
aFilename: string);
|
|
var
|
|
var
|
|
@@ -12015,6 +12083,27 @@ begin
|
|
CheckResolverException(sMessageHandlersInvalidParams,nMessageHandlersInvalidParams);
|
|
CheckResolverException(sMessageHandlersInvalidParams,nMessageHandlersInvalidParams);
|
|
end;
|
|
end;
|
|
|
|
|
|
|
|
+procedure TTestResolver.TestClass_ExtRTTI_Explicit;
|
|
|
|
+begin
|
|
|
|
+ Parser.Options:=Parser.Options+[po_CheckDirectiveRTTI];
|
|
|
|
+ StartProgram(false);
|
|
|
|
+ Add([
|
|
|
|
+ 'type',
|
|
|
|
+ ' {$RTTI explicit Fields([vcProtected,vcPublic])}',
|
|
|
|
+ ' {#rtti_TObject explicit Fields([vcProtected,vcPublic])}TObject = class',
|
|
|
|
+ ' end;',
|
|
|
|
+ ' {$RTTI explicit Fields([vcPrivate,vcProtected])}',
|
|
|
|
+ ' {#rtti_TAnimal explicit Fields([vcPrivate,vcProtected])}TAnimal = class',
|
|
|
|
+ ' end;',
|
|
|
|
+ ' {$RTTI inherit Fields([vcPublic])}',
|
|
|
|
+ ' {#rtti_TBird inherit Fields([vcPrivate,vcProtected,vcPublic])}TBird = class(TAnimal)',
|
|
|
|
+ ' end;',
|
|
|
|
+ 'begin',
|
|
|
|
+ '']);
|
|
|
|
+ ParseProgram;
|
|
|
|
+ CheckRTTIVisibilityMarkers;
|
|
|
|
+end;
|
|
|
|
+
|
|
procedure TTestResolver.TestClass_PublishedClassVarFail;
|
|
procedure TTestResolver.TestClass_PublishedClassVarFail;
|
|
begin
|
|
begin
|
|
StartProgram(false);
|
|
StartProgram(false);
|