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

Rule(s) Request unnecessary_open and missing_final #4838

Open
2 tasks
mildm8nnered opened this issue Mar 25, 2023 · 3 comments
Open
2 tasks

Rule(s) Request unnecessary_open and missing_final #4838

mildm8nnered opened this issue Mar 25, 2023 · 3 comments
Labels
discussion Topics that cannot be categorized as bugs or enhancements yet. They require further discussions. rule-request Requests for a new rules.

Comments

@mildm8nnered
Copy link
Collaborator

mildm8nnered commented Mar 25, 2023

New Issue Checklist

New rule request

The idea of unnecessary_open is that it would warn about uses of open where it is not required, because the class, var, or function is never overridden outside its own module.

missing_final would warn whenever a class, var, or method was never overridden either inside or outside its own module, and can be safely declared as final.

Presumably these would need to be analyzer rules. Because the logic required is quite similar, it might be easier to combine them into one rule.

  1. Why should this rule be added?

If open and final are strictly enforced, then it becomes possible to tell from the declaration whether and where a class is ever subclassed, or a var of fun overridden, as opposed to whether they could be subclassed or overridden in theory.

final - never overridden
internal or public - overridden, but only within the same module
open - overridden outside their defining module.

Because unnecessary open's and missing finals are not currently detected, in our own codebase, we were able to tighten these up very considerably when we did this analysis by hand.

  1. Provide several examples of what would and wouldn't trigger violations.

Triggering Examples

For unnecessary_open:

↓open class Foo {
    // Foo is never subclassed outside it's defining module
}
open class Foo {
    // bar is never overridden outside it's defining module
    ↓open var bar: Int = 0
}

For missing_final:

↓public class Foo {
    // Foo is never subclassed at all
}
public final class Foo {
    ↓public var bar: Int = 0
    // bar is never overridden
}

Non-Triggering Examples:

For unnecessary_open:

open class Foo {
    // Foo is subclassed outside its defining module
}
open class Foo {
    // bar is overridden outside its defining module
    open var bar: Int = 0
}

For missing_final:

public class Foo {
    // Foo is subclassed within its defining module
}
public final class Foo {
    public var bar: Int = 0
    // bar is overridden within its defining module
}
  1. Should the rule be configurable, if so what parameters should be configurable?

If combined into one rule, it would probably be good to allow them to be selectively enabled.

For libraries, where it is kind of expected that you will advertise your API, but not necessarily call it yourself, this could be a pain, but you can always disable it.

  1. Should the rule be opt-in or enabled by default? Why?

As an analyzer rule, by definition this would be opt-in.

Other notes:

Not entirely sure how you would unit test a rule like this that requires different compilation modules.

@SimplyDanny
Copy link
Collaborator

Do you know of Periphery? Even though it does not yet provide the requested features (I guess), it could (more easily) be extended to do so. SwiftLint would have to reimplement parts of what Periphery already provides.

@SimplyDanny SimplyDanny added rule-request Requests for a new rules. discussion Topics that cannot be categorized as bugs or enhancements yet. They require further discussions. labels Mar 25, 2023
@mildm8nnered
Copy link
Collaborator Author

Do you know of Periphery? Even though it does not yet provide the requested features (I guess), it could (more easily) be extended to do so. SwiftLint would have to reimplement parts of what Periphery already provides.

I had never seen Periphery, but I will definitely grab it and have a play now.

I tried stubbing out unnecessary_open, based on unused_declaration, and I don't think this would be actually be too hard (famous last words), but let me see what Periphery can do.

@mildm8nnered
Copy link
Collaborator Author

Oooh - I like the guided setup - that is quite nice.

% periphery scan --setup
Welcome to Periphery!
This guided setup will help you select the appropriate configuration for your project.

* Inspecting project...

Select build targets to analyze:
? Delimit choices with a single space, e.g: 1 2 3, or 'all' to select all options
 1 Foo
 2 Bar
 3 ...
> all

Select the schemes necessary to build your chosen targets:
? Delimit choices with a single space, e.g: 1 2 3
1 Foo
2 Bar
> 1 2

Assume Objective-C accessible declarations are in use?
? Declarations exposed to the Objective-C runtime explicitly with @objc, or implicitly by inheriting NSObject will be assumed to be in use. Choose 'No' if your project is pure Swift.
(Y)es/(N)o > Y

Assume all 'public' declarations are in use?
? You should choose 'Yes' here if your public interfaces are not used by any selected build target, as may be the case for a framework/library project.
(Y)es/(N)o > N

Save configuration to .periphery.yml?
(Y)es/(N)o > Y

* Executing command:
periphery scan

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
discussion Topics that cannot be categorized as bugs or enhancements yet. They require further discussions. rule-request Requests for a new rules.
Projects
None yet
Development

No branches or pull requests

2 participants