sdk icon indicating copy to clipboard operation
sdk copied to clipboard

[linter] New lint proposal: leaked_api_symbols

Open dcharkes opened this issue 6 months ago • 5 comments

https://pub.dev/packages/dart_apitool can report leaked API symbols, and we use it in the Dart health checks.

However, it would be much more user friendly if this would be a lint in the IDE.

@srawlins How complicated can the code be that we run in lints? This is like a package-wide analysis.

dcharkes avatar Jun 12 '25 07:06 dcharkes

The limiting factor is performance. The lints are run every time a library (or library cycle) needs to be analyzed, so a slow running lint can lead to poor response times in the IDE.

If by "package-wide" you mean that the analysis requires looking at everything in the package after it's been fully analyzed, then that's probably going to be too expensive.

However, unless I'm missing something, it seems like the analysis ought to be able to work on a single library at a time. Am I missing something? If that is the case, then the only problem is that the analyzer only has the current version of the code available. It would need to be able to get some information about an earlier version in order to do the comparison.

bwilkerson avatar Jun 12 '25 14:06 bwilkerson

Maybe the lint could run in the context of the lib/foo.dart that contains all the export statements, and check that all types in the method signatures and type parameters are exported in the same file. (And the lint would do nothing in lib/src/**.dart files.)

dcharkes avatar Jun 12 '25 15:06 dcharkes

That sounds a bit like library_private_types_in_public_api.

bwilkerson avatar Jun 12 '25 16:06 bwilkerson

That sounds a bit like library_private_types_in_public_api.

Yes, but I also want to flag "package private" types. In other words, the types only available in the src/ directory (https://dart.dev/tools/pub/package-layout#implementation-files) but not exported in the public API.

dcharkes avatar Jun 12 '25 17:06 dcharkes

library_private_types_in_public_api2 😁 Or... private_types_in_public_api.

Yeah this has the problem that seemingly unrelated changes would need to trigger re-analysis. In the current design of everything. So given:

// lib/src/a.dart
class A {}
// lib/src/b.dart
import 'a.dart';
abstract class B {
  A get a;
}
// lib/foo.dart
export 'src/b.dart';
export 'src/y.dart';
export 'src/z.dart';

We have lib/src/b.dart depends on lib/src/a.dart, but has no relation to lib/foo.dart. But now for the results of this lint rule to be consistent and correct, we need to re-analyze lib/src/b.dart each time lib/foo.dart changes. Because one such change might be to add an export of lib/src/a.dart.

Today we don't have a mechanism to do this without sort of... reanalyzing everything at every change. But it is totally conceivable to carve out some niche features like:

  • when lib/src/a.dart changes, invalidate the analysis of lib/src/b.dart (we already do this) because b depends on a.
  • figure out all package-public-ish libraries. This would be:
    • all public libraries found in lib but not declared in lib/src,
    • all libraries exported by a public library (even if there is a show or hide clause), the transitive set
  • once that calculation is finished, only then can we run the leaked_api_symbols rule, on all package-public-ish libraries.
  • any time one of the public libraries is changed (e.g. class A becomes mixin A, or an export is added/removed), re-run the leaked_api_symbols rule, on all package-public-ish libraries.

There are some ways to make this better, i.e. with "fine-grained dependency" management. But it definitely has the potential to make analysis crazy slow. Basically for any package where most of the code is "package-public-ish", and so changes to "most of the code" results in a need to re-analyze "most of the code." :/

srawlins avatar Jun 12 '25 17:06 srawlins