Version 2.12.0-225.0.dev
Merge commit '48503619f85c72ab4a522e4758746121ce85f797' into 'dev'
diff --git a/pkg/analysis_server/test/abstract_single_unit.dart b/pkg/analysis_server/test/abstract_single_unit.dart
index 504662a..006b6c1 100644
--- a/pkg/analysis_server/test/abstract_single_unit.dart
+++ b/pkg/analysis_server/test/abstract_single_unit.dart
@@ -4,7 +4,6 @@
import 'package:analyzer/dart/analysis/results.dart';
import 'package:analyzer/dart/ast/ast.dart';
-import 'package:analyzer/dart/ast/visitor.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/error/error.dart';
import 'package:analyzer/file_system/file_system.dart';
@@ -12,6 +11,7 @@
import 'package:analyzer/src/dart/ast/utilities.dart';
import 'package:analyzer/src/dart/error/hint_codes.dart';
import 'package:analyzer/src/generated/java_engine.dart';
+import 'package:analyzer/src/test_utilities/find_element.dart';
import 'package:analyzer/src/test_utilities/find_node.dart';
import 'package:analyzer/src/test_utilities/platform.dart';
import 'package:test/test.dart';
@@ -31,6 +31,7 @@
CompilationUnitElement testUnitElement;
LibraryElement testLibraryElement;
FindNode findNode;
+ FindElement findElement;
@override
void addSource(String path, String content) {
@@ -48,10 +49,6 @@
addSource(testFile, code);
}
- Element findElement(String name, [ElementKind kind]) {
- return findChildElement(testUnitElement, name, kind);
- }
-
int findEnd(String search) {
return findOffset(search) + search.length;
}
@@ -61,16 +58,6 @@
return findNodeAtString(search, (node) => node is SimpleIdentifier);
}
- /// Search the [testUnit] for the [LocalVariableElement] with the given
- /// [name]. Fail if there is not exactly one such variable.
- LocalVariableElement findLocalVariable(String name) {
- var finder = _ElementsByNameFinder(name);
- testUnit.accept(finder);
- var localVariables = finder.elements.whereType<LocalVariableElement>();
- expect(localVariables, hasLength(1));
- return localVariables.single;
- }
-
AstNode findNodeAtOffset(int offset, [Predicate<AstNode> predicate]) {
var result = NodeLocator(offset).searchWithin(testUnit);
if (result != null && predicate != null) {
@@ -150,6 +137,7 @@
testUnitElement = testUnit.declaredElement;
testLibraryElement = testUnitElement.library;
findNode = FindNode(testCode, testUnit);
+ findElement = FindElement(testUnit);
}
@override
@@ -158,17 +146,3 @@
testFile = convertPath('/home/test/lib/test.dart');
}
}
-
-class _ElementsByNameFinder extends RecursiveAstVisitor<void> {
- final String name;
- final List<Element> elements = [];
-
- _ElementsByNameFinder(this.name);
-
- @override
- void visitSimpleIdentifier(SimpleIdentifier node) {
- if (node.name == name && node.inDeclarationContext()) {
- elements.add(node.staticElement);
- }
- }
-}
diff --git a/pkg/analysis_server/test/services/correction/name_suggestion_test.dart b/pkg/analysis_server/test/services/correction/name_suggestion_test.dart
index 07e8f81..a4dce49 100644
--- a/pkg/analysis_server/test/services/correction/name_suggestion_test.dart
+++ b/pkg/analysis_server/test/services/correction/name_suggestion_test.dart
@@ -38,7 +38,7 @@
}
''');
var excluded = <String>{};
- var expectedType = findLocalVariable('node').type;
+ var expectedType = findElement.localVar('node').type;
Expression assignedExpression =
findNodeAtString('null;', (node) => node is NullLiteral);
var suggestions = getVariableNameSuggestionsForExpression(
@@ -52,7 +52,7 @@
double res = 0.0;
}
''');
- var expectedType = findLocalVariable('res').type;
+ var expectedType = findElement.localVar('res').type;
Expression assignedExpression = findNodeAtString('0.0;');
// first choice for "double" is "d"
expect(
@@ -72,7 +72,7 @@
int res = 0;
}
''');
- var expectedType = findLocalVariable('res').type;
+ var expectedType = findElement.localVar('res').type;
Expression assignedExpression = findNodeAtString('0;');
// first choice for "int" is "i"
expect(
@@ -92,7 +92,7 @@
String res = 'abc';
}
''');
- var expectedType = findLocalVariable('res').type;
+ var expectedType = findElement.localVar('res').type;
Expression assignedExpression = findNodeAtString("'abc';");
// first choice for "String" is "s"
expect(
diff --git a/pkg/analysis_server/test/services/correction/status_test.dart b/pkg/analysis_server/test/services/correction/status_test.dart
index 957e9cb..ac7f555 100644
--- a/pkg/analysis_server/test/services/correction/status_test.dart
+++ b/pkg/analysis_server/test/services/correction/status_test.dart
@@ -24,7 +24,7 @@
class RefactoringLocationTest extends AbstractSingleUnitTest {
Future<void> test_createLocation_forElement() async {
await resolveTestCode('class MyClass {}');
- var element = findElement('MyClass');
+ var element = findElement.class_('MyClass');
// check
var location = newLocation_fromElement(element);
expect(location.file, testFile);
@@ -36,7 +36,7 @@
Future<void> test_createLocation_forMatch() async {
await resolveTestCode('class MyClass {}');
- var element = findElement('MyClass');
+ var element = findElement.class_('MyClass');
var sourceRange = range.elementName(element);
SearchMatch match = SearchMatchImpl(
element.source.fullName,
diff --git a/pkg/analysis_server/test/services/refactoring/convert_getter_to_method_test.dart b/pkg/analysis_server/test/services/refactoring/convert_getter_to_method_test.dart
index fe8738b..5f334d4 100644
--- a/pkg/analysis_server/test/services/refactoring/convert_getter_to_method_test.dart
+++ b/pkg/analysis_server/test/services/refactoring/convert_getter_to_method_test.dart
@@ -28,7 +28,10 @@
var b = test;
}
''');
- _createRefactoring('test');
+ var element = findElement.topGet('test');
+ _createRefactoringForElement(
+ element,
+ );
// apply refactoring
return _assertSuccessfulRefactoring('''
int test() => 42;
@@ -120,7 +123,8 @@
main() {
}
''');
- _createRefactoring('test');
+ var element = findElement.topGet('test');
+ _createRefactoringForElement(element);
// check conditions
await _assertInitialConditions_fatal(
'Only explicit getters can be converted to methods.');
@@ -141,12 +145,6 @@
assertTestChangeResult(expectedCode);
}
- void _createRefactoring(String elementName) {
- PropertyAccessorElement element =
- findElement(elementName, ElementKind.GETTER);
- _createRefactoringForElement(element);
- }
-
void _createRefactoringForElement(ExecutableElement element) {
refactoring = ConvertGetterToMethodRefactoring(
searchEngine, testAnalysisResult.session, element);
diff --git a/pkg/analysis_server/test/services/refactoring/convert_method_to_getter_test.dart b/pkg/analysis_server/test/services/refactoring/convert_method_to_getter_test.dart
index 7195696..e80bb98 100644
--- a/pkg/analysis_server/test/services/refactoring/convert_method_to_getter_test.dart
+++ b/pkg/analysis_server/test/services/refactoring/convert_method_to_getter_test.dart
@@ -30,7 +30,8 @@
var b = test();
}
''');
- _createRefactoring('test');
+ var element = findElement.topFunction('test');
+ _createRefactoringForElement(element);
// apply refactoring
return _assertSuccessfulRefactoring('''
int get test => 42;
@@ -124,7 +125,7 @@
var b = test;
}
''');
- ExecutableElement element = findElement('test', ElementKind.GETTER);
+ var element = findElement.topGet('test');
_createRefactoringForElement(element);
// check conditions
await _assertInitialConditions_fatal(
@@ -138,7 +139,8 @@
var v = test(1);
}
''');
- _createRefactoring('test');
+ var element = findElement.topFunction('test');
+ _createRefactoringForElement(element);
// check conditions
await _assertInitialConditions_fatal(
'Only methods without parameters can be converted to getters.');
@@ -164,7 +166,8 @@
A.test();
}
''');
- _createRefactoring('test');
+ var element = findElement.constructor('test');
+ _createRefactoringForElement(element);
// check conditions
await _assertInitialConditions_fatal(
'Only class methods or top-level functions can be converted to getters.');
@@ -174,7 +177,8 @@
await indexTestUnit('''
void test() {}
''');
- _createRefactoring('test');
+ var element = findElement.topFunction('test');
+ _createRefactoringForElement(element);
// check conditions
await _assertInitialConditions_fatal(
'Cannot convert function returning void.');
@@ -195,11 +199,6 @@
assertTestChangeResult(expectedCode);
}
- void _createRefactoring(String elementName) {
- ExecutableElement element = findElement(elementName);
- _createRefactoringForElement(element);
- }
-
void _createRefactoringForElement(ExecutableElement element) {
refactoring = ConvertMethodToGetterRefactoring(
searchEngine, testAnalysisResult.session, element);
diff --git a/pkg/analysis_server/test/services/search/hierarchy_test.dart b/pkg/analysis_server/test/services/search/hierarchy_test.dart
index 4dbec50..e576ae2 100644
--- a/pkg/analysis_server/test/services/search/hierarchy_test.dart
+++ b/pkg/analysis_server/test/services/search/hierarchy_test.dart
@@ -41,12 +41,12 @@
}
''');
{
- ClassElement classA = findElement('A');
+ var classA = findElement.class_('A');
var members = getClassMembers(classA);
expect(members.map((e) => e.name), unorderedEquals(['ma1', 'ma2']));
}
{
- ClassElement classB = findElement('B');
+ var classB = findElement.class_('B');
var members = getClassMembers(classB);
expect(members.map((e) => e.name), unorderedEquals(['mb1', 'mb2']));
}
@@ -61,8 +61,8 @@
B() {}
}
''');
- ClassElement classA = findElement('A');
- ClassElement classB = findElement('B');
+ var classA = findElement.class_('A');
+ var classB = findElement.class_('B');
ClassMemberElement memberA = classA.constructors[0];
ClassMemberElement memberB = classB.constructors[0];
var futureA = getHierarchyMembers(searchEngine, memberA).then((members) {
@@ -89,10 +89,10 @@
int foo;
}
''');
- ClassElement classA = findElement('A');
- ClassElement classB = findElement('B');
- ClassElement classC = findElement('C');
- ClassElement classD = findElement('D');
+ var classA = findElement.class_('A');
+ var classB = findElement.class_('B');
+ var classC = findElement.class_('C');
+ var classD = findElement.class_('D');
ClassMemberElement memberA = classA.fields[0];
ClassMemberElement memberB = classB.fields[0];
ClassMemberElement memberC = classC.fields[0];
@@ -124,9 +124,9 @@
static set foo(x) {}
}
''');
- ClassElement classA = findElement('A');
- ClassElement classB = findElement('B');
- ClassElement classC = findElement('C');
+ var classA = findElement.class_('A');
+ var classB = findElement.class_('B');
+ var classC = findElement.class_('C');
ClassMemberElement memberA = classA.fields[0];
ClassMemberElement memberB = classB.fields[0];
ClassMemberElement memberC = classC.fields[0];
@@ -162,11 +162,11 @@
foo() {}
}
''');
- ClassElement classA = findElement('A');
- ClassElement classB = findElement('B');
- ClassElement classC = findElement('C');
- ClassElement classD = findElement('D');
- ClassElement classE = findElement('E');
+ var classA = findElement.class_('A');
+ var classB = findElement.class_('B');
+ var classC = findElement.class_('C');
+ var classD = findElement.class_('D');
+ var classE = findElement.class_('E');
ClassMemberElement memberA = classA.methods[0];
ClassMemberElement memberB = classB.methods[0];
ClassMemberElement memberC = classC.methods[0];
@@ -199,8 +199,8 @@
static foo() {}
}
''');
- ClassElement classA = findElement('A');
- ClassElement classB = findElement('B');
+ var classA = findElement.class_('A');
+ var classB = findElement.class_('B');
ClassMemberElement memberA = classA.methods[0];
ClassMemberElement memberB = classB.methods[0];
{
@@ -230,9 +230,9 @@
foo() {}
}
''');
- ClassElement classA = findElement('A');
- ClassElement classB = findElement('B');
- ClassElement classD = findElement('D');
+ var classA = findElement.class_('A');
+ var classB = findElement.class_('B');
+ var classD = findElement.class_('D');
ClassMemberElement memberA = classA.methods[0];
ClassMemberElement memberB = classB.methods[0];
ClassMemberElement memberD = classD.methods[0];
@@ -266,11 +266,11 @@
foo({p}) {}
}
''');
- ClassElement classA = findElement('A');
- ClassElement classB = findElement('B');
- ClassElement classC = findElement('C');
- ClassElement classD = findElement('D');
- ClassElement classE = findElement('E');
+ var classA = findElement.class_('A');
+ var classB = findElement.class_('B');
+ var classC = findElement.class_('C');
+ var classD = findElement.class_('D');
+ var classE = findElement.class_('E');
var parameterA = classA.methods[0].parameters[0];
var parameterB = classB.methods[0].parameters[0];
var parameterC = classC.methods[0].parameters[0];
@@ -313,7 +313,7 @@
foo() {}
}
''');
- ClassElement classA = findElement('A');
+ var classA = findElement.class_('A');
var parameterA = classA.methods[0].parameters[0];
var result = await getHierarchyNamedParameters(searchEngine, parameterA);
@@ -330,7 +330,7 @@
foo(p) {}
}
''');
- ClassElement classA = findElement('A');
+ var classA = findElement.class_('A');
var parameterA = classA.methods[0].parameters[0];
var result = await getHierarchyNamedParameters(searchEngine, parameterA);
@@ -352,7 +352,7 @@
}
''');
{
- ClassElement classA = findElement('A');
+ var classA = findElement.class_('A');
var members = getMembers(classA);
expect(
members.map((e) => e.name),
@@ -367,7 +367,7 @@
]));
}
{
- ClassElement classB = findElement('B');
+ var classB = findElement.class_('B');
var members = getMembers(classB);
expect(
members.map((e) => e.name),
@@ -395,12 +395,12 @@
class E extends A with M {}
class F implements A {}
''');
- ClassElement classA = findElement('A');
- ClassElement classB = findElement('B');
- ClassElement classC = findElement('C');
- ClassElement classD = findElement('D');
- ClassElement classE = findElement('E');
- ClassElement classF = findElement('F');
+ var classA = findElement.class_('A');
+ var classB = findElement.class_('B');
+ var classC = findElement.class_('C');
+ var classD = findElement.class_('D');
+ var classE = findElement.class_('E');
+ var classF = findElement.class_('F');
var objectElement = classA.supertype.element;
// Object
{
@@ -451,14 +451,14 @@
mixin M4 on M2 {}
mixin M5 on A, C {}
''');
- ClassElement a = findElement('A');
- ClassElement b = findElement('B');
- ClassElement c = findElement('C');
- ClassElement m1 = findElement('M1');
- ClassElement m2 = findElement('M2');
- ClassElement m3 = findElement('M3');
- ClassElement m4 = findElement('M4');
- ClassElement m5 = findElement('M5');
+ var a = findElement.class_('A');
+ var b = findElement.class_('B');
+ var c = findElement.class_('C');
+ var m1 = findElement.mixin('M1');
+ var m2 = findElement.mixin('M2');
+ var m3 = findElement.mixin('M3');
+ var m4 = findElement.mixin('M4');
+ var m5 = findElement.mixin('M5');
var object = a.supertype.element;
_assertSuperClasses(object, []);
diff --git a/pkg/front_end/lib/src/fasta/fasta_codes_cfe_generated.dart b/pkg/front_end/lib/src/fasta/fasta_codes_cfe_generated.dart
index 7e29218..40b6228 100644
--- a/pkg/front_end/lib/src/fasta/fasta_codes_cfe_generated.dart
+++ b/pkg/front_end/lib/src/fasta/fasta_codes_cfe_generated.dart
@@ -4590,6 +4590,44 @@
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
const Template<
+ Message Function(
+ DartType _type,
+ DartType _type2,
+ bool
+ isNonNullableByDefault)> templateSuperBoundedHint = const Template<
+ Message Function(
+ DartType _type, DartType _type2, bool isNonNullableByDefault)>(
+ messageTemplate:
+ r"""If you want '#type' to be a super-bounded type, note that the inverted type '#type2' must then satisfy its bounds, which it does not.""",
+ withArguments: _withArgumentsSuperBoundedHint);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Code<
+ Message Function(
+ DartType _type, DartType _type2, bool isNonNullableByDefault)>
+ codeSuperBoundedHint = const Code<
+ Message Function(
+ DartType _type, DartType _type2, bool isNonNullableByDefault)>(
+ "SuperBoundedHint",
+ severity: Severity.context);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+Message _withArgumentsSuperBoundedHint(
+ DartType _type, DartType _type2, bool isNonNullableByDefault) {
+ TypeLabeler labeler = new TypeLabeler(isNonNullableByDefault);
+ List<Object> typeParts = labeler.labelType(_type);
+ List<Object> type2Parts = labeler.labelType(_type2);
+ String type = typeParts.join();
+ String type2 = type2Parts.join();
+ return new Message(codeSuperBoundedHint,
+ message:
+ """If you want '${type}' to be a super-bounded type, note that the inverted type '${type2}' must then satisfy its bounds, which it does not.""" +
+ labeler.originMessages,
+ arguments: {'type': _type, 'type2': _type2});
+}
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Template<
Message Function(
DartType _type, DartType _type2, bool isNonNullableByDefault)>
templateSwitchExpressionNotAssignable = const Template<
diff --git a/pkg/front_end/lib/src/fasta/kernel/type_labeler.dart b/pkg/front_end/lib/src/fasta/kernel/type_labeler.dart
index 76a901f..aca8810 100644
--- a/pkg/front_end/lib/src/fasta/kernel/type_labeler.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/type_labeler.dart
@@ -35,6 +35,7 @@
SymbolConstant,
TearOffConstant,
TreeNode,
+ Typedef,
TypedefType,
TypeLiteralConstant,
TypeParameter,
@@ -142,7 +143,26 @@
}
void defaultDartType(DartType type) {}
- void visitTypedefType(TypedefType node) {}
+
+ void visitTypedefType(TypedefType node) {
+ Typedef typedefNode = node.typedefNode;
+ result.add(nameForEntity(
+ typedefNode,
+ typedefNode.name,
+ typedefNode.enclosingLibrary.importUri,
+ typedefNode.enclosingLibrary.fileUri));
+ if (node.typeArguments.isNotEmpty) {
+ result.add("<");
+ bool first = true;
+ for (DartType typeArg in node.typeArguments) {
+ if (!first) result.add(", ");
+ typeArg.accept(this);
+ first = false;
+ }
+ result.add(">");
+ }
+ addNullability(node.nullability);
+ }
void visitInvalidType(InvalidType node) {
// TODO(askesc): Throw internal error if InvalidType appears in diagnostics.
diff --git a/pkg/front_end/lib/src/fasta/source/source_class_builder.dart b/pkg/front_end/lib/src/fasta/source/source_class_builder.dart
index 6c3d8e8..7debb1a 100644
--- a/pkg/front_end/lib/src/fasta/source/source_class_builder.dart
+++ b/pkg/front_end/lib/src/fasta/source/source_class_builder.dart
@@ -418,8 +418,8 @@
typeParameter.name,
Variance.keywordString(variance));
}
- library.reportTypeArgumentIssue(
- message, fileUri, fileOffset, typeParameter);
+ library.reportTypeArgumentIssue(message, fileUri, fileOffset,
+ typeParameter: typeParameter);
}
}
@@ -453,18 +453,26 @@
bool inferred = libraryBuilder.inferredTypes.contains(argument);
if (isGenericFunctionTypeOrAlias(argument)) {
if (inferred) {
+ // Supertype can't be or contain super-bounded types, so null is
+ // passed for super-bounded hint here.
libraryBuilder.reportTypeArgumentIssue(
templateGenericFunctionTypeInferredAsActualTypeArgument
.withArguments(argument, library.isNonNullableByDefault),
fileUri,
charOffset,
- null);
+ typeParameter: null,
+ superBoundedAttempt: null,
+ superBoundedAttemptInverted: null);
} else {
+ // Supertype can't be or contain super-bounded types, so null is
+ // passed for super-bounded hint here.
libraryBuilder.reportTypeArgumentIssue(
messageGenericFunctionTypeUsedAsActualTypeArgument,
fileUri,
charOffset,
- null);
+ typeParameter: null,
+ superBoundedAttempt: null,
+ superBoundedAttemptInverted: null);
}
} else {
void reportProblem(
@@ -472,6 +480,8 @@
Message Function(DartType, DartType, String, String, String,
String, bool)>
template) {
+ // Supertype can't be or contain super-bounded types, so null is
+ // passed for super-bounded hint here.
libraryBuilder.reportTypeArgumentIssue(
template.withArguments(
argument,
@@ -483,7 +493,9 @@
library.isNonNullableByDefault),
fileUri,
charOffset,
- typeParameter);
+ typeParameter: typeParameter,
+ superBoundedAttempt: null,
+ superBoundedAttemptInverted: null);
}
if (inferred) {
diff --git a/pkg/front_end/lib/src/fasta/source/source_library_builder.dart b/pkg/front_end/lib/src/fasta/source/source_library_builder.dart
index f46bba9..945cd98 100644
--- a/pkg/front_end/lib/src/fasta/source/source_library_builder.dart
+++ b/pkg/front_end/lib/src/fasta/source/source_library_builder.dart
@@ -3287,20 +3287,30 @@
}
}
- reportTypeArgumentIssue(message, fileUri, offset, typeParameter);
+ reportTypeArgumentIssue(message, fileUri, offset,
+ typeParameter: typeParameter,
+ superBoundedAttempt: issue.enclosingType,
+ superBoundedAttemptInverted: issue.invertedType);
}
}
void reportTypeArgumentIssue(Message message, Uri fileUri, int fileOffset,
- TypeParameter typeParameter) {
+ {TypeParameter typeParameter,
+ DartType superBoundedAttempt,
+ DartType superBoundedAttemptInverted}) {
List<LocatedMessage> context;
if (typeParameter != null && typeParameter.fileOffset != -1) {
// It looks like when parameters come from patch files, they don't
// have a reportable location.
- context = <LocatedMessage>[
- messageIncorrectTypeArgumentVariable.withLocation(
- typeParameter.location.file, typeParameter.fileOffset, noLength)
- ];
+ (context ??= <LocatedMessage>[]).add(
+ messageIncorrectTypeArgumentVariable.withLocation(
+ typeParameter.location.file, typeParameter.fileOffset, noLength));
+ }
+ if (superBoundedAttemptInverted != null && superBoundedAttempt != null) {
+ (context ??= <LocatedMessage>[]).add(templateSuperBoundedHint
+ .withArguments(superBoundedAttempt, superBoundedAttemptInverted,
+ isNonNullableByDefault)
+ .withLocation(fileUri, fileOffset, noLength));
}
addProblem(message, fileOffset, noLength, fileUri, context: context);
}
@@ -3389,7 +3399,7 @@
messageGenericFunctionTypeUsedAsActualTypeArgument,
fileUri,
parameter.fileOffset,
- null);
+ typeParameter: null);
} else {
reportTypeArgumentIssue(
templateIncorrectTypeArgument.withArguments(
@@ -3400,7 +3410,9 @@
library.isNonNullableByDefault),
fileUri,
parameter.fileOffset,
- typeParameter);
+ typeParameter: typeParameter,
+ superBoundedAttempt: issue.enclosingType,
+ superBoundedAttemptInverted: issue.invertedType);
}
}
}
@@ -3461,7 +3473,7 @@
messageGenericFunctionTypeUsedAsActualTypeArgument,
fileUri,
fileOffset,
- null);
+ typeParameter: null);
} else {
reportTypeArgumentIssue(
templateIncorrectTypeArgumentInReturnType.withArguments(
@@ -3472,7 +3484,9 @@
isNonNullableByDefault),
fileUri,
fileOffset,
- typeParameter);
+ typeParameter: typeParameter,
+ superBoundedAttempt: issue.enclosingType,
+ superBoundedAttemptInverted: issue.invertedType);
}
}
}
diff --git a/pkg/front_end/messages.yaml b/pkg/front_end/messages.yaml
index db4e95d..1f25446 100644
--- a/pkg/front_end/messages.yaml
+++ b/pkg/front_end/messages.yaml
@@ -4139,6 +4139,10 @@
template: "This is the type variable whose bound isn't conformed to."
severity: CONTEXT
+SuperBoundedHint:
+ template: "If you want '#type' to be a super-bounded type, note that the inverted type '#type2' must then satisfy its bounds, which it does not."
+ severity: CONTEXT
+
InferredPackageUri:
template: "Interpreting this as package URI, '#uri'."
severity: WARNING
diff --git a/pkg/front_end/test/spell_checking_list_common.txt b/pkg/front_end/test/spell_checking_list_common.txt
index 040d1fe..4d427f4 100644
--- a/pkg/front_end/test/spell_checking_list_common.txt
+++ b/pkg/front_end/test/spell_checking_list_common.txt
@@ -649,6 +649,7 @@
convenient
conventional
conventions
+conversion
convert
converted
convertible
@@ -994,6 +995,7 @@
enforcement
engine
engineers
+enhance
enhancing
enough
ensure
@@ -1595,6 +1597,7 @@
invalidation
invariant
invariantly
+inversion
invert
inverted
investigate
diff --git a/pkg/front_end/test/spell_checking_list_messages.txt b/pkg/front_end/test/spell_checking_list_messages.txt
index debea18..b0f4e5e 100644
--- a/pkg/front_end/test/spell_checking_list_messages.txt
+++ b/pkg/front_end/test/spell_checking_list_messages.txt
@@ -10,6 +10,7 @@
# automatic tools might move it to the top of the file.
JS
+adjusting
argument(s)
assigning
b
diff --git a/pkg/front_end/test/spell_checking_list_tests.txt b/pkg/front_end/test/spell_checking_list_tests.txt
index c2db70b..915fdea 100644
--- a/pkg/front_end/test/spell_checking_list_tests.txt
+++ b/pkg/front_end/test/spell_checking_list_tests.txt
@@ -280,11 +280,25 @@
foo13
foo14
foo15
+foo1a
+foo1b
foo2
+foo2a
+foo2b
+foo3a
+foo3b
foo4
+foo4a
+foo4b
foo5
+foo5a
+foo5b
foo6
+foo6a
+foo6b
foo7
+foo7a
+foo7b
foo8
foo9
foo\"bar'baz
diff --git a/pkg/front_end/testcases/nnbd/issue42546.dart.outline.expect b/pkg/front_end/testcases/nnbd/issue42546.dart.outline.expect
index 8826a30..819fad7 100644
--- a/pkg/front_end/testcases/nnbd/issue42546.dart.outline.expect
+++ b/pkg/front_end/testcases/nnbd/issue42546.dart.outline.expect
@@ -28,19 +28,19 @@
Extra constant evaluation status:
-Evaluated: SymbolLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:664:13 -> SymbolConstant(#catchError)
-Evaluated: ListLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:664:13 -> ListConstant(const <Type*>[])
-Evaluated: SymbolLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:664:13 -> SymbolConstant(#test)
-Evaluated: SymbolLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:701:13 -> SymbolConstant(#whenComplete)
-Evaluated: ListLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:701:13 -> ListConstant(const <Type*>[])
-Evaluated: MapLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:701:13 -> InstanceConstant(const _ImmutableMap<Symbol*, dynamic>{_ImmutableMap._kvPairs: const <dynamic>[]})
-Evaluated: SymbolLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:728:13 -> SymbolConstant(#timeout)
-Evaluated: ListLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:728:13 -> ListConstant(const <Type*>[])
-Evaluated: SymbolLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:728:13 -> SymbolConstant(#onTimeout)
-Evaluated: SymbolLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:625:13 -> SymbolConstant(#then)
-Evaluated: SymbolLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:625:13 -> SymbolConstant(#onError)
-Evaluated: SymbolLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:712:13 -> SymbolConstant(#asStream)
-Evaluated: ListLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:712:13 -> ListConstant(const <Type*>[])
-Evaluated: ListLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:712:13 -> ListConstant(const <dynamic>[])
-Evaluated: MapLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:712:13 -> InstanceConstant(const _ImmutableMap<Symbol*, dynamic>{_ImmutableMap._kvPairs: const <dynamic>[]})
+Evaluated: SymbolLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:639:13 -> SymbolConstant(#catchError)
+Evaluated: ListLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:639:13 -> ListConstant(const <Type*>[])
+Evaluated: SymbolLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:639:13 -> SymbolConstant(#test)
+Evaluated: SymbolLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:675:13 -> SymbolConstant(#whenComplete)
+Evaluated: ListLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:675:13 -> ListConstant(const <Type*>[])
+Evaluated: MapLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:675:13 -> InstanceConstant(const _ImmutableMap<Symbol*, dynamic>{_ImmutableMap._kvPairs: const <dynamic>[]})
+Evaluated: SymbolLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:698:13 -> SymbolConstant(#timeout)
+Evaluated: ListLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:698:13 -> ListConstant(const <Type*>[])
+Evaluated: SymbolLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:698:13 -> SymbolConstant(#onTimeout)
+Evaluated: SymbolLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:602:13 -> SymbolConstant(#then)
+Evaluated: SymbolLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:602:13 -> SymbolConstant(#onError)
+Evaluated: SymbolLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:684:13 -> SymbolConstant(#asStream)
+Evaluated: ListLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:684:13 -> ListConstant(const <Type*>[])
+Evaluated: ListLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:684:13 -> ListConstant(const <dynamic>[])
+Evaluated: MapLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:684:13 -> InstanceConstant(const _ImmutableMap<Symbol*, dynamic>{_ImmutableMap._kvPairs: const <dynamic>[]})
Extra constant evaluation: evaluated: 61, effectively constant: 15
diff --git a/pkg/front_end/testcases/nnbd/super_bounded_hint.dart b/pkg/front_end/testcases/nnbd/super_bounded_hint.dart
new file mode 100644
index 0000000..1fe49cb
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/super_bounded_hint.dart
@@ -0,0 +1,80 @@
+// Copyright (c) 2021, 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.
+
+// -------------------------------- Gives hint. -------------------------------
+
+class A<X> {}
+
+typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+
+foo1a(F<A<dynamic>, A<Never>> x) {}
+foo1b(F x) {}
+
+foo2a<X extends F<A<dynamic>, A<Never>>>() {}
+foo2b<X extends F>() {}
+
+class Foo3a<X extends F<A<dynamic>, A<Never>>> {}
+
+class Foo3b<X extends F> {}
+
+F<A<dynamic>, A<Never>> foo4a() => throw 42;
+F foo4b() => throw 42;
+
+foo5a({required F<A<dynamic>, A<Never>> x}) {}
+foo5b({required F x}) {}
+
+foo6a() {
+ F<A<dynamic>, A<Never>> x;
+ for (F<A<dynamic>, A<Never>> x in []) {}
+ fooFoo1(F<A<dynamic>, A<Never>> x) {}
+ fooFoo2<X extends F<A<dynamic>, A<Never>>>() {}
+ F<A<dynamic>, A<Never>> fooFoo4() => throw 42;
+ fooFoo5({required F<A<dynamic>, A<Never>> x}) {}
+ fooFoo7([F<A<dynamic>, A<Never>>? x]) {}
+}
+
+foo6b() {
+ F x;
+ for (F x in []) {}
+ fooFoo1(F x) {}
+ fooFoo2<X extends F>() {}
+ F fooFoo4() => throw 42;
+ fooFoo5({required F x}) {}
+ fooFoo7([F? x]) {}
+}
+
+foo7a([F<A<dynamic>, A<Never>>? x]) {}
+foo7b([F? x]) {}
+
+// ---------------------------- Doesn't give hint. ----------------------------
+
+class B<X extends int> {}
+
+bar1(B<num> x) {}
+
+bar2<X extends B<num>>() {}
+
+class Bar3<X extends B<num>> {}
+
+B<num> bar4() => throw 42;
+
+bar5({required B<num> x}) {}
+
+bar6() {
+ B<num> x;
+ for (B<num> x in []) {}
+ barBar1(B<num> x) {}
+ barBar2<X extends B<num>>() {}
+ B<num> barBar4() => throw 42;
+ barBar5({required B<num> x}) {}
+ barBar7([B<num>? x]) {}
+ new B<dynamic>();
+ new A<B<dynamic>>();
+}
+
+bar7([B<num>? x]) {}
+
+class Bar8 extends B<dynamic> {}
+
+main() {}
diff --git a/pkg/front_end/testcases/nnbd/super_bounded_hint.dart.outline.expect b/pkg/front_end/testcases/nnbd/super_bounded_hint.dart.outline.expect
new file mode 100644
index 0000000..1d220b5
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/super_bounded_hint.dart.outline.expect
@@ -0,0 +1,296 @@
+library /*isNonNullableByDefault*/;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:15:7: Error: Generic type 'F' can't be used without type arguments in a type variable bound.
+// Try providing type arguments to 'F' here.
+// foo2b<X extends F>() {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: Bound of this variable references variable 'X' from the same declaration.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:19:13: Error: Generic type 'F' can't be used without type arguments in a type variable bound.
+// Try providing type arguments to 'F' here.
+// class Foo3b<X extends F> {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: Bound of this variable references variable 'X' from the same declaration.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:11:31: Error: Type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F'.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try changing type arguments so that they conform to the bounds.
+// foo1a(F<A<dynamic>, A<Never>> x) {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:11:31: Context: If you want 'F<A<dynamic>, A<Never>>' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// foo1a(F<A<dynamic>, A<Never>> x) {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:12:9: Error: Inferred type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F'.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try specifying type arguments explicitly so that they conform to the bounds.
+// foo1b(F x) {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:12:9: Context: If you want 'F<A<dynamic>, A<Never>>' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// foo1b(F x) {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:14:7: Error: Type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F'.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try changing type arguments so that they conform to the bounds.
+// foo2a<X extends F<A<dynamic>, A<Never>>>() {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:14:7: Context: If you want 'F<A<dynamic>, A<Never>>' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// foo2a<X extends F<A<dynamic>, A<Never>>>() {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:15:7: Error: Inferred type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F'.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try specifying type arguments explicitly so that they conform to the bounds.
+// foo2b<X extends F>() {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:15:7: Context: If you want 'F<A<dynamic>, A<Never>>' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// foo2b<X extends F>() {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:17:13: Error: Type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F'.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try changing type arguments so that they conform to the bounds.
+// class Foo3a<X extends F<A<dynamic>, A<Never>>> {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:17:13: Context: If you want 'F<A<dynamic>, A<Never>>' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// class Foo3a<X extends F<A<dynamic>, A<Never>>> {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:21:30: Error: Type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F' in the return type.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try changing type arguments so that they conform to the bounds.
+// F<A<dynamic>, A<Never>> foo4a() => throw 42;
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:21:30: Context: If you want 'F<A<dynamic>, A<Never>>' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// F<A<dynamic>, A<Never>> foo4a() => throw 42;
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:22:8: Error: Type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F' in the return type.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try changing type arguments so that they conform to the bounds.
+// F foo4b() => throw 42;
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:22:8: Context: If you want 'F<A<dynamic>, A<Never>>' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// F foo4b() => throw 42;
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:24:41: Error: Type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F'.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try changing type arguments so that they conform to the bounds.
+// foo5a({required F<A<dynamic>, A<Never>> x}) {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:24:41: Context: If you want 'F<A<dynamic>, A<Never>>' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// foo5a({required F<A<dynamic>, A<Never>> x}) {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:25:19: Error: Inferred type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F'.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try specifying type arguments explicitly so that they conform to the bounds.
+// foo5b({required F x}) {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:25:19: Context: If you want 'F<A<dynamic>, A<Never>>' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// foo5b({required F x}) {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:47:33: Error: Type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F'.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try changing type arguments so that they conform to the bounds.
+// foo7a([F<A<dynamic>, A<Never>>? x]) {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:47:33: Context: If you want 'F<A<dynamic>, A<Never>>?' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>?' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// foo7a([F<A<dynamic>, A<Never>>? x]) {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:48:11: Error: Inferred type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F'.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try specifying type arguments explicitly so that they conform to the bounds.
+// foo7b([F? x]) {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:48:11: Context: If you want 'F<A<dynamic>, A<Never>>?' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>?' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// foo7b([F? x]) {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:54:13: Error: Type argument 'num' doesn't conform to the bound 'int' of the type variable 'X' on 'B'.
+// Try changing type arguments so that they conform to the bounds.
+// bar1(B<num> x) {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:52:9: Context: This is the type variable whose bound isn't conformed to.
+// class B<X extends int> {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:56:6: Error: Type argument 'num' doesn't conform to the bound 'int' of the type variable 'X' on 'B'.
+// Try changing type arguments so that they conform to the bounds.
+// bar2<X extends B<num>>() {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:52:9: Context: This is the type variable whose bound isn't conformed to.
+// class B<X extends int> {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:58:12: Error: Type argument 'num' doesn't conform to the bound 'int' of the type variable 'X' on 'B'.
+// Try changing type arguments so that they conform to the bounds.
+// class Bar3<X extends B<num>> {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:52:9: Context: This is the type variable whose bound isn't conformed to.
+// class B<X extends int> {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:60:12: Error: Type argument 'num' doesn't conform to the bound 'int' of the type variable 'X' on 'B' in the return type.
+// Try changing type arguments so that they conform to the bounds.
+// B<num> bar4() => throw 42;
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:52:9: Context: This is the type variable whose bound isn't conformed to.
+// class B<X extends int> {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:62:23: Error: Type argument 'num' doesn't conform to the bound 'int' of the type variable 'X' on 'B'.
+// Try changing type arguments so that they conform to the bounds.
+// bar5({required B<num> x}) {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:52:9: Context: This is the type variable whose bound isn't conformed to.
+// class B<X extends int> {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:76:15: Error: Type argument 'num' doesn't conform to the bound 'int' of the type variable 'X' on 'B'.
+// Try changing type arguments so that they conform to the bounds.
+// bar7([B<num>? x]) {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:52:9: Context: This is the type variable whose bound isn't conformed to.
+// class B<X extends int> {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:78:7: Error: Type argument 'dynamic' doesn't conform to the bound 'int' of the type variable 'X' on 'B' in the supertype 'B' of class 'Bar8'.
+// Try changing type arguments so that they conform to the bounds.
+// class Bar8 extends B<dynamic> {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:52:9: Context: This is the type variable whose bound isn't conformed to.
+// class B<X extends int> {}
+// ^
+//
+import self as self;
+import "dart:core" as core;
+
+typedef F<X extends self::A<X> = self::A<dynamic>, contravariant Y extends self::A<Y> = self::A<Never>> = (Y) → X;
+class A<X extends core::Object? = dynamic> extends core::Object {
+ synthetic constructor •() → self::A<self::A::X%>
+ ;
+}
+class Foo3a<X extends (self::A<Never>) → self::A<dynamic> = (self::A<Never>) → self::A<dynamic>> extends core::Object {
+ synthetic constructor •() → self::Foo3a<self::Foo3a::X>
+ ;
+}
+class Foo3b<X extends (self::A<Never>) → self::A<dynamic> = dynamic> extends core::Object {
+ synthetic constructor •() → self::Foo3b<self::Foo3b::X>
+ ;
+}
+class B<X extends core::int = core::int> extends core::Object {
+ synthetic constructor •() → self::B<self::B::X>
+ ;
+}
+class Bar3<X extends self::B<core::num> = self::B<core::num>> extends core::Object {
+ synthetic constructor •() → self::Bar3<self::Bar3::X>
+ ;
+}
+class Bar8 extends self::B<dynamic> {
+ synthetic constructor •() → self::Bar8
+ ;
+}
+static method foo1a((self::A<Never>) → self::A<dynamic> x) → dynamic
+ ;
+static method foo1b((self::A<Never>) → self::A<dynamic> x) → dynamic
+ ;
+static method foo2a<X extends (self::A<Never>) → self::A<dynamic> = (self::A<Never>) → self::A<dynamic>>() → dynamic
+ ;
+static method foo2b<X extends (self::A<Never>) → self::A<dynamic> = dynamic>() → dynamic
+ ;
+static method foo4a() → (self::A<Never>) → self::A<dynamic>
+ ;
+static method foo4b() → (self::A<Never>) → self::A<dynamic>
+ ;
+static method foo5a({required (self::A<Never>) → self::A<dynamic> x}) → dynamic
+ ;
+static method foo5b({required (self::A<Never>) → self::A<dynamic> x}) → dynamic
+ ;
+static method foo6a() → dynamic
+ ;
+static method foo6b() → dynamic
+ ;
+static method foo7a([(self::A<Never>) →? self::A<dynamic> x]) → dynamic
+ ;
+static method foo7b([(self::A<Never>) →? self::A<dynamic> x]) → dynamic
+ ;
+static method bar1(self::B<core::num> x) → dynamic
+ ;
+static method bar2<X extends self::B<core::num> = self::B<core::num>>() → dynamic
+ ;
+static method bar4() → self::B<core::num>
+ ;
+static method bar5({required self::B<core::num> x}) → dynamic
+ ;
+static method bar6() → dynamic
+ ;
+static method bar7([self::B<core::num>? x]) → dynamic
+ ;
+static method main() → dynamic
+ ;
diff --git a/pkg/front_end/testcases/nnbd/super_bounded_hint.dart.strong.expect b/pkg/front_end/testcases/nnbd/super_bounded_hint.dart.strong.expect
new file mode 100644
index 0000000..98725b8
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/super_bounded_hint.dart.strong.expect
@@ -0,0 +1,618 @@
+library /*isNonNullableByDefault*/;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:15:7: Error: Generic type 'F' can't be used without type arguments in a type variable bound.
+// Try providing type arguments to 'F' here.
+// foo2b<X extends F>() {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: Bound of this variable references variable 'X' from the same declaration.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:19:13: Error: Generic type 'F' can't be used without type arguments in a type variable bound.
+// Try providing type arguments to 'F' here.
+// class Foo3b<X extends F> {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: Bound of this variable references variable 'X' from the same declaration.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:11:31: Error: Type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F'.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try changing type arguments so that they conform to the bounds.
+// foo1a(F<A<dynamic>, A<Never>> x) {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:11:31: Context: If you want 'F<A<dynamic>, A<Never>>' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// foo1a(F<A<dynamic>, A<Never>> x) {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:12:9: Error: Inferred type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F'.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try specifying type arguments explicitly so that they conform to the bounds.
+// foo1b(F x) {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:12:9: Context: If you want 'F<A<dynamic>, A<Never>>' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// foo1b(F x) {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:14:7: Error: Type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F'.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try changing type arguments so that they conform to the bounds.
+// foo2a<X extends F<A<dynamic>, A<Never>>>() {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:14:7: Context: If you want 'F<A<dynamic>, A<Never>>' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// foo2a<X extends F<A<dynamic>, A<Never>>>() {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:15:7: Error: Inferred type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F'.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try specifying type arguments explicitly so that they conform to the bounds.
+// foo2b<X extends F>() {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:15:7: Context: If you want 'F<A<dynamic>, A<Never>>' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// foo2b<X extends F>() {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:17:13: Error: Type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F'.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try changing type arguments so that they conform to the bounds.
+// class Foo3a<X extends F<A<dynamic>, A<Never>>> {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:17:13: Context: If you want 'F<A<dynamic>, A<Never>>' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// class Foo3a<X extends F<A<dynamic>, A<Never>>> {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:21:30: Error: Type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F' in the return type.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try changing type arguments so that they conform to the bounds.
+// F<A<dynamic>, A<Never>> foo4a() => throw 42;
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:21:30: Context: If you want 'F<A<dynamic>, A<Never>>' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// F<A<dynamic>, A<Never>> foo4a() => throw 42;
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:22:8: Error: Type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F' in the return type.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try changing type arguments so that they conform to the bounds.
+// F foo4b() => throw 42;
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:22:8: Context: If you want 'F<A<dynamic>, A<Never>>' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// F foo4b() => throw 42;
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:24:41: Error: Type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F'.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try changing type arguments so that they conform to the bounds.
+// foo5a({required F<A<dynamic>, A<Never>> x}) {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:24:41: Context: If you want 'F<A<dynamic>, A<Never>>' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// foo5a({required F<A<dynamic>, A<Never>> x}) {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:25:19: Error: Inferred type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F'.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try specifying type arguments explicitly so that they conform to the bounds.
+// foo5b({required F x}) {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:25:19: Context: If you want 'F<A<dynamic>, A<Never>>' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// foo5b({required F x}) {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:47:33: Error: Type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F'.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try changing type arguments so that they conform to the bounds.
+// foo7a([F<A<dynamic>, A<Never>>? x]) {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:47:33: Context: If you want 'F<A<dynamic>, A<Never>>?' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>?' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// foo7a([F<A<dynamic>, A<Never>>? x]) {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:48:11: Error: Inferred type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F'.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try specifying type arguments explicitly so that they conform to the bounds.
+// foo7b([F? x]) {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:48:11: Context: If you want 'F<A<dynamic>, A<Never>>?' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>?' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// foo7b([F? x]) {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:54:13: Error: Type argument 'num' doesn't conform to the bound 'int' of the type variable 'X' on 'B'.
+// Try changing type arguments so that they conform to the bounds.
+// bar1(B<num> x) {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:52:9: Context: This is the type variable whose bound isn't conformed to.
+// class B<X extends int> {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:56:6: Error: Type argument 'num' doesn't conform to the bound 'int' of the type variable 'X' on 'B'.
+// Try changing type arguments so that they conform to the bounds.
+// bar2<X extends B<num>>() {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:52:9: Context: This is the type variable whose bound isn't conformed to.
+// class B<X extends int> {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:58:12: Error: Type argument 'num' doesn't conform to the bound 'int' of the type variable 'X' on 'B'.
+// Try changing type arguments so that they conform to the bounds.
+// class Bar3<X extends B<num>> {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:52:9: Context: This is the type variable whose bound isn't conformed to.
+// class B<X extends int> {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:60:12: Error: Type argument 'num' doesn't conform to the bound 'int' of the type variable 'X' on 'B' in the return type.
+// Try changing type arguments so that they conform to the bounds.
+// B<num> bar4() => throw 42;
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:52:9: Context: This is the type variable whose bound isn't conformed to.
+// class B<X extends int> {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:62:23: Error: Type argument 'num' doesn't conform to the bound 'int' of the type variable 'X' on 'B'.
+// Try changing type arguments so that they conform to the bounds.
+// bar5({required B<num> x}) {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:52:9: Context: This is the type variable whose bound isn't conformed to.
+// class B<X extends int> {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:76:15: Error: Type argument 'num' doesn't conform to the bound 'int' of the type variable 'X' on 'B'.
+// Try changing type arguments so that they conform to the bounds.
+// bar7([B<num>? x]) {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:52:9: Context: This is the type variable whose bound isn't conformed to.
+// class B<X extends int> {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:78:7: Error: Type argument 'dynamic' doesn't conform to the bound 'int' of the type variable 'X' on 'B' in the supertype 'B' of class 'Bar8'.
+// Try changing type arguments so that they conform to the bounds.
+// class Bar8 extends B<dynamic> {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:52:9: Context: This is the type variable whose bound isn't conformed to.
+// class B<X extends int> {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:28:27: Error: Type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F'.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try changing type arguments so that they conform to the bounds.
+// F<A<dynamic>, A<Never>> x;
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:28:27: Context: If you want 'F<A<dynamic>, A<Never>>' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// F<A<dynamic>, A<Never>> x;
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:29:32: Error: Type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F'.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try changing type arguments so that they conform to the bounds.
+// for (F<A<dynamic>, A<Never>> x in []) {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:29:32: Context: If you want 'F<A<dynamic>, A<Never>>' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// for (F<A<dynamic>, A<Never>> x in []) {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:29:37: Error: Inferred type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F'.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try specifying type arguments explicitly so that they conform to the bounds.
+// for (F<A<dynamic>, A<Never>> x in []) {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:29:37: Context: If you want 'F<A<dynamic>, A<Never>>' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// for (F<A<dynamic>, A<Never>> x in []) {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:30:35: Error: Type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F'.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try changing type arguments so that they conform to the bounds.
+// fooFoo1(F<A<dynamic>, A<Never>> x) {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:30:35: Context: If you want 'F<A<dynamic>, A<Never>>' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// fooFoo1(F<A<dynamic>, A<Never>> x) {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:31:11: Error: Type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F'.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try changing type arguments so that they conform to the bounds.
+// fooFoo2<X extends F<A<dynamic>, A<Never>>>() {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:31:11: Context: If you want 'F<A<dynamic>, A<Never>>' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// fooFoo2<X extends F<A<dynamic>, A<Never>>>() {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:32:34: Error: Type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F' in the return type.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try changing type arguments so that they conform to the bounds.
+// F<A<dynamic>, A<Never>> fooFoo4() => throw 42;
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:32:34: Context: If you want 'F<A<dynamic>, A<Never>>' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// F<A<dynamic>, A<Never>> fooFoo4() => throw 42;
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:33:45: Error: Type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F'.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try changing type arguments so that they conform to the bounds.
+// fooFoo5({required F<A<dynamic>, A<Never>> x}) {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:33:45: Context: If you want 'F<A<dynamic>, A<Never>>' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// fooFoo5({required F<A<dynamic>, A<Never>> x}) {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:34:37: Error: Type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F'.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try changing type arguments so that they conform to the bounds.
+// fooFoo7([F<A<dynamic>, A<Never>>? x]) {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:34:37: Context: If you want 'F<A<dynamic>, A<Never>>?' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>?' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// fooFoo7([F<A<dynamic>, A<Never>>? x]) {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:38:5: Error: Type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F'.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try changing type arguments so that they conform to the bounds.
+// F x;
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:38:5: Context: If you want 'F<A<dynamic>, A<Never>>' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// F x;
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:39:10: Error: Type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F'.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try changing type arguments so that they conform to the bounds.
+// for (F x in []) {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:39:10: Context: If you want 'F<A<dynamic>, A<Never>>' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// for (F x in []) {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:39:15: Error: Inferred type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F'.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try specifying type arguments explicitly so that they conform to the bounds.
+// for (F x in []) {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:39:15: Context: If you want 'F<A<dynamic>, A<Never>>' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// for (F x in []) {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:40:13: Error: Inferred type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F'.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try specifying type arguments explicitly so that they conform to the bounds.
+// fooFoo1(F x) {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:40:13: Context: If you want 'F<A<dynamic>, A<Never>>' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// fooFoo1(F x) {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:41:11: Error: Inferred type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F'.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try specifying type arguments explicitly so that they conform to the bounds.
+// fooFoo2<X extends F>() {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:41:11: Context: If you want 'F<A<dynamic>, A<Never>>' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// fooFoo2<X extends F>() {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:42:12: Error: Type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F' in the return type.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try changing type arguments so that they conform to the bounds.
+// F fooFoo4() => throw 42;
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:42:12: Context: If you want 'F<A<dynamic>, A<Never>>' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// F fooFoo4() => throw 42;
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:43:23: Error: Inferred type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F'.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try specifying type arguments explicitly so that they conform to the bounds.
+// fooFoo5({required F x}) {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:43:23: Context: If you want 'F<A<dynamic>, A<Never>>' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// fooFoo5({required F x}) {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:44:15: Error: Inferred type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F'.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try specifying type arguments explicitly so that they conform to the bounds.
+// fooFoo7([F? x]) {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:44:15: Context: If you want 'F<A<dynamic>, A<Never>>?' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>?' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// fooFoo7([F? x]) {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:65:10: Error: Type argument 'num' doesn't conform to the bound 'int' of the type variable 'X' on 'B'.
+// Try changing type arguments so that they conform to the bounds.
+// B<num> x;
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:52:9: Context: This is the type variable whose bound isn't conformed to.
+// class B<X extends int> {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:66:15: Error: Type argument 'num' doesn't conform to the bound 'int' of the type variable 'X' on 'B'.
+// Try changing type arguments so that they conform to the bounds.
+// for (B<num> x in []) {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:52:9: Context: This is the type variable whose bound isn't conformed to.
+// class B<X extends int> {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:72:7: Error: Type argument 'dynamic' doesn't conform to the bound 'int' of the type variable 'X' on 'B'.
+// Try changing type arguments so that they conform to the bounds.
+// new B<dynamic>();
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:52:9: Context: This is the type variable whose bound isn't conformed to.
+// class B<X extends int> {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:66:20: Error: Inferred type argument 'num' doesn't conform to the bound 'int' of the type variable 'X' on 'B'.
+// Try specifying type arguments explicitly so that they conform to the bounds.
+// for (B<num> x in []) {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:52:9: Context: This is the type variable whose bound isn't conformed to.
+// class B<X extends int> {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:67:18: Error: Type argument 'num' doesn't conform to the bound 'int' of the type variable 'X' on 'B'.
+// Try changing type arguments so that they conform to the bounds.
+// barBar1(B<num> x) {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:52:9: Context: This is the type variable whose bound isn't conformed to.
+// class B<X extends int> {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:68:11: Error: Type argument 'num' doesn't conform to the bound 'int' of the type variable 'X' on 'B'.
+// Try changing type arguments so that they conform to the bounds.
+// barBar2<X extends B<num>>() {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:52:9: Context: This is the type variable whose bound isn't conformed to.
+// class B<X extends int> {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:69:17: Error: Type argument 'num' doesn't conform to the bound 'int' of the type variable 'X' on 'B' in the return type.
+// Try changing type arguments so that they conform to the bounds.
+// B<num> barBar4() => throw 42;
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:52:9: Context: This is the type variable whose bound isn't conformed to.
+// class B<X extends int> {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:70:28: Error: Type argument 'num' doesn't conform to the bound 'int' of the type variable 'X' on 'B'.
+// Try changing type arguments so that they conform to the bounds.
+// barBar5({required B<num> x}) {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:52:9: Context: This is the type variable whose bound isn't conformed to.
+// class B<X extends int> {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:71:20: Error: Type argument 'num' doesn't conform to the bound 'int' of the type variable 'X' on 'B'.
+// Try changing type arguments so that they conform to the bounds.
+// barBar7([B<num>? x]) {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:52:9: Context: This is the type variable whose bound isn't conformed to.
+// class B<X extends int> {}
+// ^
+//
+import self as self;
+import "dart:core" as core;
+
+typedef F<X extends self::A<X> = self::A<dynamic>, contravariant Y extends self::A<Y> = self::A<Never>> = (Y) → X;
+class A<X extends core::Object? = dynamic> extends core::Object {
+ synthetic constructor •() → self::A<self::A::X%>
+ : super core::Object::•()
+ ;
+}
+class Foo3a<X extends (self::A<Never>) → self::A<dynamic> = (self::A<Never>) → self::A<dynamic>> extends core::Object {
+ synthetic constructor •() → self::Foo3a<self::Foo3a::X>
+ : super core::Object::•()
+ ;
+}
+class Foo3b<X extends (self::A<Never>) → self::A<dynamic> = dynamic> extends core::Object {
+ synthetic constructor •() → self::Foo3b<self::Foo3b::X>
+ : super core::Object::•()
+ ;
+}
+class B<X extends core::int = core::int> extends core::Object {
+ synthetic constructor •() → self::B<self::B::X>
+ : super core::Object::•()
+ ;
+}
+class Bar3<X extends self::B<core::num> = self::B<core::num>> extends core::Object {
+ synthetic constructor •() → self::Bar3<self::Bar3::X>
+ : super core::Object::•()
+ ;
+}
+class Bar8 extends self::B<dynamic> {
+ synthetic constructor •() → self::Bar8
+ : super self::B::•()
+ ;
+}
+static method foo1a((self::A<Never>) → self::A<dynamic> x) → dynamic {}
+static method foo1b((self::A<Never>) → self::A<dynamic> x) → dynamic {}
+static method foo2a<X extends (self::A<Never>) → self::A<dynamic> = (self::A<Never>) → self::A<dynamic>>() → dynamic {}
+static method foo2b<X extends (self::A<Never>) → self::A<dynamic> = dynamic>() → dynamic {}
+static method foo4a() → (self::A<Never>) → self::A<dynamic>
+ return throw 42;
+static method foo4b() → (self::A<Never>) → self::A<dynamic>
+ return throw 42;
+static method foo5a({required (self::A<Never>) → self::A<dynamic> x = #C1}) → dynamic {}
+static method foo5b({required (self::A<Never>) → self::A<dynamic> x = #C1}) → dynamic {}
+static method foo6a() → dynamic {
+ (self::A<Never>) → self::A<dynamic> x;
+ for ((self::A<Never>) → self::A<dynamic> x in <(self::A<Never>) → self::A<dynamic>>[]) {
+ }
+ function fooFoo1((self::A<Never>) → self::A<dynamic> x) → Null {}
+ function fooFoo2<X extends (self::A<Never>) → self::A<dynamic> = (self::A<Never>) → self::A<dynamic>>() → Null {}
+ function fooFoo4() → (self::A<Never>) → self::A<dynamic>
+ return throw 42;
+ function fooFoo5({required (self::A<Never>) → self::A<dynamic> x = #C1}) → Null {}
+ function fooFoo7([(self::A<Never>) →? self::A<dynamic> x = #C1]) → Null {}
+}
+static method foo6b() → dynamic {
+ (self::A<Never>) → self::A<dynamic> x;
+ for ((self::A<Never>) → self::A<dynamic> x in <(self::A<Never>) → self::A<dynamic>>[]) {
+ }
+ function fooFoo1((self::A<Never>) → self::A<dynamic> x) → Null {}
+ function fooFoo2<X extends (self::A<Never>) → self::A<dynamic> = (self::A<Never>) → self::A<dynamic>>() → Null {}
+ function fooFoo4() → (self::A<Never>) → self::A<dynamic>
+ return throw 42;
+ function fooFoo5({required (self::A<Never>) → self::A<dynamic> x = #C1}) → Null {}
+ function fooFoo7([(self::A<Never>) →? self::A<dynamic> x = #C1]) → Null {}
+}
+static method foo7a([(self::A<Never>) →? self::A<dynamic> x = #C1]) → dynamic {}
+static method foo7b([(self::A<Never>) →? self::A<dynamic> x = #C1]) → dynamic {}
+static method bar1(self::B<core::num> x) → dynamic {}
+static method bar2<X extends self::B<core::num> = self::B<core::num>>() → dynamic {}
+static method bar4() → self::B<core::num>
+ return throw 42;
+static method bar5({required self::B<core::num> x = #C1}) → dynamic {}
+static method bar6() → dynamic {
+ self::B<core::num> x;
+ for (self::B<core::num> x in <self::B<core::num>>[]) {
+ }
+ function barBar1(self::B<core::num> x) → Null {}
+ function barBar2<X extends self::B<core::num> = self::B<core::num>>() → Null {}
+ function barBar4() → self::B<core::num>
+ return throw 42;
+ function barBar5({required self::B<core::num> x = #C1}) → Null {}
+ function barBar7([self::B<core::num>? x = #C1]) → Null {}
+ new self::B::•<dynamic>();
+ new self::A::•<self::B<dynamic>>();
+}
+static method bar7([self::B<core::num>? x = #C1]) → dynamic {}
+static method main() → dynamic {}
+
+constants {
+ #C1 = null
+}
diff --git a/pkg/front_end/testcases/nnbd/super_bounded_hint.dart.strong.transformed.expect b/pkg/front_end/testcases/nnbd/super_bounded_hint.dart.strong.transformed.expect
new file mode 100644
index 0000000..edb2e83
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/super_bounded_hint.dart.strong.transformed.expect
@@ -0,0 +1,633 @@
+library /*isNonNullableByDefault*/;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:15:7: Error: Generic type 'F' can't be used without type arguments in a type variable bound.
+// Try providing type arguments to 'F' here.
+// foo2b<X extends F>() {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: Bound of this variable references variable 'X' from the same declaration.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:19:13: Error: Generic type 'F' can't be used without type arguments in a type variable bound.
+// Try providing type arguments to 'F' here.
+// class Foo3b<X extends F> {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: Bound of this variable references variable 'X' from the same declaration.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:11:31: Error: Type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F'.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try changing type arguments so that they conform to the bounds.
+// foo1a(F<A<dynamic>, A<Never>> x) {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:11:31: Context: If you want 'F<A<dynamic>, A<Never>>' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// foo1a(F<A<dynamic>, A<Never>> x) {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:12:9: Error: Inferred type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F'.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try specifying type arguments explicitly so that they conform to the bounds.
+// foo1b(F x) {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:12:9: Context: If you want 'F<A<dynamic>, A<Never>>' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// foo1b(F x) {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:14:7: Error: Type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F'.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try changing type arguments so that they conform to the bounds.
+// foo2a<X extends F<A<dynamic>, A<Never>>>() {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:14:7: Context: If you want 'F<A<dynamic>, A<Never>>' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// foo2a<X extends F<A<dynamic>, A<Never>>>() {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:15:7: Error: Inferred type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F'.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try specifying type arguments explicitly so that they conform to the bounds.
+// foo2b<X extends F>() {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:15:7: Context: If you want 'F<A<dynamic>, A<Never>>' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// foo2b<X extends F>() {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:17:13: Error: Type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F'.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try changing type arguments so that they conform to the bounds.
+// class Foo3a<X extends F<A<dynamic>, A<Never>>> {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:17:13: Context: If you want 'F<A<dynamic>, A<Never>>' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// class Foo3a<X extends F<A<dynamic>, A<Never>>> {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:21:30: Error: Type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F' in the return type.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try changing type arguments so that they conform to the bounds.
+// F<A<dynamic>, A<Never>> foo4a() => throw 42;
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:21:30: Context: If you want 'F<A<dynamic>, A<Never>>' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// F<A<dynamic>, A<Never>> foo4a() => throw 42;
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:22:8: Error: Type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F' in the return type.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try changing type arguments so that they conform to the bounds.
+// F foo4b() => throw 42;
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:22:8: Context: If you want 'F<A<dynamic>, A<Never>>' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// F foo4b() => throw 42;
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:24:41: Error: Type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F'.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try changing type arguments so that they conform to the bounds.
+// foo5a({required F<A<dynamic>, A<Never>> x}) {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:24:41: Context: If you want 'F<A<dynamic>, A<Never>>' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// foo5a({required F<A<dynamic>, A<Never>> x}) {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:25:19: Error: Inferred type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F'.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try specifying type arguments explicitly so that they conform to the bounds.
+// foo5b({required F x}) {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:25:19: Context: If you want 'F<A<dynamic>, A<Never>>' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// foo5b({required F x}) {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:47:33: Error: Type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F'.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try changing type arguments so that they conform to the bounds.
+// foo7a([F<A<dynamic>, A<Never>>? x]) {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:47:33: Context: If you want 'F<A<dynamic>, A<Never>>?' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>?' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// foo7a([F<A<dynamic>, A<Never>>? x]) {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:48:11: Error: Inferred type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F'.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try specifying type arguments explicitly so that they conform to the bounds.
+// foo7b([F? x]) {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:48:11: Context: If you want 'F<A<dynamic>, A<Never>>?' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>?' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// foo7b([F? x]) {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:54:13: Error: Type argument 'num' doesn't conform to the bound 'int' of the type variable 'X' on 'B'.
+// Try changing type arguments so that they conform to the bounds.
+// bar1(B<num> x) {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:52:9: Context: This is the type variable whose bound isn't conformed to.
+// class B<X extends int> {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:56:6: Error: Type argument 'num' doesn't conform to the bound 'int' of the type variable 'X' on 'B'.
+// Try changing type arguments so that they conform to the bounds.
+// bar2<X extends B<num>>() {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:52:9: Context: This is the type variable whose bound isn't conformed to.
+// class B<X extends int> {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:58:12: Error: Type argument 'num' doesn't conform to the bound 'int' of the type variable 'X' on 'B'.
+// Try changing type arguments so that they conform to the bounds.
+// class Bar3<X extends B<num>> {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:52:9: Context: This is the type variable whose bound isn't conformed to.
+// class B<X extends int> {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:60:12: Error: Type argument 'num' doesn't conform to the bound 'int' of the type variable 'X' on 'B' in the return type.
+// Try changing type arguments so that they conform to the bounds.
+// B<num> bar4() => throw 42;
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:52:9: Context: This is the type variable whose bound isn't conformed to.
+// class B<X extends int> {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:62:23: Error: Type argument 'num' doesn't conform to the bound 'int' of the type variable 'X' on 'B'.
+// Try changing type arguments so that they conform to the bounds.
+// bar5({required B<num> x}) {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:52:9: Context: This is the type variable whose bound isn't conformed to.
+// class B<X extends int> {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:76:15: Error: Type argument 'num' doesn't conform to the bound 'int' of the type variable 'X' on 'B'.
+// Try changing type arguments so that they conform to the bounds.
+// bar7([B<num>? x]) {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:52:9: Context: This is the type variable whose bound isn't conformed to.
+// class B<X extends int> {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:78:7: Error: Type argument 'dynamic' doesn't conform to the bound 'int' of the type variable 'X' on 'B' in the supertype 'B' of class 'Bar8'.
+// Try changing type arguments so that they conform to the bounds.
+// class Bar8 extends B<dynamic> {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:52:9: Context: This is the type variable whose bound isn't conformed to.
+// class B<X extends int> {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:28:27: Error: Type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F'.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try changing type arguments so that they conform to the bounds.
+// F<A<dynamic>, A<Never>> x;
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:28:27: Context: If you want 'F<A<dynamic>, A<Never>>' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// F<A<dynamic>, A<Never>> x;
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:29:32: Error: Type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F'.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try changing type arguments so that they conform to the bounds.
+// for (F<A<dynamic>, A<Never>> x in []) {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:29:32: Context: If you want 'F<A<dynamic>, A<Never>>' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// for (F<A<dynamic>, A<Never>> x in []) {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:29:37: Error: Inferred type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F'.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try specifying type arguments explicitly so that they conform to the bounds.
+// for (F<A<dynamic>, A<Never>> x in []) {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:29:37: Context: If you want 'F<A<dynamic>, A<Never>>' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// for (F<A<dynamic>, A<Never>> x in []) {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:30:35: Error: Type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F'.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try changing type arguments so that they conform to the bounds.
+// fooFoo1(F<A<dynamic>, A<Never>> x) {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:30:35: Context: If you want 'F<A<dynamic>, A<Never>>' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// fooFoo1(F<A<dynamic>, A<Never>> x) {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:31:11: Error: Type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F'.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try changing type arguments so that they conform to the bounds.
+// fooFoo2<X extends F<A<dynamic>, A<Never>>>() {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:31:11: Context: If you want 'F<A<dynamic>, A<Never>>' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// fooFoo2<X extends F<A<dynamic>, A<Never>>>() {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:32:34: Error: Type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F' in the return type.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try changing type arguments so that they conform to the bounds.
+// F<A<dynamic>, A<Never>> fooFoo4() => throw 42;
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:32:34: Context: If you want 'F<A<dynamic>, A<Never>>' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// F<A<dynamic>, A<Never>> fooFoo4() => throw 42;
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:33:45: Error: Type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F'.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try changing type arguments so that they conform to the bounds.
+// fooFoo5({required F<A<dynamic>, A<Never>> x}) {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:33:45: Context: If you want 'F<A<dynamic>, A<Never>>' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// fooFoo5({required F<A<dynamic>, A<Never>> x}) {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:34:37: Error: Type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F'.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try changing type arguments so that they conform to the bounds.
+// fooFoo7([F<A<dynamic>, A<Never>>? x]) {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:34:37: Context: If you want 'F<A<dynamic>, A<Never>>?' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>?' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// fooFoo7([F<A<dynamic>, A<Never>>? x]) {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:38:5: Error: Type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F'.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try changing type arguments so that they conform to the bounds.
+// F x;
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:38:5: Context: If you want 'F<A<dynamic>, A<Never>>' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// F x;
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:39:10: Error: Type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F'.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try changing type arguments so that they conform to the bounds.
+// for (F x in []) {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:39:10: Context: If you want 'F<A<dynamic>, A<Never>>' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// for (F x in []) {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:39:15: Error: Inferred type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F'.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try specifying type arguments explicitly so that they conform to the bounds.
+// for (F x in []) {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:39:15: Context: If you want 'F<A<dynamic>, A<Never>>' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// for (F x in []) {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:40:13: Error: Inferred type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F'.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try specifying type arguments explicitly so that they conform to the bounds.
+// fooFoo1(F x) {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:40:13: Context: If you want 'F<A<dynamic>, A<Never>>' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// fooFoo1(F x) {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:41:11: Error: Inferred type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F'.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try specifying type arguments explicitly so that they conform to the bounds.
+// fooFoo2<X extends F>() {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:41:11: Context: If you want 'F<A<dynamic>, A<Never>>' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// fooFoo2<X extends F>() {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:42:12: Error: Type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F' in the return type.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try changing type arguments so that they conform to the bounds.
+// F fooFoo4() => throw 42;
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:42:12: Context: If you want 'F<A<dynamic>, A<Never>>' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// F fooFoo4() => throw 42;
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:43:23: Error: Inferred type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F'.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try specifying type arguments explicitly so that they conform to the bounds.
+// fooFoo5({required F x}) {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:43:23: Context: If you want 'F<A<dynamic>, A<Never>>' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// fooFoo5({required F x}) {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:44:15: Error: Inferred type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F'.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try specifying type arguments explicitly so that they conform to the bounds.
+// fooFoo7([F? x]) {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:44:15: Context: If you want 'F<A<dynamic>, A<Never>>?' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>?' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// fooFoo7([F? x]) {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:65:10: Error: Type argument 'num' doesn't conform to the bound 'int' of the type variable 'X' on 'B'.
+// Try changing type arguments so that they conform to the bounds.
+// B<num> x;
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:52:9: Context: This is the type variable whose bound isn't conformed to.
+// class B<X extends int> {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:66:15: Error: Type argument 'num' doesn't conform to the bound 'int' of the type variable 'X' on 'B'.
+// Try changing type arguments so that they conform to the bounds.
+// for (B<num> x in []) {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:52:9: Context: This is the type variable whose bound isn't conformed to.
+// class B<X extends int> {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:72:7: Error: Type argument 'dynamic' doesn't conform to the bound 'int' of the type variable 'X' on 'B'.
+// Try changing type arguments so that they conform to the bounds.
+// new B<dynamic>();
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:52:9: Context: This is the type variable whose bound isn't conformed to.
+// class B<X extends int> {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:66:20: Error: Inferred type argument 'num' doesn't conform to the bound 'int' of the type variable 'X' on 'B'.
+// Try specifying type arguments explicitly so that they conform to the bounds.
+// for (B<num> x in []) {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:52:9: Context: This is the type variable whose bound isn't conformed to.
+// class B<X extends int> {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:67:18: Error: Type argument 'num' doesn't conform to the bound 'int' of the type variable 'X' on 'B'.
+// Try changing type arguments so that they conform to the bounds.
+// barBar1(B<num> x) {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:52:9: Context: This is the type variable whose bound isn't conformed to.
+// class B<X extends int> {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:68:11: Error: Type argument 'num' doesn't conform to the bound 'int' of the type variable 'X' on 'B'.
+// Try changing type arguments so that they conform to the bounds.
+// barBar2<X extends B<num>>() {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:52:9: Context: This is the type variable whose bound isn't conformed to.
+// class B<X extends int> {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:69:17: Error: Type argument 'num' doesn't conform to the bound 'int' of the type variable 'X' on 'B' in the return type.
+// Try changing type arguments so that they conform to the bounds.
+// B<num> barBar4() => throw 42;
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:52:9: Context: This is the type variable whose bound isn't conformed to.
+// class B<X extends int> {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:70:28: Error: Type argument 'num' doesn't conform to the bound 'int' of the type variable 'X' on 'B'.
+// Try changing type arguments so that they conform to the bounds.
+// barBar5({required B<num> x}) {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:52:9: Context: This is the type variable whose bound isn't conformed to.
+// class B<X extends int> {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:71:20: Error: Type argument 'num' doesn't conform to the bound 'int' of the type variable 'X' on 'B'.
+// Try changing type arguments so that they conform to the bounds.
+// barBar7([B<num>? x]) {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:52:9: Context: This is the type variable whose bound isn't conformed to.
+// class B<X extends int> {}
+// ^
+//
+import self as self;
+import "dart:core" as core;
+
+typedef F<X extends self::A<X> = self::A<dynamic>, contravariant Y extends self::A<Y> = self::A<Never>> = (Y) → X;
+class A<X extends core::Object? = dynamic> extends core::Object {
+ synthetic constructor •() → self::A<self::A::X%>
+ : super core::Object::•()
+ ;
+}
+class Foo3a<X extends (self::A<Never>) → self::A<dynamic> = (self::A<Never>) → self::A<dynamic>> extends core::Object {
+ synthetic constructor •() → self::Foo3a<self::Foo3a::X>
+ : super core::Object::•()
+ ;
+}
+class Foo3b<X extends (self::A<Never>) → self::A<dynamic> = dynamic> extends core::Object {
+ synthetic constructor •() → self::Foo3b<self::Foo3b::X>
+ : super core::Object::•()
+ ;
+}
+class B<X extends core::int = core::int> extends core::Object {
+ synthetic constructor •() → self::B<self::B::X>
+ : super core::Object::•()
+ ;
+}
+class Bar3<X extends self::B<core::num> = self::B<core::num>> extends core::Object {
+ synthetic constructor •() → self::Bar3<self::Bar3::X>
+ : super core::Object::•()
+ ;
+}
+class Bar8 extends self::B<dynamic> {
+ synthetic constructor •() → self::Bar8
+ : super self::B::•()
+ ;
+}
+static method foo1a((self::A<Never>) → self::A<dynamic> x) → dynamic {}
+static method foo1b((self::A<Never>) → self::A<dynamic> x) → dynamic {}
+static method foo2a<X extends (self::A<Never>) → self::A<dynamic> = (self::A<Never>) → self::A<dynamic>>() → dynamic {}
+static method foo2b<X extends (self::A<Never>) → self::A<dynamic> = dynamic>() → dynamic {}
+static method foo4a() → (self::A<Never>) → self::A<dynamic>
+ return throw 42;
+static method foo4b() → (self::A<Never>) → self::A<dynamic>
+ return throw 42;
+static method foo5a({required (self::A<Never>) → self::A<dynamic> x = #C1}) → dynamic {}
+static method foo5b({required (self::A<Never>) → self::A<dynamic> x = #C1}) → dynamic {}
+static method foo6a() → dynamic {
+ (self::A<Never>) → self::A<dynamic> x;
+ {
+ core::Iterator<(self::A<Never>) → self::A<dynamic>> :sync-for-iterator = core::_GrowableList::•<(self::A<Never>) → self::A<dynamic>>(0).{core::Iterable::iterator};
+ for (; :sync-for-iterator.{core::Iterator::moveNext}(); ) {
+ (self::A<Never>) → self::A<dynamic> x = :sync-for-iterator.{core::Iterator::current};
+ {}
+ }
+ }
+ function fooFoo1((self::A<Never>) → self::A<dynamic> x) → Null {}
+ function fooFoo2<X extends (self::A<Never>) → self::A<dynamic> = (self::A<Never>) → self::A<dynamic>>() → Null {}
+ function fooFoo4() → (self::A<Never>) → self::A<dynamic>
+ return throw 42;
+ function fooFoo5({required (self::A<Never>) → self::A<dynamic> x = #C1}) → Null {}
+ function fooFoo7([(self::A<Never>) →? self::A<dynamic> x = #C1]) → Null {}
+}
+static method foo6b() → dynamic {
+ (self::A<Never>) → self::A<dynamic> x;
+ {
+ core::Iterator<(self::A<Never>) → self::A<dynamic>> :sync-for-iterator = core::_GrowableList::•<(self::A<Never>) → self::A<dynamic>>(0).{core::Iterable::iterator};
+ for (; :sync-for-iterator.{core::Iterator::moveNext}(); ) {
+ (self::A<Never>) → self::A<dynamic> x = :sync-for-iterator.{core::Iterator::current};
+ {}
+ }
+ }
+ function fooFoo1((self::A<Never>) → self::A<dynamic> x) → Null {}
+ function fooFoo2<X extends (self::A<Never>) → self::A<dynamic> = (self::A<Never>) → self::A<dynamic>>() → Null {}
+ function fooFoo4() → (self::A<Never>) → self::A<dynamic>
+ return throw 42;
+ function fooFoo5({required (self::A<Never>) → self::A<dynamic> x = #C1}) → Null {}
+ function fooFoo7([(self::A<Never>) →? self::A<dynamic> x = #C1]) → Null {}
+}
+static method foo7a([(self::A<Never>) →? self::A<dynamic> x = #C1]) → dynamic {}
+static method foo7b([(self::A<Never>) →? self::A<dynamic> x = #C1]) → dynamic {}
+static method bar1(self::B<core::num> x) → dynamic {}
+static method bar2<X extends self::B<core::num> = self::B<core::num>>() → dynamic {}
+static method bar4() → self::B<core::num>
+ return throw 42;
+static method bar5({required self::B<core::num> x = #C1}) → dynamic {}
+static method bar6() → dynamic {
+ self::B<core::num> x;
+ {
+ core::Iterator<self::B<core::num>> :sync-for-iterator = core::_GrowableList::•<self::B<core::num>>(0).{core::Iterable::iterator};
+ for (; :sync-for-iterator.{core::Iterator::moveNext}(); ) {
+ self::B<core::num> x = :sync-for-iterator.{core::Iterator::current};
+ {}
+ }
+ }
+ function barBar1(self::B<core::num> x) → Null {}
+ function barBar2<X extends self::B<core::num> = self::B<core::num>>() → Null {}
+ function barBar4() → self::B<core::num>
+ return throw 42;
+ function barBar5({required self::B<core::num> x = #C1}) → Null {}
+ function barBar7([self::B<core::num>? x = #C1]) → Null {}
+ new self::B::•<dynamic>();
+ new self::A::•<self::B<dynamic>>();
+}
+static method bar7([self::B<core::num>? x = #C1]) → dynamic {}
+static method main() → dynamic {}
+
+constants {
+ #C1 = null
+}
diff --git a/pkg/front_end/testcases/nnbd/super_bounded_hint.dart.textual_outline.expect b/pkg/front_end/testcases/nnbd/super_bounded_hint.dart.textual_outline.expect
new file mode 100644
index 0000000..7574af7
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/super_bounded_hint.dart.textual_outline.expect
@@ -0,0 +1,36 @@
+class A<X> {}
+
+typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+foo1a(F<A<dynamic>, A<Never>> x) {}
+foo1b(F x) {}
+foo2a<X extends F<A<dynamic>, A<Never>>>() {}
+foo2b<X extends F>() {}
+
+class Foo3a<X extends F<A<dynamic>, A<Never>>> {}
+
+class Foo3b<X extends F> {}
+
+F<A<dynamic>, A<Never>> foo4a() => throw 42;
+F foo4b() => throw 42;
+foo5a({required F<A<dynamic>, A<Never>> x}) {}
+foo5b({required F x}) {}
+foo6a() {}
+foo6b() {}
+foo7a([F<A<dynamic>, A<Never>>? x]) {}
+foo7b([F? x]) {}
+
+class B<X extends int> {}
+
+bar1(B<num> x) {}
+bar2<X extends B<num>>() {}
+
+class Bar3<X extends B<num>> {}
+
+B<num> bar4() => throw 42;
+bar5({required B<num> x}) {}
+bar6() {}
+bar7([B<num>? x]) {}
+
+class Bar8 extends B<dynamic> {}
+
+main() {}
diff --git a/pkg/front_end/testcases/nnbd/super_bounded_hint.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/nnbd/super_bounded_hint.dart.textual_outline_modelled.expect
new file mode 100644
index 0000000..6dbf57e
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/super_bounded_hint.dart.textual_outline_modelled.expect
@@ -0,0 +1,33 @@
+B<num> bar4() => throw 42;
+F<A<dynamic>, A<Never>> foo4a() => throw 42;
+F foo4b() => throw 42;
+bar1(B<num> x) {}
+bar2<X extends B<num>>() {}
+bar5({required B<num> x}) {}
+bar6() {}
+bar7([B<num>? x]) {}
+
+class A<X> {}
+
+class B<X extends int> {}
+
+class Bar3<X extends B<num>> {}
+
+class Bar8 extends B<dynamic> {}
+
+class Foo3a<X extends F<A<dynamic>, A<Never>>> {}
+
+class Foo3b<X extends F> {}
+
+foo1a(F<A<dynamic>, A<Never>> x) {}
+foo1b(F x) {}
+foo2a<X extends F<A<dynamic>, A<Never>>>() {}
+foo2b<X extends F>() {}
+foo5a({required F<A<dynamic>, A<Never>> x}) {}
+foo5b({required F x}) {}
+foo6a() {}
+foo6b() {}
+foo7a([F<A<dynamic>, A<Never>>? x]) {}
+foo7b([F? x]) {}
+main() {}
+typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
diff --git a/pkg/front_end/testcases/nnbd/super_bounded_hint.dart.weak.expect b/pkg/front_end/testcases/nnbd/super_bounded_hint.dart.weak.expect
new file mode 100644
index 0000000..98725b8
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/super_bounded_hint.dart.weak.expect
@@ -0,0 +1,618 @@
+library /*isNonNullableByDefault*/;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:15:7: Error: Generic type 'F' can't be used without type arguments in a type variable bound.
+// Try providing type arguments to 'F' here.
+// foo2b<X extends F>() {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: Bound of this variable references variable 'X' from the same declaration.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:19:13: Error: Generic type 'F' can't be used without type arguments in a type variable bound.
+// Try providing type arguments to 'F' here.
+// class Foo3b<X extends F> {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: Bound of this variable references variable 'X' from the same declaration.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:11:31: Error: Type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F'.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try changing type arguments so that they conform to the bounds.
+// foo1a(F<A<dynamic>, A<Never>> x) {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:11:31: Context: If you want 'F<A<dynamic>, A<Never>>' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// foo1a(F<A<dynamic>, A<Never>> x) {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:12:9: Error: Inferred type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F'.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try specifying type arguments explicitly so that they conform to the bounds.
+// foo1b(F x) {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:12:9: Context: If you want 'F<A<dynamic>, A<Never>>' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// foo1b(F x) {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:14:7: Error: Type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F'.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try changing type arguments so that they conform to the bounds.
+// foo2a<X extends F<A<dynamic>, A<Never>>>() {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:14:7: Context: If you want 'F<A<dynamic>, A<Never>>' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// foo2a<X extends F<A<dynamic>, A<Never>>>() {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:15:7: Error: Inferred type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F'.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try specifying type arguments explicitly so that they conform to the bounds.
+// foo2b<X extends F>() {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:15:7: Context: If you want 'F<A<dynamic>, A<Never>>' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// foo2b<X extends F>() {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:17:13: Error: Type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F'.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try changing type arguments so that they conform to the bounds.
+// class Foo3a<X extends F<A<dynamic>, A<Never>>> {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:17:13: Context: If you want 'F<A<dynamic>, A<Never>>' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// class Foo3a<X extends F<A<dynamic>, A<Never>>> {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:21:30: Error: Type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F' in the return type.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try changing type arguments so that they conform to the bounds.
+// F<A<dynamic>, A<Never>> foo4a() => throw 42;
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:21:30: Context: If you want 'F<A<dynamic>, A<Never>>' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// F<A<dynamic>, A<Never>> foo4a() => throw 42;
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:22:8: Error: Type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F' in the return type.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try changing type arguments so that they conform to the bounds.
+// F foo4b() => throw 42;
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:22:8: Context: If you want 'F<A<dynamic>, A<Never>>' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// F foo4b() => throw 42;
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:24:41: Error: Type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F'.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try changing type arguments so that they conform to the bounds.
+// foo5a({required F<A<dynamic>, A<Never>> x}) {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:24:41: Context: If you want 'F<A<dynamic>, A<Never>>' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// foo5a({required F<A<dynamic>, A<Never>> x}) {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:25:19: Error: Inferred type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F'.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try specifying type arguments explicitly so that they conform to the bounds.
+// foo5b({required F x}) {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:25:19: Context: If you want 'F<A<dynamic>, A<Never>>' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// foo5b({required F x}) {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:47:33: Error: Type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F'.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try changing type arguments so that they conform to the bounds.
+// foo7a([F<A<dynamic>, A<Never>>? x]) {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:47:33: Context: If you want 'F<A<dynamic>, A<Never>>?' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>?' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// foo7a([F<A<dynamic>, A<Never>>? x]) {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:48:11: Error: Inferred type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F'.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try specifying type arguments explicitly so that they conform to the bounds.
+// foo7b([F? x]) {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:48:11: Context: If you want 'F<A<dynamic>, A<Never>>?' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>?' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// foo7b([F? x]) {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:54:13: Error: Type argument 'num' doesn't conform to the bound 'int' of the type variable 'X' on 'B'.
+// Try changing type arguments so that they conform to the bounds.
+// bar1(B<num> x) {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:52:9: Context: This is the type variable whose bound isn't conformed to.
+// class B<X extends int> {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:56:6: Error: Type argument 'num' doesn't conform to the bound 'int' of the type variable 'X' on 'B'.
+// Try changing type arguments so that they conform to the bounds.
+// bar2<X extends B<num>>() {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:52:9: Context: This is the type variable whose bound isn't conformed to.
+// class B<X extends int> {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:58:12: Error: Type argument 'num' doesn't conform to the bound 'int' of the type variable 'X' on 'B'.
+// Try changing type arguments so that they conform to the bounds.
+// class Bar3<X extends B<num>> {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:52:9: Context: This is the type variable whose bound isn't conformed to.
+// class B<X extends int> {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:60:12: Error: Type argument 'num' doesn't conform to the bound 'int' of the type variable 'X' on 'B' in the return type.
+// Try changing type arguments so that they conform to the bounds.
+// B<num> bar4() => throw 42;
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:52:9: Context: This is the type variable whose bound isn't conformed to.
+// class B<X extends int> {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:62:23: Error: Type argument 'num' doesn't conform to the bound 'int' of the type variable 'X' on 'B'.
+// Try changing type arguments so that they conform to the bounds.
+// bar5({required B<num> x}) {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:52:9: Context: This is the type variable whose bound isn't conformed to.
+// class B<X extends int> {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:76:15: Error: Type argument 'num' doesn't conform to the bound 'int' of the type variable 'X' on 'B'.
+// Try changing type arguments so that they conform to the bounds.
+// bar7([B<num>? x]) {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:52:9: Context: This is the type variable whose bound isn't conformed to.
+// class B<X extends int> {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:78:7: Error: Type argument 'dynamic' doesn't conform to the bound 'int' of the type variable 'X' on 'B' in the supertype 'B' of class 'Bar8'.
+// Try changing type arguments so that they conform to the bounds.
+// class Bar8 extends B<dynamic> {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:52:9: Context: This is the type variable whose bound isn't conformed to.
+// class B<X extends int> {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:28:27: Error: Type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F'.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try changing type arguments so that they conform to the bounds.
+// F<A<dynamic>, A<Never>> x;
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:28:27: Context: If you want 'F<A<dynamic>, A<Never>>' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// F<A<dynamic>, A<Never>> x;
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:29:32: Error: Type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F'.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try changing type arguments so that they conform to the bounds.
+// for (F<A<dynamic>, A<Never>> x in []) {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:29:32: Context: If you want 'F<A<dynamic>, A<Never>>' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// for (F<A<dynamic>, A<Never>> x in []) {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:29:37: Error: Inferred type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F'.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try specifying type arguments explicitly so that they conform to the bounds.
+// for (F<A<dynamic>, A<Never>> x in []) {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:29:37: Context: If you want 'F<A<dynamic>, A<Never>>' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// for (F<A<dynamic>, A<Never>> x in []) {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:30:35: Error: Type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F'.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try changing type arguments so that they conform to the bounds.
+// fooFoo1(F<A<dynamic>, A<Never>> x) {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:30:35: Context: If you want 'F<A<dynamic>, A<Never>>' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// fooFoo1(F<A<dynamic>, A<Never>> x) {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:31:11: Error: Type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F'.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try changing type arguments so that they conform to the bounds.
+// fooFoo2<X extends F<A<dynamic>, A<Never>>>() {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:31:11: Context: If you want 'F<A<dynamic>, A<Never>>' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// fooFoo2<X extends F<A<dynamic>, A<Never>>>() {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:32:34: Error: Type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F' in the return type.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try changing type arguments so that they conform to the bounds.
+// F<A<dynamic>, A<Never>> fooFoo4() => throw 42;
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:32:34: Context: If you want 'F<A<dynamic>, A<Never>>' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// F<A<dynamic>, A<Never>> fooFoo4() => throw 42;
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:33:45: Error: Type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F'.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try changing type arguments so that they conform to the bounds.
+// fooFoo5({required F<A<dynamic>, A<Never>> x}) {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:33:45: Context: If you want 'F<A<dynamic>, A<Never>>' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// fooFoo5({required F<A<dynamic>, A<Never>> x}) {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:34:37: Error: Type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F'.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try changing type arguments so that they conform to the bounds.
+// fooFoo7([F<A<dynamic>, A<Never>>? x]) {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:34:37: Context: If you want 'F<A<dynamic>, A<Never>>?' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>?' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// fooFoo7([F<A<dynamic>, A<Never>>? x]) {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:38:5: Error: Type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F'.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try changing type arguments so that they conform to the bounds.
+// F x;
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:38:5: Context: If you want 'F<A<dynamic>, A<Never>>' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// F x;
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:39:10: Error: Type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F'.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try changing type arguments so that they conform to the bounds.
+// for (F x in []) {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:39:10: Context: If you want 'F<A<dynamic>, A<Never>>' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// for (F x in []) {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:39:15: Error: Inferred type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F'.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try specifying type arguments explicitly so that they conform to the bounds.
+// for (F x in []) {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:39:15: Context: If you want 'F<A<dynamic>, A<Never>>' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// for (F x in []) {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:40:13: Error: Inferred type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F'.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try specifying type arguments explicitly so that they conform to the bounds.
+// fooFoo1(F x) {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:40:13: Context: If you want 'F<A<dynamic>, A<Never>>' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// fooFoo1(F x) {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:41:11: Error: Inferred type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F'.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try specifying type arguments explicitly so that they conform to the bounds.
+// fooFoo2<X extends F>() {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:41:11: Context: If you want 'F<A<dynamic>, A<Never>>' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// fooFoo2<X extends F>() {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:42:12: Error: Type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F' in the return type.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try changing type arguments so that they conform to the bounds.
+// F fooFoo4() => throw 42;
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:42:12: Context: If you want 'F<A<dynamic>, A<Never>>' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// F fooFoo4() => throw 42;
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:43:23: Error: Inferred type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F'.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try specifying type arguments explicitly so that they conform to the bounds.
+// fooFoo5({required F x}) {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:43:23: Context: If you want 'F<A<dynamic>, A<Never>>' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// fooFoo5({required F x}) {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:44:15: Error: Inferred type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F'.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try specifying type arguments explicitly so that they conform to the bounds.
+// fooFoo7([F? x]) {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:44:15: Context: If you want 'F<A<dynamic>, A<Never>>?' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>?' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// fooFoo7([F? x]) {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:65:10: Error: Type argument 'num' doesn't conform to the bound 'int' of the type variable 'X' on 'B'.
+// Try changing type arguments so that they conform to the bounds.
+// B<num> x;
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:52:9: Context: This is the type variable whose bound isn't conformed to.
+// class B<X extends int> {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:66:15: Error: Type argument 'num' doesn't conform to the bound 'int' of the type variable 'X' on 'B'.
+// Try changing type arguments so that they conform to the bounds.
+// for (B<num> x in []) {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:52:9: Context: This is the type variable whose bound isn't conformed to.
+// class B<X extends int> {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:72:7: Error: Type argument 'dynamic' doesn't conform to the bound 'int' of the type variable 'X' on 'B'.
+// Try changing type arguments so that they conform to the bounds.
+// new B<dynamic>();
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:52:9: Context: This is the type variable whose bound isn't conformed to.
+// class B<X extends int> {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:66:20: Error: Inferred type argument 'num' doesn't conform to the bound 'int' of the type variable 'X' on 'B'.
+// Try specifying type arguments explicitly so that they conform to the bounds.
+// for (B<num> x in []) {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:52:9: Context: This is the type variable whose bound isn't conformed to.
+// class B<X extends int> {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:67:18: Error: Type argument 'num' doesn't conform to the bound 'int' of the type variable 'X' on 'B'.
+// Try changing type arguments so that they conform to the bounds.
+// barBar1(B<num> x) {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:52:9: Context: This is the type variable whose bound isn't conformed to.
+// class B<X extends int> {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:68:11: Error: Type argument 'num' doesn't conform to the bound 'int' of the type variable 'X' on 'B'.
+// Try changing type arguments so that they conform to the bounds.
+// barBar2<X extends B<num>>() {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:52:9: Context: This is the type variable whose bound isn't conformed to.
+// class B<X extends int> {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:69:17: Error: Type argument 'num' doesn't conform to the bound 'int' of the type variable 'X' on 'B' in the return type.
+// Try changing type arguments so that they conform to the bounds.
+// B<num> barBar4() => throw 42;
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:52:9: Context: This is the type variable whose bound isn't conformed to.
+// class B<X extends int> {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:70:28: Error: Type argument 'num' doesn't conform to the bound 'int' of the type variable 'X' on 'B'.
+// Try changing type arguments so that they conform to the bounds.
+// barBar5({required B<num> x}) {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:52:9: Context: This is the type variable whose bound isn't conformed to.
+// class B<X extends int> {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:71:20: Error: Type argument 'num' doesn't conform to the bound 'int' of the type variable 'X' on 'B'.
+// Try changing type arguments so that they conform to the bounds.
+// barBar7([B<num>? x]) {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:52:9: Context: This is the type variable whose bound isn't conformed to.
+// class B<X extends int> {}
+// ^
+//
+import self as self;
+import "dart:core" as core;
+
+typedef F<X extends self::A<X> = self::A<dynamic>, contravariant Y extends self::A<Y> = self::A<Never>> = (Y) → X;
+class A<X extends core::Object? = dynamic> extends core::Object {
+ synthetic constructor •() → self::A<self::A::X%>
+ : super core::Object::•()
+ ;
+}
+class Foo3a<X extends (self::A<Never>) → self::A<dynamic> = (self::A<Never>) → self::A<dynamic>> extends core::Object {
+ synthetic constructor •() → self::Foo3a<self::Foo3a::X>
+ : super core::Object::•()
+ ;
+}
+class Foo3b<X extends (self::A<Never>) → self::A<dynamic> = dynamic> extends core::Object {
+ synthetic constructor •() → self::Foo3b<self::Foo3b::X>
+ : super core::Object::•()
+ ;
+}
+class B<X extends core::int = core::int> extends core::Object {
+ synthetic constructor •() → self::B<self::B::X>
+ : super core::Object::•()
+ ;
+}
+class Bar3<X extends self::B<core::num> = self::B<core::num>> extends core::Object {
+ synthetic constructor •() → self::Bar3<self::Bar3::X>
+ : super core::Object::•()
+ ;
+}
+class Bar8 extends self::B<dynamic> {
+ synthetic constructor •() → self::Bar8
+ : super self::B::•()
+ ;
+}
+static method foo1a((self::A<Never>) → self::A<dynamic> x) → dynamic {}
+static method foo1b((self::A<Never>) → self::A<dynamic> x) → dynamic {}
+static method foo2a<X extends (self::A<Never>) → self::A<dynamic> = (self::A<Never>) → self::A<dynamic>>() → dynamic {}
+static method foo2b<X extends (self::A<Never>) → self::A<dynamic> = dynamic>() → dynamic {}
+static method foo4a() → (self::A<Never>) → self::A<dynamic>
+ return throw 42;
+static method foo4b() → (self::A<Never>) → self::A<dynamic>
+ return throw 42;
+static method foo5a({required (self::A<Never>) → self::A<dynamic> x = #C1}) → dynamic {}
+static method foo5b({required (self::A<Never>) → self::A<dynamic> x = #C1}) → dynamic {}
+static method foo6a() → dynamic {
+ (self::A<Never>) → self::A<dynamic> x;
+ for ((self::A<Never>) → self::A<dynamic> x in <(self::A<Never>) → self::A<dynamic>>[]) {
+ }
+ function fooFoo1((self::A<Never>) → self::A<dynamic> x) → Null {}
+ function fooFoo2<X extends (self::A<Never>) → self::A<dynamic> = (self::A<Never>) → self::A<dynamic>>() → Null {}
+ function fooFoo4() → (self::A<Never>) → self::A<dynamic>
+ return throw 42;
+ function fooFoo5({required (self::A<Never>) → self::A<dynamic> x = #C1}) → Null {}
+ function fooFoo7([(self::A<Never>) →? self::A<dynamic> x = #C1]) → Null {}
+}
+static method foo6b() → dynamic {
+ (self::A<Never>) → self::A<dynamic> x;
+ for ((self::A<Never>) → self::A<dynamic> x in <(self::A<Never>) → self::A<dynamic>>[]) {
+ }
+ function fooFoo1((self::A<Never>) → self::A<dynamic> x) → Null {}
+ function fooFoo2<X extends (self::A<Never>) → self::A<dynamic> = (self::A<Never>) → self::A<dynamic>>() → Null {}
+ function fooFoo4() → (self::A<Never>) → self::A<dynamic>
+ return throw 42;
+ function fooFoo5({required (self::A<Never>) → self::A<dynamic> x = #C1}) → Null {}
+ function fooFoo7([(self::A<Never>) →? self::A<dynamic> x = #C1]) → Null {}
+}
+static method foo7a([(self::A<Never>) →? self::A<dynamic> x = #C1]) → dynamic {}
+static method foo7b([(self::A<Never>) →? self::A<dynamic> x = #C1]) → dynamic {}
+static method bar1(self::B<core::num> x) → dynamic {}
+static method bar2<X extends self::B<core::num> = self::B<core::num>>() → dynamic {}
+static method bar4() → self::B<core::num>
+ return throw 42;
+static method bar5({required self::B<core::num> x = #C1}) → dynamic {}
+static method bar6() → dynamic {
+ self::B<core::num> x;
+ for (self::B<core::num> x in <self::B<core::num>>[]) {
+ }
+ function barBar1(self::B<core::num> x) → Null {}
+ function barBar2<X extends self::B<core::num> = self::B<core::num>>() → Null {}
+ function barBar4() → self::B<core::num>
+ return throw 42;
+ function barBar5({required self::B<core::num> x = #C1}) → Null {}
+ function barBar7([self::B<core::num>? x = #C1]) → Null {}
+ new self::B::•<dynamic>();
+ new self::A::•<self::B<dynamic>>();
+}
+static method bar7([self::B<core::num>? x = #C1]) → dynamic {}
+static method main() → dynamic {}
+
+constants {
+ #C1 = null
+}
diff --git a/pkg/front_end/testcases/nnbd/super_bounded_hint.dart.weak.transformed.expect b/pkg/front_end/testcases/nnbd/super_bounded_hint.dart.weak.transformed.expect
new file mode 100644
index 0000000..edb2e83
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/super_bounded_hint.dart.weak.transformed.expect
@@ -0,0 +1,633 @@
+library /*isNonNullableByDefault*/;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:15:7: Error: Generic type 'F' can't be used without type arguments in a type variable bound.
+// Try providing type arguments to 'F' here.
+// foo2b<X extends F>() {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: Bound of this variable references variable 'X' from the same declaration.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:19:13: Error: Generic type 'F' can't be used without type arguments in a type variable bound.
+// Try providing type arguments to 'F' here.
+// class Foo3b<X extends F> {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: Bound of this variable references variable 'X' from the same declaration.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:11:31: Error: Type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F'.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try changing type arguments so that they conform to the bounds.
+// foo1a(F<A<dynamic>, A<Never>> x) {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:11:31: Context: If you want 'F<A<dynamic>, A<Never>>' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// foo1a(F<A<dynamic>, A<Never>> x) {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:12:9: Error: Inferred type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F'.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try specifying type arguments explicitly so that they conform to the bounds.
+// foo1b(F x) {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:12:9: Context: If you want 'F<A<dynamic>, A<Never>>' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// foo1b(F x) {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:14:7: Error: Type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F'.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try changing type arguments so that they conform to the bounds.
+// foo2a<X extends F<A<dynamic>, A<Never>>>() {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:14:7: Context: If you want 'F<A<dynamic>, A<Never>>' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// foo2a<X extends F<A<dynamic>, A<Never>>>() {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:15:7: Error: Inferred type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F'.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try specifying type arguments explicitly so that they conform to the bounds.
+// foo2b<X extends F>() {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:15:7: Context: If you want 'F<A<dynamic>, A<Never>>' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// foo2b<X extends F>() {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:17:13: Error: Type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F'.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try changing type arguments so that they conform to the bounds.
+// class Foo3a<X extends F<A<dynamic>, A<Never>>> {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:17:13: Context: If you want 'F<A<dynamic>, A<Never>>' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// class Foo3a<X extends F<A<dynamic>, A<Never>>> {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:21:30: Error: Type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F' in the return type.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try changing type arguments so that they conform to the bounds.
+// F<A<dynamic>, A<Never>> foo4a() => throw 42;
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:21:30: Context: If you want 'F<A<dynamic>, A<Never>>' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// F<A<dynamic>, A<Never>> foo4a() => throw 42;
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:22:8: Error: Type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F' in the return type.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try changing type arguments so that they conform to the bounds.
+// F foo4b() => throw 42;
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:22:8: Context: If you want 'F<A<dynamic>, A<Never>>' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// F foo4b() => throw 42;
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:24:41: Error: Type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F'.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try changing type arguments so that they conform to the bounds.
+// foo5a({required F<A<dynamic>, A<Never>> x}) {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:24:41: Context: If you want 'F<A<dynamic>, A<Never>>' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// foo5a({required F<A<dynamic>, A<Never>> x}) {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:25:19: Error: Inferred type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F'.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try specifying type arguments explicitly so that they conform to the bounds.
+// foo5b({required F x}) {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:25:19: Context: If you want 'F<A<dynamic>, A<Never>>' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// foo5b({required F x}) {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:47:33: Error: Type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F'.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try changing type arguments so that they conform to the bounds.
+// foo7a([F<A<dynamic>, A<Never>>? x]) {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:47:33: Context: If you want 'F<A<dynamic>, A<Never>>?' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>?' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// foo7a([F<A<dynamic>, A<Never>>? x]) {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:48:11: Error: Inferred type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F'.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try specifying type arguments explicitly so that they conform to the bounds.
+// foo7b([F? x]) {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:48:11: Context: If you want 'F<A<dynamic>, A<Never>>?' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>?' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// foo7b([F? x]) {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:54:13: Error: Type argument 'num' doesn't conform to the bound 'int' of the type variable 'X' on 'B'.
+// Try changing type arguments so that they conform to the bounds.
+// bar1(B<num> x) {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:52:9: Context: This is the type variable whose bound isn't conformed to.
+// class B<X extends int> {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:56:6: Error: Type argument 'num' doesn't conform to the bound 'int' of the type variable 'X' on 'B'.
+// Try changing type arguments so that they conform to the bounds.
+// bar2<X extends B<num>>() {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:52:9: Context: This is the type variable whose bound isn't conformed to.
+// class B<X extends int> {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:58:12: Error: Type argument 'num' doesn't conform to the bound 'int' of the type variable 'X' on 'B'.
+// Try changing type arguments so that they conform to the bounds.
+// class Bar3<X extends B<num>> {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:52:9: Context: This is the type variable whose bound isn't conformed to.
+// class B<X extends int> {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:60:12: Error: Type argument 'num' doesn't conform to the bound 'int' of the type variable 'X' on 'B' in the return type.
+// Try changing type arguments so that they conform to the bounds.
+// B<num> bar4() => throw 42;
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:52:9: Context: This is the type variable whose bound isn't conformed to.
+// class B<X extends int> {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:62:23: Error: Type argument 'num' doesn't conform to the bound 'int' of the type variable 'X' on 'B'.
+// Try changing type arguments so that they conform to the bounds.
+// bar5({required B<num> x}) {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:52:9: Context: This is the type variable whose bound isn't conformed to.
+// class B<X extends int> {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:76:15: Error: Type argument 'num' doesn't conform to the bound 'int' of the type variable 'X' on 'B'.
+// Try changing type arguments so that they conform to the bounds.
+// bar7([B<num>? x]) {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:52:9: Context: This is the type variable whose bound isn't conformed to.
+// class B<X extends int> {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:78:7: Error: Type argument 'dynamic' doesn't conform to the bound 'int' of the type variable 'X' on 'B' in the supertype 'B' of class 'Bar8'.
+// Try changing type arguments so that they conform to the bounds.
+// class Bar8 extends B<dynamic> {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:52:9: Context: This is the type variable whose bound isn't conformed to.
+// class B<X extends int> {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:28:27: Error: Type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F'.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try changing type arguments so that they conform to the bounds.
+// F<A<dynamic>, A<Never>> x;
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:28:27: Context: If you want 'F<A<dynamic>, A<Never>>' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// F<A<dynamic>, A<Never>> x;
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:29:32: Error: Type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F'.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try changing type arguments so that they conform to the bounds.
+// for (F<A<dynamic>, A<Never>> x in []) {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:29:32: Context: If you want 'F<A<dynamic>, A<Never>>' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// for (F<A<dynamic>, A<Never>> x in []) {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:29:37: Error: Inferred type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F'.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try specifying type arguments explicitly so that they conform to the bounds.
+// for (F<A<dynamic>, A<Never>> x in []) {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:29:37: Context: If you want 'F<A<dynamic>, A<Never>>' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// for (F<A<dynamic>, A<Never>> x in []) {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:30:35: Error: Type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F'.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try changing type arguments so that they conform to the bounds.
+// fooFoo1(F<A<dynamic>, A<Never>> x) {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:30:35: Context: If you want 'F<A<dynamic>, A<Never>>' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// fooFoo1(F<A<dynamic>, A<Never>> x) {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:31:11: Error: Type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F'.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try changing type arguments so that they conform to the bounds.
+// fooFoo2<X extends F<A<dynamic>, A<Never>>>() {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:31:11: Context: If you want 'F<A<dynamic>, A<Never>>' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// fooFoo2<X extends F<A<dynamic>, A<Never>>>() {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:32:34: Error: Type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F' in the return type.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try changing type arguments so that they conform to the bounds.
+// F<A<dynamic>, A<Never>> fooFoo4() => throw 42;
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:32:34: Context: If you want 'F<A<dynamic>, A<Never>>' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// F<A<dynamic>, A<Never>> fooFoo4() => throw 42;
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:33:45: Error: Type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F'.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try changing type arguments so that they conform to the bounds.
+// fooFoo5({required F<A<dynamic>, A<Never>> x}) {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:33:45: Context: If you want 'F<A<dynamic>, A<Never>>' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// fooFoo5({required F<A<dynamic>, A<Never>> x}) {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:34:37: Error: Type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F'.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try changing type arguments so that they conform to the bounds.
+// fooFoo7([F<A<dynamic>, A<Never>>? x]) {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:34:37: Context: If you want 'F<A<dynamic>, A<Never>>?' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>?' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// fooFoo7([F<A<dynamic>, A<Never>>? x]) {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:38:5: Error: Type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F'.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try changing type arguments so that they conform to the bounds.
+// F x;
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:38:5: Context: If you want 'F<A<dynamic>, A<Never>>' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// F x;
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:39:10: Error: Type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F'.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try changing type arguments so that they conform to the bounds.
+// for (F x in []) {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:39:10: Context: If you want 'F<A<dynamic>, A<Never>>' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// for (F x in []) {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:39:15: Error: Inferred type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F'.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try specifying type arguments explicitly so that they conform to the bounds.
+// for (F x in []) {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:39:15: Context: If you want 'F<A<dynamic>, A<Never>>' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// for (F x in []) {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:40:13: Error: Inferred type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F'.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try specifying type arguments explicitly so that they conform to the bounds.
+// fooFoo1(F x) {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:40:13: Context: If you want 'F<A<dynamic>, A<Never>>' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// fooFoo1(F x) {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:41:11: Error: Inferred type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F'.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try specifying type arguments explicitly so that they conform to the bounds.
+// fooFoo2<X extends F>() {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:41:11: Context: If you want 'F<A<dynamic>, A<Never>>' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// fooFoo2<X extends F>() {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:42:12: Error: Type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F' in the return type.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try changing type arguments so that they conform to the bounds.
+// F fooFoo4() => throw 42;
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:42:12: Context: If you want 'F<A<dynamic>, A<Never>>' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// F fooFoo4() => throw 42;
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:43:23: Error: Inferred type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F'.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try specifying type arguments explicitly so that they conform to the bounds.
+// fooFoo5({required F x}) {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:43:23: Context: If you want 'F<A<dynamic>, A<Never>>' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// fooFoo5({required F x}) {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:44:15: Error: Inferred type argument 'A<dynamic>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'F'.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// Try specifying type arguments explicitly so that they conform to the bounds.
+// fooFoo7([F? x]) {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:9:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef F<X extends A<X>, Y extends A<Y>> = X Function(Y);
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:44:15: Context: If you want 'F<A<dynamic>, A<Never>>?' to be a super-bounded type, note that the inverted type 'F<A<Never>, A<Object?>>?' must then satisfy its bounds, which it does not.
+// - 'A' is from 'pkg/front_end/testcases/nnbd/super_bounded_hint.dart'.
+// - 'Object' is from 'dart:core'.
+// fooFoo7([F? x]) {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:65:10: Error: Type argument 'num' doesn't conform to the bound 'int' of the type variable 'X' on 'B'.
+// Try changing type arguments so that they conform to the bounds.
+// B<num> x;
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:52:9: Context: This is the type variable whose bound isn't conformed to.
+// class B<X extends int> {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:66:15: Error: Type argument 'num' doesn't conform to the bound 'int' of the type variable 'X' on 'B'.
+// Try changing type arguments so that they conform to the bounds.
+// for (B<num> x in []) {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:52:9: Context: This is the type variable whose bound isn't conformed to.
+// class B<X extends int> {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:72:7: Error: Type argument 'dynamic' doesn't conform to the bound 'int' of the type variable 'X' on 'B'.
+// Try changing type arguments so that they conform to the bounds.
+// new B<dynamic>();
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:52:9: Context: This is the type variable whose bound isn't conformed to.
+// class B<X extends int> {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:66:20: Error: Inferred type argument 'num' doesn't conform to the bound 'int' of the type variable 'X' on 'B'.
+// Try specifying type arguments explicitly so that they conform to the bounds.
+// for (B<num> x in []) {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:52:9: Context: This is the type variable whose bound isn't conformed to.
+// class B<X extends int> {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:67:18: Error: Type argument 'num' doesn't conform to the bound 'int' of the type variable 'X' on 'B'.
+// Try changing type arguments so that they conform to the bounds.
+// barBar1(B<num> x) {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:52:9: Context: This is the type variable whose bound isn't conformed to.
+// class B<X extends int> {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:68:11: Error: Type argument 'num' doesn't conform to the bound 'int' of the type variable 'X' on 'B'.
+// Try changing type arguments so that they conform to the bounds.
+// barBar2<X extends B<num>>() {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:52:9: Context: This is the type variable whose bound isn't conformed to.
+// class B<X extends int> {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:69:17: Error: Type argument 'num' doesn't conform to the bound 'int' of the type variable 'X' on 'B' in the return type.
+// Try changing type arguments so that they conform to the bounds.
+// B<num> barBar4() => throw 42;
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:52:9: Context: This is the type variable whose bound isn't conformed to.
+// class B<X extends int> {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:70:28: Error: Type argument 'num' doesn't conform to the bound 'int' of the type variable 'X' on 'B'.
+// Try changing type arguments so that they conform to the bounds.
+// barBar5({required B<num> x}) {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:52:9: Context: This is the type variable whose bound isn't conformed to.
+// class B<X extends int> {}
+// ^
+//
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:71:20: Error: Type argument 'num' doesn't conform to the bound 'int' of the type variable 'X' on 'B'.
+// Try changing type arguments so that they conform to the bounds.
+// barBar7([B<num>? x]) {}
+// ^
+// pkg/front_end/testcases/nnbd/super_bounded_hint.dart:52:9: Context: This is the type variable whose bound isn't conformed to.
+// class B<X extends int> {}
+// ^
+//
+import self as self;
+import "dart:core" as core;
+
+typedef F<X extends self::A<X> = self::A<dynamic>, contravariant Y extends self::A<Y> = self::A<Never>> = (Y) → X;
+class A<X extends core::Object? = dynamic> extends core::Object {
+ synthetic constructor •() → self::A<self::A::X%>
+ : super core::Object::•()
+ ;
+}
+class Foo3a<X extends (self::A<Never>) → self::A<dynamic> = (self::A<Never>) → self::A<dynamic>> extends core::Object {
+ synthetic constructor •() → self::Foo3a<self::Foo3a::X>
+ : super core::Object::•()
+ ;
+}
+class Foo3b<X extends (self::A<Never>) → self::A<dynamic> = dynamic> extends core::Object {
+ synthetic constructor •() → self::Foo3b<self::Foo3b::X>
+ : super core::Object::•()
+ ;
+}
+class B<X extends core::int = core::int> extends core::Object {
+ synthetic constructor •() → self::B<self::B::X>
+ : super core::Object::•()
+ ;
+}
+class Bar3<X extends self::B<core::num> = self::B<core::num>> extends core::Object {
+ synthetic constructor •() → self::Bar3<self::Bar3::X>
+ : super core::Object::•()
+ ;
+}
+class Bar8 extends self::B<dynamic> {
+ synthetic constructor •() → self::Bar8
+ : super self::B::•()
+ ;
+}
+static method foo1a((self::A<Never>) → self::A<dynamic> x) → dynamic {}
+static method foo1b((self::A<Never>) → self::A<dynamic> x) → dynamic {}
+static method foo2a<X extends (self::A<Never>) → self::A<dynamic> = (self::A<Never>) → self::A<dynamic>>() → dynamic {}
+static method foo2b<X extends (self::A<Never>) → self::A<dynamic> = dynamic>() → dynamic {}
+static method foo4a() → (self::A<Never>) → self::A<dynamic>
+ return throw 42;
+static method foo4b() → (self::A<Never>) → self::A<dynamic>
+ return throw 42;
+static method foo5a({required (self::A<Never>) → self::A<dynamic> x = #C1}) → dynamic {}
+static method foo5b({required (self::A<Never>) → self::A<dynamic> x = #C1}) → dynamic {}
+static method foo6a() → dynamic {
+ (self::A<Never>) → self::A<dynamic> x;
+ {
+ core::Iterator<(self::A<Never>) → self::A<dynamic>> :sync-for-iterator = core::_GrowableList::•<(self::A<Never>) → self::A<dynamic>>(0).{core::Iterable::iterator};
+ for (; :sync-for-iterator.{core::Iterator::moveNext}(); ) {
+ (self::A<Never>) → self::A<dynamic> x = :sync-for-iterator.{core::Iterator::current};
+ {}
+ }
+ }
+ function fooFoo1((self::A<Never>) → self::A<dynamic> x) → Null {}
+ function fooFoo2<X extends (self::A<Never>) → self::A<dynamic> = (self::A<Never>) → self::A<dynamic>>() → Null {}
+ function fooFoo4() → (self::A<Never>) → self::A<dynamic>
+ return throw 42;
+ function fooFoo5({required (self::A<Never>) → self::A<dynamic> x = #C1}) → Null {}
+ function fooFoo7([(self::A<Never>) →? self::A<dynamic> x = #C1]) → Null {}
+}
+static method foo6b() → dynamic {
+ (self::A<Never>) → self::A<dynamic> x;
+ {
+ core::Iterator<(self::A<Never>) → self::A<dynamic>> :sync-for-iterator = core::_GrowableList::•<(self::A<Never>) → self::A<dynamic>>(0).{core::Iterable::iterator};
+ for (; :sync-for-iterator.{core::Iterator::moveNext}(); ) {
+ (self::A<Never>) → self::A<dynamic> x = :sync-for-iterator.{core::Iterator::current};
+ {}
+ }
+ }
+ function fooFoo1((self::A<Never>) → self::A<dynamic> x) → Null {}
+ function fooFoo2<X extends (self::A<Never>) → self::A<dynamic> = (self::A<Never>) → self::A<dynamic>>() → Null {}
+ function fooFoo4() → (self::A<Never>) → self::A<dynamic>
+ return throw 42;
+ function fooFoo5({required (self::A<Never>) → self::A<dynamic> x = #C1}) → Null {}
+ function fooFoo7([(self::A<Never>) →? self::A<dynamic> x = #C1]) → Null {}
+}
+static method foo7a([(self::A<Never>) →? self::A<dynamic> x = #C1]) → dynamic {}
+static method foo7b([(self::A<Never>) →? self::A<dynamic> x = #C1]) → dynamic {}
+static method bar1(self::B<core::num> x) → dynamic {}
+static method bar2<X extends self::B<core::num> = self::B<core::num>>() → dynamic {}
+static method bar4() → self::B<core::num>
+ return throw 42;
+static method bar5({required self::B<core::num> x = #C1}) → dynamic {}
+static method bar6() → dynamic {
+ self::B<core::num> x;
+ {
+ core::Iterator<self::B<core::num>> :sync-for-iterator = core::_GrowableList::•<self::B<core::num>>(0).{core::Iterable::iterator};
+ for (; :sync-for-iterator.{core::Iterator::moveNext}(); ) {
+ self::B<core::num> x = :sync-for-iterator.{core::Iterator::current};
+ {}
+ }
+ }
+ function barBar1(self::B<core::num> x) → Null {}
+ function barBar2<X extends self::B<core::num> = self::B<core::num>>() → Null {}
+ function barBar4() → self::B<core::num>
+ return throw 42;
+ function barBar5({required self::B<core::num> x = #C1}) → Null {}
+ function barBar7([self::B<core::num>? x = #C1]) → Null {}
+ new self::B::•<dynamic>();
+ new self::A::•<self::B<dynamic>>();
+}
+static method bar7([self::B<core::num>? x = #C1]) → dynamic {}
+static method main() → dynamic {}
+
+constants {
+ #C1 = null
+}
diff --git a/pkg/kernel/lib/src/bounds_checks.dart b/pkg/kernel/lib/src/bounds_checks.dart
index c68cace..a9c7859 100644
--- a/pkg/kernel/lib/src/bounds_checks.dart
+++ b/pkg/kernel/lib/src/bounds_checks.dart
@@ -226,8 +226,20 @@
/// The enclosing type of the issue, that is, the one with [typeParameter].
final DartType enclosingType;
+ /// The type computed from [enclosingType] for the super-boundness check.
+ ///
+ /// This field can be null. [invertedType] is supposed to enhance error
+ /// messages, providing the auxiliary type for super-boundness checks for the
+ /// user. It is set to null if it's not helpful, for example, if
+ /// [enclosingType] is well-bounded or is strictly required to be
+ /// regular-bounded, so the super-boundness check is skipped. It is set to
+ /// null also if the inversion didn't change the type at all, and it's not
+ /// helpful to show the same type to the user.
+ DartType invertedType;
+
TypeArgumentIssue(
- this.index, this.argument, this.typeParameter, this.enclosingType);
+ this.index, this.argument, this.typeParameter, this.enclosingType,
+ {this.invertedType});
int get hashCode {
int hash = 0x3fffffff & index;
@@ -293,30 +305,53 @@
variables = type.typedefNode.typeParameters;
arguments = type.typeArguments;
} else if (type is FunctionType) {
- List<TypeArgumentIssue> result = <TypeArgumentIssue>[];
+ List<TypeArgumentIssue> result = null;
+
for (TypeParameter parameter in type.typeParameters) {
- result.addAll(findTypeArgumentIssues(
- library, parameter.bound, typeEnvironment, subtypeCheckMode,
- allowSuperBounded: true) ??
- const <TypeArgumentIssue>[]);
+ List<TypeArgumentIssue> parameterResult = findTypeArgumentIssues(
+ library, parameter.bound, typeEnvironment, subtypeCheckMode,
+ allowSuperBounded: true);
+ if (result == null) {
+ result = parameterResult;
+ } else if (parameterResult != null) {
+ result.addAll(parameterResult);
+ }
}
+
for (DartType formal in type.positionalParameters) {
- result.addAll(findTypeArgumentIssues(
- library, formal, typeEnvironment, subtypeCheckMode,
- allowSuperBounded: true) ??
- const <TypeArgumentIssue>[]);
+ List<TypeArgumentIssue> parameterResult = findTypeArgumentIssues(
+ library, formal, typeEnvironment, subtypeCheckMode,
+ allowSuperBounded: true);
+ if (result == null) {
+ result = parameterResult;
+ } else if (parameterResult != null) {
+ result.addAll(parameterResult);
+ }
}
+
for (NamedType named in type.namedParameters) {
- result.addAll(findTypeArgumentIssues(
- library, named.type, typeEnvironment, subtypeCheckMode,
- allowSuperBounded: true) ??
- const <TypeArgumentIssue>[]);
+ List<TypeArgumentIssue> parameterResult = findTypeArgumentIssues(
+ library, named.type, typeEnvironment, subtypeCheckMode,
+ allowSuperBounded: true);
+ if (result == null) {
+ result = parameterResult;
+ } else if (parameterResult != null) {
+ result.addAll(parameterResult);
+ }
}
- result.addAll(findTypeArgumentIssues(
- library, type.returnType, typeEnvironment, subtypeCheckMode,
- allowSuperBounded: true) ??
- const <TypeArgumentIssue>[]);
- return result.isEmpty ? null : result;
+
+ {
+ List<TypeArgumentIssue> returnTypeResult = findTypeArgumentIssues(
+ library, type.returnType, typeEnvironment, subtypeCheckMode,
+ allowSuperBounded: true);
+ if (result == null) {
+ result = returnTypeResult;
+ } else if (returnTypeResult != null) {
+ result.addAll(returnTypeResult);
+ }
+ }
+
+ return result;
} else if (type is FutureOrType) {
variables = typeEnvironment.coreTypes.futureClass.typeParameters;
arguments = <DartType>[type.typeArgument];
@@ -326,8 +361,8 @@
if (variables == null) return null;
- List<TypeArgumentIssue> result;
- List<TypeArgumentIssue> argumentsResult;
+ List<TypeArgumentIssue> result = null;
+ List<TypeArgumentIssue> argumentsResult = null;
Map<TypeParameter, DartType> substitutionMap =
new Map<TypeParameter, DartType>.fromIterables(variables, arguments);
@@ -343,11 +378,12 @@
bound = legacyErasure(bound);
}
if (!typeEnvironment.isSubtypeOf(argument, bound, subtypeCheckMode)) {
- // If the bound is InvalidType it's not checked, because an error was
- // reported already at the time of the creation of InvalidType.
result ??= <TypeArgumentIssue>[];
result.add(new TypeArgumentIssue(i, argument, variables[i], type));
}
+ } else {
+ // The bound is InvalidType so it's not checked, because an error was
+ // reported already at the time of the creation of InvalidType.
}
List<TypeArgumentIssue> issues = findTypeArgumentIssues(
@@ -371,18 +407,25 @@
if (result == null) return null;
if (!allowSuperBounded) return result;
- List<TypeArgumentIssue> convertedResult = null;
- type = convertSuperBoundedToRegularBounded(library, typeEnvironment, type);
- List<DartType> argumentsToReport = arguments.toList();
- if (type is InterfaceType) {
- variables = type.classNode.typeParameters;
- arguments = type.typeArguments;
- } else if (type is TypedefType) {
- variables = type.typedefNode.typeParameters;
- arguments = type.typeArguments;
- } else if (type is FutureOrType) {
+ bool isCorrectSuperBounded = true;
+ DartType invertedType =
+ convertSuperBoundedToRegularBounded(library, typeEnvironment, type);
+
+ // The auxiliary type is the same as [type]. At this point we know that
+ // [type] is not regular-bounded, which means that the inverted type is also
+ // not regular-bounded. These two judgments together allow us to conclude
+ // that [type] is not well-bounded.
+ if (invertedType == null) return result;
+
+ if (invertedType is InterfaceType) {
+ variables = invertedType.classNode.typeParameters;
+ arguments = invertedType.typeArguments;
+ } else if (invertedType is TypedefType) {
+ variables = invertedType.typedefNode.typeParameters;
+ arguments = invertedType.typeArguments;
+ } else if (invertedType is FutureOrType) {
variables = typeEnvironment.coreTypes.futureClass.typeParameters;
- arguments = <DartType>[type.typeArgument];
+ arguments = <DartType>[invertedType.typeArgument];
}
substitutionMap =
new Map<TypeParameter, DartType>.fromIterables(variables, arguments);
@@ -390,25 +433,31 @@
DartType argument = arguments[i];
if (isGenericFunctionTypeOrAlias(argument)) {
// Generic function types aren't allowed as type arguments either.
- convertedResult ??= <TypeArgumentIssue>[];
- convertedResult.add(
- new TypeArgumentIssue(i, argumentsToReport[i], variables[i], type));
+ isCorrectSuperBounded = false;
} else if (!typeEnvironment.isSubtypeOf(argument,
substitute(variables[i].bound, substitutionMap), subtypeCheckMode)) {
- convertedResult ??= <TypeArgumentIssue>[];
- convertedResult.add(
- new TypeArgumentIssue(i, argumentsToReport[i], variables[i], type));
+ isCorrectSuperBounded = false;
}
}
if (argumentsResult != null) {
- convertedResult ??= <TypeArgumentIssue>[];
- convertedResult.addAll(argumentsResult);
+ isCorrectSuperBounded = false;
}
if (typedefRhsResult != null) {
- convertedResult ??= <TypeArgumentIssue>[];
- convertedResult.addAll(typedefRhsResult);
+ isCorrectSuperBounded = false;
}
- return convertedResult == null || convertedResult.isEmpty ? null : result;
+
+ // The inverted type is regular-bounded, which means that [type] is
+ // well-bounded.
+ if (isCorrectSuperBounded) return null;
+
+ // The inverted type isn't regular-bounded, but it's different from [type].
+ // In this case we'll provide the programmer with the inverted type as a hint,
+ // in case they were going for a super-bounded type and will benefit from that
+ // information correcting the program.
+ for (TypeArgumentIssue issue in result) {
+ issue.invertedType = invertedType;
+ }
+ return result;
}
// Finds type arguments that don't follow the rules of well-boundness.
@@ -478,7 +527,7 @@
/// Replaces all covariant occurrences of `dynamic`, `Object`, and `void` with
/// [BottomType] and all contravariant occurrences of `Null` and [BottomType]
-/// with `Object`.
+/// with `Object`. Returns null if the converted type is the same as [type].
DartType convertSuperBoundedToRegularBounded(
Library clientLibrary, TypeEnvironment typeEnvironment, DartType type,
{int variance = Variance.covariant}) {
@@ -515,70 +564,162 @@
type.classNode.typeParameters.isNotEmpty) {
assert(type.classNode.typeParameters.length == type.typeArguments.length);
- List<DartType> replacedTypeArguments = <DartType>[];
+ List<DartType> convertedTypeArguments = null;
for (int i = 0; i < type.typeArguments.length; ++i) {
- replacedTypeArguments.add(convertSuperBoundedToRegularBounded(
+ DartType convertedTypeArgument = convertSuperBoundedToRegularBounded(
clientLibrary, typeEnvironment, type.typeArguments[i],
- variance: variance));
+ variance: variance);
+ if (convertedTypeArgument != null) {
+ convertedTypeArguments ??= new List<DartType>.of(type.typeArguments);
+ convertedTypeArguments[i] = convertedTypeArgument;
+ }
}
+ if (convertedTypeArguments == null) return null;
return new InterfaceType(
- type.classNode, type.nullability, replacedTypeArguments);
+ type.classNode, type.nullability, convertedTypeArguments);
} else if (type is TypedefType &&
type.typedefNode.typeParameters.isNotEmpty) {
assert(type.typedefNode.typeParameters.length == type.typeArguments.length);
- List<DartType> replacedTypeArguments = <DartType>[];
+ List<DartType> convertedTypeArguments = null;
for (int i = 0; i < type.typeArguments.length; i++) {
// The implementation of instantiate-to-bound in legacy mode ignored the
// variance of type parameters of the typedef. This behavior is preserved
// here in passing the 'variance' parameter unchanged in for legacy
// libraries.
- replacedTypeArguments.add(convertSuperBoundedToRegularBounded(
+ DartType convertedTypeArgument = convertSuperBoundedToRegularBounded(
clientLibrary, typeEnvironment, type.typeArguments[i],
variance: clientLibrary.isNonNullableByDefault
? Variance.combine(
variance, type.typedefNode.typeParameters[i].variance)
- : variance));
+ : variance);
+ if (convertedTypeArgument != null) {
+ convertedTypeArguments ??= new List<DartType>.of(type.typeArguments);
+ convertedTypeArguments[i] = convertedTypeArgument;
+ }
}
+ if (convertedTypeArguments == null) return null;
return new TypedefType(
- type.typedefNode, type.nullability, replacedTypeArguments);
+ type.typedefNode, type.nullability, convertedTypeArguments);
} else if (type is FunctionType) {
if (clientLibrary.isNonNullableByDefault && type.typedefType != null) {
return convertSuperBoundedToRegularBounded(
clientLibrary, typeEnvironment, type.typedefType,
variance: variance);
}
- DartType replacedReturnType = convertSuperBoundedToRegularBounded(
+
+ DartType convertedReturnType = convertSuperBoundedToRegularBounded(
clientLibrary, typeEnvironment, type.returnType,
variance: variance);
- List<DartType> replacedPositionalParameters = <DartType>[];
+
+ List<DartType> convertedPositionalParameters = null;
for (int i = 0; i < type.positionalParameters.length; i++) {
- replacedPositionalParameters.add(convertSuperBoundedToRegularBounded(
- clientLibrary, typeEnvironment, type.positionalParameters[i],
- variance: Variance.combine(variance, Variance.contravariant)));
+ DartType convertedPositionalParameter =
+ convertSuperBoundedToRegularBounded(
+ clientLibrary, typeEnvironment, type.positionalParameters[i],
+ variance: Variance.combine(variance, Variance.contravariant));
+ if (convertedPositionalParameter != null) {
+ convertedPositionalParameters ??=
+ new List<DartType>.of(type.positionalParameters);
+ convertedPositionalParameters[i] = convertedPositionalParameter;
+ }
}
- List<NamedType> replacedNamedParameters = <NamedType>[];
+
+ List<NamedType> convertedNamedParameters = null;
for (int i = 0; i < type.namedParameters.length; i++) {
- replacedNamedParameters.add(new NamedType(
+ NamedType convertedNamedParameter = new NamedType(
type.namedParameters[i].name,
convertSuperBoundedToRegularBounded(
clientLibrary, typeEnvironment, type.namedParameters[i].type,
- variance: Variance.combine(variance, Variance.contravariant))));
+ variance: Variance.combine(variance, Variance.contravariant)),
+ isRequired: type.namedParameters[i].isRequired);
+ if (convertedNamedParameter != null) {
+ convertedNamedParameters ??=
+ new List<NamedType>.of(type.namedParameters);
+ convertedNamedParameters[i] = convertedNamedParameter;
+ }
}
+
+ List<TypeParameter> convertedTypeParameters = null;
+ if (clientLibrary.isNonNullableByDefault &&
+ type.typeParameters.isNotEmpty) {
+ for (int i = 0; i < type.typeParameters.length; ++i) {
+ DartType convertedBound = convertSuperBoundedToRegularBounded(
+ clientLibrary, typeEnvironment, type.typeParameters[i].bound,
+ variance: Variance.combine(variance, Variance.invariant));
+ if (convertedBound != null) {
+ if (convertedTypeParameters == null) {
+ convertedTypeParameters = <TypeParameter>[];
+ for (TypeParameter parameter in type.typeParameters) {
+ convertedTypeParameters.add(new TypeParameter(parameter.name));
+ }
+ }
+ convertedTypeParameters[i].bound = convertedBound;
+ }
+ }
+
+ Map<TypeParameter, DartType> substitutionMap =
+ <TypeParameter, DartType>{};
+ for (int i = 0; i < type.typeParameters.length; ++i) {
+ substitutionMap[type.typeParameters[i]] =
+ new TypeParameterType.forAlphaRenaming(
+ type.typeParameters[i], convertedTypeParameters[i]);
+ }
+ for (TypeParameter parameter in convertedTypeParameters) {
+ parameter.bound = substitute(parameter.bound, substitutionMap);
+ }
+ List<DartType> defaultTypes = calculateBounds(convertedTypeParameters,
+ typeEnvironment.coreTypes.objectClass, clientLibrary);
+ for (int i = 0; i < convertedTypeParameters.length; ++i) {
+ convertedTypeParameters[i].defaultType = defaultTypes[i];
+ }
+
+ if (convertedReturnType != null) {
+ convertedReturnType = substitute(convertedReturnType, substitutionMap);
+ }
+ if (convertedPositionalParameters != null) {
+ for (int i = 0; i < convertedPositionalParameters.length; ++i) {
+ convertedPositionalParameters[i] =
+ substitute(convertedPositionalParameters[i], substitutionMap);
+ }
+ }
+ if (convertedNamedParameters != null) {
+ for (int i = 0; i < convertedNamedParameters.length; ++i) {
+ convertedNamedParameters[i] = new NamedType(
+ convertedNamedParameters[i].name,
+ substitute(convertedNamedParameters[i].type, substitutionMap),
+ isRequired: convertedNamedParameters[i].isRequired);
+ }
+ }
+ }
+
+ if (convertedReturnType == null &&
+ convertedPositionalParameters == null &&
+ convertedNamedParameters == null &&
+ convertedTypeParameters == null) {
+ return null;
+ }
+
+ convertedReturnType ??= type.returnType;
+ convertedPositionalParameters ??= type.positionalParameters;
+ convertedNamedParameters ??= type.namedParameters;
+ convertedTypeParameters ??= type.typeParameters;
+
return new FunctionType(
- replacedPositionalParameters, replacedReturnType, type.nullability,
- namedParameters: replacedNamedParameters,
+ convertedPositionalParameters, convertedReturnType, type.nullability,
+ namedParameters: convertedNamedParameters,
typeParameters: type.typeParameters,
requiredParameterCount: type.requiredParameterCount,
typedefType: type.typedefType);
} else if (type is FutureOrType) {
- return new FutureOrType(
- convertSuperBoundedToRegularBounded(
- clientLibrary, typeEnvironment, type.typeArgument,
- variance: variance),
- type.declaredNullability);
+ DartType convertedTypeArgument = convertSuperBoundedToRegularBounded(
+ clientLibrary, typeEnvironment, type.typeArgument,
+ variance: variance);
+ if (convertedTypeArgument == null) return null;
+ return new FutureOrType(convertedTypeArgument, type.declaredNullability);
}
- return type;
+
+ return null;
}
int computeVariance(TypeParameter typeParameter, DartType type,
diff --git a/sdk/lib/async/async.dart b/sdk/lib/async/async.dart
index 43e3c1c..0d1539d 100644
--- a/sdk/lib/async/async.dart
+++ b/sdk/lib/async/async.dart
@@ -2,95 +2,93 @@
// 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.
-/**
- * Support for asynchronous programming,
- * with classes such as Future and Stream.
- *
- * Understanding [Future]s and [Stream]s is a prerequisite for
- * writing just about any Dart program.
- *
- * To use this library in your code:
- *
- * import 'dart:async';
- *
- * ## Future
- *
- * A Future object represents a computation whose return value
- * might not yet be available.
- * The Future returns the value of the computation
- * when it completes at some time in the future.
- * Futures are often used for potentially lengthy computations
- * such as I/O and interaction with users.
- *
- * Many methods in the Dart libraries return Futures when
- * performing tasks. For example, when binding an HttpServer
- * to a host and port, the `bind()` method returns a Future.
- *
- * HttpServer.bind('127.0.0.1', 4444)
- * .then((server) => print('${server.isBroadcast}'))
- * .catchError(print);
- *
- * [Future.then] registers a callback function that runs
- * when the Future's operation, in this case the `bind()` method,
- * completes successfully.
- * The value returned by the operation
- * is passed into the callback function.
- * In this example, the `bind()` method returns the HttpServer
- * object. The callback function prints one of its properties.
- * [Future.catchError] registers a callback function that
- * runs if an error occurs within the Future.
- *
- * ## Stream
- *
- * A Stream provides an asynchronous sequence of data.
- * Examples of data sequences include individual events, like mouse clicks,
- * or sequential chunks of larger data, like multiple byte lists with the
- * contents of a file
- * such as mouse clicks, and a stream of byte lists read from a file.
- * The following example opens a file for reading.
- * [Stream.listen] registers a callback function that runs
- * each time more data is available.
- *
- * Stream<List<int>> stream = new File('quotes.txt').openRead();
- * stream.transform(utf8.decoder).listen(print);
- *
- * The stream emits a sequence of a list of bytes.
- * The program must interpret the bytes or handle the raw byte data.
- * Here, the code uses a UTF-8 decoder (provided in the `dart:convert` library)
- * to convert the sequence of bytes into a sequence
- * of Dart strings.
- *
- * Another common use of streams is for user-generated events
- * in a web app: The following code listens for mouse clicks on a button.
- *
- * querySelector('#myButton').onClick.listen((_) => print('Click.'));
- *
- * ## Other resources
- *
- * * The [dart:async section of the library tour][asynchronous-programming]:
- * A brief overview of asynchronous programming.
- *
- * * [Use Future-Based APIs][futures-tutorial]: A closer look at Futures and
- * how to use them to write asynchronous Dart code.
- *
- * * [Futures and Error Handling][futures-error-handling]: Everything you
- * wanted to know about handling errors and exceptions when working with
- * Futures (but were afraid to ask).
- *
- * * [The Event Loop and Dart](https://dart.dev/articles/event-loop/):
- * Learn how Dart handles the event queue and microtask queue, so you can
- * write better asynchronous code with fewer surprises.
- *
- * * [test package: Asynchronous Tests][test-readme]: How to test asynchronous
- * code.
- *
- * [asynchronous-programming]: https://dart.dev/guides/libraries/library-tour#dartasync---asynchronous-programming
- * [futures-tutorial]: https://dart.dev/codelabs/async-await
- * [futures-error-handling]: https://dart.dev/guides/libraries/futures-error-handling
- * [test-readme]: https://pub.dev/packages/test
- *
- * {@category Core}
- */
+/// Support for asynchronous programming,
+/// with classes such as Future and Stream.
+///
+/// Understanding [Future]s and [Stream]s is a prerequisite for
+/// writing just about any Dart program.
+///
+/// To use this library in your code:
+/// ```dart
+/// import 'dart:async';
+/// ```
+/// ## Future
+///
+/// A Future object represents a computation whose return value
+/// might not yet be available.
+/// The Future returns the value of the computation
+/// when it completes at some time in the future.
+/// Futures are often used for potentially lengthy computations
+/// such as I/O and interaction with users.
+///
+/// Many methods in the Dart libraries return Futures when
+/// performing tasks. For example, when binding an HttpServer
+/// to a host and port, the `bind()` method returns a Future.
+/// ```dart
+/// HttpServer.bind('127.0.0.1', 4444)
+/// .then((server) => print('${server.isBroadcast}'))
+/// .catchError(print);
+/// ```
+/// [Future.then] registers a callback function that runs
+/// when the Future's operation, in this case the `bind()` method,
+/// completes successfully.
+/// The value returned by the operation
+/// is passed into the callback function.
+/// In this example, the `bind()` method returns the HttpServer
+/// object. The callback function prints one of its properties.
+/// [Future.catchError] registers a callback function that
+/// runs if an error occurs within the Future.
+///
+/// ## Stream
+///
+/// A Stream provides an asynchronous sequence of data.
+/// Examples of data sequences include individual events, like mouse clicks,
+/// or sequential chunks of larger data, like multiple byte lists with the
+/// contents of a file
+/// such as mouse clicks, and a stream of byte lists read from a file.
+/// The following example opens a file for reading.
+/// [Stream.listen] registers a callback function that runs
+/// each time more data is available.
+/// ```dart
+/// Stream<List<int>> stream = File('quotes.txt').openRead();
+/// stream.transform(utf8.decoder).listen(print);
+/// ```
+/// The stream emits a sequence of a list of bytes.
+/// The program must interpret the bytes or handle the raw byte data.
+/// Here, the code uses a UTF-8 decoder (provided in the `dart:convert` library)
+/// to convert the sequence of bytes into a sequence
+/// of Dart strings.
+///
+/// Another common use of streams is for user-generated events
+/// in a web app: The following code listens for mouse clicks on a button.
+/// ```dart
+/// querySelector('#myButton').onClick.listen((_) => print('Click.'));
+/// ```
+/// ## Other resources
+///
+/// * The [dart:async section of the library tour][asynchronous-programming]:
+/// A brief overview of asynchronous programming.
+///
+/// * [Use Future-Based APIs][futures-tutorial]: A closer look at Futures and
+/// how to use them to write asynchronous Dart code.
+///
+/// * [Futures and Error Handling][futures-error-handling]: Everything you
+/// wanted to know about handling errors and exceptions when working with
+/// Futures (but were afraid to ask).
+///
+/// * [The Event Loop and Dart](https://dart.dev/articles/event-loop/):
+/// Learn how Dart handles the event queue and microtask queue, so you can
+/// write better asynchronous code with fewer surprises.
+///
+/// * [test package: Asynchronous Tests][test-readme]: How to test asynchronous
+/// code.
+///
+/// [asynchronous-programming]: https://dart.dev/guides/libraries/library-tour#dartasync---asynchronous-programming
+/// [futures-tutorial]: https://dart.dev/codelabs/async-await
+/// [futures-error-handling]: https://dart.dev/guides/libraries/futures-error-handling
+/// [test-readme]: https://pub.dev/packages/test
+///
+/// {@category Core}
library dart.async;
import "dart:collection" show HashMap;
diff --git a/sdk/lib/async/async_error.dart b/sdk/lib/async/async_error.dart
index a7172bd..2340a41 100644
--- a/sdk/lib/async/async_error.dart
+++ b/sdk/lib/async/async_error.dart
@@ -4,6 +4,35 @@
part of dart.async;
+/// An error and a stack trace.
+///
+/// Used when an error and stack trace need to be handled as a single
+/// value, for example when returned by [Zone.errorCallback].
+class AsyncError implements Error {
+ final Object error;
+ final StackTrace stackTrace;
+
+ AsyncError(Object error, StackTrace? stackTrace)
+ : error = checkNotNullable(error, "error"),
+ stackTrace = stackTrace ?? defaultStackTrace(error);
+
+ /// A default stack trace for an error.
+ ///
+ /// If [error] is an [Error] and it has an [Error.stackTrace],
+ /// that stack trace is returned.
+ /// If not, the [StackTrace.empty] default stack trace is returned.
+ static StackTrace defaultStackTrace(Object error) {
+ if (error is Error) {
+ var stackTrace = error.stackTrace;
+ if (stackTrace != null) return stackTrace;
+ }
+ return StackTrace.empty;
+ }
+
+ String toString() => '$error';
+}
+
+// Helper function used by stream method implementations.
_invokeErrorHandler(
Function errorHandler, Object error, StackTrace stackTrace) {
var handler = errorHandler; // Rename to avoid promotion.
diff --git a/sdk/lib/async/broadcast_stream_controller.dart b/sdk/lib/async/broadcast_stream_controller.dart
index 4db2a34..c2db270 100644
--- a/sdk/lib/async/broadcast_stream_controller.dart
+++ b/sdk/lib/async/broadcast_stream_controller.dart
@@ -81,19 +81,17 @@
// Extra state used during an [addStream] call.
_AddStreamState<T>? _addStreamState;
- /**
- * Future returned by [close] and [done].
- *
- * The future is completed whenever the done event has been sent to all
- * relevant listeners.
- * The relevant listeners are the ones that were listening when [close] was
- * called. When all of these have been canceled (sending the done event makes
- * them cancel, but they can also be canceled before sending the event),
- * this future completes.
- *
- * Any attempt to listen after calling [close] will throw, so there won't
- * be any further listeners.
- */
+ /// Future returned by [close] and [done].
+ ///
+ /// The future is completed whenever the done event has been sent to all
+ /// relevant listeners.
+ /// The relevant listeners are the ones that were listening when [close] was
+ /// called. When all of these have been canceled (sending the done event makes
+ /// them cancel, but they can also be canceled before sending the event),
+ /// this future completes.
+ ///
+ /// Any attempt to listen after calling [close] will throw, so there won't
+ /// be any further listeners.
_Future<void>? _doneFuture;
_BroadcastStreamController(this.onListen, this.onCancel)
@@ -127,28 +125,24 @@
bool get isClosed => (_state & _STATE_CLOSED) != 0;
- /**
- * A broadcast controller is never paused.
- *
- * Each receiving stream may be paused individually, and they handle their
- * own buffering.
- */
+ /// A broadcast controller is never paused.
+ ///
+ /// Each receiving stream may be paused individually, and they handle their
+ /// own buffering.
bool get isPaused => false;
- /** Whether there are currently one or more subscribers. */
+ /// Whether there are currently one or more subscribers.
bool get hasListener => !_isEmpty;
- /**
- * Test whether the stream has exactly one listener.
- *
- * Assumes that the stream has a listener (not [_isEmpty]).
- */
+ /// Test whether the stream has exactly one listener.
+ ///
+ /// Assumes that the stream has a listener (not [_isEmpty]).
bool get _hasOneListener {
assert(!_isEmpty);
return identical(_firstSubscription, _lastSubscription);
}
- /** Whether an event is being fired (sent to some, but not all, listeners). */
+ /// Whether an event is being fired (sent to some, but not all, listeners).
bool get _isFiring => (_state & _STATE_FIRING) != 0;
bool get _isAddingStream => (_state & _STATE_ADDSTREAM) != 0;
@@ -161,7 +155,7 @@
bool get _isEmpty => _firstSubscription == null;
- /** Adds subscription to linked list of active listeners. */
+ /// Adds subscription to linked list of active listeners.
void _addListener(_BroadcastSubscription<T> subscription) {
assert(identical(subscription._next, subscription));
subscription._eventState = (_state & _STATE_EVENT_ID);
@@ -448,17 +442,15 @@
}
}
-/**
- * Stream controller that is used by [Stream.asBroadcastStream].
- *
- * This stream controller allows incoming events while it is firing
- * other events. This is handled by delaying the events until the
- * current event is done firing, and then fire the pending events.
- *
- * This class extends [_SyncBroadcastStreamController]. Events of
- * an "asBroadcastStream" stream are always initiated by events
- * on another stream, and it is fine to forward them synchronously.
- */
+/// Stream controller that is used by [Stream.asBroadcastStream].
+///
+/// This stream controller allows incoming events while it is firing
+/// other events. This is handled by delaying the events until the
+/// current event is done firing, and then fire the pending events.
+///
+/// This class extends [_SyncBroadcastStreamController]. Events of
+/// an "asBroadcastStream" stream are always initiated by events
+/// on another stream, and it is fine to forward them synchronously.
class _AsBroadcastStreamController<T> extends _SyncBroadcastStreamController<T>
implements _EventDispatch<T> {
_StreamImplEvents<T>? _pending;
diff --git a/sdk/lib/async/deferred_load.dart b/sdk/lib/async/deferred_load.dart
index 419db58..81d9f80 100644
--- a/sdk/lib/async/deferred_load.dart
+++ b/sdk/lib/async/deferred_load.dart
@@ -4,12 +4,12 @@
part of dart.async;
-/**
- * Indicates that loading of [libraryName] is deferred.
- *
- * This class is obsolete. Instead use the syntax:
- * import "library.dart" deferred as prefix;
- */
+/// Indicates that loading of [libraryName] is deferred.
+///
+/// This class is obsolete. Instead use the syntax:
+/// ```dart
+/// import "library.dart" deferred as prefix;
+/// ```
@Deprecated("Dart sdk v. 1.8")
class DeferredLibrary {
final String libraryName;
@@ -17,18 +17,14 @@
const DeferredLibrary(this.libraryName, {this.uri});
- /**
- * Ensure that [libraryName] has been loaded.
- *
- * If the library fails to load, the Future will complete with a
- * DeferredLoadException.
- */
+ /// Ensure that [libraryName] has been loaded.
+ ///
+ /// If the library fails to load, the [Future] will complete with a
+ /// [DeferredLoadException].
external Future<Null> load();
}
-/**
- * Thrown when a deferred library fails to load.
- */
+/// Thrown when a deferred library fails to load.
class DeferredLoadException implements Exception {
DeferredLoadException(String message) : _s = message;
String toString() => "DeferredLoadException: '$_s'";
diff --git a/sdk/lib/async/future.dart b/sdk/lib/async/future.dart
index 0473bc3..317c1fe 100644
--- a/sdk/lib/async/future.dart
+++ b/sdk/lib/async/future.dart
@@ -43,108 +43,106 @@
}
}
-/**
- * An object representing a delayed computation.
- *
- * A [Future] is used to represent a potential value, or error,
- * that will be available at some time in the future.
- * Receivers of a [Future] can register callbacks
- * that handle the value or error once it is available.
- * For example:
- *
- * Future<int> future = getFuture();
- * future.then((value) => handleValue(value))
- * .catchError((error) => handleError(error));
- *
- * A [Future] can be completed in two ways:
- * with a value ("the future succeeds")
- * or with an error ("the future fails").
- * Users can install callbacks for each case.
- *
- * In some cases we say that a future is completed with another future.
- * This is a short way of stating that the future is completed in the same way,
- * with the same value or error,
- * as the other future once that completes.
- * Whenever a function in the core library may complete a future
- * (for example [Completer.complete] or [new Future.value]),
- * then it also accepts another future and does this work for the developer.
- *
- * The result of registering a pair of callbacks is a new Future (the
- * "successor") which in turn is completed with the result of invoking the
- * corresponding callback.
- * The successor is completed with an error if the invoked callback throws.
- * For example:
- * ```
- * Future<int> successor = future.then((int value) {
- * // Invoked when the future is completed with a value.
- * return 42; // The successor is completed with the value 42.
- * },
- * onError: (e) {
- * // Invoked when the future is completed with an error.
- * if (canHandle(e)) {
- * return 499; // The successor is completed with the value 499.
- * } else {
- * throw e; // The successor is completed with the error e.
- * }
- * });
- * ```
- *
- * If a future does not have a successor when it completes with an error,
- * it forwards the error message to the global error-handler.
- * This behavior makes sure that no error is silently dropped.
- * However, it also means that error handlers should be installed early,
- * so that they are present as soon as a future is completed with an error.
- * The following example demonstrates this potential bug:
- * ```
- * var future = getFuture();
- * new Timer(new Duration(milliseconds: 5), () {
- * // The error-handler is not attached until 5 ms after the future has
- * // been received. If the future fails before that, the error is
- * // forwarded to the global error-handler, even though there is code
- * // (just below) to eventually handle the error.
- * future.then((value) { useValue(value); },
- * onError: (e) { handleError(e); });
- * });
- * ```
- *
- * When registering callbacks, it's often more readable to register the two
- * callbacks separately, by first using [then] with one argument
- * (the value handler) and using a second [catchError] for handling errors.
- * Each of these will forward the result that they don't handle
- * to their successors, and together they handle both value and error result.
- * It also has the additional benefit of the [catchError] handling errors in the
- * [then] value callback too.
- * Using sequential handlers instead of parallel ones often leads to code that
- * is easier to reason about.
- * It also makes asynchronous code very similar to synchronous code:
- * ```
- * // Synchronous code.
- * try {
- * int value = foo();
- * return bar(value);
- * } catch (e) {
- * return 499;
- * }
- * ```
- *
- * Equivalent asynchronous code, based on futures:
- * ```
- * Future<int> future = new Future(foo); // Result of foo() as a future.
- * future.then((int value) => bar(value))
- * .catchError((e) => 499);
- * ```
- *
- * Similar to the synchronous code, the error handler (registered with
- * [catchError]) is handling any errors thrown by either `foo` or `bar`.
- * If the error-handler had been registered as the `onError` parameter of
- * the `then` call, it would not catch errors from the `bar` call.
- *
- * Futures can have more than one callback-pair registered. Each successor is
- * treated independently and is handled as if it was the only successor.
- *
- * A future may also fail to ever complete. In that case, no callbacks are
- * called.
- */
+/// An object representing a delayed computation.
+///
+/// A [Future] is used to represent a potential value, or error,
+/// that will be available at some time in the future.
+/// Receivers of a [Future] can register callbacks
+/// that handle the value or error once it is available.
+/// For example:
+/// ```dart
+/// Future<int> future = getFuture();
+/// future.then((value) => handleValue(value))
+/// .catchError((error) => handleError(error));
+/// ```
+/// A [Future] can be completed in two ways:
+/// with a value ("the future succeeds")
+/// or with an error ("the future fails").
+/// Users can install callbacks for each case.
+///
+/// In some cases we say that a future is completed with another future.
+/// This is a short way of stating that the future is completed in the same way,
+/// with the same value or error,
+/// as the other future once that completes.
+/// Whenever a function in the core library may complete a future
+/// (for example [Completer.complete] or [Future.value]),
+/// then it also accepts another future and does this work for the developer.
+///
+/// The result of registering a pair of callbacks is a Future (the
+/// "successor") which in turn is completed with the result of invoking the
+/// corresponding callback.
+/// The successor is completed with an error if the invoked callback throws.
+/// For example:
+/// ```
+/// Future<int> successor = future.then((int value) {
+/// // Invoked when the future is completed with a value.
+/// return 42; // The successor is completed with the value 42.
+/// },
+/// onError: (e) {
+/// // Invoked when the future is completed with an error.
+/// if (canHandle(e)) {
+/// return 499; // The successor is completed with the value 499.
+/// } else {
+/// throw e; // The successor is completed with the error e.
+/// }
+/// });
+/// ```
+///
+/// If a future does not have a successor when it completes with an error,
+/// it forwards the error message to an uncaught-error handler.
+/// This behavior makes sure that no error is silently dropped.
+/// However, it also means that error handlers should be installed early,
+/// so that they are present as soon as a future is completed with an error.
+/// The following example demonstrates this potential bug:
+/// ```
+/// var future = getFuture();
+/// Timer(const Duration(milliseconds: 5), () {
+/// // The error-handler is not attached until 5 ms after the future has
+/// // been received. If the future fails before that, the error is
+/// // forwarded to the global error-handler, even though there is code
+/// // (just below) to eventually handle the error.
+/// future.then((value) { useValue(value); },
+/// onError: (e) { handleError(e); });
+/// });
+/// ```
+///
+/// When registering callbacks, it's often more readable to register the two
+/// callbacks separately, by first using [then] with one argument
+/// (the value handler) and using a second [catchError] for handling errors.
+/// Each of these will forward the result that they don't handle
+/// to their successors, and together they handle both value and error result.
+/// It also has the additional benefit of the [catchError] handling errors in the
+/// [then] value callback too.
+/// Using sequential handlers instead of parallel ones often leads to code that
+/// is easier to reason about.
+/// It also makes asynchronous code very similar to synchronous code:
+/// ```
+/// // Synchronous code.
+/// try {
+/// int value = foo();
+/// return bar(value);
+/// } catch (e) {
+/// return 499;
+/// }
+/// ```
+///
+/// Equivalent asynchronous code, based on futures:
+/// ```
+/// Future<int> future = Future(foo); // Result of foo() as a future.
+/// future.then((int value) => bar(value))
+/// .catchError((e) => 499);
+/// ```
+///
+/// Similar to the synchronous code, the error handler (registered with
+/// [catchError]) is handling any errors thrown by either `foo` or `bar`.
+/// If the error-handler had been registered as the `onError` parameter of
+/// the `then` call, it would not catch errors from the `bar` call.
+///
+/// Futures can have more than one callback-pair registered. Each successor is
+/// treated independently and is handled as if it was the only successor.
+///
+/// A future may also fail to ever complete. In that case, no callbacks are
+/// called.
abstract class Future<T> {
/// A `Future<Null>` completed with `null`.
///
@@ -157,20 +155,18 @@
static final _Future<bool> _falseFuture =
new _Future<bool>.zoneValue(false, _rootZone);
- /**
- * Creates a future containing the result of calling [computation]
- * asynchronously with [Timer.run].
- *
- * If the result of executing [computation] throws, the returned future is
- * completed with the error.
- *
- * If the returned value is itself a [Future], completion of
- * the created future will wait until the returned future completes,
- * and will then complete with the same result.
- *
- * If a non-future value is returned, the returned future is completed
- * with that value.
- */
+ /// Creates a future containing the result of calling [computation]
+ /// asynchronously with [Timer.run].
+ ///
+ /// If the result of executing [computation] throws, the returned future is
+ /// completed with the error.
+ ///
+ /// If the returned value is itself a [Future], completion of
+ /// the created future will wait until the returned future completes,
+ /// and will then complete with the same result.
+ ///
+ /// If a non-future value is returned, the returned future is completed
+ /// with that value.
factory Future(FutureOr<T> computation()) {
_Future<T> result = new _Future<T>();
Timer.run(() {
@@ -183,20 +179,18 @@
return result;
}
- /**
- * Creates a future containing the result of calling [computation]
- * asynchronously with [scheduleMicrotask].
- *
- * If executing [computation] throws,
- * the returned future is completed with the thrown error.
- *
- * If calling [computation] returns a [Future], completion of
- * the created future will wait until the returned future completes,
- * and will then complete with the same result.
- *
- * If calling [computation] returns a non-future value,
- * the returned future is completed with that value.
- */
+ /// Creates a future containing the result of calling [computation]
+ /// asynchronously with [scheduleMicrotask].
+ ///
+ /// If executing [computation] throws,
+ /// the returned future is completed with the thrown error.
+ ///
+ /// If calling [computation] returns a [Future], completion of
+ /// the created future will wait until the returned future completes,
+ /// and will then complete with the same result.
+ ///
+ /// If calling [computation] returns a non-future value,
+ /// the returned future is completed with that value.
factory Future.microtask(FutureOr<T> computation()) {
_Future<T> result = new _Future<T>();
scheduleMicrotask(() {
@@ -209,18 +203,16 @@
return result;
}
- /**
- * Returns a future containing the result of immediately calling
- * [computation].
- *
- * If calling [computation] throws, the returned future is completed with the
- * error.
- *
- * If calling [computation] returns a `Future<T>`, that future is returned.
- *
- * If calling [computation] returns a non-future value,
- * a future is returned which has been completed with that value.
- */
+ /// Returns a future containing the result of immediately calling
+ /// [computation].
+ ///
+ /// If calling [computation] throws, the returned future is completed with the
+ /// error.
+ ///
+ /// If calling [computation] returns a `Future<T>`, that future is returned.
+ ///
+ /// If calling [computation] returns a non-future value,
+ /// a future is returned which has been completed with that value.
factory Future.sync(FutureOr<T> computation()) {
try {
var result = computation();
@@ -242,40 +234,36 @@
}
}
- /**
- * Creates a future completed with [value].
- *
- * If [value] is a future, the created future waits for the
- * [value] future to complete, and then completes with the same result.
- * Since a [value] future can complete with an error, so can the future
- * created by [Future.value], even if the name suggests otherwise.
- *
- * If [value] is not a [Future], the created future is completed
- * with the [value] value,
- * equivalently to `new Future<T>.sync(() => value)`.
- *
- * If [value] is omitted or `null`, it is converted to `FutureOr<T>` by
- * `value as FutureOr<T>`. If `T` is not nullable, then the [value] is
- * required, otherwise the construction throws.
- *
- * Use [Completer] to create a future and complete it later.
- */
+ /// Creates a future completed with [value].
+ ///
+ /// If [value] is a future, the created future waits for the
+ /// [value] future to complete, and then completes with the same result.
+ /// Since a [value] future can complete with an error, so can the future
+ /// created by [Future.value], even if the name suggests otherwise.
+ ///
+ /// If [value] is not a [Future], the created future is completed
+ /// with the [value] value,
+ /// equivalently to `new Future<T>.sync(() => value)`.
+ ///
+ /// If [value] is omitted or `null`, it is converted to `FutureOr<T>` by
+ /// `value as FutureOr<T>`. If `T` is not nullable, then a non-`null` [value]
+ /// must be provided, otherwise the construction throws.
+ ///
+ /// Use [Completer] to create a future now and complete it later.
@pragma("vm:entry-point")
@pragma("vm:prefer-inline")
factory Future.value([FutureOr<T>? value]) {
return new _Future<T>.immediate(value == null ? value as T : value);
}
- /**
- * Creates a future that completes with an error.
- *
- * The created future will be completed with an error in a future microtask.
- * This allows enough time for someone to add an error handler on the future.
- * If an error handler isn't added before the future completes, the error
- * will be considered unhandled.
- *
- * Use [Completer] to create a future and complete it later.
- */
+ /// Creates a future that completes with an error.
+ ///
+ /// The created future will be completed with an error in a future microtask.
+ /// This allows enough time for someone to add an error handler on the future.
+ /// If an error handler isn't added before the future completes, the error
+ /// will be considered unhandled.
+ ///
+ /// Use [Completer] to create a future and complete it later.
factory Future.error(Object error, [StackTrace? stackTrace]) {
// TODO(40614): Remove once non-nullability is sound.
checkNotNullable(error, "error");
@@ -290,31 +278,29 @@
return new _Future<T>.immediateError(error, stackTrace);
}
- /**
- * Creates a future that runs its computation after a delay.
- *
- * The [computation] will be executed after the given [duration] has passed,
- * and the future is completed with the result of the computation.
- *
- * If [computation] returns a future,
- * the future returned by this constructor will complete with the value or
- * error of that future.
- *
- * If the duration is 0 or less,
- * it completes no sooner than in the next event-loop iteration,
- * after all microtasks have run.
- *
- * If [computation] is omitted,
- * it will be treated as if [computation] was `() => null`,
- * and the future will eventually complete with the `null` value.
- * In that case, [T] must be nullable.
- *
- * If calling [computation] throws, the created future will complete with the
- * error.
- *
- * See also [Completer] for a way to create and complete a future at a
- * later time that isn't necessarily after a known fixed duration.
- */
+ /// Creates a future that runs its computation after a delay.
+ ///
+ /// The [computation] will be executed after the given [duration] has passed,
+ /// and the future is completed with the result of the computation.
+ ///
+ /// If [computation] returns a future,
+ /// the future returned by this constructor will complete with the value or
+ /// error of that future.
+ ///
+ /// If the duration is 0 or less,
+ /// it completes no sooner than in the next event-loop iteration,
+ /// after all microtasks have run.
+ ///
+ /// If [computation] is omitted,
+ /// it will be treated as if [computation] was `() => null`,
+ /// and the future will eventually complete with the `null` value.
+ /// In that case, [T] must be nullable.
+ ///
+ /// If calling [computation] throws, the created future will complete with the
+ /// error.
+ ///
+ /// See also [Completer] for a way to create and complete a future at a
+ /// later time that isn't necessarily after a known fixed duration.
factory Future.delayed(Duration duration, [FutureOr<T> computation()?]) {
if (computation == null && !typeAcceptsNull<T>()) {
throw ArgumentError.value(
@@ -335,35 +321,33 @@
return result;
}
- /**
- * Waits for multiple futures to complete and collects their results.
- *
- * Returns a future which will complete once all the provided futures
- * have completed, either with their results, or with an error if any
- * of the provided futures fail.
- *
- * The value of the returned future will be a list of all the values that
- * were produced in the order that the futures are provided by iterating
- * [futures].
- *
- * If any future completes with an error,
- * then the returned future completes with that error.
- * If further futures also complete with errors, those errors are discarded.
- *
- * If `eagerError` is true, the returned future completes with an error
- * immediately on the first error from one of the futures. Otherwise all
- * futures must complete before the returned future is completed (still with
- * the first error; the remaining errors are silently dropped).
- *
- * In the case of an error, [cleanUp] (if provided), is invoked on any
- * non-null result of successful futures.
- * This makes it possible to `cleanUp` resources that would otherwise be
- * lost (since the returned future does not provide access to these values).
- * The [cleanUp] function is unused if there is no error.
- *
- * The call to [cleanUp] should not throw. If it does, the error will be an
- * uncaught asynchronous error.
- */
+ /// Waits for multiple futures to complete and collects their results.
+ ///
+ /// Returns a future which will complete once all the provided futures
+ /// have completed, either with their results, or with an error if any
+ /// of the provided futures fail.
+ ///
+ /// The value of the returned future will be a list of all the values that
+ /// were produced in the order that the futures are provided by iterating
+ /// [futures].
+ ///
+ /// If any future completes with an error,
+ /// then the returned future completes with that error.
+ /// If further futures also complete with errors, those errors are discarded.
+ ///
+ /// If `eagerError` is true, the returned future completes with an error
+ /// immediately on the first error from one of the futures. Otherwise all
+ /// futures must complete before the returned future is completed (still with
+ /// the first error; the remaining errors are silently dropped).
+ ///
+ /// In the case of an error, [cleanUp] (if provided), is invoked on any
+ /// non-null result of successful futures.
+ /// This makes it possible to `cleanUp` resources that would otherwise be
+ /// lost (since the returned future does not provide access to these values).
+ /// The [cleanUp] function is unused if there is no error.
+ ///
+ /// The call to [cleanUp] should not throw. If it does, the error will be an
+ /// uncaught asynchronous error.
@pragma("vm:recognized", "other")
static Future<List<T>> wait<T>(Iterable<Future<T>> futures,
{bool eagerError = false, void cleanUp(T successValue)?}) {
@@ -463,17 +447,15 @@
return _future;
}
- /**
- * Returns the result of the first future in [futures] to complete.
- *
- * The returned future is completed with the result of the first
- * future in [futures] to report that it is complete,
- * whether it's with a value or an error.
- * The results of all the other futures are discarded.
- *
- * If [futures] is empty, or if none of its futures complete,
- * the returned future never completes.
- */
+ /// Returns the result of the first future in [futures] to complete.
+ ///
+ /// The returned future is completed with the result of the first
+ /// future in [futures] to report that it is complete,
+ /// whether it's with a value or an error.
+ /// The results of all the other futures are discarded.
+ ///
+ /// If [futures] is empty, or if none of its futures complete,
+ /// the returned future never completes.
static Future<T> any<T>(Iterable<Future<T>> futures) {
var completer = new Completer<T>.sync();
void onValue(T value) {
@@ -490,24 +472,22 @@
return completer.future;
}
- /**
- * Performs an action for each element of the iterable, in turn.
- *
- * The [action] may be either synchronous or asynchronous.
- *
- * Calls [action] with each element in [elements] in order.
- * If the call to [action] returns a `Future<T>`, the iteration waits
- * until the future is completed before continuing with the next element.
- *
- * Returns a [Future] that completes with `null` when all elements have been
- * processed.
- *
- * Non-[Future] return values, and completion-values of returned [Future]s,
- * are discarded.
- *
- * Any error from [action], synchronous or asynchronous,
- * will stop the iteration and be reported in the returned [Future].
- */
+ /// Performs an action for each element of the iterable, in turn.
+ ///
+ /// The [action] may be either synchronous or asynchronous.
+ ///
+ /// Calls [action] with each element in [elements] in order.
+ /// If the call to [action] returns a `Future<T>`, the iteration waits
+ /// until the future is completed before continuing with the next element.
+ ///
+ /// Returns a [Future] that completes with `null` when all elements have been
+ /// processed.
+ ///
+ /// Non-[Future] return values, and completion-values of returned [Future]s,
+ /// are discarded.
+ ///
+ /// Any error from [action], synchronous or asynchronous,
+ /// will stop the iteration and be reported in the returned [Future].
static Future forEach<T>(Iterable<T> elements, FutureOr action(T element)) {
var iterator = elements.iterator;
return doWhile(() {
@@ -521,28 +501,26 @@
// Constant `true` function, used as callback by [forEach].
static bool _kTrue(Object? _) => true;
- /**
- * Performs an operation repeatedly until it returns `false`.
- *
- * The operation, [action], may be either synchronous or asynchronous.
- *
- * The operation is called repeatedly as long as it returns either the [bool]
- * value `true` or a `Future<bool>` which completes with the value `true`.
- *
- * If a call to [action] returns `false` or a [Future] that completes to
- * `false`, iteration ends and the future returned by [doWhile] is completed
- * with a `null` value.
- *
- * If a call to [action] throws or a future returned by [action] completes
- * with an error, iteration ends and the future returned by [doWhile]
- * completes with the same error.
- *
- * Calls to [action] may happen at any time,
- * including immediately after calling `doWhile`.
- * The only restriction is a new call to [action] won't happen before
- * the previous call has returned, and if it returned a `Future<bool>`, not
- * until that future has completed.
- */
+ /// Performs an operation repeatedly until it returns `false`.
+ ///
+ /// The operation, [action], may be either synchronous or asynchronous.
+ ///
+ /// The operation is called repeatedly as long as it returns either the [bool]
+ /// value `true` or a `Future<bool>` which completes with the value `true`.
+ ///
+ /// If a call to [action] returns `false` or a [Future] that completes to
+ /// `false`, iteration ends and the future returned by [doWhile] is completed
+ /// with a `null` value.
+ ///
+ /// If a call to [action] throws or a future returned by [action] completes
+ /// with an error, iteration ends and the future returned by [doWhile]
+ /// completes with the same error.
+ ///
+ /// Calls to [action] may happen at any time,
+ /// including immediately after calling `doWhile`.
+ /// The only restriction is a new call to [action] won't happen before
+ /// the previous call has returned, and if it returned a `Future<bool>`, not
+ /// until that future has completed.
static Future doWhile(FutureOr<bool> action()) {
_Future<void> doneSignal = new _Future<void>();
late void Function(bool) nextIteration;
@@ -574,88 +552,85 @@
return doneSignal;
}
- /**
- * Register callbacks to be called when this future completes.
- *
- * When this future completes with a value,
- * the [onValue] callback will be called with that value.
- * If this future is already completed, the callback will not be called
- * immediately, but will be scheduled in a later microtask.
- *
- * If [onError] is provided, and this future completes with an error,
- * the `onError` callback is called with that error and its stack trace.
- * The `onError` callback must accept either one argument or two arguments
- * where the latter is a [StackTrace].
- * If `onError` accepts two arguments,
- * it is called with both the error and the stack trace,
- * otherwise it is called with just the error object.
- * The `onError` callback must return a value or future that can be used
- * to complete the returned future, so it must be something assignable to
- * `FutureOr<R>`.
- *
- * Returns a new [Future]
- * which is completed with the result of the call to `onValue`
- * (if this future completes with a value)
- * or to `onError` (if this future completes with an error).
- *
- * If the invoked callback throws,
- * the returned future is completed with the thrown error
- * and a stack trace for the error.
- * In the case of `onError`,
- * if the exception thrown is `identical` to the error argument to `onError`,
- * the throw is considered a rethrow,
- * and the original stack trace is used instead.
- *
- * If the callback returns a [Future],
- * the future returned by `then` will be completed with
- * the same result as the future returned by the callback.
- *
- * If [onError] is not given, and this future completes with an error,
- * the error is forwarded directly to the returned future.
- *
- * In most cases, it is more readable to use [catchError] separately, possibly
- * with a `test` parameter, instead of handling both value and error in a
- * single [then] call.
- *
- * Note that futures don't delay reporting of errors until listeners are
- * added. If the first `then` or `catchError` call happens after this future
- * has completed with an error then the error is reported as unhandled error.
- * See the description on [Future].
- */
+ /// Register callbacks to be called when this future completes.
+ ///
+ /// When this future completes with a value,
+ /// the [onValue] callback will be called with that value.
+ /// If this future is already completed, the callback will not be called
+ /// immediately, but will be scheduled in a later microtask.
+ ///
+ /// If [onError] is provided, and this future completes with an error,
+ /// the `onError` callback is called with that error and its stack trace.
+ /// The `onError` callback must accept either one argument or two arguments
+ /// where the latter is a [StackTrace].
+ /// If `onError` accepts two arguments,
+ /// it is called with both the error and the stack trace,
+ /// otherwise it is called with just the error object.
+ /// The `onError` callback must return a value or future that can be used
+ /// to complete the returned future, so it must be something assignable to
+ /// `FutureOr<R>`.
+ ///
+ /// Returns a new [Future]
+ /// which is completed with the result of the call to `onValue`
+ /// (if this future completes with a value)
+ /// or to `onError` (if this future completes with an error).
+ ///
+ /// If the invoked callback throws,
+ /// the returned future is completed with the thrown error
+ /// and a stack trace for the error.
+ /// In the case of `onError`,
+ /// if the exception thrown is `identical` to the error argument to `onError`,
+ /// the throw is considered a rethrow,
+ /// and the original stack trace is used instead.
+ ///
+ /// If the callback returns a [Future],
+ /// the future returned by `then` will be completed with
+ /// the same result as the future returned by the callback.
+ ///
+ /// If [onError] is not given, and this future completes with an error,
+ /// the error is forwarded directly to the returned future.
+ ///
+ /// In most cases, it is more readable to use [catchError] separately,
+ /// possibly with a `test` parameter,
+ /// instead of handling both value and error in a single [then] call.
+ ///
+ /// Note that futures don't delay reporting of errors until listeners are
+ /// added. If the first `then` or `catchError` call happens
+ /// after this future has completed with an error,
+ /// then the error is reported as unhandled error.
+ /// See the description on [Future].
Future<R> then<R>(FutureOr<R> onValue(T value), {Function? onError});
- /**
- * Handles errors emitted by this [Future].
- *
- * This is the asynchronous equivalent of a "catch" block.
- *
- * Returns a new [Future] that will be completed with either the result of
- * this future or the result of calling the `onError` callback.
- *
- * If this future completes with a value,
- * the returned future completes with the same value.
- *
- * If this future completes with an error,
- * then [test] is first called with the error value.
- *
- * If `test` returns false, the exception is not handled by this `catchError`,
- * and the returned future completes with the same error and stack trace
- * as this future.
- *
- * If `test` returns `true`,
- * [onError] is called with the error and possibly stack trace,
- * and the returned future is completed with the result of this call
- * in exactly the same way as for [then]'s `onError`.
- *
- * If `test` is omitted, it defaults to a function that always returns true.
- * The `test` function should not throw, but if it does, it is handled as
- * if the `onError` function had thrown.
- *
- * Note that futures don't delay reporting of errors until listeners are
- * added. If the first `catchError` (or `then`) call happens after this future
- * has completed with an error then the error is reported as unhandled error.
- * See the description on [Future].
- */
+ /// Handles errors emitted by this [Future].
+ ///
+ /// This is the asynchronous equivalent of a "catch" block.
+ ///
+ /// Returns a new [Future] that will be completed with either the result of
+ /// this future or the result of calling the `onError` callback.
+ ///
+ /// If this future completes with a value,
+ /// the returned future completes with the same value.
+ ///
+ /// If this future completes with an error,
+ /// then [test] is first called with the error value.
+ ///
+ /// If `test` returns false, the exception is not handled by this `catchError`,
+ /// and the returned future completes with the same error and stack trace
+ /// as this future.
+ ///
+ /// If `test` returns `true`,
+ /// [onError] is called with the error and possibly stack trace,
+ /// and the returned future is completed with the result of this call
+ /// in exactly the same way as for [then]'s `onError`.
+ ///
+ /// If `test` is omitted, it defaults to a function that always returns true.
+ /// The `test` function should not throw, but if it does, it is handled as
+ /// if the `onError` function had thrown.
+ ///
+ /// Note that futures don't delay reporting of errors until listeners are
+ /// added. If the first `catchError` (or `then`) call happens after this future
+ /// has completed with an error then the error is reported as unhandled error.
+ /// See the description on [Future].
// The `Function` below stands for one of two types:
// - (dynamic) -> FutureOr<T>
// - (dynamic, StackTrace) -> FutureOr<T>
@@ -663,78 +638,72 @@
// `isCheck` we should also expect functions that take a specific argument.
Future<T> catchError(Function onError, {bool test(Object error)?});
- /**
- * Registers a function to be called when this future completes.
- *
- * The [action] function is called when this future completes, whether it
- * does so with a value or with an error.
- *
- * This is the asynchronous equivalent of a "finally" block.
- *
- * The future returned by this call, `f`, will complete the same way
- * as this future unless an error occurs in the [action] call, or in
- * a [Future] returned by the [action] call. If the call to [action]
- * does not return a future, its return value is ignored.
- *
- * If the call to [action] throws, then `f` is completed with the
- * thrown error.
- *
- * If the call to [action] returns a [Future], `f2`, then completion of
- * `f` is delayed until `f2` completes. If `f2` completes with
- * an error, that will be the result of `f` too. The value of `f2` is always
- * ignored.
- *
- * This method is equivalent to:
- *
- * Future<T> whenComplete(action()) {
- * return this.then((v) {
- * var f2 = action();
- * if (f2 is Future) return f2.then((_) => v);
- * return v
- * }, onError: (e) {
- * var f2 = action();
- * if (f2 is Future) return f2.then((_) { throw e; });
- * throw e;
- * });
- * }
- */
+ /// Registers a function to be called when this future completes.
+ ///
+ /// The [action] function is called when this future completes, whether it
+ /// does so with a value or with an error.
+ ///
+ /// This is the asynchronous equivalent of a "finally" block.
+ ///
+ /// The future returned by this call, `f`, will complete the same way
+ /// as this future unless an error occurs in the [action] call, or in
+ /// a [Future] returned by the [action] call. If the call to [action]
+ /// does not return a future, its return value is ignored.
+ ///
+ /// If the call to [action] throws, then `f` is completed with the
+ /// thrown error.
+ ///
+ /// If the call to [action] returns a [Future], `f2`, then completion of
+ /// `f` is delayed until `f2` completes. If `f2` completes with
+ /// an error, that will be the result of `f` too. The value of `f2` is always
+ /// ignored.
+ ///
+ /// This method is equivalent to:
+ /// ```dart
+ /// Future<T> whenComplete(action()) {
+ /// return this.then((v) {
+ /// var f2 = action();
+ /// if (f2 is Future) return f2.then((_) => v);
+ /// return v
+ /// }, onError: (e) {
+ /// var f2 = action();
+ /// if (f2 is Future) return f2.then((_) { throw e; });
+ /// throw e;
+ /// });
+ /// }
+ /// ```
Future<T> whenComplete(FutureOr<void> action());
- /**
- * Creates a [Stream] containing the result of this future.
- *
- * The stream will produce single data or error event containing the
- * completion result of this future, and then it will close with a
- * done event.
- *
- * If the future never completes, the stream will not produce any events.
- */
+ /// Creates a [Stream] containing the result of this future.
+ ///
+ /// The stream will produce single data or error event containing the
+ /// completion result of this future, and then it will close with a
+ /// done event.
+ ///
+ /// If the future never completes, the stream will not produce any events.
Stream<T> asStream();
- /**
- * Time-out the future computation after [timeLimit] has passed.
- *
- * Returns a new future that completes with the same value as this future,
- * if this future completes in time.
- *
- * If this future does not complete before `timeLimit` has passed,
- * the [onTimeout] action is executed instead, and its result (whether it
- * returns or throws) is used as the result of the returned future.
- * The [onTimeout] function must return a [T] or a `Future<T>`.
- *
- * If `onTimeout` is omitted, a timeout will cause the returned future to
- * complete with a [TimeoutException].
- */
+ /// Time-out the future computation after [timeLimit] has passed.
+ ///
+ /// Returns a new future that completes with the same value as this future,
+ /// if this future completes in time.
+ ///
+ /// If this future does not complete before `timeLimit` has passed,
+ /// the [onTimeout] action is executed instead, and its result (whether it
+ /// returns or throws) is used as the result of the returned future.
+ /// The [onTimeout] function must return a [T] or a `Future<T>`.
+ ///
+ /// If `onTimeout` is omitted, a timeout will cause the returned future to
+ /// complete with a [TimeoutException].
Future<T> timeout(Duration timeLimit, {FutureOr<T> onTimeout()?});
}
-/**
- * Thrown when a scheduled timeout happens while waiting for an async result.
- */
+/// Thrown when a scheduled timeout happens while waiting for an async result.
class TimeoutException implements Exception {
- /** Description of the cause of the timeout. */
+ /// Description of the cause of the timeout.
final String? message;
- /** The duration that was exceeded. */
+
+ /// The duration that was exceeded.
final Duration? duration;
TimeoutException(this.message, [this.duration]);
@@ -747,183 +716,170 @@
}
}
-/**
- * A way to produce Future objects and to complete them later
- * with a value or error.
- *
- * Most of the time, the simplest way to create a future is to just use
- * one of the [Future] constructors to capture the result of a single
- * asynchronous computation:
- * ```
- * new Future(() { doSomething(); return result; });
- * ```
- * or, if the future represents the result of a sequence of asynchronous
- * computations, they can be chained using [Future.then] or similar functions
- * on [Future]:
- * ```
- * Future doStuff(){
- * return someAsyncOperation().then((result) {
- * return someOtherAsyncOperation(result);
- * });
- * }
- * ```
- * If you do need to create a Future from scratch — for example,
- * when you're converting a callback-based API into a Future-based
- * one — you can use a Completer as follows:
- * ```
- * class AsyncOperation {
- * Completer _completer = new Completer();
- *
- * Future<T> doOperation() {
- * _startOperation();
- * return _completer.future; // Send future object back to client.
- * }
- *
- * // Something calls this when the value is ready.
- * void _finishOperation(T result) {
- * _completer.complete(result);
- * }
- *
- * // If something goes wrong, call this.
- * void _errorHappened(error) {
- * _completer.completeError(error);
- * }
- * }
- * ```
- */
+/// A way to produce Future objects and to complete them later
+/// with a value or error.
+///
+/// Most of the time, the simplest way to create a future is to just use
+/// one of the [Future] constructors to capture the result of a single
+/// asynchronous computation:
+/// ```
+/// Future(() { doSomething(); return result; });
+/// ```
+/// or, if the future represents the result of a sequence of asynchronous
+/// computations, they can be chained using [Future.then] or similar functions
+/// on [Future]:
+/// ```
+/// Future doStuff(){
+/// return someAsyncOperation().then((result) {
+/// return someOtherAsyncOperation(result);
+/// });
+/// }
+/// ```
+/// If you do need to create a Future from scratch — for example,
+/// when you're converting a callback-based API into a Future-based
+/// one — you can use a Completer as follows:
+/// ```
+/// class AsyncOperation {
+/// final Completer _completer = new Completer();
+///
+/// Future<T> doOperation() {
+/// _startOperation();
+/// return _completer.future; // Send future object back to client.
+/// }
+///
+/// // Something calls this when the value is ready.
+/// void _finishOperation(T result) {
+/// _completer.complete(result);
+/// }
+///
+/// // If something goes wrong, call this.
+/// void _errorHappened(error) {
+/// _completer.completeError(error);
+/// }
+/// }
+/// ```
abstract class Completer<T> {
- /**
- * Creates a new completer.
- *
- * The general workflow for creating a new future is to 1) create a
- * new completer, 2) hand out its future, and, at a later point, 3) invoke
- * either [complete] or [completeError].
- *
- * The completer completes the future asynchronously. That means that
- * callbacks registered on the future are not called immediately when
- * [complete] or [completeError] is called. Instead the callbacks are
- * delayed until a later microtask.
- *
- * Example:
- * ```
- * var completer = new Completer();
- * handOut(completer.future);
- * later: {
- * completer.complete('completion value');
- * }
- * ```
- */
+ /// Creates a new completer.
+ ///
+ /// The general workflow for creating a new future is to 1) create a
+ /// new completer, 2) hand out its future, and, at a later point, 3) invoke
+ /// either [complete] or [completeError].
+ ///
+ /// The completer completes the future asynchronously. That means that
+ /// callbacks registered on the future are not called immediately when
+ /// [complete] or [completeError] is called. Instead the callbacks are
+ /// delayed until a later microtask.
+ ///
+ /// Example:
+ /// ```
+ /// var completer = new Completer();
+ /// handOut(completer.future);
+ /// later: {
+ /// completer.complete('completion value');
+ /// }
+ /// ```
factory Completer() => new _AsyncCompleter<T>();
- /**
- * Completes the future synchronously.
- *
- * This constructor should be avoided unless the completion of the future is
- * known to be the final result of another asynchronous operation. If in doubt
- * use the default [Completer] constructor.
- *
- * Using an normal, asynchronous, completer will never give the wrong
- * behavior, but using a synchronous completer incorrectly can cause
- * otherwise correct programs to break.
- *
- * A synchronous completer is only intended for optimizing event
- * propagation when one asynchronous event immediately triggers another.
- * It should not be used unless the calls to [complete] and [completeError]
- * are guaranteed to occur in places where it won't break `Future` invariants.
- *
- * Completing synchronously means that the completer's future will be
- * completed immediately when calling the [complete] or [completeError]
- * method on a synchronous completer, which also calls any callbacks
- * registered on that future.
- *
- * Completing synchronously must not break the rule that when you add a
- * callback on a future, that callback must not be called until the code
- * that added the callback has completed.
- * For that reason, a synchronous completion must only occur at the very end
- * (in "tail position") of another synchronous event,
- * because at that point, completing the future immediately is be equivalent
- * to returning to the event loop and completing the future in the next
- * microtask.
- *
- * Example:
- *
- * var completer = new Completer.sync();
- * // The completion is the result of the asynchronous onDone event.
- * // No other operation is performed after the completion. It is safe
- * // to use the Completer.sync constructor.
- * stream.listen(print, onDone: () { completer.complete("done"); });
- *
- * Bad example. Do not use this code. Only for illustrative purposes:
- *
- * var completer = new Completer.sync();
- * completer.future.then((_) { bar(); });
- * // The completion is the result of the asynchronous onDone event.
- * // However, there is still code executed after the completion. This
- * // operation is *not* safe.
- * stream.listen(print, onDone: () {
- * completer.complete("done");
- * foo(); // In this case, foo() runs after bar().
- * });
- */
+ /// Completes the future synchronously.
+ ///
+ /// This constructor should be avoided unless the completion of the future is
+ /// known to be the final result of another asynchronous operation. If in doubt
+ /// use the default [Completer] constructor.
+ ///
+ /// Using an normal, asynchronous, completer will never give the wrong
+ /// behavior, but using a synchronous completer incorrectly can cause
+ /// otherwise correct programs to break.
+ ///
+ /// A synchronous completer is only intended for optimizing event
+ /// propagation when one asynchronous event immediately triggers another.
+ /// It should not be used unless the calls to [complete] and [completeError]
+ /// are guaranteed to occur in places where it won't break `Future` invariants.
+ ///
+ /// Completing synchronously means that the completer's future will be
+ /// completed immediately when calling the [complete] or [completeError]
+ /// method on a synchronous completer, which also calls any callbacks
+ /// registered on that future.
+ ///
+ /// Completing synchronously must not break the rule that when you add a
+ /// callback on a future, that callback must not be called until the code
+ /// that added the callback has completed.
+ /// For that reason, a synchronous completion must only occur at the very end
+ /// (in "tail position") of another synchronous event,
+ /// because at that point, completing the future immediately is be equivalent
+ /// to returning to the event loop and completing the future in the next
+ /// microtask.
+ ///
+ /// Example:
+ /// ```dart
+ /// var completer = Completer.sync();
+ /// // The completion is the result of the asynchronous onDone event.
+ /// // No other operation is performed after the completion. It is safe
+ /// // to use the Completer.sync constructor.
+ /// stream.listen(print, onDone: () { completer.complete("done"); });
+ /// ```
+ /// Bad example. Do not use this code. Only for illustrative purposes:
+ /// ```dart
+ /// var completer = Completer.sync();
+ /// completer.future.then((_) { bar(); });
+ /// // The completion is the result of the asynchronous onDone event.
+ /// // However, there is still code executed after the completion. This
+ /// // operation is *not* safe.
+ /// stream.listen(print, onDone: () {
+ /// completer.complete("done");
+ /// foo(); // In this case, foo() runs after bar().
+ /// });
+ /// ```
factory Completer.sync() => new _SyncCompleter<T>();
- /**
- * The future that is completed by this completer.
- *
- * The future that is completed when [complete] or [completeError] is called.
- */
+ /// The future that is completed by this completer.
+ ///
+ /// The future that is completed when [complete] or [completeError] is called.
Future<T> get future;
- /**
- * Completes [future] with the supplied values.
- *
- * The value must be either a value of type [T]
- * or a future of type `Future<T>`.
- * If the value is omitted or null, and `T` is not nullable, the call
- * to `complete` throws.
- *
- * If the value is itself a future, the completer will wait for that future
- * to complete, and complete with the same result, whether it is a success
- * or an error.
- *
- * Calling [complete] or [completeError] must be done at most once.
- *
- * All listeners on the future are informed about the value.
- */
+ /// Completes [future] with the supplied values.
+ ///
+ /// The value must be either a value of type [T]
+ /// or a future of type `Future<T>`.
+ /// If the value is omitted or `null`, and `T` is not nullable, the call
+ /// to `complete` throws.
+ ///
+ /// If the value is itself a future, the completer will wait for that future
+ /// to complete, and complete with the same result, whether it is a success
+ /// or an error.
+ ///
+ /// Calling [complete] or [completeError] must be done at most once.
+ ///
+ /// All listeners on the future are informed about the value.
void complete([FutureOr<T>? value]);
- /**
- * Complete [future] with an error.
- *
- * Calling [complete] or [completeError] must be done at most once.
- *
- * Completing a future with an error indicates that an exception was thrown
- * while trying to produce a value.
- *
- * If `error` is a `Future`, the future itself is used as the error value.
- * If you want to complete with the result of the future, you can use:
- * ```
- * thisCompleter.complete(theFuture)
- * ```
- * or if you only want to handle an error from the future:
- * ```
- * theFuture.catchError(thisCompleter.completeError);
- * ```
- */
+ /// Complete [future] with an error.
+ ///
+ /// Calling [complete] or [completeError] must be done at most once.
+ ///
+ /// Completing a future with an error indicates that an exception was thrown
+ /// while trying to produce a value.
+ ///
+ /// If `error` is a `Future`, the future itself is used as the error value.
+ /// If you want to complete with the result of the future, you can use:
+ /// ```
+ /// thisCompleter.complete(theFuture)
+ /// ```
+ /// or if you only want to handle an error from the future:
+ /// ```
+ /// theFuture.catchError(thisCompleter.completeError);
+ /// ```
void completeError(Object error, [StackTrace? stackTrace]);
- /**
- * Whether the [future] has been completed.
- *
- * Reflects whether [complete] or [completeError] has been called.
- * A `true` value doesn't necessarily mean that listeners of this future
- * have been invoked yet, either because the completer usually waits until
- * a later microtask to propagate the result, or because [complete]
- * was called with a future that hasn't completed yet.
- *
- * When this value is `true`, [complete] and [completeError] must not be
- * called again.
- */
+ /// Whether the [future] has been completed.
+ ///
+ /// Reflects whether [complete] or [completeError] has been called.
+ /// A `true` value doesn't necessarily mean that listeners of this future
+ /// have been invoked yet, either because the completer usually waits until
+ /// a later microtask to propagate the result, or because [complete]
+ /// was called with a future that hasn't completed yet.
+ ///
+ /// When this value is `true`, [complete] and [completeError] must not be
+ /// called again.
bool get isCompleted;
}
diff --git a/sdk/lib/async/future_impl.dart b/sdk/lib/async/future_impl.dart
index 5b3b77b..df8a347 100644
--- a/sdk/lib/async/future_impl.dart
+++ b/sdk/lib/async/future_impl.dart
@@ -4,11 +4,13 @@
part of dart.async;
-/** The onValue and onError handlers return either a value or a future */
+/// The onValue and onError handlers return either a value or a future
typedef FutureOr<T> _FutureOnValue<S, T>(S value);
-/** Test used by [Future.catchError] to handle skip some errors. */
+
+/// Test used by [Future.catchError] to handle skip some errors.
typedef bool _FutureErrorTest(Object error);
-/** Used by [WhenFuture]. */
+
+/// Used by [WhenFuture].
typedef dynamic _FutureAction();
abstract class _Completer<T> implements Completer<T> {
@@ -202,33 +204,29 @@
/// The future has been completed with an error result.
static const int _stateError = 8;
- /** Whether the future is complete, and as what. */
+ /// Whether the future is complete, and as what.
int _state = _stateIncomplete;
- /**
- * Zone that the future was completed from.
- * This is the zone that an error result belongs to.
- *
- * Until the future is completed, the field may hold the zone that
- * listener callbacks used to create this future should be run in.
- */
+ /// Zone that the future was completed from.
+ /// This is the zone that an error result belongs to.
+ ///
+ /// Until the future is completed, the field may hold the zone that
+ /// listener callbacks used to create this future should be run in.
final _Zone _zone;
- /**
- * Either the result, a list of listeners or another future.
- *
- * The result of the future is either a value or an error.
- * A result is only stored when the future has completed.
- *
- * The listeners is an internally linked list of [_FutureListener]s.
- * Listeners are only remembered while the future is not yet complete,
- * and it is not chained to another future.
- *
- * The future is another future that his future is chained to. This future
- * is waiting for the other future to complete, and when it does, this future
- * will complete with the same result.
- * All listeners are forwarded to the other future.
- */
+ /// Either the result, a list of listeners or another future.
+ ///
+ /// The result of the future is either a value or an error.
+ /// A result is only stored when the future has completed.
+ ///
+ /// The listeners is an internally linked list of [_FutureListener]s.
+ /// Listeners are only remembered while the future is not yet complete,
+ /// and it is not chained to another future.
+ ///
+ /// The future is another future that his future is chained to. This future
+ /// is waiting for the other future to complete, and when it does, this future
+ /// will complete with the same result.
+ /// All listeners are forwarded to the other future.
@pragma("vm:entry-point")
var _resultOrListeners;
@@ -239,7 +237,7 @@
_asyncComplete(result);
}
- /** Creates a future with the value and the specified zone. */
+ /// Creates a future with the value and the specified zone.
_Future.zoneValue(T value, this._zone) {
_setValue(value);
}
@@ -249,7 +247,7 @@
_asyncCompleteError(error, stackTrace);
}
- /** Creates a future that is already completed with the value. */
+ /// Creates a future that is already completed with the value.
_Future.value(T value) : this.zoneValue(value, Zone._current);
bool get _mayComplete => _state == _stateIncomplete;
@@ -606,10 +604,8 @@
});
}
- /**
- * Propagates the value/error of [source] to its [listeners], executing the
- * listeners' callbacks.
- */
+ /// Propagates the value/error of [source] to its [listeners], executing the
+ /// listeners' callbacks.
static void _propagateToListeners(
_Future source, _FutureListener? listeners) {
while (true) {
diff --git a/sdk/lib/async/schedule_microtask.dart b/sdk/lib/async/schedule_microtask.dart
index d01b483..bb6d600 100644
--- a/sdk/lib/async/schedule_microtask.dart
+++ b/sdk/lib/async/schedule_microtask.dart
@@ -12,24 +12,23 @@
_AsyncCallbackEntry(this.callback);
}
-/** Head of single linked list of pending callbacks. */
+/// Head of single linked list of pending callbacks.
_AsyncCallbackEntry? _nextCallback;
-/** Tail of single linked list of pending callbacks. */
+
+/// Tail of single linked list of pending callbacks.
_AsyncCallbackEntry? _lastCallback;
-/**
- * Tail of priority callbacks added by the currently executing callback.
- *
- * Priority callbacks are put at the beginning of the
- * callback queue, so that if one callback schedules more than one
- * priority callback, they are still enqueued in scheduling order.
- */
+
+/// Tail of priority callbacks added by the currently executing callback.
+///
+/// Priority callbacks are put at the beginning of the
+/// callback queue, so that if one callback schedules more than one
+/// priority callback, they are still enqueued in scheduling order.
_AsyncCallbackEntry? _lastPriorityCallback;
-/**
- * Whether we are currently inside the callback loop.
- *
- * If we are inside the loop, we never need to schedule the loop,
- * even if adding a first element.
- */
+
+/// Whether we are currently inside the callback loop.
+///
+/// If we are inside the loop, we never need to schedule the loop,
+/// even if adding a first element.
bool _isInCallbackLoop = false;
void _microtaskLoop() {
@@ -57,12 +56,10 @@
}
}
-/**
- * Schedules a callback to be called as a microtask.
- *
- * The microtask is called after all other currently scheduled
- * microtasks, but as part of the current system event.
- */
+/// Schedules a callback to be called as a microtask.
+///
+/// The microtask is called after all other currently scheduled
+/// microtasks, but as part of the current system event.
void _scheduleAsyncCallback(_AsyncCallback callback) {
_AsyncCallbackEntry newEntry = new _AsyncCallbackEntry(callback);
_AsyncCallbackEntry? lastCallback = _lastCallback;
@@ -77,14 +74,12 @@
}
}
-/**
- * Schedules a callback to be called before all other currently scheduled ones.
- *
- * This callback takes priority over existing scheduled callbacks.
- * It is only used internally to give higher priority to error reporting.
- *
- * Is always run in the root zone.
- */
+/// Schedules a callback to be called before all other currently scheduled ones.
+///
+/// This callback takes priority over existing scheduled callbacks.
+/// It is only used internally to give higher priority to error reporting.
+///
+/// Is always run in the root zone.
void _schedulePriorityAsyncCallback(_AsyncCallback callback) {
if (_nextCallback == null) {
_scheduleAsyncCallback(callback);
@@ -107,32 +102,29 @@
}
}
-/**
- * Runs a function asynchronously.
- *
- * Callbacks registered through this function are always executed in order and
- * are guaranteed to run before other asynchronous events (like [Timer] events,
- * or DOM events).
- *
- * **Warning:** it is possible to starve the DOM by registering asynchronous
- * callbacks through this method. For example the following program runs
- * the callbacks without ever giving the Timer callback a chance to execute:
- *
- * main() {
- * Timer.run(() { print("executed"); }); // Will never be executed.
- * foo() {
- * scheduleMicrotask(foo); // Schedules [foo] in front of other events.
- * }
- * foo();
- * }
- *
- * ## Other resources
- *
- * * [The Event Loop and Dart](https://dart.dev/articles/event-loop/):
- * Learn how Dart handles the event queue and microtask queue, so you can write
- * better asynchronous code with fewer surprises.
- */
-@pragma('vm:entry-point', 'call')
+/// Runs a function asynchronously.
+///
+/// Callbacks registered through this function are always executed in order and
+/// are guaranteed to run before other asynchronous events (like [Timer] events,
+/// or DOM events).
+///
+/// **Warning:** it is possible to starve the DOM by registering asynchronous
+/// callbacks through this method. For example the following program runs
+/// the callbacks without ever giving the Timer callback a chance to execute:
+/// ```dart
+/// main() {
+/// Timer.run(() { print("executed"); }); // Will never be executed.
+/// foo() {
+/// scheduleMicrotask(foo); // Schedules [foo] in front of other events.
+/// }
+/// foo();
+/// }
+/// ```
+/// ## Other resources
+///
+/// * [The Event Loop and Dart](https://dart.dev/articles/event-loop/):
+/// Learn how Dart handles the event queue and microtask queue, so you can write
+/// better asynchronous code with fewer surprises.
void scheduleMicrotask(void Function() callback) {
_Zone currentZone = Zone._current;
if (identical(_rootZone, currentZone)) {
@@ -152,6 +144,6 @@
}
class _AsyncRun {
- /** Schedule the given callback before any other event in the event-loop. */
+ /// Schedule the given callback before any other event in the event-loop.
external static void _scheduleImmediate(void Function() callback);
}
diff --git a/sdk/lib/async/stream.dart b/sdk/lib/async/stream.dart
index 60f4aa1..f779e02 100644
--- a/sdk/lib/async/stream.dart
+++ b/sdk/lib/async/stream.dart
@@ -10,114 +10,106 @@
typedef void _TimerCallback();
-/**
- * A source of asynchronous data events.
- *
- * A Stream provides a way to receive a sequence of events.
- * Each event is either a data event, also called an *element* of the stream,
- * or an error event, which is a notification that something has failed.
- * When a stream has emitted all its event,
- * a single "done" event will notify the listener that the end has been reached.
- *
- * You [listen] on a stream to make it start generating events,
- * and to set up listeners that receive the events.
- * When you listen, you receive a [StreamSubscription] object
- * which is the active object providing the events,
- * and which can be used to stop listening again,
- * or to temporarily pause events from the subscription.
- *
- * There are two kinds of streams: "Single-subscription" streams and
- * "broadcast" streams.
- *
- * *A single-subscription stream* allows only a single listener during the whole
- * lifetime of the stream.
- * It doesn't start generating events until it has a listener,
- * and it stops sending events when the listener is unsubscribed,
- * even if the source of events could still provide more.
- *
- * Listening twice on a single-subscription stream is not allowed, even after
- * the first subscription has been canceled.
- *
- * Single-subscription streams are generally used for streaming chunks of
- * larger contiguous data like file I/O.
- *
- * *A broadcast stream* allows any number of listeners, and it fires
- * its events when they are ready, whether there are listeners or not.
- *
- * Broadcast streams are used for independent events/observers.
- *
- * If several listeners want to listen to a single subscription stream,
- * use [asBroadcastStream] to create a broadcast stream on top of the
- * non-broadcast stream.
- *
- * On either kind of stream, stream transformations, such as [where] and
- * [skip], return the same type of stream as the one the method was called on,
- * unless otherwise noted.
- *
- * When an event is fired, the listener(s) at that time will receive the event.
- * If a listener is added to a broadcast stream while an event is being fired,
- * that listener will not receive the event currently being fired.
- * If a listener is canceled, it immediately stops receiving events.
- * Listening on a broadcast stream can be treated as listening on a new stream
- * containing only the events that have not yet been emitted when the [listen]
- * call occurs.
- * For example, the [first] getter listens to the stream, then returns the first
- * event that listener receives.
- * This is not necessarily the first even emitted by the stream, but the first
- * of the *remaining* events of the broadcast stream.
- *
- * When the "done" event is fired, subscribers are unsubscribed before
- * receiving the event. After the event has been sent, the stream has no
- * subscribers. Adding new subscribers to a broadcast stream after this point
- * is allowed, but they will just receive a new "done" event as soon
- * as possible.
- *
- * Stream subscriptions always respect "pause" requests. If necessary they need
- * to buffer their input, but often, and preferably, they can simply request
- * their input to pause too.
- *
- * The default implementation of [isBroadcast] returns false.
- * A broadcast stream inheriting from [Stream] must override [isBroadcast]
- * to return `true`.
- */
+/// A source of asynchronous data events.
+///
+/// A Stream provides a way to receive a sequence of events.
+/// Each event is either a data event, also called an *element* of the stream,
+/// or an error event, which is a notification that something has failed.
+/// When a stream has emitted all its event,
+/// a single "done" event will notify the listener that the end has been reached.
+///
+/// You [listen] on a stream to make it start generating events,
+/// and to set up listeners that receive the events.
+/// When you listen, you receive a [StreamSubscription] object
+/// which is the active object providing the events,
+/// and which can be used to stop listening again,
+/// or to temporarily pause events from the subscription.
+///
+/// There are two kinds of streams: "Single-subscription" streams and
+/// "broadcast" streams.
+///
+/// *A single-subscription stream* allows only a single listener during the whole
+/// lifetime of the stream.
+/// It doesn't start generating events until it has a listener,
+/// and it stops sending events when the listener is unsubscribed,
+/// even if the source of events could still provide more.
+///
+/// Listening twice on a single-subscription stream is not allowed, even after
+/// the first subscription has been canceled.
+///
+/// Single-subscription streams are generally used for streaming chunks of
+/// larger contiguous data like file I/O.
+///
+/// *A broadcast stream* allows any number of listeners, and it fires
+/// its events when they are ready, whether there are listeners or not.
+///
+/// Broadcast streams are used for independent events/observers.
+///
+/// If several listeners want to listen to a single subscription stream,
+/// use [asBroadcastStream] to create a broadcast stream on top of the
+/// non-broadcast stream.
+///
+/// On either kind of stream, stream transformations, such as [where] and
+/// [skip], return the same type of stream as the one the method was called on,
+/// unless otherwise noted.
+///
+/// When an event is fired, the listener(s) at that time will receive the event.
+/// If a listener is added to a broadcast stream while an event is being fired,
+/// that listener will not receive the event currently being fired.
+/// If a listener is canceled, it immediately stops receiving events.
+/// Listening on a broadcast stream can be treated as listening on a new stream
+/// containing only the events that have not yet been emitted when the [listen]
+/// call occurs.
+/// For example, the [first] getter listens to the stream, then returns the first
+/// event that listener receives.
+/// This is not necessarily the first even emitted by the stream, but the first
+/// of the *remaining* events of the broadcast stream.
+///
+/// When the "done" event is fired, subscribers are unsubscribed before
+/// receiving the event. After the event has been sent, the stream has no
+/// subscribers. Adding new subscribers to a broadcast stream after this point
+/// is allowed, but they will just receive a new "done" event as soon
+/// as possible.
+///
+/// Stream subscriptions always respect "pause" requests. If necessary they need
+/// to buffer their input, but often, and preferably, they can simply request
+/// their input to pause too.
+///
+/// The default implementation of [isBroadcast] returns false.
+/// A broadcast stream inheriting from [Stream] must override [isBroadcast]
+/// to return `true`.
abstract class Stream<T> {
Stream();
- /**
- * Internal use only. We do not want to promise that Stream stays const.
- *
- * If mixins become compatible with const constructors, we may use a
- * stream mixin instead of extending Stream from a const class.
- */
+ /// Internal use only. We do not want to promise that Stream stays const.
+ ///
+ /// If mixins become compatible with const constructors, we may use a
+ /// stream mixin instead of extending Stream from a const class.
const Stream._internal();
- /**
- * Creates an empty broadcast stream.
- *
- * This is a stream which does nothing except sending a done event
- * when it's listened to.
- */
+ /// Creates an empty broadcast stream.
+ ///
+ /// This is a stream which does nothing except sending a done event
+ /// when it's listened to.
const factory Stream.empty() = _EmptyStream<T>;
- /**
- * Creates a stream which emits a single data event before completing.
- *
- * This stream emits a single data event of [value]
- * and then completes with a done event.
- *
- * Example:
- * ```dart
- * Future<void> printThings(Stream<String> data) async {
- * await for (var x in data) {
- * print(x);
- * }
- * }
- * printThings(Stream<String>.value("ok")); // prints "ok".
- * ```
- *
- * The returned stream is effectively equivalent to one created by
- * `(() async* { yield value; } ())` or `Future<T>.value(value).asStream()`.
- */
+ /// Creates a stream which emits a single data event before completing.
+ ///
+ /// This stream emits a single data event of [value]
+ /// and then completes with a done event.
+ ///
+ /// Example:
+ /// ```dart
+ /// Future<void> printThings(Stream<String> data) async {
+ /// await for (var x in data) {
+ /// print(x);
+ /// }
+ /// }
+ /// printThings(Stream<String>.value("ok")); // prints "ok".
+ /// ```
+ ///
+ /// The returned stream is effectively equivalent to one created by
+ /// `(() async* { yield value; } ())` or `Future<T>.value(value).asStream()`.
@Since("2.5")
factory Stream.value(T value) =>
(_AsyncStreamController<T>(null, null, null, null)
@@ -125,30 +117,28 @@
.._closeUnchecked())
.stream;
- /**
- * Creates a stream which emits a single error event before completing.
- *
- * This stream emits a single error event of [error] and [stackTrace]
- * and then completes with a done event.
- *
- * Example:
- * ```dart
- * Future<void> tryThings(Stream<int> data) async {
- * try {
- * await for (var x in data) {
- * print("Data: $x");
- * }
- * } catch (e) {
- * print(e);
- * }
- * }
- * tryThings(Stream<int>.error("Error")); // prints "Error".
- * ```
- * The returned stream is effectively equivalent to one created by
- * `Future<T>.error(error, stackTrace).asStream()`, by or
- * `(() async* { throw error; } ())`, except that you can control the
- * stack trace as well.
- */
+ /// Creates a stream which emits a single error event before completing.
+ ///
+ /// This stream emits a single error event of [error] and [stackTrace]
+ /// and then completes with a done event.
+ ///
+ /// Example:
+ /// ```dart
+ /// Future<void> tryThings(Stream<int> data) async {
+ /// try {
+ /// await for (var x in data) {
+ /// print("Data: $x");
+ /// }
+ /// } catch (e) {
+ /// print(e);
+ /// }
+ /// }
+ /// tryThings(Stream<int>.error("Error")); // prints "Error".
+ /// ```
+ /// The returned stream is effectively equivalent to one created by
+ /// `Future<T>.error(error, stackTrace).asStream()`, by or
+ /// `(() async* { throw error; } ())`, except that you can control the
+ /// stack trace as well.
@Since("2.5")
factory Stream.error(Object error, [StackTrace? stackTrace]) {
// TODO(40614): Remove once non-nullability is sound.
@@ -159,12 +149,10 @@
.stream;
}
- /**
- * Creates a new single-subscription stream from the future.
- *
- * When the future completes, the stream will fire one event, either
- * data or error, and then close with a done-event.
- */
+ /// Creates a new single-subscription stream from the future.
+ ///
+ /// When the future completes, the stream will fire one event, either
+ /// data or error, and then close with a done-event.
factory Stream.fromFuture(Future<T> future) {
// Use the controller's buffering to fill in the value even before
// the stream has a listener. For a single value, it's not worth it
@@ -181,21 +169,19 @@
return controller.stream;
}
- /**
- * Create a stream from a group of futures.
- *
- * The stream reports the results of the futures on the stream in the order
- * in which the futures complete.
- * Each future provides either a data event or an error event,
- * depending on how the future completes.
- *
- * If some futures have already completed when `Stream.fromFutures` is called,
- * their results will be emitted in some unspecified order.
- *
- * When all futures have completed, the stream is closed.
- *
- * If [futures] is empty, the stream closes as soon as possible.
- */
+ /// Create a single-subscription stream from a group of futures.
+ ///
+ /// The stream reports the results of the futures on the stream in the order
+ /// in which the futures complete.
+ /// Each future provides either a data event or an error event,
+ /// depending on how the future completes.
+ ///
+ /// If some futures have already completed when `Stream.fromFutures` is called,
+ /// their results will be emitted in some unspecified order.
+ ///
+ /// When all futures have completed, the stream is closed.
+ ///
+ /// If [futures] is empty, the stream closes as soon as possible.
factory Stream.fromFutures(Iterable<Future<T>> futures) {
_StreamController<T> controller =
new _SyncStreamController<T>(null, null, null, null);
@@ -229,109 +215,103 @@
return controller.stream;
}
- /**
- * Creates a single-subscription stream that gets its data from [elements].
- *
- * The iterable is iterated when the stream receives a listener, and stops
- * iterating if the listener cancels the subscription, or if the
- * [Iterator.moveNext] method returns `false` or throws.
- * Iteration is suspended while the stream subscription is paused.
- *
- * If calling [Iterator.moveNext] on `elements.iterator` throws,
- * the stream emits that error and then it closes.
- * If reading [Iterator.current] on `elements.iterator` throws,
- * the stream emits that error, but keeps iterating.
- */
+ /// Creates a single-subscription stream that gets its data from [elements].
+ ///
+ /// The iterable is iterated when the stream receives a listener, and stops
+ /// iterating if the listener cancels the subscription, or if the
+ /// [Iterator.moveNext] method returns `false` or throws.
+ /// Iteration is suspended while the stream subscription is paused.
+ ///
+ /// If calling [Iterator.moveNext] on `elements.iterator` throws,
+ /// the stream emits that error and then it closes.
+ /// If reading [Iterator.current] on `elements.iterator` throws,
+ /// the stream emits that error, but keeps iterating.
factory Stream.fromIterable(Iterable<T> elements) {
return new _GeneratedStreamImpl<T>(
() => new _IterablePendingEvents<T>(elements));
}
- /**
- * Creates a multi-subscription stream.
- *
- * Each time the created stream is listened to,
- * the [onListen] callback is invoked with a new [MultiStreamController]
- * which forwards events to the [StreamSubscription]
- * returned by that [listen] call.
- *
- * This allows each listener to be treated as an individual stream.
- *
- * The [MultiStreamController] does not support reading its
- * [StreamController.stream]. Setting its [StreamController.onListen]
- * has no effect since the [onListen] callback is called instead,
- * and the [StreamController.onListen] won't be called later.
- * The controller acts like an asynchronous controller,
- * but provides extra methods for delivering events synchronously.
- *
- * If [isBroadcast] is set to `true`, the returned stream's
- * [Stream.isBroadcast] will be `true`.
- * This has no effect on the stream behavior,
- * it is up to the [onListen] function
- * to act like a broadcast stream if it claims to be one.
- *
- * A multi-subscription stream can behave like any other stream.
- * If the [onListen] callback throws on every call after the first,
- * the stream behaves like a single-subscription stream.
- * If the stream emits the same events to all current listeners,
- * it behaves like a broadcast stream.
- *
- * It can also choose to emit different events to different listeners.
- * For example, a stream which repeats the most recent
- * non-`null` event to new listeners, could be implemented as this example:
- * ```dart
- * extension StreamRepeatLatestExtension<T extends Object> on Stream<T> {
- * Stream<T> repeatLatest() {
- * var done = false;
- * T? latest = null;
- * var currentListeners = <MultiStreamController<T>>{};
- * this.listen((event) {
- * latest = event;
- * for (var listener in [...currentListeners]) listener.addSync(event);
- * }, onError: (Object error, StackTrace stack) {
- * for (var listener in [...currentListeners]) listener.addErrorSync(error, stack);
- * }, onDone: () {
- * done = true;
- * latest = null;
- * for (var listener in currentListeners) listener.closeSync();
- * currentListeners.clear();
- * });
- * return Stream.multi((controller) {
- * if (done) {
- * controller.close();
- * return;
- * }
- * currentListeners.add(controller);
- * var latestValue = latest;
- * if (latestValue != null) controller.add(latestValue);
- * controller.onCancel = () {
- * currentListeners.remove(controller);
- * };
- * });
- * }
- * }
- * ```
- */
+ /// Creates a multi-subscription stream.
+ ///
+ /// Each time the created stream is listened to,
+ /// the [onListen] callback is invoked with a new [MultiStreamController]
+ /// which forwards events to the [StreamSubscription]
+ /// returned by that [listen] call.
+ ///
+ /// This allows each listener to be treated as an individual stream.
+ ///
+ /// The [MultiStreamController] does not support reading its
+ /// [StreamController.stream]. Setting its [StreamController.onListen]
+ /// has no effect since the [onListen] callback is called instead,
+ /// and the [StreamController.onListen] won't be called later.
+ /// The controller acts like an asynchronous controller,
+ /// but provides extra methods for delivering events synchronously.
+ ///
+ /// If [isBroadcast] is set to `true`, the returned stream's
+ /// [Stream.isBroadcast] will be `true`.
+ /// This has no effect on the stream behavior,
+ /// it is up to the [onListen] function
+ /// to act like a broadcast stream if it claims to be one.
+ ///
+ /// A multi-subscription stream can behave like any other stream.
+ /// If the [onListen] callback throws on every call after the first,
+ /// the stream behaves like a single-subscription stream.
+ /// If the stream emits the same events to all current listeners,
+ /// it behaves like a broadcast stream.
+ ///
+ /// It can also choose to emit different events to different listeners.
+ /// For example, a stream which repeats the most recent
+ /// non-`null` event to new listeners, could be implemented as this example:
+ /// ```dart
+ /// extension StreamRepeatLatestExtension<T extends Object> on Stream<T> {
+ /// Stream<T> repeatLatest() {
+ /// var done = false;
+ /// T? latest = null;
+ /// var currentListeners = <MultiStreamController<T>>{};
+ /// this.listen((event) {
+ /// latest = event;
+ /// for (var listener in [...currentListeners]) listener.addSync(event);
+ /// }, onError: (Object error, StackTrace stack) {
+ /// for (var listener in [...currentListeners]) listener.addErrorSync(error, stack);
+ /// }, onDone: () {
+ /// done = true;
+ /// latest = null;
+ /// for (var listener in currentListeners) listener.closeSync();
+ /// currentListeners.clear();
+ /// });
+ /// return Stream.multi((controller) {
+ /// if (done) {
+ /// controller.close();
+ /// return;
+ /// }
+ /// currentListeners.add(controller);
+ /// var latestValue = latest;
+ /// if (latestValue != null) controller.add(latestValue);
+ /// controller.onCancel = () {
+ /// currentListeners.remove(controller);
+ /// };
+ /// });
+ /// }
+ /// }
+ /// ```
@Since("2.9")
factory Stream.multi(void Function(MultiStreamController<T>) onListen,
{bool isBroadcast = false}) {
return _MultiStream<T>(onListen, isBroadcast);
}
- /**
- * Creates a stream that repeatedly emits events at [period] intervals.
- *
- * The event values are computed by invoking [computation]. The argument to
- * this callback is an integer that starts with 0 and is incremented for
- * every event.
- *
- * The [period] must a non-negative [Duration].
- *
- * If [computation] is omitted the event values will all be `null`.
- *
- * The [computation] must not be omitted if the event type [T] does not
- * allow `null` as a value.
- */
+ /// Creates a stream that repeatedly emits events at [period] intervals.
+ ///
+ /// The event values are computed by invoking [computation]. The argument to
+ /// this callback is an integer that starts with 0 and is incremented for
+ /// every event.
+ ///
+ /// The [period] must a non-negative [Duration].
+ ///
+ /// If [computation] is omitted the event values will all be `null`.
+ ///
+ /// The [computation] must not be omitted if the event type [T] does not
+ /// allow `null` as a value.
factory Stream.periodic(Duration period,
[T computation(int computationCount)?]) {
if (computation == null && !typeAcceptsNull<T>()) {
@@ -381,191 +361,175 @@
return controller.stream;
}
- /**
- * Creates a stream where all events of an existing stream are piped through
- * a sink-transformation.
- *
- * The given [mapSink] closure is invoked when the returned stream is
- * listened to. All events from the [source] are added into the event sink
- * that is returned from the invocation. The transformation puts all
- * transformed events into the sink the [mapSink] closure received during
- * its invocation. Conceptually the [mapSink] creates a transformation pipe
- * with the input sink being the returned [EventSink] and the output sink
- * being the sink it received.
- *
- * This constructor is frequently used to build transformers.
- *
- * Example use for a duplicating transformer:
- *
- * class DuplicationSink implements EventSink<String> {
- * final EventSink<String> _outputSink;
- * DuplicationSink(this._outputSink);
- *
- * void add(String data) {
- * _outputSink.add(data);
- * _outputSink.add(data);
- * }
- *
- * void addError(e, [st]) { _outputSink.addError(e, st); }
- * void close() { _outputSink.close(); }
- * }
- *
- * class DuplicationTransformer extends StreamTransformerBase<String, String> {
- * // Some generic types omitted for brevity.
- * Stream bind(Stream stream) => new Stream<String>.eventTransformed(
- * stream,
- * (EventSink sink) => new DuplicationSink(sink));
- * }
- *
- * stringStream.transform(new DuplicationTransformer());
- *
- * The resulting stream is a broadcast stream if [source] is.
- */
+ /// Creates a stream where all events of an existing stream are piped through
+ /// a sink-transformation.
+ ///
+ /// The given [mapSink] closure is invoked when the returned stream is
+ /// listened to. All events from the [source] are added into the event sink
+ /// that is returned from the invocation. The transformation puts all
+ /// transformed events into the sink the [mapSink] closure received during
+ /// its invocation. Conceptually the [mapSink] creates a transformation pipe
+ /// with the input sink being the returned [EventSink] and the output sink
+ /// being the sink it received.
+ ///
+ /// This constructor is frequently used to build transformers.
+ ///
+ /// Example use for a duplicating transformer:
+ /// ```dart
+ /// class DuplicationSink implements EventSink<String> {
+ /// final EventSink<String> _outputSink;
+ /// DuplicationSink(this._outputSink);
+ ///
+ /// void add(String data) {
+ /// _outputSink.add(data);
+ /// _outputSink.add(data);
+ /// }
+ ///
+ /// void addError(e, [st]) { _outputSink.addError(e, st); }
+ /// void close() { _outputSink.close(); }
+ /// }
+ ///
+ /// class DuplicationTransformer extends StreamTransformerBase<String, String> {
+ /// // Some generic types omitted for brevity.
+ /// Stream bind(Stream stream) => Stream<String>.eventTransformed(
+ /// stream,
+ /// (EventSink sink) => DuplicationSink(sink));
+ /// }
+ ///
+ /// stringStream.transform(DuplicationTransformer());
+ /// ```
+ /// The resulting stream is a broadcast stream if [source] is.
factory Stream.eventTransformed(
Stream<dynamic> source, EventSink<dynamic> mapSink(EventSink<T> sink)) {
return new _BoundSinkStream(source, mapSink);
}
- /**
- * Adapts [source] to be a `Stream<T>`.
- *
- * This allows [source] to be used at the new type, but at run-time it
- * must satisfy the requirements of both the new type and its original type.
- *
- * Data events created by the source stream must also be instances of [T].
- */
+ /// Adapts [source] to be a `Stream<T>`.
+ ///
+ /// This allows [source] to be used at the new type, but at run-time it
+ /// must satisfy the requirements of both the new type and its original type.
+ ///
+ /// Data events created by the source stream must also be instances of [T].
static Stream<T> castFrom<S, T>(Stream<S> source) =>
new CastStream<S, T>(source);
- /**
- * Whether this stream is a broadcast stream.
- */
+ /// Whether this stream is a broadcast stream.
bool get isBroadcast => false;
- /**
- * Returns a multi-subscription stream that produces the same events as this.
- *
- * The returned stream will subscribe to this stream when its first
- * subscriber is added, and will stay subscribed until this stream ends,
- * or a callback cancels the subscription.
- *
- * If [onListen] is provided, it is called with a subscription-like object
- * that represents the underlying subscription to this stream. It is
- * possible to pause, resume or cancel the subscription during the call
- * to [onListen]. It is not possible to change the event handlers, including
- * using [StreamSubscription.asFuture].
- *
- * If [onCancel] is provided, it is called in a similar way to [onListen]
- * when the returned stream stops having listener. If it later gets
- * a new listener, the [onListen] function is called again.
- *
- * Use the callbacks, for example, for pausing the underlying subscription
- * while having no subscribers to prevent losing events, or canceling the
- * subscription when there are no listeners.
- */
+ /// Returns a multi-subscription stream that produces the same events as this.
+ ///
+ /// The returned stream will subscribe to this stream when its first
+ /// subscriber is added, and will stay subscribed until this stream ends,
+ /// or a callback cancels the subscription.
+ ///
+ /// If [onListen] is provided, it is called with a subscription-like object
+ /// that represents the underlying subscription to this stream. It is
+ /// possible to pause, resume or cancel the subscription during the call
+ /// to [onListen]. It is not possible to change the event handlers, including
+ /// using [StreamSubscription.asFuture].
+ ///
+ /// If [onCancel] is provided, it is called in a similar way to [onListen]
+ /// when the returned stream stops having listener. If it later gets
+ /// a new listener, the [onListen] function is called again.
+ ///
+ /// Use the callbacks, for example, for pausing the underlying subscription
+ /// while having no subscribers to prevent losing events, or canceling the
+ /// subscription when there are no listeners.
Stream<T> asBroadcastStream(
{void onListen(StreamSubscription<T> subscription)?,
void onCancel(StreamSubscription<T> subscription)?}) {
return new _AsBroadcastStream<T>(this, onListen, onCancel);
}
- /**
- * Adds a subscription to this stream.
- *
- * Returns a [StreamSubscription] which handles events from this stream using
- * the provided [onData], [onError] and [onDone] handlers.
- * The handlers can be changed on the subscription, but they start out
- * as the provided functions.
- *
- * On each data event from this stream, the subscriber's [onData] handler
- * is called. If [onData] is `null`, nothing happens.
- *
- * On errors from this stream, the [onError] handler is called with the
- * error object and possibly a stack trace.
- *
- * The [onError] callback must be of type `void Function(Object error)` or
- * `void Function(Object error, StackTrace)`.
- * The function type determines whether [onError] is invoked with a stack
- * trace argument.
- * The stack trace argument may be [StackTrace.empty] if this stream received
- * an error without a stack trace.
- *
- * Otherwise it is called with just the error object.
- * If [onError] is omitted, any errors on this stream are considered unhandled,
- * and will be passed to the current [Zone]'s error handler.
- * By default unhandled async errors are treated
- * as if they were uncaught top-level errors.
- *
- * If this stream closes and sends a done event, the [onDone] handler is
- * called. If [onDone] is `null`, nothing happens.
- *
- * If [cancelOnError] is true, the subscription is automatically canceled
- * when the first error event is delivered. The default is `false`.
- *
- * While a subscription is paused, or when it has been canceled,
- * the subscription doesn't receive events and none of the
- * event handler functions are called.
- */
+ /// Adds a subscription to this stream.
+ ///
+ /// Returns a [StreamSubscription] which handles events from this stream using
+ /// the provided [onData], [onError] and [onDone] handlers.
+ /// The handlers can be changed on the subscription, but they start out
+ /// as the provided functions.
+ ///
+ /// On each data event from this stream, the subscriber's [onData] handler
+ /// is called. If [onData] is `null`, nothing happens.
+ ///
+ /// On errors from this stream, the [onError] handler is called with the
+ /// error object and possibly a stack trace.
+ ///
+ /// The [onError] callback must be of type `void Function(Object error)` or
+ /// `void Function(Object error, StackTrace)`.
+ /// The function type determines whether [onError] is invoked with a stack
+ /// trace argument.
+ /// The stack trace argument may be [StackTrace.empty] if this stream received
+ /// an error without a stack trace.
+ ///
+ /// Otherwise it is called with just the error object.
+ /// If [onError] is omitted, any errors on this stream are considered unhandled,
+ /// and will be passed to the current [Zone]'s error handler.
+ /// By default unhandled async errors are treated
+ /// as if they were uncaught top-level errors.
+ ///
+ /// If this stream closes and sends a done event, the [onDone] handler is
+ /// called. If [onDone] is `null`, nothing happens.
+ ///
+ /// If [cancelOnError] is `true`, the subscription is automatically canceled
+ /// when the first error event is delivered. The default is `false`.
+ ///
+ /// While a subscription is paused, or when it has been canceled,
+ /// the subscription doesn't receive events and none of the
+ /// event handler functions are called.
StreamSubscription<T> listen(void onData(T event)?,
{Function? onError, void onDone()?, bool? cancelOnError});
- /**
- * Creates a new stream from this stream that discards some elements.
- *
- * The new stream sends the same error and done events as this stream,
- * but it only sends the data events that satisfy the [test].
- *
- * If the [test] function throws, the data event is dropped and the
- * error is emitted on the returned stream instead.
- *
- * The returned stream is a broadcast stream if this stream is.
- * If a broadcast stream is listened to more than once, each subscription
- * will individually perform the `test`.
- */
+ /// Creates a new stream from this stream that discards some elements.
+ ///
+ /// The new stream sends the same error and done events as this stream,
+ /// but it only sends the data events that satisfy the [test].
+ ///
+ /// If the [test] function throws, the data event is dropped and the
+ /// error is emitted on the returned stream instead.
+ ///
+ /// The returned stream is a broadcast stream if this stream is.
+ /// If a broadcast stream is listened to more than once, each subscription
+ /// will individually perform the `test`.
Stream<T> where(bool test(T event)) {
return new _WhereStream<T>(this, test);
}
- /**
- * Transforms each element of this stream into a new stream event.
- *
- * Creates a new stream that converts each element of this stream
- * to a new value using the [convert] function, and emits the result.
- *
- * For each data event, `o`, in this stream, the returned stream
- * provides a data event with the value `convert(o)`.
- * If [convert] throws, the returned stream reports it as an error
- * event instead.
- *
- * Error and done events are passed through unchanged to the returned stream.
- *
- * The returned stream is a broadcast stream if this stream is.
- * The [convert] function is called once per data event per listener.
- * If a broadcast stream is listened to more than once, each subscription
- * will individually call [convert] on each data event.
- *
- * Unlike [transform], this method does not treat the stream as
- * chunks of a single value. Instead each event is converted independently
- * of the previous and following events, which may not always be correct.
- * For example, UTF-8 encoding, or decoding, will give wrong results
- * if a surrogate pair, or a multibyte UTF-8 encoding, is split into
- * separate events, and those events are attempted encoded or decoded
- * independently.
- */
+ /// Transforms each element of this stream into a new stream event.
+ ///
+ /// Creates a new stream that converts each element of this stream
+ /// to a new value using the [convert] function, and emits the result.
+ ///
+ /// For each data event, `o`, in this stream, the returned stream
+ /// provides a data event with the value `convert(o)`.
+ /// If [convert] throws, the returned stream reports it as an error
+ /// event instead.
+ ///
+ /// Error and done events are passed through unchanged to the returned stream.
+ ///
+ /// The returned stream is a broadcast stream if this stream is.
+ /// The [convert] function is called once per data event per listener.
+ /// If a broadcast stream is listened to more than once, each subscription
+ /// will individually call [convert] on each data event.
+ ///
+ /// Unlike [transform], this method does not treat the stream as
+ /// chunks of a single value. Instead each event is converted independently
+ /// of the previous and following events, which may not always be correct.
+ /// For example, UTF-8 encoding, or decoding, will give wrong results
+ /// if a surrogate pair, or a multibyte UTF-8 encoding, is split into
+ /// separate events, and those events are attempted encoded or decoded
+ /// independently.
Stream<S> map<S>(S convert(T event)) {
return new _MapStream<T, S>(this, convert);
}
- /**
- * Creates a new stream with each data event of this stream asynchronously
- * mapped to a new event.
- *
- * This acts like [map], except that [convert] may return a [Future],
- * and in that case, this stream waits for that future to complete before
- * continuing with its result.
- *
- * The returned stream is a broadcast stream if this stream is.
- */
+ /// Creates a new stream with each data event of this stream asynchronously
+ /// mapped to a new event.
+ ///
+ /// This acts like [map], except that [convert] may return a [Future],
+ /// and in that case, this stream waits for that future to complete before
+ /// continuing with its result.
+ ///
+ /// The returned stream is a broadcast stream if this stream is.
Stream<E> asyncMap<E>(FutureOr<E> convert(T event)) {
_StreamControllerBase<E> controller;
if (isBroadcast) {
@@ -610,24 +574,22 @@
return controller.stream;
}
- /**
- * Transforms each element into a sequence of asynchronous events.
- *
- * Returns a new stream and for each event of this stream, do the following:
- *
- * * If the event is an error event or a done event, it is emitted directly
- * by the returned stream.
- * * Otherwise it is an element. Then the [convert] function is called
- * with the element as argument to produce a convert-stream for the element.
- * * If that call throws, the error is emitted on the returned stream.
- * * If the call returns `null`, no further action is taken for the elements.
- * * Otherwise, this stream is paused and convert-stream is listened to.
- * Every data and error event of the convert-stream is emitted on the returned
- * stream in the order it is produced.
- * When the convert-stream ends, this stream is resumed.
- *
- * The returned stream is a broadcast stream if this stream is.
- */
+ /// Transforms each element into a sequence of asynchronous events.
+ ///
+ /// Returns a new stream and for each event of this stream, do the following:
+ ///
+ /// * If the event is an error event or a done event, it is emitted directly
+ /// by the returned stream.
+ /// * Otherwise it is an element. Then the [convert] function is called
+ /// with the element as argument to produce a convert-stream for the element.
+ /// * If that call throws, the error is emitted on the returned stream.
+ /// * If the call returns `null`, no further action is taken for the elements.
+ /// * Otherwise, this stream is paused and convert-stream is listened to.
+ /// Every data and error event of the convert-stream is emitted on the returned
+ /// stream in the order it is produced.
+ /// When the convert-stream ends, this stream is resumed.
+ ///
+ /// The returned stream is a broadcast stream if this stream is.
Stream<E> asyncExpand<E>(Stream<E>? convert(T event)) {
_StreamControllerBase<E> controller;
if (isBroadcast) {
@@ -663,135 +625,125 @@
return controller.stream;
}
- /**
- * Creates a wrapper Stream that intercepts some errors from this stream.
- *
- * If this stream sends an error that matches [test], then it is intercepted
- * by the [onError] function.
- *
- * The [onError] callback must be of type `void Function(Object error)` or
- * `void Function(Object error, StackTrace)`.
- * The function type determines whether [onError] is invoked with a stack
- * trace argument.
- * The stack trace argument may be [StackTrace.empty] if this stream received
- * an error without a stack trace.
- *
- * An asynchronous error `error` is matched by a test function if
- *`test(error)` returns true. If [test] is omitted, every error is considered
- * matching.
- *
- * If the error is intercepted, the [onError] function can decide what to do
- * with it. It can throw if it wants to raise a new (or the same) error,
- * or simply return to make this stream forget the error.
- * If the received `error` value is thrown again by the [onError] function,
- * it acts like a `rethrow` and it is emitted along with its original
- * stack trace, not the stack trace of the `throw` inside [onError].
- *
- * If you need to transform an error into a data event, use the more generic
- * [Stream.transform] to handle the event by writing a data event to
- * the output sink.
- *
- * The returned stream is a broadcast stream if this stream is.
- * If a broadcast stream is listened to more than once, each subscription
- * will individually perform the `test` and handle the error.
- */
+ /// Creates a wrapper Stream that intercepts some errors from this stream.
+ ///
+ /// If this stream sends an error that matches [test], then it is intercepted
+ /// by the [onError] function.
+ ///
+ /// The [onError] callback must be of type `void Function(Object error)` or
+ /// `void Function(Object error, StackTrace)`.
+ /// The function type determines whether [onError] is invoked with a stack
+ /// trace argument.
+ /// The stack trace argument may be [StackTrace.empty] if this stream received
+ /// an error without a stack trace.
+ ///
+ /// An asynchronous error `error` is matched by a test function if
+ ///`test(error)` returns true. If [test] is omitted, every error is considered
+ /// matching.
+ ///
+ /// If the error is intercepted, the [onError] function can decide what to do
+ /// with it. It can throw if it wants to raise a new (or the same) error,
+ /// or simply return to make this stream forget the error.
+ /// If the received `error` value is thrown again by the [onError] function,
+ /// it acts like a `rethrow` and it is emitted along with its original
+ /// stack trace, not the stack trace of the `throw` inside [onError].
+ ///
+ /// If you need to transform an error into a data event, use the more generic
+ /// [Stream.transform] to handle the event by writing a data event to
+ /// the output sink.
+ ///
+ /// The returned stream is a broadcast stream if this stream is.
+ /// If a broadcast stream is listened to more than once, each subscription
+ /// will individually perform the `test` and handle the error.
Stream<T> handleError(Function onError, {bool test(error)?}) {
return new _HandleErrorStream<T>(this, onError, test);
}
- /**
- * Transforms each element of this stream into a sequence of elements.
- *
- * Returns a new stream where each element of this stream is replaced
- * by zero or more data events.
- * The event values are provided as an [Iterable] by a call to [convert]
- * with the element as argument, and the elements of that iterable is
- * emitted in iteration order.
- * If calling [convert] throws, or if the iteration of the returned values
- * throws, the error is emitted on the returned stream and iteration ends
- * for that element of this stream.
- *
- * Error events and the done event of this stream are forwarded directly
- * to the returned stream.
- *
- * The returned stream is a broadcast stream if this stream is.
- * If a broadcast stream is listened to more than once, each subscription
- * will individually call `convert` and expand the events.
- */
+ /// Transforms each element of this stream into a sequence of elements.
+ ///
+ /// Returns a new stream where each element of this stream is replaced
+ /// by zero or more data events.
+ /// The event values are provided as an [Iterable] by a call to [convert]
+ /// with the element as argument, and the elements of that iterable is
+ /// emitted in iteration order.
+ /// If calling [convert] throws, or if the iteration of the returned values
+ /// throws, the error is emitted on the returned stream and iteration ends
+ /// for that element of this stream.
+ ///
+ /// Error events and the done event of this stream are forwarded directly
+ /// to the returned stream.
+ ///
+ /// The returned stream is a broadcast stream if this stream is.
+ /// If a broadcast stream is listened to more than once, each subscription
+ /// will individually call `convert` and expand the events.
Stream<S> expand<S>(Iterable<S> convert(T element)) {
return new _ExpandStream<T, S>(this, convert);
}
- /**
- * Pipes the events of this stream into [streamConsumer].
- *
- * All events of this stream are added to `streamConsumer` using
- * [StreamConsumer.addStream].
- * The `streamConsumer` is closed when this stream has been successfully added
- * to it - when the future returned by `addStream` completes without an error.
- *
- * Returns a future which completes when this stream has been consumed
- * and the consumer has been closed.
- *
- * The returned future completes with the same result as the future returned
- * by [StreamConsumer.close].
- * If the call to [StreamConsumer.addStream] fails in some way, this
- * method fails in the same way.
- */
+ /// Pipes the events of this stream into [streamConsumer].
+ ///
+ /// All events of this stream are added to `streamConsumer` using
+ /// [StreamConsumer.addStream].
+ /// The `streamConsumer` is closed when this stream has been successfully added
+ /// to it - when the future returned by `addStream` completes without an error.
+ ///
+ /// Returns a future which completes when this stream has been consumed
+ /// and the consumer has been closed.
+ ///
+ /// The returned future completes with the same result as the future returned
+ /// by [StreamConsumer.close].
+ /// If the call to [StreamConsumer.addStream] fails in some way, this
+ /// method fails in the same way.
Future pipe(StreamConsumer<T> streamConsumer) {
return streamConsumer.addStream(this).then((_) => streamConsumer.close());
}
- /**
- * Applies [streamTransformer] to this stream.
- *
- * Returns the transformed stream,
- * that is, the result of `streamTransformer.bind(this)`.
- * This method simply allows writing the call to `streamTransformer.bind`
- * in a chained fashion, like
- * ```
- * stream.map(mapping).transform(transformation).toList()
- * ```
- * which can be more convenient than calling `bind` directly.
- *
- * The [streamTransformer] can return any stream.
- * Whether the returned stream is a broadcast stream or not,
- * and which elements it will contain,
- * is entirely up to the transformation.
- *
- * This method should always be used for transformations which treat
- * the entire stream as representing a single value
- * which has perhaps been split into several parts for transport,
- * like a file being read from disk or being fetched over a network.
- * The transformation will then produce a new stream which
- * transforms the stream's value incrementally (perhaps using
- * [Converter.startChunkedConversion]). The resulting stream
- * may again be chunks of the result, but does not have to
- * correspond to specific events from the source string.
- */
+ /// Applies [streamTransformer] to this stream.
+ ///
+ /// Returns the transformed stream,
+ /// that is, the result of `streamTransformer.bind(this)`.
+ /// This method simply allows writing the call to `streamTransformer.bind`
+ /// in a chained fashion, like
+ /// ```
+ /// stream.map(mapping).transform(transformation).toList()
+ /// ```
+ /// which can be more convenient than calling `bind` directly.
+ ///
+ /// The [streamTransformer] can return any stream.
+ /// Whether the returned stream is a broadcast stream or not,
+ /// and which elements it will contain,
+ /// is entirely up to the transformation.
+ ///
+ /// This method should always be used for transformations which treat
+ /// the entire stream as representing a single value
+ /// which has perhaps been split into several parts for transport,
+ /// like a file being read from disk or being fetched over a network.
+ /// The transformation will then produce a new stream which
+ /// transforms the stream's value incrementally (perhaps using
+ /// [Converter.startChunkedConversion]). The resulting stream
+ /// may again be chunks of the result, but does not have to
+ /// correspond to specific events from the source string.
Stream<S> transform<S>(StreamTransformer<T, S> streamTransformer) {
return streamTransformer.bind(this);
}
- /**
- * Combines a sequence of values by repeatedly applying [combine].
- *
- * Similar to [Iterable.reduce], this function maintains a value,
- * starting with the first element of this stream
- * and updated for each further element of this stream.
- * For each element after the first,
- * the value is updated to the result of calling [combine]
- * with the previous value and the element.
- *
- * When this stream is done, the returned future is completed with
- * the value at that time.
- *
- * If this stream is empty, the returned future is completed with
- * an error.
- * If this stream emits an error, or the call to [combine] throws,
- * the returned future is completed with that error,
- * and processing is stopped.
- */
+ /// Combines a sequence of values by repeatedly applying [combine].
+ ///
+ /// Similar to [Iterable.reduce], this function maintains a value,
+ /// starting with the first element of this stream
+ /// and updated for each further element of this stream.
+ /// For each element after the first,
+ /// the value is updated to the result of calling [combine]
+ /// with the previous value and the element.
+ ///
+ /// When this stream is done, the returned future is completed with
+ /// the value at that time.
+ ///
+ /// If this stream is empty, the returned future is completed with
+ /// an error.
+ /// If this stream emits an error, or the call to [combine] throws,
+ /// the returned future is completed with that error,
+ /// and processing is stopped.
Future<T> reduce(T combine(T previous, T element)) {
_Future<T> result = new _Future<T>();
bool seenFirst = false;
@@ -824,23 +776,21 @@
return result;
}
- /**
- * Combines a sequence of values by repeatedly applying [combine].
- *
- * Similar to [Iterable.fold], this function maintains a value,
- * starting with [initialValue] and updated for each element of
- * this stream.
- * For each element, the value is updated to the result of calling
- * [combine] with the previous value and the element.
- *
- * When this stream is done, the returned future is completed with
- * the value at that time.
- * For an empty stream, the future is completed with [initialValue].
- *
- * If this stream emits an error, or the call to [combine] throws,
- * the returned future is completed with that error,
- * and processing is stopped.
- */
+ /// Combines a sequence of values by repeatedly applying [combine].
+ ///
+ /// Similar to [Iterable.fold], this function maintains a value,
+ /// starting with [initialValue] and updated for each element of
+ /// this stream.
+ /// For each element, the value is updated to the result of calling
+ /// [combine] with the previous value and the element.
+ ///
+ /// When this stream is done, the returned future is completed with
+ /// the value at that time.
+ /// For an empty stream, the future is completed with [initialValue].
+ ///
+ /// If this stream emits an error, or the call to [combine] throws,
+ /// the returned future is completed with that error,
+ /// and processing is stopped.
Future<S> fold<S>(S initialValue, S combine(S previous, T element)) {
_Future<S> result = new _Future<S>();
S value = initialValue;
@@ -856,20 +806,18 @@
return result;
}
- /**
- * Combines the string representation of elements into a single string.
- *
- * Each element is converted to a string using its [Object.toString] method.
- * If [separator] is provided, it is inserted between element string
- * representations.
- *
- * The returned future is completed with the combined string when this stream
- * is done.
- *
- * If this stream emits an error, or the call to [Object.toString] throws,
- * the returned future is completed with that error,
- * and processing stops.
- */
+ /// Combines the string representation of elements into a single string.
+ ///
+ /// Each element is converted to a string using its [Object.toString] method.
+ /// If [separator] is provided, it is inserted between element string
+ /// representations.
+ ///
+ /// The returned future is completed with the combined string when this stream
+ /// is done.
+ ///
+ /// If this stream emits an error, or the call to [Object.toString] throws,
+ /// the returned future is completed with that error,
+ /// and processing stops.
Future<String> join([String separator = ""]) {
_Future<String> result = new _Future<String>();
StringBuffer buffer = new StringBuffer();
@@ -900,18 +848,16 @@
return result;
}
- /**
- * Returns whether [needle] occurs in the elements provided by this stream.
- *
- * Compares each element of this stream to [needle] using [Object.==].
- * If an equal element is found, the returned future is completed with `true`.
- * If this stream ends without finding a match, the future is completed with
- * `false`.
- *
- * If this stream emits an error, or the call to [Object.==] throws,
- * the returned future is completed with that error,
- * and processing stops.
- */
+ /// Returns whether [needle] occurs in the elements provided by this stream.
+ ///
+ /// Compares each element of this stream to [needle] using [Object.==].
+ /// If an equal element is found, the returned future is completed with `true`.
+ /// If this stream ends without finding a match, the future is completed with
+ /// `false`.
+ ///
+ /// If this stream emits an error, or the call to [Object.==] throws,
+ /// the returned future is completed with that error,
+ /// and processing stops.
Future<bool> contains(Object? needle) {
_Future<bool> future = new _Future<bool>();
StreamSubscription<T> subscription =
@@ -928,16 +874,14 @@
return future;
}
- /**
- * Executes [action] on each element of this stream.
- *
- * Completes the returned [Future] when all elements of this stream
- * have been processed.
- *
- * If this stream emits an error, or if the call to [action] throws,
- * the returned future completes with that error,
- * and processing stops.
- */
+ /// Executes [action] on each element of this stream.
+ ///
+ /// Completes the returned [Future] when all elements of this stream
+ /// have been processed.
+ ///
+ /// If this stream emits an error, or if the call to [action] throws,
+ /// the returned future completes with that error,
+ /// and processing stops.
Future forEach(void action(T element)) {
_Future future = new _Future();
StreamSubscription<T> subscription =
@@ -951,20 +895,18 @@
return future;
}
- /**
- * Checks whether [test] accepts all elements provided by this stream.
- *
- * Calls [test] on each element of this stream.
- * If the call returns `false`, the returned future is completed with `false`
- * and processing stops.
- *
- * If this stream ends without finding an element that [test] rejects,
- * the returned future is completed with `true`.
- *
- * If this stream emits an error, or if the call to [test] throws,
- * the returned future is completed with that error,
- * and processing stops.
- */
+ /// Checks whether [test] accepts all elements provided by this stream.
+ ///
+ /// Calls [test] on each element of this stream.
+ /// If the call returns `false`, the returned future is completed with `false`
+ /// and processing stops.
+ ///
+ /// If this stream ends without finding an element that [test] rejects,
+ /// the returned future is completed with `true`.
+ ///
+ /// If this stream emits an error, or if the call to [test] throws,
+ /// the returned future is completed with that error,
+ /// and processing stops.
Future<bool> every(bool test(T element)) {
_Future<bool> future = new _Future<bool>();
StreamSubscription<T> subscription =
@@ -981,20 +923,18 @@
return future;
}
- /**
- * Checks whether [test] accepts any element provided by this stream.
- *
- * Calls [test] on each element of this stream.
- * If the call returns `true`, the returned future is completed with `true`
- * and processing stops.
- *
- * If this stream ends without finding an element that [test] accepts,
- * the returned future is completed with `false`.
- *
- * If this stream emits an error, or if the call to [test] throws,
- * the returned future is completed with that error,
- * and processing stops.
- */
+ /// Checks whether [test] accepts any element provided by this stream.
+ ///
+ /// Calls [test] on each element of this stream.
+ /// If the call returns `true`, the returned future is completed with `true`
+ /// and processing stops.
+ ///
+ /// If this stream ends without finding an element that [test] accepts,
+ /// the returned future is completed with `false`.
+ ///
+ /// If this stream emits an error, or if the call to [test] throws,
+ /// the returned future is completed with that error,
+ /// and processing stops.
Future<bool> any(bool test(T element)) {
_Future<bool> future = new _Future<bool>();
StreamSubscription<T> subscription =
@@ -1011,19 +951,17 @@
return future;
}
- /**
- * The number of elements in this stream.
- *
- * Waits for all elements of this stream. When this stream ends,
- * the returned future is completed with the number of elements.
- *
- * If this stream emits an error,
- * the returned future is completed with that error,
- * and processing stops.
- *
- * This operation listens to this stream, and a non-broadcast stream cannot
- * be reused after finding its length.
- */
+ /// The number of elements in this stream.
+ ///
+ /// Waits for all elements of this stream. When this stream ends,
+ /// the returned future is completed with the number of elements.
+ ///
+ /// If this stream emits an error,
+ /// the returned future is completed with that error,
+ /// and processing stops.
+ ///
+ /// This operation listens to this stream, and a non-broadcast stream cannot
+ /// be reused after finding its length.
Future<int> get length {
_Future<int> future = new _Future<int>();
int count = 0;
@@ -1039,20 +977,18 @@
return future;
}
- /**
- * Whether this stream contains any elements.
- *
- * Waits for the first element of this stream, then completes the returned
- * future with `false`.
- * If this stream ends without emitting any elements, the returned future is
- * completed with `true`.
- *
- * If the first event is an error, the returned future is completed with that
- * error.
- *
- * This operation listens to this stream, and a non-broadcast stream cannot
- * be reused after checking whether it is empty.
- */
+ /// Whether this stream contains any elements.
+ ///
+ /// Waits for the first element of this stream, then completes the returned
+ /// future with `false`.
+ /// If this stream ends without emitting any elements, the returned future is
+ /// completed with `true`.
+ ///
+ /// If the first event is an error, the returned future is completed with that
+ /// error.
+ ///
+ /// This operation listens to this stream, and a non-broadcast stream cannot
+ /// be reused after checking whether it is empty.
Future<bool> get isEmpty {
_Future<bool> future = new _Future<bool>();
StreamSubscription<T> subscription =
@@ -1065,24 +1001,21 @@
return future;
}
- /**
- * Adapt this stream to be a `Stream<R>`.
- *
- * This stream is wrapped as a `Stream<R>` which checks at run-time that
- * each data event emitted by this stream is also an instance of [R].
- */
+ /// Adapt this stream to be a `Stream<R>`.
+ ///
+ /// This stream is wrapped as a `Stream<R>` which checks at run-time that
+ /// each data event emitted by this stream is also an instance of [R].
Stream<R> cast<R>() => Stream.castFrom<T, R>(this);
- /**
- * Collects all elements of this stream in a [List].
- *
- * Creates a `List<T>` and adds all elements of this stream to the list
- * in the order they arrive.
- * When this stream ends, the returned future is completed with that list.
- *
- * If this stream emits an error,
- * the returned future is completed with that error,
- * and processing stops.
- */
+
+ /// Collects all elements of this stream in a [List].
+ ///
+ /// Creates a `List<T>` and adds all elements of this stream to the list
+ /// in the order they arrive.
+ /// When this stream ends, the returned future is completed with that list.
+ ///
+ /// If this stream emits an error,
+ /// the returned future is completed with that error,
+ /// and processing stops.
Future<List<T>> toList() {
List<T> result = <T>[];
_Future<List<T>> future = new _Future<List<T>>();
@@ -1098,23 +1031,21 @@
return future;
}
- /**
- * Collects the data of this stream in a [Set].
- *
- * Creates a `Set<T>` and adds all elements of this stream to the set.
- * in the order they arrive.
- * When this stream ends, the returned future is completed with that set.
- *
- * The returned set is the same type as returned by `new Set<T>()`.
- * If another type of set is needed, either use [forEach] to add each
- * element to the set, or use
- * `toList().then((list) => new SomeOtherSet.from(list))`
- * to create the set.
- *
- * If this stream emits an error,
- * the returned future is completed with that error,
- * and processing stops.
- */
+ /// Collects the data of this stream in a [Set].
+ ///
+ /// Creates a `Set<T>` and adds all elements of this stream to the set.
+ /// in the order they arrive.
+ /// When this stream ends, the returned future is completed with that set.
+ ///
+ /// The returned set is the same type as created by `<T>{}`.
+ /// If another type of set is needed, either use [forEach] to add each
+ /// element to the set, or use
+ /// `toList().then((list) => new SomeOtherSet.from(list))`
+ /// to create the set.
+ ///
+ /// If this stream emits an error,
+ /// the returned future is completed with that error,
+ /// and processing stops.
Future<Set<T>> toSet() {
Set<T> result = new Set<T>();
_Future<Set<T>> future = new _Future<Set<T>>();
@@ -1130,22 +1061,20 @@
return future;
}
- /**
- * Discards all data on this stream, but signals when it is done or an error
- * occurred.
- *
- * When subscribing using [drain], cancelOnError will be true. This means
- * that the future will complete with the first error on this stream and then
- * cancel the subscription.
- *
- * If this stream emits an error, the returned future is completed with
- * that error, and processing is stopped.
- *
- * In case of a `done` event the future completes with the given
- * [futureValue].
- *
- * The [futureValue] must not be omitted if `null` is not assignable to [E].
- */
+ /// Discards all data on this stream, but signals when it is done or an error
+ /// occurred.
+ ///
+ /// When subscribing using [drain], cancelOnError will be true. This means
+ /// that the future will complete with the first error on this stream and then
+ /// cancel the subscription.
+ ///
+ /// If this stream emits an error, the returned future is completed with
+ /// that error, and processing is stopped.
+ ///
+ /// In case of a `done` event the future completes with the given
+ /// [futureValue].
+ ///
+ /// The [futureValue] must not be omitted if `null` is not assignable to [E].
Future<E> drain<E>([E? futureValue]) {
if (futureValue == null) {
futureValue = futureValue as E;
@@ -1153,141 +1082,129 @@
return listen(null, cancelOnError: true).asFuture<E>(futureValue);
}
- /**
- * Provides at most the first [count] data events of this stream.
- *
- * Returns a stream that emits the same events that this stream would
- * if listened to at the same time,
- * until either this stream ends or it has emitted [count] data events,
- * at which point the returned stream is done.
- *
- * If this stream produces fewer than [count] data events before it's done,
- * so will the returned stream.
- *
- * Starts listening to this stream when the returned stream is listened to
- * and stops listening when the first [count] data events have been received.
- *
- * This means that if this is a single-subscription (non-broadcast) streams
- * it cannot be reused after the returned stream has been listened to.
- *
- * If this is a broadcast stream, the returned stream is a broadcast stream.
- * In that case, the events are only counted from the time
- * the returned stream is listened to.
- */
+ /// Provides at most the first [count] data events of this stream.
+ ///
+ /// Returns a stream that emits the same events that this stream would
+ /// if listened to at the same time,
+ /// until either this stream ends or it has emitted [count] data events,
+ /// at which point the returned stream is done.
+ ///
+ /// If this stream produces fewer than [count] data events before it's done,
+ /// so will the returned stream.
+ ///
+ /// Starts listening to this stream when the returned stream is listened to
+ /// and stops listening when the first [count] data events have been received.
+ ///
+ /// This means that if this is a single-subscription (non-broadcast) streams
+ /// it cannot be reused after the returned stream has been listened to.
+ ///
+ /// If this is a broadcast stream, the returned stream is a broadcast stream.
+ /// In that case, the events are only counted from the time
+ /// the returned stream is listened to.
Stream<T> take(int count) {
return new _TakeStream<T>(this, count);
}
- /**
- * Forwards data events while [test] is successful.
- *
- * Returns a stream that provides the same events as this stream
- * until [test] fails for a data event.
- * The returned stream is done when either this stream is done,
- * or when this stream first emits a data event that fails [test].
- *
- * The `test` call is considered failing if it returns a non-`true` value
- * or if it throws. If the `test` call throws, the error is emitted as the
- * last event on the returned streams.
- *
- * Stops listening to this stream after the accepted elements.
- *
- * Internally the method cancels its subscription after these elements. This
- * means that single-subscription (non-broadcast) streams are closed and
- * cannot be reused after a call to this method.
- *
- * The returned stream is a broadcast stream if this stream is.
- * For a broadcast stream, the events are only tested from the time
- * the returned stream is listened to.
- */
+ /// Forwards data events while [test] is successful.
+ ///
+ /// Returns a stream that provides the same events as this stream
+ /// until [test] fails for a data event.
+ /// The returned stream is done when either this stream is done,
+ /// or when this stream first emits a data event that fails [test].
+ ///
+ /// The `test` call is considered failing if it returns a non-`true` value
+ /// or if it throws. If the `test` call throws, the error is emitted as the
+ /// last event on the returned streams.
+ ///
+ /// Stops listening to this stream after the accepted elements.
+ ///
+ /// Internally the method cancels its subscription after these elements. This
+ /// means that single-subscription (non-broadcast) streams are closed and
+ /// cannot be reused after a call to this method.
+ ///
+ /// The returned stream is a broadcast stream if this stream is.
+ /// For a broadcast stream, the events are only tested from the time
+ /// the returned stream is listened to.
Stream<T> takeWhile(bool test(T element)) {
return new _TakeWhileStream<T>(this, test);
}
- /**
- * Skips the first [count] data events from this stream.
- *
- * Returns a stream that emits the same events as this stream would
- * if listened to at the same time, except that the first [count]
- * data events are not emitted.
- * The returned stream is done when this stream is.
- *
- * If this stream emits fewer than [count] data events
- * before being done, the returned stream emits no data events.
- *
- * The returned stream is a broadcast stream if this stream is.
- * For a broadcast stream, the events are only counted from the time
- * the returned stream is listened to.
- */
+ /// Skips the first [count] data events from this stream.
+ ///
+ /// Returns a stream that emits the same events as this stream would
+ /// if listened to at the same time, except that the first [count]
+ /// data events are not emitted.
+ /// The returned stream is done when this stream is.
+ ///
+ /// If this stream emits fewer than [count] data events
+ /// before being done, the returned stream emits no data events.
+ ///
+ /// The returned stream is a broadcast stream if this stream is.
+ /// For a broadcast stream, the events are only counted from the time
+ /// the returned stream is listened to.
Stream<T> skip(int count) {
return new _SkipStream<T>(this, count);
}
- /**
- * Skip data events from this stream while they are matched by [test].
- *
- * Returns a stream that emits the same events as this stream,
- * except that data events are not emitted until a data event fails `test`.
- * The test fails when called with a data event
- * if it returns a non-`true` value or if the call to `test` throws.
- * If the call throws, the error is emitted as an error event
- * on the returned stream instead of the data event,
- * otherwise the event that made `test` return non-true is emitted as the
- * first data event.
- *
- * Error and done events are provided by the returned stream unmodified.
- *
- * The returned stream is a broadcast stream if this stream is.
- * For a broadcast stream, the events are only tested from the time
- * the returned stream is listened to.
- */
+ /// Skip data events from this stream while they are matched by [test].
+ ///
+ /// Returns a stream that emits the same events as this stream,
+ /// except that data events are not emitted until a data event fails `test`.
+ /// The test fails when called with a data event
+ /// if it returns a non-`true` value or if the call to `test` throws.
+ /// If the call throws, the error is emitted as an error event
+ /// on the returned stream instead of the data event,
+ /// otherwise the event that made `test` return non-true is emitted as the
+ /// first data event.
+ ///
+ /// Error and done events are provided by the returned stream unmodified.
+ ///
+ /// The returned stream is a broadcast stream if this stream is.
+ /// For a broadcast stream, the events are only tested from the time
+ /// the returned stream is listened to.
Stream<T> skipWhile(bool test(T element)) {
return new _SkipWhileStream<T>(this, test);
}
- /**
- * Skips data events if they are equal to the previous data event.
- *
- * The returned stream provides the same events as this stream, except
- * that it never provides two consecutive data events that are equal.
- * That is, errors are passed through to the returned stream, and
- * data events are passed through if they are distinct from the most
- * recently emitted data event.
- *
- * Equality is determined by the provided [equals] method. If that is
- * omitted, the '==' operator on the last provided data element is used.
- *
- * If [equals] throws, the data event is replaced by an error event
- * containing the thrown error. The behavior is equivalent to the
- * original stream emitting the error event, and it doesn't change
- * the what the most recently emitted data event is.
- *
- * The returned stream is a broadcast stream if this stream is.
- * If a broadcast stream is listened to more than once, each subscription
- * will individually perform the `equals` test.
- */
+ /// Skips data events if they are equal to the previous data event.
+ ///
+ /// The returned stream provides the same events as this stream, except
+ /// that it never provides two consecutive data events that are equal.
+ /// That is, errors are passed through to the returned stream, and
+ /// data events are passed through if they are distinct from the most
+ /// recently emitted data event.
+ ///
+ /// Equality is determined by the provided [equals] method. If that is
+ /// omitted, the '==' operator on the last provided data element is used.
+ ///
+ /// If [equals] throws, the data event is replaced by an error event
+ /// containing the thrown error. The behavior is equivalent to the
+ /// original stream emitting the error event, and it doesn't change
+ /// the what the most recently emitted data event is.
+ ///
+ /// The returned stream is a broadcast stream if this stream is.
+ /// If a broadcast stream is listened to more than once, each subscription
+ /// will individually perform the `equals` test.
Stream<T> distinct([bool equals(T previous, T next)?]) {
return new _DistinctStream<T>(this, equals);
}
- /**
- * The first element of this stream.
- *
- * Stops listening to this stream after the first element has been received.
- *
- * Internally the method cancels its subscription after the first element.
- * This means that single-subscription (non-broadcast) streams are closed
- * and cannot be reused after a call to this getter.
- *
- * If an error event occurs before the first data event, the returned future
- * is completed with that error.
- *
- * If this stream is empty (a done event occurs before the first data event),
- * the returned future completes with an error.
- *
- * Except for the type of the error, this method is equivalent to
- * `this.elementAt(0)`.
- */
+ /// The first element of this stream.
+ ///
+ /// Stops listening to this stream after the first element has been received.
+ ///
+ /// Internally the method cancels its subscription after the first element.
+ /// This means that single-subscription (non-broadcast) streams are closed
+ /// and cannot be reused after a call to this getter.
+ ///
+ /// If an error event occurs before the first data event, the returned future
+ /// is completed with that error.
+ ///
+ /// If this stream is empty (a done event occurs before the first data event),
+ /// the returned future completes with an error.
+ ///
+ /// Except for the type of the error, this method is equivalent to
+ /// `this.elementAt(0)`.
Future<T> get first {
_Future<T> future = new _Future<T>();
StreamSubscription<T> subscription =
@@ -1304,16 +1221,14 @@
return future;
}
- /**
- * The last element of this stream.
- *
- * If this stream emits an error event,
- * the returned future is completed with that error
- * and processing stops.
- *
- * If this stream is empty (the done event is the first event),
- * the returned future completes with an error.
- */
+ /// The last element of this stream.
+ ///
+ /// If this stream emits an error event,
+ /// the returned future is completed with that error
+ /// and processing stops.
+ ///
+ /// If this stream is empty (the done event is the first event),
+ /// the returned future completes with an error.
Future<T> get last {
_Future<T> future = new _Future<T>();
late T result;
@@ -1339,16 +1254,14 @@
return future;
}
- /**
- * The single element of this stream.
- *
- * If this stream emits an error event,
- * the returned future is completed with that error
- * and processing stops.
- *
- * If [this] is empty or has more than one element,
- * the returned future completes with an error.
- */
+ /// The single element of this stream.
+ ///
+ /// If this stream emits an error event,
+ /// the returned future is completed with that error
+ /// and processing stops.
+ ///
+ /// If [this] is empty or has more than one element,
+ /// the returned future completes with an error.
Future<T> get single {
_Future<T> future = new _Future<T>();
late T result;
@@ -1381,31 +1294,29 @@
return future;
}
- /**
- * Finds the first element of this stream matching [test].
- *
- * Returns a future that is completed with the first element of this stream
- * that [test] returns `true` for.
- *
- * If no such element is found before this stream is done, and a
- * [orElse] function is provided, the result of calling [orElse]
- * becomes the value of the future. If [orElse] throws, the returned
- * future is completed with that error.
- *
- * If this stream emits an error before the first matching element,
- * the returned future is completed with that error, and processing stops.
- *
- * Stops listening to this stream after the first matching element or error
- * has been received.
- *
- * Internally the method cancels its subscription after the first element that
- * matches the predicate. This means that single-subscription (non-broadcast)
- * streams are closed and cannot be reused after a call to this method.
- *
- * If an error occurs, or if this stream ends without finding a match and
- * with no [orElse] function provided,
- * the returned future is completed with an error.
- */
+ /// Finds the first element of this stream matching [test].
+ ///
+ /// Returns a future that is completed with the first element of this stream
+ /// that [test] returns `true` for.
+ ///
+ /// If no such element is found before this stream is done, and a
+ /// [orElse] function is provided, the result of calling [orElse]
+ /// becomes the value of the future. If [orElse] throws, the returned
+ /// future is completed with that error.
+ ///
+ /// If this stream emits an error before the first matching element,
+ /// the returned future is completed with that error, and processing stops.
+ ///
+ /// Stops listening to this stream after the first matching element or error
+ /// has been received.
+ ///
+ /// Internally the method cancels its subscription after the first element that
+ /// matches the predicate. This means that single-subscription (non-broadcast)
+ /// streams are closed and cannot be reused after a call to this method.
+ ///
+ /// If an error occurs, or if this stream ends without finding a match and
+ /// with no [orElse] function provided,
+ /// the returned future is completed with an error.
Future<T> firstWhere(bool test(T element), {T orElse()?}) {
_Future<T> future = new _Future();
StreamSubscription<T> subscription =
@@ -1432,17 +1343,15 @@
return future;
}
- /**
- * Finds the last element in this stream matching [test].
- *
- * If this stream emits an error, the returned future is completed with that
- * error, and processing stops.
- *
- * Otherwise as [firstWhere], except that the last matching element is found
- * instead of the first.
- * That means that a non-error result cannot be provided before this stream
- * is done.
- */
+ /// Finds the last element in this stream matching [test].
+ ///
+ /// If this stream emits an error, the returned future is completed with that
+ /// error, and processing stops.
+ ///
+ /// Otherwise as [firstWhere], except that the last matching element is found
+ /// instead of the first.
+ /// That means that a non-error result cannot be provided before this stream
+ /// is done.
Future<T> lastWhere(bool test(T element), {T orElse()?}) {
_Future<T> future = new _Future();
late T result;
@@ -1475,12 +1384,10 @@
return future;
}
- /**
- * Finds the single element in this stream matching [test].
- *
- * Like [lastWhere], except that it is an error if more than one
- * matching element occurs in this stream.
- */
+ /// Finds the single element in this stream matching [test].
+ ///
+ /// Like [lastWhere], except that it is an error if more than one
+ /// matching element occurs in this stream.
Future<T> singleWhere(bool test(T element), {T orElse()?}) {
_Future<T> future = new _Future<T>();
late T result;
@@ -1521,22 +1428,20 @@
return future;
}
- /**
- * Returns the value of the [index]th data event of this stream.
- *
- * Stops listening to this stream after the [index]th data event has been
- * received.
- *
- * Internally the method cancels its subscription after these elements. This
- * means that single-subscription (non-broadcast) streams are closed and
- * cannot be reused after a call to this method.
- *
- * If an error event occurs before the value is found, the future completes
- * with this error.
- *
- * If a done event occurs before the value is found, the future completes
- * with a [RangeError].
- */
+ /// Returns the value of the [index]th data event of this stream.
+ ///
+ /// Stops listening to this stream after the [index]th data event has been
+ /// received.
+ ///
+ /// Internally the method cancels its subscription after these elements. This
+ /// means that single-subscription (non-broadcast) streams are closed and
+ /// cannot be reused after a call to this method.
+ ///
+ /// If an error event occurs before the value is found, the future completes
+ /// with this error.
+ ///
+ /// If a done event occurs before the value is found, the future completes
+ /// with a [RangeError].
Future<T> elementAt(int index) {
RangeError.checkNotNegative(index, "index");
_Future<T> result = new _Future<T>();
@@ -1559,33 +1464,31 @@
return result;
}
- /**
- * Creates a new stream with the same events as this stream.
- *
- * Whenever more than [timeLimit] passes between two events from this stream,
- * the [onTimeout] function is called, which can emit further events on
- * the returned stream.
- *
- * The countdown doesn't start until the returned stream is listened to.
- * The countdown is reset every time an event is forwarded from this stream,
- * or when this stream is paused and resumed.
- *
- * The [onTimeout] function is called with one argument: an
- * [EventSink] that allows putting events into the returned stream.
- * This `EventSink` is only valid during the call to [onTimeout].
- * Calling [EventSink.close] on the sink passed to [onTimeout] closes the
- * returned stream, and no further events are processed.
- *
- * If [onTimeout] is omitted, a timeout will just put a [TimeoutException]
- * into the error channel of the returned stream.
- * If the call to [onTimeout] throws, the error is emitted on the returned
- * stream.
- *
- * The returned stream is a broadcast stream if this stream is.
- * If a broadcast stream is listened to more than once, each subscription
- * will have its individually timer that starts counting on listen,
- * and the subscriptions' timers can be paused individually.
- */
+ /// Creates a new stream with the same events as this stream.
+ ///
+ /// Whenever more than [timeLimit] passes between two events from this stream,
+ /// the [onTimeout] function is called, which can emit further events on
+ /// the returned stream.
+ ///
+ /// The countdown doesn't start until the returned stream is listened to.
+ /// The countdown is reset every time an event is forwarded from this stream,
+ /// or when this stream is paused and resumed.
+ ///
+ /// The [onTimeout] function is called with one argument: an
+ /// [EventSink] that allows putting events into the returned stream.
+ /// This `EventSink` is only valid during the call to [onTimeout].
+ /// Calling [EventSink.close] on the sink passed to [onTimeout] closes the
+ /// returned stream, and no further events are processed.
+ ///
+ /// If [onTimeout] is omitted, a timeout will just put a [TimeoutException]
+ /// into the error channel of the returned stream.
+ /// If the call to [onTimeout] throws, the error is emitted on the returned
+ /// stream.
+ ///
+ /// The returned stream is a broadcast stream if this stream is.
+ /// If a broadcast stream is listened to more than once, each subscription
+ /// will have its individually timer that starts counting on listen,
+ /// and the subscriptions' timers can be paused individually.
Stream<T> timeout(Duration timeLimit, {void onTimeout(EventSink<T> sink)?}) {
_StreamControllerBase<T> controller;
if (isBroadcast) {
@@ -1662,198 +1565,169 @@
}
}
-/**
- * A subscription on events from a [Stream].
- *
- * When you listen on a [Stream] using [Stream.listen],
- * a [StreamSubscription] object is returned.
- *
- * The subscription provides events to the listener,
- * and holds the callbacks used to handle the events.
- * The subscription can also be used to unsubscribe from the events,
- * or to temporarily pause the events from the stream.
- */
+/// A subscription on events from a [Stream].
+///
+/// When you listen on a [Stream] using [Stream.listen],
+/// a [StreamSubscription] object is returned.
+///
+/// The subscription provides events to the listener,
+/// and holds the callbacks used to handle the events.
+/// The subscription can also be used to unsubscribe from the events,
+/// or to temporarily pause the events from the stream.
abstract class StreamSubscription<T> {
- /**
- * Cancels this subscription.
- *
- * After this call, the subscription no longer receives events.
- *
- * The stream may need to shut down the source of events and clean up after
- * the subscription is canceled.
- *
- * Returns a future that is completed once the stream has finished
- * its cleanup.
- *
- * Typically, cleanup happens when the stream needs to release resources.
- * For example, a stream might need to close an open file (as an asynchronous
- * operation). If the listener wants to delete the file after having
- * canceled the subscription, it must wait for the cleanup future to complete.
- *
- * If the cleanup throws, which it really shouldn't, the returned future
- * completes with that error.
- */
+ /// Cancels this subscription.
+ ///
+ /// After this call, the subscription no longer receives events.
+ ///
+ /// The stream may need to shut down the source of events and clean up after
+ /// the subscription is canceled.
+ ///
+ /// Returns a future that is completed once the stream has finished
+ /// its cleanup.
+ ///
+ /// Typically, cleanup happens when the stream needs to release resources.
+ /// For example, a stream might need to close an open file (as an asynchronous
+ /// operation). If the listener wants to delete the file after having
+ /// canceled the subscription, it must wait for the cleanup future to complete.
+ ///
+ /// If the cleanup throws, which it really shouldn't, the returned future
+ /// completes with that error.
Future<void> cancel();
- /**
- * Replaces the data event handler of this subscription.
- *
- * The [handleData] function is called for each element of the stream
- * after this function is called.
- * If [handleData] is `null`, further elements are ignored.
- *
- * This method replaces the current handler set by the invocation of
- * [Stream.listen] or by a previous call to [onData].
- */
+ /// Replaces the data event handler of this subscription.
+ ///
+ /// The [handleData] function is called for each data event of the stream
+ /// after this function is called.
+ /// If [handleData] is `null`, data events are ignored.
+ ///
+ /// This method replaces the current handler set by the invocation of
+ /// [Stream.listen] or by a previous call to [onData].
void onData(void handleData(T data)?);
- /**
- * Replaces the error event handler of this subscription.
- *
- * The [handleError] function must be able to be called with either
- * one positional argument, or with two positional arguments
- * where the seconds is always a [StackTrace].
- *
- * The [handleError] argument may be `null`, in which case further
- * error events are considered unhandled, and will be reported to
- * [Zone.handleUncaughtError].
- *
- * The provided function is called for all error events from the
- * stream subscription.
- *
- * This method replaces the current handler set by the invocation of
- * [Stream.listen], by calling [asFuture], or by a previous call to [onError].
- */
+ /// Replaces the error event handler of this subscription.
+ ///
+ /// The [handleError] function must be able to be called with either
+ /// one positional argument, or with two positional arguments
+ /// where the seconds is always a [StackTrace].
+ ///
+ /// The [handleError] argument may be `null`, in which case further
+ /// error events are considered *unhandled*, and will be reported to
+ /// [Zone.handleUncaughtError].
+ ///
+ /// The provided function is called for all error events from the
+ /// stream subscription.
+ ///
+ /// This method replaces the current handler set by the invocation of
+ /// [Stream.listen], by calling [asFuture], or by a previous call to [onError].
void onError(Function? handleError);
- /**
- * Replaces the done event handler of this subscription.
- *
- * The [handleDone] function is called when the stream closes.
- * The value may be `null`, in which case no function is called.
- *
- * This method replaces the current handler set by the invocation of
- * [Stream.listen], by calling [asFuture], or by a previous call to [onDone].
- */
+ /// Replaces the done event handler of this subscription.
+ ///
+ /// The [handleDone] function is called when the stream closes.
+ /// The value may be `null`, in which case no function is called.
+ ///
+ /// This method replaces the current handler set by the invocation of
+ /// [Stream.listen], by calling [asFuture], or by a previous call to [onDone].
void onDone(void handleDone()?);
- /**
- * Request that the stream pauses events until further notice.
- *
- * While paused, the subscription will not fire any events.
- * If it receives events from its source, they will be buffered until
- * the subscription is resumed.
- * For non-broadcast streams, the underlying source is usually informed
- * about the pause,
- * so it can stop generating events until the subscription is resumed.
- *
- * To avoid buffering events on a broadcast stream, it is better to
- * cancel this subscription, and start to listen again when events
- * are needed, if the intermediate events are not important.
- *
- * If [resumeSignal] is provided, the stream subscription will undo the pause
- * when the future completes, as if by a call to [resume].
- * If the future completes with an error,
- * the stream will still resume, but the error will be considered unhandled
- * and is passed to [Zone.handleUncaughtError].
- *
- * A call to [resume] will also undo a pause.
- *
- * If the subscription is paused more than once, an equal number
- * of resumes must be performed to resume the stream.
- * Calls to [resume] and the completion of a [resumeSignal] are
- * interchangeable - the [pause] which was passed a [resumeSignal] may be
- * ended by a call to [resume], and completing the [resumeSignal] may end a
- * different [pause].
- *
- * It is safe to [resume] or complete a [resumeSignal] even when the
- * subscription is not paused, and the resume will have no effect.
- *
- * Currently DOM streams silently drop events when the stream is paused. This
- * is a bug and will be fixed.
- */
+ /// Requests that the stream pauses events until further notice.
+ ///
+ /// While paused, the subscription will not fire any events.
+ /// If it receives events from its source, they will be buffered until
+ /// the subscription is resumed.
+ /// For non-broadcast streams, the underlying source is usually informed
+ /// about the pause,
+ /// so it can stop generating events until the subscription is resumed.
+ ///
+ /// To avoid buffering events on a broadcast stream, it is better to
+ /// cancel this subscription, and start to listen again when events
+ /// are needed, if the intermediate events are not important.
+ ///
+ /// If [resumeSignal] is provided, the stream subscription will undo the pause
+ /// when the future completes, as if by a call to [resume].
+ /// If the future completes with an error,
+ /// the stream will still resume, but the error will be considered unhandled
+ /// and is passed to [Zone.handleUncaughtError].
+ ///
+ /// A call to [resume] will also undo a pause.
+ ///
+ /// If the subscription is paused more than once, an equal number
+ /// of resumes must be performed to resume the stream.
+ /// Calls to [resume] and the completion of a [resumeSignal] are
+ /// interchangeable - the [pause] which was passed a [resumeSignal] may be
+ /// ended by a call to [resume], and completing the [resumeSignal] may end a
+ /// different [pause].
+ ///
+ /// It is safe to [resume] or complete a [resumeSignal] even when the
+ /// subscription is not paused, and the resume will have no effect.
void pause([Future<void>? resumeSignal]);
- /**
- * Resume after a pause.
- *
- * This undoes one previous call to [pause].
- * When all previously calls to [pause] have been matched by a calls to
- * [resume], possibly through a `resumeSignal` passed to [pause],
- * the stream subscription may emit events again.
- *
- * It is safe to [resume] even when the subscription is not paused, and the
- * resume will have no effect.
- */
+ /// Resumes after a pause.
+ ///
+ /// This undoes one previous call to [pause].
+ /// When all previously calls to [pause] have been matched by a calls to
+ /// [resume], possibly through a `resumeSignal` passed to [pause],
+ /// the stream subscription may emit events again.
+ ///
+ /// It is safe to [resume] even when the subscription is not paused, and the
+ /// resume will have no effect.
void resume();
- /**
- * Whether the [StreamSubscription] is currently paused.
- *
- * If there have been more calls to [pause] than to [resume] on this
- * stream subscription, the subscription is paused, and this getter
- * returns `true`.
- *
- * Returns `false` if the stream can currently emit events, or if
- * the subscription has completed or been cancelled.
- */
+ /// Whether the [StreamSubscription] is currently paused.
+ ///
+ /// If there have been more calls to [pause] than to [resume] on this
+ /// stream subscription, the subscription is paused, and this getter
+ /// returns `true`.
+ ///
+ /// Returns `false` if the stream can currently emit events, or if
+ /// the subscription has completed or been cancelled.
bool get isPaused;
- /**
- * Returns a future that handles the [onDone] and [onError] callbacks.
- *
- * This method *overwrites* the existing [onDone] and [onError] callbacks
- * with new ones that complete the returned future.
- *
- * In case of an error the subscription will automatically cancel (even
- * when it was listening with `cancelOnError` set to `false`).
- *
- * In case of a `done` event the future completes with the given
- * [futureValue].
- *
- * If [futureValue] is omitted, the value `null as E` is used as a default.
- * If `E` is not nullable, this will throw immediately when [asFuture]
- * is called.
- */
+ /// Returns a future that handles the [onDone] and [onError] callbacks.
+ ///
+ /// This method *overwrites* the existing [onDone] and [onError] callbacks
+ /// with new ones that complete the returned future.
+ ///
+ /// In case of an error the subscription will automatically cancel (even
+ /// when it was listening with `cancelOnError` set to `false`).
+ ///
+ /// In case of a `done` event the future completes with the given
+ /// [futureValue].
+ ///
+ /// If [futureValue] is omitted, the value `null as E` is used as a default.
+ /// If `E` is not nullable, this will throw immediately when [asFuture]
+ /// is called.
Future<E> asFuture<E>([E? futureValue]);
}
-/**
- * A [Sink] that supports adding errors.
- *
- * This makes it suitable for capturing the results of asynchronous
- * computations, which can complete with a value or an error.
- *
- * The [EventSink] has been designed to handle asynchronous events from
- * [Stream]s. See, for example, [Stream.eventTransformed] which uses
- * `EventSink`s to transform events.
- */
+/// A [Sink] that supports adding errors.
+///
+/// This makes it suitable for capturing the results of asynchronous
+/// computations, which can complete with a value or an error.
+///
+/// The [EventSink] has been designed to handle asynchronous events from
+/// [Stream]s. See, for example, [Stream.eventTransformed] which uses
+/// `EventSink`s to transform events.
abstract class EventSink<T> implements Sink<T> {
- /**
- * Adds a data [event] to the sink.
- *
- * Must not be called on a closed sink.
- */
+ /// Adds a data [event] to the sink.
+ ///
+ /// Must not be called on a closed sink.
void add(T event);
- /**
- * Adds an [error] to the sink.
- *
- * Must not be called on a closed sink.
- */
+ /// Adds an [error] to the sink.
+ ///
+ /// Must not be called on a closed sink.
void addError(Object error, [StackTrace? stackTrace]);
- /**
- * Closes the sink.
- *
- * Calling this method more than once is allowed, but does nothing.
- *
- * Neither [add] nor [addError] must be called after this method.
- */
+ /// Closes the sink.
+ ///
+ /// Calling this method more than once is allowed, but does nothing.
+ ///
+ /// Neither [add] nor [addError] must be called after this method.
void close();
}
-/** [Stream] wrapper that only exposes the [Stream] interface. */
+/// [Stream] wrapper that only exposes the [Stream] interface.
class StreamView<T> extends Stream<T> {
final Stream<T> _stream;
@@ -1875,335 +1749,307 @@
}
}
-/**
- * Abstract interface for a "sink" accepting multiple entire streams.
- *
- * A consumer can accept a number of consecutive streams using [addStream],
- * and when no further data need to be added, the [close] method tells the
- * consumer to complete its work and shut down.
- *
- * The [Stream.pipe] accepts a `StreamConsumer` and will pass the stream
- * to the consumer's [addStream] method. When that completes, it will
- * call [close] and then complete its own returned future.
- */
+/// Abstract interface for a "sink" accepting multiple entire streams.
+///
+/// A consumer can accept a number of consecutive streams using [addStream],
+/// and when no further data need to be added, the [close] method tells the
+/// consumer to complete its work and shut down.
+///
+/// The [Stream.pipe] accepts a `StreamConsumer` and will pass the stream
+/// to the consumer's [addStream] method. When that completes, it will
+/// call [close] and then complete its own returned future.
abstract class StreamConsumer<S> {
- /**
- * Consumes the elements of [stream].
- *
- * Listens on [stream] and does something for each event.
- *
- * Returns a future which is completed when the stream is done being added,
- * and the consumer is ready to accept a new stream.
- * No further calls to [addStream] or [close] should happen before the
- * returned future has completed.
- *
- * The consumer may stop listening to the stream after an error,
- * it may consume all the errors and only stop at a done event,
- * or it may be canceled early if the receiver don't want any further events.
- *
- * If the consumer stops listening because of some error preventing it
- * from continuing, it may report this error in the returned future,
- * otherwise it will just complete the future with `null`.
- */
+ /// Consumes the elements of [stream].
+ ///
+ /// Listens on [stream] and does something for each event.
+ ///
+ /// Returns a future which is completed when the stream is done being added,
+ /// and the consumer is ready to accept a new stream.
+ /// No further calls to [addStream] or [close] should happen before the
+ /// returned future has completed.
+ ///
+ /// The consumer may stop listening to the stream after an error,
+ /// it may consume all the errors and only stop at a done event,
+ /// or it may be canceled early if the receiver don't want any further events.
+ ///
+ /// If the consumer stops listening because of some error preventing it
+ /// from continuing, it may report this error in the returned future,
+ /// otherwise it will just complete the future with `null`.
Future addStream(Stream<S> stream);
- /**
- * Tells the consumer that no further streams will be added.
- *
- * This allows the consumer to complete any remaining work and release
- * resources that are no longer needed
- *
- * Returns a future which is completed when the consumer has shut down.
- * If cleaning up can fail, the error may be reported in the returned future,
- * otherwise it completes with `null`.
- */
+ /// Tells the consumer that no further streams will be added.
+ ///
+ /// This allows the consumer to complete any remaining work and release
+ /// resources that are no longer needed
+ ///
+ /// Returns a future which is completed when the consumer has shut down.
+ /// If cleaning up can fail, the error may be reported in the returned future,
+ /// otherwise it completes with `null`.
Future close();
}
-/**
- * A object that accepts stream events both synchronously and asynchronously.
- *
- * A [StreamSink] combines the methods from [StreamConsumer] and [EventSink].
- *
- * The [EventSink] methods can't be used while the [addStream] is called.
- * As soon as the [addStream]'s [Future] completes with a value, the
- * [EventSink] methods can be used again.
- *
- * If [addStream] is called after any of the [EventSink] methods, it'll
- * be delayed until the underlying system has consumed the data added by the
- * [EventSink] methods.
- *
- * When [EventSink] methods are used, the [done] [Future] can be used to
- * catch any errors.
- *
- * When [close] is called, it will return the [done] [Future].
- */
+/// A object that accepts stream events both synchronously and asynchronously.
+///
+/// A [StreamSink] combines the methods from [StreamConsumer] and [EventSink].
+///
+/// The [EventSink] methods can't be used while the [addStream] is called.
+/// As soon as the [addStream]'s [Future] completes with a value, the
+/// [EventSink] methods can be used again.
+///
+/// If [addStream] is called after any of the [EventSink] methods, it'll
+/// be delayed until the underlying system has consumed the data added by the
+/// [EventSink] methods.
+///
+/// When [EventSink] methods are used, the [done] [Future] can be used to
+/// catch any errors.
+///
+/// When [close] is called, it will return the [done] [Future].
abstract class StreamSink<S> implements EventSink<S>, StreamConsumer<S> {
- /**
- * Tells the stream sink that no further streams will be added.
- *
- * This allows the stream sink to complete any remaining work and release
- * resources that are no longer needed
- *
- * Returns a future which is completed when the stream sink has shut down.
- * If cleaning up can fail, the error may be reported in the returned future,
- * otherwise it completes with `null`.
- *
- * Returns the same future as [done].
- *
- * The stream sink may close before the [close] method is called, either due
- * to an error or because it is itself providing events to someone who has
- * stopped listening. In that case, the [done] future is completed first,
- * and the `close` method will return the `done` future when called.
- *
- * Unifies [StreamConsumer.close] and [EventSink.close] which both mark their
- * object as not expecting any further events.
- */
+ /// Tells the stream sink that no further streams will be added.
+ ///
+ /// This allows the stream sink to complete any remaining work and release
+ /// resources that are no longer needed
+ ///
+ /// Returns a future which is completed when the stream sink has shut down.
+ /// If cleaning up can fail, the error may be reported in the returned future,
+ /// otherwise it completes with `null`.
+ ///
+ /// Returns the same future as [done].
+ ///
+ /// The stream sink may close before the [close] method is called, either due
+ /// to an error or because it is itself providing events to someone who has
+ /// stopped listening. In that case, the [done] future is completed first,
+ /// and the `close` method will return the `done` future when called.
+ ///
+ /// Unifies [StreamConsumer.close] and [EventSink.close] which both mark their
+ /// object as not expecting any further events.
Future close();
- /**
- * Return a future which is completed when the [StreamSink] is finished.
- *
- * If the `StreamSink` fails with an error,
- * perhaps in response to adding events using [add], [addError] or [close],
- * the [done] future will complete with that error.
- *
- * Otherwise, the returned future will complete when either:
- *
- * * all events have been processed and the sink has been closed, or
- * * the sink has otherwise been stopped from handling more events
- * (for example by canceling a stream subscription).
- */
+ /// Return a future which is completed when the [StreamSink] is finished.
+ ///
+ /// If the `StreamSink` fails with an error,
+ /// perhaps in response to adding events using [add], [addError] or [close],
+ /// the [done] future will complete with that error.
+ ///
+ /// Otherwise, the returned future will complete when either:
+ ///
+ /// * all events have been processed and the sink has been closed, or
+ /// * the sink has otherwise been stopped from handling more events
+ /// (for example by canceling a stream subscription).
Future get done;
}
-/**
- * Transforms a Stream.
- *
- * When a stream's [Stream.transform] method is invoked with a
- * [StreamTransformer], the stream calls the [bind] method on the provided
- * transformer. The resulting stream is then returned from the
- * [Stream.transform] method.
- *
- * Conceptually, a transformer is simply a function from [Stream] to [Stream]
- * that is encapsulated into a class.
- *
- * It is good practice to write transformers that can be used multiple times.
- *
- * All other transforming methods on [Stream], such as [Stream.map],
- * [Stream.where] or [Stream.expand] can be implemented using
- * [Stream.transform]. A [StreamTransformer] is thus very powerful but often
- * also a bit more complicated to use.
- */
+/// Transforms a Stream.
+///
+/// When a stream's [Stream.transform] method is invoked with a
+/// [StreamTransformer], the stream calls the [bind] method on the provided
+/// transformer. The resulting stream is then returned from the
+/// [Stream.transform] method.
+///
+/// Conceptually, a transformer is simply a function from [Stream] to [Stream]
+/// that is encapsulated into a class.
+///
+/// It is good practice to write transformers that can be used multiple times.
+///
+/// All other transforming methods on [Stream], such as [Stream.map],
+/// [Stream.where] or [Stream.expand] can be implemented using
+/// [Stream.transform]. A [StreamTransformer] is thus very powerful but often
+/// also a bit more complicated to use.
abstract class StreamTransformer<S, T> {
- /**
- * Creates a [StreamTransformer] based on the given [onListen] callback.
- *
- * The returned stream transformer uses the provided [onListen] callback
- * when a transformed stream is listened to. At that time, the callback
- * receives the input stream (the one passed to [bind]) and a
- * boolean flag `cancelOnError` to create a [StreamSubscription].
- *
- * If the transformed stream is a broadcast stream, so is the stream
- * returned by the [StreamTransformer.bind] method by this transformer.
- *
- * If the transformed stream is listened to multiple times, the [onListen]
- * callback is called again for each new [Stream.listen] call.
- * This happens whether the stream is a broadcast stream or not,
- * but the call will usually fail for non-broadcast streams.
- *
- * The [onListen] callback does *not* receive the handlers that were passed
- * to [Stream.listen]. These are automatically set after the call to the
- * [onListen] callback (using [StreamSubscription.onData],
- * [StreamSubscription.onError] and [StreamSubscription.onDone]).
- *
- * Most commonly, an [onListen] callback will first call [Stream.listen] on
- * the provided stream (with the corresponding `cancelOnError` flag), and then
- * return a new [StreamSubscription].
- *
- * There are two common ways to create a StreamSubscription:
- *
- * 1. by allocating a [StreamController] and to return the result of
- * listening to its stream. It's important to forward pause, resume and
- * cancel events (unless the transformer intentionally wants to change
- * this behavior).
- * 2. by creating a new class that implements [StreamSubscription].
- * Note that the subscription should run callbacks in the [Zone] the
- * stream was listened to (see [Zone] and [Zone.bindCallback]).
- *
- * Example:
- *
- * ```
- * /// Starts listening to [input] and duplicates all non-error events.
- * StreamSubscription<int> _onListen(Stream<int> input, bool cancelOnError) {
- * StreamSubscription<String> subscription;
- * // Create controller that forwards pause, resume and cancel events.
- * var controller = new StreamController<String>(
- * onPause: () {
- * subscription.pause();
- * },
- * onResume: () {
- * subscription.resume();
- * },
- * onCancel: () => subscription.cancel(),
- * sync: true); // "sync" is correct here, since events are forwarded.
- *
- * // Listen to the provided stream using `cancelOnError`.
- * subscription = input.listen((data) {
- * // Duplicate the data.
- * controller.add(data);
- * controller.add(data);
- * },
- * onError: controller.addError,
- * onDone: controller.close,
- * cancelOnError: cancelOnError);
- *
- * // Return a new [StreamSubscription] by listening to the controller's
- * // stream.
- * return controller.stream.listen(null);
- * }
- *
- * // Instantiate a transformer:
- * var duplicator = const StreamTransformer<int, int>(_onListen);
- *
- * // Use as follows:
- * intStream.transform(duplicator);
- * ```
- */
+ /// Creates a [StreamTransformer] based on the given [onListen] callback.
+ ///
+ /// The returned stream transformer uses the provided [onListen] callback
+ /// when a transformed stream is listened to. At that time, the callback
+ /// receives the input stream (the one passed to [bind]) and a
+ /// boolean flag `cancelOnError` to create a [StreamSubscription].
+ ///
+ /// If the transformed stream is a broadcast stream, so is the stream
+ /// returned by the [StreamTransformer.bind] method by this transformer.
+ ///
+ /// If the transformed stream is listened to multiple times, the [onListen]
+ /// callback is called again for each new [Stream.listen] call.
+ /// This happens whether the stream is a broadcast stream or not,
+ /// but the call will usually fail for non-broadcast streams.
+ ///
+ /// The [onListen] callback does *not* receive the handlers that were passed
+ /// to [Stream.listen]. These are automatically set after the call to the
+ /// [onListen] callback (using [StreamSubscription.onData],
+ /// [StreamSubscription.onError] and [StreamSubscription.onDone]).
+ ///
+ /// Most commonly, an [onListen] callback will first call [Stream.listen] on
+ /// the provided stream (with the corresponding `cancelOnError` flag), and then
+ /// return a new [StreamSubscription].
+ ///
+ /// There are two common ways to create a StreamSubscription:
+ ///
+ /// 1. by allocating a [StreamController] and to return the result of
+ /// listening to its stream. It's important to forward pause, resume and
+ /// cancel events (unless the transformer intentionally wants to change
+ /// this behavior).
+ /// 2. by creating a new class that implements [StreamSubscription].
+ /// Note that the subscription should run callbacks in the [Zone] the
+ /// stream was listened to (see [Zone] and [Zone.bindCallback]).
+ ///
+ /// Example:
+ ///
+ /// ```
+ /// /// Starts listening to [input] and duplicates all non-error events.
+ /// StreamSubscription<int> _onListen(Stream<int> input, bool cancelOnError) {
+ /// late StreamSubscription<String> subscription;
+ /// // Create controller that forwards pause, resume and cancel events.
+ /// var controller = new StreamController<String>(
+ /// onPause: () {
+ /// subscription.pause();
+ /// },
+ /// onResume: () {
+ /// subscription.resume();
+ /// },
+ /// onCancel: () => subscription.cancel(),
+ /// sync: true); // "sync" is correct here, since events are forwarded.
+ ///
+ /// // Listen to the provided stream.
+ /// subscription = input.listen((data) {
+ /// // Duplicate the data.
+ /// controller.add(data);
+ /// controller.add(data);
+ /// },
+ /// onError: controller.addError,
+ /// onDone: controller.close,
+ /// cancelOnError: cancelOnError);
+ ///
+ /// // Return a new [StreamSubscription] by listening to the controller's
+ /// // stream.
+ /// return controller.stream.listen(null);
+ /// }
+ ///
+ /// // Instantiate a transformer:
+ /// var duplicator = const StreamTransformer<int, int>(_onListen);
+ ///
+ /// // Use as follows:
+ /// intStream.transform(duplicator);
+ /// ```
const factory StreamTransformer(
StreamSubscription<T> onListen(
Stream<S> stream, bool cancelOnError)) =
_StreamSubscriptionTransformer<S, T>;
- /**
- * Creates a [StreamTransformer] that delegates events to the given functions.
- *
- * Example use of a duplicating transformer:
- *
- * ```
- * stringStream.transform(new StreamTransformer<String, String>.fromHandlers(
- * handleData: (String value, EventSink<String> sink) {
- * sink.add(value);
- * sink.add(value); // Duplicate the incoming events.
- * }));
- * ```
- *
- * Transformers that are constructed this way cannot use captured state if
- * they are used in streams that can be listened to multiple times.
- * ```
- * StreamController<String> controller;
- * controller = new StreamController.broadcast(onListen: () {
- * scheduleMicrotask(() {
- * controller.addError("Bad");
- * controller.addError("Worse");
- * controller.addError("Worst");
- * });
- * });
- * var sharedState = 0;
- * var transformedStream = controller.stream.transform(
- * new StreamTransformer<String>.fromHandlers(
- * handleError: (error, stackTrace, sink) {
- * sharedState++; // Increment shared error-counter.
- * sink.add("Error $sharedState: $error");
- * }));
- *
- * transformedStream.listen(print);
- * transformedStream.listen(print); // Listen twice.
- * // Listening twice to the same stream makes the transformer share the same
- * // state. Instead of having "Error 1: Bad", "Error 2: Worse",
- * // "Error 3: Worst" as output (each twice for the separate subscriptions),
- * // this program emits:
- * // Error 1: Bad
- * // Error 2: Bad
- * // Error 3: Worse
- * // Error 4: Worse
- * // Error 5: Worst
- * // Error 6: Worst
- * ```
- */
+ /// Creates a [StreamTransformer] that delegates events to the given functions.
+ ///
+ /// Example use of a duplicating transformer:
+ ///
+ /// ```
+ /// stringStream.transform(StreamTransformer<String, String>.fromHandlers(
+ /// handleData: (String value, EventSink<String> sink) {
+ /// sink.add(value);
+ /// sink.add(value); // Duplicate the incoming events.
+ /// }));
+ /// ```
+ ///
+ /// Transformers that are constructed this way cannot use captured state if
+ /// they are used in streams that can be listened to multiple times.
+ /// ```
+ /// StreamController<String> controller = StreamController.broadcast()
+ /// controller.onListen = () {
+ /// scheduleMicrotask(() {
+ /// controller.addError("Bad");
+ /// controller.addError("Worse");
+ /// controller.addError("Worst");
+ /// });
+ /// };
+ /// var sharedState = 0;
+ /// var transformedStream = controller.stream.transform(
+ /// StreamTransformer<String>.fromHandlers(
+ /// handleError: (error, stackTrace, sink) {
+ /// sharedState++; // Increment shared error-counter.
+ /// sink.add("Error $sharedState: $error");
+ /// }));
+ ///
+ /// transformedStream.listen(print);
+ /// transformedStream.listen(print); // Listen twice.
+ /// // Listening twice to the same stream makes the transformer share the same
+ /// // state. Instead of having "Error 1: Bad", "Error 2: Worse",
+ /// // "Error 3: Worst" as output (each twice for the separate subscriptions),
+ /// // this program emits:
+ /// // Error 1: Bad
+ /// // Error 2: Bad
+ /// // Error 3: Worse
+ /// // Error 4: Worse
+ /// // Error 5: Worst
+ /// // Error 6: Worst
+ /// ```
factory StreamTransformer.fromHandlers(
{void handleData(S data, EventSink<T> sink)?,
void handleError(Object error, StackTrace stackTrace, EventSink<T> sink)?,
void handleDone(EventSink<T> sink)?}) = _StreamHandlerTransformer<S, T>;
- /**
- * Creates a [StreamTransformer] based on a [bind] callback.
- *
- * The returned stream transformer uses the [bind] argument to implement the
- * [StreamTransformer.bind] API and can be used when the transformation is
- * available as a stream-to-stream function.
- *
- * ```dart
- * final splitDecoded = StreamTransformer<List<int>, String>.fromBind(
- * (stream) => stream.transform(utf8.decoder).transform(LineSplitter()));
- * ```
- */
+ /// Creates a [StreamTransformer] based on a [bind] callback.
+ ///
+ /// The returned stream transformer uses the [bind] argument to implement the
+ /// [StreamTransformer.bind] API and can be used when the transformation is
+ /// available as a stream-to-stream function.
+ ///
+ /// ```dart
+ /// final splitDecoded = StreamTransformer<List<int>, String>.fromBind(
+ /// (stream) => stream.transform(utf8.decoder).transform(LineSplitter()));
+ /// ```
@Since("2.1")
factory StreamTransformer.fromBind(Stream<T> Function(Stream<S>) bind) =
_StreamBindTransformer<S, T>;
- /**
- * Adapts [source] to be a `StreamTransformer<TS, TT>`.
- *
- * This allows [source] to be used at the new type, but at run-time it
- * must satisfy the requirements of both the new type and its original type.
- *
- * Data events passed into the returned transformer must also be instances
- * of [SS], and data events produced by [source] for those events must
- * also be instances of [TT].
- */
+ /// Adapts [source] to be a `StreamTransformer<TS, TT>`.
+ ///
+ /// This allows [source] to be used at the new type, but at run-time it
+ /// must satisfy the requirements of both the new type and its original type.
+ ///
+ /// Data events passed into the returned transformer must also be instances
+ /// of [SS], and data events produced by [source] for those events must
+ /// also be instances of [TT].
static StreamTransformer<TS, TT> castFrom<SS, ST, TS, TT>(
StreamTransformer<SS, ST> source) {
return new CastStreamTransformer<SS, ST, TS, TT>(source);
}
- /**
- * Transforms the provided [stream].
- *
- * Returns a new stream with events that are computed from events of the
- * provided [stream].
- *
- * The [StreamTransformer] interface is completely generic,
- * so it cannot say what subclasses do.
- * Each [StreamTransformer] should document clearly how it transforms the
- * stream (on the class or variable used to access the transformer),
- * as well as any differences from the following typical behavior:
- *
- * * When the returned stream is listened to, it starts listening to the
- * input [stream].
- * * Subscriptions of the returned stream forward (in a reasonable time)
- * a [StreamSubscription.pause] call to the subscription of the input
- * [stream].
- * * Similarly, canceling a subscription of the returned stream eventually
- * (in reasonable time) cancels the subscription of the input [stream].
- *
- * "Reasonable time" depends on the transformer and stream. Some transformers,
- * like a "timeout" transformer, might make these operations depend on a
- * duration. Others might not delay them at all, or just by a microtask.
- *
- * Transformers are free to handle errors in any way.
- * A transformer implementation may choose to propagate errors,
- * or convert them to other events, or ignore them completely,
- * but if errors are ignored, it should be documented explicitly.
- */
+ /// Transforms the provided [stream].
+ ///
+ /// Returns a new stream with events that are computed from events of the
+ /// provided [stream].
+ ///
+ /// The [StreamTransformer] interface is completely generic,
+ /// so it cannot say what subclasses do.
+ /// Each [StreamTransformer] should document clearly how it transforms the
+ /// stream (on the class or variable used to access the transformer),
+ /// as well as any differences from the following typical behavior:
+ ///
+ /// * When the returned stream is listened to, it starts listening to the
+ /// input [stream].
+ /// * Subscriptions of the returned stream forward (in a reasonable time)
+ /// a [StreamSubscription.pause] call to the subscription of the input
+ /// [stream].
+ /// * Similarly, canceling a subscription of the returned stream eventually
+ /// (in reasonable time) cancels the subscription of the input [stream].
+ ///
+ /// "Reasonable time" depends on the transformer and stream. Some transformers,
+ /// like a "timeout" transformer, might make these operations depend on a
+ /// duration. Others might not delay them at all, or just by a microtask.
+ ///
+ /// Transformers are free to handle errors in any way.
+ /// A transformer implementation may choose to propagate errors,
+ /// or convert them to other events, or ignore them completely,
+ /// but if errors are ignored, it should be documented explicitly.
Stream<T> bind(Stream<S> stream);
- /**
- * Provides a `StreamTransformer<RS, RT>` view of this stream transformer.
- *
- * The resulting transformer will check at run-time that all data events
- * of the stream it transforms are actually instances of [S],
- * and it will check that all data events produced by this transformer
- * are actually instances of [RT].
- */
+ /// Provides a `StreamTransformer<RS, RT>` view of this stream transformer.
+ ///
+ /// The resulting transformer will check at run-time that all data events
+ /// of the stream it transforms are actually instances of [S],
+ /// and it will check that all data events produced by this transformer
+ /// are actually instances of [RT].
StreamTransformer<RS, RT> cast<RS, RT>();
}
-/**
- * Base class for implementing [StreamTransformer].
- *
- * Contains default implementations of every method except [bind].
- */
+/// Base class for implementing [StreamTransformer].
+///
+/// Contains default implementations of every method except [bind].
abstract class StreamTransformerBase<S, T> implements StreamTransformer<S, T> {
const StreamTransformerBase();
@@ -2211,80 +2057,71 @@
StreamTransformer.castFrom<S, T, RS, RT>(this);
}
-/**
- * An [Iterator]-like interface for the values of a [Stream].
- *
- * This wraps a [Stream] and a subscription on the stream. It listens
- * on the stream, and completes the future returned by [moveNext] when the
- * next value becomes available.
- *
- * The stream may be paused between calls to [moveNext].
- *
- * The [current] value must only be used after a future returned by [moveNext]
- * has completed with `true`, and only until [moveNext] is called again.
- */
+/// An [Iterator]-like interface for the values of a [Stream].
+///
+/// This wraps a [Stream] and a subscription on the stream. It listens
+/// on the stream, and completes the future returned by [moveNext] when the
+/// next value becomes available.
+///
+/// The stream may be paused between calls to [moveNext].
+///
+/// The [current] value must only be used after a future returned by [moveNext]
+/// has completed with `true`, and only until [moveNext] is called again.
abstract class StreamIterator<T> {
- /** Create a [StreamIterator] on [stream]. */
+ /// Create a [StreamIterator] on [stream].
factory StreamIterator(Stream<T> stream) =>
// TODO(lrn): use redirecting factory constructor when type
// arguments are supported.
new _StreamIterator<T>(stream);
- /**
- * Wait for the next stream value to be available.
- *
- * Returns a future which will complete with either `true` or `false`.
- * Completing with `true` means that another event has been received and
- * can be read as [current].
- * Completing with `false` means that the stream iteration is done and
- * no further events will ever be available.
- * The future may complete with an error, if the stream produces an error,
- * which also ends iteration.
- *
- * The function must not be called again until the future returned by a
- * previous call is completed.
- */
+ /// Wait for the next stream value to be available.
+ ///
+ /// Returns a future which will complete with either `true` or `false`.
+ /// Completing with `true` means that another event has been received and
+ /// can be read as [current].
+ /// Completing with `false` means that the stream iteration is done and
+ /// no further events will ever be available.
+ /// The future may complete with an error, if the stream produces an error,
+ /// which also ends iteration.
+ ///
+ /// The function must not be called again until the future returned by a
+ /// previous call is completed.
Future<bool> moveNext();
- /**
- * The current value of the stream.
- *
- * When a [moveNext] call completes with `true`, the [current] field holds
- * the most recent event of the stream, and it stays like that until the next
- * call to [moveNext]. This value must only be read after a call to [moveNext]
- * has completed with `true`, and only until the [moveNext] is called again.
- *
- * If the StreamIterator has not yet been moved to the first element
- * ([moveNext] has not been called and completed yet), or if the
- * StreamIterator has been moved past the last element ([moveNext] has
- * returned `false`), then [current] is unspecified. A [StreamIterator] may
- * either throw or return an iterator-specific default value in that case.
- */
+ /// The current value of the stream.
+ ///
+ /// When a [moveNext] call completes with `true`, the [current] field holds
+ /// the most recent event of the stream, and it stays like that until the next
+ /// call to [moveNext]. This value must only be read after a call to [moveNext]
+ /// has completed with `true`, and only until the [moveNext] is called again.
+ ///
+ /// If the StreamIterator has not yet been moved to the first element
+ /// ([moveNext] has not been called and completed yet), or if the
+ /// StreamIterator has been moved past the last element ([moveNext] has
+ /// returned `false`), then [current] is unspecified. A [StreamIterator] may
+ /// either throw or return an iterator-specific default value in that case.
T get current;
- /**
- * Cancels the stream iterator (and the underlying stream subscription) early.
- *
- * The stream iterator is automatically canceled if the [moveNext] future
- * completes with either `false` or an error.
- *
- * If you need to stop listening for values before the stream iterator is
- * automatically closed, you must call [cancel] to ensure that the stream
- * is properly closed.
- *
- * If [moveNext] has been called when the iterator is canceled,
- * its returned future will complete with `false` as value,
- * as will all further calls to [moveNext].
- *
- * Returns a future if the cancel-operation is not completed synchronously.
- * Otherwise returns `null`.
- */
+ /// Cancels the stream iterator (and the underlying stream subscription) early.
+ ///
+ /// The stream iterator is automatically canceled if the [moveNext] future
+ /// completes with either `false` or an error.
+ ///
+ /// If you need to stop listening for values before the stream iterator is
+ /// automatically closed, you must call [cancel] to ensure that the stream
+ /// is properly closed.
+ ///
+ /// If [moveNext] has been called when the iterator is canceled,
+ /// its returned future will complete with `false` as value,
+ /// as will all further calls to [moveNext].
+ ///
+ /// Returns a future which completes when the cancellation is complete.
+ /// This can be an already completed future if the cancellation happens
+ /// synchronously.
Future cancel();
}
-/**
- * Wraps an [_EventSink] so it exposes only the [EventSink] interface.
- */
+/// Wraps an [_EventSink] so it exposes only the [EventSink] interface.
class _ControllerEventSinkWrapper<T> implements EventSink<T> {
EventSink? _sink;
_ControllerEventSinkWrapper(this._sink);
@@ -2308,50 +2145,42 @@
}
}
-/**
- * An enhanced stream controller provided by [Stream.multi].
- *
- * Acts like a normal asynchronous controller, but also allows
- * adding events synchronously.
- * As with any synchronous event delivery, the sender should be very careful
- * to not deliver events at times when a new listener might not
- * be ready to receive them.
- * That generally means only delivering events synchronously in response to other
- * asynchronous events, because that is a time when an asynchronous event could
- * happen.
- */
+/// An enhanced stream controller provided by [Stream.multi].
+///
+/// Acts like a normal asynchronous controller, but also allows
+/// adding events synchronously.
+/// As with any synchronous event delivery, the sender should be very careful
+/// to not deliver events at times when a new listener might not
+/// be ready to receive them.
+/// That generally means only delivering events synchronously in response to other
+/// asynchronous events, because that is a time when an asynchronous event could
+/// happen.
@Since("2.9")
abstract class MultiStreamController<T> implements StreamController<T> {
- /**
- * Adds and delivers an event.
- *
- * Adds an event like [add] and attempts to deliver it immediately.
- * Delivery can be delayed if other previously added events are
- * still pending delivery, if the subscription is paused,
- * or if the subscription isn't listening yet.
- */
+ /// Adds and delivers an event.
+ ///
+ /// Adds an event like [add] and attempts to deliver it immediately.
+ /// Delivery can be delayed if other previously added events are
+ /// still pending delivery, if the subscription is paused,
+ /// or if the subscription isn't listening yet.
void addSync(T value);
- /**
- * Adds and delivers an error event.
- *
- * Adds an error like [addError] and attempts to deliver it immediately.
- * Delivery can be delayed if other previously added events are
- * still pending delivery, if the subscription is paused,
- * or if the subscription isn't listening yet.
- */
+ /// Adds and delivers an error event.
+ ///
+ /// Adds an error like [addError] and attempts to deliver it immediately.
+ /// Delivery can be delayed if other previously added events are
+ /// still pending delivery, if the subscription is paused,
+ /// or if the subscription isn't listening yet.
void addErrorSync(Object error, [StackTrace? stackTrace]);
- /**
- * Closes the controller and delivers a done event.
- *
- * Closes the controller like [close] and attempts to deliver a "done"
- * event immediately.
- * Delivery can be delayed if other previously added events are
- * still pending delivery, if the subscription is paused,
- * or if the subscription isn't listening yet.
- * If it's necessary to know whether the "done" event has been delievered,
- * [done] future will complete when that has happened.
- */
+ /// Closes the controller and delivers a done event.
+ ///
+ /// Closes the controller like [close] and attempts to deliver a "done"
+ /// event immediately.
+ /// Delivery can be delayed if other previously added events are
+ /// still pending delivery, if the subscription is paused,
+ /// or if the subscription isn't listening yet.
+ /// If it's necessary to know whether the "done" event has been delievered,
+ /// [done] future will complete when that has happened.
void closeSync();
}
diff --git a/sdk/lib/async/stream_controller.dart b/sdk/lib/async/stream_controller.dart
index dbaacc1..981cfb2 100644
--- a/sdk/lib/async/stream_controller.dart
+++ b/sdk/lib/async/stream_controller.dart
@@ -8,71 +8,63 @@
// Controller for creating and adding events to a stream.
// -------------------------------------------------------------------
-/**
- * Type of a stream controller's `onListen`, `onPause` and `onResume` callbacks.
- */
+/// Type of a stream controller's `onListen`, `onPause` and `onResume` callbacks.
typedef void ControllerCallback();
-/**
- * Type of stream controller `onCancel` callbacks.
- */
+/// Type of stream controller `onCancel` callbacks.
typedef FutureOr<void> ControllerCancelCallback();
-/**
- * A controller with the stream it controls.
- *
- * This controller allows sending data, error and done events on
- * its [stream].
- * This class can be used to create a simple stream that others
- * can listen on, and to push events to that stream.
- *
- * It's possible to check whether the stream is paused or not, and whether
- * it has subscribers or not, as well as getting a callback when either of
- * these change.
- */
+/// A controller with the stream it controls.
+///
+/// This controller allows sending data, error and done events on
+/// its [stream].
+/// This class can be used to create a simple stream that others
+/// can listen on, and to push events to that stream.
+///
+/// It's possible to check whether the stream is paused or not, and whether
+/// it has subscribers or not, as well as getting a callback when either of
+/// these change.
abstract class StreamController<T> implements StreamSink<T> {
- /** The stream that this controller is controlling. */
+ /// The stream that this controller is controlling.
Stream<T> get stream;
- /**
- * A controller with a [stream] that supports only one single subscriber.
- *
- * If [sync] is true, the returned stream controller is a
- * [SynchronousStreamController], and must be used with the care
- * and attention necessary to not break the [Stream] contract. If in doubt,
- * use the non-sync version.
- *
- * Using an asynchronous controller will never give the wrong
- * behavior, but using a synchronous controller incorrectly can cause
- * otherwise correct programs to break.
- *
- * A synchronous controller is only intended for optimizing event
- * propagation when one asynchronous event immediately triggers another.
- * It should not be used unless the calls to [add] or [addError]
- * are guaranteed to occur in places where it won't break `Stream` invariants.
- *
- * Use synchronous controllers only to forward (potentially transformed)
- * events from another stream or a future.
- *
- * A Stream should be inert until a subscriber starts listening on it (using
- * the [onListen] callback to start producing events). Streams should not
- * leak resources (like websockets) when no user ever listens on the stream.
- *
- * The controller buffers all incoming events until a subscriber is
- * registered, but this feature should only be used in rare circumstances.
- *
- * The [onPause] function is called when the stream becomes
- * paused. [onResume] is called when the stream resumed.
- *
- * The [onListen] callback is called when the stream
- * receives its listener and [onCancel] when the listener ends
- * its subscription. If [onCancel] needs to perform an asynchronous operation,
- * [onCancel] should return a future that completes when the cancel operation
- * is done.
- *
- * If the stream is canceled before the controller needs data the
- * [onResume] call might not be executed.
- */
+ /// A controller with a [stream] that supports only one single subscriber.
+ ///
+ /// If [sync] is true, the returned stream controller is a
+ /// [SynchronousStreamController], and must be used with the care
+ /// and attention necessary to not break the [Stream] contract. If in doubt,
+ /// use the non-sync version.
+ ///
+ /// Using an asynchronous controller will never give the wrong
+ /// behavior, but using a synchronous controller incorrectly can cause
+ /// otherwise correct programs to break.
+ ///
+ /// A synchronous controller is only intended for optimizing event
+ /// propagation when one asynchronous event immediately triggers another.
+ /// It should not be used unless the calls to [add] or [addError]
+ /// are guaranteed to occur in places where it won't break `Stream` invariants.
+ ///
+ /// Use synchronous controllers only to forward (potentially transformed)
+ /// events from another stream or a future.
+ ///
+ /// A Stream should be inert until a subscriber starts listening on it (using
+ /// the [onListen] callback to start producing events). Streams should not
+ /// leak resources (like websockets) when no user ever listens on the stream.
+ ///
+ /// The controller buffers all incoming events until a subscriber is
+ /// registered, but this feature should only be used in rare circumstances.
+ ///
+ /// The [onPause] function is called when the stream becomes
+ /// paused. [onResume] is called when the stream resumed.
+ ///
+ /// The [onListen] callback is called when the stream
+ /// receives its listener and [onCancel] when the listener ends
+ /// its subscription. If [onCancel] needs to perform an asynchronous operation,
+ /// [onCancel] should return a future that completes when the cancel operation
+ /// is done.
+ ///
+ /// If the stream is canceled before the controller needs data the
+ /// [onResume] call might not be executed.
factory StreamController(
{void onListen()?,
void onPause()?,
@@ -84,57 +76,55 @@
: _AsyncStreamController<T>(onListen, onPause, onResume, onCancel);
}
- /**
- * A controller where [stream] can be listened to more than once.
- *
- * The [Stream] returned by [stream] is a broadcast stream.
- * It can be listened to more than once.
- *
- * A Stream should be inert until a subscriber starts listening on it (using
- * the [onListen] callback to start producing events). Streams should not
- * leak resources (like websockets) when no user ever listens on the stream.
- *
- * Broadcast streams do not buffer events when there is no listener.
- *
- * The controller distributes any events to all currently subscribed
- * listeners at the time when [add], [addError] or [close] is called.
- * It is not allowed to call `add`, `addError`, or `close` before a previous
- * call has returned. The controller does not have any internal queue of
- * events, and if there are no listeners at the time the event is added,
- * it will just be dropped, or, if it is an error, be reported as uncaught.
- *
- * Each listener subscription is handled independently,
- * and if one pauses, only the pausing listener is affected.
- * A paused listener will buffer events internally until unpaused or canceled.
- *
- * If [sync] is true, events may be fired directly by the stream's
- * subscriptions during an [add], [addError] or [close] call.
- * The returned stream controller is a [SynchronousStreamController],
- * and must be used with the care and attention necessary to not break
- * the [Stream] contract.
- * See [Completer.sync] for some explanations on when a synchronous
- * dispatching can be used.
- * If in doubt, keep the controller non-sync.
- *
- * If [sync] is false, the event will always be fired at a later time,
- * after the code adding the event has completed.
- * In that case, no guarantees are given with regard to when
- * multiple listeners get the events, except that each listener will get
- * all events in the correct order. Each subscription handles the events
- * individually.
- * If two events are sent on an async controller with two listeners,
- * one of the listeners may get both events
- * before the other listener gets any.
- * A listener must be subscribed both when the event is initiated
- * (that is, when [add] is called)
- * and when the event is later delivered,
- * in order to receive the event.
- *
- * The [onListen] callback is called when the first listener is subscribed,
- * and the [onCancel] is called when there are no longer any active listeners.
- * If a listener is added again later, after the [onCancel] was called,
- * the [onListen] will be called again.
- */
+ /// A controller where [stream] can be listened to more than once.
+ ///
+ /// The [Stream] returned by [stream] is a broadcast stream.
+ /// It can be listened to more than once.
+ ///
+ /// A Stream should be inert until a subscriber starts listening on it (using
+ /// the [onListen] callback to start producing events). Streams should not
+ /// leak resources (like websockets) when no user ever listens on the stream.
+ ///
+ /// Broadcast streams do not buffer events when there is no listener.
+ ///
+ /// The controller distributes any events to all currently subscribed
+ /// listeners at the time when [add], [addError] or [close] is called.
+ /// It is not allowed to call `add`, `addError`, or `close` before a previous
+ /// call has returned. The controller does not have any internal queue of
+ /// events, and if there are no listeners at the time the event is added,
+ /// it will just be dropped, or, if it is an error, be reported as uncaught.
+ ///
+ /// Each listener subscription is handled independently,
+ /// and if one pauses, only the pausing listener is affected.
+ /// A paused listener will buffer events internally until unpaused or canceled.
+ ///
+ /// If [sync] is true, events may be fired directly by the stream's
+ /// subscriptions during an [add], [addError] or [close] call.
+ /// The returned stream controller is a [SynchronousStreamController],
+ /// and must be used with the care and attention necessary to not break
+ /// the [Stream] contract.
+ /// See [Completer.sync] for some explanations on when a synchronous
+ /// dispatching can be used.
+ /// If in doubt, keep the controller non-sync.
+ ///
+ /// If [sync] is false, the event will always be fired at a later time,
+ /// after the code adding the event has completed.
+ /// In that case, no guarantees are given with regard to when
+ /// multiple listeners get the events, except that each listener will get
+ /// all events in the correct order. Each subscription handles the events
+ /// individually.
+ /// If two events are sent on an async controller with two listeners,
+ /// one of the listeners may get both events
+ /// before the other listener gets any.
+ /// A listener must be subscribed both when the event is initiated
+ /// (that is, when [add] is called)
+ /// and when the event is later delivered,
+ /// in order to receive the event.
+ ///
+ /// The [onListen] callback is called when the first listener is subscribed,
+ /// and the [onCancel] is called when there are no longer any active listeners.
+ /// If a listener is added again later, after the [onCancel] was called,
+ /// the [onListen] will be called again.
factory StreamController.broadcast(
{void onListen()?, void onCancel()?, bool sync = false}) {
return sync
@@ -142,259 +132,219 @@
: _AsyncBroadcastStreamController<T>(onListen, onCancel);
}
- /**
- * The callback which is called when the stream is listened to.
- *
- * May be set to `null`, in which case no callback will happen.
- */
- void Function()? get onListen;
+ /// The callback which is called when the stream is listened to.
+ ///
+ /// May be set to `null`, in which case no callback will happen.
+ abstract void Function()? onListen;
- void set onListen(void onListenHandler()?);
+ /// The callback which is called when the stream is paused.
+ ///
+ /// May be set to `null`, in which case no callback will happen.
+ ///
+ /// Pause related callbacks are not supported on broadcast stream controllers.
+ abstract void Function()? onPause;
- /**
- * The callback which is called when the stream is paused.
- *
- * May be set to `null`, in which case no callback will happen.
- *
- * Pause related callbacks are not supported on broadcast stream controllers.
- */
- void Function()? get onPause;
+ /// The callback which is called when the stream is resumed.
+ ///
+ /// May be set to `null`, in which case no callback will happen.
+ ///
+ /// Pause related callbacks are not supported on broadcast stream controllers.
+ abstract void Function()? onResume;
- void set onPause(void onPauseHandler()?);
+ /// The callback which is called when the stream is canceled.
+ ///
+ /// May be set to `null`, in which case no callback will happen.
+ abstract FutureOr<void> Function()? onCancel;
- /**
- * The callback which is called when the stream is resumed.
- *
- * May be set to `null`, in which case no callback will happen.
- *
- * Pause related callbacks are not supported on broadcast stream controllers.
- */
- void Function()? get onResume;
-
- void set onResume(void onResumeHandler()?);
-
- /**
- * The callback which is called when the stream is canceled.
- *
- * May be set to `null`, in which case no callback will happen.
- */
- FutureOr<void> Function()? get onCancel;
-
- void set onCancel(FutureOr<void> onCancelHandler()?);
-
- /**
- * Returns a view of this object that only exposes the [StreamSink] interface.
- */
+ /// Returns a view of this object that only exposes the [StreamSink] interface.
StreamSink<T> get sink;
- /**
- * Whether the stream controller is closed for adding more events.
- *
- * The controller becomes closed by calling the [close] method.
- * New events cannot be added, by calling [add] or [addError],
- * to a closed controller.
- *
- * If the controller is closed,
- * the "done" event might not have been delivered yet,
- * but it has been scheduled, and it is too late to add more events.
- */
+ /// Whether the stream controller is closed for adding more events.
+ ///
+ /// The controller becomes closed by calling the [close] method.
+ /// New events cannot be added, by calling [add] or [addError],
+ /// to a closed controller.
+ ///
+ /// If the controller is closed,
+ /// the "done" event might not have been delivered yet,
+ /// but it has been scheduled, and it is too late to add more events.
bool get isClosed;
- /**
- * Whether the subscription would need to buffer events.
- *
- * This is the case if the controller's stream has a listener and it is
- * paused, or if it has not received a listener yet. In that case, the
- * controller is considered paused as well.
- *
- * A broadcast stream controller is never considered paused. It always
- * forwards its events to all uncanceled subscriptions, if any,
- * and let the subscriptions handle their own pausing and buffering.
- */
+ /// Whether the subscription would need to buffer events.
+ ///
+ /// This is the case if the controller's stream has a listener and it is
+ /// paused, or if it has not received a listener yet. In that case, the
+ /// controller is considered paused as well.
+ ///
+ /// A broadcast stream controller is never considered paused. It always
+ /// forwards its events to all uncanceled subscriptions, if any,
+ /// and let the subscriptions handle their own pausing and buffering.
bool get isPaused;
- /** Whether there is a subscriber on the [Stream]. */
+ /// Whether there is a subscriber on the [Stream].
bool get hasListener;
- /**
- * Sends a data [event].
- *
- * Listeners receive this event in a later microtask.
- *
- * Note that a synchronous controller (created by passing true to the `sync`
- * parameter of the `StreamController` constructor) delivers events
- * immediately. Since this behavior violates the contract mentioned here,
- * synchronous controllers should only be used as described in the
- * documentation to ensure that the delivered events always *appear* as if
- * they were delivered in a separate microtask.
- */
+ /// Sends a data [event].
+ ///
+ /// Listeners receive this event in a later microtask.
+ ///
+ /// Note that a synchronous controller (created by passing true to the `sync`
+ /// parameter of the `StreamController` constructor) delivers events
+ /// immediately. Since this behavior violates the contract mentioned here,
+ /// synchronous controllers should only be used as described in the
+ /// documentation to ensure that the delivered events always *appear* as if
+ /// they were delivered in a separate microtask.
void add(T event);
- /**
- * Sends or enqueues an error event.
- *
- * If [error] is `null`, it is replaced by a [NullThrownError].
- *
- * Listeners receive this event at a later microtask. This behavior can be
- * overridden by using `sync` controllers. Note, however, that sync
- * controllers have to satisfy the preconditions mentioned in the
- * documentation of the constructors.
- */
+ /// Sends or enqueues an error event.
+ ///
+ /// If [error] is `null`, it is replaced by a [NullThrownError].
+ ///
+ /// Listeners receive this event at a later microtask. This behavior can be
+ /// overridden by using `sync` controllers. Note, however, that sync
+ /// controllers have to satisfy the preconditions mentioned in the
+ /// documentation of the constructors.
void addError(Object error, [StackTrace? stackTrace]);
- /**
- * Closes the stream.
- *
- * No further events can be added to a closed stream.
- *
- * The returned future is the same future provided by [done].
- * It is completed when the stream listeners is done sending events,
- * This happens either when the done event has been sent,
- * or when the subscriber on a single-subscription stream is canceled.
- *
- * A broadcast stream controller will send the done event
- * even if listeners are paused, so some broadcast events may not have been
- * received yet when the returned future completes.
- *
- * If noone listens to a non-broadcast stream,
- * or the listener pauses and never resumes,
- * the done event will not be sent and this future will never complete.
- */
+ /// Closes the stream.
+ ///
+ /// No further events can be added to a closed stream.
+ ///
+ /// The returned future is the same future provided by [done].
+ /// It is completed when the stream listeners is done sending events,
+ /// This happens either when the done event has been sent,
+ /// or when the subscriber on a single-subscription stream is canceled.
+ ///
+ /// A broadcast stream controller will send the done event
+ /// even if listeners are paused, so some broadcast events may not have been
+ /// received yet when the returned future completes.
+ ///
+ /// If noone listens to a non-broadcast stream,
+ /// or the listener pauses and never resumes,
+ /// the done event will not be sent and this future will never complete.
Future close();
- /**
- * A future which is completed when the stream controller is done
- * sending events.
- *
- * This happens either when the done event has been sent, or if the
- * subscriber on a single-subscription stream is canceled.
- *
- * A broadcast stream controller will send the done event
- * even if listeners are paused, so some broadcast events may not have been
- * received yet when the returned future completes.
- *
- * If there is no listener on a non-broadcast stream,
- * or the listener pauses and never resumes,
- * the done event will not be sent and this future will never complete.
- */
+ /// A future which is completed when the stream controller is done
+ /// sending events.
+ ///
+ /// This happens either when the done event has been sent, or if the
+ /// subscriber on a single-subscription stream is canceled.
+ ///
+ /// A broadcast stream controller will send the done event
+ /// even if listeners are paused, so some broadcast events may not have been
+ /// received yet when the returned future completes.
+ ///
+ /// If there is no listener on a non-broadcast stream,
+ /// or the listener pauses and never resumes,
+ /// the done event will not be sent and this future will never complete.
Future get done;
- /**
- * Receives events from [source] and puts them into this controller's stream.
- *
- * Returns a future which completes when the source stream is done.
- *
- * Events must not be added directly to this controller using [add],
- * [addError], [close] or [addStream], until the returned future
- * is complete.
- *
- * Data and error events are forwarded to this controller's stream. A done
- * event on the source will end the `addStream` operation and complete the
- * returned future.
- *
- * If [cancelOnError] is true, only the first error on [source] is
- * forwarded to the controller's stream, and the `addStream` ends
- * after this. If [cancelOnError] is false, all errors are forwarded
- * and only a done event will end the `addStream`.
- * If [cancelOnError] is omitted or `null`, it defaults to false.
- */
+ /// Receives events from [source] and puts them into this controller's stream.
+ ///
+ /// Returns a future which completes when the source stream is done.
+ ///
+ /// Events must not be added directly to this controller using [add],
+ /// [addError], [close] or [addStream], until the returned future
+ /// is complete.
+ ///
+ /// Data and error events are forwarded to this controller's stream. A done
+ /// event on the source will end the `addStream` operation and complete the
+ /// returned future.
+ ///
+ /// If [cancelOnError] is `true`, only the first error on [source] is
+ /// forwarded to the controller's stream, and the `addStream` ends
+ /// after this. If [cancelOnError] is false, all errors are forwarded
+ /// and only a done event will end the `addStream`.
+ /// If [cancelOnError] is omitted or `null`, it defaults to `false`.
Future addStream(Stream<T> source, {bool? cancelOnError});
}
-/**
- * A stream controller that delivers its events synchronously.
- *
- * A synchronous stream controller is intended for cases where
- * an already asynchronous event triggers an event on a stream.
- *
- * Instead of adding the event to the stream in a later microtask,
- * causing extra latency, the event is instead fired immediately by the
- * synchronous stream controller, as if the stream event was
- * the current event or microtask.
- *
- * The synchronous stream controller can be used to break the contract
- * on [Stream], and it must be used carefully to avoid doing so.
- *
- * The only advantage to using a [SynchronousStreamController] over a
- * normal [StreamController] is the improved latency.
- * Only use the synchronous version if the improvement is significant,
- * and if its use is safe. Otherwise just use a normal stream controller,
- * which will always have the correct behavior for a [Stream], and won't
- * accidentally break other code.
- *
- * Adding events to a synchronous controller should only happen as the
- * very last part of the handling of the original event.
- * At that point, adding an event to the stream is equivalent to
- * returning to the event loop and adding the event in the next microtask.
- *
- * Each listener callback will be run as if it was a top-level event
- * or microtask. This means that if it throws, the error will be reported as
- * uncaught as soon as possible.
- * This is one reason to add the event as the last thing in the original event
- * handler - any action done after adding the event will delay the report of
- * errors in the event listener callbacks.
- *
- * If an event is added in a setting that isn't known to be another event,
- * it may cause the stream's listener to get that event before the listener
- * is ready to handle it. We promise that after calling [Stream.listen],
- * you won't get any events until the code doing the listen has completed.
- * Calling [add] in response to a function call of unknown origin may break
- * that promise.
- *
- * An [onListen] callback from the controller is *not* an asynchronous event,
- * and adding events to the controller in the `onListen` callback is always
- * wrong. The events will be delivered before the listener has even received
- * the subscription yet.
- *
- * The synchronous broadcast stream controller also has a restrictions that a
- * normal stream controller does not:
- * The [add], [addError], [close] and [addStream] methods *must not* be
- * called while an event is being delivered.
- * That is, if a callback on a subscription on the controller's stream causes
- * a call to any of the functions above, the call will fail.
- * A broadcast stream may have more than one listener, and if an
- * event is added synchronously while another is being also in the process
- * of being added, the latter event might reach some listeners before
- * the former. To prevent that, an event cannot be added while a previous
- * event is being fired.
- * This guarantees that an event is fully delivered when the
- * first [add], [addError] or [close] returns,
- * and further events will be delivered in the correct order.
- *
- * This still only guarantees that the event is delivered to the subscription.
- * If the subscription is paused, the actual callback may still happen later,
- * and the event will instead be buffered by the subscription.
- * Barring pausing, and the following buffered events that haven't been
- * delivered yet, callbacks will be called synchronously when an event is added.
- *
- * Adding an event to a synchronous non-broadcast stream controller while
- * another event is in progress may cause the second event to be delayed
- * and not be delivered synchronously, and until that event is delivered,
- * the controller will not act synchronously.
- */
+/// A stream controller that delivers its events synchronously.
+///
+/// A synchronous stream controller is intended for cases where
+/// an already asynchronous event triggers an event on a stream.
+///
+/// Instead of adding the event to the stream in a later microtask,
+/// causing extra latency, the event is instead fired immediately by the
+/// synchronous stream controller, as if the stream event was
+/// the current event or microtask.
+///
+/// The synchronous stream controller can be used to break the contract
+/// on [Stream], and it must be used carefully to avoid doing so.
+///
+/// The only advantage to using a [SynchronousStreamController] over a
+/// normal [StreamController] is the improved latency.
+/// Only use the synchronous version if the improvement is significant,
+/// and if its use is safe. Otherwise just use a normal stream controller,
+/// which will always have the correct behavior for a [Stream], and won't
+/// accidentally break other code.
+///
+/// Adding events to a synchronous controller should only happen as the
+/// very last part of the handling of the original event.
+/// At that point, adding an event to the stream is equivalent to
+/// returning to the event loop and adding the event in the next microtask.
+///
+/// Each listener callback will be run as if it was a top-level event
+/// or microtask. This means that if it throws, the error will be reported as
+/// uncaught as soon as possible.
+/// This is one reason to add the event as the last thing in the original event
+/// handler - any action done after adding the event will delay the report of
+/// errors in the event listener callbacks.
+///
+/// If an event is added in a setting that isn't known to be another event,
+/// it may cause the stream's listener to get that event before the listener
+/// is ready to handle it. We promise that after calling [Stream.listen],
+/// you won't get any events until the code doing the listen has completed.
+/// Calling [add] in response to a function call of unknown origin may break
+/// that promise.
+///
+/// An [onListen] callback from the controller is *not* an asynchronous event,
+/// and adding events to the controller in the `onListen` callback is always
+/// wrong. The events will be delivered before the listener has even received
+/// the subscription yet.
+///
+/// The synchronous broadcast stream controller also has a restrictions that a
+/// normal stream controller does not:
+/// The [add], [addError], [close] and [addStream] methods *must not* be
+/// called while an event is being delivered.
+/// That is, if a callback on a subscription on the controller's stream causes
+/// a call to any of the functions above, the call will fail.
+/// A broadcast stream may have more than one listener, and if an
+/// event is added synchronously while another is being also in the process
+/// of being added, the latter event might reach some listeners before
+/// the former. To prevent that, an event cannot be added while a previous
+/// event is being fired.
+/// This guarantees that an event is fully delivered when the
+/// first [add], [addError] or [close] returns,
+/// and further events will be delivered in the correct order.
+///
+/// This still only guarantees that the event is delivered to the subscription.
+/// If the subscription is paused, the actual callback may still happen later,
+/// and the event will instead be buffered by the subscription.
+/// Barring pausing, and the following buffered events that haven't been
+/// delivered yet, callbacks will be called synchronously when an event is added.
+///
+/// Adding an event to a synchronous non-broadcast stream controller while
+/// another event is in progress may cause the second event to be delayed
+/// and not be delivered synchronously, and until that event is delivered,
+/// the controller will not act synchronously.
abstract class SynchronousStreamController<T> implements StreamController<T> {
- /**
- * Adds event to the controller's stream.
- *
- * As [StreamController.add], but must not be called while an event is
- * being added by [add], [addError] or [close].
- */
+ /// Adds event to the controller's stream.
+ ///
+ /// As [StreamController.add], but must not be called while an event is
+ /// being added by [add], [addError] or [close].
void add(T data);
- /**
- * Adds error to the controller's stream.
- *
- * As [StreamController.addError], but must not be called while an event is
- * being added by [add], [addError] or [close].
- */
+ /// Adds error to the controller's stream.
+ ///
+ /// As [StreamController.addError], but must not be called while an event is
+ /// being added by [add], [addError] or [close].
void addError(Object error, [StackTrace? stackTrace]);
- /**
- * Closes the controller's stream.
- *
- * As [StreamController.close], but must not be called while an event is
- * being added by [add], [addError] or [close].
- */
+ /// Closes the controller's stream.
+ ///
+ /// As [StreamController.close], but must not be called while an event is
+ /// being added by [add], [addError] or [close].
Future close();
}
@@ -414,11 +364,9 @@
_EventSink<T>,
_EventDispatch<T> {}
-/**
- * Default implementation of [StreamController].
- *
- * Controls a stream that only supports a single controller.
- */
+/// Default implementation of [StreamController].
+///
+/// Controls a stream that only supports a single controller.
abstract class _StreamController<T> implements _StreamControllerBase<T> {
// The states are bit-flags. More than one can be set at a time.
//
@@ -430,18 +378,19 @@
// subscription, the done event is queued. If done after cancel, the done
// event is ignored (just as any other event after a cancel).
- /** The controller is in its initial state with no subscription. */
+ /// The controller is in its initial state with no subscription.
static const int _STATE_INITIAL = 0;
- /**
- * The controller has a subscription, but hasn't been closed or canceled.
- *
- * Keep in sync with
- * runtime/vm/stack_trace.cc:kStreamController_StateSubscribed.
- */
+
+ /// The controller has a subscription, but hasn't been closed or canceled.
+ ///
+ /// Keep in sync with
+ /// runtime/vm/stack_trace.cc:kStreamController_StateSubscribed.
static const int _STATE_SUBSCRIBED = 1;
- /** The subscription is canceled. */
+
+ /// The subscription is canceled.
static const int _STATE_CANCELED = 2;
- /** Mask for the subscription state. */
+
+ /// Mask for the subscription state.
static const int _STATE_SUBSCRIPTION_MASK = 3;
// The following state relate to the controller, not the subscription.
@@ -449,45 +398,38 @@
// If executing an [addStream], new events are not allowed either, but will
// be added by the stream.
- /**
- * The controller is closed due to calling [close].
- *
- * When the stream is closed, you can neither add new events nor add new
- * listeners.
- */
+ /// The controller is closed due to calling [close].
+ ///
+ /// When the stream is closed, you can neither add new events nor add new
+ /// listeners.
static const int _STATE_CLOSED = 4;
- /**
- * The controller is in the middle of an [addStream] operation.
- *
- * While adding events from a stream, no new events can be added directly
- * on the controller.
- */
+
+ /// The controller is in the middle of an [addStream] operation.
+ ///
+ /// While adding events from a stream, no new events can be added directly
+ /// on the controller.
static const int _STATE_ADDSTREAM = 8;
- /**
- * Field containing different data depending on the current subscription
- * state.
- *
- * If [_state] is [_STATE_INITIAL], the field may contain a [_PendingEvents]
- * for events added to the controller before a subscription.
- *
- * While [_state] is [_STATE_SUBSCRIBED], the field contains the subscription.
- *
- * When [_state] is [_STATE_CANCELED] the field is currently not used,
- * and will contain `null`.
- */
+ /// Field containing different data depending on the current subscription
+ /// state.
+ ///
+ /// If [_state] is [_STATE_INITIAL], the field may contain a [_PendingEvents]
+ /// for events added to the controller before a subscription.
+ ///
+ /// While [_state] is [_STATE_SUBSCRIBED], the field contains the subscription.
+ ///
+ /// When [_state] is [_STATE_CANCELED] the field is currently not used,
+ /// and will contain `null`.
@pragma("vm:entry-point")
Object? _varData;
- /** Current state of the controller. */
+ /// Current state of the controller.
@pragma("vm:entry-point")
int _state = _STATE_INITIAL;
- /**
- * Future completed when the stream sends its last event.
- *
- * This is also the future returned by [close].
- */
+ /// Future completed when the stream sends its last event.
+ ///
+ /// This is also the future returned by [close].
// TODO(lrn): Could this be stored in the varData field too, if it's not
// accessed until the call to "close"? Then we need to special case if it's
// accessed earlier, or if close is called before subscribing.
@@ -503,22 +445,18 @@
// Return a new stream every time. The streams are equal, but not identical.
Stream<T> get stream => _ControllerStream<T>(this);
- /**
- * Returns a view of this object that only exposes the [StreamSink] interface.
- */
+ /// Returns a view of this object that only exposes the [StreamSink] interface.
StreamSink<T> get sink => _StreamSinkWrapper<T>(this);
- /**
- * Whether a listener has existed and been canceled.
- *
- * After this, adding more events will be ignored.
- */
+ /// Whether a listener has existed and been canceled.
+ ///
+ /// After this, adding more events will be ignored.
bool get _isCanceled => (_state & _STATE_CANCELED) != 0;
- /** Whether there is an active listener. */
+ /// Whether there is an active listener.
bool get hasListener => (_state & _STATE_SUBSCRIBED) != 0;
- /** Whether there has not been a listener yet. */
+ /// Whether there has not been a listener yet.
bool get _isInitialState =>
(_state & _STATE_SUBSCRIPTION_MASK) == _STATE_INITIAL;
@@ -529,7 +467,7 @@
bool get _isAddingStream => (_state & _STATE_ADDSTREAM) != 0;
- /** New events may not be added after close, or during addStream. */
+ /// New events may not be added after close, or during addStream.
bool get _mayAddEvent => (_state < _STATE_CLOSED);
// Returns the pending events.
@@ -579,11 +517,9 @@
return varData as dynamic;
}
- /**
- * Creates an error describing why an event cannot be added.
- *
- * The reason, and therefore the error message, depends on the current state.
- */
+ /// Creates an error describing why an event cannot be added.
+ ///
+ /// The reason, and therefore the error message, depends on the current state.
Error _badEventState() {
if (isClosed) {
return StateError("Cannot add event after closing");
@@ -604,29 +540,23 @@
return addState.addStreamFuture;
}
- /**
- * Returns a future that is completed when the stream is done
- * processing events.
- *
- * This happens either when the done event has been sent, or if the
- * subscriber of a single-subscription stream is cancelled.
- */
+ /// Returns a future that is completed when the stream is done
+ /// processing events.
+ ///
+ /// This happens either when the done event has been sent, or if the
+ /// subscriber of a single-subscription stream is cancelled.
Future<void> get done => _ensureDoneFuture();
Future<void> _ensureDoneFuture() =>
_doneFuture ??= _isCanceled ? Future._nullFuture : _Future<void>();
- /**
- * Send or enqueue a data event.
- */
+ /// Send or enqueue a data event.
void add(T value) {
if (!_mayAddEvent) throw _badEventState();
_add(value);
}
- /**
- * Send or enqueue an error event.
- */
+ /// Send or enqueue an error event.
void addError(Object error, [StackTrace? stackTrace]) {
checkNotNullable(error, "error");
if (!_mayAddEvent) throw _badEventState();
@@ -641,20 +571,18 @@
_addError(error, stackTrace);
}
- /**
- * Closes this controller and sends a done event on the stream.
- *
- * The first time a controller is closed, a "done" event is added to its
- * stream.
- *
- * You are allowed to close the controller more than once, but only the first
- * call has any effect.
- *
- * After closing, no further events may be added using [add], [addError]
- * or [addStream].
- *
- * The returned future is completed when the done event has been delivered.
- */
+ /// Closes this controller and sends a done event on the stream.
+ ///
+ /// The first time a controller is closed, a "done" event is added to its
+ /// stream.
+ ///
+ /// You are allowed to close the controller more than once, but only the first
+ /// call has any effect.
+ ///
+ /// After closing, no further events may be added using [add], [addError]
+ /// or [addStream].
+ ///
+ /// The returned future is completed when the done event has been delivered.
Future close() {
if (isClosed) {
return _ensureDoneFuture();
@@ -892,7 +820,7 @@
}
}
-/** A class that exposes only the [StreamSink] interface of an object. */
+/// A class that exposes only the [StreamSink] interface of an object.
class _StreamSinkWrapper<T> implements StreamSink<T> {
final StreamController _target;
_StreamSinkWrapper(this._target);
@@ -911,9 +839,7 @@
Future get done => _target.done;
}
-/**
- * Object containing the state used to handle [StreamController.addStream].
- */
+/// Object containing the state used to handle [StreamController.addStream].
class _AddStreamState<T> {
// [_Future] returned by call to addStream.
final _Future addStreamFuture;
@@ -944,14 +870,12 @@
addSubscription.resume();
}
- /**
- * Stop adding the stream.
- *
- * Complete the future returned by `StreamController.addStream` when
- * the cancel is complete.
- *
- * Return a future if the cancel takes time, otherwise return `null`.
- */
+ /// Stop adding the stream.
+ ///
+ /// Complete the future returned by `StreamController.addStream` when
+ /// the cancel is complete.
+ ///
+ /// Return a future if the cancel takes time, otherwise return `null`.
Future<void> cancel() {
var cancel = addSubscription.cancel();
if (cancel == null) {
diff --git a/sdk/lib/async/stream_impl.dart b/sdk/lib/async/stream_impl.dart
index bb4a1ba..02c4504 100644
--- a/sdk/lib/async/stream_impl.dart
+++ b/sdk/lib/async/stream_impl.dart
@@ -4,83 +4,75 @@
part of dart.async;
-/** Abstract and private interface for a place to put events. */
+/// Abstract and private interface for a place to put events.
abstract class _EventSink<T> {
void _add(T data);
void _addError(Object error, StackTrace stackTrace);
void _close();
}
-/**
- * Abstract and private interface for a place to send events.
- *
- * Used by event buffering to finally dispatch the pending event, where
- * [_EventSink] is where the event first enters the stream subscription,
- * and may yet be buffered.
- */
+/// Abstract and private interface for a place to send events.
+///
+/// Used by event buffering to finally dispatch the pending event, where
+/// [_EventSink] is where the event first enters the stream subscription,
+/// and may yet be buffered.
abstract class _EventDispatch<T> {
void _sendData(T data);
void _sendError(Object error, StackTrace stackTrace);
void _sendDone();
}
-/**
- * Default implementation of stream subscription of buffering events.
- *
- * The only public methods are those of [StreamSubscription], so instances of
- * [_BufferingStreamSubscription] can be returned directly as a
- * [StreamSubscription] without exposing internal functionality.
- *
- * The [StreamController] is a public facing version of [Stream] and this class,
- * with some methods made public.
- *
- * The user interface of [_BufferingStreamSubscription] are the following
- * methods:
- *
- * * [_add]: Add a data event to the stream.
- * * [_addError]: Add an error event to the stream.
- * * [_close]: Request to close the stream.
- * * [_onCancel]: Called when the subscription will provide no more events,
- * either due to being actively canceled, or after sending a done event.
- * * [_onPause]: Called when the subscription wants the event source to pause.
- * * [_onResume]: Called when allowing new events after a pause.
- *
- * The user should not add new events when the subscription requests a paused,
- * but if it happens anyway, the subscription will enqueue the events just as
- * when new events arrive while still firing an old event.
- */
+/// Default implementation of stream subscription of buffering events.
+///
+/// The only public methods are those of [StreamSubscription], so instances of
+/// [_BufferingStreamSubscription] can be returned directly as a
+/// [StreamSubscription] without exposing internal functionality.
+///
+/// The [StreamController] is a public facing version of [Stream] and this class,
+/// with some methods made public.
+///
+/// The user interface of [_BufferingStreamSubscription] are the following
+/// methods:
+///
+/// * [_add]: Add a data event to the stream.
+/// * [_addError]: Add an error event to the stream.
+/// * [_close]: Request to close the stream.
+/// * [_onCancel]: Called when the subscription will provide no more events,
+/// either due to being actively canceled, or after sending a done event.
+/// * [_onPause]: Called when the subscription wants the event source to pause.
+/// * [_onResume]: Called when allowing new events after a pause.
+///
+/// The user should not add new events when the subscription requests a paused,
+/// but if it happens anyway, the subscription will enqueue the events just as
+/// when new events arrive while still firing an old event.
class _BufferingStreamSubscription<T>
implements StreamSubscription<T>, _EventSink<T>, _EventDispatch<T> {
- /** The `cancelOnError` flag from the `listen` call. */
+ /// The `cancelOnError` flag from the `listen` call.
static const int _STATE_CANCEL_ON_ERROR = 1;
- /**
- * Whether the "done" event has been received.
- * No further events are accepted after this.
- */
+
+ /// Whether the "done" event has been received.
+ /// No further events are accepted after this.
static const int _STATE_CLOSED = 2;
- /**
- * Set if the input has been asked not to send events.
- *
- * This is not the same as being paused, since the input will remain paused
- * after a call to [resume] if there are pending events.
- */
+
+ /// Set if the input has been asked not to send events.
+ ///
+ /// This is not the same as being paused, since the input will remain paused
+ /// after a call to [resume] if there are pending events.
static const int _STATE_INPUT_PAUSED = 4;
- /**
- * Whether the subscription has been canceled.
- *
- * Set by calling [cancel], or by handling a "done" event, or an "error" event
- * when `cancelOnError` is true.
- */
+
+ /// Whether the subscription has been canceled.
+ ///
+ /// Set by calling [cancel], or by handling a "done" event, or an "error" event
+ /// when `cancelOnError` is true.
static const int _STATE_CANCELED = 8;
- /**
- * Set when either:
- *
- * * an error is sent, and [cancelOnError] is true, or
- * * a done event is sent.
- *
- * If the subscription is canceled while _STATE_WAIT_FOR_CANCEL is set, the
- * state is unset, and no further events must be delivered.
- */
+
+ /// Set when either:
+ ///
+ /// * an error is sent, and [cancelOnError] is true, or
+ /// * a done event is sent.
+ ///
+ /// If the subscription is canceled while _STATE_WAIT_FOR_CANCEL is set, the
+ /// state is unset, and no further events must be delivered.
static const int _STATE_WAIT_FOR_CANCEL = 16;
static const int _STATE_IN_CALLBACK = 32;
static const int _STATE_HAS_PENDING = 64;
@@ -94,18 +86,16 @@
final Zone _zone;
- /** Bit vector based on state-constants above. */
+ /// Bit vector based on state-constants above.
int _state;
// TODO(floitsch): reuse another field
- /** The future [_onCancel] may return. */
+ /// The future [_onCancel] may return.
Future? _cancelFuture;
- /**
- * Queue of pending events.
- *
- * Is created when necessary, or set in constructor for preconfigured events.
- */
+ /// Queue of pending events.
+ ///
+ /// Is created when necessary, or set in constructor for preconfigured events.
_PendingEvents<T>? _pending;
_BufferingStreamSubscription(void onData(T data)?, Function? onError,
@@ -119,12 +109,10 @@
_onError = _registerErrorHandler(_zone, onError),
_onDone = _registerDoneHandler(_zone, onDone);
- /**
- * Sets the subscription's pending events object.
- *
- * This can only be done once. The pending events object is used for the
- * rest of the subscription's life cycle.
- */
+ /// Sets the subscription's pending events object.
+ ///
+ /// This can only be done once. The pending events object is used for the
+ /// rest of the subscription's life cycle.
void _setPendingEvents(_PendingEvents<T>? pendingEvents) {
assert(_pending == null);
if (pendingEvents == null) return;
@@ -264,13 +252,11 @@
_cancelFuture = _onCancel();
}
- /**
- * Decrements the pause count.
- *
- * Does not automatically unpause the input (call [_onResume]) when
- * the pause count reaches zero. This is handled elsewhere, and only
- * if there are no pending events buffered.
- */
+ /// Decrements the pause count.
+ ///
+ /// Does not automatically unpause the input (call [_onResume]) when
+ /// the pause count reaches zero. This is handled elsewhere, and only
+ /// if there are no pending events buffered.
void _decrementPauseCount() {
assert(_isPaused);
_state -= _STATE_PAUSE_COUNT;
@@ -327,12 +313,10 @@
// Handle pending events.
- /**
- * Add a pending event.
- *
- * If the subscription is not paused, this also schedules a firing
- * of pending events later (if necessary).
- */
+ /// Add a pending event.
+ ///
+ /// If the subscription is not paused, this also schedules a firing
+ /// of pending events later (if necessary).
void _addPending(_DelayedEvent event) {
_StreamImplEvents<T>? pending = _pending as dynamic;
pending ??= _StreamImplEvents<T>();
@@ -421,13 +405,11 @@
}
}
- /**
- * Call a hook function.
- *
- * The call is properly wrapped in code to avoid other callbacks
- * during the call, and it checks for state changes after the call
- * that should cause further callbacks.
- */
+ /// Call a hook function.
+ ///
+ /// The call is properly wrapped in code to avoid other callbacks
+ /// during the call, and it checks for state changes after the call
+ /// that should cause further callbacks.
void _guardCallback(void Function() callback) {
assert(!_inCallback);
bool wasInputPaused = _isInputPaused;
@@ -437,16 +419,14 @@
_checkState(wasInputPaused);
}
- /**
- * Check if the input needs to be informed of state changes.
- *
- * State changes are pausing, resuming and canceling.
- *
- * After canceling, no further callbacks will happen.
- *
- * The cancel callback is called after a user cancel, or after
- * the final done event is sent.
- */
+ /// Check if the input needs to be informed of state changes.
+ ///
+ /// State changes are pausing, resuming and canceling.
+ ///
+ /// After canceling, no further callbacks will happen.
+ ///
+ /// The cancel callback is called after a user cancel, or after
+ /// the final done event is sent.
void _checkState(bool wasInputPaused) {
assert(!_inCallback);
if (_hasPending && _pending!.isEmpty) {
@@ -496,29 +476,28 @@
}
// -------------------------------------------------------------------
- /** Create a subscription object. Called by [subcribe]. */
+ /// Create a subscription object. Called by [subcribe].
StreamSubscription<T> _createSubscription(void onData(T data)?,
Function? onError, void onDone()?, bool cancelOnError) {
return new _BufferingStreamSubscription<T>(
onData, onError, onDone, cancelOnError);
}
- /** Hook called when the subscription has been created. */
+ /// Hook called when the subscription has been created.
void _onListen(StreamSubscription subscription) {}
}
typedef _PendingEvents<T> _EventGenerator<T>();
-/** Stream that generates its own events. */
+/// Stream that generates its own events.
class _GeneratedStreamImpl<T> extends _StreamImpl<T> {
final _EventGenerator<T> _pending;
bool _isUsed = false;
- /**
- * Initializes the stream to have only the events provided by a
- * [_PendingEvents].
- *
- * A new [_PendingEvents] must be generated for each listen.
- */
+
+ /// Initializes the stream to have only the events provided by a
+ /// [_PendingEvents].
+ ///
+ /// A new [_PendingEvents] must be generated for each listen.
_GeneratedStreamImpl(this._pending);
StreamSubscription<T> _createSubscription(void onData(T data)?,
@@ -531,7 +510,7 @@
}
}
-/** Pending events object that gets its events from an [Iterable]. */
+/// Pending events object that gets its events from an [Iterable].
class _IterablePendingEvents<T> extends _PendingEvents<T> {
// The iterator providing data for data events.
// Set to null when iteration has completed.
@@ -584,26 +563,27 @@
typedef void _DataHandler<T>(T value);
typedef void _DoneHandler();
-/** Default data handler, does nothing. */
+/// Default data handler, does nothing.
void _nullDataHandler(dynamic value) {}
-/** Default error handler, reports the error to the current zone's handler. */
+/// Default error handler, reports the error to the current zone's handler.
void _nullErrorHandler(Object error, StackTrace stackTrace) {
Zone.current.handleUncaughtError(error, stackTrace);
}
-/** Default done handler, does nothing. */
+/// Default done handler, does nothing.
void _nullDoneHandler() {}
-/** A delayed event on a buffering stream subscription. */
+/// A delayed event on a buffering stream subscription.
abstract class _DelayedEvent<T> {
- /** Added as a linked list on the [StreamController]. */
+ /// Added as a linked list on the [StreamController].
_DelayedEvent? next;
- /** Execute the delayed event on the [StreamController]. */
+
+ /// Execute the delayed event on the [StreamController].
void perform(_EventDispatch<T> dispatch);
}
-/** A delayed data event. */
+/// A delayed data event.
class _DelayedData<T> extends _DelayedEvent<T> {
final T value;
_DelayedData(this.value);
@@ -612,7 +592,7 @@
}
}
-/** A delayed error event. */
+/// A delayed error event.
class _DelayedError extends _DelayedEvent {
final Object error;
final StackTrace stackTrace;
@@ -623,7 +603,7 @@
}
}
-/** A delayed done event. */
+/// A delayed done event.
class _DelayedDone implements _DelayedEvent {
const _DelayedDone();
void perform(_EventDispatch dispatch) {
@@ -637,7 +617,7 @@
}
}
-/** Superclass for provider of pending events. */
+/// Superclass for provider of pending events.
abstract class _PendingEvents<T> {
// No async event has been scheduled.
static const int _STATE_UNSCHEDULED = 0;
@@ -647,18 +627,16 @@
// Async events can't be preempted.
static const int _STATE_CANCELED = 3;
- /**
- * State of being scheduled.
- *
- * Set to [_STATE_SCHEDULED] when pending events are scheduled for
- * async dispatch. Since we can't cancel a [scheduleMicrotask] call, if
- * scheduling is "canceled", the _state is simply set to [_STATE_CANCELED]
- * which will make the async code do nothing except resetting [_state].
- *
- * If events are scheduled while the state is [_STATE_CANCELED], it is
- * merely switched back to [_STATE_SCHEDULED], but no new call to
- * [scheduleMicrotask] is performed.
- */
+ /// State of being scheduled.
+ ///
+ /// Set to [_STATE_SCHEDULED] when pending events are scheduled for
+ /// async dispatch. Since we can't cancel a [scheduleMicrotask] call, if
+ /// scheduling is "canceled", the _state is simply set to [_STATE_CANCELED]
+ /// which will make the async code do nothing except resetting [_state].
+ ///
+ /// If events are scheduled while the state is [_STATE_CANCELED], it is
+ /// merely switched back to [_STATE_SCHEDULED], but no new call to
+ /// [scheduleMicrotask] is performed.
int _state = _STATE_UNSCHEDULED;
bool get isEmpty;
@@ -666,12 +644,10 @@
bool get isScheduled => _state == _STATE_SCHEDULED;
bool get _eventScheduled => _state >= _STATE_SCHEDULED;
- /**
- * Schedule an event to run later.
- *
- * If called more than once, it should be called with the same dispatch as
- * argument each time. It may reuse an earlier argument in some cases.
- */
+ /// Schedule an event to run later.
+ ///
+ /// If called more than once, it should be called with the same dispatch as
+ /// argument each time. It may reuse an earlier argument in some cases.
void schedule(_EventDispatch<T> dispatch) {
if (isScheduled) return;
assert(!isEmpty);
@@ -695,11 +671,11 @@
void handleNext(_EventDispatch<T> dispatch);
- /** Throw away any pending events and cancel scheduled events. */
+ /// Throw away any pending events and cancel scheduled events.
void clear();
}
-/** Class holding pending events for a [_StreamImpl]. */
+/// Class holding pending events for a [_StreamImpl].
class _StreamImplEvents<T> extends _PendingEvents<T> {
/// Single linked list of [_DelayedEvent] objects.
_DelayedEvent? firstPendingEvent;
@@ -738,9 +714,7 @@
typedef void _BroadcastCallback<T>(StreamSubscription<T> subscription);
-/**
- * Done subscription that will send one done event as soon as possible.
- */
+/// Done subscription that will send one done event as soon as possible.
class _DoneStreamSubscription<T> implements StreamSubscription<T> {
static const int _DONE_SENT = 1;
static const int _SCHEDULED = 2;
@@ -900,9 +874,7 @@
}
}
-/**
- * Wrapper for subscription that disallows changing handlers.
- */
+/// Wrapper for subscription that disallows changing handlers.
class _BroadcastSubscriptionWrapper<T> implements StreamSubscription<T> {
final _AsBroadcastStream _stream;
@@ -1118,7 +1090,7 @@
}
}
-/** An empty broadcast stream, sending a done event as soon as possible. */
+/// An empty broadcast stream, sending a done event as soon as possible.
class _EmptyStream<T> extends Stream<T> {
const _EmptyStream() : super._internal();
bool get isBroadcast => true;
@@ -1128,10 +1100,11 @@
}
}
-/** A stream which creates a new controller for each listener. */
+/// A stream which creates a new controller for each listener.
class _MultiStream<T> extends Stream<T> {
final bool isBroadcast;
- /** The callback called for each listen. */
+
+ /// The callback called for each listen.
final void Function(MultiStreamController<T>) _onListen;
_MultiStream(this._onListen, this.isBroadcast);
diff --git a/sdk/lib/async/stream_pipe.dart b/sdk/lib/async/stream_pipe.dart
index fd20228..60aff7e 100644
--- a/sdk/lib/async/stream_pipe.dart
+++ b/sdk/lib/async/stream_pipe.dart
@@ -4,7 +4,7 @@
part of dart.async;
-/** Runs user code and takes actions depending on success or failure. */
+/// Runs user code and takes actions depending on success or failure.
_runUserCode<T>(T userCode(), onSuccess(T value),
onError(Object error, StackTrace stackTrace)) {
try {
@@ -43,7 +43,7 @@
_cancelAndError(subscription, future, error, stackTrace);
}
-/** Helper function to make an onError argument to [_runUserCode]. */
+/// Helper function to make an onError argument to [_runUserCode].
void Function(Object error, StackTrace stackTrace) _cancelAndErrorClosure(
StreamSubscription subscription, _Future future) {
return (Object error, StackTrace stackTrace) {
@@ -62,15 +62,13 @@
}
}
-/**
- * A [Stream] that forwards subscriptions to another stream.
- *
- * This stream implements [Stream], but forwards all subscriptions
- * to an underlying stream, and wraps the returned subscription to
- * modify the events on the way.
- *
- * This class is intended for internal use only.
- */
+/// A [Stream] that forwards subscriptions to another stream.
+///
+/// This stream implements [Stream], but forwards all subscriptions
+/// to an underlying stream, and wraps the returned subscription to
+/// modify the events on the way.
+///
+/// This class is intended for internal use only.
abstract class _ForwardingStream<S, T> extends Stream<T> {
final Stream<S> _source;
@@ -102,9 +100,7 @@
}
}
-/**
- * Abstract superclass for subscriptions that forward to other subscriptions.
- */
+/// Abstract superclass for subscriptions that forward to other subscriptions.
class _ForwardingStreamSubscription<S, T>
extends _BufferingStreamSubscription<T> {
final _ForwardingStream<S, T> _stream;
@@ -203,9 +199,7 @@
typedef T _Transformation<S, T>(S value);
-/**
- * A stream pipe that converts data events before passing them on.
- */
+/// A stream pipe that converts data events before passing them on.
class _MapStream<S, T> extends _ForwardingStream<S, T> {
final _Transformation<S, T> _transform;
@@ -225,9 +219,7 @@
}
}
-/**
- * A stream pipe that converts data events before passing them on.
- */
+/// A stream pipe that converts data events before passing them on.
class _ExpandStream<S, T> extends _ForwardingStream<S, T> {
final _Transformation<S, Iterable<T>> _expand;
@@ -248,10 +240,8 @@
}
}
-/**
- * A stream pipe that converts or disposes error events
- * before passing them on.
- */
+/// A stream pipe that converts or disposes error events
+/// before passing them on.
class _HandleErrorStream<T> extends _ForwardingStream<T, T> {
final Function _transform;
final bool Function(Object)? _test;
@@ -327,11 +317,9 @@
}
}
-/**
- * A [_ForwardingStreamSubscription] with one extra state field.
- *
- * Use by several different classes, storing an integer, bool or general.
- */
+/// A [_ForwardingStreamSubscription] with one extra state field.
+///
+/// Use by several different classes, storing an integer, bool or general.
class _StateStreamSubscription<S, T>
extends _ForwardingStreamSubscription<T, T> {
S _subState;
diff --git a/sdk/lib/async/stream_transformers.dart b/sdk/lib/async/stream_transformers.dart
index aa99f5f..9193071 100644
--- a/sdk/lib/async/stream_transformers.dart
+++ b/sdk/lib/async/stream_transformers.dart
@@ -4,9 +4,7 @@
part of dart.async;
-/**
- * Wraps an [_EventSink] so it exposes only the [EventSink] interface.
- */
+/// Wraps an [_EventSink] so it exposes only the [EventSink] interface.
class _EventSinkWrapper<T> implements EventSink<T> {
_EventSink<T> _sink;
_EventSinkWrapper(this._sink);
@@ -24,13 +22,11 @@
}
}
-/**
- * A StreamSubscription that pipes data through a sink.
- *
- * The constructor of this class takes a [_SinkMapper] which maps from
- * [EventSink] to [EventSink]. The input to the mapper is the output of
- * the transformation. The returned sink is the transformation's input.
- */
+/// A StreamSubscription that pipes data through a sink.
+///
+/// The constructor of this class takes a [_SinkMapper] which maps from
+/// [EventSink] to [EventSink]. The input to the mapper is the output of
+/// the transformation. The returned sink is the transformation's input.
class _SinkTransformerStreamSubscription<S, T>
extends _BufferingStreamSubscription<T> {
/// The transformer's input sink.
@@ -55,13 +51,11 @@
// _EventSink interface.
- /**
- * Adds an event to this subscriptions.
- *
- * Contrary to normal [_BufferingStreamSubscription]s we may receive
- * events when the stream is already closed. Report them as state
- * error.
- */
+ /// Adds an event to this subscriptions.
+ ///
+ /// Contrary to normal [_BufferingStreamSubscription]s we may receive
+ /// events when the stream is already closed. Report them as state
+ /// error.
void _add(T data) {
if (_isClosed) {
throw StateError("Stream is already closed");
@@ -69,13 +63,11 @@
super._add(data);
}
- /**
- * Adds an error event to this subscriptions.
- *
- * Contrary to normal [_BufferingStreamSubscription]s we may receive
- * events when the stream is already closed. Report them as state
- * error.
- */
+ /// Adds an error event to this subscriptions.
+ ///
+ /// Contrary to normal [_BufferingStreamSubscription]s we may receive
+ /// events when the stream is already closed. Report them as state
+ /// error.
void _addError(Object error, StackTrace stackTrace) {
if (_isClosed) {
throw new StateError("Stream is already closed");
@@ -83,13 +75,11 @@
super._addError(error, stackTrace);
}
- /**
- * Adds a close event to this subscriptions.
- *
- * Contrary to normal [_BufferingStreamSubscription]s we may receive
- * events when the stream is already closed. Report them as state
- * error.
- */
+ /// Adds a close event to this subscriptions.
+ ///
+ /// Contrary to normal [_BufferingStreamSubscription]s we may receive
+ /// events when the stream is already closed. Report them as state
+ /// error.
void _close() {
if (_isClosed) {
throw new StateError("Stream is already closed");
@@ -148,14 +138,12 @@
typedef EventSink<S> _SinkMapper<S, T>(EventSink<T> output);
-/**
- * A StreamTransformer for Sink-mappers.
- *
- * A Sink-mapper takes an [EventSink] (its output) and returns another
- * EventSink (its input).
- *
- * Note that this class can be `const`.
- */
+/// A [StreamTransformer] for [Sink]-mappers.
+///
+/// A Sink-mapper takes an [EventSink] (its output) and returns another
+/// [EventSink] (its input).
+///
+/// Note that this class can be `const`.
class _StreamSinkTransformer<S, T> extends StreamTransformerBase<S, T> {
final _SinkMapper<S, T> _sinkMapper;
const _StreamSinkTransformer(this._sinkMapper);
@@ -164,13 +152,11 @@
new _BoundSinkStream<S, T>(stream, _sinkMapper);
}
-/**
- * The result of binding a StreamTransformer for Sink-mappers.
- *
- * It contains the bound Stream and the sink-mapper. Only when the user starts
- * listening to this stream is the sink-mapper invoked. The result is used
- * to create a StreamSubscription that transforms events.
- */
+/// The result of binding a [StreamTransformer] for [Sink]-mappers.
+///
+/// It contains the bound Stream and the sink-mapper. Only when the user starts
+/// listening to this stream is the sink-mapper invoked. The result is used
+/// to create a StreamSubscription that transforms events.
class _BoundSinkStream<S, T> extends Stream<T> {
final _SinkMapper<S, T> _sinkMapper;
final Stream<S> _stream;
@@ -198,11 +184,9 @@
/// Done-handler coming from [StreamTransformer.fromHandlers].
typedef void _TransformDoneHandler<T>(EventSink<T> sink);
-/**
- * Wraps handlers (from [StreamTransformer.fromHandlers]) into an `EventSink`.
- *
- * This way we can reuse the code from [_StreamSinkTransformer].
- */
+/// Wraps handlers (from [StreamTransformer.fromHandlers]) into an `EventSink`.
+///
+/// This way we can reuse the code from [_StreamSinkTransformer].
class _HandlerEventSink<S, T> implements EventSink<S> {
final _TransformDataHandler<S, T>? _handleData;
final _TransformErrorHandler<T>? _handleError;
@@ -256,11 +240,9 @@
}
}
-/**
- * A StreamTransformer that transformers events with the given handlers.
- *
- * Note that this transformer can only be used once.
- */
+/// A StreamTransformer that transformers events with the given handlers.
+///
+/// Note that this transformer can only be used once.
class _StreamHandlerTransformer<S, T> extends _StreamSinkTransformer<S, T> {
_StreamHandlerTransformer(
{void handleData(S data, EventSink<T> sink)?,
@@ -276,9 +258,7 @@
}
}
-/**
- * A StreamTransformer that overrides [StreamTransformer.bind] with a callback.
- */
+/// A StreamTransformer that overrides [StreamTransformer.bind] with a callback.
class _StreamBindTransformer<S, T> extends StreamTransformerBase<S, T> {
final Stream<T> Function(Stream<S>) _bind;
_StreamBindTransformer(this._bind);
@@ -290,18 +270,16 @@
typedef StreamSubscription<T> _SubscriptionTransformer<S, T>(
Stream<S> stream, bool cancelOnError);
-/**
- * A [StreamTransformer] that minimizes the number of additional classes.
- *
- * Instead of implementing three classes: a [StreamTransformer], a [Stream]
- * (as the result of a `bind` call) and a [StreamSubscription] (which does the
- * actual work), this class only requires a function that is invoked when the
- * last bit (the subscription) of the transformer-workflow is needed.
- *
- * The given transformer function maps from Stream and cancelOnError to a
- * `StreamSubscription`. As such it can also act on `cancel` events, making it
- * fully general.
- */
+/// A [StreamTransformer] that minimizes the number of additional classes.
+///
+/// Instead of implementing three classes: a [StreamTransformer], a [Stream]
+/// (as the result of a `bind` call) and a [StreamSubscription] (which does the
+/// actual work), this class only requires a function that is invoked when the
+/// last bit (the subscription) of the transformer-workflow is needed.
+///
+/// The given transformer function maps from Stream and cancelOnError to a
+/// `StreamSubscription`. As such it can also act on `cancel` events, making it
+/// fully general.
class _StreamSubscriptionTransformer<S, T> extends StreamTransformerBase<S, T> {
final _SubscriptionTransformer<S, T> _onListen;
@@ -311,13 +289,11 @@
new _BoundSubscriptionStream<S, T>(stream, _onListen);
}
-/**
- * A stream transformed by a [_StreamSubscriptionTransformer].
- *
- * When this stream is listened to it invokes the [_onListen] function with
- * the stored [_stream]. Usually the transformer starts listening at this
- * moment.
- */
+/// A stream transformed by a [_StreamSubscriptionTransformer].
+///
+/// When this stream is listened to it invokes the [_onListen] function with
+/// the stored [_stream]. Usually the transformer starts listening at this
+/// moment.
class _BoundSubscriptionStream<S, T> extends Stream<T> {
final _SubscriptionTransformer<S, T> _onListen;
final Stream<S> _stream;
diff --git a/sdk/lib/async/timer.dart b/sdk/lib/async/timer.dart
index e0ef447..973a543 100644
--- a/sdk/lib/async/timer.dart
+++ b/sdk/lib/async/timer.dart
@@ -4,44 +4,40 @@
part of dart.async;
-/**
- * A count-down timer that can be configured to fire once or repeatedly.
- *
- * The timer counts down from the specified duration to 0.
- * When the timer reaches 0, the timer invokes the specified callback function.
- * Use a periodic timer to repeatedly count down the same interval.
- *
- * A negative duration is treated the same as a duration of 0.
- * If the duration is statically known to be 0, consider using [run].
- *
- * Frequently the duration is either a constant or computed as in the
- * following example (taking advantage of the multiplication operator of
- * the [Duration] class):
- * ```dart
- * const timeout = const Duration(seconds: 3);
- * const ms = const Duration(milliseconds: 1);
- *
- * startTimeout([int milliseconds]) {
- * var duration = milliseconds == null ? timeout : ms * milliseconds;
- * return new Timer(duration, handleTimeout);
- * }
- * ...
- * void handleTimeout() { // callback function
- * ...
- * }
- * ```
- * Note: If Dart code using Timer is compiled to JavaScript, the finest
- * granularity available in the browser is 4 milliseconds.
- *
- * See [Stopwatch] for measuring elapsed time.
- */
+/// A count-down timer that can be configured to fire once or repeatedly.
+///
+/// The timer counts down from the specified duration to 0.
+/// When the timer reaches 0, the timer invokes the specified callback function.
+/// Use a periodic timer to repeatedly count down the same interval.
+///
+/// A negative duration is treated the same as a duration of 0.
+/// If the duration is statically known to be 0, consider using [run].
+///
+/// Frequently the duration is either a constant or computed as in the
+/// following example (taking advantage of the multiplication operator of
+/// the [Duration] class):
+/// ```dart
+/// const timeout = Duration(seconds: 3);
+/// const ms = Duration(milliseconds: 1);
+///
+/// Timer startTimeout([int? milliseconds]) {
+/// var duration = milliseconds == null ? timeout : ms * milliseconds;
+/// return Timer(duration, handleTimeout);
+/// }
+/// ...
+/// void handleTimeout() { // callback function
+/// ...
+/// }
+/// ```
+/// Note: If Dart code using [Timer] is compiled to JavaScript, the finest
+/// granularity available in the browser is 4 milliseconds.
+///
+/// See [Stopwatch] for measuring elapsed time.
abstract class Timer {
- /**
- * Creates a new timer.
- *
- * The [callback] function is invoked after the given [duration].
- *
- */
+ /// Creates a new timer.
+ ///
+ /// The [callback] function is invoked after the given [duration].
+ ///
factory Timer(Duration duration, void Function() callback) {
if (Zone.current == Zone.root) {
// No need to bind the callback. We know that the root's timer will
@@ -52,24 +48,22 @@
.createTimer(duration, Zone.current.bindCallbackGuarded(callback));
}
- /**
- * Creates a new repeating timer.
- *
- * The [callback] is invoked repeatedly with [duration] intervals until
- * canceled with the [cancel] function.
- *
- * The exact timing depends on the underlying timer implementation.
- * No more than `n` callbacks will be made in `duration * n` time,
- * but the time between two consecutive callbacks
- * can be shorter and longer than `duration`.
- *
- * In particular, an implementation may schedule the next callback, e.g.,
- * a `duration` after either when the previous callback ended,
- * when the previous callback started, or when the previous callback was
- * scheduled for - even if the actual callback was delayed.
- *
- * [duration] must a non-negative [Duration].
- */
+ /// Creates a new repeating timer.
+ ///
+ /// The [callback] is invoked repeatedly with [duration] intervals until
+ /// canceled with the [cancel] function.
+ ///
+ /// The exact timing depends on the underlying timer implementation.
+ /// No more than `n` callbacks will be made in `duration * n` time,
+ /// but the time between two consecutive callbacks
+ /// can be shorter and longer than `duration`.
+ ///
+ /// In particular, an implementation may schedule the next callback, e.g.,
+ /// a `duration` after either when the previous callback ended,
+ /// when the previous callback started, or when the previous callback was
+ /// scheduled for - even if the actual callback was delayed.
+ ///
+ /// [duration] must a non-negative [Duration].
factory Timer.periodic(Duration duration, void callback(Timer timer)) {
if (Zone.current == Zone.root) {
// No need to bind the callback. We know that the root's timer will
@@ -80,47 +74,39 @@
return Zone.current.createPeriodicTimer(duration, boundCallback);
}
- /**
- * Runs the given [callback] asynchronously as soon as possible.
- *
- * This function is equivalent to `new Timer(Duration.zero, callback)`.
- */
+ /// Runs the given [callback] asynchronously as soon as possible.
+ ///
+ /// This function is equivalent to `new Timer(Duration.zero, callback)`.
static void run(void Function() callback) {
new Timer(Duration.zero, callback);
}
- /**
- * Cancels the timer.
- *
- * Once a [Timer] has been canceled, the callback function will not be called
- * by the timer. Calling [cancel] more than once on a [Timer] is allowed, and
- * will have no further effect.
- */
+ /// Cancels the timer.
+ ///
+ /// Once a [Timer] has been canceled, the callback function will not be called
+ /// by the timer. Calling [cancel] more than once on a [Timer] is allowed, and
+ /// will have no further effect.
void cancel();
- /**
- * The number of durations preceding the most recent timer event.
- *
- * The value starts at zero and is incremented each time a timer event
- * occurs, so each callback will see a larger value than the previous one.
- *
- * If a periodic timer with a non-zero duration is delayed too much,
- * so more than one tick should have happened,
- * all but the last tick in the past are considered "missed",
- * and no callback is invoked for them.
- * The [tick] count reflects the number of durations that have passed and
- * not the number of callback invocations that have happened.
- */
+ /// The number of durations preceding the most recent timer event.
+ ///
+ /// The value starts at zero and is incremented each time a timer event
+ /// occurs, so each callback will see a larger value than the previous one.
+ ///
+ /// If a periodic timer with a non-zero duration is delayed too much,
+ /// so more than one tick should have happened,
+ /// all but the last tick in the past are considered "missed",
+ /// and no callback is invoked for them.
+ /// The [tick] count reflects the number of durations that have passed and
+ /// not the number of callback invocations that have happened.
int get tick;
- /**
- * Returns whether the timer is still active.
- *
- * A non-periodic timer is active if the callback has not been executed,
- * and the timer has not been canceled.
- *
- * A periodic timer is active if it has not been canceled.
- */
+ /// Returns whether the timer is still active.
+ ///
+ /// A non-periodic timer is active if the callback has not been executed,
+ /// and the timer has not been canceled.
+ ///
+ /// A periodic timer is active if it has not been canceled.
bool get isActive;
external static Timer _createTimer(
diff --git a/sdk/lib/async/zone.dart b/sdk/lib/async/zone.dart
index f662d53..4f0873d 100644
--- a/sdk/lib/async/zone.dart
+++ b/sdk/lib/async/zone.dart
@@ -8,59 +8,236 @@
typedef R ZoneUnaryCallback<R, T>(T arg);
typedef R ZoneBinaryCallback<R, T1, T2>(T1 arg1, T2 arg2);
+/// The type of a custom [Zone.handleUncaughtError] implementation function.
+///
+/// Receives the [Zone] that the handler was registered on as [self],
+/// a delegate forwarding to the handlers of [self]'s parent zone as [parent],
+/// and the current zone where the error was uncaught as [zone],
+/// which will have [self] as a parent zone.
+///
+/// The [error] and [stackTrace] are the error and stack trace that
+/// was uncaught in [zone].
typedef HandleUncaughtErrorHandler = void Function(Zone self,
ZoneDelegate parent, Zone zone, Object error, StackTrace stackTrace);
+
+/// The type of a custom [Zone.run] implementation function.
+///
+/// Receives the [Zone] that the handler was registered on as [self],
+/// a delegate forwarding to the handlers of [self]'s parent zone as [parent],
+/// and the current zone where the error was uncaught as [zone],
+/// which will have [self] as a parent zone.
+///
+/// The function [f] is the function which was passed to the
+/// [Zone.run] of [zone].
+///
+/// The default behavior of [Zone.run] is
+/// to call [f] in the current zone, [zone].
+/// A custom handler can do things before, after or instead of
+/// calling [f].
typedef RunHandler = R Function<R>(
Zone self, ZoneDelegate parent, Zone zone, R Function() f);
+
+/// The type of a custom [Zone.runUnary] implementation function.
+///
+/// Receives the [Zone] that the handler was registered on as [self],
+/// a delegate forwarding to the handlers of [self]'s parent zone as [parent],
+/// and the current zone where the error was uncaught as [zone],
+/// which will have [self] as a parent zone.
+///
+/// The function [f] and value [arg] are the function and argument
+/// which was passed to the [Zone.runUnary] of [zone].
+///
+/// The default behavior of [Zone.runUnary] is
+/// to call [f] with argument [arg] in the current zone, [zone].
+/// A custom handler can do things before, after or instead of
+/// calling [f].
typedef RunUnaryHandler = R Function<R, T>(
Zone self, ZoneDelegate parent, Zone zone, R Function(T arg) f, T arg);
+
+/// The type of a custom [Zone.runBinary] implementation function.
+///
+/// Receives the [Zone] that the handler was registered on as [self],
+/// a delegate forwarding to the handlers of [self]'s parent zone as [parent],
+/// and the current zone where the error was uncaught as [zone],
+/// which will have [self] as a parent zone.
+///
+/// The function [f] and values [arg1] and [arg2] are the function and arguments
+/// which was passed to the [Zone.runBinary] of [zone].
+///
+/// The default behavior of [Zone.runUnary] is
+/// to call [f] with arguments [arg1] and [arg2] in the current zone, [zone].
+/// A custom handler can do things before, after or instead of
+/// calling [f].
typedef RunBinaryHandler = R Function<R, T1, T2>(Zone self, ZoneDelegate parent,
Zone zone, R Function(T1 arg1, T2 arg2) f, T1 arg1, T2 arg2);
+
+/// The type of a custom [Zone.registerCallback] implementation function.
+///
+/// Receives the [Zone] that the handler was registered on as [self],
+/// a delegate forwarding to the handlers of [self]'s parent zone as [parent],
+/// and the current zone where the error was uncaught as [zone],
+/// which will have [self] as a parent zone.
+///
+/// The function [f] is the function which was passed to the
+/// [Zone.registerCallback] of [zone].
+///
+/// The handler should return either the function [f]
+/// or another function replacing [f],
+/// typically by wrapping [f] in a function
+/// which does something extra before and after invoking [f]
typedef RegisterCallbackHandler = ZoneCallback<R> Function<R>(
Zone self, ZoneDelegate parent, Zone zone, R Function() f);
+
+/// The type of a custom [Zone.registerUnary] implementation function.
+///
+/// Receives the [Zone] that the handler was registered on as [self],
+/// a delegate forwarding to the handlers of [self]'s parent zone as [parent],
+/// and the current zone where the error was uncaught as [zone],
+/// which will have [self] as a parent zone.
+///
+/// The function [f] is the function which was passed to the
+/// which was passed to the [Zone.registerUnary] of [zone].
+///
+/// The handler should return either the function [f]
+/// or another function replacing [f],
+/// typically by wrapping [f] in a function
+/// which does something extra before and after invoking [f]
typedef RegisterUnaryCallbackHandler = ZoneUnaryCallback<R, T> Function<R, T>(
Zone self, ZoneDelegate parent, Zone zone, R Function(T arg) f);
+
+/// The type of a custom [Zone.registerBinary] implementation function.
+///
+/// Receives the [Zone] that the handler was registered on as [self],
+/// a delegate forwarding to the handlers of [self]'s parent zone as [parent],
+/// and the current zone where the error was uncaught as [zone],
+/// which will have [self] as a parent zone.
+///
+/// The function [f] is the function which was passed to the
+/// which was passed to the [Zone.registerBinary] of [zone].
+///
+/// The handler should return either the function [f]
+/// or another function replacing [f],
+/// typically by wrapping [f] in a function
+/// which does something extra before and after invoking [f]
typedef RegisterBinaryCallbackHandler
= ZoneBinaryCallback<R, T1, T2> Function<R, T1, T2>(Zone self,
ZoneDelegate parent, Zone zone, R Function(T1 arg1, T2 arg2) f);
+
+/// The type of a custom [Zone.errorCallback] implementation function.
+///
+/// Receives the [Zone] that the handler was registered on as [self],
+/// a delegate forwarding to the handlers of [self]'s parent zone as [parent],
+/// and the current zone where the error was uncaught as [zone],
+/// which will have [self] as a parent zone.
+///
+/// The [error] and [stackTrace] are the error and stack trace
+/// passed to [Zone.errorCallback] of [zone].
+///
+/// The function should return either `null` if it doesn't want
+/// to replace the original error and stack trace,
+/// or an [AsyncError] containg a replacement error and stack trace
+/// which will be used to replace the originals.
typedef AsyncError? ErrorCallbackHandler(Zone self, ZoneDelegate parent,
Zone zone, Object error, StackTrace? stackTrace);
+
+/// The type of a custom [Zone.scheduleMicrotask] implementation function.
+///
+/// Receives the [Zone] that the handler was registered on as [self],
+/// a delegate forwarding to the handlers of [self]'s parent zone as [parent],
+/// and the current zone where the error was uncaught as [zone],
+/// which will have [self] as a parent zone.
+///
+/// The function [f] is the function which was
+/// passed to [Zone.scheduleMicrotask] of [zone].
+///
+/// The custom handler can choose to replace the function [f]
+/// with one that does something before, after or instead of calling [f],
+/// and then call `parent.scheduleMicrotask(zone, replacement)`.
+/// or it can implement its own microtask scheduling queue, which typically
+/// still depends on `parent.scheduleMicrotask` to as a way to get started.
typedef void ScheduleMicrotaskHandler(
Zone self, ZoneDelegate parent, Zone zone, void f());
+
+/// The type of a custom [Zone.createTimer] implementation function.
+///
+/// Receives the [Zone] that the handler was registered on as [self],
+/// a delegate forwarding to the handlers of [self]'s parent zone as [parent],
+/// and the current zone where the error was uncaught as [zone],
+/// which will have [self] as a parent zone.
+///
+/// The callback function [f] and [duration] are the ones which were
+/// passed to [Zone.createTimer] of [zone]
+/// (possibly through the [Timer] constructor).
+///
+/// The custom handler can choose to replace the function [f]
+/// with one that does something before, after or instead of calling [f],
+/// and then call `parent.createTimer(zone, replacement)`.
+/// or it can implement its own timer queue, which typically
+/// still depends on `parent.createTimer` to as a way to get started.
+///
+/// The function should return a [Timer] object which can be used
+/// to inspect and control the scheduled timer callback.
typedef Timer CreateTimerHandler(
Zone self, ZoneDelegate parent, Zone zone, Duration duration, void f());
+
+/// The type of a custom [Zone.createPeriodicTimer] implementation function.
+///
+/// Receives the [Zone] that the handler was registered on as [self],
+/// a delegate forwarding to the handlers of [self]'s parent zone as [parent],
+/// and the current zone where the error was uncaught as [zone],
+/// which will have [self] as a parent zone.
+///
+/// The callback function [f] and [duration] are the ones which were
+/// passed to [Zone.createPeriodicTimer] of [zone]
+/// (possibly through the [Timer.periodic] constructor).
+///
+/// The custom handler can choose to replace the function [f]
+/// with one that does something before, after or instead of calling [f],
+/// and then call `parent.createTimer(zone, replacement)`.
+/// or it can implement its own timer queue, which typically
+/// still depends on `parent.createTimer` to as a way to get started.
+///
+/// The function should return a [Timer] object which can be used
+/// to inspect and control the scheduled timer callbacks.
typedef Timer CreatePeriodicTimerHandler(Zone self, ZoneDelegate parent,
Zone zone, Duration period, void f(Timer timer));
+
+/// The type of a custom [Zone.print] implementation function.
+///
+/// Receives the [Zone] that the handler was registered on as [self],
+/// a delegate forwarding to the handlers of [self]'s parent zone as [parent],
+/// and the current zone where the error was uncaught as [zone],
+/// which will have [self] as a parent zone.
+///
+/// The string [line] is the one which was passed to [Zone.print] of [zone],
+/// (possibly through the global [print] function).
+///
+/// The custom handler can intercept print operations and
+/// redirect them to other targets than the console.
typedef void PrintHandler(
Zone self, ZoneDelegate parent, Zone zone, String line);
+
+/// The type of a custom [Zone.fork] implementation function.
+///
+/// Receives the [Zone] that the handler was registered on as [self],
+/// a delegate forwarding to the handlers of [self]'s parent zone as [parent],
+/// and the current zone where the error was uncaught as [zone],
+/// which will have [self] as a parent zone.
+///
+/// The handler should create a new zone with [zone] as its
+/// immediate parent zone.
+///
+/// The [specification] and [zoneValues] are the ones which were
+/// passed to [Zone.fork] of [zone]. They specify the custom zone
+/// handlers and zone variables that the new zone should have.
+///
+/// The custom handler can change the specification or zone
+/// values before calling `parent.fork(zone, specification, zoneValues)`,
+/// but it has to call the [parent]'s [ZoneDelegate.fork] in order
+/// to create a valid [Zone] object.
typedef Zone ForkHandler(Zone self, ZoneDelegate parent, Zone zone,
ZoneSpecification? specification, Map<Object?, Object?>? zoneValues);
-/// Pair of error and stack trace. Returned by [Zone.errorCallback].
-class AsyncError implements Error {
- final Object error;
- final StackTrace stackTrace;
-
- AsyncError(Object error, StackTrace? stackTrace)
- : error = checkNotNullable(error, "error"),
- stackTrace = stackTrace ?? defaultStackTrace(error);
-
- /// A default stack trace for an error.
- ///
- /// If [error] is an [Error] and it has an [Error.stackTrace],
- /// that stack trace is returned.
- /// If not, the [StackTrace.empty] default stack trace is returned.
- static StackTrace defaultStackTrace(Object error) {
- if (error is Error) {
- var stackTrace = error.stackTrace;
- if (stackTrace != null) return stackTrace;
- }
- return StackTrace.empty;
- }
-
- String toString() => '$error';
-}
-
class _ZoneFunction<T extends Function> {
final _Zone zone;
final T function;
@@ -103,29 +280,33 @@
const _RegisterBinaryZoneFunction(this.zone, this.function);
}
-/**
- * This class provides the specification for a forked zone.
- *
- * When forking a new zone (see [Zone.fork]) one can override the default
- * behavior of the zone by providing callbacks. These callbacks must be
- * given in an instance of this class.
- *
- * Handlers have the same signature as the same-named methods on [Zone] but
- * receive three additional arguments:
- *
- * 1. the zone the handlers are attached to (the "self" zone).
- * 2. a [ZoneDelegate] to the parent zone.
- * 3. the zone that first received the request (before the request was
- * bubbled up).
- *
- * Handlers can either stop propagation the request (by simply not calling the
- * parent handler), or forward to the parent zone, potentially modifying the
- * arguments on the way.
- */
+/// A parameter object with custom zone function handlers for [Zone.fork].
+///
+/// A zone specification is a parameter object passed to [Zone.fork]
+/// and any underlying [ForkHandler] custom implementations.
+/// The individual handlers, if set to a non-null value,
+/// will be the implementation of the correpsonding [Zone] methods
+/// for a forked zone created using this zone specification.
+///
+/// Handlers have the same signature as the same-named methods on [Zone],
+/// but receive three additional arguments:
+///
+/// 1. The zone the handlers are attached to (the "self" zone).
+/// This is the zone created by [Zone.fork] where the handler is
+/// passed as part of the zone delegation.
+/// 2. A [ZoneDelegate] to the parent zone.
+/// 3. The "current" zone at the time the request was made.
+/// The self zone is always a parent zone of the current zone.
+///
+/// Handlers can either stop propagating the request (by simply not calling the
+/// parent handler), or forward to the parent zone, potentially modifying the
+/// arguments on the way.
abstract class ZoneSpecification {
- /**
- * Creates a specification with the provided handlers.
- */
+ /// Creates a specification with the provided handlers.
+ ///
+ /// If the [handleUncaughtError] is provided, the new zone will be a new
+ /// "error zone" which will prevent errors from flowing into other
+ /// error zones (see [errorZone], [sameErrorZone]).
const factory ZoneSpecification(
{HandleUncaughtErrorHandler? handleUncaughtError,
RunHandler? run,
@@ -141,10 +322,12 @@
PrintHandler? print,
ForkHandler? fork}) = _ZoneSpecification;
- /**
- * Creates a specification from [other] with the provided handlers overriding
- * the ones in [other].
- */
+ /// Creates a specification from [other] and provided handlers.
+ ///
+ /// The created zone specification has the handlers of [other]
+ /// and any individually provided handlers.
+ /// If a handler is provided both through [other] and individually,
+ /// the individually provided handler overries the one from [other].
factory ZoneSpecification.from(ZoneSpecification other,
{HandleUncaughtErrorHandler? handleUncaughtError,
RunHandler? run,
@@ -177,28 +360,51 @@
fork: fork ?? other.fork);
}
+ /// A custom [Zone.handleUncaughtError] implementation for a new zone.
HandleUncaughtErrorHandler? get handleUncaughtError;
+
+ /// A custom [Zone.run] implementation for a new zone.
RunHandler? get run;
+
+ /// A custom [Zone.runUnary] implementation for a new zone.
RunUnaryHandler? get runUnary;
+
+ /// A custom [Zone.runBinary] implementation for a new zone.
RunBinaryHandler? get runBinary;
+
+ /// A custom [Zone.registerCallback] implementation for a new zone.
RegisterCallbackHandler? get registerCallback;
+
+ /// A custom [Zone.registerUnaryCallback] implementation for a new zone.
RegisterUnaryCallbackHandler? get registerUnaryCallback;
+
+ /// A custom [Zone.registerBinaryCallback] implementation for a new zone.
RegisterBinaryCallbackHandler? get registerBinaryCallback;
+
+ /// A custom [Zone.errorCallback] implementation for a new zone.
ErrorCallbackHandler? get errorCallback;
+
+ /// A custom [Zone.scheduleMicrotask] implementation for a new zone.
ScheduleMicrotaskHandler? get scheduleMicrotask;
+
+ /// A custom [Zone.createTimer] implementation for a new zone.
CreateTimerHandler? get createTimer;
+
+ /// A custom [Zone.createPeriodicTimer] implementation for a new zone.
CreatePeriodicTimerHandler? get createPeriodicTimer;
+
+ /// A custom [Zone.print] implementation for a new zone.
PrintHandler? get print;
+
+ /// A custom [Zone.handleUncaughtError] implementation for a new zone.
ForkHandler? get fork;
}
-/**
- * Internal [ZoneSpecification] class.
- *
- * The implementation wants to rely on the fact that the getters cannot change
- * dynamically. We thus require users to go through the redirecting
- * [ZoneSpecification] constructor which instantiates this class.
- */
+/// Internal [ZoneSpecification] class.
+///
+/// The implementation wants to rely on the fact that the getters cannot change
+/// dynamically. We thus require users to go through the redirecting
+/// [ZoneSpecification] constructor which instantiates this class.
class _ZoneSpecification implements ZoneSpecification {
const _ZoneSpecification(
{this.handleUncaughtError,
@@ -230,482 +436,452 @@
final ForkHandler? fork;
}
-/**
- * An adapted view of the parent zone.
- *
- * This class allows the implementation of a zone method to invoke methods on
- * the parent zone while retaining knowledge of the originating zone.
- *
- * Custom zones (created through [Zone.fork] or [runZoned]) can provide
- * implementations of most methods of zones. This is similar to overriding
- * methods on [Zone], except that this mechanism doesn't require subclassing.
- *
- * A custom zone function (provided through a [ZoneSpecification]) typically
- * records or wraps its parameters and then delegates the operation to its
- * parent zone using the provided [ZoneDelegate].
- *
- * While zones have access to their parent zone (through [Zone.parent]) it is
- * recommended to call the methods on the provided parent delegate for two
- * reasons:
- * 1. the delegate methods take an additional `zone` argument which is the
- * zone the action has been initiated in.
- * 2. delegate calls are more efficient, since the implementation knows how
- * to skip zones that would just delegate to their parents.
- */
+/// An adapted view of the parent zone.
+///
+/// This class allows the implementation of a zone method to invoke methods on
+/// the parent zone while retaining knowledge of the originating zone.
+///
+/// Custom zones (created through [Zone.fork] or [runZoned]) can provide
+/// implementations of most methods of zones. This is similar to overriding
+/// methods on [Zone], except that this mechanism doesn't require subclassing.
+///
+/// A custom zone function (provided through a [ZoneSpecification]) typically
+/// records or wraps its parameters and then delegates the operation to its
+/// parent zone using the provided [ZoneDelegate].
+///
+/// While zones have access to their parent zone (through [Zone.parent]) it is
+/// recommended to call the methods on the provided parent delegate for two
+/// reasons:
+/// 1. the delegate methods take an additional `zone` argument which is the
+/// zone the action has been initiated in.
+/// 2. delegate calls are more efficient, since the implementation knows how
+/// to skip zones that would just delegate to their parents.
abstract class ZoneDelegate {
+ // Invoke the [HandleUncaughtErrorHandler] of the zone with a current zone.
void handleUncaughtError(Zone zone, Object error, StackTrace stackTrace);
+
+ // Invokes the [RunHandler] of the zone with a current zone.
R run<R>(Zone zone, R f());
+
+ // Invokes the [RunUnaryHandler] of the zone with a current zone.
R runUnary<R, T>(Zone zone, R f(T arg), T arg);
+
+ // Invokes the [RunBinaryHandler] of the zone with a current zone.
R runBinary<R, T1, T2>(Zone zone, R f(T1 arg1, T2 arg2), T1 arg1, T2 arg2);
+
+ // Invokes the [RegisterCallbackHandler] of the zone with a current zone.
ZoneCallback<R> registerCallback<R>(Zone zone, R f());
+
+ // Invokes the [RegisterUnaryHandler] of the zone with a current zone.
ZoneUnaryCallback<R, T> registerUnaryCallback<R, T>(Zone zone, R f(T arg));
+
+ // Invokes the [RegisterBinaryHandler] of the zone with a current zone.
ZoneBinaryCallback<R, T1, T2> registerBinaryCallback<R, T1, T2>(
Zone zone, R f(T1 arg1, T2 arg2));
+
+ // Invokes the [ErrorCallbackHandler] of the zone with a current zone.
AsyncError? errorCallback(Zone zone, Object error, StackTrace? stackTrace);
+
+ // Invokes the [ScheduleMicrotaskHandler] of the zone with a current zone.
void scheduleMicrotask(Zone zone, void f());
+
+ // Invokes the [CreateTimerHandler] of the zone with a current zone.
Timer createTimer(Zone zone, Duration duration, void f());
+
+ // Invokes the [CreatePeriodicTimerHandler] of the zone with a current zone.
Timer createPeriodicTimer(Zone zone, Duration period, void f(Timer timer));
+
+ // Invokes the [PrintHandler] of the zone with a current zone.
void print(Zone zone, String line);
+
+ // Invokes the [ForkHandler] of the zone with a current zone.
Zone fork(Zone zone, ZoneSpecification? specification, Map? zoneValues);
}
-/**
- * A zone represents an environment that remains stable across asynchronous
- * calls.
- *
- * Code is always executed in the context of a zone, available as
- * [Zone.current]. The initial `main` function runs in the context of the
- * default zone ([Zone.root]). Code can be run in a different zone using either
- * [runZoned], to create a new zone, or [Zone.run] to run code in the context of
- * an existing zone likely created using [Zone.fork].
- *
- * Developers can create a new zone that overrides some of the functionality of
- * an existing zone. For example, custom zones can replace of modify the
- * behavior of `print`, timers, microtasks or how uncaught errors are handled.
- *
- * The [Zone] class is not subclassable, but users can provide custom zones by
- * forking an existing zone (usually [Zone.current]) with a [ZoneSpecification].
- * This is similar to creating a new class that extends the base `Zone` class
- * and that overrides some methods, except without actually creating a new
- * class. Instead the overriding methods are provided as functions that
- * explicitly take the equivalent of their own class, the "super" class and the
- * `this` object as parameters.
- *
- * Asynchronous callbacks always run in the context of the zone where they were
- * scheduled. This is implemented using two steps:
- * 1. the callback is first registered using one of [registerCallback],
- * [registerUnaryCallback], or [registerBinaryCallback]. This allows the zone
- * to record that a callback exists and potentially modify it (by returning a
- * different callback). The code doing the registration (e.g., `Future.then`)
- * also remembers the current zone so that it can later run the callback in
- * that zone.
- * 2. At a later point the registered callback is run in the remembered zone.
- *
- * This is all handled internally by the platform code and most users don't need
- * to worry about it. However, developers of new asynchronous operations,
- * provided by the underlying system or through native extensions, must follow
- * the protocol to be zone compatible.
- *
- * For convenience, zones provide [bindCallback] (and the corresponding
- * [bindUnaryCallback] and [bindBinaryCallback]) to make it easier to respect
- * the zone contract: these functions first invoke the corresponding `register`
- * functions and then wrap the returned function so that it runs in the current
- * zone when it is later asynchronously invoked.
- *
- * Similarly, zones provide [bindCallbackGuarded] (and the corresponding
- * [bindUnaryCallbackGuarded] and [bindBinaryCallbackGuarded]), when the
- * callback should be invoked through [Zone.runGuarded].
- */
+/// A zone represents an environment that remains stable across asynchronous
+/// calls.
+///
+/// Code is always executed in the context of a zone, available as
+/// [Zone.current]. The initial `main` function runs in the context of the
+/// default zone ([Zone.root]). Code can be run in a different zone using either
+/// [runZoned], to create a new zone, or [Zone.run] to run code in the context of
+/// an existing zone which was created earlier using [Zone.fork].
+///
+/// Developers can create a new zone that overrides some of the functionality of
+/// an existing zone. For example, custom zones can replace of modify the
+/// behavior of `print`, timers, microtasks or how uncaught errors are handled.
+///
+/// The [Zone] class is not subclassable, but users can provide custom zones by
+/// forking an existing zone (usually [Zone.current]) with a [ZoneSpecification].
+/// This is similar to creating a new class that extends the base `Zone` class
+/// and that overrides some methods, except without actually creating a new
+/// class. Instead the overriding methods are provided as functions that
+/// explicitly take the equivalent of their own class, the "super" class and the
+/// `this` object as parameters.
+///
+/// Asynchronous callbacks always run in the context of the zone where they were
+/// scheduled. This is implemented using two steps:
+/// 1. the callback is first registered using one of [registerCallback],
+/// [registerUnaryCallback], or [registerBinaryCallback]. This allows the zone
+/// to record that a callback exists and potentially modify it (by returning a
+/// different callback). The code doing the registration (e.g., `Future.then`)
+/// also remembers the current zone so that it can later run the callback in
+/// that zone.
+/// 2. At a later point the registered callback is run in the remembered zone,
+/// using one of [run], [runUnary] or [runBinary].
+///
+/// This is all handled internally by the platform code and most users don't need
+/// to worry about it. However, developers of new asynchronous operations,
+/// provided by the underlying system or through native extensions, must follow
+/// the protocol to be zone compatible.
+///
+/// For convenience, zones provide [bindCallback] (and the corresponding
+/// [bindUnaryCallback] and [bindBinaryCallback]) to make it easier to respect
+/// the zone contract: these functions first invoke the corresponding `register`
+/// functions and then wrap the returned function so that it runs in the current
+/// zone when it is later asynchronously invoked.
+///
+/// Similarly, zones provide [bindCallbackGuarded] (and the corresponding
+/// [bindUnaryCallbackGuarded] and [bindBinaryCallbackGuarded]), when the
+/// callback should be invoked through [Zone.runGuarded].
abstract class Zone {
// Private constructor so that it is not possible instantiate a Zone class.
Zone._();
- /**
- * The root zone.
- *
- * All isolate entry functions (`main` or spawned functions) start running in
- * the root zone (that is, [Zone.current] is identical to [Zone.root] when the
- * entry function is called). If no custom zone is created, the rest of the
- * program always runs in the root zone.
- *
- * The root zone implements the default behavior of all zone operations.
- * Many methods, like [registerCallback] do the bare minimum required of the
- * function, and are only provided as a hook for custom zones. Others, like
- * [scheduleMicrotask], interact with the underlying system to implement the
- * desired behavior.
- */
+ /// The root zone.
+ ///
+ /// All isolate entry functions (`main` or spawned functions) start running in
+ /// the root zone (that is, [Zone.current] is identical to [Zone.root] when the
+ /// entry function is called). If no custom zone is created, the rest of the
+ /// program always runs in the root zone.
+ ///
+ /// The root zone implements the default behavior of all zone operations.
+ /// Many methods, like [registerCallback] do the bare minimum required of the
+ /// function, and are only provided as a hook for custom zones. Others, like
+ /// [scheduleMicrotask], interact with the underlying system to implement the
+ /// desired behavior.
static const Zone root = _rootZone;
- /** The currently running zone. */
+ /// The currently running zone.
static _Zone _current = _rootZone;
- /** The zone that is currently active. */
+ /// The zone that is currently active.
static Zone get current => _current;
- /**
- * Handles uncaught asynchronous errors.
- *
- * There are two kind of asynchronous errors that are handled by this
- * function:
- * 1. Uncaught errors that were thrown in asynchronous callbacks, for example,
- * a `throw` in the function passed to [Timer.run].
- * 2. Asynchronous errors that are pushed through [Future] and [Stream]
- * chains, but for which no child registered an error handler.
- * Most asynchronous classes, like [Future] or [Stream] push errors to their
- * listeners. Errors are propagated this way until either a listener handles
- * the error (for example with [Future.catchError]), or no listener is
- * available anymore. In the latter case, futures and streams invoke the
- * zone's [handleUncaughtError].
- *
- * By default, when handled by the root zone, uncaught asynchronous errors are
- * treated like uncaught synchronous exceptions.
- */
+ /// Handles uncaught asynchronous errors.
+ ///
+ /// There are two kind of asynchronous errors that are handled by this
+ /// function:
+ /// 1. Uncaught errors that were thrown in asynchronous callbacks, for example,
+ /// a `throw` in the function passed to [Timer.run].
+ /// 2. Asynchronous errors that are pushed through [Future] and [Stream]
+ /// chains, but for which nobody registered an error handler.
+ /// Most asynchronous classes, like [Future] or [Stream] push errors to their
+ /// listeners. Errors are propagated this way until either a listener handles
+ /// the error (for example with [Future.catchError]), or no listener is
+ /// available anymore. In the latter case, futures and streams invoke the
+ /// zone's [handleUncaughtError].
+ ///
+ /// By default, when handled by the root zone, uncaught asynchronous errors are
+ /// treated like uncaught synchronous exceptions.
void handleUncaughtError(Object error, StackTrace stackTrace);
- /**
- * The parent zone of the this zone.
- *
- * Is `null` if `this` is the [root] zone.
- *
- * Zones are created by [fork] on an existing zone, or by [runZoned] which
- * forks the [current] zone. The new zone's parent zone is the zone it was
- * forked from.
- */
+ /// The parent zone of the this zone.
+ ///
+ /// Is `null` if `this` is the [root] zone.
+ ///
+ /// Zones are created by [fork] on an existing zone, or by [runZoned] which
+ /// forks the [current] zone. The new zone's parent zone is the zone it was
+ /// forked from.
Zone? get parent;
- /**
- * The error zone is the one that is responsible for dealing with uncaught
- * errors.
- *
- * This is the closest parent zone of this zone that provides a
- * [handleUncaughtError] method.
- *
- * Asynchronous errors never cross zone boundaries between zones with
- * different error handlers.
- *
- * Example:
- * ```
- * import 'dart:async';
- *
- * main() {
- * var future;
- * runZoned(() {
- * // The asynchronous error is caught by the custom zone which prints
- * // 'asynchronous error'.
- * future = Future.error("asynchronous error");
- * }, onError: (e) { print(e); }); // Creates a zone with an error handler.
- * // The following `catchError` handler is never invoked, because the
- * // custom zone created by the call to `runZoned` provides an
- * // error handler.
- * future.catchError((e) { throw "is never reached"; });
- * }
- * ```
- *
- * Note that errors cannot enter a child zone with a different error handler
- * either:
- * ```
- * import 'dart:async';
- *
- * main() {
- * runZoned(() {
- * // The following asynchronous error is *not* caught by the `catchError`
- * // in the nested zone, since errors are not to cross zone boundaries
- * // with different error handlers.
- * // Instead the error is handled by the current error handler,
- * // printing "Caught by outer zone: asynchronous error".
- * var future = Future.error("asynchronous error");
- * runZoned(() {
- * future.catchError((e) { throw "is never reached"; });
- * }, onError: (e) { throw "is never reached"; });
- * }, onError: (e) { print("Caught by outer zone: $e"); });
- * }
- * ```
- */
+ /// The error zone is responsible for dealing with uncaught errors.
+ ///
+ /// This is the closest parent zone of this zone that provides a
+ /// [handleUncaughtError] method.
+ ///
+ /// Asynchronous errors never cross zone boundaries between zones with
+ /// different error handlers.
+ ///
+ /// Example:
+ /// ```
+ /// import 'dart:async';
+ ///
+ /// main() {
+ /// var future;
+ /// runZoned(() {
+ /// // The asynchronous error is caught by the custom zone which prints
+ /// // 'asynchronous error'.
+ /// future = Future.error("asynchronous error");
+ /// }, onError: (e) { print(e); }); // Creates a zone with an error handler.
+ /// // The following `catchError` handler is never invoked, because the
+ /// // custom zone created by the call to `runZoned` provides an
+ /// // error handler.
+ /// future.catchError((e) { throw "is never reached"; });
+ /// }
+ /// ```
+ ///
+ /// Note that errors cannot enter a child zone with a different error handler
+ /// either:
+ /// ```
+ /// import 'dart:async';
+ ///
+ /// main() {
+ /// runZoned(() {
+ /// // The following asynchronous error is *not* caught by the `catchError`
+ /// // in the nested zone, since errors are not to cross zone boundaries
+ /// // with different error handlers.
+ /// // Instead the error is handled by the current error handler,
+ /// // printing "Caught by outer zone: asynchronous error".
+ /// var future = Future.error("asynchronous error");
+ /// runZoned(() {
+ /// future.catchError((e) { throw "is never reached"; });
+ /// }, onError: (e) { throw "is never reached"; });
+ /// }, onError: (e) { print("Caught by outer zone: $e"); });
+ /// }
+ /// ```
Zone get errorZone;
- /**
- * Returns true if `this` and [otherZone] are in the same error zone.
- *
- * Two zones are in the same error zone if they have the same [errorZone].
- */
+ /// Whether this zone and [otherZone] are in the same error zone.
+ ///
+ /// Two zones are in the same error zone if they have the same [errorZone].
bool inSameErrorZone(Zone otherZone);
- /**
- * Creates a new zone as a child of `this`.
- *
- * The new zone uses the closures in the given [specification] to override
- * the current's zone behavior. All specification entries that are `null`
- * inherit the behavior from the parent zone (`this`).
- *
- * The new zone inherits the stored values (accessed through [operator []])
- * of this zone and updates them with values from [zoneValues], which either
- * adds new values or overrides existing ones.
- *
- * Note that the fork operation is interceptible. A zone can thus change
- * the zone specification (or zone values), giving the forking zone full
- * control over the child zone.
- */
+ /// Creates a new zone as a child zone of this zone.
+ ///
+ /// The new zone uses the closures in the given [specification] to override
+ /// the current's zone behavior. All specification entries that are `null`
+ /// inherit the behavior from the parent zone (`this`).
+ ///
+ /// The new zone inherits the stored values (accessed through [operator []])
+ /// of this zone and updates them with values from [zoneValues], which either
+ /// adds new values or overrides existing ones.
+ ///
+ /// Note that the fork operation is interceptible. A zone can thus change
+ /// the zone specification (or zone values), giving the forking zone full
+ /// control over the child zone.
Zone fork(
{ZoneSpecification? specification, Map<Object?, Object?>? zoneValues});
- /**
- * Executes [action] in this zone.
- *
- * By default (as implemented in the [root] zone), runs [action]
- * with [current] set to this zone.
- *
- * If [action] throws, the synchronous exception is not caught by the zone's
- * error handler. Use [runGuarded] to achieve that.
- *
- * Since the root zone is the only zone that can modify the value of
- * [current], custom zones intercepting run should always delegate to their
- * parent zone. They may take actions before and after the call.
- */
+ /// Executes [action] in this zone.
+ ///
+ /// By default (as implemented in the [root] zone), runs [action]
+ /// with [current] set to this zone.
+ ///
+ /// If [action] throws, the synchronous exception is not caught by the zone's
+ /// error handler. Use [runGuarded] to achieve that.
+ ///
+ /// Since the root zone is the only zone that can modify the value of
+ /// [current], custom zones intercepting run should always delegate to their
+ /// parent zone. They may take actions before and after the call.
R run<R>(R action());
- /**
- * Executes the given [action] with [argument] in this zone.
- *
- * As [run] except that [action] is called with one [argument] instead of
- * none.
- */
+ /// Executes the given [action] with [argument] in this zone.
+ ///
+ /// As [run] except that [action] is called with one [argument] instead of
+ /// none.
R runUnary<R, T>(R action(T argument), T argument);
- /**
- * Executes the given [action] with [argument1] and [argument2] in this
- * zone.
- *
- * As [run] except that [action] is called with two arguments instead of none.
- */
+ /// Executes the given [action] with [argument1] and [argument2] in this
+ /// zone.
+ ///
+ /// As [run] except that [action] is called with two arguments instead of none.
R runBinary<R, T1, T2>(
R action(T1 argument1, T2 argument2), T1 argument1, T2 argument2);
- /**
- * Executes the given [action] in this zone and catches synchronous
- * errors.
- *
- * This function is equivalent to:
- * ```
- * try {
- * this.run(action);
- * } catch (e, s) {
- * this.handleUncaughtError(e, s);
- * }
- * ```
- *
- * See [run].
- */
+ /// Executes the given [action] in this zone and catches synchronous
+ /// errors.
+ ///
+ /// This function is equivalent to:
+ /// ```
+ /// try {
+ /// this.run(action);
+ /// } catch (e, s) {
+ /// this.handleUncaughtError(e, s);
+ /// }
+ /// ```
+ ///
+ /// See [run].
void runGuarded(void action());
- /**
- * Executes the given [action] with [argument] in this zone and
- * catches synchronous errors.
- *
- * See [runGuarded].
- */
+ /// Executes the given [action] with [argument] in this zone and
+ /// catches synchronous errors.
+ ///
+ /// See [runGuarded].
void runUnaryGuarded<T>(void action(T argument), T argument);
- /**
- * Executes the given [action] with [argument1] and [argument2] in this
- * zone and catches synchronous errors.
- *
- * See [runGuarded].
- */
+ /// Executes the given [action] with [argument1] and [argument2] in this
+ /// zone and catches synchronous errors.
+ ///
+ /// See [runGuarded].
void runBinaryGuarded<T1, T2>(
void action(T1 argument1, T2 argument2), T1 argument1, T2 argument2);
- /**
- * Registers the given callback in this zone.
- *
- * When implementing an asynchronous primitive that uses callbacks, the
- * callback must be registered using [registerCallback] at the point where the
- * user provides the callback. This allows zones to record other information
- * that they need at the same time, perhaps even wrapping the callback, so
- * that the callback is prepared when it is later run in the same zones
- * (using [run]). For example, a zone may decide
- * to store the stack trace (at the time of the registration) with the
- * callback.
- *
- * Returns the callback that should be used in place of the provided
- * [callback]. Frequently zones simply return the original callback.
- *
- * Custom zones may intercept this operation. The default implementation in
- * [Zone.root] returns the original callback unchanged.
- */
+ /// Registers the given callback in this zone.
+ ///
+ /// When implementing an asynchronous primitive that uses callbacks, the
+ /// callback must be registered using [registerCallback] at the point where the
+ /// user provides the callback. This allows zones to record other information
+ /// that they need at the same time, perhaps even wrapping the callback, so
+ /// that the callback is prepared when it is later run in the same zones
+ /// (using [run]). For example, a zone may decide
+ /// to store the stack trace (at the time of the registration) with the
+ /// callback.
+ ///
+ /// Returns the callback that should be used in place of the provided
+ /// [callback]. Frequently zones simply return the original callback.
+ ///
+ /// Custom zones may intercept this operation. The default implementation in
+ /// [Zone.root] returns the original callback unchanged.
ZoneCallback<R> registerCallback<R>(R callback());
- /**
- * Registers the given callback in this zone.
- *
- * Similar to [registerCallback] but with a unary callback.
- */
+ /// Registers the given callback in this zone.
+ ///
+ /// Similar to [registerCallback] but with a unary callback.
ZoneUnaryCallback<R, T> registerUnaryCallback<R, T>(R callback(T arg));
- /**
- * Registers the given callback in this zone.
- *
- * Similar to [registerCallback] but with a unary callback.
- */
+ /// Registers the given callback in this zone.
+ ///
+ /// Similar to [registerCallback] but with a unary callback.
ZoneBinaryCallback<R, T1, T2> registerBinaryCallback<R, T1, T2>(
R callback(T1 arg1, T2 arg2));
- /**
- * Registers the provided [callback] and returns a function that will
- * execute in this zone.
- *
- * Equivalent to:
- *
- * ZoneCallback registered = this.registerCallback(callback);
- * return () => this.run(registered);
- *
- */
+ /// Registers the provided [callback] and returns a function that will
+ /// execute in this zone.
+ ///
+ /// Equivalent to:
+ /// ```dart
+ /// ZoneCallback registered = this.registerCallback(callback);
+ /// return () => this.run(registered);
+ /// ```
ZoneCallback<R> bindCallback<R>(R callback());
- /**
- * Registers the provided [callback] and returns a function that will
- * execute in this zone.
- *
- * Equivalent to:
- *
- * ZoneCallback registered = this.registerUnaryCallback(callback);
- * return (arg) => thin.runUnary(registered, arg);
- */
+ /// Registers the provided [callback] and returns a function that will
+ /// execute in this zone.
+ ///
+ /// Equivalent to:
+ /// ```dart
+ /// ZoneCallback registered = this.registerUnaryCallback(callback);
+ /// return (arg) => thin.runUnary(registered, arg);
+ /// ```
ZoneUnaryCallback<R, T> bindUnaryCallback<R, T>(R callback(T argument));
- /**
- * Registers the provided [callback] and returns a function that will
- * execute in this zone.
- *
- * Equivalent to:
- *
- * ZoneCallback registered = registerBinaryCallback(callback);
- * return (arg1, arg2) => thin.runBinary(registered, arg1, arg2);
- */
+ /// Registers the provided [callback] and returns a function that will
+ /// execute in this zone.
+ ///
+ /// Equivalent to:
+ /// ```dart
+ /// ZoneCallback registered = registerBinaryCallback(callback);
+ /// return (arg1, arg2) => thin.runBinary(registered, arg1, arg2);
+ /// ```
ZoneBinaryCallback<R, T1, T2> bindBinaryCallback<R, T1, T2>(
R callback(T1 argument1, T2 argument2));
- /**
- * Registers the provided [callback] and returns a function that will
- * execute in this zone.
- *
- * When the function executes, errors are caught and treated as uncaught
- * errors.
- *
- * Equivalent to:
- *
- * ZoneCallback registered = this.registerCallback(callback);
- * return () => this.runGuarded(registered);
- *
- */
+ /// Registers the provided [callback] and returns a function that will
+ /// execute in this zone.
+ ///
+ /// When the function executes, errors are caught and treated as uncaught
+ /// errors.
+ ///
+ /// Equivalent to:
+ /// ```dart
+ /// ZoneCallback registered = this.registerCallback(callback);
+ /// return () => this.runGuarded(registered);
+ /// ```
void Function() bindCallbackGuarded(void Function() callback);
- /**
- * Registers the provided [callback] and returns a function that will
- * execute in this zone.
- *
- * When the function executes, errors are caught and treated as uncaught
- * errors.
- *
- * Equivalent to:
- *
- * ZoneCallback registered = this.registerUnaryCallback(callback);
- * return (arg) => this.runUnaryGuarded(registered, arg);
- */
+ /// Registers the provided [callback] and returns a function that will
+ /// execute in this zone.
+ ///
+ /// When the function executes, errors are caught and treated as uncaught
+ /// errors.
+ ///
+ /// Equivalent to:
+ /// ```dart
+ /// ZoneCallback registered = this.registerUnaryCallback(callback);
+ /// return (arg) => this.runUnaryGuarded(registered, arg);
+ /// ```
void Function(T) bindUnaryCallbackGuarded<T>(void callback(T argument));
- /**
- * Registers the provided [callback] and returns a function that will
- * execute in this zone.
- *
- * Equivalent to:
- *
- * ZoneCallback registered = registerBinaryCallback(callback);
- * return (arg1, arg2) => this.runBinaryGuarded(registered, arg1, arg2);
- */
+ /// Registers the provided [callback] and returns a function that will
+ /// execute in this zone.
+ ///
+ /// Equivalent to:
+ /// ```dart
+ /// ZoneCallback registered = registerBinaryCallback(callback);
+ /// return (arg1, arg2) => this.runBinaryGuarded(registered, arg1, arg2);
+ /// ```
void Function(T1, T2) bindBinaryCallbackGuarded<T1, T2>(
void callback(T1 argument1, T2 argument2));
- /**
- * Intercepts errors when added programmatically to a `Future` or `Stream`.
- *
- * When calling [Completer.completeError], [StreamController.addError],
- * or some [Future] constructors, the current zone is allowed to intercept
- * and replace the error.
- *
- * Future constructors invoke this function when the error is received
- * directly, for example with [Future.error], or when the error is caught
- * synchronously, for example with [Future.sync].
- *
- * There is no guarantee that an error is only sent through [errorCallback]
- * once. Libraries that use intermediate controllers or completers might
- * end up invoking [errorCallback] multiple times.
- *
- * Returns `null` if no replacement is desired. Otherwise returns an instance
- * of [AsyncError] holding the new pair of error and stack trace.
- *
- * Although not recommended, the returned instance may have its `error` member
- * ([AsyncError.error]) be equal to `null` in which case the error should be
- * replaced by a [NullThrownError].
- *
- * Custom zones may intercept this operation.
- *
- * Implementations of a new asynchronous primitive that converts synchronous
- * errors to asynchronous errors rarely need to invoke [errorCallback], since
- * errors are usually reported through future completers or stream
- * controllers.
- */
+ /// Intercepts errors when added programmatically to a [Future] or [Stream].
+ ///
+ /// When calling [Completer.completeError], [StreamController.addError],
+ /// or some [Future] constructors, the current zone is allowed to intercept
+ /// and replace the error.
+ ///
+ /// Future constructors invoke this function when the error is received
+ /// directly, for example with [Future.error], or when the error is caught
+ /// synchronously, for example with [Future.sync].
+ ///
+ /// There is no guarantee that an error is only sent through [errorCallback]
+ /// once. Libraries that use intermediate controllers or completers might
+ /// end up invoking [errorCallback] multiple times.
+ ///
+ /// Returns `null` if no replacement is desired. Otherwise returns an instance
+ /// of [AsyncError] holding the new pair of error and stack trace.
+ ///
+ /// Custom zones may intercept this operation.
+ ///
+ /// Implementations of a new asynchronous primitive that converts synchronous
+ /// errors to asynchronous errors rarely need to invoke [errorCallback], since
+ /// errors are usually reported through future completers or stream
+ /// controllers.
AsyncError? errorCallback(Object error, StackTrace? stackTrace);
- /**
- * Runs [callback] asynchronously in this zone.
- *
- * The global `scheduleMicrotask` delegates to the current zone's
- * [scheduleMicrotask]. The root zone's implementation interacts with the
- * underlying system to schedule the given callback as a microtask.
- *
- * Custom zones may intercept this operation (for example to wrap the given
- * [callback]).
- */
+ /// Runs [callback] asynchronously in this zone.
+ ///
+ /// The global `scheduleMicrotask` delegates to the [current] zone's
+ /// [scheduleMicrotask]. The root zone's implementation interacts with the
+ /// underlying system to schedule the given callback as a microtask.
+ ///
+ /// Custom zones may intercept this operation (for example to wrap the given
+ /// [callback]), or to implement their own microtask scheduler.
+ /// In the latter case, they will usually still use the parent zone's
+ /// [ZoneDelegate.scheduleMicrotask] to attach themselves to the existing
+ /// event loop.
void scheduleMicrotask(void Function() callback);
- /**
- * Creates a Timer where the callback is executed in this zone.
- */
+ /// Creates a [Timer] where the callback is executed in this zone.
Timer createTimer(Duration duration, void Function() callback);
- /**
- * Creates a periodic Timer where the callback is executed in this zone.
- */
+ /// Creates a periodic [Timer] where the callback is executed in this zone.
Timer createPeriodicTimer(Duration period, void callback(Timer timer));
- /**
- * Prints the given [line].
- *
- * The global `print` function delegates to the current zone's [print]
- * function which makes it possible to intercept printing.
- *
- * Example:
- * ```
- * import 'dart:async';
- *
- * main() {
- * runZoned(() {
- * // Ends up printing: "Intercepted: in zone".
- * print("in zone");
- * }, zoneSpecification: new ZoneSpecification(
- * print: (Zone self, ZoneDelegate parent, Zone zone, String line) {
- * parent.print(zone, "Intercepted: $line");
- * }));
- * }
- * ```
- */
+ /// Prints the given [line].
+ ///
+ /// The global `print` function delegates to the current zone's [print]
+ /// function which makes it possible to intercept printing.
+ ///
+ /// Example:
+ /// ```
+ /// import 'dart:async';
+ ///
+ /// main() {
+ /// runZoned(() {
+ /// // Ends up printing: "Intercepted: in zone".
+ /// print("in zone");
+ /// }, zoneSpecification: new ZoneSpecification(
+ /// print: (Zone self, ZoneDelegate parent, Zone zone, String line) {
+ /// parent.print(zone, "Intercepted: $line");
+ /// }));
+ /// }
+ /// ```
void print(String line);
- /**
- * Call to enter the Zone.
- *
- * The previous current zone is returned.
- */
+ /// Call to enter the [Zone].
+ ///
+ /// The previous current zone is returned.
static _Zone _enter(_Zone zone) {
assert(!identical(zone, _current));
_Zone previous = _current;
@@ -713,27 +889,23 @@
return previous;
}
- /**
- * Call to leave the Zone.
- *
- * The previous Zone must be provided as `previous`.
- */
+ /// Call to leave the [Zone].
+ ///
+ /// The previous [Zone] must be provided as `previous`.
static void _leave(_Zone previous) {
assert(previous != null);
Zone._current = previous;
}
- /**
- * Retrieves the zone-value associated with [key].
- *
- * If this zone does not contain the value looks up the same key in the
- * parent zone. If the [key] is not found returns `null`.
- *
- * Any object can be used as key, as long as it has compatible `operator ==`
- * and `hashCode` implementations.
- * By controlling access to the key, a zone can grant or deny access to the
- * zone value.
- */
+ /// Retrieves the zone-value associated with [key].
+ ///
+ /// If this zone does not contain the value looks up the same key in the
+ /// parent zone. If the [key] is not found returns `null`.
+ ///
+ /// Any object can be used as key, as long as it has compatible `operator ==`
+ /// and `hashCode` implementations.
+ /// By controlling access to the key, a zone can grant or deny access to the
+ /// zone value.
dynamic operator [](Object? key);
}
@@ -839,9 +1011,7 @@
}
}
-/**
- * Base class for Zone implementations.
- */
+/// Base class for Zone implementations.
abstract class _Zone implements Zone {
const _Zone();
@@ -981,12 +1151,10 @@
}
}
- /**
- * The closest error-handling zone.
- *
- * Returns `this` if `this` has an error-handler. Otherwise returns the
- * parent's error-zone.
- */
+ /// The closest error-handling zone.
+ ///
+ /// Returns this zone if it has an error-handler. Otherwise returns the
+ /// parent's error-zone.
Zone get errorZone => _handleUncaughtError.zone;
void runGuarded(void f()) {
@@ -1352,12 +1520,10 @@
// It's a lie, but the root zone never uses the parent delegate.
ZoneDelegate get _parentDelegate => _delegate;
- /**
- * The closest error-handling zone.
- *
- * Returns `this` if `this` has an error-handler. Otherwise returns the
- * parent's error-zone.
- */
+ /// The closest error-handling zone.
+ ///
+ /// Returns `this` if `this` has an error-handler. Otherwise returns the
+ /// parent's error-zone.
Zone get errorZone => this;
// Zone interface.
@@ -1481,49 +1647,47 @@
const _Zone _rootZone = const _RootZone();
-/**
- * Runs [body] in its own zone.
- *
- * Creates a new zone using [Zone.fork] based on [zoneSpecification] and
- * [zoneValues], then runs [body] in that zone and returns the result.
- *
- * If [onError] is provided, it must have one of the types
- * * `void Function(Object)`
- * * `void Function(Object, StackTrace)`
- * and the [onError] handler is used *both* to handle asynchronous errors
- * by overriding [ZoneSpecification.handleUncaughtError] in [zoneSpecification],
- * if any, *and* to handle errors thrown synchronously by the call to [body].
- *
- * If an error occurs synchronously in [body],
- * then throwing in the [onError] handler
- * makes the call to `runZone` throw that error,
- * and otherwise the call to `runZoned` attempt to return `null`.
- *
- * If the zone specification has a `handleUncaughtError` value or the [onError]
- * parameter is provided, the zone becomes an error-zone.
- *
- * Errors will never cross error-zone boundaries by themselves.
- * Errors that try to cross error-zone boundaries are considered uncaught in
- * their originating error zone.
- *
- * var future = new Future.value(499);
- * runZoned(() {
- * var future2 = future.then((_) { throw "error in first error-zone"; });
- * runZoned(() {
- * var future3 = future2.catchError((e) { print("Never reached!"); });
- * }, onError: (e, s) { print("unused error handler"); });
- * }, onError: (e, s) { print("catches error of first error-zone."); });
- *
- * Example:
- *
- * runZoned(() {
- * new Future(() { throw "asynchronous error"; });
- * }, onError: (e, s) => print(e)); // Will print "asynchronous error".
- *
- * It is possible to manually pass an error from one error zone to another
- * by re-throwing it in the new zone. If [onError] throws, that error will
- * occur in the original zone where [runZoned] was called.
- */
+/// Runs [body] in its own zone.
+///
+/// Creates a new zone using [Zone.fork] based on [zoneSpecification] and
+/// [zoneValues], then runs [body] in that zone and returns the result.
+///
+/// If [onError] is provided, it must have one of the types
+/// * `void Function(Object)`
+/// * `void Function(Object, StackTrace)`
+/// and the [onError] handler is used *both* to handle asynchronous errors
+/// by overriding [ZoneSpecification.handleUncaughtError] in [zoneSpecification],
+/// if any, *and* to handle errors thrown synchronously by the call to [body].
+///
+/// If an error occurs synchronously in [body],
+/// then throwing in the [onError] handler
+/// makes the call to `runZone` throw that error,
+/// and otherwise the call to `runZoned` attempt to return `null`.
+///
+/// If the zone specification has a `handleUncaughtError` value or the [onError]
+/// parameter is provided, the zone becomes an error-zone.
+///
+/// Errors will never cross error-zone boundaries by themselves.
+/// Errors that try to cross error-zone boundaries are considered uncaught in
+/// their originating error zone.
+/// ```dart
+/// var future = Future.value(499);
+/// runZoned(() {
+/// var future2 = future.then((_) { throw "error in first error-zone"; });
+/// runZoned(() {
+/// var future3 = future2.catchError((e) { print("Never reached!"); });
+/// }, onError: (e, s) { print("unused error handler"); });
+/// }, onError: (e, s) { print("catches error of first error-zone."); });
+/// ```
+/// Example:
+/// ```dart
+/// runZoned(() {
+/// Future(() { throw "asynchronous error"; });
+/// }, onError: (e, s) => print(e)); // Will print "asynchronous error".
+/// ```
+/// It is possible to manually pass an error from one error zone to another
+/// by re-throwing it in the new zone. If [onError] throws, that error will
+/// occur in the original zone where [runZoned] was called.
R runZoned<R>(R body(),
{Map<Object?, Object?>? zoneValues,
ZoneSpecification? zoneSpecification,
@@ -1546,45 +1710,43 @@
return _runZoned<R>(body, zoneValues, zoneSpecification);
}
-/**
- * Runs [body] in its own error zone.
- *
- * Creates a new zone using [Zone.fork] based on [zoneSpecification] and
- * [zoneValues], then runs [body] in that zone and returns the result.
- *
- * The [onError] function is used *both* to handle asynchronous errors
- * by overriding [ZoneSpecification.handleUncaughtError] in [zoneSpecification],
- * if any, *and* to handle errors thrown synchronously by the call to [body].
- *
- * If an error occurs synchronously in [body],
- * then throwing in the [onError] handler
- * makes the call to `runZonedGuarded` throw that error,
- * and otherwise the call to `runZonedGuarded` returns `null`.
- *
- * The zone will always be an error-zone.
- *
- * Errors will never cross error-zone boundaries by themselves.
- * Errors that try to cross error-zone boundaries are considered uncaught in
- * their originating error zone.
- * ```dart
- * var future = Future.value(499);
- * runZonedGuarded(() {
- * var future2 = future.then((_) { throw "error in first error-zone"; });
- * runZonedGuarded(() {
- * var future3 = future2.catchError((e) { print("Never reached!"); });
- * }, (e, s) { print("unused error handler"); });
- * }, (e, s) { print("catches error of first error-zone."); });
- * ```
- * Example:
- * ```dart
- * runZonedGuarded(() {
- * new Future(() { throw "asynchronous error"; });
- * }, (e, s) => print(e)); // Will print "asynchronous error".
- * ```
- * It is possible to manually pass an error from one error zone to another
- * by re-throwing it in the new zone. If [onError] throws, that error will
- * occur in the original zone where [runZoned] was called.
- */
+/// Runs [body] in its own error zone.
+///
+/// Creates a new zone using [Zone.fork] based on [zoneSpecification] and
+/// [zoneValues], then runs [body] in that zone and returns the result.
+///
+/// The [onError] function is used *both* to handle asynchronous errors
+/// by overriding [ZoneSpecification.handleUncaughtError] in [zoneSpecification],
+/// if any, *and* to handle errors thrown synchronously by the call to [body].
+///
+/// If an error occurs synchronously in [body],
+/// then throwing in the [onError] handler
+/// makes the call to `runZonedGuarded` throw that error,
+/// and otherwise the call to `runZonedGuarded` returns `null`.
+///
+/// The zone will always be an error-zone.
+///
+/// Errors will never cross error-zone boundaries by themselves.
+/// Errors that try to cross error-zone boundaries are considered uncaught in
+/// their originating error zone.
+/// ```dart
+/// var future = Future.value(499);
+/// runZonedGuarded(() {
+/// var future2 = future.then((_) { throw "error in first error-zone"; });
+/// runZonedGuarded(() {
+/// var future3 = future2.catchError((e) { print("Never reached!"); });
+/// }, (e, s) { print("unused error handler"); });
+/// }, (e, s) { print("catches error of first error-zone."); });
+/// ```
+/// Example:
+/// ```dart
+/// runZonedGuarded(() {
+/// Future(() { throw "asynchronous error"; });
+/// }, (e, s) => print(e)); // Will print "asynchronous error".
+/// ```
+/// It is possible to manually pass an error from one error zone to another
+/// by re-throwing it in the new zone. If [onError] throws, that error will
+/// occur in the original zone where [runZoned] was called.
@Since("2.8")
R? runZonedGuarded<R>(R body(), void onError(Object error, StackTrace stack),
{Map<Object?, Object?>? zoneValues, ZoneSpecification? zoneSpecification}) {
diff --git a/sdk/lib/core/duration.dart b/sdk/lib/core/duration.dart
index facbcdf..57075cb 100644
--- a/sdk/lib/core/duration.dart
+++ b/sdk/lib/core/duration.dart
@@ -55,12 +55,22 @@
static const int millisecondsPerSecond = 1000;
/// The number of seconds per minute.
+ ///
+ /// Notice that some minutes of official clock time might
+ /// differ in length because of leap seconds.
+ /// The [Duration] and [DateTime] classes ignore leap seconds
+ /// and consider all minutes to have 60 seconds.
static const int secondsPerMinute = 60;
/// The number of minutes per hour.
static const int minutesPerHour = 60;
/// The number of hours per day.
+ ///
+ /// Notice that some days may differ in length because
+ /// of time zone changes due to daylight saving.
+ /// The [Duration] class is time zone agnostic and
+ /// considers all days to have 24 hours.
static const int hoursPerDay = 24;
/// The number of microseconds per second.
diff --git a/tools/VERSION b/tools/VERSION
index 5fccaeb..f98a1fa 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
MAJOR 2
MINOR 12
PATCH 0
-PRERELEASE 224
+PRERELEASE 225
PRERELEASE_PATCH 0
\ No newline at end of file