Recent

Author Topic: Generic records // Hide instance methods // Hide static methods  (Read 1370 times)

AlexanderK

  • New Member
  • *
  • Posts: 12
I'm using the `Nullable<T>` type of the `nullable` unit which is implemented as an advanced generic record. The type has one `static` method - `Empty`, and the rest of the methods/properties are instance methods. When I access them via the type itself - `TNullable.` or `TNullableInt.`, I expect to see only the static method `Empty`, but I see all of them. When I access the methods via an instance - `a.`, I expect to see only the instance methods, but I see all of them. Why is this happening?
See the screenshots.
« Last Edit: August 17, 2025, 03:39:51 pm by AlexanderK »

jamie

  • Hero Member
  • *****
  • Posts: 7308
Re: Generic records // Hide instance methods // Hide static methods
« Reply #1 on: August 17, 2025, 04:01:37 pm »
it looks correct to me.

Open the Nullable unit and you can see they are all in a PUBLIC section, this means they are visible to your code.

Jamie
The only true wisdom is knowing you know nothing

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 11812
  • Debugger - SynEdit - and more
    • wiki
Re: Generic records // Hide instance methods // Hide static methods
« Reply #2 on: August 17, 2025, 05:01:37 pm »
When I access them via the type itself - `TNullable.` or `TNullableInt.`, I expect to see only the static method `Empty`, but I see all of them. When I access the methods via an instance - `a.`, I expect to see only the instance methods, but I see all of them. Why is this happening?

You are right, you can't call a normal (non class/static) method on the class. That wont compile.

But, if you have a variable of the type, then you can call any kind of method on it. (At least it compiles for me).

AlexanderK

  • New Member
  • *
  • Posts: 12
Re: Generic records // Hide instance methods // Hide static methods
« Reply #3 on: August 17, 2025, 05:35:05 pm »
I understand this, but what I didn't understand why unrelated methods present in the completion code. In other languages, like F#, this is not the case (which makes sense). I'm new to Lazarus/Free Pascal so I wanted to clear this point. I understand now that everything that appears in the Public section of a type will appear in the completion code. This is how the compiler works.
Thank you!

Warfley

  • Hero Member
  • *****
  • Posts: 2021
Re: Generic records // Hide instance methods // Hide static methods
« Reply #4 on: August 17, 2025, 09:13:09 pm »
Because the methods can be used both ways:
Code: Pascal  [Select][+][-]
  1. type
  2.   TMyClass = class
  3.   public
  4.     class procedure Foo;
  5.     procedure Bar;
  6.   end;
  7.  
  8. class procedure TMyClass.Foo;
  9. begin
  10.   WriteLn('Foo');
  11. end;
  12.  
  13. procedure TMyClass.Bar;
  14. begin
  15.   WriteLn('Bar');
  16. end;
  17.  
  18. type
  19.   TMethodProcedure = procedure of object;
  20.  
  21. var
  22.   i: TMyClass;
  23.   p: TMethodProcedure;
  24.   m: TMethod;
  25. begin
  26.   i:=TMyClass.Create;
  27.   i.Foo; // Calling class method from instance is perfectly valid
  28.   m.Code:=@TMyClass.Bar; // Taking the pointer of a method from the class is perfectly valid
  29.   m.Data:=i;
  30.   p:=TMethodProcedure(m);
  31.   p(); // Calling constructed TMethod function pointer is perfectly valid
  32. end.

You can use ClassName.Method to get the pointer of a method, as a raw pointer to use in TMethod. Also you call call a class Method from an instance. Both are valid constructs, so Lazarus suggests both.

Also note that Lazarus tends to show more than FPC actually allows, because to do the error checking FPC does multiple passes, which require the whole context. Lazarus tries to give you suggestions even when the surrounding code is faulty, otherwise you would get error messages left and right when trying to use autocomplete.
So this is why Lazarus will show you those methods anyways, because they applicable in some circumstances, and filtering those out would be to restrictive. It's better to show more and have the user filter, instead of filtering to much and accidentally hiding something because the user made a mistake earlier on in the same line

I already hate it when Lazarus tries to be smart and only show functions that return something when on the right hand side of an assignment, because sometimes you vaguely remember there is another method, but it turns out that instead of having a return type, it uses a var param, so it is hidden. So even though Lazarus is correct in not showing this method because it's "wrong" on the right hand side of an assignment, I still want to see it, because I can just change the assignment to a call with a var parameter if I see that it is called differently.

I have a well functioning brain, so it should just show me everything that might be applicable and let me figure out how to deal with it

Also as a side note:
Quote
This is how the compiler works.
Lazarus is not the compiler, it is more leanient than the compiler, as most LSPs are as well, to allow typos while writing the code not effecting the utility. If you have a single typo most compilers will just quit, Lazarus tries to backtrack and continue with the next instruction anew to not have a single typo in a file ruin code completion in the rest of the file.
This is also the case in other languages, if I take the following code in C#:
Code: Java  [Select][+][-]
  1. class Test {
  2.     static void Foo( {
  3.         Test.F
  4.     }
  5. }
The language tools will still offer me to complete to "Foo", even tho it is clearly not a valid construct and the actual compiler would never get this far because it's an LL(1) compiler that would not be able to recover from the incomplete parameter list.
I know exactly one language server which uses the same parser as the compiler and that the Haskell language server and it is awful for exactly that reason.

Second:
Quote
like F#
Most tooling actually behaves the same. I just checked for example with the Java LSP, and it works exactly the same. The Clang LSP for C++ also does the same showing static methods as function methods. I think with IntelliSense for F# and C# you probably just found the only LSP where this is not the case.
And I would say this is because filtering out the static methods from the method list is a bad thing because it makes discovering the class much harder. So this is a fault of intellisense and a feature of Lazarus, the Java LSP and clangd rather than the other way around
« Last Edit: August 17, 2025, 09:34:45 pm by Warfley »

AlexanderK

  • New Member
  • *
  • Posts: 12
Re: Generic records // Hide instance methods // Hide static methods
« Reply #5 on: August 18, 2025, 07:54:36 pm »
Warfley, I appreciate your detailed explanation. Thank you!

 

TinyPortal © 2005-2018