Initial documentation for a small sample of diagnostics and a script to generate a markdown file containing that documentation
Change-Id: I06602a29c95829b42add1d655ad8d7190ae7100a
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/105141
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Commit-Queue: Brian Wilkerson <brianwilkerson@google.com>
diff --git a/pkg/analyzer/lib/src/dart/error/hint_codes.dart b/pkg/analyzer/lib/src/dart/error/hint_codes.dart
index 76e63a9..651d581 100644
--- a/pkg/analyzer/lib/src/dart/error/hint_codes.dart
+++ b/pkg/analyzer/lib/src/dart/error/hint_codes.dart
@@ -57,11 +57,30 @@
"removing the unreachable catch clause.");
/**
- * Deprecated members should not be invoked or used.
- *
* Parameters:
* 0: the name of the member
*/
+ // #### Description
+ //
+ // The analyzer produces this diagnostic when a deprecated library or class
+ // member is used in a different package.
+ //
+ // #### Example
+ //
+ // If the method `m` in the class `C` is annotated with `@deprecated`, then
+ // the following code produces this diagnostic:
+ //
+ // ```dart
+ // void f(C c) {
+ // c.!m!();
+ // }
+ // ```
+ //
+ // #### Common fixes
+ //
+ // The documentation for declarations that are annotated with `@deprecated`
+ // should have documentation to indicate what code to use in place of the
+ // deprecated code.
static const HintCode DEPRECATED_MEMBER_USE = const HintCode(
'DEPRECATED_MEMBER_USE', "'{0}' is deprecated and shouldn't be used.",
correction:
@@ -244,12 +263,32 @@
"Only classes can be annotated as being immutable.");
/**
- * This hint is generated anywhere a @literal annotation is associated with
- * anything other than a const constructor.
+ * No parameters.
*/
+ // #### Description
+ //
+ // The meaning of the `@literal` annotation is only defined when it's applied
+ // to a const constructor.
+ //
+ // #### Example
+ //
+ // The following code produces this diagnostic:
+ //
+ // ```dart
+ // !@literal!
+ // var x;
+ // ```
+ //
+ // #### Common fixes
+ //
+ // Remove the annotation:
+ //
+ // ```dart
+ // var x;
+ // ```
static const HintCode INVALID_LITERAL_ANNOTATION = const HintCode(
'INVALID_LITERAL_ANNOTATION',
- "Only const constructors can be annotated as being literal.");
+ "Only const constructors can have the `@literal` annotation.");
/**
* This hint is generated anywhere where `@required` annotates a non named
@@ -618,13 +657,51 @@
correction: "Try updating the SDK constraints.");
/**
- * A set literal is being used in code that is expected to run on versions of
- * the SDK that did not support them.
+ * No parameters.
*/
+ // #### Description
+ //
+ // The analyzer produces this diagnostic when a set literal is found in code
+ // that has an SDK constraint whose lower bound is less than 2.2. Set literals
+ // were not supported in earlier versions, so this code won't be able to run
+ // against earlier versions of the SDK.
+ //
+ // #### Example
+ //
+ // In a package that defines SDK constraints in the `pubspec.yaml` file that
+ // have a lower bound that's less than 2.2:
+ //
+ // ```yaml
+ // environment:
+ // sdk: '>=2.1.0 <2.4.0'
+ // ```
+ //
+ // The following code generates this diagnostic:
+ //
+ // ```dart
+ // var s = !<int>{}!;
+ // ```
+ //
+ // #### Common fixes
+ //
+ // If you don't need to support older versions of the SDK, then you can
+ // increase the SDK constraint to allow the syntax to be used:
+ //
+ // ```yaml
+ // environment:
+ // sdk: '>=2.2.0 <2.4.0'
+ // ```
+ //
+ // If you do need to support older versions of the SDK, then replace the set
+ // literal with code that creates the set without the use of a literal:
+ //
+ // ```dart
+ // var s = new Set<int>();
+ // ```
static const HintCode SDK_VERSION_SET_LITERAL = const HintCode(
'SDK_VERSION_SET_LITERAL',
- "Set literals were not supported until version 2.2, "
- "but this code is required to be able to run on earlier versions.",
+ "Set literals weren't supported until version 2.2, "
+ "but this code must be able to run on earlier versions.",
correction: "Try updating the SDK constraints.");
/**
diff --git a/pkg/analyzer/lib/src/error/codes.dart b/pkg/analyzer/lib/src/error/codes.dart
index 9363922..1b304fd 100644
--- a/pkg/analyzer/lib/src/error/codes.dart
+++ b/pkg/analyzer/lib/src/error/codes.dart
@@ -142,22 +142,142 @@
correction: "Try removing the export of one of the libraries, or "
"explicitly hiding the name in one of the export directives.");
+ /**
+ * No parameters.
+ */
+ // #### Description
+ //
+ // Because map and set literals use the same delimiters (`{` and `}`), the
+ // analyzer looks at the type arguments and the elements to determine which
+ // kind of literal you meant. When there are no type arguments and all of the
+ // elements are spread elements (which are allowed in both kinds of literals),
+ // then the analyzer uses the types of the expressions that are being spread.
+ // If all of the expressions have the type `Iterable`, then it's a set
+ // literal; if they all have the type `Map`, then it's a map literal.
+ //
+ // The analyzer produces this diagnostic when some of the expressions being
+ // spread have the type `Iterable` and others have the type `Map`, making it
+ // impossible for the analyzer to determine whether you are writing a map
+ // literal or a set literal.
+ //
+ // #### Example
+ //
+ // The following code produces this diagnostic:
+ //
+ // ```dart
+ // union(Map<String, String> a, List<String> b, Map<String, String> c) =>
+ // {...a, ...b, ...c};
+ // ```
+ //
+ // The list `b` can only be spread into a set, and the maps `a` and `c` can
+ // only be spread into a map, and the literal can't be both.
+ //
+ // #### Common fixes
+ //
+ // There are two common ways to fix this problem. The first is to remove all
+ // of the spread elements of one kind or the other, so that the elements are
+ // consistent. In this case, that likely means removing the list (and
+ // deciding what to do about the now unused parameter):
+ //
+ // ```dart
+ // union(Map<String, String> a, List<String> b, Map<String, String> c) =>
+ // {...a, ...c};
+ // ```
+ //
+ // The second fix is to change the elements of one kind into elements that are
+ // consistent with the other elements. For example, you could add the elements
+ // of the list as keys that map to themselves:
+ //
+ // ```dart
+ // union(Map<String, String> a, List<String> b, Map<String, String> c) =>
+ // {...a, for (String s in b) s: s, ...c};
+ // ```
static const CompileTimeErrorCode AMBIGUOUS_SET_OR_MAP_LITERAL_BOTH =
const CompileTimeErrorCode(
'AMBIGUOUS_SET_OR_MAP_LITERAL_BOTH',
- "This literal must be both a map and a set, because some elements "
- "spread a 'Map' and others spread an 'Iterable', but that isn't "
- "allowed.",
+ "This literal contains both 'Map' and 'Iterable' spreads, "
+ "which makes it impossible to determine whether the literal is "
+ "a map or a set.",
correction:
"Try removing or changing some of the elements so that all of "
"the elements are consistent.");
+ /**
+ * No parameters.
+ */
+ // #### Description
+ //
+ // Because map and set literals use the same delimiters (`‘{` and `}`), the
+ // analyzer looks at the type arguments and the elements to determine which
+ // kind of literal you meant. When there are no type arguments and all of the
+ // elements are spread elements (which are allowed in both kinds of literals)
+ // then the analyzer uses the types of the expressions that are being spread
+ // to decide. If all of the expressions have the type `Iterable`, then it's a
+ // set literal, if they all have the type `Map`, then it's a map literal.
+ //
+ // This diagnostic is produced when none of the expressions being spread has a
+ // type that allows the analyzer to decide whether you were writing a map
+ // literal or a set literal.
+ //
+ // #### Example
+ //
+ // The following code produces this diagnostic:
+ //
+ // ```dart
+ // union(a, b) => !{...a, ...b}!;
+ // ```
+ //
+ // The problem occurs because there are no type arguments, and there is no
+ // information about the type of either `a` or `b`.
+ //
+ // #### Common fixes
+ //
+ // There are three common ways to fix this problem. The first is to add type
+ // arguments to the literal. For example, if the literal is intended to be a
+ // map literal, you might write something like this:
+ //
+ // ```dart
+ // union(a, b) => <String, String>{...a, ...b};
+ // ```
+ //
+ // The second fix is to add type information so that the expressions have
+ // either the type `Iterable` or the type `Map`. You could add an explicit
+ // cast or, in this case, add types to the declarations of the two parameters:
+ //
+ // ```dart
+ // union(List<int> a, List<int> b) => {...a, ...b};
+ // ```
+ //
+ // The third fix is to add context information. In this case, that means
+ // adding a return type to the function:
+ //
+ // ```dart
+ // Set<String> union(a, b) => {...a, ...b};
+ // ```
+ //
+ // In other cases, you might add a type somewhere else. For example, say the
+ // original code looks like this:
+ //
+ // ```dart
+ // union(a, b) {
+ // var x = {...a, ...b};
+ // return x;
+ // }
+ // ```
+ //
+ // You might add a type annotation on `x`, like this:
+ //
+ // ```dart
+ // union(a, b) {
+ // Map<String, String> x = {...a, ...b};
+ // return x;
+ // }
+ // ```
static const CompileTimeErrorCode AMBIGUOUS_SET_OR_MAP_LITERAL_EITHER =
const CompileTimeErrorCode(
'AMBIGUOUS_SET_OR_MAP_LITERAL_EITHER',
- "This literal must be either a map or a set, but none of the "
- "elements have enough type information to know which, and that isn't "
- "allowed.",
+ "This literal must be either a map or a set, but the elements don't "
+ "have enough type information for type inference to work.",
correction:
"Try adding type arguments to the literal (one for sets, two "
"for maps).");
@@ -820,11 +940,40 @@
correction: "Try removing the default value.");
/**
- * It is an error if a required named parameter has a default value.
+ * No parameters.
*/
+ // #### Description
+ //
+ // The analyzer produces this diagnostic when a named parameter has both the
+ // `required` modifier and a default value. If the parameter is required, then
+ // a value for the parameter is always provided at the call sites, so the
+ // default value can never be used.
+ //
+ // #### Example
+ //
+ // The following code generates this diagnostic:
+ //
+ // ```dart
+ // void log({required String !message! = 'no message'}) {}
+ // ```
+ //
+ // #### Common fixes
+ //
+ // If the parameter is really required, then remove the default value:
+ //
+ // ```dart
+ // void log({required String message}) {}
+ // ```
+ //
+ // If the parameter isn't always required, then remove the `required`
+ // modifier:
+ //
+ // ```dart
+ // void log({String message = 'no message'}) {}
+ // ```
static const CompileTimeErrorCode DEFAULT_VALUE_ON_REQUIRED_PARAMETER =
const CompileTimeErrorCode('DEFAULT_VALUE_ON_REQUIRED_PARAMETER',
- "Required named parameters cannot have a default value.",
+ "Required named parameters can't have a default value.",
correction: "Try removing either the default value or the 'required' "
"modifier.");
@@ -932,9 +1081,33 @@
"The exported library '{0}' can't have a part-of directive.",
correction: "Try exporting the library that the part is a part of.");
+ /**
+ * No parameters.
+ */
+ // #### Description
+ //
+ // The analyzer produces this diagnostic when the analyzer finds an
+ // expression, rather than a map entry, in what appears to be a map literal.
+ //
+ // #### Example
+ //
+ // The following code generates this diagnostic:
+ //
+ // ```dart
+ // var map = <String, int>{'a': 0, 'b': 1, !'c'!};
+ // ```
+ //
+ // #### Common fixes
+ //
+ // If the expression is intended to compute either a key or a value in an
+ // entry, fix the issue by completing the code:
+ //
+ // ```dart
+ // var map = <String, int>{'a': 0, 'b': 1, 'c': 2};
+ // ```
static const CompileTimeErrorCode EXPRESSION_IN_MAP =
const CompileTimeErrorCode(
- 'EXPRESSION_IN_MAP', "Expressions cannot be used in a map literal.",
+ 'EXPRESSION_IN_MAP', "Expressions can't be used in a map literal.",
correction:
"Try removing the expression or converting it to be a map entry.");
@@ -1707,13 +1880,50 @@
correction: "Check your Dart SDK installation for completeness.");
/**
- * It is an error if an optional parameter (named or otherwise) with no
- * default value has a potentially non-nullable type.
+ * No parameters.
*/
+ // #### Description
+ //
+ // The analyzer produces this diagnostic when an optional parameter doesn't
+ // have a default value, but has a
+ // <a href=”#potentially-non-nullable”>potentially non-nullable</a> type.
+ // Optional parameters that have no explicit default value have an implicit
+ // default value of `null`. If the type of the parameter doesn't allow the
+ // parameter to have a value of null, then the implicit default value is not
+ // valid.
+ //
+ // #### Example
+ //
+ // The following code generates this diagnostic:
+ //
+ // ```dart
+ // void log({String !message!}) {}
+ // ```
+ //
+ // #### Common fixes
+ //
+ // If the parameter can have the value `null`, then add a question mark after
+ // the type annotation:
+ //
+ // ```dart
+ // void log({String? message}) {}
+ // ```
+ //
+ // If the parameter can't be null, then either provide a default value:
+ //
+ // ```dart
+ // void log({String message = ''}) {}
+ // ```
+ //
+ // or add the `required` modifier to the parameter:
+ //
+ // ```dart
+ // void log({required String message}) {}
+ // ```
static const CompileTimeErrorCode MISSING_DEFAULT_VALUE_FOR_PARAMETER =
const CompileTimeErrorCode(
'MISSING_DEFAULT_VALUE_FOR_PARAMETER',
- "The parameter '{0}' cannot have a value of 'null' because of its "
+ "The parameter '{0}' can't have a value of 'null' because of its "
"type, so it must either be a required parameter or have a "
"default value.",
correction:
@@ -2241,6 +2451,33 @@
"{0} required argument(s) expected, but {1} found.",
correction: "Try adding the missing arguments.");
+ /**
+ * No parameters.
+ */
+ // #### Description
+ //
+ // The analyzer produces this diagnostic when the static type of the
+ // expression of a spread element that appears in either a list literal or a
+ // set literal doesn't implement the type `Iterable`.
+ //
+ // #### Example
+ //
+ // The following code generates this diagnostic:
+ //
+ // ```dart
+ // var m = <String, int>{'a': 0, 'b': 1};
+ // var s = <String>{...m};
+ // ```
+ //
+ // #### Common fixes
+ //
+ // The most common fix is to replace the expression with one that produces an
+ // iterable object:
+ //
+ // ```dart
+ // var m = <String, int>{'a': 0, 'b': 1};
+ // var s = <String>{...m.keys};
+ // ```
static const CompileTimeErrorCode NOT_ITERABLE_SPREAD =
const CompileTimeErrorCode('NOT_ITERABLE_SPREAD',
"Spread elements in list or set literals must implement 'Iterable'.");
@@ -2279,12 +2516,39 @@
correction: "Try removing the question mark.");
/**
- * It is a compile-time error for a class to extend, implement, or mixin a
- * type of the form T? for any T.
+ * No parameters.
*/
+ // #### Description
+ //
+ // The analyzer produces this diagnostic when a class declaration uses an
+ // extends clause to specify a superclass, and the type that's specified is a
+ // nullable type.
+ //
+ // The reason the supertype is a _type_ rather than a class name is to allow
+ // you to control the signatures of the members to be inherited from the
+ // supertype, such as by specifying type arguments. However, the nullability
+ // of a type doesn't change the signatures of any members, so there isn't any
+ // reason to allow the nullability to be specified when used in the extends
+ // clause.
+ //
+ // #### Example
+ //
+ // The following code generates this diagnostic:
+ //
+ // ```dart
+ // class Invalid extends !Duration?! {}
+ // ```
+ //
+ // #### Common fixes
+ //
+ // The most common fix is to remove the question mark:
+ //
+ // ```dart
+ // class Invalid extends Duration {}
+ // ```
static const CompileTimeErrorCode NULLABLE_TYPE_IN_EXTENDS_CLAUSE =
const CompileTimeErrorCode('NULLABLE_TYPE_IN_EXTENDS_CLAUSE',
- "A class cannot extend a nullable type.",
+ "A class can't extend a nullable type.",
correction: "Try removing the question mark.");
/**
diff --git a/pkg/analyzer/tool/diagnostics/diagnostics.md b/pkg/analyzer/tool/diagnostics/diagnostics.md
new file mode 100644
index 0000000..b3fc35b
--- /dev/null
+++ b/pkg/analyzer/tool/diagnostics/diagnostics.md
@@ -0,0 +1,415 @@
+---
+title: Diagnostics
+description: Details for diagnostics produced by the Dart analyzer.
+---
+
+This page lists diagnostic messages produced by the Dart analyzer,
+with details about what those messages mean and how you can fix your code.
+For more information about the analyzer, see
+[Customizing static analysis](/guides/language/analysis-options).
+
+## Glossary
+
+This page uses the following terms.
+
+### Potentially non-nullable
+
+A type is _potentially non-nullable_ if it's either explicitly non-nullable or
+if it's a type parameter. The latter case is included because the actual runtime
+type might be non-nullable.
+
+## Diagnostics
+
+The analyzer produces the following diagnostics for code that
+doesn't conform to the language specification or
+that might work in unexpected ways.
+
+### ambiguous\_set\_or\_map\_literal\_both
+
+_This literal contains both 'Map' and 'Iterable' spreads, which makes it
+impossible to determine whether the literal is a map or a set._
+
+#### Description
+
+Because map and set literals use the same delimiters (`{` and `}`), the
+analyzer looks at the type arguments and the elements to determine which
+kind of literal you meant. When there are no type arguments and all of the
+elements are spread elements (which are allowed in both kinds of literals),
+then the analyzer uses the types of the expressions that are being spread.
+If all of the expressions have the type `Iterable`, then it's a set
+literal; if they all have the type `Map`, then it's a map literal.
+
+The analyzer produces this diagnostic when some of the expressions being
+spread have the type `Iterable` and others have the type `Map`, making it
+impossible for the analyzer to determine whether you are writing a map
+literal or a set literal.
+
+#### Example
+
+The following code produces this diagnostic:
+
+```dart
+union(Map<String, String> a, List<String> b, Map<String, String> c) =>
+ {...a, ...b, ...c};
+```
+
+The list `b` can only be spread into a set, and the maps `a` and `c` can
+only be spread into a map, and the literal can't be both.
+
+#### Common fixes
+
+There are two common ways to fix this problem. The first is to remove all
+of the spread elements of one kind or the other, so that the elements are
+consistent. In this case, that likely means removing the list (and
+deciding what to do about the now unused parameter):
+
+```dart
+union(Map<String, String> a, List<String> b, Map<String, String> c) =>
+ {...a, ...c};
+```
+
+The second fix is to change the elements of one kind into elements that are
+consistent with the other elements. For example, you could add the elements
+of the list as keys that map to themselves:
+
+```dart
+union(Map<String, String> a, List<String> b, Map<String, String> c) =>
+ {...a, for (String s in b) s: s, ...c};
+```
+
+### ambiguous\_set\_or\_map\_literal\_either
+
+_This literal must be either a map or a set, but the elements don't have enough
+type information for type inference to work._
+
+#### Description
+
+Because map and set literals use the same delimiters (`‘{` and `}`), the
+analyzer looks at the type arguments and the elements to determine which
+kind of literal you meant. When there are no type arguments and all of the
+elements are spread elements (which are allowed in both kinds of literals)
+then the analyzer uses the types of the expressions that are being spread
+to decide. If all of the expressions have the type `Iterable`, then it's a
+set literal, if they all have the type `Map`, then it's a map literal.
+
+This diagnostic is produced when none of the expressions being spread has a
+type that allows the analyzer to decide whether you were writing a map
+literal or a set literal.
+
+#### Example
+
+The following code produces this diagnostic:
+
+```dart
+union(a, b) => !{...a, ...b}!;
+```
+
+The problem occurs because there are no type arguments, and there is no
+information about the type of either `a` or `b`.
+
+#### Common fixes
+
+There are three common ways to fix this problem. The first is to add type
+arguments to the literal. For example, if the literal is intended to be a
+map literal, you might write something like this:
+
+```dart
+union(a, b) => <String, String>{...a, ...b};
+```
+
+The second fix is to add type information so that the expressions have
+either the type `Iterable` or the type `Map`. You could add an explicit
+cast or, in this case, add types to the declarations of the two parameters:
+
+```dart
+union(List<int> a, List<int> b) => {...a, ...b};
+```
+
+The third fix is to add context information. In this case, that means
+adding a return type to the function:
+
+```dart
+Set<String> union(a, b) => {...a, ...b};
+```
+
+In other cases, you might add a type somewhere else. For example, say the
+original code looks like this:
+
+```dart
+union(a, b) {
+ var x = {...a, ...b};
+ return x;
+}
+```
+
+You might add a type annotation on `x`, like this:
+
+```dart
+union(a, b) {
+ Map<String, String> x = {...a, ...b};
+ return x;
+}
+```
+
+### default\_value\_on\_required\_parameter
+
+_Required named parameters can't have a default value._
+
+#### Description
+
+The analyzer produces this diagnostic when a named parameter has both the
+`required` modifier and a default value. If the parameter is required, then
+a value for the parameter is always provided at the call sites, so the
+default value can never be used.
+
+#### Example
+
+The following code generates this diagnostic:
+
+```dart
+void log({required String !message! = 'no message'}) {}
+```
+
+#### Common fixes
+
+If the parameter is really required, then remove the default value:
+
+```dart
+void log({required String message}) {}
+```
+
+If the parameter isn't always required, then remove the `required`
+modifier:
+
+```dart
+void log({String message = 'no message'}) {}
+```
+
+### deprecated\_member\_use
+
+_'{0}' is deprecated and shouldn't be used._
+
+#### Description
+
+The analyzer produces this diagnostic when a deprecated library or class
+member is used in a different package.
+
+#### Example
+
+If the method `m` in the class `C` is annotated with `@deprecated`, then
+the following code produces this diagnostic:
+
+```dart
+void f(C c) {
+ c.!m!();
+}
+```
+
+#### Common fixes
+
+The documentation for declarations that are annotated with `@deprecated`
+should have documentation to indicate what code to use in place of the
+deprecated code.
+
+### expression\_in\_map
+
+_Expressions can't be used in a map literal._
+
+#### Description
+
+The analyzer produces this diagnostic when the analyzer finds an
+expression, rather than a map entry, in what appears to be a map literal.
+
+#### Example
+
+The following code generates this diagnostic:
+
+```dart
+var map = <String, int>{'a': 0, 'b': 1, !'c'!};
+```
+
+#### Common fixes
+
+If the expression is intended to compute either a key or a value in an
+entry, fix the issue by completing the code:
+
+```dart
+var map = <String, int>{'a': 0, 'b': 1, 'c': 2};
+```
+
+### invalid\_literal\_annotation
+
+_Only const constructors can have the `@literal` annotation._
+
+#### Description
+
+The meaning of the `@literal` annotation is only defined when it's applied
+to a const constructor.
+
+#### Example
+
+The following code produces this diagnostic:
+
+```dart
+!@literal!
+var x;
+```
+
+#### Common fixes
+
+Remove the annotation:
+
+```dart
+var x;
+```
+
+### missing\_default\_value\_for\_parameter
+
+_The parameter '{0}' can't have a value of 'null' because of its type, so it
+must either be a required parameter or have a default value._
+
+#### Description
+
+The analyzer produces this diagnostic when an optional parameter doesn't
+have a default value, but has a
+<a href=”#potentially-non-nullable”>potentially non-nullable</a> type.
+Optional parameters that have no explicit default value have an implicit
+default value of `null`. If the type of the parameter doesn't allow the
+parameter to have a value of null, then the implicit default value is not
+valid.
+
+#### Example
+
+The following code generates this diagnostic:
+
+```dart
+void log({String !message!}) {}
+```
+
+#### Common fixes
+
+If the parameter can have the value `null`, then add a question mark after
+the type annotation:
+
+```dart
+void log({String? message}) {}
+```
+
+If the parameter can't be null, then either provide a default value:
+
+```dart
+void log({String message = ''}) {}
+```
+
+or add the `required` modifier to the parameter:
+
+```dart
+void log({required String message}) {}
+```
+
+### not\_iterable\_spread
+
+_Spread elements in list or set literals must implement 'Iterable'._
+
+#### Description
+
+The analyzer produces this diagnostic when the static type of the
+expression of a spread element that appears in either a list literal or a
+set literal doesn't implement the type `Iterable`.
+
+#### Example
+
+The following code generates this diagnostic:
+
+```dart
+var m = <String, int>{'a': 0, 'b': 1};
+var s = <String>{...m};
+```
+
+#### Common fixes
+
+The most common fix is to replace the expression with one that produces an
+iterable object:
+
+```dart
+var m = <String, int>{'a': 0, 'b': 1};
+var s = <String>{...m.keys};
+```
+
+### nullable\_type\_in\_extends\_clause
+
+_A class can't extend a nullable type._
+
+#### Description
+
+The analyzer produces this diagnostic when a class declaration uses an
+extends clause to specify a superclass, and the type that's specified is a
+nullable type.
+
+The reason the supertype is a _type_ rather than a class name is to allow
+you to control the signatures of the members to be inherited from the
+supertype, such as by specifying type arguments. However, the nullability
+of a type doesn't change the signatures of any members, so there isn't any
+reason to allow the nullability to be specified when used in the extends
+clause.
+
+#### Example
+
+The following code generates this diagnostic:
+
+```dart
+class Invalid extends !Duration?! {}
+```
+
+#### Common fixes
+
+The most common fix is to remove the question mark:
+
+```dart
+class Invalid extends Duration {}
+```
+
+### sdk\_version\_set\_literal
+
+_Set literals weren't supported until version 2.2, but this code must be able to
+run on earlier versions._
+
+#### Description
+
+The analyzer produces this diagnostic when a set literal is found in code
+that has an SDK constraint whose lower bound is less than 2.2. Set literals
+were not supported in earlier versions, so this code won't be able to run
+against earlier versions of the SDK.
+
+#### Example
+
+In a package that defines SDK constraints in the `pubspec.yaml` file that
+have a lower bound that's less than 2.2:
+
+```yaml
+environment:
+ sdk: '>=2.1.0 <2.4.0'
+```
+
+The following code generates this diagnostic:
+
+```dart
+var s = !<int>{}!;
+```
+
+#### Common fixes
+
+If you don't need to support older versions of the SDK, then you can
+increase the SDK constraint to allow the syntax to be used:
+
+```yaml
+environment:
+ sdk: '>=2.2.0 <2.4.0'
+```
+
+If you do need to support older versions of the SDK, then replace the set
+literal with code that creates the set without the use of a literal:
+
+```dart
+var s = new Set<int>();
+```
diff --git a/pkg/analyzer/tool/diagnostics/generate.dart b/pkg/analyzer/tool/diagnostics/generate.dart
new file mode 100644
index 0000000..17350b5
--- /dev/null
+++ b/pkg/analyzer/tool/diagnostics/generate.dart
@@ -0,0 +1,216 @@
+// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:io';
+
+import 'package:analyzer/dart/analysis/analysis_context_collection.dart';
+import 'package:analyzer/dart/analysis/results.dart';
+import 'package:analyzer/dart/analysis/session.dart';
+import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer/dart/ast/token.dart';
+import 'package:analyzer/file_system/physical_file_system.dart';
+import 'package:front_end/src/testing/package_root.dart' as package_root;
+import 'package:path/src/context.dart';
+
+/// Generate the file `diagnostics.md` based on the documentation associated
+/// with the declarations of the error codes.
+void main() async {
+ Context pathContext = PhysicalResourceProvider.INSTANCE.pathContext;
+ String packageRoot = pathContext.normalize(package_root.packageRoot);
+ String analyzerPath = pathContext.join(packageRoot, 'analyzer');
+ List<String> docPaths = [
+ pathContext.join(
+ analyzerPath, 'lib', 'src', 'dart', 'error', 'hint_codes.dart'),
+ pathContext.join(analyzerPath, 'lib', 'src', 'error', 'codes.dart'),
+ ];
+ String outputPath =
+ pathContext.join(analyzerPath, 'tool', 'diagnostics', 'diagnostics.md');
+
+ DocumentationGenerator generator = DocumentationGenerator(docPaths);
+ generator.writeDocumentation(outputPath);
+}
+
+/// A class used to generate diagnostic documentation.
+class DocumentationGenerator {
+ /// The absolute paths of the files containing the declarations of the error
+ /// codes.
+ final List<String> docPaths;
+
+ /// A map from the name of a diagnostic code to the lines of the documentation
+ /// for that code.
+ Map<String, List<String>> docsByCode = {};
+
+ /// Initialize a newly created documentation generator.
+ DocumentationGenerator(this.docPaths) {
+ _extractAllDocs();
+ }
+
+ /// Write the documentation to the file at the given [outputPath].
+ void writeDocumentation(String outputPath) async {
+ IOSink sink = File(outputPath).openWrite();
+ _writeHeader(sink);
+ _writeGlossary(sink);
+ _writeDiagnostics(sink);
+ await sink.flush();
+ await sink.close();
+ }
+
+ /// Return a version of the [text] in which characters that have special
+ /// meaning in markdown have been escaped.
+ String _escape(String text) {
+ return text.replaceAll('_', '\\_');
+ }
+
+ /// Extract documentation from all of the files containing the definitions of
+ /// diagnostics.
+ void _extractAllDocs() {
+ AnalysisContextCollection collection = new AnalysisContextCollection(
+ includedPaths: docPaths,
+ resourceProvider: PhysicalResourceProvider.INSTANCE);
+ for (String docPath in docPaths) {
+ _extractDocs(_parse(collection, docPath));
+ }
+ }
+
+ /// Extract documentation from the given [field] declaration.
+ List<String> _extractDoc(FieldDeclaration field) {
+ Token comments = field.firstTokenAfterCommentAndMetadata.precedingComments;
+ if (comments == null) {
+ return null;
+ }
+ List<String> docs = [];
+ while (comments != null) {
+ String lexeme = comments.lexeme;
+ if (lexeme.startsWith('// TODO')) {
+ break;
+ } else if (lexeme.startsWith('// ')) {
+ docs.add(lexeme.substring(3));
+ } else if (lexeme == '//') {
+ docs.add('');
+ }
+ comments = comments.next;
+ }
+ if (docs.isEmpty) {
+ return null;
+ }
+ return docs;
+ }
+
+ /// Extract documentation from the file that was parsed to produce the given
+ /// [result].
+ void _extractDocs(ParsedUnitResult result) {
+ CompilationUnit unit = result.unit;
+ for (CompilationUnitMember declaration in unit.declarations) {
+ if (declaration is ClassDeclaration) {
+ for (ClassMember member in declaration.members) {
+ if (member is FieldDeclaration) {
+ List<String> docs = _extractDoc(member);
+ if (docs != null) {
+ VariableDeclaration variable = member.fields.variables[0];
+ String variableName = variable.name.name;
+ if (docsByCode.containsKey(variableName)) {
+ throw StateError('Duplicate diagnostic code');
+ }
+ String message =
+ ((variable.initializer as InstanceCreationExpression)
+ .argumentList
+ .arguments[1] as StringLiteral)
+ .stringValue;
+ docs = [
+ '### ${_escape(variableName.toLowerCase())}',
+ '',
+ ..._split('_${_escape(message)}_'),
+ '',
+ ...docs,
+ ];
+ docsByCode[variableName] = docs;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /// Use the analysis context [collection] to parse the file at the given
+ /// [path] and return the result.
+ ParsedUnitResult _parse(AnalysisContextCollection collection, String path) {
+ AnalysisSession session = collection.contextFor(path).currentSession;
+ if (session == null) {
+ throw new StateError('No session for "$path"');
+ }
+ ParsedUnitResult result = session.getParsedUnit(path);
+ if (result.state != ResultState.VALID) {
+ throw new StateError('Unable to parse "$path"');
+ }
+ return result;
+ }
+
+ /// Split the [message] into multiple lines, each of which is less than 80
+ /// characters long.
+ List<String> _split(String message) {
+ // This uses a brute force approach because we don't expect to have messages
+ // that need to be split more than once.
+ int length = message.length;
+ if (length <= 80) {
+ return [message];
+ }
+ int endIndex = message.lastIndexOf(' ', 80);
+ if (endIndex < 0) {
+ return [message];
+ }
+ return [message.substring(0, endIndex), message.substring(endIndex + 1)];
+ }
+
+ /// Write the documentation for all of the diagnostics.
+ void _writeDiagnostics(IOSink sink) {
+ sink.write('''
+
+## Diagnostics
+
+The analyzer produces the following diagnostics for code that
+doesn't conform to the language specification or
+that might work in unexpected ways.
+''');
+ List<String> errorCodes = docsByCode.keys.toList();
+ errorCodes.sort();
+ for (String errorCode in errorCodes) {
+ List<String> docs = docsByCode[errorCode];
+ sink.writeln();
+ for (String line in docs) {
+ sink.writeln(line);
+ }
+ }
+ }
+
+ /// Write the glossary.
+ void _writeGlossary(IOSink sink) {
+ sink.write('''
+
+## Glossary
+
+This page uses the following terms.
+
+### Potentially non-nullable
+
+A type is _potentially non-nullable_ if it's either explicitly non-nullable or
+if it's a type parameter. The latter case is included because the actual runtime
+type might be non-nullable.
+''');
+ }
+
+ /// Write the header of the file.
+ void _writeHeader(IOSink sink) {
+ sink.write('''
+---
+title: Diagnostics
+description: Details for diagnostics produced by the Dart analyzer.
+---
+
+This page lists diagnostic messages produced by the Dart analyzer,
+with details about what those messages mean and how you can fix your code.
+For more information about the analyzer, see
+[Customizing static analysis](/guides/language/analysis-options).
+''');
+ }
+}