Description
Resolved: We will have a strict
mode.
Some checks could be made lints instead where it makes sense, on a case-by-case basis.
Meta: Should we have a set of "strict mode" lints, or a "strict mode" flag to the analyzer?
We can meet offline as well, but I'd like to post this issue for posterity and for external reference.
As seen in:
- Implement a set of "strict" static analysis rules #33749
- I would like a
--strict-await
static analysis option (2.1+) #33823 (comment) - Would like a way to avoid raw generic types #33119 (comment)
In the issues above @jmesserly correctly brought up that it's unclear whether we need lints (i.e. rules, implemented in package:linter
, that are opt-in via adding the individual rules to analysis_options.yaml
) or hints (implemented directly in package:analyzer
, that are opt-in via a potential strict
flag in analysis_options.yaml
.
So, the question is, do we want:
dartanalyzer --options analysis_options.yaml
analyzer:
linter:
rules:
avoid_dynamic_calls: true
avoid_inferred_casts: true
avoid_raw_types: true
or do we want an option of either:
dartanalyzer --strict
dartanalyzer --options analysis_options.yaml
analyzer:
strict: true
(Or, with more specificity)
dartanalyzer --strict-dynamic-calls --strict_raw_types
analyzer:
strict:
- dynamic_calls
- raw_types
A bit of history
Originally when @jmesserly et al wrote the strong-mode:
flag in the analyzer, this was an optional (off-by-default) mode, and she added additional opt-in flags for even stricter analysis: --no-implicit-dynamic
and --no-implicit-casts
. We (well, at least @srawlins and I) ❤️ these flags, but they are a little too demanding of most code bases - but they serve as an excellent way to frame this discussion.
Now that strong-mode is the default (and only option), it's less clear whether we need (or actually desire) a strict mode - i.e. a way to run stricter analysis checks to catch more errors at analysis-time and less at runtime in Dart 2.0+.
What differentiates strict
flags from lints?
Nothing, though strict
flags - based on discussion in #33749 are more nuanced (closer to hints):
- They do not enforce style or readability rules
- They do not change coding habits for entirely aesthetic purposes
- They do not make any decisions that could have false positives
... rather, it is possible to write a strict
check as a lint, but most lints do not meet criteria for strict.
Possible flags (taken from #33749, and some offline discussion)
... to help serve as a basis of informing the decision.
--strict-dynamic-calls
: Do not allow dynamic calls (perhaps with an opt-in mechanism)--strict-inferred-casts
: Do not allow inference to produce a top type unless top was an input--strict-raw-types
: RequireList<dynamic>
instead ofList
implicitly beingList<dynamic>
Comparative approach
Non-exhaustive - just wrote down what I could think of on top of my head.
Lints
PROs:
- Authored in the
linter
package, which is much more external/non-SDK developer friendly ⭐️ - Can iterate much faster
- Sit side by side with other rules trying to accomplish (sometimes) similar goals
- Can be modified, created at will without needing extra integration within the analyzer
CONs:
- Not really "on the radar" of the language/core team, i.e. most work here happens w/o much notice
- Not simple to add "quick fixes" (suggestions) for IDE users
- Needs to be rolled into the SDK
Hints
PROs:
- Easier and more natural to add "quick fixes" and suggestions for IDE users
- A
strict
flag is a simpler concept to wrap your head around as a concept from individual lints - Can be more directly driven by the language/core team (i.e. positive but non-breaking changes)
- Easy to adapt 1-1 as a recommendation for "large code bases" (i.e. google3 and similar)
CONs:
- Less friendly to contributors not already experienced with developing against the SDK ⭐️
- UX issue of a
strict
mode flag in the analyzer/related tools (compared to "strong" mode etc)
⭐️ I don't think this is that important, given we don't expect external users to contribute these flags anymore than we expect external users to change the type system or the core libraries.