-
Notifications
You must be signed in to change notification settings - Fork 1.7k
proposal: <remove_unused_mixin>
#59360
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
Comments
I'd like to mention one scenario which could be a systematic exception (causing false positives for this lint): class Interface {
void _fn();
... // Other members, public and/or private.
}
mixin M1 implements Interface {
void _fn() => print('Implementation 1');
... // A set of private member implementations.
}
mixin M2 implements Interface {
void _fn() => print('Implementation 2');
... // The same set of private members with a different implementation.
} The idea is that client code (in other libraries) can use declarations like I wouldn't call that an "unused" mixin. |
In that scenario, I think this could be solved by considering the other declarations. If all the other declarations are private, then neither I'm not sure if that would be hard on the analyzer to depend on the other declarations inside the same scope. |
Oh, but the difference between mixing in It's a kind of "remote control": The author of I don't see why we shouldn't support this kind of configurability where a client gets to mix and match some pre-packaged sets of method implementations according to their needs, but they don't get to call or override those members directly. Of course, we'd have a different kind of lint which says that a given private member is unused. I'm just saying that an all-private-members mixin can very well be significant outside the declaring library if those members are used in any way by the declaring library. |
Sorry if I wasn't clear enough, I would only mark that as unused if the actual If either |
But even that isn't enough, a client might certainly have a preference for one or the other implementation of a private member if it is at all reachable: // Library 'lib.dart'.
abstract class Interface {
void _draw();
}
mixin M1 implements Interface {
void _draw() => print('Create a nice drawing of a flower in a vase!');
}
mixin M2 implements Interface {
void _draw() => print('Pull the gun and threaten some innocent bystanders!');
}
void doDraw(Interface x) => x._draw();
// Library 'main.dart'.
import 'lib.dart';
class MyClass extends Interface with M1 {}
void main() {
doDraw(MyClass());
} |
I see. But that is a public member that calls the private member of |
I think it should work fine to rely on each member being reachable: Each private member declaration may be used in one way or another, but if it isn't used at all then this declaration could be flagged by an We could insist that this liveness analysis should find a public member in the dependency chain, but I don't think that's necessary because otherwise the entire dependency graph would consist of private members, and the first one would be unreachable. That one would be removed based on the lint, and then the ones that it called, and so on. So we'll get the "must be callable via some public declaration" for free. (OK, we can have cycles where private members call each other and are otherwise unreachable, but let's just say that we stop here and accept that there is some reasonable way to perform an analysis about reachability of private declarations. ;-) So let's assume that we cleaned up the code such that no such unreachable private member exists. This implies that there is a way to invoke each private member in the library (well, that is undecidable, but we can't prove that it is impossible). With that, I think every non-empty public mixin should be considered worthwhile (even in the case where it only contains private members). However, we can't even conclude that a mixin is useless just because it does not declare any members. For instance, we might want to use a final class Foo {
Object m(int i) => 'Foo: $i';
}
final class FooA extends Foo {
String m(num n) => 'FooA: ${super.m(n.toInt())}';
}
final class FooB extends Foo {
Comparable<String> m(num n) => 'FooB: ${super.m(-n.toInt())}';
}
void f(Foo foo, int i) => print(foo.m(i));
@reopen
@visibleForTesting
base mixin FooTestInterface implements Foo {}
@reopen
@visibleForTesting
base mixin FooATestInterface implements FooA {}
@reopen
@visibleForTesting
base mixin FooBTestInterface implements FooB {} The point is that However, we want to create a very specific exception: It should be possible to create a // Testing. OK to access declarations that are `@visibleForTesting`.
base class FakeFoo with FooTestInterface { // NB: `FakeFoo` is a subtype of `Foo`.
Object m(int _) => 'FakeFoo: 42'; // Shallow implementation, just for testing.
}
void main() {
f(FakeFoo(), 42);
} This shows that even an empty mixin can be useful because it can provide a certain supertype relationship to declarations where it is mixed in. It is crucial that it is a mixin, because there cannot be more than one immediate superclass of the fake class, and we might need to use the superclass "slot" for some other purpose. In contrast, the fake class can have as many mixins as it needs. On the other hand, if we want to use OK, this example is controversial (check out the discussion in dart-lang/site-www#5143), but it does illustrate that it's really difficult to characterize a particular kind of mixin declaration as useless, and getting it right. ;-) |
I'm taking some time to think about it all, just one simple thing that I'd like to post here which I'm fairly certain at this point.
In those cases, I'd agree with srawlins comment on 54374:
Even more so if one is using something like |
Could you elaborate on that a bit? I'd like to understand your parenthesis better. |
remove_unused_mixin
Description
This lint suggests removing mixins or implementations with only private declarations in Dart code.
Details
This lint aims to identify mixins or implementations that consist solely of private members without any external usage. The suggestion is to remove such elements to maintain a cleaner and more maintainable codebase.
Kind
This lint falls under the category of enforcing style advice to improve code quality and maintainability.
Bad Examples
Good Examples
Discussion
The remove_unused_mixin lint is designed to encourage developers to remove mixins or implementations that do not contribute to the public API of a class and only contain private members. This enhances code clarity and reduces unnecessary complexity.
More context at #54374
I'd like to discuss about: I'm not sure if there is any use case where implementations (I am more convinced extended types would probably not fall into this category but could also be discussed) could maybe be included in this lint (which would require renaming).
Discussion checklist
The text was updated successfully, but these errors were encountered: