Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ban public top-level members in a test #1258

Closed
srawlins opened this issue Nov 7, 2018 · 19 comments
Closed

Ban public top-level members in a test #1258

srawlins opened this issue Nov 7, 2018 · 19 comments
Labels
lint-request type-enhancement A request for a change that isn't a bug

Comments

@srawlins
Copy link
Member

srawlins commented Nov 7, 2018

Proposal

Given a library with URI with pattern /test/.*_test.dart, report any public top-level members except main. "Top-level members in a test should be private (or relocated to within main), to prevent unused members."

Secondary proposal

Given a library which does not live in it's declaring package's public API (i.e. files in bin/, test/, and tool/), report any public top-level members except main.

Justification

This lint helps prevent code bloat. Without full-program analysis, we cannot tell if a public member is used anywhere, so it is not reported by the analyzer as unused_variable or something similar. For top-level members, only private ones can be rigorously checked for usage. But in tests (and bin/ and tool/ scripts), which are typically their own entry points and never imported by another library, we want to be able to report unused top-level members.

This includes top-level variables, constants, functions, typedefs, enums, mixins, and classes.

This is a counter-request to #685. I think this is better choice for a few reasons. #685 proposes either a change to the analyzer's unused analysis to take into account the library URI ("does it end with _test.dart?"), leading to possible false positives. I don't think that's going to go well. The alternative is to implement a new unused analysis in the linter. Unused analysis is expensive though, and adding a second one in linter (duplicating a ton of code) will make it more expensive.

Downsides

One downside to recommending private top-level members is that it might be counter to what users typically do today. I think a lot of people just write regular, public, top-level consts and helper functions. Personally, I always write private top-level members, so that I know when something becomes unused. Perhaps a study should be done on what most people do. #685 does not have this downside.

@pq pq added the type-enhancement A request for a change that isn't a bug label Apr 29, 2019
@kevmoo
Copy link
Member

kevmoo commented Apr 29, 2019

Like #1034, right?

@natebosch
Copy link
Member

I think it would be mostly covered by #1034 but it's still worth tracking separately.

  • Implementing this is likely a lot easier since it isn't blocked by the linter having an understanding of "package level" analysis.
  • Even with the package level analysis, defining a public member in a *_test.dart file is something we'd discourage. You don't want to import a test file from another to use a shared utility. That kind of stuff should go in it's own library (though this does mean that the "secondary proposal" above is not feasible, since we explicitly do want to define utility libraries under test/ for shared testing code).

@kevmoo
Copy link
Member

kevmoo commented Apr 29, 2019

We should also be careful, though. Make sure the lib in question is importing package:test/test.dart etc etc

Most folks use pkg:test – but we shouldn't assume

@natebosch
Copy link
Member

Most folks use pkg:test – but we shouldn't assume

The folks who don't shouldn't enable this lint...

@goderbauer
Copy link
Contributor

As a data point: Recently did some clean-up in the framework with the help of this tool: https://pub.dev/packages/dart_code_metrics. We had accumulated quite a bit of dead code in tests: flutter/flutter#104550.

This proposal (or #685 or #2395) probably could have prevented that build-up of dead code.

@pq
Copy link
Member

pq commented May 25, 2022

Thanks for the concrete example @goderbauer!

@a14n
Copy link
Contributor

a14n commented Jun 14, 2022

In #3453 I implemented such a lint but not specifically for tests. The lint checks every executable libraries (ie. a library containing a main top level function).

On Flutter codebase there are only few cases (among tens of thousands) that are triggered out of tests. Most of them can easily be refactored to prevent the lint (and make the code structured in a better way)

@a14n
Copy link
Contributor

a14n commented Jun 16, 2022

Any opinion about limiting the application of the lint to tests or to extend it to executable libraries (ie. a library containing a main top level function)?

The current PR (that is ready to land) applies on executable libraries but I can change it if we think it will be a too broadened application.

@a14n
Copy link
Contributor

a14n commented Jun 29, 2022

Any opinion about limiting the application of the lint to tests or to extend it to executable libraries (ie. a library containing a main top level function)?

Nobody?

@kevmoo
Copy link
Member

kevmoo commented Jun 29, 2022 via email

@a14n
Copy link
Contributor

a14n commented Jun 29, 2022

Extending the lint to executable libraries could be a way to cover/workaround Report unused public elements in an entrypoint library. User can always limits the application of the lint to /test by defining an analysis_option.yaml file inside the test directory.

My point is that this lint could have a lot of value also outside of the test directory. Should we have 2 lints (one for every executable library and one limited to test)?

@a14n
Copy link
Contributor

a14n commented Jul 11, 2022

Re-reading the issue description the lint request doesn't seem limited to tests:

Secondary proposal

Given a library which does not live in it's declaring package's public API (i.e. files in bin/, test/, and tool/), report any public top-level members except main.

I'm concern about provinding 2 lints no_top_public_members_in_executable_libraries and no_top_public_members_in_tests that do exactly the same thing but with a limited range for the latter. WDYT?

@bwilkerson
Copy link
Member

I think we want to get some data about common usage patterns before we make a decision. How many top-level members (other than main) do people typically implement, both inside and outside of test?

I'm not familiar with test code outside the analyzer packages, but I do know of several libraries / packages that make fairly significant use of top-level members, so it's possible that this is a not-uncommon coding style.

@srawlins
Copy link
Member Author

I think it's fairly common to have top-level helper functions in test files, and these would be the one's we'd want to ensure are used. I hypothesize that top-level constants like Strings and ints are cheap and not horrible if left unused, but top-level helpers can reference imported elements, so that it is more likely that an unused top-level function can represent actual bloat.

All of that being said, I'm much more in favor, these days, of just writing public top-level members, and improving our tooling to find unused elements like these. I used to write private top-level consts, helper classes, etc. in tests, but I think it does get pretty hard to read anything like that.

@a14n
Copy link
Contributor

a14n commented Jul 11, 2022

So should we close this issue in favor of #685 and #2395?

@srawlins
Copy link
Member Author

I would be in favor of that. We don't have to close until one is implemented 🤣 because I think there is very good discussion.

@a14n
Copy link
Contributor

a14n commented Jul 11, 2022

The same question about the pertinence to have 2 lints doing the same thing (only on tests vs. on executable libs) remains. Any opinion on that?

@bwilkerson
Copy link
Member

Again, I think the data would be useful. If it's common to define public top-level functions outside test, and if they're intended to be part of the public API (not just helpers that could have been private), then that would make it more important for the rules to be separate so that users could configure the behavior that they want. But if public top-level functions outside of test are rare, then it might not be worth the overhead of having two lints.

@srawlins
Copy link
Member Author

I don't feel strongly about this issue any more. I'm happy with #3513 and think it is fairly non-idiomatic to declare private helpers and consts in test libraries. Anyone who feels strongly can re-open.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
lint-request type-enhancement A request for a change that isn't a bug
Projects
None yet
Development

Successfully merging a pull request may close this issue.

7 participants