View Issue Details

IDProjectCategoryView StatusLast Update
0037396LazarusLCLpublic2020-07-23 07:29
ReporterImants Gulbis Assigned ToJuha Manninen  
PrioritynormalSeverityminorReproducibilityalways
Status assignedResolutionopen 
Summary0037396: LResources can't find resource file for generic form
DescriptionIf form or data module is made generics then lazarus do not have any problems to create it in design time but in runtime I get message that resource is not found. Reason is because to find resource classname is used but class name contains generics section and resource with such a name do not exist. I attached patch where LResources will remove "<>" section and will be able to find and load resource even for generics form.
TagsNo tags attached.
Fixed in Revisionr63627, r63628
LazTarget-
Widgetset
Attached Files

Activities

Imants Gulbis

2020-07-21 07:23

reporter  

lresources.pp.patch (540 bytes)   
Index: lcl/lresources.pp
===================================================================
--- lcl/lresources.pp	(revision 63553)
+++ lcl/lresources.pp	(working copy)
@@ -3127,6 +3127,9 @@
       
     Stream := nil;
     ResName := ClassType.ClassName;
+    //In case of generics class class name could contain <> and resource files do not support this
+    if ResName.IndexOf('<') > 0 then
+      ResName := ResName.Substring(0, ResName.IndexOf('<'));
     
     {$ifdef UseLRS}
     LazResource := LazarusResources.Find(ResName);
lresources.pp.patch (540 bytes)   

Imants Gulbis

2020-07-21 09:22

reporter   ~0124200

I attached patch for code tools to so that it can read right ancestor class in case if ancestor is specialized
like TChildForm = class(specialize TParentForm<String>);
codetools.patch (1,029 bytes)   
Index: components/codetools/basiccodetools.pas
===================================================================
--- components/codetools/basiccodetools.pas	(revision 63553)
+++ components/codetools/basiccodetools.pas	(working copy)
@@ -1204,6 +1204,8 @@
   if SearchCodeInSource(Source,FormClassName+'=class(',1,SrcPos,false)<1 then
     exit;
   Result:=ReadNextPascalAtom(Source,SrcPos,AtomStart);
+  if CompareText(Result, 'specialize') = 0 then
+    Result:=ReadNextPascalAtom(Source,SrcPos,AtomStart);
   if not IsValidIdent(Result) then
     Result:='';
 end;
Index: components/codetools/stdcodetools.pas
===================================================================
--- components/codetools/stdcodetools.pas	(revision 63553)
+++ components/codetools/stdcodetools.pas	(working copy)
@@ -3114,6 +3114,8 @@
   ReadNextAtom;
   if AtomIsChar('(') then begin
     ReadNextAtom;
+    if UpAtomIs('SPECIALIZE') then
+      ReadNextAtom;
     if AtomIsIdentifier then
       AncestorClassName:=GetAtom;
   end;
codetools.patch (1,029 bytes)   

Sven Barth

2020-07-21 12:05

manager   ~0124202

Stripping away the type arguments will lead to loading the wrong resource if a non-generic form with that name exists.

E.g. TMyForm = class(specialize TSomeForm<String>)

If there is a TSomeForm without any generic parameters (e.g. in a different unit, but the same project) then the wrong resource would be loaded (or there will already be a problem generating the resources).

The same is also true if there is a TSomeForm with a different amount of type parameters (maybe also in a different unit or the same unit in mode Delphi, which allows type overloads).

So best would be to find a way to include the generic parameters in the resource name as well (the compiler internally uses TGenericForm$1 for TGenericForm<T>).

Imants Gulbis

2020-07-21 12:25

reporter   ~0124204

Last edited: 2020-07-21 12:27

View 2 revisions

Problem with including generic names in resource file is that in resource file it is possible to include them with generic names like.

Example:
TGenericForm<T> can only be included in resource file with name TGenericForm<T>
but when we are searching for resource file name will be TGenericForm<String>, TGenericForm<Integer> e.c. So I do not think this will help much.

Second.
Lazarus do not allow to create in same project two forms with same name even if one of forms is generic. I just tried it now and I got:

There is already a form with the name "EditForm" with value "EditForm"

Where one form was defined as
generic TEditForm<T> = class(TRttiFm)
and
I tried to name second form just TEditForm = class(TForm)

Juha Manninen

2020-07-21 12:40

developer   ~0124205

> TGenericForm<T> can only be included in resource file with name TGenericForm<T> but when
> we are searching for resource file name will be TGenericForm<String>, TGenericForm<Integer> e.c.

Can all of them have resources? I mean TGenericForm<T>, TGenericForm<String> and TGenericForm<Integer>.
In that case the logic would be to search for specialized type resource first and then generic type resource if not found.

Juha Manninen

2020-07-21 12:49

developer   ~0124206

Last edited: 2020-07-21 12:49

View 2 revisions

> I attached patch for code tools to so that it can read right ancestor class in case
> if ancestor is specialized like TChildForm = class(specialize TParentForm<String>);

This is a different issue. Please attach an example unit that confuses Codetools. Here it works well.
Did you test with Lazarus trunk? You forgot to mention your Lazarus version.

Imants Gulbis

2020-07-21 13:02

reporter   ~0124207

>Can all of them have resources? I mean TGenericForm<T>, TGenericForm<String> and TGenericForm<Integer>.
>In that case the logic would be to search for specialized type resource first and then generic type resource if not found.

Only TGenericForm needs to have resources because nothing in resource file can be generics. TGenericForm<String> and TGenericForm<Integer> will use same resource file and initialize same components there should be no difference between specialization in regards of resource file.

