Version 2.14.0-237.0.dev
Merge commit '9b4ff5e5a6eeced3a8ad7ea57af906d2f4a570bc' into 'dev'
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4b4cef9..7d5d4ba3 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -12,9 +12,14 @@
#### `dart:async`
-* The uncaught error handlers of `Zone`s are now run in the parent zone
- of the zone where they were declared. This prevents a throwing handler
- from causing an infinite loop by repeatedly triggering itself.
+* The uncaught error handlers of `Zone`s are now run in the parent zone
+ of the zone where they were declared. This prevents a throwing handler
+ from causing an infinite loop by repeatedly triggering itself.
+
+* Added `ignore()` as extension member on futures.
+
+* Added `void unawaited(Future)` top-level function to deal with
+ the `unawaited_futures` lint.
#### `dart:core`
diff --git a/pkg/analysis_server/lib/src/computer/computer_signature.dart b/pkg/analysis_server/lib/src/computer/computer_signature.dart
index 04fdea1..0e852c2 100644
--- a/pkg/analysis_server/lib/src/computer/computer_signature.dart
+++ b/pkg/analysis_server/lib/src/computer/computer_signature.dart
@@ -28,7 +28,6 @@
bool get offsetIsValid => _node != null;
- // Return the closest argument list surrounding the [_node].
/// Returns the computed signature information, maybe `null`.
AnalysisGetSignatureResult? compute() {
var argumentList = _findArgumentList();
@@ -80,6 +79,7 @@
defaultValue: param.defaultValueCode);
}
+ /// Return the closest argument list surrounding the [_node].
ArgumentList? _findArgumentList() {
var node = _node;
while (node != null && node is! ArgumentList) {
diff --git a/pkg/analysis_server/lib/src/computer/computer_type_arguments_signature.dart b/pkg/analysis_server/lib/src/computer/computer_type_arguments_signature.dart
new file mode 100644
index 0000000..58aafe2
--- /dev/null
+++ b/pkg/analysis_server/lib/src/computer/computer_type_arguments_signature.dart
@@ -0,0 +1,116 @@
+// 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.
+
+import 'package:analysis_server/lsp_protocol/protocol_generated.dart' as lsp;
+import 'package:analysis_server/src/computer/computer_hover.dart';
+import 'package:analysis_server/src/lsp/dartdoc.dart';
+import 'package:analysis_server/src/lsp/mapping.dart';
+import 'package:analysis_server/src/utilities/extensions/ast.dart';
+import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer/dart/element/element.dart';
+import 'package:analyzer/src/dart/ast/element_locator.dart';
+import 'package:analyzer/src/dart/ast/utilities.dart';
+import 'package:analyzer/src/dartdoc/dartdoc_directive_info.dart';
+
+/// A computer for the signature help information about the type parameters for
+/// the [TypeArgumentList] surrounding the specified offset of a Dart
+/// [CompilationUnit].
+class DartTypeArgumentsSignatureComputer {
+ final DartdocDirectiveInfo _dartdocInfo;
+ final AstNode? _node;
+ final Set<lsp.MarkupKind>? preferredFormats;
+ late TypeArgumentList _argumentList;
+ final bool _isNonNullableByDefault;
+ DartTypeArgumentsSignatureComputer(
+ this._dartdocInfo,
+ CompilationUnit _unit,
+ int _offset,
+ this.preferredFormats,
+ ) : _node = NodeLocator(_offset).searchWithin(_unit),
+ _isNonNullableByDefault = _unit.isNonNullableByDefault;
+
+ /// The [TypeArgumentList] node located by [compute].
+ TypeArgumentList get argumentList => _argumentList;
+
+ bool get offsetIsValid => _node != null;
+
+ /// Returns the computed signature information, maybe `null`.
+ lsp.SignatureHelp? compute() {
+ var argumentList = _findTypeArgumentList();
+ if (argumentList == null) {
+ return null;
+ }
+ final parent = argumentList.parent;
+ Element? element;
+ if (parent is TypeName) {
+ element = ElementLocator.locate(parent.name);
+ } else if (parent is MethodInvocation) {
+ element = ElementLocator.locate(parent.methodName);
+ }
+ if (element is! TypeParameterizedElement ||
+ element.typeParameters.isEmpty) {
+ return null;
+ }
+
+ _argumentList = argumentList;
+
+ final label =
+ element.getDisplayString(withNullability: _isNonNullableByDefault);
+ final documentation =
+ DartUnitHoverComputer.computeDocumentation(_dartdocInfo, element)?.full;
+
+ return _toSignatureHelp(
+ label,
+ cleanDartdoc(documentation),
+ element.typeParameters,
+ );
+ }
+
+ /// Return the closest type argument list surrounding the [_node].
+ TypeArgumentList? _findTypeArgumentList() {
+ var node = _node;
+ while (node != null && node is! TypeArgumentList) {
+ // Certain nodes don't make sense to search above for an argument list
+ // (for example when inside a function expression).
+ if (node is FunctionExpression) {
+ return null;
+ }
+ node = node.parent;
+ }
+ return node as TypeArgumentList?;
+ }
+
+ /// Builds an [lsp.SignatureHelp] for the given type parameters.
+ lsp.SignatureHelp? _toSignatureHelp(
+ String label,
+ String? documentation,
+ List<TypeParameterElement> typeParameters,
+ ) {
+ final parameters = typeParameters
+ .map((param) => lsp.ParameterInformation(
+ label: param.getDisplayString(
+ withNullability: _isNonNullableByDefault),
+ ))
+ .toList();
+
+ final signatures = [
+ lsp.SignatureInformation(
+ label: label,
+ documentation: documentation != null
+ ? asStringOrMarkupContent(preferredFormats, documentation)
+ : null,
+ parameters: parameters,
+ ),
+ ];
+
+ return lsp.SignatureHelp(
+ signatures: signatures,
+ activeSignature: 0,
+ // As with [toSignatureHelp], we don't currently use this, but need to set
+ // it to something that doesn't match a parameter to avoid one being
+ // selected.
+ activeParameter: -1,
+ );
+ }
+}
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handler_completion.dart b/pkg/analysis_server/lib/src/lsp/handlers/handler_completion.dart
index cf29def..ccf8bb2 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/handler_completion.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_completion.dart
@@ -474,9 +474,9 @@
node = node.parent;
}
return (node is ast.InvocationExpression &&
- node.argumentList.length != 0) ||
+ !node.argumentList.beginToken.isSynthetic) ||
(node is ast.InstanceCreationExpression &&
- node.argumentList.length != 0);
+ !node.argumentList.beginToken.isSynthetic);
}
Iterable<CompletionItem> _pluginResultsToItems(
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handler_signature_help.dart b/pkg/analysis_server/lib/src/lsp/handlers/handler_signature_help.dart
index b57381e..34f4193 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/handler_signature_help.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_signature_help.dart
@@ -5,9 +5,12 @@
import 'package:analysis_server/lsp_protocol/protocol_generated.dart';
import 'package:analysis_server/lsp_protocol/protocol_special.dart';
import 'package:analysis_server/src/computer/computer_signature.dart';
+import 'package:analysis_server/src/computer/computer_type_arguments_signature.dart';
import 'package:analysis_server/src/lsp/handlers/handlers.dart';
import 'package:analysis_server/src/lsp/lsp_analysis_server.dart';
import 'package:analysis_server/src/lsp/mapping.dart';
+import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer/src/dartdoc/dartdoc_directive_info.dart';
class SignatureHelpHandler
extends MessageHandler<SignatureHelpParams, SignatureHelp?> {
@@ -52,10 +55,24 @@
final offset = await unit.mapResult((unit) => toOffset(unit.lineInfo, pos));
return offset.mapResult((offset) {
- final computer = DartUnitSignatureComputer(
- server.getDartdocDirectiveInfoFor(unit.result),
- unit.result.unit!,
- offset);
+ final formats = clientCapabilities.signatureHelpDocumentationFormats;
+ final dartDocInfo = server.getDartdocDirectiveInfoFor(unit.result);
+
+ // First check if we're in a type args list and if so build some
+ // signature help for that.
+ final typeArgsSignature = _tryGetTypeArgsSignatureHelp(
+ dartDocInfo,
+ unit.result.unit!,
+ offset,
+ autoTriggered,
+ formats,
+ );
+ if (typeArgsSignature != null) {
+ return success(typeArgsSignature);
+ }
+
+ final computer =
+ DartUnitSignatureComputer(dartDocInfo, unit.result.unit!, offset);
if (!computer.offsetIsValid) {
return success(null); // No error, just no valid hover.
}
@@ -72,8 +89,39 @@
return success(null);
}
- final formats = clientCapabilities.signatureHelpDocumentationFormats;
return success(toSignatureHelp(formats, signature));
});
}
+
+ /// Tries to create signature information for a surrounding [TypeArgumentList].
+ ///
+ /// Returns `null` if [offset] is in an invalid location, not inside a type
+ /// argument list or was auto-triggered in a location that was not the start
+ /// of a type argument list.
+ SignatureHelp? _tryGetTypeArgsSignatureHelp(
+ DartdocDirectiveInfo dartDocInfo,
+ CompilationUnit unit,
+ int offset,
+ bool autoTriggered,
+ Set<MarkupKind>? formats,
+ ) {
+ final typeArgsComputer =
+ DartTypeArgumentsSignatureComputer(dartDocInfo, unit, offset, formats);
+ if (!typeArgsComputer.offsetIsValid) {
+ return null;
+ }
+
+ final typeSignature = typeArgsComputer.compute();
+ if (typeSignature == null) {
+ return null;
+ }
+
+ // If auto-triggered from typing a `<`, only show if that `<` was at
+ // the start of the arg list (to avoid triggering on other `<`s).
+ if (autoTriggered && offset != typeArgsComputer.argumentList.offset + 1) {
+ return null;
+ }
+
+ return typeSignature;
+ }
}
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/add_type_annotation.dart b/pkg/analysis_server/lib/src/services/correction/dart/add_type_annotation.dart
index 8a89c1f..95ddbc7 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/add_type_annotation.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/add_type_annotation.dart
@@ -8,7 +8,10 @@
import 'package:analysis_server/src/services/correction/fix.dart';
import 'package:analysis_server/src/utilities/extensions/ast.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/dart/element/type.dart';
+import 'package:analyzer/dart/element/type_system.dart';
import 'package:analyzer_plugin/src/utilities/change_builder/change_builder_core.dart';
import 'package:analyzer_plugin/utilities/assist/assist.dart';
import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart';
@@ -58,6 +61,31 @@
}
}
+ Future<void> _applyChange(
+ ChangeBuilder builder, Token? keyword, int offset, DartType type) async {
+ Future<bool> tryToApplyChange(ChangeBuilder builder) async {
+ var validChange = true;
+ await builder.addDartFileEdit(file, (builder) {
+ if (keyword != null && keyword.keyword == Keyword.VAR) {
+ builder.addReplacement(range.token(keyword), (builder) {
+ validChange = builder.writeType(type);
+ });
+ } else {
+ builder.addInsertion(offset, (builder) {
+ validChange = builder.writeType(type);
+ builder.write(' ');
+ });
+ }
+ });
+ return validChange;
+ }
+
+ _configureTargetLocation(node);
+ if (await tryToApplyChange(_temporaryBuilder(builder))) {
+ await tryToApplyChange(builder);
+ }
+ }
+
/// Configure the [utils] using the given [target].
void _configureTargetLocation(Object target) {
utils.targetClassElement = null;
@@ -80,29 +108,8 @@
if (type is! InterfaceType && type is! FunctionType) {
return;
}
- _configureTargetLocation(node);
-
- Future<bool> applyChange(ChangeBuilder builder) async {
- var validChange = true;
- await builder.addDartFileEdit(file, (builder) {
- var keyword = declaredIdentifier.keyword;
- if (keyword != null && keyword.keyword == Keyword.VAR) {
- builder.addReplacement(range.token(keyword), (builder) {
- validChange = builder.writeType(type);
- });
- } else {
- builder.addInsertion(declaredIdentifier.identifier.offset, (builder) {
- validChange = builder.writeType(type);
- builder.write(' ');
- });
- }
- });
- return validChange;
- }
-
- if (await applyChange(_temporaryBuilder(builder))) {
- await applyChange(builder);
- }
+ await _applyChange(builder, declaredIdentifier.keyword,
+ declaredIdentifier.identifier.offset, type);
}
Future<void> _forSimpleFormalParameter(ChangeBuilder builder,
@@ -120,24 +127,7 @@
if (type is! InterfaceType) {
return;
}
- _configureTargetLocation(node);
-
- Future<bool> applyChange(ChangeBuilder builder) async {
- var validChange = true;
- await builder.addDartFileEdit(file, (builder) {
- builder.addInsertion(name.offset, (builder) {
- validChange = builder.writeType(type);
- if (validChange) {
- builder.write(' ');
- }
- });
- });
- return validChange;
- }
-
- if (await applyChange(_temporaryBuilder(builder))) {
- await applyChange(builder);
- }
+ await _applyChange(builder, null, name.offset, type);
}
Future<void> _forVariableDeclaration(
@@ -157,44 +147,82 @@
return;
}
// Ensure that there is an initializer to get the type from.
- var initializer = variable.initializer;
- if (initializer == null) {
+ var type = _typeForVariable(variable);
+ if (type == null) {
return;
}
- var type = initializer.staticType;
- // prepare type source
if ((type is! InterfaceType || type.isDartCoreNull) &&
type is! FunctionType) {
return;
}
- _configureTargetLocation(node);
-
- Future<bool> applyChange(ChangeBuilder builder) async {
- var validChange = true;
- await builder.addDartFileEdit(file, (builder) {
- var keyword = declarationList.keyword;
- if (keyword != null && keyword.keyword == Keyword.VAR) {
- builder.addReplacement(range.token(keyword), (builder) {
- validChange = builder.writeType(type);
- });
- } else {
- builder.addInsertion(variable.offset, (builder) {
- validChange = builder.writeType(type);
- builder.write(' ');
- });
- }
- });
- return validChange;
- }
-
- if (await applyChange(_temporaryBuilder(builder))) {
- await applyChange(builder);
- }
+ await _applyChange(builder, declarationList.keyword, variable.offset, type);
}
ChangeBuilder _temporaryBuilder(ChangeBuilder builder) =>
ChangeBuilder(workspace: (builder as ChangeBuilderImpl).workspace);
+ DartType? _typeForVariable(VariableDeclaration variable) {
+ var initializer = variable.initializer;
+ if (initializer != null) {
+ return initializer.staticType;
+ }
+ // The parents should be a [VariableDeclarationList],
+ // [VariableDeclarationStatement], and [Block], in that order.
+ var statement = variable.parent?.parent;
+ var block = statement?.parent;
+ if (statement is! VariableDeclarationStatement || block is! Block) {
+ return null;
+ }
+ var element = variable.declaredElement;
+ if (element is! LocalVariableElement) {
+ return null;
+ }
+ var statements = block.statements;
+ var index = statements.indexOf(statement);
+ var visitor = _AssignedTypeCollector(typeSystem, element);
+ for (var i = index + 1; i < statements.length; i++) {
+ statements[i].accept(visitor);
+ }
+ return visitor.bestType;
+ }
+
/// Return an instance of this class. Used as a tear-off in `FixProcessor`.
static AddTypeAnnotation newInstance() => AddTypeAnnotation();
}
+
+class _AssignedTypeCollector extends RecursiveAstVisitor<void> {
+ /// The type system used to compute the best type.
+ final TypeSystem typeSystem;
+
+ final LocalVariableElement variable;
+
+ /// The types that are assigned to the variable.
+ final Set<DartType> assignedTypes = {};
+
+ _AssignedTypeCollector(this.typeSystem, this.variable);
+
+ DartType? get bestType {
+ if (assignedTypes.isEmpty) {
+ return null;
+ }
+ var types = assignedTypes.toList();
+ var bestType = types[0];
+ for (var i = 1; i < assignedTypes.length; i++) {
+ bestType = typeSystem.leastUpperBound(bestType, types[i]);
+ }
+ return bestType;
+ }
+
+ @override
+ void visitAssignmentExpression(AssignmentExpression node) {
+ var leftHandSide = node.leftHandSide;
+ if (leftHandSide is SimpleIdentifier &&
+ leftHandSide.staticElement == variable) {
+ var type = node.rightHandSide.staticType;
+ if (type != null) {
+ assignedTypes.add(type);
+ }
+ }
+ return super.visitAssignmentExpression(node);
+ }
+}
diff --git a/pkg/analysis_server/lib/src/services/correction/fix.dart b/pkg/analysis_server/lib/src/services/correction/fix.dart
index f2a7e4b..2c2cf47 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix.dart
@@ -640,11 +640,11 @@
static const REMOVE_THIS_EXPRESSION = FixKind(
'dart.fix.remove.thisExpression',
DartFixKindPriority.DEFAULT,
- 'Remove this expression');
+ "Remove 'this' expression");
static const REMOVE_THIS_EXPRESSION_MULTI = FixKind(
'dart.fix.remove.thisExpression.multi',
DartFixKindPriority.IN_FILE,
- 'Remove unnecessary this expressions everywhere in file');
+ "Remove unnecessary 'this' expressions everywhere in file");
static const REMOVE_TYPE_ANNOTATION = FixKind(
'dart.fix.remove.typeAnnotation',
DartFixKindPriority.DEFAULT,
diff --git a/pkg/analysis_server/lib/src/services/correction/fix_internal.dart b/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
index 61a67e1..de4a8fc 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
@@ -501,6 +501,9 @@
LintNames.prefer_spread_collections: [
ConvertAddAllToSpread.newInstance,
],
+ LintNames.prefer_typing_uninitialized_variables: [
+ AddTypeAnnotation.newInstance,
+ ],
LintNames.slash_for_doc_comments: [
ConvertDocumentationIntoLine.newInstance,
],
diff --git a/pkg/analysis_server/lib/src/services/correction/util.dart b/pkg/analysis_server/lib/src/services/correction/util.dart
index 0409ea4..31d0876 100644
--- a/pkg/analysis_server/lib/src/services/correction/util.dart
+++ b/pkg/analysis_server/lib/src/services/correction/util.dart
@@ -17,6 +17,7 @@
import 'package:analyzer/dart/ast/precedence.dart';
import 'package:analyzer/dart/ast/visitor.dart';
import 'package:analyzer/dart/element/element.dart';
+import 'package:analyzer/dart/element/nullability_suffix.dart';
import 'package:analyzer/dart/element/type.dart';
import 'package:analyzer/error/listener.dart';
import 'package:analyzer/src/dart/ast/extensions.dart';
@@ -865,6 +866,7 @@
return _getTypeCodeElementArguments(
librariesToImport: librariesToImport,
element: aliasElement,
+ isNullable: type.nullabilitySuffix == NullabilitySuffix.question,
typeArguments: aliasArguments,
);
}
@@ -895,6 +897,7 @@
return _getTypeCodeElementArguments(
librariesToImport: librariesToImport,
element: type.element,
+ isNullable: type.nullabilitySuffix == NullabilitySuffix.question,
typeArguments: type.typeArguments,
);
}
@@ -1161,6 +1164,7 @@
String? _getTypeCodeElementArguments({
required Set<Source> librariesToImport,
required Element element,
+ required bool isNullable,
required List<DartType> typeArguments,
}) {
var sb = StringBuffer();
@@ -1188,6 +1192,9 @@
// append simple name
var name = element.displayName;
sb.write(name);
+ if (isNullable) {
+ sb.write('?');
+ }
// append type arguments
if (typeArguments.isNotEmpty) {
diff --git a/pkg/analysis_server/lib/src/services/linter/lint_names.dart b/pkg/analysis_server/lib/src/services/linter/lint_names.dart
index 20e6839..e091101 100644
--- a/pkg/analysis_server/lib/src/services/linter/lint_names.dart
+++ b/pkg/analysis_server/lib/src/services/linter/lint_names.dart
@@ -88,6 +88,8 @@
static const String prefer_relative_imports = 'prefer_relative_imports';
static const String prefer_single_quotes = 'prefer_single_quotes';
static const String prefer_spread_collections = 'prefer_spread_collections';
+ static const String prefer_typing_uninitialized_variables =
+ 'prefer_typing_uninitialized_variables';
static const String slash_for_doc_comments = 'slash_for_doc_comments';
static const String sort_child_properties_last = 'sort_child_properties_last';
static const String sort_constructors_first = 'sort_constructors_first';
diff --git a/pkg/analysis_server/test/edit/refactoring_test.dart b/pkg/analysis_server/test/edit/refactoring_test.dart
index d14a01d..9c02d35 100644
--- a/pkg/analysis_server/test/edit/refactoring_test.dart
+++ b/pkg/analysis_server/test/edit/refactoring_test.dart
@@ -763,6 +763,43 @@
''');
}
+ Future<void> test_statements_nullableReturnType() {
+ addTestFile('''
+void foo(int b) {
+// start
+ int? x;
+ if (b < 2) {
+ x = 42;
+ }
+ if (b >= 2) {
+ x = 43;
+ }
+// end
+ print(x!);
+}
+''');
+ _setOffsetLengthForStartEnd();
+ return assertSuccessfulRefactoring(_computeChange, '''
+void foo(int b) {
+// start
+ int? x = res(b);
+// end
+ print(x!);
+}
+
+int? res(int b) {
+ int? x;
+ if (b < 2) {
+ x = 42;
+ }
+ if (b >= 2) {
+ x = 43;
+ }
+ return x;
+}
+''');
+ }
+
Future<Response> _computeChange() async {
await _prepareOptions();
// send request with the options
diff --git a/pkg/analysis_server/test/lsp/completion_dart_test.dart b/pkg/analysis_server/test/lsp/completion_dart_test.dart
index 072fc7b..fee07a1 100644
--- a/pkg/analysis_server/test/lsp/completion_dart_test.dart
+++ b/pkg/analysis_server/test/lsp/completion_dart_test.dart
@@ -268,6 +268,20 @@
insertText: 'print',
);
+ Future<void> test_completeFunctionCalls_existingPartialArgList() =>
+ checkCompleteFunctionCallInsertText(
+ '''
+ class Aaaaa {
+ Aaaaa(int a);
+ }
+ void main(int aaa) {
+ var a = new [[Aaa^]](
+ }
+ ''',
+ 'Aaaaa(…)',
+ insertText: 'Aaaaa',
+ );
+
Future<void> test_completeFunctionCalls_expression() =>
checkCompleteFunctionCallInsertText(
'''
diff --git a/pkg/analysis_server/test/lsp/signature_help_test.dart b/pkg/analysis_server/test/lsp/signature_help_test.dart
index dacaefe..edaeefd 100644
--- a/pkg/analysis_server/test/lsp/signature_help_test.dart
+++ b/pkg/analysis_server/test/lsp/signature_help_test.dart
@@ -465,6 +465,86 @@
));
}
+ Future<void> test_typeParams_class() async {
+ final content = '''
+ /// My Foo.
+ class Foo<T1, T2 extends String> {}
+
+ class Bar extends Foo<^> {}
+ ''';
+
+ const expectedLabel = 'class Foo<T1, T2 extends String>';
+ const expectedDoc = 'My Foo.';
+
+ await initialize(
+ textDocumentCapabilities: withSignatureHelpContentFormat(
+ emptyTextDocumentClientCapabilities, [MarkupKind.Markdown]));
+ await openFile(mainFileUri, withoutMarkers(content));
+ await testSignature(
+ content,
+ expectedLabel,
+ expectedDoc,
+ [
+ ParameterInformation(label: 'T1'),
+ ParameterInformation(label: 'T2 extends String'),
+ ],
+ );
+ }
+
+ Future<void> test_typeParams_function() async {
+ final content = '''
+ /// My Foo.
+ void foo<T1, T2 extends String>() {
+ foo<^>();
+ }
+ ''';
+
+ const expectedLabel = 'void foo<T1, T2 extends String>()';
+ const expectedDoc = 'My Foo.';
+
+ await initialize(
+ textDocumentCapabilities: withSignatureHelpContentFormat(
+ emptyTextDocumentClientCapabilities, [MarkupKind.Markdown]));
+ await openFile(mainFileUri, withoutMarkers(content));
+ await testSignature(
+ content,
+ expectedLabel,
+ expectedDoc,
+ [
+ ParameterInformation(label: 'T1'),
+ ParameterInformation(label: 'T2 extends String'),
+ ],
+ );
+ }
+
+ Future<void> test_typeParams_method() async {
+ final content = '''
+ class Foo {
+ /// My Foo.
+ void foo<T1, T2 extends String>() {
+ foo<^>();
+ }
+ }
+ ''';
+
+ const expectedLabel = 'void foo<T1, T2 extends String>()';
+ const expectedDoc = 'My Foo.';
+
+ await initialize(
+ textDocumentCapabilities: withSignatureHelpContentFormat(
+ emptyTextDocumentClientCapabilities, [MarkupKind.Markdown]));
+ await openFile(mainFileUri, withoutMarkers(content));
+ await testSignature(
+ content,
+ expectedLabel,
+ expectedDoc,
+ [
+ ParameterInformation(label: 'T1'),
+ ParameterInformation(label: 'T2 extends String'),
+ ],
+ );
+ }
+
Future<void> test_unopenFile() async {
final content = '''
/// Does foo.
diff --git a/pkg/analysis_server/test/services/refactoring/extract_method_test.dart b/pkg/analysis_server/test/services/refactoring/extract_method_test.dart
index fec1997..e530fd5 100644
--- a/pkg/analysis_server/test/services/refactoring/extract_method_test.dart
+++ b/pkg/analysis_server/test/services/refactoring/extract_method_test.dart
@@ -1209,7 +1209,7 @@
_createRefactoringForStartEndComments();
// do check
await refactoring.checkInitialConditions();
- expect(refactoring.returnType, 'int');
+ expect(refactoring.returnType, 'int?');
}
Future<void> test_returnType_statements_void() async {
diff --git a/pkg/analysis_server/test/src/services/completion/dart/completion_test.dart b/pkg/analysis_server/test/src/services/completion/dart/completion_test.dart
index 59352d0..14b5877 100644
--- a/pkg/analysis_server/test/src/services/completion/dart/completion_test.dart
+++ b/pkg/analysis_server/test/src/services/completion/dart/completion_test.dart
@@ -13,6 +13,7 @@
defineReflectiveTests(AsExpressionCompletionTest);
defineReflectiveTests(AssertStatementCompletionTest);
defineReflectiveTests(ConstructorCompletionTest);
+ defineReflectiveTests(DeclaredIdentifierCompletionTest);
defineReflectiveTests(ExpressionFunctionBodyCompletionTest);
defineReflectiveTests(ExtensionCompletionTest);
defineReflectiveTests(FormalParameterCompletionTest);
@@ -122,6 +123,33 @@
}
@reflectiveTest
+class DeclaredIdentifierCompletionTest extends CompletionTestCase {
+ Future<void> test_afterFinal_withIdentifier() async {
+ addTestFile('''
+class C {
+ void m(List<C> cs) {
+ for (final ^ x in cs) {}
+ }
+}
+''');
+ await getSuggestions();
+ assertHasCompletion('C');
+ }
+
+ Future<void> test_afterFinal_withoutIdentifier() async {
+ addTestFile('''
+class C {
+ void m(List<C> cs) {
+ for (final ^) {}
+ }
+}
+''');
+ await getSuggestions();
+ assertHasCompletion('C');
+ }
+}
+
+@reflectiveTest
class ExpressionFunctionBodyCompletionTest extends CompletionTestCase {
Future<void> test_voidReturn_localFunction() async {
addTestFile('''
diff --git a/pkg/analysis_server/test/src/services/correction/assist/add_type_annotation_test.dart b/pkg/analysis_server/test/src/services/correction/assist/add_type_annotation_test.dart
index 06724fb..e0639a0 100644
--- a/pkg/analysis_server/test/src/services/correction/assist/add_type_annotation_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/assist/add_type_annotation_test.dart
@@ -20,6 +20,9 @@
@override
AssistKind get kind => DartAssistKind.ADD_TYPE_ANNOTATION;
+ @override
+ String? get testPackageLanguageVersion => '2.12';
+
Future<void> test_classField_final() async {
await resolveTestCode('''
class A {
@@ -59,21 +62,21 @@
Future<void> test_declaredIdentifier() async {
await resolveTestCode('''
-main(List<String> items) {
+void f(List<String> items) {
for (var item in items) {
}
}
''');
// on identifier
await assertHasAssistAt('item in', '''
-main(List<String> items) {
+void f(List<String> items) {
for (String item in items) {
}
}
''');
// on "for"
await assertHasAssistAt('for (', '''
-main(List<String> items) {
+void f(List<String> items) {
for (String item in items) {
}
}
@@ -87,7 +90,7 @@
''');
await resolveTestCode('''
import 'my_lib.dart';
-main() {
+void f() {
for (var map in getMap()) {
}
}
@@ -96,7 +99,7 @@
import 'dart:collection';
import 'my_lib.dart';
-main() {
+void f() {
for (HashMap<String, int> map in getMap()) {
}
}
@@ -105,13 +108,13 @@
Future<void> test_declaredIdentifier_final() async {
await resolveTestCode('''
-main(List<String> items) {
+void f(List<String> items) {
for (final item in items) {
}
}
''');
await assertHasAssistAt('item in', '''
-main(List<String> items) {
+void f(List<String> items) {
for (final String item in items) {
}
}
@@ -121,7 +124,7 @@
Future<void> test_declaredIdentifier_generic() async {
await resolveTestCode('''
class A<T> {
- main(List<List<T>> items) {
+ void f(List<List<T>> items) {
for (var item in items) {
}
}
@@ -129,7 +132,7 @@
''');
await assertHasAssistAt('item in', '''
class A<T> {
- main(List<List<T>> items) {
+ void f(List<List<T>> items) {
for (List<T> item in items) {
}
}
@@ -139,7 +142,7 @@
Future<void> test_declaredIdentifier_hasTypeAnnotation() async {
await resolveTestCode('''
-main(List<String> items) {
+void f(List<String> items) {
for (String item in items) {
}
}
@@ -149,7 +152,7 @@
Future<void> test_declaredIdentifier_inForEachBody() async {
await resolveTestCode('''
-main(List<String> items) {
+void f(List<String> items) {
for (var item in items) {
42;
}
@@ -161,7 +164,7 @@
Future<void> test_declaredIdentifier_unknownType() async {
verifyNoTestUnitErrors = false;
await resolveTestCode('''
-main() {
+void f() {
for (var item in unknownList) {
}
}
@@ -176,7 +179,7 @@
''');
await resolveTestCode('''
import 'my_lib.dart';
-main() {
+void f() {
var v = getMap();
}
''');
@@ -184,7 +187,7 @@
import 'dart:collection';
import 'my_lib.dart';
-main() {
+void f() {
HashMap<String, int> v = getMap();
}
''');
@@ -203,7 +206,7 @@
''';
addTestSource(r'''
part of my_app;
-main() {
+void f() {
var /*caret*/v = getMap();
}
''');
@@ -215,7 +218,7 @@
await assertHasAssist('''
part of my_app;
-main() {
+void f() {
HashMap<String, int> v = getMap();
}
''', additionallyChangedFiles: {
@@ -243,14 +246,14 @@
''');
await resolveTestCode('''
import 'ccc/lib_b.dart';
-main() {
+void f() {
var v = newMyClass();
}
''');
await assertHasAssistAt('v =', '''
import 'aa/bbb/lib_a.dart';
import 'ccc/lib_b.dart';
-main() {
+void f() {
MyClass v = newMyClass();
}
''');
@@ -258,7 +261,7 @@
Future<void> test_local_bottom() async {
await resolveTestCode('''
-main() {
+void f() {
var v = throw 42;
}
''');
@@ -267,12 +270,12 @@
Future<void> test_local_Function() async {
await resolveTestCode('''
-main() {
+void f() {
var v = () => 1;
}
''');
await assertHasAssistAt('v =', '''
-main() {
+void f() {
int Function() v = () => 1;
}
''');
@@ -281,14 +284,14 @@
Future<void> test_local_generic_literal() async {
await resolveTestCode('''
class A {
- main(List<int> items) {
+ void m(List<int> items) {
var v = items;
}
}
''');
await assertHasAssistAt('v =', '''
class A {
- main(List<int> items) {
+ void m(List<int> items) {
List<int> v = items;
}
}
@@ -298,14 +301,14 @@
Future<void> test_local_generic_local() async {
await resolveTestCode('''
class A<T> {
- main(List<T> items) {
+ void m(List<T> items) {
var v = items;
}
}
''');
await assertHasAssistAt('v =', '''
class A<T> {
- main(List<T> items) {
+ void m(List<T> items) {
List<T> v = items;
}
}
@@ -314,7 +317,7 @@
Future<void> test_local_hasTypeAnnotation() async {
await resolveTestCode('''
-main() {
+void f() {
int v = 42;
}
''');
@@ -323,12 +326,12 @@
Future<void> test_local_int() async {
await resolveTestCode('''
-main() {
+void f() {
var v = 0;
}
''');
await assertHasAssistAt('v =', '''
-main() {
+void f() {
int v = 0;
}
''');
@@ -336,12 +339,12 @@
Future<void> test_local_List() async {
await resolveTestCode('''
-main() {
+void f() {
var v = <String>[];
}
''');
await assertHasAssistAt('v =', '''
-main() {
+void f() {
List<String> v = <String>[];
}
''');
@@ -350,15 +353,15 @@
Future<void> test_local_localType() async {
await resolveTestCode('''
class C {}
-C f() => null;
-main() {
+C f() => C();
+void g() {
var x = f();
}
''');
await assertHasAssistAt('x =', '''
class C {}
-C f() => null;
-main() {
+C f() => C();
+void g() {
C x = f();
}
''');
@@ -366,26 +369,183 @@
Future<void> test_local_multiple() async {
await resolveTestCode('''
-main() {
+void f() {
var a = 1, b = '';
}
''');
await assertNoAssistAt('var ');
}
- Future<void> test_local_noValue() async {
+ Future<void> test_local_noInitializer_noAssignments() async {
verifyNoTestUnitErrors = false;
await resolveTestCode('''
-main() {
+void f() {
var v;
}
''');
await assertNoAssistAt('var ');
}
+ Future<void> test_local_noInitializer_oneAssignment_dynamic() async {
+ verifyNoTestUnitErrors = false;
+ await resolveTestCode('''
+void f(p) {
+ var v;
+ v = p;
+}
+''');
+ await assertNoAssistAt('var ');
+ }
+
+ Future<void> test_local_noInitializer_oneAssignment_functionType() async {
+ verifyNoTestUnitErrors = false;
+ await resolveTestCode('''
+void f(int Function(int) p) {
+ var v;
+ v = p;
+}
+''');
+ // TODO(brianwilkerson) Improve `DartChangeBuilder.writeType` so that
+ // unnecessary parameter names (`p1`) are not written.
+ await assertHasAssistAt('var ', '''
+void f(int Function(int) p) {
+ int Function(int p1) v;
+ v = p;
+}
+''');
+ }
+
+ Future<void> test_local_noInitializer_oneAssignment_insideClosure() async {
+ verifyNoTestUnitErrors = false;
+ await resolveTestCode('''
+void f() {
+ var v;
+ () {
+ v = '';
+ }();
+}
+''');
+ await assertHasAssistAt('var ', '''
+void f() {
+ String v;
+ () {
+ v = '';
+ }();
+}
+''');
+ }
+
+ Future<void> test_local_noInitializer_oneAssignment_interfaceType() async {
+ verifyNoTestUnitErrors = false;
+ await resolveTestCode('''
+void f() {
+ var v;
+ v = '';
+}
+''');
+ await assertHasAssistAt('var ', '''
+void f() {
+ String v;
+ v = '';
+}
+''');
+ }
+
+ Future<void> test_local_noInitializer_threeAssignments() async {
+ verifyNoTestUnitErrors = false;
+ await resolveTestCode('''
+void f(int a, String b) {
+ var v;
+ v = a;
+ v = null;
+ v = b;
+}
+''');
+ await assertHasAssistAt('var ', '''
+void f(int a, String b) {
+ Object? v;
+ v = a;
+ v = null;
+ v = b;
+}
+''');
+ }
+
+ Future<void> test_local_noInitializer_twoAssignments_differentTypes() async {
+ verifyNoTestUnitErrors = false;
+ await resolveTestCode('''
+void f() {
+ var v;
+ v = 0;
+ v = 3.1;
+}
+''');
+ await assertHasAssistAt('var ', '''
+void f() {
+ num v;
+ v = 0;
+ v = 3.1;
+}
+''');
+ }
+
+ Future<void> test_local_noInitializer_twoAssignments_oneNull() async {
+ verifyNoTestUnitErrors = false;
+ await resolveTestCode('''
+void f() {
+ var v;
+ v = null;
+ v = 0;
+}
+''');
+ await assertHasAssistAt('var ', '''
+void f() {
+ int? v;
+ v = null;
+ v = 0;
+}
+''');
+ }
+
+ Future<void> test_local_noInitializer_twoAssignments_oneNullable() async {
+ verifyNoTestUnitErrors = false;
+ await resolveTestCode('''
+void f(int a, int? b) {
+ var v;
+ v = a;
+ v = b;
+}
+''');
+ await assertHasAssistAt('var ', '''
+void f(int a, int? b) {
+ int? v;
+ v = a;
+ v = b;
+}
+''');
+ }
+
+ Future<void> test_local_noInitializer_twoAssignments_sameTypes() async {
+ verifyNoTestUnitErrors = false;
+ await resolveTestCode('''
+void f() {
+ var v;
+ v = 'a';
+ v = 'b';
+}
+''');
+ await assertHasAssistAt('var ', '''
+void f() {
+ String v;
+ v = 'a';
+ v = 'b';
+}
+''');
+ }
+
Future<void> test_local_null() async {
await resolveTestCode('''
-main() {
+void f() {
var v = null;
}
''');
@@ -394,7 +554,7 @@
Future<void> test_local_onInitializer() async {
await resolveTestCode('''
-main() {
+void f() {
var abc = 0;
}
''');
@@ -403,12 +563,12 @@
Future<void> test_local_onName() async {
await resolveTestCode('''
-main() {
+void f() {
var abc = 0;
}
''');
await assertHasAssistAt('bc', '''
-main() {
+void f() {
int abc = 0;
}
''');
@@ -416,12 +576,12 @@
Future<void> test_local_onVar() async {
await resolveTestCode('''
-main() {
+void f() {
var v = 0;
}
''');
await assertHasAssistAt('var ', '''
-main() {
+void f() {
int v = 0;
}
''');
@@ -430,7 +590,7 @@
Future<void> test_local_unknown() async {
verifyNoTestUnitErrors = false;
await resolveTestCode('''
-main() {
+void f() {
var v = unknownVar;
}
''');
@@ -440,13 +600,13 @@
Future<void> test_parameter() async {
await resolveTestCode('''
foo(f(int p)) {}
-main() {
+void f() {
foo((test) {});
}
''');
await assertHasAssistAt('test', '''
foo(f(int p)) {}
-main() {
+void f() {
foo((int test) {});
}
''');
@@ -455,7 +615,7 @@
Future<void> test_parameter_hasExplicitType() async {
await resolveTestCode('''
foo(f(int p)) {}
-main() {
+void f() {
foo((num test) {});
}
''');
@@ -465,7 +625,7 @@
Future<void> test_parameter_noPropagatedType() async {
await resolveTestCode('''
foo(f(p)) {}
-main() {
+void f() {
foo((test) {});
}
''');
@@ -481,7 +641,7 @@
''');
await resolveTestCode('''
import 'my_lib.dart';
-main() {
+void f() {
foo((test) {});
}
''');
@@ -498,7 +658,7 @@
await resolveTestCode('''
import 'my_lib.dart';
class A<T> {
- main() {
+ void m() {
for (var item in getValues()) {
}
}
@@ -518,13 +678,13 @@
''');
await resolveTestCode('''
import 'my_lib.dart';
-main() {
+void f() {
var v = getValues();
}
''');
await assertHasAssistAt('var ', '''
import 'my_lib.dart';
-main() {
+void f() {
List v = getValues();
}
''');
@@ -534,14 +694,14 @@
await resolveTestCode('''
class _A {}
_A getValue() => _A();
-main() {
+void f() {
var v = getValue();
}
''');
await assertHasAssistAt('var ', '''
class _A {}
_A getValue() => _A();
-main() {
+void f() {
_A v = getValue();
}
''');
@@ -556,7 +716,7 @@
''');
await resolveTestCode('''
import 'my_lib.dart';
-main() {
+void f() {
var v = getValue();
}
''');
diff --git a/pkg/analysis_server/test/src/services/correction/fix/add_type_annotation_test.dart b/pkg/analysis_server/test/src/services/correction/fix/add_type_annotation_test.dart
index dd375be..edf86dc 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/add_type_annotation_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/add_type_annotation_test.dart
@@ -11,36 +11,13 @@
void main() {
defineReflectiveSuite(() {
- defineReflectiveTests(AddTypeAnnotationLintTest);
defineReflectiveTests(AddTypeAnnotationTest);
+ defineReflectiveTests(AlwaysSpecifyTypesLintTest);
+ defineReflectiveTests(PreferTypingUninitializedVariablesLintTest);
});
}
@reflectiveTest
-class AddTypeAnnotationLintTest extends FixProcessorLintTest {
- @override
- FixKind get kind => DartFixKind.ADD_TYPE_ANNOTATION;
-
- @override
- String get lintCode => LintNames.always_specify_types;
-
- // More coverage in the `add_type_annotation_test.dart` assist test.
-
- Future<void> test_do_block() async {
- await resolveTestCode('''
-class A {
- final f = 0;
-}
-''');
- await assertHasFix('''
-class A {
- final int f = 0;
-}
-''');
- }
-}
-
-@reflectiveTest
class AddTypeAnnotationTest extends FixProcessorTest {
@override
FixKind get kind => DartFixKind.ADD_TYPE_ANNOTATION;
@@ -73,3 +50,55 @@
''');
}
}
+
+@reflectiveTest
+class AlwaysSpecifyTypesLintTest extends FixProcessorLintTest {
+ @override
+ FixKind get kind => DartFixKind.ADD_TYPE_ANNOTATION;
+
+ @override
+ String get lintCode => LintNames.always_specify_types;
+
+ // More coverage in the `add_type_annotation_test.dart` assist test.
+
+ Future<void> test_field() async {
+ await resolveTestCode('''
+class A {
+ final f = 0;
+}
+''');
+ await assertHasFix('''
+class A {
+ final int f = 0;
+}
+''');
+ }
+}
+
+@reflectiveTest
+class PreferTypingUninitializedVariablesLintTest extends FixProcessorLintTest {
+ @override
+ FixKind get kind => DartFixKind.ADD_TYPE_ANNOTATION;
+
+ @override
+ String get lintCode => LintNames.prefer_typing_uninitialized_variables;
+
+ // More coverage in the `add_type_annotation_test.dart` assist test.
+
+ Future<void> test_local() async {
+ await resolveTestCode('''
+void f() {
+ var l;
+ l = 0;
+ print(l);
+}
+''');
+ await assertHasFix('''
+void f() {
+ int l;
+ l = 0;
+ print(l);
+}
+''');
+ }
+}
diff --git a/pkg/analyzer/CHANGELOG.md b/pkg/analyzer/CHANGELOG.md
index 86f31f2..79fe467 100644
--- a/pkg/analyzer/CHANGELOG.md
+++ b/pkg/analyzer/CHANGELOG.md
@@ -6,7 +6,9 @@
* Deprecated `CompilationUnitElement.types`, use `classes` instead.
* Added `Element.nonSynthetic`, use it to get the element that caused creation
of this element, e.g. the field for a synthetic getter.
+* `FieldElement.isFinal` is `true` only when the field is not synthetic.
* Synthetic getters and setters now use `-1` as `nameOffset`.
+* Fixed bug that `defaultValueCode` is `null` for field formal parameters.
## 1.7.0
* Require `meta: ^1.4.0`.
diff --git a/pkg/analyzer/lib/src/dart/element/element.dart b/pkg/analyzer/lib/src/dart/element/element.dart
index 57905b9..bbdc21f 100644
--- a/pkg/analyzer/lib/src/dart/element/element.dart
+++ b/pkg/analyzer/lib/src/dart/element/element.dart
@@ -1620,6 +1620,11 @@
/// [nameOffset].
DefaultFieldFormalParameterElementImpl(String name, int nameOffset)
: super(name, nameOffset);
+
+ @override
+ String? get defaultValueCode {
+ return constantInitializer?.toSource();
+ }
}
/// A [ParameterElement] for parameters that have an initializer.
@@ -1632,8 +1637,7 @@
@override
String? get defaultValueCode {
- var ast = constantInitializer;
- return ast != null ? ast.toSource() : null;
+ return constantInitializer?.toSource();
}
}
diff --git a/pkg/analyzer/lib/src/dart/resolver/function_reference_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/function_reference_resolver.dart
index b8c9a8a..96cc743 100644
--- a/pkg/analyzer/lib/src/dart/resolver/function_reference_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/function_reference_resolver.dart
@@ -167,9 +167,6 @@
var invokeType = rawType.instantiate(typeArguments);
node.typeArgumentTypes = typeArguments;
node.staticType = invokeType;
-
- // TODO(srawlins): Verify that type arguments conform to bounds. This will
- // probably be done later, not in this resolution phase.
}
void _resolveFunctionReferenceMethod({
diff --git a/pkg/analyzer/lib/src/error/type_arguments_verifier.dart b/pkg/analyzer/lib/src/error/type_arguments_verifier.dart
index a14d9f4..e4a6930 100644
--- a/pkg/analyzer/lib/src/error/type_arguments_verifier.dart
+++ b/pkg/analyzer/lib/src/error/type_arguments_verifier.dart
@@ -33,10 +33,22 @@
_libraryElement.typeSystem as TypeSystemImpl;
void checkFunctionExpressionInvocation(FunctionExpressionInvocation node) {
- _checkTypeArguments(node);
+ _checkTypeArguments(
+ node.typeArguments?.arguments,
+ node.function.staticType,
+ node.staticInvokeType,
+ );
_checkForImplicitDynamicInvoke(node);
}
+ void checkFunctionReference(FunctionReference node) {
+ _checkTypeArguments(
+ node.typeArguments?.arguments,
+ node.function.staticType,
+ node.staticType,
+ );
+ }
+
void checkListLiteral(ListLiteral node) {
var typeArguments = node.typeArguments;
if (typeArguments != null) {
@@ -68,7 +80,11 @@
}
void checkMethodInvocation(MethodInvocation node) {
- _checkTypeArguments(node);
+ _checkTypeArguments(
+ node.typeArguments?.arguments,
+ node.function.staticType,
+ node.staticInvokeType,
+ );
_checkForImplicitDynamicInvoke(node);
}
@@ -324,63 +340,65 @@
}
}
- /// Verify that the given [typeArguments] are all within their bounds, as
- /// defined by the given [element].
- void _checkTypeArguments(InvocationExpression node) {
- var typeArgumentList = node.typeArguments?.arguments;
+ /// Verify that each type argument in [typeArgumentList] is within its bounds,
+ /// as defined by [genericType].
+ void _checkTypeArguments(
+ List<TypeAnnotation>? typeArgumentList,
+ DartType? genericType,
+ DartType? instantiatedType,
+ ) {
if (typeArgumentList == null) {
return;
}
- var genericType = node.function.staticType;
- var instantiatedType = node.staticInvokeType;
- if (genericType is FunctionType && instantiatedType is FunctionType) {
- var fnTypeParams = genericType.typeFormals;
- var typeArgs = typeArgumentList.map((t) => t.typeOrThrow).toList();
+ if (genericType is! FunctionType || instantiatedType is! FunctionType) {
+ return;
+ }
- // If the amount mismatches, clean up the lists to be substitutable. The
- // mismatch in size is reported elsewhere, but we must successfully
- // perform substitution to validate bounds on mismatched lists.
- final providedLength = math.min(typeArgs.length, fnTypeParams.length);
- fnTypeParams = fnTypeParams.sublist(0, providedLength);
- typeArgs = typeArgs.sublist(0, providedLength);
+ var fnTypeParams = genericType.typeFormals;
+ var typeArgs = typeArgumentList.map((t) => t.typeOrThrow).toList();
- for (int i = 0; i < providedLength; i++) {
- // Check the `extends` clause for the type parameter, if any.
- //
- // Also substitute to handle cases like this:
- //
- // <TFrom, TTo extends TFrom>
- // <TFrom, TTo extends Iterable<TFrom>>
- // <T extends Cloneable<T>>
- //
- DartType argType = typeArgs[i];
+ // If the amount mismatches, clean up the lists to be substitutable. The
+ // mismatch in size is reported elsewhere, but we must successfully
+ // perform substitution to validate bounds on mismatched lists.
+ var providedLength = math.min(typeArgs.length, fnTypeParams.length);
+ fnTypeParams = fnTypeParams.sublist(0, providedLength);
+ typeArgs = typeArgs.sublist(0, providedLength);
- if (argType is FunctionType && argType.typeFormals.isNotEmpty) {
- if (!_libraryElement.featureSet.isEnabled(Feature.generic_metadata)) {
- _errorReporter.reportErrorForNode(
- CompileTimeErrorCode
- .GENERIC_FUNCTION_TYPE_CANNOT_BE_TYPE_ARGUMENT,
- typeArgumentList[i],
- );
- continue;
- }
- }
+ for (int i = 0; i < providedLength; i++) {
+ // Check the `extends` clause for the type parameter, if any.
+ //
+ // Also substitute to handle cases like this:
+ //
+ // <TFrom, TTo extends TFrom>
+ // <TFrom, TTo extends Iterable<TFrom>>
+ // <T extends Cloneable<T>>
+ //
+ DartType argType = typeArgs[i];
- var fnTypeParam = fnTypeParams[i];
- var rawBound = fnTypeParam.bound;
- if (rawBound == null) {
+ if (argType is FunctionType && argType.typeFormals.isNotEmpty) {
+ if (!_libraryElement.featureSet.isEnabled(Feature.generic_metadata)) {
+ _errorReporter.reportErrorForNode(
+ CompileTimeErrorCode.GENERIC_FUNCTION_TYPE_CANNOT_BE_TYPE_ARGUMENT,
+ typeArgumentList[i],
+ );
continue;
}
+ }
- var substitution = Substitution.fromPairs(fnTypeParams, typeArgs);
- var bound = substitution.substituteType(rawBound);
- if (!_typeSystem.isSubtypeOf(argType, bound)) {
- _errorReporter.reportErrorForNode(
- CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS,
- typeArgumentList[i],
- [argType, fnTypeParam.name, bound]);
- }
+ var fnTypeParam = fnTypeParams[i];
+ var rawBound = fnTypeParam.bound;
+ if (rawBound == null) {
+ continue;
+ }
+
+ var substitution = Substitution.fromPairs(fnTypeParams, typeArgs);
+ var bound = substitution.substituteType(rawBound);
+ if (!_typeSystem.isSubtypeOf(argType, bound)) {
+ _errorReporter.reportErrorForNode(
+ CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS,
+ typeArgumentList[i],
+ [argType, fnTypeParam.name, bound]);
}
}
}
diff --git a/pkg/analyzer/lib/src/error/use_result_verifier.dart b/pkg/analyzer/lib/src/error/use_result_verifier.dart
index 7a32f02..c96ee1d 100644
--- a/pkg/analyzer/lib/src/error/use_result_verifier.dart
+++ b/pkg/analyzer/lib/src/error/use_result_verifier.dart
@@ -64,14 +64,24 @@
var message = _getUseResultMessage(annotation);
if (message == null || message.isEmpty) {
- _errorReporter
- .reportErrorForNode(HintCode.UNUSED_RESULT, node, [displayName]);
- } else {
_errorReporter.reportErrorForNode(
- HintCode.UNUSED_RESULT_WITH_MESSAGE, node, [displayName, message]);
+ HintCode.UNUSED_RESULT, _getNodeToAnnotate(node), [displayName]);
+ } else {
+ _errorReporter.reportErrorForNode(HintCode.UNUSED_RESULT_WITH_MESSAGE,
+ _getNodeToAnnotate(node), [displayName, message]);
}
}
+ static AstNode _getNodeToAnnotate(AstNode node) {
+ if (node is MethodInvocation) {
+ return node.methodName;
+ }
+ if (node is PropertyAccess) {
+ return node.propertyName;
+ }
+ return node;
+ }
+
static String? _getUseResultMessage(ElementAnnotation annotation) {
if (annotation.element is PropertyAccessorElement) {
return null;
@@ -94,7 +104,9 @@
return false;
}
- if (parent is ParenthesizedExpression || parent is ConditionalExpression) {
+ if (parent is ParenthesizedExpression ||
+ parent is ConditionalExpression ||
+ parent is CascadeExpression) {
return _isUsed(parent);
}
diff --git a/pkg/analyzer/lib/src/generated/error_verifier.dart b/pkg/analyzer/lib/src/generated/error_verifier.dart
index cfe297c4..7d6e8ab 100644
--- a/pkg/analyzer/lib/src/generated/error_verifier.dart
+++ b/pkg/analyzer/lib/src/generated/error_verifier.dart
@@ -757,6 +757,11 @@
}
@override
+ void visitFunctionReference(FunctionReference node) {
+ _typeArgumentsVerifier.checkFunctionReference(node);
+ }
+
+ @override
void visitFunctionTypeAlias(FunctionTypeAlias node) {
_checkForBuiltInIdentifierAsName(
node.name, CompileTimeErrorCode.BUILT_IN_IDENTIFIER_AS_TYPEDEF_NAME);
diff --git a/pkg/analyzer/lib/src/pubspec/pubspec_validator.dart b/pkg/analyzer/lib/src/pubspec/pubspec_validator.dart
index 5bed787..b0ab45b 100644
--- a/pkg/analyzer/lib/src/pubspec/pubspec_validator.dart
+++ b/pkg/analyzer/lib/src/pubspec/pubspec_validator.dart
@@ -7,6 +7,7 @@
import 'package:analyzer/file_system/file_system.dart';
import 'package:analyzer/src/generated/source.dart';
import 'package:analyzer/src/pubspec/validators/dependency_validator.dart';
+import 'package:analyzer/src/pubspec/validators/field_validator.dart';
import 'package:analyzer/src/pubspec/validators/flutter_validator.dart';
import 'package:analyzer/src/pubspec/validators/name_validator.dart';
import 'package:source_span/src/span.dart';
@@ -21,6 +22,16 @@
BasePubspecValidator(this.provider, this.source);
+ String? asString(dynamic node) {
+ if (node is String) {
+ return node;
+ }
+ if (node is YamlScalar && node.value is String) {
+ return node.value as String;
+ }
+ return null;
+ }
+
/// Report an error for the given node.
void reportErrorForNode(
ErrorReporter reporter, YamlNode node, ErrorCode errorCode,
@@ -70,6 +81,7 @@
final Source source;
final DependencyValidator _dependencyValidator;
+ final FieldValidator _fieldValidator;
final FlutterValidator _flutterValidator;
final NameValidator _nameValidator;
@@ -77,6 +89,7 @@
/// [source].
PubspecValidator(this.provider, this.source)
: _dependencyValidator = DependencyValidator(provider, source),
+ _fieldValidator = FieldValidator(provider, source),
_flutterValidator = FlutterValidator(provider, source),
_nameValidator = NameValidator(provider, source);
@@ -90,6 +103,7 @@
);
_dependencyValidator.validate(reporter, contents);
+ _fieldValidator.validate(reporter, contents);
_flutterValidator.validate(reporter, contents);
_nameValidator.validate(reporter, contents);
diff --git a/pkg/analyzer/lib/src/pubspec/pubspec_warning_code.dart b/pkg/analyzer/lib/src/pubspec/pubspec_warning_code.dart
index 0c03a1f..7a2d01d 100644
--- a/pkg/analyzer/lib/src/pubspec/pubspec_warning_code.dart
+++ b/pkg/analyzer/lib/src/pubspec/pubspec_warning_code.dart
@@ -4,10 +4,10 @@
import 'package:analyzer/error/error.dart';
-/// The error codes used for warnings in analysis options files. The convention
-/// for this class is for the name of the error code to indicate the problem
-/// that caused the error to be generated and for the error message to explain
-/// what is wrong and, when appropriate, how the problem can be corrected.
+/// The error codes used for warnings in pubspec files. The convention for this
+/// class is for the name of the error code to indicate the problem that caused
+/// the error to be generated and for the error message to explain what is wrong
+/// and, when appropriate, how the problem can be corrected.
class PubspecWarningCode extends ErrorCode {
/// A code indicating that a specified asset does not exist.
///
@@ -46,6 +46,12 @@
"The value of the '{0}' field is expected to be a map.",
correction: "Try converting the value to be a map.");
+ /// A code indicating that a field is deprecated.
+ static const PubspecWarningCode DEPRECATED_FIELD = PubspecWarningCode(
+ 'DEPRECATED_FIELD',
+ "The '{0}' field is no longer used and may be removed.",
+ correction: "Try removing the field.");
+
/// A code indicating that the value of the flutter field is not a map.
static const PubspecWarningCode FLUTTER_FIELD_NOT_MAP = PubspecWarningCode(
'FLUTTER_FIELD_NOT_MAP',
diff --git a/pkg/analyzer/lib/src/pubspec/validators/dependency_validator.dart b/pkg/analyzer/lib/src/pubspec/validators/dependency_validator.dart
index 49f859c..e091ea27 100644
--- a/pkg/analyzer/lib/src/pubspec/validators/dependency_validator.dart
+++ b/pkg/analyzer/lib/src/pubspec/validators/dependency_validator.dart
@@ -26,7 +26,7 @@
bool isPublishablePackage = false;
var version = contents[PubspecField.VERSION_FIELD];
if (version != null) {
- var publishTo = _asString(contents[PubspecField.PUBLISH_TO_FIELD]);
+ var publishTo = asString(contents[PubspecField.PUBLISH_TO_FIELD]);
if (publishTo != 'none') {
isPublishablePackage = true;
}
@@ -46,16 +46,6 @@
}
}
- String? _asString(dynamic node) {
- if (node is String) {
- return node;
- }
- if (node is YamlScalar && node.value is String) {
- return node.value as String;
- }
- return null;
- }
-
/// Return a map whose keys are the names of declared dependencies and whose
/// values are the specifications of those dependencies. The map is extracted
/// from the given [contents] using the given [key].
@@ -84,7 +74,7 @@
void _validatePathEntries(ErrorReporter reporter, YamlNode dependency,
bool checkForPathAndGitDeps) {
if (dependency is YamlMap) {
- var pathEntry = _asString(dependency[PubspecField.PATH_FIELD]);
+ var pathEntry = asString(dependency[PubspecField.PATH_FIELD]);
if (pathEntry != null) {
YamlNode pathKey() => dependency.getKey(PubspecField.PATH_FIELD)!;
YamlNode pathValue() => dependency.valueAt(PubspecField.PATH_FIELD)!;
diff --git a/pkg/analyzer/lib/src/pubspec/validators/field_validator.dart b/pkg/analyzer/lib/src/pubspec/validators/field_validator.dart
new file mode 100644
index 0000000..1d2ac0e
--- /dev/null
+++ b/pkg/analyzer/lib/src/pubspec/validators/field_validator.dart
@@ -0,0 +1,33 @@
+// 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.
+
+import 'package:analyzer/error/listener.dart';
+import 'package:analyzer/file_system/file_system.dart';
+import 'package:analyzer/src/generated/source.dart';
+import 'package:analyzer/src/pubspec/pubspec_validator.dart';
+import 'package:analyzer/src/pubspec/pubspec_warning_code.dart';
+import 'package:yaml/yaml.dart';
+
+class FieldValidator extends BasePubspecValidator {
+ static const deprecatedFields = [
+ 'author',
+ 'authors',
+ 'transformers',
+ 'web',
+ ];
+
+ FieldValidator(ResourceProvider provider, Source source)
+ : super(provider, source);
+
+ /// Validate fields.
+ void validate(ErrorReporter reporter, Map<dynamic, YamlNode> contents) {
+ for (var field in contents.keys) {
+ var name = asString(field);
+ if (name != null && deprecatedFields.contains(name)) {
+ reportErrorForNode(
+ reporter, field, PubspecWarningCode.DEPRECATED_FIELD, [name]);
+ }
+ }
+ }
+}
diff --git a/pkg/analyzer/test/src/dart/resolution/prefix_element_test.dart b/pkg/analyzer/test/src/dart/resolution/prefix_element_test.dart
index a87e051..06faef1 100644
--- a/pkg/analyzer/test/src/dart/resolution/prefix_element_test.dart
+++ b/pkg/analyzer/test/src/dart/resolution/prefix_element_test.dart
@@ -2,8 +2,6 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
-import 'package:analyzer/dart/element/element.dart';
-import 'package:analyzer/src/dart/element/element.dart';
import 'package:test/test.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
@@ -63,20 +61,20 @@
var aImport = findElement.importFind('package:test/a.dart');
var bImport = findElement.importFind('package:test/b.dart');
- _assertMultiplyDefinedElement(
+ expect(
scope.lookup('foo').getter,
- [
+ multiplyDefinedElementMatcher([
aImport.topGet('foo'),
bImport.topGet('foo'),
- ],
+ ]),
);
- _assertMultiplyDefinedElement(
+ expect(
scope.lookup('foo').setter,
- [
+ multiplyDefinedElementMatcher([
aImport.topSet('foo'),
bImport.topSet('foo'),
- ],
+ ]),
);
}
@@ -250,12 +248,4 @@
scope.lookup('cos').getter,
);
}
-
- void _assertMultiplyDefinedElement(
- Element? element,
- List<Element> expected,
- ) {
- element as MultiplyDefinedElementImpl;
- expect(element.conflictingElements, unorderedEquals(expected));
- }
}
diff --git a/pkg/analyzer/test/src/diagnostics/test_all.dart b/pkg/analyzer/test/src/diagnostics/test_all.dart
index fefb145..188a40a 100644
--- a/pkg/analyzer/test/src/diagnostics/test_all.dart
+++ b/pkg/analyzer/test/src/diagnostics/test_all.dart
@@ -684,13 +684,13 @@
import 'unused_import_test.dart' as unused_import;
import 'unused_label_test.dart' as unused_label;
import 'unused_local_variable_test.dart' as unused_local_variable;
+import 'unused_result_test.dart' as unused_result;
import 'unused_shown_name_test.dart' as unused_shown_name;
import 'uri_does_not_exist_test.dart' as uri_does_not_exist;
import 'uri_with_interpolation_test.dart' as uri_with_interpolation;
import 'use_of_native_extension_test.dart' as use_of_native_extension;
import 'use_of_nullable_value_test.dart' as use_of_nullable_value_test;
import 'use_of_void_result_test.dart' as use_of_void_result;
-import 'use_result_test.dart' as use_result;
import 'variable_type_mismatch_test.dart' as variable_type_mismatch;
import 'void_with_type_arguments_test.dart' as void_with_type_arguments_test;
import 'wrong_number_of_parameters_for_operator_test.dart'
@@ -1168,13 +1168,13 @@
unused_import.main();
unused_label.main();
unused_local_variable.main();
+ unused_result.main();
unused_shown_name.main();
uri_does_not_exist.main();
uri_with_interpolation.main();
use_of_native_extension.main();
use_of_nullable_value_test.main();
use_of_void_result.main();
- use_result.main();
variable_type_mismatch.main();
void_with_type_arguments_test.main();
wrong_number_of_parameters_for_operator.main();
diff --git a/pkg/analyzer/test/src/diagnostics/type_argument_not_matching_bounds_test.dart b/pkg/analyzer/test/src/diagnostics/type_argument_not_matching_bounds_test.dart
index 25184c2..72f8042 100644
--- a/pkg/analyzer/test/src/diagnostics/type_argument_not_matching_bounds_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/type_argument_not_matching_bounds_test.dart
@@ -451,6 +451,32 @@
''');
}
+ test_functionReference() async {
+ await assertErrorsInCode('''
+void bar(void Function<T extends num>(T a) foo) {
+ foo<String>;
+}
+''', [
+ error(CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, 56, 6),
+ ]);
+ }
+
+ test_functionReference_matching() async {
+ await assertNoErrorsInCode('''
+void bar(void Function<T extends num>(T a) foo) {
+ foo<int>;
+}
+''');
+ }
+
+ test_functionReference_regularBounded() async {
+ await assertNoErrorsInCode('''
+void bar(void Function<T>(T a) foo) {
+ foo<String>;
+}
+''');
+ }
+
test_genericFunctionTypeArgument_invariant() async {
await assertErrorsInCode(r'''
typedef F = T Function<T>(T);
diff --git a/pkg/analyzer/test/src/diagnostics/use_result_test.dart b/pkg/analyzer/test/src/diagnostics/unused_result_test.dart
similarity index 85%
rename from pkg/analyzer/test/src/diagnostics/use_result_test.dart
rename to pkg/analyzer/test/src/diagnostics/unused_result_test.dart
index 154d9c0..0a0a134 100644
--- a/pkg/analyzer/test/src/diagnostics/use_result_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/unused_result_test.dart
@@ -9,12 +9,12 @@
main() {
defineReflectiveSuite(() {
- defineReflectiveTests(UseResultTest);
+ defineReflectiveTests(UnusedResultTest);
});
}
@reflectiveTest
-class UseResultTest extends PubPackageResolutionTest {
+class UnusedResultTest extends PubPackageResolutionTest {
@override
void setUp() {
super.setUp();
@@ -114,7 +114,7 @@
A().foo;
}
''', [
- error(HintCode.UNUSED_RESULT, 100, 7),
+ error(HintCode.UNUSED_RESULT, 104, 3),
]);
}
@@ -207,7 +207,7 @@
A().foo;
}
''', [
- error(HintCode.UNUSED_RESULT, 91, 7),
+ error(HintCode.UNUSED_RESULT, 95, 3),
]);
}
@@ -224,7 +224,7 @@
b ? A().foo : 0;
}
''', [
- error(HintCode.UNUSED_RESULT, 98, 7),
+ error(HintCode.UNUSED_RESULT, 102, 3),
]);
}
@@ -241,7 +241,7 @@
b ? (A().foo) : 0;
}
''', [
- error(HintCode.UNUSED_RESULT, 99, 7),
+ error(HintCode.UNUSED_RESULT, 103, 3),
]);
}
@@ -262,7 +262,7 @@
});
}
''', [
- error(HintCode.UNUSED_RESULT, 126, 7),
+ error(HintCode.UNUSED_RESULT, 130, 3),
]);
}
@@ -346,6 +346,23 @@
''');
}
+ test_field_static_result_unassigned() async {
+ await assertErrorsInCode(r'''
+import 'package:meta/meta.dart';
+
+class A {
+ @useResult
+ static int foo = 0;
+}
+
+void main() {
+ A.foo;
+}
+''', [
+ error(HintCode.UNUSED_RESULT, 100, 3),
+ ]);
+ }
+
test_getter_result_passed() async {
await assertNoErrorsInCode(r'''
import 'package:meta/meta.dart';
@@ -419,7 +436,9 @@
void main() {
A().foo;
}
-''', [error(HintCode.UNUSED_RESULT, 96, 7)]);
+''', [
+ error(HintCode.UNUSED_RESULT, 100, 3),
+ ]);
}
test_method_result_assigned() async {
@@ -472,12 +491,10 @@
test_method_result_targetedMethod() async {
await assertNoErrorsInCode(r'''
import 'package:meta/meta.dart';
-
class A {
@useResult
String foo() => '';
}
-
void main() {
A().foo().toString(); // OK
}
@@ -512,7 +529,27 @@
A().foo();
}
''', [
- error(HintCode.UNUSED_RESULT, 94, 9),
+ error(HintCode.UNUSED_RESULT, 98, 3),
+ ]);
+ }
+
+ test_method_result_unassigned_cascade() async {
+ await assertErrorsInCode(r'''
+import 'package:meta/meta.dart';
+
+class C {
+ @useResult
+ C m1() => throw '';
+
+ C m2() => throw '';
+
+ void m3() {
+ m2()..m1();
+ }
+}
+''', [
+ error(HintCode.UNUSED_RESULT, 131, 2,
+ messageContains: "'m1' should be used."),
]);
}
@@ -530,6 +567,20 @@
''');
}
+ test_topLevelFunction_result_assigned_cascade() async {
+ await assertNoErrorsInCode(r'''
+import 'package:meta/meta.dart';
+
+@useResult
+int foo() => 0;
+
+void main() {
+ var x = foo()..toString(); // OK
+ print(x);
+}
+''');
+ }
+
test_topLevelFunction_result_passed() async {
await assertNoErrorsInCode(r'''
import 'package:meta/meta.dart';
@@ -569,7 +620,6 @@
''');
}
- // todo(pq):implement visitExpressionStatement?
test_topLevelFunction_result_unassigned() async {
await assertErrorsInCode(r'''
import 'package:meta/meta.dart';
@@ -585,7 +635,22 @@
baz(); // OK
}
''', [
- error(HintCode.UNUSED_RESULT, 108, 5),
+ error(HintCode.UNUSED_RESULT, 108, 3),
+ ]);
+ }
+
+ test_topLevelFunction_result_unassigned_cascade() async {
+ await assertErrorsInCode(r'''
+import 'package:meta/meta.dart';
+
+@useResult
+int foo() => 0;
+
+void main() {
+ foo()..toString();
+}
+''', [
+ error(HintCode.UNUSED_RESULT, 78, 3),
]);
}
diff --git a/pkg/analyzer/test/src/pubspec/diagnostics/deprecated_field_test.dart b/pkg/analyzer/test/src/pubspec/diagnostics/deprecated_field_test.dart
new file mode 100644
index 0000000..63d1a3b
--- /dev/null
+++ b/pkg/analyzer/test/src/pubspec/diagnostics/deprecated_field_test.dart
@@ -0,0 +1,48 @@
+// 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.
+
+import 'package:analyzer/src/pubspec/pubspec_warning_code.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import '../pubspec_test_support.dart';
+
+main() {
+ defineReflectiveSuite(() {
+ defineReflectiveTests(DeprecatedFieldTest);
+ });
+}
+
+@reflectiveTest
+class DeprecatedFieldTest extends PubspecDiagnosticTest {
+ test_deprecated_author() {
+ assertErrors('''
+name: sample
+author: foo
+''', [PubspecWarningCode.DEPRECATED_FIELD]);
+ }
+
+ test_deprecated_authors() {
+ assertErrors('''
+name: sample
+authors:
+ - foo
+ - bar
+''', [PubspecWarningCode.DEPRECATED_FIELD]);
+ }
+
+ test_deprecated_transformers() {
+ assertErrors('''
+name: sample
+transformers:
+ - foo
+''', [PubspecWarningCode.DEPRECATED_FIELD]);
+ }
+
+ test_deprecated_web() {
+ assertErrors('''
+name: sample
+web: foo
+''', [PubspecWarningCode.DEPRECATED_FIELD]);
+ }
+}
diff --git a/pkg/analyzer/test/src/pubspec/diagnostics/test_all.dart b/pkg/analyzer/test/src/pubspec/diagnostics/test_all.dart
index 7b1b0ed..4a862bc 100644
--- a/pkg/analyzer/test/src/pubspec/diagnostics/test_all.dart
+++ b/pkg/analyzer/test/src/pubspec/diagnostics/test_all.dart
@@ -10,6 +10,7 @@
import 'asset_field_not_list_test.dart' as asset_field_not_list;
import 'asset_not_string_test.dart' as asset_not_string;
import 'dependencies_field_not_map_test.dart' as dependencies_field_not_map;
+import 'deprecated_field_test.dart' as deprecated_field;
import 'flutter_field_not_map_test.dart' as flutter_field_not_map;
import 'invalid_dependency_test.dart' as invalid_dependency;
import 'missing_name_test.dart' as missing_name;
@@ -26,6 +27,7 @@
asset_field_not_list.main();
asset_not_string.main();
dependencies_field_not_map.main();
+ deprecated_field.main();
flutter_field_not_map.main();
invalid_dependency.main();
missing_name.main();
diff --git a/pkg/analyzer_plugin/lib/src/utilities/completion/optype.dart b/pkg/analyzer_plugin/lib/src/utilities/completion/optype.dart
index 14ea739..4db68d9 100644
--- a/pkg/analyzer_plugin/lib/src/utilities/completion/optype.dart
+++ b/pkg/analyzer_plugin/lib/src/utilities/completion/optype.dart
@@ -543,6 +543,17 @@
}
@override
+ void visitDeclaredIdentifier(DeclaredIdentifier node) {
+ var identifier = node.identifier;
+ if (identifier == entity &&
+ offset < identifier.offset &&
+ node.type == null) {
+ // Adding a type before the identifier.
+ optype.includeTypeNameSuggestions = true;
+ }
+ }
+
+ @override
void visitDefaultFormalParameter(DefaultFormalParameter node) {
if (identical(entity, node.defaultValue)) {
optype.completionLocation = 'DefaultFormalParameter_defaultValue';
diff --git a/pkg/analyzer_plugin/pubspec.yaml b/pkg/analyzer_plugin/pubspec.yaml
index 06f98e5..1a166c4 100644
--- a/pkg/analyzer_plugin/pubspec.yaml
+++ b/pkg/analyzer_plugin/pubspec.yaml
@@ -1,7 +1,6 @@
name: analyzer_plugin
description: A framework and support code for building plugins for the analysis server.
version: 0.6.0
-author: Dart Team <misc@dartlang.org>
homepage: https://github.com/dart-lang/sdk/tree/master/pkg/analyzer_plugin
environment:
diff --git a/pkg/dart2native/analysis_options.yaml b/pkg/dart2native/analysis_options.yaml
index c84f27c..dd90435 100644
--- a/pkg/dart2native/analysis_options.yaml
+++ b/pkg/dart2native/analysis_options.yaml
@@ -1,13 +1,8 @@
-include: package:pedantic/analysis_options.1.8.0.yaml
-
-analyzer:
- errors:
- # Increase the severity of several hints.
- prefer_single_quotes: warning
- unused_import: warning
+include: package:lints/recommended.yaml
linter:
rules:
- - directives_ordering
- - prefer_relative_imports
- - prefer_single_quotes
+ # Enable additional rules.
+ depend_on_referenced_packages: true
+ directives_ordering: true
+ sort_pub_dependencies: true
diff --git a/pkg/dart2native/bin/dart2native.dart b/pkg/dart2native/bin/dart2native.dart
index ed18daf..d5d888a 100644
--- a/pkg/dart2native/bin/dart2native.dart
+++ b/pkg/dart2native/bin/dart2native.dart
@@ -103,7 +103,7 @@
final String sourceFile = parsedArgs.rest[0];
if (!FileSystemEntity.isFileSync(sourceFile)) {
stderr.writeln(
- '"${sourceFile}" is not a file. See \'--help\' for more information.');
+ '"$sourceFile" is not a file. See \'--help\' for more information.');
await stderr.flush();
exit(1);
}
diff --git a/pkg/dart2native/lib/dart2native.dart b/pkg/dart2native/lib/dart2native.dart
index 3e51b53..3329a8d 100644
--- a/pkg/dart2native/lib/dart2native.dart
+++ b/pkg/dart2native/lib/dart2native.dart
@@ -52,10 +52,10 @@
genKernel,
'--platform',
platformDill,
- if (enableExperiment.isNotEmpty) '--enable-experiment=${enableExperiment}',
+ if (enableExperiment.isNotEmpty) '--enable-experiment=$enableExperiment',
'--aot',
'-Ddart.vm.product=true',
- ...(defines.map((d) => '-D${d}')),
+ ...(defines.map((d) => '-D$d')),
if (packages != null) ...['--packages', packages],
'-o',
kernelFile,
@@ -73,7 +73,7 @@
List<String> extraGenSnapshotOptions) {
return Process.run(genSnapshot, [
'--snapshot-kind=app-aot-elf',
- '--elf=${snapshotFile}',
+ '--elf=$snapshotFile',
if (debugFile != null) '--save-debugging-info=$debugFile',
if (debugFile != null) '--dwarf-stack-traces',
if (debugFile != null) '--strip',
diff --git a/pkg/dart2native/lib/generate.dart b/pkg/dart2native/lib/generate.dart
index 3df6176..8c773d6 100644
--- a/pkg/dart2native/lib/generate.dart
+++ b/pkg/dart2native/lib/generate.dart
@@ -3,17 +3,19 @@
// BSD-style license that can be found in the LICENSE file.
import 'dart:io';
+
import 'package:path/path.dart' as path;
+
import 'dart2native.dart';
final Directory binDir = File(Platform.resolvedExecutable).parent;
final String executableSuffix = Platform.isWindows ? '.exe' : '';
final String dartaotruntime =
- path.join(binDir.path, 'dartaotruntime${executableSuffix}');
+ path.join(binDir.path, 'dartaotruntime$executableSuffix');
final String genKernel =
path.join(binDir.path, 'snapshots', 'gen_kernel.dart.snapshot');
final String genSnapshot =
- path.join(binDir.path, 'utils', 'gen_snapshot${executableSuffix}');
+ path.join(binDir.path, 'utils', 'gen_snapshot$executableSuffix');
final String productPlatformDill = path.join(
binDir.parent.path, 'lib', '_internal', 'vm_platform_strong_product.dill');
@@ -42,12 +44,11 @@
'exe': Kind.exe,
}[kind];
final sourceWithoutDart = sourcePath.replaceFirst(RegExp(r'\.dart$'), '');
- final outputPath = path.canonicalize(path.normalize(outputFile != null
- ? outputFile
- : {
- Kind.aot: '${sourceWithoutDart}.aot',
- Kind.exe: '${sourceWithoutDart}.exe',
- }[outputKind]));
+ final outputPath = path.canonicalize(path.normalize(outputFile ??
+ {
+ Kind.aot: '$sourceWithoutDart.aot',
+ Kind.exe: '$sourceWithoutDart.exe',
+ }[outputKind]));
final debugPath =
debugFile != null ? path.canonicalize(path.normalize(debugFile)) : null;
@@ -113,7 +114,7 @@
}
}
- print('Generated: ${outputPath}');
+ print('Generated: $outputPath');
} finally {
tempDir.deleteSync(recursive: true);
}
diff --git a/pkg/dart2native/pubspec.yaml b/pkg/dart2native/pubspec.yaml
index 4c274e7..1e858a5 100644
--- a/pkg/dart2native/pubspec.yaml
+++ b/pkg/dart2native/pubspec.yaml
@@ -11,8 +11,9 @@
dependencies:
args: ^1.4.0
- path: any
front_end:
path: ../front_end
+ path: any
dev_dependencies:
+ lints: any
diff --git a/pkg/dartdev/lib/dartdev.dart b/pkg/dartdev/lib/dartdev.dart
index 83ba6bd..ed23c1e 100644
--- a/pkg/dartdev/lib/dartdev.dart
+++ b/pkg/dartdev/lib/dartdev.dart
@@ -63,6 +63,9 @@
}
class DartdevRunner extends CommandRunner<int> {
+ static const String dartdevDescription =
+ 'A command-line utility for Dart development';
+
@override
final ArgParser argParser = ArgParser(
usageLineLength: dartdevUsageLineLength,
@@ -71,8 +74,7 @@
final bool verbose;
- static const String dartdevDescription =
- 'A command-line utility for Dart development';
+ Analytics _analytics;
DartdevRunner(List<String> args)
: verbose = args.contains('-v') || args.contains('--verbose'),
@@ -108,17 +110,15 @@
addCommand(TestCommand());
}
- @override
- String get usageFooter =>
- 'See https://dart.dev/tools/dart-tool for detailed documentation.';
+ @visibleForTesting
+ Analytics get analytics => _analytics;
@override
String get invocation =>
'dart ${verbose ? '[vm-options] ' : ''}<command|dart-file> [arguments]';
-
- @visibleForTesting
- Analytics get analytics => _analytics;
- Analytics _analytics;
+ @override
+ String get usageFooter =>
+ 'See https://dart.dev/tools/dart-tool for detailed documentation.';
@override
Future<int> runCommand(ArgResults topLevelResults) async {
diff --git a/pkg/telemetry/pubspec.yaml b/pkg/telemetry/pubspec.yaml
index 950cf63..98d68d6 100644
--- a/pkg/telemetry/pubspec.yaml
+++ b/pkg/telemetry/pubspec.yaml
@@ -2,7 +2,6 @@
description: A library to facilitate reporting analytics and crash reports.
# This package is not intended for consumption on pub.dev. DO NOT publish.
publish_to: none
-author: Dart Team <misc@dartlang.org>
environment:
sdk: '>=2.12.0 <3.0.0'
diff --git a/pkg/vm_service/example/vm_service_assert.dart b/pkg/vm_service/example/vm_service_assert.dart
index f9f2d6e..b458ad6 100644
--- a/pkg/vm_service/example/vm_service_assert.dart
+++ b/pkg/vm_service/example/vm_service_assert.dart
@@ -159,6 +159,7 @@
if (obj == "ServiceUnregistered") return obj;
if (obj == "TimelineEvents") return obj;
if (obj == "TimelineStreamSubscriptionsUpdate") return obj;
+ if (obj == "UserTagChanged") return obj;
if (obj == "VMFlagUpdate") return obj;
if (obj == "VMUpdate") return obj;
if (obj == "WriteEvent") return obj;
diff --git a/pkg/vm_service/java/version.properties b/pkg/vm_service/java/version.properties
index 88bab86..62cbe79 100644
--- a/pkg/vm_service/java/version.properties
+++ b/pkg/vm_service/java/version.properties
@@ -1 +1 @@
-version=3.47
+version=3.48
diff --git a/pkg/vm_service/lib/src/vm_service.dart b/pkg/vm_service/lib/src/vm_service.dart
index 983d0f7..903bb0c 100644
--- a/pkg/vm_service/lib/src/vm_service.dart
+++ b/pkg/vm_service/lib/src/vm_service.dart
@@ -26,7 +26,7 @@
HeapSnapshotObjectNoData,
HeapSnapshotObjectNullData;
-const String vmServiceVersion = '3.47.0';
+const String vmServiceVersion = '3.48.0';
/// @optional
const String optional = 'optional';
@@ -1150,6 +1150,7 @@
/// Debug | PauseStart, PauseExit, PauseBreakpoint, PauseInterrupted,
/// PauseException, PausePostRequest, Resume, BreakpointAdded,
/// BreakpointResolved, BreakpointRemoved, BreakpointUpdated, Inspect, None
+ /// Profiler | UserTagChanged
/// GC | GC
/// Extension | Extension
/// Timeline | TimelineEvents, TimelineStreamsSubscriptionUpdate
@@ -1690,6 +1691,9 @@
// PauseStart, PauseExit, PauseBreakpoint, PauseInterrupted, PauseException, PausePostRequest, Resume, BreakpointAdded, BreakpointResolved, BreakpointRemoved, BreakpointUpdated, Inspect, None
Stream<Event> get onDebugEvent => _getEventController('Debug').stream;
+ // UserTagChanged
+ Stream<Event> get onProfilerEvent => _getEventController('Profiler').stream;
+
// GC
Stream<Event> get onGCEvent => _getEventController('GC').stream;
@@ -2384,6 +2388,7 @@
static const String kVM = 'VM';
static const String kIsolate = 'Isolate';
static const String kDebug = 'Debug';
+ static const String kProfiler = 'Profiler';
static const String kGC = 'GC';
static const String kExtension = 'Extension';
static const String kTimeline = 'Timeline';
@@ -2496,6 +2501,9 @@
/// Notification that a Service has been removed from the Service Protocol
/// from another client.
static const String kServiceUnregistered = 'ServiceUnregistered';
+
+ /// Notification that the UserTag for an isolate has been changed.
+ static const String kUserTagChanged = 'UserTagChanged';
}
/// Adding new values to `InstanceKind` is considered a backwards compatible
@@ -3885,6 +3893,14 @@
@optional
bool? last;
+ /// The current UserTag label.
+ @optional
+ String? updatedTag;
+
+ /// The previous UserTag label.
+ @optional
+ String? previousTag;
+
/// Binary data associated with the event.
///
/// This is provided for the event kinds:
@@ -3917,6 +3933,8 @@
this.flag,
this.newValue,
this.last,
+ this.updatedTag,
+ this.previousTag,
this.data,
});
@@ -3959,6 +3977,8 @@
flag = json['flag'];
newValue = json['newValue'];
last = json['last'];
+ updatedTag = json['updatedTag'];
+ previousTag = json['previousTag'];
data = json['data'];
}
@@ -3998,6 +4018,8 @@
_setIfNotNull(json, 'flag', flag);
_setIfNotNull(json, 'newValue', newValue);
_setIfNotNull(json, 'last', last);
+ _setIfNotNull(json, 'updatedTag', updatedTag);
+ _setIfNotNull(json, 'previousTag', previousTag);
_setIfNotNull(json, 'data', data);
return json;
}
diff --git a/pkg/vm_service/test/common/test_helper.dart b/pkg/vm_service/test/common/test_helper.dart
index eee241f..45ce97b 100644
--- a/pkg/vm_service/test/common/test_helper.dart
+++ b/pkg/vm_service/test/common/test_helper.dart
@@ -346,7 +346,7 @@
completer!.complete(vmIsolates.first);
completer = null;
}
- return await (completer!.future as FutureOr<IsolateRef>);
+ return (await completer!.future) as IsolateRef;
}
}
diff --git a/pkg/vm_service/test/user_tag_changed_test.dart b/pkg/vm_service/test/user_tag_changed_test.dart
new file mode 100644
index 0000000..e2b63fa
--- /dev/null
+++ b/pkg/vm_service/test/user_tag_changed_test.dart
@@ -0,0 +1,62 @@
+// 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.
+
+import 'dart:developer';
+
+import 'package:async/async.dart';
+import 'package:test/test.dart';
+import 'package:vm_service/vm_service.dart';
+
+import 'common/service_test_common.dart';
+import 'common/test_helper.dart';
+
+void testMain() {
+ final tag = UserTag('Foo');
+ final origTag = tag.makeCurrent();
+ origTag.makeCurrent();
+}
+
+late StreamQueue<Event> stream;
+
+var tests = <IsolateTest>[
+ hasPausedAtStart,
+ (VmService service, IsolateRef isolate) async {
+ await service.streamListen(EventStreams.kProfiler);
+ stream = StreamQueue(
+ service.onProfilerEvent.transform(
+ SingleSubscriptionTransformer<Event, Event>(),
+ ),
+ );
+ },
+ resumeIsolate,
+ hasStoppedAtExit,
+ (VmService service, IsolateRef isolate) async {
+ await service.streamCancel(EventStreams.kProfiler);
+ expect(await stream.hasNext, true);
+
+ var event = await stream.next;
+ expect(event.kind, EventKind.kUserTagChanged);
+ expect(event.updatedTag, 'Foo');
+ expect(event.previousTag, 'Default');
+
+ expect(await stream.hasNext, true);
+ event = await stream.next;
+ expect(event.kind, EventKind.kUserTagChanged);
+ expect(event.updatedTag, 'Default');
+ expect(event.previousTag, 'Foo');
+ },
+ resumeIsolate,
+ (VmService service, IsolateRef isolate) async {
+ expect(await stream.hasNext, false);
+ }
+];
+
+main([args = const <String>[]]) async => await runIsolateTests(
+ args,
+ tests,
+ 'user_tag_changed_test.dart',
+ pause_on_start: true,
+ pause_on_exit: true,
+ testeeConcurrent: testMain,
+ );
diff --git a/runtime/include/dart_tools_api.h b/runtime/include/dart_tools_api.h
index 3ed741b..3d1b890 100644
--- a/runtime/include/dart_tools_api.h
+++ b/runtime/include/dart_tools_api.h
@@ -496,4 +496,53 @@
DART_EXPORT int64_t
Dart_IsolateRunnableHeapSizeMetric(Dart_Isolate isolate); // Byte
+/*
+ * ========
+ * UserTags
+ * ========
+ */
+
+/*
+ * Gets the current isolate's currently set UserTag instance.
+ *
+ * \return The currently set UserTag instance.
+ */
+DART_EXPORT Dart_Handle Dart_GetCurrentUserTag();
+
+/*
+ * Gets the current isolate's default UserTag instance.
+ *
+ * \return The default UserTag with label 'Default'
+ */
+DART_EXPORT Dart_Handle Dart_GetDefaultUserTag();
+
+/*
+ * Creates a new UserTag instance.
+ *
+ * \param label The name of the new UserTag.
+ *
+ * \return The newly created UserTag instance or an error handle.
+ */
+DART_EXPORT Dart_Handle Dart_NewUserTag(const char* label);
+
+/*
+ * Updates the current isolate's UserTag to a new value.
+ *
+ * \param user_tag The UserTag to be set as the current UserTag.
+ *
+ * \return The previously set UserTag instance or an error handle.
+ */
+DART_EXPORT Dart_Handle Dart_SetCurrentUserTag(Dart_Handle user_tag);
+
+/*
+ * Returns the label of a given UserTag instance.
+ *
+ * \param user_tag The UserTag from which the label will be retrieved.
+ *
+ * \return The UserTag's label. NULL if the user_tag is invalid. The caller is
+ * responsible for freeing the returned label.
+ */
+DART_WARN_UNUSED_RESULT DART_EXPORT char* Dart_GetUserTagLabel(
+ Dart_Handle user_tag);
+
#endif // RUNTIME_INCLUDE_DART_TOOLS_API_H_
diff --git a/runtime/lib/profiler.cc b/runtime/lib/profiler.cc
index 956597a..9a9fc88 100644
--- a/runtime/lib/profiler.cc
+++ b/runtime/lib/profiler.cc
@@ -29,12 +29,7 @@
DEFINE_NATIVE_ENTRY(UserTag_makeCurrent, 0, 1) {
const UserTag& self = UserTag::CheckedHandle(zone, arguments->NativeArgAt(0));
- if (FLAG_trace_intrinsified_natives) {
- OS::PrintErr("UserTag_makeCurrent: %s\n", self.ToCString());
- }
- const UserTag& old = UserTag::Handle(zone, isolate->current_tag());
- self.MakeActive();
- return old.ptr();
+ return self.MakeActive();
}
DEFINE_NATIVE_ENTRY(UserTag_defaultTag, 0, 0) {
diff --git a/runtime/observatory/tests/service/get_version_rpc_test.dart b/runtime/observatory/tests/service/get_version_rpc_test.dart
index fad888f..75d1743 100644
--- a/runtime/observatory/tests/service/get_version_rpc_test.dart
+++ b/runtime/observatory/tests/service/get_version_rpc_test.dart
@@ -12,7 +12,7 @@
final result = await vm.invokeRpcNoUpgrade('getVersion', {});
expect(result['type'], 'Version');
expect(result['major'], 3);
- expect(result['minor'], 47);
+ expect(result['minor'], 48);
expect(result['_privateMajor'], 0);
expect(result['_privateMinor'], 0);
},
diff --git a/runtime/observatory_2/tests/service_2/get_version_rpc_test.dart b/runtime/observatory_2/tests/service_2/get_version_rpc_test.dart
index c7b238a..443f8bb 100644
--- a/runtime/observatory_2/tests/service_2/get_version_rpc_test.dart
+++ b/runtime/observatory_2/tests/service_2/get_version_rpc_test.dart
@@ -9,10 +9,10 @@
var tests = <VMTest>[
(VM vm) async {
- var result = await vm.invokeRpcNoUpgrade('getVersion', {});
+ final result = await vm.invokeRpcNoUpgrade('getVersion', {});
expect(result['type'], equals('Version'));
expect(result['major'], equals(3));
- expect(result['minor'], equals(47));
+ expect(result['minor'], equals(48));
expect(result['_privateMajor'], equals(0));
expect(result['_privateMinor'], equals(0));
},
diff --git a/runtime/vm/clustered_snapshot.cc b/runtime/vm/clustered_snapshot.cc
index 99f2596..9040385 100644
--- a/runtime/vm/clustered_snapshot.cc
+++ b/runtime/vm/clustered_snapshot.cc
@@ -5560,7 +5560,6 @@
*code ^= d->ReadRef();
StubCode::EntryAtPut(i, code);
}
- StubCode::InitializationDone();
}
}
diff --git a/runtime/vm/compiler/asm_intrinsifier_arm.cc b/runtime/vm/compiler/asm_intrinsifier_arm.cc
index f41c51d..375c454 100644
--- a/runtime/vm/compiler/asm_intrinsifier_arm.cc
+++ b/runtime/vm/compiler/asm_intrinsifier_arm.cc
@@ -1272,15 +1272,19 @@
// Compares cid1 and cid2 to see if they're syntactically equivalent. If this
// can be determined by this fast path, it jumps to either equal or not_equal,
-// otherwise it jumps to normal_ir_body. May clobber cid1, cid2, and scratch.
+// if equal but belonging to a generic class, it falls through with the scratch
+// register containing host_type_arguments_field_offset_in_words,
+// otherwise it jumps to normal_ir_body. May clobber scratch.
static void EquivalentClassIds(Assembler* assembler,
Label* normal_ir_body,
Label* equal,
Label* not_equal,
Register cid1,
Register cid2,
- Register scratch) {
- Label different_cids, not_integer, not_integer_or_string;
+ Register scratch,
+ bool testing_instance_cids) {
+ Label different_cids, equal_cids_but_generic, not_integer,
+ not_integer_or_string;
// Check if left hand side is a closure. Closures are handled in the runtime.
__ CompareImmediate(cid1, kClosureCid);
@@ -1296,10 +1300,13 @@
// Check if there are no type arguments. In this case we can return true.
// Otherwise fall through into the runtime to handle comparison.
__ LoadClassById(scratch, cid1);
- __ ldrh(scratch,
- FieldAddress(scratch, target::Class::num_type_arguments_offset()));
- __ CompareImmediate(scratch, 0);
- __ b(normal_ir_body, NE);
+ __ ldr(
+ scratch,
+ FieldAddress(
+ scratch,
+ target::Class::host_type_arguments_field_offset_in_words_offset()));
+ __ CompareImmediate(scratch, target::Class::kNoTypeArguments);
+ __ b(&equal_cids_but_generic, NE);
__ b(equal);
// Class ids are different. Check if we are comparing two string types (with
@@ -1318,35 +1325,50 @@
__ Bind(¬_integer);
// Check if both are String types.
- JumpIfNotString(assembler, cid1, scratch, ¬_integer_or_string);
+ JumpIfNotString(assembler, cid1, scratch,
+ testing_instance_cids ? ¬_integer_or_string : not_equal);
// First type is String. Check if the second is a string too.
JumpIfString(assembler, cid2, scratch, equal);
// String types are only equivalent to other String types.
__ b(not_equal);
- __ Bind(¬_integer_or_string);
- // Check if the first type is a Type. If it is not then types are not
- // equivalent because they have different class ids and they are not String
- // or integer or Type.
- JumpIfNotType(assembler, cid1, scratch, not_equal);
+ if (testing_instance_cids) {
+ __ Bind(¬_integer_or_string);
+ // Check if the first type is a Type. If it is not then types are not
+ // equivalent because they have different class ids and they are not String
+ // or integer or Type.
+ JumpIfNotType(assembler, cid1, scratch, not_equal);
- // First type is a Type. Check if the second is a Type too.
- JumpIfType(assembler, cid2, scratch, equal);
- // Type types are only equivalent to other Type types.
- __ b(not_equal);
+ // First type is a Type. Check if the second is a Type too.
+ JumpIfType(assembler, cid2, scratch, equal);
+ // Type types are only equivalent to other Type types.
+ __ b(not_equal);
+ }
+
+ // The caller must compare the type arguments.
+ __ Bind(&equal_cids_but_generic);
}
void AsmIntrinsifier::ObjectHaveSameRuntimeType(Assembler* assembler,
Label* normal_ir_body) {
- __ ldr(R0, Address(SP, 0 * target::kWordSize));
- __ LoadClassIdMayBeSmi(R1, R0);
-
- __ ldr(R0, Address(SP, 1 * target::kWordSize));
- __ LoadClassIdMayBeSmi(R2, R0);
+ __ ldm(IA, SP, (1 << R1 | 1 << R2));
+ __ LoadClassIdMayBeSmi(R1, R1);
+ __ LoadClassIdMayBeSmi(R2, R2);
Label equal, not_equal;
- EquivalentClassIds(assembler, normal_ir_body, &equal, ¬_equal, R1, R2, R0);
+ EquivalentClassIds(assembler, normal_ir_body, &equal, ¬_equal, R1, R2, R0,
+ /* testing_instance_cids = */ true);
+
+ // Compare type arguments, host_type_arguments_field_offset_in_words in R0.
+ __ ldm(IA, SP, (1 << R1 | 1 << R2));
+ __ AddImmediate(R1, -kHeapObjectTag);
+ __ ldr(R1, Address(R1, R0, LSL, target::kWordSizeLog2));
+ __ AddImmediate(R2, -kHeapObjectTag);
+ __ ldr(R2, Address(R2, R0, LSL, target::kWordSizeLog2));
+ __ cmp(R1, Operand(R2));
+ __ b(normal_ir_body, NE);
+ // Fall through to equal case if type arguments are equal.
__ Bind(&equal);
__ LoadObject(R0, CastHandle<Object>(TrueObject()));
@@ -1396,8 +1418,16 @@
__ SmiUntag(R3);
__ ldr(R4, FieldAddress(R2, target::Type::type_class_id_offset()));
__ SmiUntag(R4);
+ // We are not testing instance cids, but type class cids of Type instances.
EquivalentClassIds(assembler, normal_ir_body, &equiv_cids, ¬_equal, R3, R4,
- R0);
+ R0, /* testing_instance_cids = */ false);
+
+ // Compare type arguments in Type instances.
+ __ ldr(R3, FieldAddress(R1, target::Type::arguments_offset()));
+ __ ldr(R4, FieldAddress(R2, target::Type::arguments_offset()));
+ __ cmp(R3, Operand(R4));
+ __ b(normal_ir_body, NE);
+ // Fall through to check nullability if type arguments are equal.
// Check nullability.
__ Bind(&equiv_cids);
@@ -1990,24 +2020,6 @@
__ Branch(FieldAddress(R0, target::Function::entry_point_offset()));
}
-// On stack: user tag (+0).
-void AsmIntrinsifier::UserTag_makeCurrent(Assembler* assembler,
- Label* normal_ir_body) {
- // R1: Isolate.
- __ LoadIsolate(R1);
- // R0: Current user tag.
- __ ldr(R0, Address(R1, target::Isolate::current_tag_offset()));
- // R2: UserTag.
- __ ldr(R2, Address(SP, +0 * target::kWordSize));
- // Set target::Isolate::current_tag_.
- __ str(R2, Address(R1, target::Isolate::current_tag_offset()));
- // R2: UserTag's tag.
- __ ldr(R2, FieldAddress(R2, target::UserTag::tag_offset()));
- // Set target::Isolate::user_tag_.
- __ str(R2, Address(R1, target::Isolate::user_tag_offset()));
- __ Ret();
-}
-
void AsmIntrinsifier::UserTag_defaultTag(Assembler* assembler,
Label* normal_ir_body) {
__ LoadIsolate(R0);
diff --git a/runtime/vm/compiler/asm_intrinsifier_arm64.cc b/runtime/vm/compiler/asm_intrinsifier_arm64.cc
index 5749365..ce8d67d 100644
--- a/runtime/vm/compiler/asm_intrinsifier_arm64.cc
+++ b/runtime/vm/compiler/asm_intrinsifier_arm64.cc
@@ -1418,15 +1418,19 @@
// Compares cid1 and cid2 to see if they're syntactically equivalent. If this
// can be determined by this fast path, it jumps to either equal or not_equal,
-// otherwise it jumps to normal_ir_body. May clobber cid1, cid2, and scratch.
+// if equal but belonging to a generic class, it falls through with the scratch
+// register containing host_type_arguments_field_offset_in_words,
+// otherwise it jumps to normal_ir_body. May clobber scratch.
static void EquivalentClassIds(Assembler* assembler,
Label* normal_ir_body,
Label* equal,
Label* not_equal,
Register cid1,
Register cid2,
- Register scratch) {
- Label different_cids, not_integer, not_integer_or_string;
+ Register scratch,
+ bool testing_instance_cids) {
+ Label different_cids, equal_cids_but_generic, not_integer,
+ not_integer_or_string;
// Check if left hand side is a closure. Closures are handled in the runtime.
__ CompareImmediate(cid1, kClosureCid);
@@ -1443,10 +1447,12 @@
// Otherwise fall through into the runtime to handle comparison.
__ LoadClassById(scratch, cid1);
__ ldr(scratch,
- FieldAddress(scratch, target::Class::num_type_arguments_offset(),
- kTwoBytes),
- kTwoBytes);
- __ cbnz(normal_ir_body, scratch);
+ FieldAddress(
+ scratch,
+ target::Class::host_type_arguments_field_offset_in_words_offset()),
+ kFourBytes);
+ __ CompareImmediate(scratch, target::Class::kNoTypeArguments);
+ __ b(&equal_cids_but_generic, NE);
__ b(equal);
// Class ids are different. Check if we are comparing two string types (with
@@ -1465,35 +1471,50 @@
__ Bind(¬_integer);
// Check if both are String types.
- JumpIfNotString(assembler, cid1, scratch, ¬_integer_or_string);
+ JumpIfNotString(assembler, cid1, scratch,
+ testing_instance_cids ? ¬_integer_or_string : not_equal);
// First type is String. Check if the second is a string too.
JumpIfString(assembler, cid2, scratch, equal);
// String types are only equivalent to other String types.
__ b(not_equal);
- __ Bind(¬_integer_or_string);
- // Check if the first type is a Type. If it is not then types are not
- // equivalent because they have different class ids and they are not String
- // or integer or Type.
- JumpIfNotType(assembler, cid1, scratch, not_equal);
+ if (testing_instance_cids) {
+ __ Bind(¬_integer_or_string);
+ // Check if the first type is a Type. If it is not then types are not
+ // equivalent because they have different class ids and they are not String
+ // or integer or Type.
+ JumpIfNotType(assembler, cid1, scratch, not_equal);
- // First type is a Type. Check if the second is a Type too.
- JumpIfType(assembler, cid2, scratch, equal);
- // Type types are only equivalent to other Type types.
- __ b(not_equal);
+ // First type is a Type. Check if the second is a Type too.
+ JumpIfType(assembler, cid2, scratch, equal);
+ // Type types are only equivalent to other Type types.
+ __ b(not_equal);
+ }
+
+ // The caller must compare the type arguments.
+ __ Bind(&equal_cids_but_generic);
}
void AsmIntrinsifier::ObjectHaveSameRuntimeType(Assembler* assembler,
Label* normal_ir_body) {
- __ ldr(R0, Address(SP, 0 * target::kWordSize));
+ __ ldp(R0, R1, Address(SP, 0 * target::kWordSize, Address::PairOffset));
+ __ LoadClassIdMayBeSmi(R2, R1);
__ LoadClassIdMayBeSmi(R1, R0);
- __ ldr(R0, Address(SP, 1 * target::kWordSize));
- __ LoadClassIdMayBeSmi(R2, R0);
-
Label equal, not_equal;
- EquivalentClassIds(assembler, normal_ir_body, &equal, ¬_equal, R1, R2, R0);
+ EquivalentClassIds(assembler, normal_ir_body, &equal, ¬_equal, R1, R2, R0,
+ /* testing_instance_cids = */ true);
+
+ // Compare type arguments, host_type_arguments_field_offset_in_words in R0.
+ __ ldp(R1, R2, Address(SP, 0 * target::kWordSize, Address::PairOffset));
+ __ AddImmediate(R1, -kHeapObjectTag);
+ __ ldr(R1, Address(R1, R0, UXTX, Address::Scaled));
+ __ AddImmediate(R2, -kHeapObjectTag);
+ __ ldr(R2, Address(R2, R0, UXTX, Address::Scaled));
+ __ CompareObjectRegisters(R1, R2);
+ __ b(normal_ir_body, NE);
+ // Fall through to equal case if type arguments are equal.
__ Bind(&equal);
__ LoadObject(R0, CastHandle<Object>(TrueObject()));
@@ -1549,8 +1570,16 @@
__ LoadCompressedSmi(R4,
FieldAddress(R2, target::Type::type_class_id_offset()));
__ SmiUntag(R4);
+ // We are not testing instance cids, but type class cids of Type instances.
EquivalentClassIds(assembler, normal_ir_body, &equiv_cids, ¬_equal, R3, R4,
- R0);
+ R0, /* testing_instance_cids = */ false);
+
+ // Compare type arguments in Type instances.
+ __ LoadCompressed(R3, FieldAddress(R1, target::Type::arguments_offset()));
+ __ LoadCompressed(R4, FieldAddress(R2, target::Type::arguments_offset()));
+ __ CompareObjectRegisters(R3, R4);
+ __ b(normal_ir_body, NE);
+ // Fall through to check nullability if type arguments are equal.
// Check nullability.
__ Bind(&equiv_cids);
@@ -2195,24 +2224,6 @@
__ br(R1);
}
-// On stack: user tag (+0).
-void AsmIntrinsifier::UserTag_makeCurrent(Assembler* assembler,
- Label* normal_ir_body) {
- // R1: Isolate.
- __ LoadIsolate(R1);
- // R0: Current user tag.
- __ ldr(R0, Address(R1, target::Isolate::current_tag_offset()));
- // R2: UserTag.
- __ ldr(R2, Address(SP, +0 * target::kWordSize));
- // Set target::Isolate::current_tag_.
- __ str(R2, Address(R1, target::Isolate::current_tag_offset()));
- // R2: UserTag's tag.
- __ ldr(R2, FieldAddress(R2, target::UserTag::tag_offset()));
- // Set target::Isolate::user_tag_.
- __ str(R2, Address(R1, target::Isolate::user_tag_offset()));
- __ ret();
-}
-
void AsmIntrinsifier::UserTag_defaultTag(Assembler* assembler,
Label* normal_ir_body) {
__ LoadIsolate(R0);
diff --git a/runtime/vm/compiler/asm_intrinsifier_ia32.cc b/runtime/vm/compiler/asm_intrinsifier_ia32.cc
index 5909eab..a17f7a8 100644
--- a/runtime/vm/compiler/asm_intrinsifier_ia32.cc
+++ b/runtime/vm/compiler/asm_intrinsifier_ia32.cc
@@ -1358,15 +1358,19 @@
// Compares cid1 and cid2 to see if they're syntactically equivalent. If this
// can be determined by this fast path, it jumps to either equal or not_equal,
-// otherwise it jumps to normal_ir_body. May clobber cid1, cid2, and scratch.
+// if equal but belonging to a generic class, it falls through with the scratch
+// register containing host_type_arguments_field_offset_in_words,
+// otherwise it jumps to normal_ir_body. May clobber scratch.
static void EquivalentClassIds(Assembler* assembler,
Label* normal_ir_body,
Label* equal,
Label* not_equal,
Register cid1,
Register cid2,
- Register scratch) {
- Label different_cids, not_integer, not_integer_or_string;
+ Register scratch,
+ bool testing_instance_cids) {
+ Label different_cids, equal_cids_but_generic, not_integer,
+ not_integer_or_string;
// Check if left hand side is a closure. Closures are handled in the runtime.
__ cmpl(cid1, Immediate(kClosureCid));
@@ -1382,10 +1386,13 @@
// Check if there are no type arguments. In this case we can return true.
// Otherwise fall through into the runtime to handle comparison.
__ LoadClassById(scratch, cid1);
- __ movzxw(scratch,
- FieldAddress(scratch, target::Class::num_type_arguments_offset()));
- __ cmpl(scratch, Immediate(0));
- __ j(NOT_EQUAL, normal_ir_body);
+ __ movl(
+ scratch,
+ FieldAddress(
+ scratch,
+ target::Class::host_type_arguments_field_offset_in_words_offset()));
+ __ cmpl(scratch, Immediate(target::Class::kNoTypeArguments));
+ __ j(NOT_EQUAL, &equal_cids_but_generic, Assembler::kNearJump);
__ jmp(equal);
// Class ids are different. Check if we are comparing two string types (with
@@ -1405,23 +1412,29 @@
__ Bind(¬_integer);
// Check if both are String types.
- JumpIfNotString(assembler, cid1, ¬_integer_or_string);
+ JumpIfNotString(assembler, cid1,
+ testing_instance_cids ? ¬_integer_or_string : not_equal);
// First type is a String. Check if the second is a String too.
JumpIfString(assembler, cid2, equal);
// String types are only equivalent to other String types.
__ jmp(not_equal);
- __ Bind(¬_integer_or_string);
- // Check if the first type is a Type. If it is not then types are not
- // equivalent because they have different class ids and they are not String
- // or integer or Type.
- JumpIfNotType(assembler, cid1, not_equal);
+ if (testing_instance_cids) {
+ __ Bind(¬_integer_or_string);
+ // Check if the first type is a Type. If it is not then types are not
+ // equivalent because they have different class ids and they are not String
+ // or integer or Type.
+ JumpIfNotType(assembler, cid1, not_equal);
- // First type is a Type. Check if the second is a Type too.
- JumpIfType(assembler, cid2, equal);
- // Type types are only equivalent to other Type types.
- __ jmp(not_equal);
+ // First type is a Type. Check if the second is a Type too.
+ JumpIfType(assembler, cid2, equal);
+ // Type types are only equivalent to other Type types.
+ __ jmp(not_equal);
+ }
+
+ // The caller must compare the type arguments.
+ __ Bind(&equal_cids_but_generic);
}
void AsmIntrinsifier::ObjectHaveSameRuntimeType(Assembler* assembler,
@@ -1434,7 +1447,16 @@
Label equal, not_equal;
EquivalentClassIds(assembler, normal_ir_body, &equal, ¬_equal, EDI, EBX,
- EAX);
+ EAX, /* testing_instance_cids = */ true);
+
+ // Compare type arguments, host_type_arguments_field_offset_in_words in EAX.
+ __ movl(EDI, Address(ESP, +1 * target::kWordSize));
+ __ movl(EBX, Address(ESP, +2 * target::kWordSize));
+ __ movl(EDI, FieldAddress(EDI, EAX, TIMES_4, 0));
+ __ movl(EBX, FieldAddress(EBX, EAX, TIMES_4, 0));
+ __ cmpl(EDI, EBX);
+ __ j(NOT_EQUAL, normal_ir_body, Assembler::kNearJump);
+ // Fall through to equal case if type arguments are equal.
__ Bind(&equal);
__ LoadObject(EAX, CastHandle<Object>(TrueObject()));
@@ -1489,8 +1511,16 @@
__ SmiUntag(ECX);
__ movl(EDX, FieldAddress(EBX, target::Type::type_class_id_offset()));
__ SmiUntag(EDX);
+ // We are not testing instance cids, but type class cids of Type instances.
EquivalentClassIds(assembler, normal_ir_body, &equiv_cids, ¬_equal, ECX,
- EDX, EAX);
+ EDX, EAX, /* testing_instance_cids = */ false);
+
+ // Compare type arguments in Type instances.
+ __ movl(ECX, FieldAddress(EDI, target::Type::arguments_offset()));
+ __ movl(EDX, FieldAddress(EBX, target::Type::arguments_offset()));
+ __ cmpl(ECX, EDX);
+ __ j(NOT_EQUAL, normal_ir_body, Assembler::kNearJump);
+ // Fall through to check nullability if type arguments are equal.
// Check nullability.
__ Bind(&equiv_cids);
@@ -1979,24 +2009,6 @@
__ jmp(FieldAddress(EAX, target::Function::entry_point_offset()));
}
-// On stack: user tag (+1), return-address (+0).
-void AsmIntrinsifier::UserTag_makeCurrent(Assembler* assembler,
- Label* normal_ir_body) {
- // EDI: Isolate.
- __ LoadIsolate(EDI);
- // EAX: Current user tag.
- __ movl(EAX, Address(EDI, target::Isolate::current_tag_offset()));
- // EAX: UserTag.
- __ movl(EBX, Address(ESP, +1 * target::kWordSize));
- // Set target::Isolate::current_tag_.
- __ movl(Address(EDI, target::Isolate::current_tag_offset()), EBX);
- // EAX: UserTag's tag.
- __ movl(EBX, FieldAddress(EBX, target::UserTag::tag_offset()));
- // Set target::Isolate::user_tag_.
- __ movl(Address(EDI, target::Isolate::user_tag_offset()), EBX);
- __ ret();
-}
-
void AsmIntrinsifier::UserTag_defaultTag(Assembler* assembler,
Label* normal_ir_body) {
__ LoadIsolate(EAX);
diff --git a/runtime/vm/compiler/asm_intrinsifier_x64.cc b/runtime/vm/compiler/asm_intrinsifier_x64.cc
index f691931..3ba88b8 100644
--- a/runtime/vm/compiler/asm_intrinsifier_x64.cc
+++ b/runtime/vm/compiler/asm_intrinsifier_x64.cc
@@ -1262,15 +1262,19 @@
// Compares cid1 and cid2 to see if they're syntactically equivalent. If this
// can be determined by this fast path, it jumps to either equal or not_equal,
-// otherwise it jumps to normal_ir_body. May clobber cid1, cid2, and scratch.
+// if equal but belonging to a generic class, it falls through with the scratch
+// register containing host_type_arguments_field_offset_in_words,
+// otherwise it jumps to normal_ir_body. May clobber scratch.
static void EquivalentClassIds(Assembler* assembler,
Label* normal_ir_body,
Label* equal,
Label* not_equal,
Register cid1,
Register cid2,
- Register scratch) {
- Label different_cids, not_integer, not_integer_or_string;
+ Register scratch,
+ bool testing_instance_cids) {
+ Label different_cids, equal_cids_but_generic, not_integer,
+ not_integer_or_string;
// Check if left hand side is a closure. Closures are handled in the runtime.
__ cmpq(cid1, Immediate(kClosureCid));
@@ -1286,10 +1290,13 @@
// Check if there are no type arguments. In this case we can return true.
// Otherwise fall through into the runtime to handle comparison.
__ LoadClassById(scratch, cid1);
- __ movzxw(scratch,
- FieldAddress(scratch, target::Class::num_type_arguments_offset()));
- __ cmpq(scratch, Immediate(0));
- __ j(NOT_EQUAL, normal_ir_body);
+ __ movl(
+ scratch,
+ FieldAddress(
+ scratch,
+ target::Class::host_type_arguments_field_offset_in_words_offset()));
+ __ cmpl(scratch, Immediate(target::Class::kNoTypeArguments));
+ __ j(NOT_EQUAL, &equal_cids_but_generic, Assembler::kNearJump);
__ jmp(equal);
// Class ids are different. Check if we are comparing two string types (with
@@ -1309,23 +1316,29 @@
__ Bind(¬_integer);
// Check if both are String types.
- JumpIfNotString(assembler, cid1, ¬_integer_or_string);
+ JumpIfNotString(assembler, cid1,
+ testing_instance_cids ? ¬_integer_or_string : not_equal);
// First type is a String. Check if the second is a String too.
JumpIfString(assembler, cid2, equal);
// String types are only equivalent to other String types.
__ jmp(not_equal);
- __ Bind(¬_integer_or_string);
- // Check if the first type is a Type. If it is not then types are not
- // equivalent because they have different class ids and they are not String
- // or integer or Type.
- JumpIfNotType(assembler, cid1, not_equal);
+ if (testing_instance_cids) {
+ __ Bind(¬_integer_or_string);
+ // Check if the first type is a Type. If it is not then types are not
+ // equivalent because they have different class ids and they are not String
+ // or integer or Type.
+ JumpIfNotType(assembler, cid1, not_equal);
- // First type is a Type. Check if the second is a Type too.
- JumpIfType(assembler, cid2, equal);
- // Type types are only equivalent to other Type types.
- __ jmp(not_equal);
+ // First type is a Type. Check if the second is a Type too.
+ JumpIfType(assembler, cid2, equal);
+ // Type types are only equivalent to other Type types.
+ __ jmp(not_equal);
+ }
+
+ // The caller must compare the type arguments.
+ __ Bind(&equal_cids_but_generic);
}
void AsmIntrinsifier::ObjectHaveSameRuntimeType(Assembler* assembler,
@@ -1338,7 +1351,16 @@
Label equal, not_equal;
EquivalentClassIds(assembler, normal_ir_body, &equal, ¬_equal, RCX, RDX,
- RAX);
+ RAX, /* testing_instance_cids = */ true);
+
+ // Compare type arguments, host_type_arguments_field_offset_in_words in RAX.
+ __ movq(RCX, Address(RSP, +1 * target::kWordSize));
+ __ movq(RDX, Address(RSP, +2 * target::kWordSize));
+ __ movq(RCX, FieldAddress(RCX, RAX, TIMES_8, 0));
+ __ movq(RDX, FieldAddress(RDX, RAX, TIMES_8, 0));
+ __ cmpq(RCX, RDX);
+ __ j(NOT_EQUAL, normal_ir_body, Assembler::kNearJump);
+ // Fall through to equal case if type arguments are equal.
__ Bind(&equal);
__ LoadObject(RAX, CastHandle<Object>(TrueObject()));
@@ -1399,8 +1421,16 @@
__ LoadCompressedSmi(RSI,
FieldAddress(RDX, target::Type::type_class_id_offset()));
__ SmiUntag(RSI);
+ // We are not testing instance cids, but type class cids of Type instances.
EquivalentClassIds(assembler, normal_ir_body, &equiv_cids, ¬_equal, RDI,
- RSI, RAX);
+ RSI, RAX, /* testing_instance_cids = */ false);
+
+ // Compare type arguments in Type instances.
+ __ LoadCompressed(RDI, FieldAddress(RCX, target::Type::arguments_offset()));
+ __ LoadCompressed(RSI, FieldAddress(RDX, target::Type::arguments_offset()));
+ __ cmpq(RDI, RSI);
+ __ j(NOT_EQUAL, normal_ir_body, Assembler::kNearJump);
+ // Fall through to check nullability if type arguments are equal.
// Check nullability.
__ Bind(&equiv_cids);
@@ -2056,24 +2086,6 @@
__ jmp(RDI);
}
-// On stack: user tag (+1), return-address (+0).
-void AsmIntrinsifier::UserTag_makeCurrent(Assembler* assembler,
- Label* normal_ir_body) {
- // RBX: Isolate.
- __ LoadIsolate(RBX);
- // RAX: Current user tag.
- __ movq(RAX, Address(RBX, target::Isolate::current_tag_offset()));
- // R10: UserTag.
- __ movq(R10, Address(RSP, +1 * target::kWordSize));
- // Set Isolate::current_tag_.
- __ movq(Address(RBX, target::Isolate::current_tag_offset()), R10);
- // R10: UserTag's tag.
- __ movq(R10, FieldAddress(R10, target::UserTag::tag_offset()));
- // Set Isolate::user_tag_.
- __ movq(Address(RBX, target::Isolate::user_tag_offset()), R10);
- __ ret();
-}
-
void AsmIntrinsifier::UserTag_defaultTag(Assembler* assembler,
Label* normal_ir_body) {
__ LoadIsolate(RAX);
diff --git a/runtime/vm/compiler/recognized_methods_list.h b/runtime/vm/compiler/recognized_methods_list.h
index 4c4af85..3f46559 100644
--- a/runtime/vm/compiler/recognized_methods_list.h
+++ b/runtime/vm/compiler/recognized_methods_list.h
@@ -377,7 +377,6 @@
GRAPH_MATH_LIB_INTRINSIC_LIST(V) \
#define DEVELOPER_LIB_INTRINSIC_LIST(V) \
- V(_UserTag, makeCurrent, UserTag_makeCurrent, 0x5bd9b88e) \
V(::, _getDefaultTag, UserTag_defaultTag, 0x6c19c8a5) \
V(::, _getCurrentTag, Profiler_getCurrentTag, 0x70ead08e) \
V(::, _isDartStreamEnabled, Timeline_isDartStreamEnabled, 0xc97aafb3) \
diff --git a/runtime/vm/dart_api_impl.cc b/runtime/vm/dart_api_impl.cc
index 7ba1fc0..db08698 100644
--- a/runtime/vm/dart_api_impl.cc
+++ b/runtime/vm/dart_api_impl.cc
@@ -7011,4 +7011,53 @@
OS::PrepareToAbort();
}
+DART_EXPORT Dart_Handle Dart_GetCurrentUserTag() {
+ Thread* thread = Thread::Current();
+ CHECK_ISOLATE(thread->isolate());
+ DARTSCOPE(thread);
+ Isolate* isolate = thread->isolate();
+ return Api::NewHandle(thread, isolate->current_tag());
+}
+
+DART_EXPORT Dart_Handle Dart_GetDefaultUserTag() {
+ Thread* thread = Thread::Current();
+ CHECK_ISOLATE(thread->isolate());
+ DARTSCOPE(thread);
+ Isolate* isolate = thread->isolate();
+ return Api::NewHandle(thread, isolate->default_tag());
+}
+
+DART_EXPORT Dart_Handle Dart_NewUserTag(const char* label) {
+ Thread* thread = Thread::Current();
+ CHECK_ISOLATE(thread->isolate());
+ DARTSCOPE(thread);
+ if (label == nullptr) {
+ return Api::NewError(
+ "Dart_NewUserTag expects argument 'label' to be non-null");
+ }
+ const String& value = String::Handle(String::New(label));
+ return Api::NewHandle(thread, UserTag::New(value));
+}
+
+DART_EXPORT Dart_Handle Dart_SetCurrentUserTag(Dart_Handle user_tag) {
+ Thread* thread = Thread::Current();
+ CHECK_ISOLATE(thread->isolate());
+ DARTSCOPE(thread);
+ const UserTag& tag = Api::UnwrapUserTagHandle(Z, user_tag);
+ if (tag.IsNull()) {
+ RETURN_TYPE_ERROR(Z, user_tag, UserTag);
+ }
+ return Api::NewHandle(thread, tag.MakeActive());
+}
+
+DART_EXPORT char* Dart_GetUserTagLabel(Dart_Handle user_tag) {
+ DARTSCOPE(Thread::Current());
+ const UserTag& tag = Api::UnwrapUserTagHandle(Z, user_tag);
+ if (tag.IsNull()) {
+ return nullptr;
+ }
+ const String& label = String::Handle(Z, tag.label());
+ return Utils::StrDup(label.ToCString());
+}
+
} // namespace dart
diff --git a/runtime/vm/dart_api_impl_test.cc b/runtime/vm/dart_api_impl_test.cc
index 1f2f190..6f116a0 100644
--- a/runtime/vm/dart_api_impl_test.cc
+++ b/runtime/vm/dart_api_impl_test.cc
@@ -9363,6 +9363,52 @@
"Invalid argument(s): Couldn't resolve function: 'DoesNotResolve'");
}
+TEST_CASE(DartAPI_UserTags) {
+ Dart_Handle default_tag = Dart_GetDefaultUserTag();
+ EXPECT_VALID(default_tag);
+
+ auto default_label =
+ Utils::CStringUniquePtr(Dart_GetUserTagLabel(default_tag), std::free);
+ EXPECT_STREQ(default_label.get(), "Default");
+
+ Dart_Handle current_tag = Dart_GetCurrentUserTag();
+ EXPECT(Dart_IdentityEquals(default_tag, current_tag));
+
+ auto current_label =
+ Utils::CStringUniquePtr(Dart_GetUserTagLabel(current_tag), std::free);
+ EXPECT_STREQ(default_label.get(), current_label.get());
+
+ Dart_Handle new_tag = Dart_NewUserTag("Foo");
+ EXPECT_VALID(new_tag);
+
+ auto new_tag_label =
+ Utils::CStringUniquePtr(Dart_GetUserTagLabel(new_tag), std::free);
+ EXPECT_STREQ(new_tag_label.get(), "Foo");
+
+ Dart_Handle old_tag = Dart_SetCurrentUserTag(new_tag);
+ EXPECT_VALID(old_tag);
+
+ auto old_label =
+ Utils::CStringUniquePtr(Dart_GetUserTagLabel(old_tag), std::free);
+ EXPECT_STREQ(old_label.get(), default_label.get());
+
+ current_tag = Dart_GetCurrentUserTag();
+ EXPECT(Dart_IdentityEquals(new_tag, current_tag));
+
+ current_label =
+ Utils::CStringUniquePtr(Dart_GetUserTagLabel(current_tag), std::free);
+ EXPECT_STREQ(current_label.get(), new_tag_label.get());
+
+ EXPECT(Dart_GetUserTagLabel(Dart_Null()) == nullptr);
+
+ EXPECT_ERROR(Dart_NewUserTag(nullptr),
+ "Dart_NewUserTag expects argument 'label' to be non-null");
+
+ EXPECT_ERROR(
+ Dart_SetCurrentUserTag(Dart_Null()),
+ "Dart_SetCurrentUserTag expects argument 'user_tag' to be non-null");
+}
+
#endif // !PRODUCT
} // namespace dart
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index b78b400..fbc6aba 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -6183,17 +6183,18 @@
printer->AddString(TypeParameter::CanonicalNameCString(
are_class_type_parameters, base, base + i));
}
- if (!AllDynamicBounds()) {
+ if (FLAG_show_internal_names || !AllDynamicBounds()) {
type = BoundAt(i);
// Do not print default bound or non-nullable Object bound in weak mode.
if (!type.IsNull() &&
- (!type.IsObjectType() ||
+ (FLAG_show_internal_names || !type.IsObjectType() ||
(thread->isolate_group()->null_safety() && type.IsNonNullable()))) {
printer->AddString(" extends ");
type.PrintName(name_visibility, printer);
if (FLAG_show_internal_names && !AllDynamicDefaults()) {
type = DefaultAt(i);
- if (!type.IsNull() && !type.IsDynamicType()) {
+ if (!type.IsNull() &&
+ (FLAG_show_internal_names || !type.IsDynamicType())) {
printer->AddString(" defaults to ");
type.PrintName(name_visibility, printer);
}
@@ -6427,18 +6428,19 @@
if (this->ptr() == other.ptr()) {
return true;
}
- if (IsNull() || other.IsNull()) {
- return false;
- }
- const intptr_t num_types = Length();
- if (num_types != other.Length()) {
- return false;
+ if (kind == TypeEquality::kCanonical) {
+ if (IsNull() || other.IsNull()) {
+ return false;
+ }
+ if (Length() != other.Length()) {
+ return false;
+ }
}
AbstractType& type = AbstractType::Handle();
AbstractType& other_type = AbstractType::Handle();
for (intptr_t i = from_index; i < from_index + len; i++) {
- type = TypeAt(i);
- other_type = other.TypeAt(i);
+ type = IsNull() ? Type::DynamicType() : TypeAt(i);
+ other_type = other.IsNull() ? Type::DynamicType() : other.TypeAt(i);
// Still unfinalized vectors should not be considered equivalent.
if (type.IsNull() || !type.IsEquivalent(other_type, kind, trail)) {
return false;
@@ -20252,6 +20254,7 @@
if (stub.IsNull()) {
// This only happens during bootstrapping when creating Type objects before
// we have the instructions.
+ ASSERT(type_class_id() == kDynamicCid || type_class_id() == kVoidCid);
StoreNonPointer(&untag()->type_test_stub_entry_point_, 0);
untag()->set_type_test_stub(stub.ptr());
return;
@@ -25687,10 +25690,25 @@
return "_MirrorReference";
}
-void UserTag::MakeActive() const {
+UserTagPtr UserTag::MakeActive() const {
Isolate* isolate = Isolate::Current();
ASSERT(isolate != NULL);
+ UserTag& old = UserTag::Handle(isolate->current_tag());
isolate->set_current_tag(*this);
+
+#if !defined(PRODUCT)
+ // Notify VM service clients that the current UserTag has changed.
+ if (Service::profiler_stream.enabled()) {
+ ServiceEvent event(ServiceEvent::kUserTagChanged);
+ String& name = String::Handle(old.label());
+ event.set_previous_tag(name.ToCString());
+ name ^= label();
+ event.set_updated_tag(name.ToCString());
+ Service::HandleEvent(&event);
+ }
+#endif // !defined(PRODUCT)
+
+ return old.ptr();
}
UserTagPtr UserTag::New(const String& label, Heap::Space space) {
diff --git a/runtime/vm/object.h b/runtime/vm/object.h
index 3620f18..70dcdcc 100644
--- a/runtime/vm/object.h
+++ b/runtime/vm/object.h
@@ -7735,8 +7735,9 @@
bool IsEquivalent(const TypeArguments& other,
TypeEquality kind,
TrailPtr trail = nullptr) const {
- return IsSubvectorEquivalent(other, 0, IsNull() ? 0 : Length(), kind,
- trail);
+ // Make a null vector a vector of dynamic as long as the other vector.
+ return IsSubvectorEquivalent(other, 0, IsNull() ? other.Length() : Length(),
+ kind, trail);
}
bool IsSubvectorEquivalent(const TypeArguments& other,
intptr_t from_index,
@@ -11558,7 +11559,7 @@
StringPtr label() const { return untag()->label(); }
- void MakeActive() const;
+ UserTagPtr MakeActive() const;
static intptr_t InstanceSize() {
return RoundedAllocationSize(sizeof(UntaggedUserTag));
diff --git a/runtime/vm/object_test.cc b/runtime/vm/object_test.cc
index 70256cf..a920d42 100644
--- a/runtime/vm/object_test.cc
+++ b/runtime/vm/object_test.cc
@@ -4605,7 +4605,7 @@
"vmName\":\"\",\"location\":{\"type\":\"SourceLocation\",\"script\":{"
"\"type\":\"@Script\",\"fixedId\":true,\"id\":\"\",\"uri\":\"dart:"
"developer-patch\\/profiler.dart\",\"_kind\":\"kernel\"},\"tokenPos\":"
- "414,\"endTokenPos\":672},\"library\":{\"type\":\"@Library\","
+ "414,\"endTokenPos\":628},\"library\":{\"type\":\"@Library\","
"\"fixedId\":true,\"id\":\"\",\"name\":\"dart.developer\",\"uri\":"
"\"dart:developer\"}},"
// Handle non-zero identity hash.
diff --git a/runtime/vm/service.cc b/runtime/vm/service.cc
index 4ebfbae..804ca50 100644
--- a/runtime/vm/service.cc
+++ b/runtime/vm/service.cc
@@ -128,16 +128,18 @@
StreamInfo Service::logging_stream("Logging");
StreamInfo Service::extension_stream("Extension");
StreamInfo Service::timeline_stream("Timeline");
+StreamInfo Service::profiler_stream("Profiler");
const uint8_t* Service::dart_library_kernel_ = NULL;
intptr_t Service::dart_library_kernel_len_ = 0;
static StreamInfo* const streams_[] = {
- &Service::vm_stream, &Service::isolate_stream,
- &Service::debug_stream, &Service::gc_stream,
- &Service::echo_stream, &Service::heapsnapshot_stream,
- &Service::logging_stream, &Service::extension_stream,
- &Service::timeline_stream};
+ &Service::vm_stream, &Service::isolate_stream,
+ &Service::debug_stream, &Service::gc_stream,
+ &Service::echo_stream, &Service::heapsnapshot_stream,
+ &Service::logging_stream, &Service::extension_stream,
+ &Service::timeline_stream, &Service::profiler_stream,
+};
bool Service::ListenStream(const char* stream_id) {
if (FLAG_trace_service) {
diff --git a/runtime/vm/service.h b/runtime/vm/service.h
index 4b411a3..e178c6a 100644
--- a/runtime/vm/service.h
+++ b/runtime/vm/service.h
@@ -15,7 +15,7 @@
namespace dart {
#define SERVICE_PROTOCOL_MAJOR_VERSION 3
-#define SERVICE_PROTOCOL_MINOR_VERSION 47
+#define SERVICE_PROTOCOL_MINOR_VERSION 48
class Array;
class EmbedderServiceHandler;
@@ -171,6 +171,7 @@
static StreamInfo logging_stream;
static StreamInfo extension_stream;
static StreamInfo timeline_stream;
+ static StreamInfo profiler_stream;
static bool ListenStream(const char* stream_id);
static void CancelStream(const char* stream_id);
diff --git a/runtime/vm/service/service.md b/runtime/vm/service/service.md
index ed0eebb..d8f1e79 100644
--- a/runtime/vm/service/service.md
+++ b/runtime/vm/service/service.md
@@ -1,8 +1,8 @@
-# Dart VM Service Protocol 3.47
+# Dart VM Service Protocol 3.48
> Please post feedback to the [observatory-discuss group][discuss-list]
-This document describes of _version 3.47_ of the Dart VM Service Protocol. This
+This document describes of _version 3.48_ of the Dart VM Service Protocol. This
protocol is used to communicate with a running Dart Virtual Machine.
To use the Service Protocol, start the VM with the *--observe* flag.
@@ -1477,6 +1477,7 @@
VM | VMUpdate, VMFlagUpdate
Isolate | IsolateStart, IsolateRunnable, IsolateExit, IsolateUpdate, IsolateReload, ServiceExtensionAdded
Debug | PauseStart, PauseExit, PauseBreakpoint, PauseInterrupted, PauseException, PausePostRequest, Resume, BreakpointAdded, BreakpointResolved, BreakpointRemoved, BreakpointUpdated, Inspect, None
+Profiler | UserTagChanged
GC | GC
Extension | Extension
Timeline | TimelineEvents, TimelineStreamsSubscriptionUpdate
@@ -2154,6 +2155,12 @@
// This is provided for the event kinds:
// HeapSnapshot
bool last [optional];
+
+ // The current UserTag label.
+ string updatedTag [optional];
+
+ // The previous UserTag label.
+ string previousTag [optional];
}
```
@@ -2264,6 +2271,9 @@
// Notification that a Service has been removed from the Service Protocol
// from another client.
ServiceUnregistered,
+
+ // Notification that the UserTag for an isolate has been changed.
+ UserTagChanged,
}
```
@@ -4058,5 +4068,6 @@
3.45 | Added `setBreakpointState` RPC and `BreakpointUpdated` event kind.
3.46 | Moved `sourceLocation` property into reference types for `Class`, `Field`, and `Function`.
3.47 | Added `shows` and `hides` properties to `LibraryDependency`.
+3.48 | Added `Profiler` stream, `UserTagChanged` event kind, and `updatedTag` and `previousTag` properties to `Event`.
[discuss-list]: https://groups.google.com/a/dartlang.org/forum/#!forum/observatory-discuss
diff --git a/runtime/vm/service_event.cc b/runtime/vm/service_event.cc
index 6b97b6d..f8ab174 100644
--- a/runtime/vm/service_event.cc
+++ b/runtime/vm/service_event.cc
@@ -28,22 +28,24 @@
: isolate_(isolate),
isolate_group_(isolate_group),
kind_(event_kind),
- flag_name_(NULL),
- flag_new_value_(NULL),
- embedder_kind_(NULL),
- embedder_stream_id_(NULL),
- breakpoint_(NULL),
- top_frame_(NULL),
- timeline_event_block_(NULL),
- extension_rpc_(NULL),
- exception_(NULL),
- reload_error_(NULL),
- spawn_token_(NULL),
- spawn_error_(NULL),
+ flag_name_(nullptr),
+ flag_new_value_(nullptr),
+ previous_tag_(nullptr),
+ updated_tag_(nullptr),
+ embedder_kind_(nullptr),
+ embedder_stream_id_(nullptr),
+ breakpoint_(nullptr),
+ top_frame_(nullptr),
+ timeline_event_block_(nullptr),
+ extension_rpc_(nullptr),
+ exception_(nullptr),
+ reload_error_(nullptr),
+ spawn_token_(nullptr),
+ spawn_error_(nullptr),
at_async_jump_(false),
- inspectee_(NULL),
- gc_stats_(NULL),
- bytes_(NULL),
+ inspectee_(nullptr),
+ gc_stats_(nullptr),
+ bytes_(nullptr),
bytes_length_(0),
timestamp_(OS::GetCurrentTimeMillis()) {
// We should never generate events for the vm isolate as it is never reported
@@ -137,6 +139,8 @@
return "TimelineEvents";
case kTimelineStreamSubscriptionsUpdate:
return "TimelineStreamSubscriptionsUpdate";
+ case kUserTagChanged:
+ return "UserTagChanged";
default:
UNREACHABLE();
return "Unknown";
@@ -187,11 +191,14 @@
return &Service::timeline_stream;
case kEmbedder:
- return NULL;
+ return nullptr;
+
+ case kUserTagChanged:
+ return &Service::profiler_stream;
default:
UNREACHABLE();
- return NULL;
+ return nullptr;
}
}
@@ -213,6 +220,10 @@
// For backwards compatibility, "new_value" is also provided.
jsobj.AddProperty("newValue", flag_new_value());
}
+ if (kind() == kUserTagChanged) {
+ jsobj.AddProperty("previousTag", previous_tag());
+ jsobj.AddProperty("updatedTag", updated_tag());
+ }
if (kind() == kIsolateReload) {
if (reload_error_ == NULL) {
jsobj.AddProperty("status", "success");
diff --git a/runtime/vm/service_event.h b/runtime/vm/service_event.h
index 80aaa8a..3b8de0e 100644
--- a/runtime/vm/service_event.h
+++ b/runtime/vm/service_event.h
@@ -60,6 +60,8 @@
// Sent when SetVMTimelineFlags is called.
kTimelineStreamSubscriptionsUpdate,
+ kUserTagChanged,
+
kIllegal,
};
@@ -111,6 +113,14 @@
const char* flag_new_value() const { return flag_new_value_; }
void set_flag_new_value(const char* value) { flag_new_value_ = value; }
+ const char* previous_tag() const { return previous_tag_; }
+ void set_previous_tag(const char* previous_tag) {
+ previous_tag_ = previous_tag;
+ }
+
+ const char* updated_tag() const { return updated_tag_; }
+ void set_updated_tag(const char* updated_tag) { updated_tag_ = updated_tag; }
+
const char* embedder_kind() const { return embedder_kind_; }
const char* KindAsCString() const;
@@ -217,6 +227,8 @@
EventKind kind_;
const char* flag_name_;
const char* flag_new_value_;
+ const char* previous_tag_;
+ const char* updated_tag_;
const char* embedder_kind_;
const char* embedder_stream_id_;
Breakpoint* breakpoint_;
diff --git a/runtime/vm/stub_code.cc b/runtime/vm/stub_code.cc
index aee8934..78a3355 100644
--- a/runtime/vm/stub_code.cc
+++ b/runtime/vm/stub_code.cc
@@ -35,7 +35,6 @@
VM_STUB_CODE_LIST(STUB_CODE_DECLARE)
#undef STUB_CODE_DECLARE
};
-AcqRelAtomic<bool> StubCode::initialized_ = {false};
#if defined(DART_PRECOMPILED_RUNTIME)
void StubCode::Init() {
@@ -85,8 +84,6 @@
ASSERT(StubCode::UnknownDartCode().IsFunctionCode());
}
#endif // defined(DART_PRECOMPILER)
-
- InitializationDone();
}
#undef STUB_CODE_GENERATE
@@ -114,13 +111,16 @@
#endif // defined(DART_PRECOMPILED_RUNTIME)
void StubCode::Cleanup() {
- initialized_.store(false, std::memory_order_release);
-
for (size_t i = 0; i < ARRAY_SIZE(entries_); i++) {
entries_[i].code = nullptr;
}
}
+bool StubCode::HasBeenInitialized() {
+ // Use AsynchronousGapMarker as canary.
+ return entries_[kAsynchronousGapMarkerIndex].code != nullptr;
+}
+
bool StubCode::InInvocationStub(uword pc) {
ASSERT(HasBeenInitialized());
uword entry = StubCode::InvokeDartCode().EntryPoint();
diff --git a/runtime/vm/stub_code.h b/runtime/vm/stub_code.h
index ae39cae..6dcedc5 100644
--- a/runtime/vm/stub_code.h
+++ b/runtime/vm/stub_code.h
@@ -42,12 +42,7 @@
static void Cleanup();
// Returns true if stub code has been initialized.
- static bool HasBeenInitialized() {
- return initialized_.load(std::memory_order_acquire);
- }
- static void InitializationDone() {
- initialized_.store(true, std::memory_order_release);
- }
+ static bool HasBeenInitialized();
// Check if specified pc is in the dart invocation stub used for
// transitioning into dart code.
@@ -125,7 +120,6 @@
#endif
};
static StubCodeEntry entries_[kNumStubEntries];
- static AcqRelAtomic<bool> initialized_;
};
} // namespace dart
diff --git a/runtime/vm/type_testing_stubs.cc b/runtime/vm/type_testing_stubs.cc
index 98842ce..879677c 100644
--- a/runtime/vm/type_testing_stubs.cc
+++ b/runtime/vm/type_testing_stubs.cc
@@ -106,6 +106,9 @@
// During bootstrapping we have no access to stubs yet, so we'll just return
// `null` and patch these later in `Object::FinishInit()`.
if (!StubCode::HasBeenInitialized()) {
+ ASSERT(type.IsType());
+ const classid_t cid = type.type_class_id();
+ ASSERT(cid == kDynamicCid || cid == kVoidCid);
return Code::null();
}
diff --git a/sdk/lib/_internal/fix_data.yaml b/sdk/lib/_internal/fix_data.yaml
index 1d4cc33..baf52a4 100644
--- a/sdk/lib/_internal/fix_data.yaml
+++ b/sdk/lib/_internal/fix_data.yaml
@@ -3,7 +3,7 @@
# BSD-style license that can be found in the LICENSE file.
# Please add new fixes to the top of the file, separated by one blank line
-# from other fixes. Add corresponding golden tests to
+# from other fixes. Add corresponding golden tests to
# `tests/lib/fix_data_tests` for each new fix.
# For documentation about this file format, see
@@ -59,4 +59,433 @@
inClass: 'FileMode'
changes:
- kind: 'rename'
- newName: 'writeOnlyAppend'
\ No newline at end of file
+ newName: 'writeOnlyAppend'
+
+ - title: "Rename to 'continue_'"
+ date: 2021-06-15
+ element:
+ uris: [ 'dart:io', 'dart:html' ]
+ field: 'CONTINUE'
+ inClass: 'HttpStatus'
+ changes:
+ - kind: 'rename'
+ newName: 'continue_'
+
+ - title: "Rename to 'switchingProtocols'"
+ date: 2021-06-15
+ element:
+ uris: [ 'dart:io', 'dart:html' ]
+ field: 'SWITCHING_PROTOCOLS'
+ inClass: 'HttpStatus'
+ changes:
+ - kind: 'rename'
+ newName: 'switchingProtocols'
+
+ - title: "Rename to 'ok'"
+ date: 2021-06-15
+ element:
+ uris: [ 'dart:io', 'dart:html' ]
+ field: 'OK'
+ inClass: 'HttpStatus'
+ changes:
+ - kind: 'rename'
+ newName: 'ok'
+
+ - title: "Rename to 'created'"
+ date: 2021-06-15
+ element:
+ uris: [ 'dart:io', 'dart:html' ]
+ field: 'CREATED'
+ inClass: 'HttpStatus'
+ changes:
+ - kind: 'rename'
+ newName: 'created'
+
+ - title: "Rename to 'accepted'"
+ date: 2021-06-15
+ element:
+ uris: [ 'dart:io', 'dart:html' ]
+ field: 'ACCEPTED'
+ inClass: 'HttpStatus'
+ changes:
+ - kind: 'rename'
+ newName: 'accepted'
+
+ - title: "Rename to 'nonAuthoritativeInformation'"
+ date: 2021-06-15
+ element:
+ uris: [ 'dart:io', 'dart:html' ]
+ field: 'NON_AUTHORITATIVE_INFORMATION'
+ inClass: 'HttpStatus'
+ changes:
+ - kind: 'rename'
+ newName: 'nonAuthoritativeInformation'
+
+ - title: "Rename to 'noContent'"
+ date: 2021-06-15
+ element:
+ uris: [ 'dart:io', 'dart:html' ]
+ field: 'NO_CONTENT'
+ inClass: 'HttpStatus'
+ changes:
+ - kind: 'rename'
+ newName: 'noContent'
+
+ - title: "Rename to 'resetContent'"
+ date: 2021-06-15
+ element:
+ uris: [ 'dart:io', 'dart:html' ]
+ field: 'RESET_CONTENT'
+ inClass: 'HttpStatus'
+ changes:
+ - kind: 'rename'
+ newName: 'resetContent'
+
+ - title: "Rename to 'partialContent'"
+ date: 2021-06-15
+ element:
+ uris: [ 'dart:io', 'dart:html' ]
+ field: 'PARTIAL_CONTENT'
+ inClass: 'HttpStatus'
+ changes:
+ - kind: 'rename'
+ newName: 'partialContent'
+
+ - title: "Rename to 'multipleChoices'"
+ date: 2021-06-15
+ element:
+ uris: [ 'dart:io', 'dart:html' ]
+ field: 'MULTIPLE_CHOICES'
+ inClass: 'HttpStatus'
+ changes:
+ - kind: 'rename'
+ newName: 'multipleChoices'
+
+ - title: "Rename to 'movedPermanently'"
+ date: 2021-06-15
+ element:
+ uris: [ 'dart:io', 'dart:html' ]
+ field: 'MOVED_PERMANENTLY'
+ inClass: 'HttpStatus'
+ changes:
+ - kind: 'rename'
+ newName: 'movedPermanently'
+
+ - title: "Rename to 'found'"
+ date: 2021-06-15
+ element:
+ uris: [ 'dart:io', 'dart:html' ]
+ field: 'FOUND'
+ inClass: 'HttpStatus'
+ changes:
+ - kind: 'rename'
+ newName: 'found'
+
+ - title: "Rename to 'movedTemporarily'"
+ date: 2021-06-15
+ element:
+ uris: [ 'dart:io', 'dart:html' ]
+ field: 'MOVED_TEMPORARILY'
+ inClass: 'HttpStatus'
+ changes:
+ - kind: 'rename'
+ newName: 'movedTemporarily'
+
+ - title: "Rename to 'seeOther'"
+ date: 2021-06-15
+ element:
+ uris: [ 'dart:io', 'dart:html' ]
+ field: 'SEE_OTHER'
+ inClass: 'HttpStatus'
+ changes:
+ - kind: 'rename'
+ newName: 'seeOther'
+
+ - title: "Rename to 'notModified'"
+ date: 2021-06-15
+ element:
+ uris: [ 'dart:io', 'dart:html' ]
+ field: 'NOT_MODIFIED'
+ inClass: 'HttpStatus'
+ changes:
+ - kind: 'rename'
+ newName: 'notModified'
+
+ - title: "Rename to 'useProxy'"
+ date: 2021-06-15
+ element:
+ uris: [ 'dart:io', 'dart:html' ]
+ field: 'USE_PROXY'
+ inClass: 'HttpStatus'
+ changes:
+ - kind: 'rename'
+ newName: 'useProxy'
+
+ - title: "Rename to 'temporaryRedirect'"
+ date: 2021-06-15
+ element:
+ uris: [ 'dart:io', 'dart:html' ]
+ field: 'TEMPORARY_REDIRECT'
+ inClass: 'HttpStatus'
+ changes:
+ - kind: 'rename'
+ newName: 'temporaryRedirect'
+
+ - title: "Rename to 'badRequest'"
+ date: 2021-06-15
+ element:
+ uris: [ 'dart:io', 'dart:html' ]
+ field: 'BAD_REQUEST'
+ inClass: 'HttpStatus'
+ changes:
+ - kind: 'rename'
+ newName: 'badRequest'
+
+ - title: "Rename to 'unauthorized'"
+ date: 2021-06-15
+ element:
+ uris: [ 'dart:io', 'dart:html' ]
+ field: 'UNAUTHORIZED'
+ inClass: 'HttpStatus'
+ changes:
+ - kind: 'rename'
+ newName: 'unauthorized'
+
+ - title: "Rename to 'paymentRequired'"
+ date: 2021-06-15
+ element:
+ uris: [ 'dart:io', 'dart:html' ]
+ field: 'PAYMENT_REQUIRED'
+ inClass: 'HttpStatus'
+ changes:
+ - kind: 'rename'
+ newName: 'paymentRequired'
+
+ - title: "Rename to 'forbidden'"
+ date: 2021-06-15
+ element:
+ uris: [ 'dart:io', 'dart:html' ]
+ field: 'FORBIDDEN'
+ inClass: 'HttpStatus'
+ changes:
+ - kind: 'rename'
+ newName: 'forbidden'
+
+ - title: "Rename to 'notFound'"
+ date: 2021-06-15
+ element:
+ uris: [ 'dart:io', 'dart:html' ]
+ field: 'NOT_FOUND'
+ inClass: 'HttpStatus'
+ changes:
+ - kind: 'rename'
+ newName: 'notFound'
+
+ - title: "Rename to 'methodNotAllowed'"
+ date: 2021-06-15
+ element:
+ uris: [ 'dart:io', 'dart:html' ]
+ field: 'METHOD_NOT_ALLOWED'
+ inClass: 'HttpStatus'
+ changes:
+ - kind: 'rename'
+ newName: 'methodNotAllowed'
+
+ - title: "Rename to 'notAcceptable'"
+ date: 2021-06-15
+ element:
+ uris: [ 'dart:io', 'dart:html' ]
+ field: 'NOT_ACCEPTABLE'
+ inClass: 'HttpStatus'
+ changes:
+ - kind: 'rename'
+ newName: 'notAcceptable'
+
+ - title: "Rename to 'proxyAuthenticationRequired'"
+ date: 2021-06-15
+ element:
+ uris: [ 'dart:io', 'dart:html' ]
+ field: 'PROXY_AUTHENTICATION_REQUIRED'
+ inClass: 'HttpStatus'
+ changes:
+ - kind: 'rename'
+ newName: 'proxyAuthenticationRequired'
+
+ - title: "Rename to 'requestTimeout'"
+ date: 2021-06-15
+ element:
+ uris: [ 'dart:io', 'dart:html' ]
+ field: 'REQUEST_TIMEOUT'
+ inClass: 'HttpStatus'
+ changes:
+ - kind: 'rename'
+ newName: 'requestTimeout'
+
+ - title: "Rename to 'conflict'"
+ date: 2021-06-15
+ element:
+ uris: [ 'dart:io', 'dart:html' ]
+ field: 'CONFLICT'
+ inClass: 'HttpStatus'
+ changes:
+ - kind: 'rename'
+ newName: 'conflict'
+
+ - title: "Rename to 'gone'"
+ date: 2021-06-15
+ element:
+ uris: [ 'dart:io', 'dart:html' ]
+ field: 'GONE'
+ inClass: 'HttpStatus'
+ changes:
+ - kind: 'rename'
+ newName: 'gone'
+
+ - title: "Rename to 'lengthRequired'"
+ date: 2021-06-15
+ element:
+ uris: [ 'dart:io', 'dart:html' ]
+ field: 'LENGTH_REQUIRED'
+ inClass: 'HttpStatus'
+ changes:
+ - kind: 'rename'
+ newName: 'lengthRequired'
+
+ - title: "Rename to 'preconditionFailed'"
+ date: 2021-06-15
+ element:
+ uris: [ 'dart:io', 'dart:html' ]
+ field: 'PRECONDITION_FAILED'
+ inClass: 'HttpStatus'
+ changes:
+ - kind: 'rename'
+ newName: 'preconditionFailed'
+
+ - title: "Rename to 'requestEntityTooLarge'"
+ date: 2021-06-15
+ element:
+ uris: [ 'dart:io', 'dart:html' ]
+ field: 'REQUEST_ENTITY_TOO_LARGE'
+ inClass: 'HttpStatus'
+ changes:
+ - kind: 'rename'
+ newName: 'requestEntityTooLarge'
+
+ - title: "Rename to 'requestUriTooLong'"
+ date: 2021-06-15
+ element:
+ uris: [ 'dart:io', 'dart:html' ]
+ field: 'REQUEST_URI_TOO_LONG'
+ inClass: 'HttpStatus'
+ changes:
+ - kind: 'rename'
+ newName: 'requestUriTooLong'
+
+ - title: "Rename to 'unsupportedMediaType'"
+ date: 2021-06-15
+ element:
+ uris: [ 'dart:io', 'dart:html' ]
+ field: 'UNSUPPORTED_MEDIA_TYPE'
+ inClass: 'HttpStatus'
+ changes:
+ - kind: 'rename'
+ newName: 'unsupportedMediaType'
+
+ - title: "Rename to 'requestedRangeNotSatisfiable'"
+ date: 2021-06-15
+ element:
+ uris: [ 'dart:io', 'dart:html' ]
+ field: 'REQUESTED_RANGE_NOT_SATISFIABLE'
+ inClass: 'HttpStatus'
+ changes:
+ - kind: 'rename'
+ newName: 'requestedRangeNotSatisfiable'
+ - title: "Rename to 'expectationFailed'"
+ date: 2021-06-15
+ element:
+ uris: [ 'dart:io', 'dart:html' ]
+ field: 'EXPECTATION_FAILED'
+ inClass: 'HttpStatus'
+ changes:
+ - kind: 'rename'
+ newName: 'expectationFailed'
+
+ - title: "Rename to 'upgradeRequired'"
+ date: 2021-06-15
+ element:
+ uris: [ 'dart:io', 'dart:html' ]
+ field: 'UPGRADE_REQUIRED'
+ inClass: 'HttpStatus'
+ changes:
+ - kind: 'rename'
+ newName: 'upgradeRequired'
+
+ - title: "Rename to 'internalServerError'"
+ date: 2021-06-15
+ element:
+ uris: [ 'dart:io', 'dart:html' ]
+ field: 'INTERNAL_SERVER_ERROR'
+ inClass: 'HttpStatus'
+ changes:
+ - kind: 'rename'
+ newName: 'internalServerError'
+
+ - title: "Rename to 'notImplemented'"
+ date: 2021-06-15
+ element:
+ uris: [ 'dart:io', 'dart:html' ]
+ field: 'NOT_IMPLEMENTED'
+ inClass: 'HttpStatus'
+ changes:
+ - kind: 'rename'
+ newName: 'notImplemented'
+
+ - title: "Rename to 'badGateway'"
+ date: 2021-06-15
+ element:
+ uris: [ 'dart:io', 'dart:html' ]
+ field: 'BAD_GATEWAY'
+ inClass: 'HttpStatus'
+ changes:
+ - kind: 'rename'
+ newName: 'badGateway'
+
+ - title: "Rename to 'serviceUnavailable'"
+ date: 2021-06-15
+ element:
+ uris: [ 'dart:io', 'dart:html' ]
+ field: 'SERVICE_UNAVAILABLE'
+ inClass: 'HttpStatus'
+ changes:
+ - kind: 'rename'
+ newName: 'serviceUnavailable'
+
+ - title: "Rename to 'gatewayTimeout'"
+ date: 2021-06-15
+ element:
+ uris: [ 'dart:io', 'dart:html' ]
+ field: 'GATEWAY_TIMEOUT'
+ inClass: 'HttpStatus'
+ changes:
+ - kind: 'rename'
+ newName: 'gatewayTimeout'
+
+ - title: "Rename to 'httpVersionNotSupported'"
+ date: 2021-06-15
+ element:
+ uris: [ 'dart:io', 'dart:html' ]
+ field: 'HTTP_VERSION_NOT_SUPPORTED'
+ inClass: 'HttpStatus'
+ changes:
+ - kind: 'rename'
+ newName: 'httpVersionNotSupported'
+
+ - title: "Rename to 'networkConnectTimeoutError'"
+ date: 2021-06-15
+ element:
+ uris: [ 'dart:io', 'dart:html' ]
+ field: 'NETWORK_CONNECT_TIMEOUT_ERROR'
+ inClass: 'HttpStatus'
+ changes:
+ - kind: 'rename'
+ newName: 'networkConnectTimeoutError'
diff --git a/sdk/lib/_internal/vm/lib/profiler.dart b/sdk/lib/_internal/vm/lib/profiler.dart
index 812916e..722d938 100644
--- a/sdk/lib/_internal/vm/lib/profiler.dart
+++ b/sdk/lib/_internal/vm/lib/profiler.dart
@@ -18,7 +18,6 @@
class _UserTag implements UserTag {
factory _UserTag(String label) native "UserTag_new";
String get label native "UserTag_label";
- @pragma("vm:recognized", "asm-intrinsic")
UserTag makeCurrent() native "UserTag_makeCurrent";
}
diff --git a/sdk/lib/async/future.dart b/sdk/lib/async/future.dart
index 4d98a53..edc6264 100644
--- a/sdk/lib/async/future.dart
+++ b/sdk/lib/async/future.dart
@@ -698,6 +698,30 @@
Future<T> timeout(Duration timeLimit, {FutureOr<T> onTimeout()?});
}
+/// Explicitly ignores a future.
+///
+/// Not all futures need to be awaited.
+/// The Dart linter has an optional ["unawaited futures" lint](https://dart-lang.github.io/linter/lints/unawaited_futures.html)
+/// which enforces that futures (expressions with a static type of [Future])
+/// in asynchronous functions are handled *somehow*.
+/// If a particular future value doesn't need to be awaited,
+/// you can call `unawaited(...)` with it, which will avoid the lint,
+/// simply because the expression no longer has type [Future].
+/// Using `unawaited` has no other effect.
+/// You should use `unawaited` to convey the *intention* of
+/// deliberately not waiting for the future.
+///
+/// If the future completes with an error,
+/// it was likely a mistake to not await it.
+/// That error will still occur and will be considered unhandled
+/// unless the same future is awaited (or otherwise handled) elsewhere too.
+/// Because of that, `unawaited` should only be used for futures that
+/// are *expected* to complete with a value.
+/// You can use [FutureExtension.ignore] if you also don't want to know
+/// about errors from this future.
+@Since("2.15")
+void unawaited(Future<void> future) {}
+
/// Convenience methods on futures.
///
/// Adds functionality to futures which makes it easier to
@@ -770,6 +794,32 @@
handleError(error as E, stackTrace),
test: (Object error) => error is E && (test == null || test(error)));
}
+
+ /// Completely ignores this future and its result.
+ ///
+ /// Not all futures are important, not even if they contain errors,
+ /// for example if a request was made, but the response is no longer needed.
+ /// Simply ignoring a future can result in uncaught asynchronous errors.
+ /// This method instead handles (and ignores) any values or errors
+ /// coming from this future, making it safe to otherwise ignore
+ /// the future.
+ ///
+ /// Use `ignore` to signal that the result of the future is
+ /// no longer important to the program, not even if it's an error.
+ /// If you merely want to silence the ["unawaited futures" lint](https://dart-lang.github.io/linter/lints/unawaited_futures.html),
+ /// use the [unawaited] function instead.
+ /// That will ensure that an unexpected error is still reported.
+ @Since("2.15")
+ void ignore() {
+ var self = this;
+ if (self is _Future<T>) {
+ self._ignore();
+ } else {
+ self.then<void>(_ignore, onError: _ignore);
+ }
+ }
+
+ static void _ignore(Object? _, [Object? __]) {}
}
/// Thrown when a scheduled timeout happens while waiting for an async result.
diff --git a/sdk/lib/async/future_impl.dart b/sdk/lib/async/future_impl.dart
index e73300f..c89d345 100644
--- a/sdk/lib/async/future_impl.dart
+++ b/sdk/lib/async/future_impl.dart
@@ -69,9 +69,14 @@
static const int maskTestError = 4;
static const int maskWhenComplete = 8;
static const int stateChain = 0;
+ // Handles values, passes errors on.
static const int stateThen = maskValue;
+ // Handles values and errors.
static const int stateThenOnerror = maskValue | maskError;
+ // Handles errors, has errorCallback.
static const int stateCatchError = maskError;
+ // Ignores both values and errors. Has no callback or errorCallback.
+ // The [result] future is ignored, its always the same as the source.
static const int stateCatchErrorTest = maskError | maskTestError;
static const int stateWhenComplete = maskWhenComplete;
static const int maskType =
@@ -191,21 +196,40 @@
/// [_FutureListener] listeners.
static const int _stateIncomplete = 0;
+ /// Flag set when an error need not be handled.
+ ///
+ /// Set by the [FutureExtensions.ignore] method to avoid
+ /// having to introduce an unnecessary listener.
+ /// Only relevant until the future is completed.
+ static const int _stateIgnoreError = 1;
+
/// Pending completion. Set when completed using [_asyncComplete] or
/// [_asyncCompleteError]. It is an error to try to complete it again.
/// [_resultOrListeners] holds listeners.
- static const int _statePendingComplete = 1;
+ static const int _statePendingComplete = 2;
- /// The future has been chained to another future. The result of that
- /// other future becomes the result of this future as well.
+ /// The future has been chained to another "source" [_Future].
+ ///
+ /// The result of that other future becomes the result of this future
+ /// as well, when the other future completes.
+ /// This future cannot be completed again.
/// [_resultOrListeners] contains the source future.
- static const int _stateChained = 2;
+ /// Listeners have been moved to the chained future.
+ static const int _stateChained = 4;
/// The future has been completed with a value result.
- static const int _stateValue = 4;
+ ///
+ /// [_resultOrListeners] contains the value.
+ static const int _stateValue = 8;
/// The future has been completed with an error result.
- static const int _stateError = 8;
+ ///
+ /// [_resultOrListeners] contains an [AsyncEror]
+ /// holding the error and stack trace.
+ static const int _stateError = 16;
+
+ /// Mask for the states above except [_stateIgnoreError].
+ static const int _completionStateMask = 30;
/// Whether the future is complete, and as what.
int _state = _stateIncomplete;
@@ -227,8 +251,8 @@
/// and it is not chained to another future.
///
/// The future is another future that this 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.
+ /// 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;
@@ -253,12 +277,14 @@
/// Creates a future that is already completed with the value.
_Future.value(T value) : this.zoneValue(value, Zone._current);
- bool get _mayComplete => _state == _stateIncomplete;
- bool get _isPendingComplete => _state == _statePendingComplete;
- bool get _mayAddListener => _state <= _statePendingComplete;
- bool get _isChained => _state == _stateChained;
- bool get _isComplete => _state >= _stateValue;
- bool get _hasError => _state == _stateError;
+ bool get _mayComplete => (_state & _completionStateMask) == _stateIncomplete;
+ bool get _isPendingComplete => (_state & _statePendingComplete) != 0;
+ bool get _mayAddListener =>
+ _state <= (_statePendingComplete | _stateIgnoreError);
+ bool get _isChained => (_state & _stateChained) != 0;
+ bool get _isComplete => (_state & (_stateValue | _stateError)) != 0;
+ bool get _hasError => (_state & _stateError) != 0;
+ bool get _ignoreError => (_state & _stateIgnoreError) != 0;
static List<Function>? _continuationFunctions(_Future<Object> future) {
List<Function>? result = null;
@@ -283,7 +309,7 @@
void _setChained(_Future source) {
assert(_mayAddListener);
- _state = _stateChained;
+ _state = _stateChained | (_state & _stateIgnoreError);
_resultOrListeners = source;
}
@@ -315,6 +341,10 @@
return result;
}
+ void _ignore() {
+ _state |= _stateIgnoreError;
+ }
+
Future<T> catchError(Function onError, {bool test(Object error)?}) {
_Future<T> result = new _Future<T>();
if (!identical(result._zone, _rootZone)) {
@@ -337,13 +367,13 @@
Stream<T> asStream() => new Stream<T>.fromFuture(this);
void _setPendingComplete() {
- assert(_mayComplete);
- _state = _statePendingComplete;
+ assert(_mayComplete); // Aka _statIncomplete
+ _state ^= _stateIncomplete ^ _statePendingComplete;
}
void _clearPendingComplete() {
assert(_isPendingComplete);
- _state = _stateIncomplete;
+ _state ^= _statePendingComplete ^ _stateIncomplete;
}
AsyncError get _error {
@@ -365,7 +395,7 @@
void _setErrorObject(AsyncError error) {
assert(!_isComplete); // But may have a completion pending.
- _state = _stateError;
+ _state = _stateError | (_state & _stateIgnoreError);
_resultOrListeners = error;
}
@@ -379,7 +409,8 @@
void _cloneResult(_Future source) {
assert(!_isComplete);
assert(source._isComplete);
- _state = source._state;
+ _state =
+ (source._state & _completionStateMask) | (_state & _stateIgnoreError);
_resultOrListeners = source._resultOrListeners;
}
@@ -615,7 +646,7 @@
assert(source._isComplete);
bool hasError = source._hasError;
if (listeners == null) {
- if (hasError) {
+ if (hasError && !source._ignoreError) {
AsyncError asyncError = source._error;
source._zone
.handleUncaughtError(asyncError.error, asyncError.stackTrace);
diff --git a/tests/language/regress/regress23746_test.dart b/tests/language/regress/regress23746_test.dart
new file mode 100644
index 0000000..fd9a510
--- /dev/null
+++ b/tests/language/regress/regress23746_test.dart
@@ -0,0 +1,22 @@
+// 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.
+
+import "package:expect/expect.dart";
+
+class A<T, U> {
+ B<T> get b => B<T>();
+}
+
+class B<T> {}
+
+@pragma('vm:never-inline')
+bool test(Object a, Object b) {
+ print(a.runtimeType);
+ print(b.runtimeType);
+ return a.runtimeType == b.runtimeType;
+}
+
+void main() {
+ Expect.isTrue(test(B<int>(), A<int, String>().b));
+}
diff --git a/tests/language/static_type_helper.dart b/tests/language/static_type_helper.dart
index 9484387..4a7809a 100644
--- a/tests/language/static_type_helper.dart
+++ b/tests/language/static_type_helper.dart
@@ -2,7 +2,10 @@
// 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.
-// Ensures a context type of [T] for the operand.
+/// Helper to create [Type] values.
+Type typeOf<T>() => T;
+
+/// Ensures a context type of [T] for the operand.
void context<T>(T x) {}
/// Captures the context type of the call and returns the same type.
@@ -34,6 +37,23 @@
T expectStaticType<R extends Exactly<T>>() {
return this;
}
+
+ /// Invokes [callback] with the static type of `this`.
+ ///
+ /// Allows any operation on the type.
+ T captureStaticType(void Function<X>() callback) {
+ callback<T>();
+ return this;
+ }
+}
+
+/// Invokes [callback] with the static type of [value].
+///
+/// Similar to [StaticType.captureStaticType], but works
+/// for types like `void` and `dynamic` which do not allow
+/// extension methods.
+void captureStaticType<T>(T value, void Function<X>(X value) callback) {
+ callback<T>(value);
}
/// Use with [StaticType.expectStaticType] to expect precisely the type [T].
diff --git a/tests/language_2/regress/regress23746_test.dart b/tests/language_2/regress/regress23746_test.dart
new file mode 100644
index 0000000..fd9a510
--- /dev/null
+++ b/tests/language_2/regress/regress23746_test.dart
@@ -0,0 +1,22 @@
+// 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.
+
+import "package:expect/expect.dart";
+
+class A<T, U> {
+ B<T> get b => B<T>();
+}
+
+class B<T> {}
+
+@pragma('vm:never-inline')
+bool test(Object a, Object b) {
+ print(a.runtimeType);
+ print(b.runtimeType);
+ return a.runtimeType == b.runtimeType;
+}
+
+void main() {
+ Expect.isTrue(test(B<int>(), A<int, String>().b));
+}
diff --git a/tests/language_2/static_type_helper.dart b/tests/language_2/static_type_helper.dart
new file mode 100644
index 0000000..4a7809a
--- /dev/null
+++ b/tests/language_2/static_type_helper.dart
@@ -0,0 +1,90 @@
+// Copyright (c) 2020, 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.
+
+/// Helper to create [Type] values.
+Type typeOf<T>() => T;
+
+/// Ensures a context type of [T] for the operand.
+void context<T>(T x) {}
+
+/// Captures the context type of the call and returns the same type.
+///
+/// Can be used to check the context type as:
+/// ```dart
+/// int x = contextType(1 /* valid value */)..expectStaticType<Exactly<int>>;
+/// ```
+T contextType<T>(Object result) => result as T;
+
+extension StaticType<T> on T {
+ /// Check the static type.
+ ///
+ /// Use as follows (assuming `e` has static type `num`):
+ /// ```dart
+ /// e.expectStaticType<Exactly<num>>() // No context type.
+ /// e.expectStaticType<SubtypeOf<Object>>() // No context type.
+ /// e.expectStaticType<SupertypeOf<int>>() // No context type.
+ /// ```
+ /// or
+ /// ```dart
+ /// e..expectStaticType<Exactly<num>>() // Preserve context type.
+ /// e..expectStaticType<SubtypeOf<Object>>() // Preserve context type.
+ /// e..expectStaticType<SupertypeOf<int>>() // Preserve context type.
+ /// ```
+ /// This will be a *compile-time error* if the static type is not
+ /// as required by the constraints type (the one passed to [Exactly],
+ /// [SubtypeOf] or [SupertypeOf].)
+ T expectStaticType<R extends Exactly<T>>() {
+ return this;
+ }
+
+ /// Invokes [callback] with the static type of `this`.
+ ///
+ /// Allows any operation on the type.
+ T captureStaticType(void Function<X>() callback) {
+ callback<T>();
+ return this;
+ }
+}
+
+/// Invokes [callback] with the static type of [value].
+///
+/// Similar to [StaticType.captureStaticType], but works
+/// for types like `void` and `dynamic` which do not allow
+/// extension methods.
+void captureStaticType<T>(T value, void Function<X>(X value) callback) {
+ callback<T>(value);
+}
+
+/// Use with [StaticType.expectStaticType] to expect precisely the type [T].
+///
+/// Example use:
+/// ```dart
+/// "abc".expectStaticType<Exactly<String>>();
+/// ```
+typedef Exactly<T> = T Function(T);
+
+/// Use with [StaticType.expectStaticType] to expect a subtype of [T].
+///
+/// Example use:
+/// ```dart
+/// num x = 1;
+/// x.expectStaticType<SubtypeOf<Object>>();
+/// ```
+typedef SubtypeOf<T> = Never Function(T);
+
+/// Use with [StaticType.expectStaticType] to expect a supertype of [T].
+///
+/// Example use:
+/// ```dart
+/// num x = 1;
+/// x.expectStaticType<SupertypeOf<int>>();
+/// ```
+typedef SupertypeOf<T> = T Function(Object?);
+
+/// Checks that an expression is assignable to [T1], [T2] and [Object].
+///
+/// This ensures that the static type of the expression is either dynamic,
+/// Never, or a type assignable to both [T1] and [T2], and if those are
+/// unrelated, it must be an intersection type.
+void checkIntersectionType<T1, T2>(T1 v1, T2 v2, Object v3) {}
diff --git a/tests/lib/async/future_extension_test.dart b/tests/lib/async/future_extension_test.dart
new file mode 100644
index 0000000..cd28986
--- /dev/null
+++ b/tests/lib/async/future_extension_test.dart
@@ -0,0 +1,54 @@
+// 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.
+
+import 'package:async_helper/async_helper.dart';
+import "package:expect/expect.dart";
+import 'dart:async' show Completer, runZonedGuarded;
+import '../../language/static_type_helper.dart';
+
+void main() {
+ testIgnore();
+}
+
+void testIgnore() {
+ var future = Future<int>.value(42);
+ captureStaticType(future.ignore(), <T>(T value) {
+ Expect.equals(typeOf<void>(), T);
+ });
+
+ asyncStart();
+ // Ignored futures can still be listend to.
+ {
+ var c = Completer<int>.sync();
+ var f = c.future;
+ f.ignore();
+ asyncStart();
+ f.catchError((e) {
+ Expect.equals("ERROR1", e);
+ asyncEnd();
+ return 0;
+ });
+ c.completeError("ERROR1");
+ }
+
+ // Ignored futures are not uncaught errors.
+ {
+ asyncStart();
+ bool threw = false;
+ runZonedGuarded(() {
+ var c = Completer<int>.sync();
+ var f = c.future;
+ f.ignore();
+ c.completeError("ERROR2");
+ }, (e, s) {
+ threw = true;
+ Expect.fail("Should not happen: $e");
+ });
+ Future.delayed(Duration.zero, () {
+ if (threw) Expect.fail("Future not ignored.");
+ asyncEnd();
+ });
+ }
+ asyncEnd();
+}
diff --git a/tests/lib/async/unawaited_error_test.dart b/tests/lib/async/unawaited_error_test.dart
new file mode 100644
index 0000000..597a9f8
--- /dev/null
+++ b/tests/lib/async/unawaited_error_test.dart
@@ -0,0 +1,11 @@
+// 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.
+
+void main() {
+ // The `unawaited` function is not exposed by dart:core.
+ unawaited;
+ // [error line 7, column 3, length 9]
+ // [cfe] Getter not found: 'unawaited'.
+ // [analyzer] COMPILE_TIME_ERROR.UNDEFINED_IDENTIFIER
+}
diff --git a/tests/lib/async/unawaited_test.dart b/tests/lib/async/unawaited_test.dart
new file mode 100644
index 0000000..dc095cc
--- /dev/null
+++ b/tests/lib/async/unawaited_test.dart
@@ -0,0 +1,52 @@
+// 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.
+
+import 'package:async_helper/async_helper.dart';
+import "package:expect/expect.dart";
+import 'dart:async' show Completer, runZonedGuarded, unawaited;
+import 'dart:async' as prefix;
+import '../../language/static_type_helper.dart';
+
+void main() {
+ testUnawaited();
+}
+
+void testUnawaited() {
+ // Exists where expected.
+ prefix.unawaited.expectStaticType<Exactly<void Function(Future<Object?>)>>();
+
+ var future = Future<int>.value(42);
+ captureStaticType(unawaited(future), <T>(value) {
+ Expect.equals(typeOf<void>(), T);
+ });
+
+ asyncStart();
+ // Unawaited futures still throw.
+ {
+ var c = Completer<int>();
+ var f = c.future;
+ unawaited(f);
+ asyncStart();
+ f.catchError((e) {
+ Expect.equals("ERROR1", e);
+ asyncEnd();
+ return 0;
+ });
+ c.completeError("ERROR1");
+ }
+ // Unawaited futures are still uncaught errors.
+ {
+ asyncStart();
+ runZonedGuarded(() {
+ var c = Completer<int>();
+ var f = c.future;
+ unawaited(f);
+ c.completeError("ERROR2");
+ }, (e, s) {
+ Expect.equals("ERROR2", e);
+ asyncEnd();
+ });
+ }
+ asyncEnd();
+}
diff --git a/tests/lib/fix_data_tests/file.dart b/tests/lib/fix_data_tests/file.dart
deleted file mode 100644
index 0e5435d..0000000
--- a/tests/lib/fix_data_tests/file.dart
+++ /dev/null
@@ -1,13 +0,0 @@
-// 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.
-
-import 'dart:io';
-
-void main() {
- print(FileMode.APPEND);
- print(FileMode.READ);
- print(FileMode.WRITE);
- print(FileMode.WRITE_ONLY);
- print(FileMode.WRITE_ONLY_APPEND);
-}
diff --git a/tests/lib/fix_data_tests/file.dart.expect b/tests/lib/fix_data_tests/file.dart.expect
deleted file mode 100644
index aafbba6..0000000
--- a/tests/lib/fix_data_tests/file.dart.expect
+++ /dev/null
@@ -1,13 +0,0 @@
-// 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.
-
-import 'dart:io';
-
-void main() {
- print(FileMode.append);
- print(FileMode.read);
- print(FileMode.write);
- print(FileMode.writeOnly);
- print(FileMode.writeOnlyAppend);
-}
diff --git a/tests/lib/fix_data_tests/html.dart b/tests/lib/fix_data_tests/html.dart
new file mode 100644
index 0000000..e0f3ac3
--- /dev/null
+++ b/tests/lib/fix_data_tests/html.dart
@@ -0,0 +1,51 @@
+// 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.
+
+import 'dart:html';
+
+void main() {
+ print(HttpStatus.CONTINUE);
+ print(HttpStatus.SWITCHING_PROTOCOLS);
+ print(HttpStatus.OK);
+ print(HttpStatus.CREATED);
+ print(HttpStatus.ACCEPTED);
+ print(HttpStatus.NON_AUTHORITATIVE_INFORMATION);
+ print(HttpStatus.NO_CONTENT);
+ print(HttpStatus.RESET_CONTENT);
+ print(HttpStatus.PARTIAL_CONTENT);
+ print(HttpStatus.MULTIPLE_CHOICES);
+ print(HttpStatus.MOVED_PERMANENTLY);
+ print(HttpStatus.FOUND);
+ print(HttpStatus.MOVED_TEMPORARILY);
+ print(HttpStatus.SEE_OTHER);
+ print(HttpStatus.NOT_MODIFIED);
+ print(HttpStatus.USE_PROXY);
+ print(HttpStatus.TEMPORARY_REDIRECT);
+ print(HttpStatus.BAD_REQUEST);
+ print(HttpStatus.UNAUTHORIZED);
+ print(HttpStatus.PAYMENT_REQUIRED);
+ print(HttpStatus.FORBIDDEN);
+ print(HttpStatus.NOT_FOUND);
+ print(HttpStatus.METHOD_NOT_ALLOWED);
+ print(HttpStatus.NOT_ACCEPTABLE);
+ print(HttpStatus.PROXY_AUTHENTICATION_REQUIRED);
+ print(HttpStatus.REQUEST_TIMEOUT);
+ print(HttpStatus.CONFLICT);
+ print(HttpStatus.GONE);
+ print(HttpStatus.LENGTH_REQUIRED);
+ print(HttpStatus.PRECONDITION_FAILED);
+ print(HttpStatus.REQUEST_ENTITY_TOO_LARGE);
+ print(HttpStatus.REQUEST_URI_TOO_LONG);
+ print(HttpStatus.UNSUPPORTED_MEDIA_TYPE);
+ print(HttpStatus.REQUESTED_RANGE_NOT_SATISFIABLE);
+ print(HttpStatus.EXPECTATION_FAILED);
+ print(HttpStatus.UPGRADE_REQUIRED);
+ print(HttpStatus.INTERNAL_SERVER_ERROR);
+ print(HttpStatus.NOT_IMPLEMENTED);
+ print(HttpStatus.BAD_GATEWAY);
+ print(HttpStatus.SERVICE_UNAVAILABLE);
+ print(HttpStatus.GATEWAY_TIMEOUT);
+ print(HttpStatus.HTTP_VERSION_NOT_SUPPORTED);
+ print(HttpStatus.NETWORK_CONNECT_TIMEOUT_ERROR);
+}
diff --git a/tests/lib/fix_data_tests/html.dart.expect b/tests/lib/fix_data_tests/html.dart.expect
new file mode 100644
index 0000000..066f0b3
--- /dev/null
+++ b/tests/lib/fix_data_tests/html.dart.expect
@@ -0,0 +1,51 @@
+// 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.
+
+import 'dart:html';
+
+void main() {
+ print(HttpStatus.continue_);
+ print(HttpStatus.switchingProtocols);
+ print(HttpStatus.ok);
+ print(HttpStatus.created);
+ print(HttpStatus.accepted);
+ print(HttpStatus.nonAuthoritativeInformation);
+ print(HttpStatus.noContent);
+ print(HttpStatus.resetContent);
+ print(HttpStatus.partialContent);
+ print(HttpStatus.multipleChoices);
+ print(HttpStatus.movedPermanently);
+ print(HttpStatus.found);
+ print(HttpStatus.movedTemporarily);
+ print(HttpStatus.seeOther);
+ print(HttpStatus.notModified);
+ print(HttpStatus.useProxy);
+ print(HttpStatus.temporaryRedirect);
+ print(HttpStatus.badRequest);
+ print(HttpStatus.unauthorized);
+ print(HttpStatus.paymentRequired);
+ print(HttpStatus.forbidden);
+ print(HttpStatus.notFound);
+ print(HttpStatus.methodNotAllowed);
+ print(HttpStatus.notAcceptable);
+ print(HttpStatus.proxyAuthenticationRequired);
+ print(HttpStatus.requestTimeout);
+ print(HttpStatus.conflict);
+ print(HttpStatus.gone);
+ print(HttpStatus.lengthRequired);
+ print(HttpStatus.preconditionFailed);
+ print(HttpStatus.requestEntityTooLarge);
+ print(HttpStatus.requestUriTooLong);
+ print(HttpStatus.unsupportedMediaType);
+ print(HttpStatus.requestedRangeNotSatisfiable);
+ print(HttpStatus.expectationFailed);
+ print(HttpStatus.upgradeRequired);
+ print(HttpStatus.internalServerError);
+ print(HttpStatus.notImplemented);
+ print(HttpStatus.badGateway);
+ print(HttpStatus.serviceUnavailable);
+ print(HttpStatus.gatewayTimeout);
+ print(HttpStatus.httpVersionNotSupported);
+ print(HttpStatus.networkConnectTimeoutError);
+}
diff --git a/tests/lib/fix_data_tests/io.dart b/tests/lib/fix_data_tests/io.dart
new file mode 100644
index 0000000..c43ec21
--- /dev/null
+++ b/tests/lib/fix_data_tests/io.dart
@@ -0,0 +1,56 @@
+// 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.
+
+import 'dart:io';
+
+void main() {
+ print(FileMode.APPEND);
+ print(FileMode.READ);
+ print(FileMode.WRITE);
+ print(FileMode.WRITE_ONLY);
+ print(FileMode.WRITE_ONLY_APPEND);
+ print(HttpStatus.CONTINUE);
+ print(HttpStatus.SWITCHING_PROTOCOLS);
+ print(HttpStatus.OK);
+ print(HttpStatus.CREATED);
+ print(HttpStatus.ACCEPTED);
+ print(HttpStatus.NON_AUTHORITATIVE_INFORMATION);
+ print(HttpStatus.NO_CONTENT);
+ print(HttpStatus.RESET_CONTENT);
+ print(HttpStatus.PARTIAL_CONTENT);
+ print(HttpStatus.MULTIPLE_CHOICES);
+ print(HttpStatus.MOVED_PERMANENTLY);
+ print(HttpStatus.FOUND);
+ print(HttpStatus.MOVED_TEMPORARILY);
+ print(HttpStatus.SEE_OTHER);
+ print(HttpStatus.NOT_MODIFIED);
+ print(HttpStatus.USE_PROXY);
+ print(HttpStatus.TEMPORARY_REDIRECT);
+ print(HttpStatus.BAD_REQUEST);
+ print(HttpStatus.UNAUTHORIZED);
+ print(HttpStatus.PAYMENT_REQUIRED);
+ print(HttpStatus.FORBIDDEN);
+ print(HttpStatus.NOT_FOUND);
+ print(HttpStatus.METHOD_NOT_ALLOWED);
+ print(HttpStatus.NOT_ACCEPTABLE);
+ print(HttpStatus.PROXY_AUTHENTICATION_REQUIRED);
+ print(HttpStatus.REQUEST_TIMEOUT);
+ print(HttpStatus.CONFLICT);
+ print(HttpStatus.GONE);
+ print(HttpStatus.LENGTH_REQUIRED);
+ print(HttpStatus.PRECONDITION_FAILED);
+ print(HttpStatus.REQUEST_ENTITY_TOO_LARGE);
+ print(HttpStatus.REQUEST_URI_TOO_LONG);
+ print(HttpStatus.UNSUPPORTED_MEDIA_TYPE);
+ print(HttpStatus.REQUESTED_RANGE_NOT_SATISFIABLE);
+ print(HttpStatus.EXPECTATION_FAILED);
+ print(HttpStatus.UPGRADE_REQUIRED);
+ print(HttpStatus.INTERNAL_SERVER_ERROR);
+ print(HttpStatus.NOT_IMPLEMENTED);
+ print(HttpStatus.BAD_GATEWAY);
+ print(HttpStatus.SERVICE_UNAVAILABLE);
+ print(HttpStatus.GATEWAY_TIMEOUT);
+ print(HttpStatus.HTTP_VERSION_NOT_SUPPORTED);
+ print(HttpStatus.NETWORK_CONNECT_TIMEOUT_ERROR);
+}
diff --git a/tests/lib/fix_data_tests/io.dart.expect b/tests/lib/fix_data_tests/io.dart.expect
new file mode 100644
index 0000000..8c95823
--- /dev/null
+++ b/tests/lib/fix_data_tests/io.dart.expect
@@ -0,0 +1,56 @@
+// 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.
+
+import 'dart:io';
+
+void main() {
+ print(FileMode.append);
+ print(FileMode.read);
+ print(FileMode.write);
+ print(FileMode.writeOnly);
+ print(FileMode.writeOnlyAppend);
+ print(HttpStatus.continue_);
+ print(HttpStatus.switchingProtocols);
+ print(HttpStatus.ok);
+ print(HttpStatus.created);
+ print(HttpStatus.accepted);
+ print(HttpStatus.nonAuthoritativeInformation);
+ print(HttpStatus.noContent);
+ print(HttpStatus.resetContent);
+ print(HttpStatus.partialContent);
+ print(HttpStatus.multipleChoices);
+ print(HttpStatus.movedPermanently);
+ print(HttpStatus.found);
+ print(HttpStatus.movedTemporarily);
+ print(HttpStatus.seeOther);
+ print(HttpStatus.notModified);
+ print(HttpStatus.useProxy);
+ print(HttpStatus.temporaryRedirect);
+ print(HttpStatus.badRequest);
+ print(HttpStatus.unauthorized);
+ print(HttpStatus.paymentRequired);
+ print(HttpStatus.forbidden);
+ print(HttpStatus.notFound);
+ print(HttpStatus.methodNotAllowed);
+ print(HttpStatus.notAcceptable);
+ print(HttpStatus.proxyAuthenticationRequired);
+ print(HttpStatus.requestTimeout);
+ print(HttpStatus.conflict);
+ print(HttpStatus.gone);
+ print(HttpStatus.lengthRequired);
+ print(HttpStatus.preconditionFailed);
+ print(HttpStatus.requestEntityTooLarge);
+ print(HttpStatus.requestUriTooLong);
+ print(HttpStatus.unsupportedMediaType);
+ print(HttpStatus.requestedRangeNotSatisfiable);
+ print(HttpStatus.expectationFailed);
+ print(HttpStatus.upgradeRequired);
+ print(HttpStatus.internalServerError);
+ print(HttpStatus.notImplemented);
+ print(HttpStatus.badGateway);
+ print(HttpStatus.serviceUnavailable);
+ print(HttpStatus.gatewayTimeout);
+ print(HttpStatus.httpVersionNotSupported);
+ print(HttpStatus.networkConnectTimeoutError);
+}
diff --git a/tests/lib_2/async/future_extension_test.dart b/tests/lib_2/async/future_extension_test.dart
new file mode 100644
index 0000000..cd28986
--- /dev/null
+++ b/tests/lib_2/async/future_extension_test.dart
@@ -0,0 +1,54 @@
+// 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.
+
+import 'package:async_helper/async_helper.dart';
+import "package:expect/expect.dart";
+import 'dart:async' show Completer, runZonedGuarded;
+import '../../language/static_type_helper.dart';
+
+void main() {
+ testIgnore();
+}
+
+void testIgnore() {
+ var future = Future<int>.value(42);
+ captureStaticType(future.ignore(), <T>(T value) {
+ Expect.equals(typeOf<void>(), T);
+ });
+
+ asyncStart();
+ // Ignored futures can still be listend to.
+ {
+ var c = Completer<int>.sync();
+ var f = c.future;
+ f.ignore();
+ asyncStart();
+ f.catchError((e) {
+ Expect.equals("ERROR1", e);
+ asyncEnd();
+ return 0;
+ });
+ c.completeError("ERROR1");
+ }
+
+ // Ignored futures are not uncaught errors.
+ {
+ asyncStart();
+ bool threw = false;
+ runZonedGuarded(() {
+ var c = Completer<int>.sync();
+ var f = c.future;
+ f.ignore();
+ c.completeError("ERROR2");
+ }, (e, s) {
+ threw = true;
+ Expect.fail("Should not happen: $e");
+ });
+ Future.delayed(Duration.zero, () {
+ if (threw) Expect.fail("Future not ignored.");
+ asyncEnd();
+ });
+ }
+ asyncEnd();
+}
diff --git a/tests/lib_2/async/unawaited_error_test.dart b/tests/lib_2/async/unawaited_error_test.dart
new file mode 100644
index 0000000..597a9f8
--- /dev/null
+++ b/tests/lib_2/async/unawaited_error_test.dart
@@ -0,0 +1,11 @@
+// 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.
+
+void main() {
+ // The `unawaited` function is not exposed by dart:core.
+ unawaited;
+ // [error line 7, column 3, length 9]
+ // [cfe] Getter not found: 'unawaited'.
+ // [analyzer] COMPILE_TIME_ERROR.UNDEFINED_IDENTIFIER
+}
diff --git a/tests/lib_2/async/unawaited_test.dart b/tests/lib_2/async/unawaited_test.dart
new file mode 100644
index 0000000..16bd285
--- /dev/null
+++ b/tests/lib_2/async/unawaited_test.dart
@@ -0,0 +1,54 @@
+// 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.
+
+// @dart = 2.9
+
+import 'package:async_helper/async_helper.dart';
+import "package:expect/expect.dart";
+import 'dart:async' show Completer, runZonedGuarded, unawaited;
+import 'dart:async' as prefix;
+import '../../language/static_type_helper.dart';
+
+void main() {
+ testUnawaited();
+}
+
+void testUnawaited() {
+ // Exists where expected.
+ prefix.unawaited.expectStaticType<Exactly<void Function(Future<Object>)>>();
+
+ var future = Future<int>.value(42);
+ captureStaticType(unawaited(future), <T>(value) {
+ Expect.equals(typeOf<void>(), T);
+ });
+
+ asyncStart();
+ // Unawaited futures still throw.
+ {
+ var c = Completer<int>();
+ var f = c.future;
+ unawaited(f);
+ asyncStart();
+ f.catchError((e) {
+ Expect.equals("ERROR1", e);
+ asyncEnd();
+ return 0;
+ });
+ c.completeError("ERROR1");
+ }
+ // Unawaited futures are still uncaught errors.
+ {
+ asyncStart();
+ runZonedGuarded(() {
+ var c = Completer<int>();
+ var f = c.future;
+ unawaited(f);
+ c.completeError("ERROR2");
+ }, (e, s) {
+ Expect.equals("ERROR2", e);
+ asyncEnd();
+ });
+ }
+ asyncEnd();
+}
diff --git a/tools/VERSION b/tools/VERSION
index d15e618..991514c 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
MAJOR 2
MINOR 14
PATCH 0
-PRERELEASE 236
+PRERELEASE 237
PRERELEASE_PATCH 0
\ No newline at end of file