This document is a place to record information about the coding style that‘s currently being used by the developers of the analyzer packages (the analysis_server
, analyzer
, analyzer_plugin
, analyzer_utilities
, dartdoc
, linter
, and markdown
packages). As such, it’s subject to change as the language evolves and as our experience dictates.
The document is divided into three main sections covering the coding styles we
Our goal is to make as much as possible of the style be automatically enforced, either through lints or tests, but this document also includes style choices that can't reasonably be enforced automatically.
This section documents the styles we have agreed to follow.
We follow the styles laid out by Effective Dart, the Dart style guide, unless otherwise noted below. Most of the rules in Effective Dart have lint rules that keep us honest, but not all.
flutter_style_todos
- Keeps our todo format canonical.prefer_single_quotes
- We don't enforce this in each of our packages yet, but we aspire to.unnecessary_breaks
- All our packages require at least Dart 3.0.0
, so we can do it.All of our source code is expected to be
dart format
command, and(With the exception that we don't sort in either the dartdoc
or markdown
packages.)
Formatting is enforced by presubmit and sorting is enforced by tests. We recommend that you enable both the formatter and the Sort Members command to be run on save, at least within our packages.
We generally follow the naming conventions from the Dart style guide. We do have some legacy code that uses screaming caps for constant names, but newer code doesn't use that style.
The Dart style guide doesn't explicitly specify a naming convention for import prefixes beyond the guidance to use snake case. (See DO name import prefixes using lowercase_with_underscores.)
However, the examples in the Dart style guide all use the name of the file with the .dart
suffix removed. That‘s the standard we follow, with the exception that we also drop _test.dart
for test files. That includes using the prefix path
for the path package, even though it’s a commonly used variable name. In code where there would be a conflict, we prefix the variable name with a qualifying noun to form a unique name, such as filePath
.
Public extensions (which are intended to be accessible outside their declaring library) are named in a consistent style. An extension on a type Foo
is named FooExtension
. An extension on a nullable type Foo?
uses the word “Nullable” in place of the question mark, like FooNullableExtension
. While this can lead to long names (like AnalysisOptionsImplExtension
), the extension name is rarely used (only in explicit extension overrides), and we value consistency.
We use var
to declare local variables with two exceptions:
We use a type annotation if the type of the variable would be incorrectly inferred without it, such as when a variable that needs to be nullable would be inferred to be non-nullable because it's initialized to a non-null value.
We use final
if the local variable shadows a field and is being used to allow the type of the field to be promoted.
The reason to prefer var
over final
is that the majority of the team felt that the benefit of using var
was slightly greater than the benefit of using final
.
This section documents the styles we are currently discussing. The purpose is to capture the state of the discussions so that we don‘t forget what we’ve already discussed.
For historic reasons we have enabled some of the lint rules in the core and recommended rule sets, but some have been disabled in various packages.
In addition, there are lint rules outside those sets that are enabled in some packages but not in all packages.
All of these lint rules need to be discussed, and descriptions will be added as discussions occur.
There is a proposal to disallow any new uses of the unary null-assert operator (!
). Use of the operator is generally undesirable because if the value of the operand is null
an exception will be thrown.
The biggest concern is with the fact that we have some nullable properties that can't be made non-nullable (because of limitations in the type system) but that are known to be non-nullable in practice. The most obvious examples of this are Token.next
and Token.previous
. We routinely use the null-assert operator in those cases.
A similar case is Expression.staticType
where we know that the type will be non-null after resolution, but there we‘ve wrapped the null check in an extension getter named typeOrThrow
. We could use this as a convention to signal that we’ve decided that the possibility of an exception is rare.
It has also been suggested that it might be better, however, to just use !
to signal those cases, and to only disallow the operator in cases where we don't think the exception will be rare. That might have the advantage of making the presence of the check (and possibility of an exception) more explicit in the code.
We might want to allow this in tests, where the null-assert becomes another form of expectation.
There is a proposal to disallow any new uses of type casts, with two exceptions: when using covariant
and where there isn't any other reasonable way.
There‘s concern about using covariant
other than in places where we know it to be safe (for example, the AstNode
hierarchy is designed in such a way that it’s safe to use covariant
if the covariant type is the Impl
type of the overridden method).
The second exclusion is not well defined. We probably need to clarify what cases we would allow so we know that the style can be consistently applied.
There's also speculation that we might want to not impose this restriction in test code because using type casts is effectively equivalent to an expectation but with the advantage that the type of the variable can be promoted.
This section documents the styles that we‘ve explicitly decided we’re not going to follow. For each style there should be a description of why the decision was made. The purpose is to know when circumstances have changed enough that it might be worth reconsidering.
TBD
TBD