Recent

Author Topic: Detect if unit is used in interface or implementation only  (Read 1209 times)

Eugene Loza

  • Hero Member
  • *****
  • Posts: 729
    • My games in Pascal
Detect if unit is used in interface or implementation only
« on: July 28, 2024, 04:39:21 pm »
There's an extremely useful feature in Lazarus: Source -> Refactoring -> Unused units.

However, it doesn't consider if the unit is used in interface or implementation. I wonder if there is any way to do so automatically? It may be good to "lower" a used unit to implementation only if it's not used in interface, in context of cyclic dependencies in the first place.
My FOSS games in FreePascal&CastleGameEngine: https://decoherence.itch.io/ (Sources: https://gitlab.com/EugeneLoza)

Joanna

  • Hero Member
  • *****
  • Posts: 994
Re: Detect if unit is used in interface or implementation only
« Reply #1 on: July 28, 2024, 05:00:49 pm »
That’s an interesting feature that I didn’t even know existed. I just looked at it and it seems to sort the unused units by interface or implementation sections  and buttons to remove the units.
✨ 🙋🏻‍♀️ More Pascal enthusiasts are needed on IRC .. https://libera.chat/guides/ IRC.LIBERA.CHAT  Ports [6667 plaintext ] or [6697 secure] channel #fpc  #pascal Please private Message me if you have any questions or need assistance. 💁🏻‍♀️

Eugene Loza

  • Hero Member
  • *****
  • Posts: 729
    • My games in Pascal
Re: Detect if unit is used in interface or implementation only
« Reply #2 on: July 28, 2024, 05:25:28 pm »
Indeed. But let's say we have

Quote
interface
uses Classes;

implementation

var
  MyList: TStringList;

it will say that Classes is used (providing TStringList), however, it is added to "uses" of interface, while used only in implementation.

And that's the issue I'd like to autodetect.

E.g. in the example above it doesn't matter, but in case:

Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2. interface
  3. uses Unit2;
  4.  
  5. type TClass1 = class(TObject) end;
  6.  
  7. implementation
  8. var
  9.   C2: TClass2;
  10.  
  11. ...
  12.  
  13. unit Unit2;
  14. interface
  15. uses Unit1;
  16.  
  17. type TClass2 = class(TObject) end;
  18.  
  19. implementation
  20.  
  21. var
  22.   C1: TClass1;
  23.  

we get a cyclic dependency error which in this case can be easily solved by moving uses from interface into implementation and potentially should be very easy to detect with something very similar to unused units.
My FOSS games in FreePascal&CastleGameEngine: https://decoherence.itch.io/ (Sources: https://gitlab.com/EugeneLoza)

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 10249
  • Debugger - SynEdit - and more
    • wiki
Re: Detect if unit is used in interface or implementation only
« Reply #3 on: July 28, 2024, 06:37:36 pm »
1) With regards to unit cycles => menu: View > Unit Dependencies.
Then the "project and packages" tab. In the upper graph select the package (or project) to view.
Red lines go backwards.


2) uses in implementation.

Personally, I prefer to keep all uses in the interface (even if only needed for implementation). That means, if I decide (against my own better judgment) to allow a circle, I have to deliberately change the "uses". I can't accidentally add a circle.

But also, it avoids errors like this
Code: Pascal  [Select][+][-]
  1. unit MyUnit;
  2. interface
  3. uses SomeUnit; // Defines TFoo
  4.  
  5. procedure Foo(Bar: TFoo);
  6.  
  7. implementation
  8. uses OtherUnit; // Contains a different declaration of TFoo
  9.  
  10. procedure Foo(Bar: TFoo); // fails to compile, because it is a different TFoo, from OtherUnit.
  11. begin
  12. end;
  13.  
  14. end.

Joanna

  • Hero Member
  • *****
  • Posts: 994
Re: Detect if unit is used in interface or implementation only
« Reply #4 on: July 29, 2024, 01:39:24 am »
The adding units to interface by default definitely makes more sense for readability. The implementation section often requires some scrolling to get to.
So here is a question, if unit1 uses classes and unit2 uses unit1 and also uses classes , can unit2 use the classes unit declared in unit1 instead of having classes unit in its own uses section? Logically it should work but if it does it’s bad to not have everything that unit2 uses visible at top of unit.
« Last Edit: August 01, 2024, 09:32:13 am by Joanna »
✨ 🙋🏻‍♀️ More Pascal enthusiasts are needed on IRC .. https://libera.chat/guides/ IRC.LIBERA.CHAT  Ports [6667 plaintext ] or [6697 secure] channel #fpc  #pascal Please private Message me if you have any questions or need assistance. 💁🏻‍♀️

PascalDragon

  • Hero Member
  • *****
  • Posts: 5649
  • Compiler Developer
Re: Detect if unit is used in interface or implementation only
« Reply #5 on: July 31, 2024, 11:04:23 pm »
So here is a question, if unit1 uses classes and unit2 uses unit1 and also uses classes , can unit2 use the classes unit declared in unit1 instead of having classes unit in its own uses section? Logically it should word but if it does it’s bad to not have everything that unit2 uses visible at top of unit.

Every unit must have any unit it directly(*) accesses in one of its uses-sections. That's the whole point of the unit concept, so that it's clear what a unit uses (for both the user and the compiler).

(*) There are some exceptions, e.g. if you use a type only indirectly like here (note: this is a stupid example that has a memory leak, it's only to highlight the indirect use):

Unit utest.pp:

Code: Pascal  [Select][+][-]
  1. unit utest;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes;
  9.  
  10. function Test: TStrings;
  11.  
  12. implementation
  13.  
  14. function Test: TStrings;
  15. begin
  16.   Result := TStringList.Create;
  17.   Result.Add('Hello World');
  18. end;
  19.  
  20. end.

Program ttest.pp:

Code: Pascal  [Select][+][-]
  1. program ttest;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. uses
  6.   utest;
  7.  
  8. begin
  9.   Writeln(Test.Text);
  10. end.

As you can see the compiler knows about the methods of TStrings inside ttest despite not having Classes in the uses-clause. If you'd want to store the return value of Test however you'd have to declare a suitable variable and thus declare the Classes unit in the uses-clause.

cdbc

  • Hero Member
  • *****
  • Posts: 1497
    • http://www.cdbc.dk
Re: Detect if unit is used in interface or implementation only
« Reply #6 on: August 01, 2024, 07:43:20 am »
Hi
@Joanna: If need be, you can /forward/ a type, by aliasing it in the unit, e.g.:
Code: Pascal  [Select][+][-]
  1. unit utest;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes;
  9. //////////////// here /////////
  10. type  
  11.   TStrings = classes.TStrings; // alias
  12. ///////////////
  13. function Test: TStrings;
  14.  
  15. implementation
  16. ...
Regards Benny
If it ain't broke, don't fix it ;)
PCLinuxOS(rolling release) 64bit -> KDE5 -> FPC 3.2.2 -> Lazarus 2.2.6 up until Jan 2024 from then on it's: KDE5/QT5 -> FPC 3.3.1 -> Lazarus 3.0

Joanna

  • Hero Member
  • *****
  • Posts: 994
Re: Detect if unit is used in interface or implementation only
« Reply #7 on: August 01, 2024, 09:34:09 am »
That’s an idea  :)
✨ 🙋🏻‍♀️ More Pascal enthusiasts are needed on IRC .. https://libera.chat/guides/ IRC.LIBERA.CHAT  Ports [6667 plaintext ] or [6697 secure] channel #fpc  #pascal Please private Message me if you have any questions or need assistance. 💁🏻‍♀️

 

TinyPortal © 2005-2018