>This is a different issue. Please attach an example unit that confuses Codetools. Here it works well.
>Did you test with Lazarus trunk? You forgot to mention your Lazarus version.

I am using Lazarus trunk.
When I use
Form := specialize TGenericForm<String>.Create(Owner)
at runtime with my fix everything worked fine. Form was created and right resource file was found. But if I tired to inherit from my generic form like

TAnotherForm = class(specialize TGenericForm<String>)
I got error from codetools that ansestor specialize not found
if I switch to delphi mode and write
TAnotherForm = class(TGenericForm<String>)
everything worked

Juha Manninen

2020-07-21 20:34

developer   ~0124216

Last edited: 2020-07-21 21:09

View 2 revisions

> Form := specialize TGenericForm<String>.Create(Owner)

What is the type of "Form" in that example?
I must ask you again to upload a unit or simple program that FPC accepts but Codetools does not. I am not able to reproduce the problem. I am not an expert with generics but that is another issue.

Please also upload an example project for the resource issue. I guess the same project can demonstrate the Codetools problem.

Imants Gulbis

2020-07-22 14:06

reporter   ~0124228

I added simple example of problem. When I try to open unit2 I got error shown in error.png
minimal.example.zip (108,947 bytes)
error.png (7,817 bytes)   
error.png (7,817 bytes)   

Imants Gulbis

2020-07-22 15:27

reporter   ~0124230

I updated example with resource issue too. When button is presed you will get error in error2.png
error2.png (9,278 bytes)   
error2.png (9,278 bytes)   
minimal.example.2.zip (109,163 bytes)

Juha Manninen

2020-07-22 22:25

developer   ~0124240

Last edited: 2020-07-23 00:47

View 2 revisions

Thank you for the example projects. I was able to reproduce the problems.

I applied the StdCodetools part in r63627. I left out the BasicCodetools part because it never triggered in my tests. It may even be dead code. If you find code that triggers it, I will debug more.

I applied the LResources patch in r63628. It makes the IDE usable with generic / specialized forms. The comment by Sven is valid :
 > So best would be to find a way to include the generic parameters in the resource name as well
 > (the compiler internally uses TGenericForm$1 for TGenericForm<T>).
I keep this report open and return to it later, after asking advice from others. Patches are welcome of course. :)

Imants Gulbis

2020-07-23 07:29

reporter   ~0124248

Both places I found from CodeToolManager.pas in TCodeToolManager.FindFormAncestor method.

function TCodeToolManager.FindFormAncestor(Code: TCodeBuffer;
  const FormClassName: string; var AncestorClassName: string;
  DirtySearch: boolean): boolean;
begin
  Result:=false;
  {$IFDEF CTDEBUG}
  DebugLn('TCodeToolManager.FindFormAncestor A ',Code.Filename,' ',FormClassName);
  {$ENDIF}
  AncestorClassName:='';
  if not InitCurCodeTool(Code) then exit;
  try
    Result:=FCurCodeTool.FindFormAncestor(FormClassName,AncestorClassName);
  except
    on e: Exception do Result:=HandleException(e);
  end;
  if (not Result) and DirtySearch then begin
    AncestorClassName:=FindClassAncestorName(Code.Source,FormClassName);
    Result:=AncestorClassName<>'';
  end;
end;

I think code tools firs try to use TStandartCodeTool.FindFormAncestor and if it fails and DirtySearch search is on then FindClassAncestorName function from BasicCodeTools is used.
Maybe it is dead code I did not trigger it myself.

Issue History

Date Modified Username Field Change
2020-07-21 07:23 Imants Gulbis New Issue
2020-07-21 07:23 Imants Gulbis File Added: lresources.pp.patch
2020-07-21 09:19 Marco van de Voort Project FPC => Lazarus
2020-07-21 09:22 Imants Gulbis Note Added: 0124200
2020-07-21 09:22 Imants Gulbis File Added: codetools.patch
2020-07-21 12:05 Sven Barth Note Added: 0124202
2020-07-21 12:25 Imants Gulbis Note Added: 0124204
2020-07-21 12:27 Imants Gulbis Note Edited: 0124204 View Revisions
2020-07-21 12:40 Juha Manninen Note Added: 0124205
2020-07-21 12:49 Juha Manninen Note Added: 0124206
2020-07-21 12:49 Juha Manninen Note Edited: 0124206 View Revisions
2020-07-21 13:02 Imants Gulbis Note Added: 0124207
2020-07-21 20:34 Juha Manninen Note Added: 0124216
2020-07-21 21:09 Juha Manninen Note Edited: 0124216 View Revisions
2020-07-22 14:06 Imants Gulbis Note Added: 0124228
2020-07-22 14:06 Imants Gulbis File Added: minimal.example.zip
2020-07-22 14:06 Imants Gulbis File Added: error.png
2020-07-22 15:27 Imants Gulbis Note Added: 0124230
2020-07-22 15:27 Imants Gulbis File Added: error2.png
2020-07-22 15:27 Imants Gulbis File Added: minimal.example.2.zip
2020-07-22 21:31 Juha Manninen Assigned To => Juha Manninen
2020-07-22 21:31 Juha Manninen Status new => assigned
2020-07-22 22:25 Juha Manninen Note Added: 0124240
2020-07-22 22:26 Juha Manninen Product Version 3.3.1 =>
2020-07-22 22:26 Juha Manninen Fixed in Revision => r63627, r63628
2020-07-22 22:26 Juha Manninen LazTarget => -
2020-07-23 00:47 Juha Manninen Note Edited: 0124240 View Revisions
2020-07-23 07:29 Imants Gulbis Note Added: 0124248