Version 2.14.0-121.0.dev
Merge commit '8fd81f72281d9d3aa5ef3890c947cc7305c56a50' into 'dev'
diff --git a/pkg/analysis_server/lib/src/lsp/mapping.dart b/pkg/analysis_server/lib/src/lsp/mapping.dart
index fa51e73..84a8767 100644
--- a/pkg/analysis_server/lib/src/lsp/mapping.dart
+++ b/pkg/analysis_server/lib/src/lsp/mapping.dart
@@ -65,11 +65,6 @@
text ??= '';
offsetLengthPairs ??= const [];
- String escape(String input) => input.replaceAllMapped(
- RegExp(r'[$}\\]'), // Replace any of $ } \
- (c) => '\\${c[0]}', // Prefix with a backslash
- );
-
// Snippets syntax is documented in the LSP spec:
// https://microsoft.github.io/language-server-protocol/specifications/specification-current/#snippet_syntax
//
@@ -90,18 +85,18 @@
final pairLength = offsetLengthPairs[i + 1];
// Add any text that came before this tabstop to the result.
- output.add(escape(text.substring(offset, pairOffset)));
+ output.add(escapeSnippetString(text.substring(offset, pairOffset)));
// Add this tabstop
- final tabStopText =
- escape(text.substring(pairOffset, pairOffset + pairLength));
+ final tabStopText = escapeSnippetString(
+ text.substring(pairOffset, pairOffset + pairLength));
output.add('\${${tabStopNumber++}:$tabStopText}');
offset = pairOffset + pairLength;
}
// Add any remaining text that was after the last tabstop.
- output.add(escape(text.substring(offset)));
+ output.add(escapeSnippetString(text.substring(offset)));
return output.join('');
}
@@ -528,6 +523,15 @@
.firstWhere(isSupported, orElse: () => lsp.SymbolKind.Obj);
}
+/// Escapes a string to be used in an LSP edit that uses Snippet mode.
+///
+/// Snippets can contain special markup like `${a:b}` so some characters need
+/// escaping (according to the LSP spec, those are `$`, `}` and `\`).
+String escapeSnippetString(String input) => input.replaceAllMapped(
+ RegExp(r'[$}\\]'), // Replace any of $ } \
+ (c) => '\\${c[0]}', // Prefix with a backslash
+ );
+
String? getCompletionDetail(
server.CompletionSuggestion suggestion,
lsp.CompletionItemKind? completionKind,
@@ -1415,7 +1419,7 @@
defaultArgumentListTextRanges,
)
: '\${0:}'; // No required params still gets a tabstop in the parens.
- insertText += '($functionCallSuffix)';
+ insertText = '${escapeSnippetString(insertText)}($functionCallSuffix)';
} else if (selectionOffset != 0 &&
// We don't need a tabstop if the selection is the end of the string.
selectionOffset != completion.length) {
diff --git a/pkg/analysis_server/test/lsp/completion_dart_test.dart b/pkg/analysis_server/test/lsp/completion_dart_test.dart
index f460280..f682c33 100644
--- a/pkg/analysis_server/test/lsp/completion_dart_test.dart
+++ b/pkg/analysis_server/test/lsp/completion_dart_test.dart
@@ -181,6 +181,32 @@
insertText: r'Aaaaa(${0:a})',
);
+ Future<void> test_completeFunctionCalls_escapesDollarArgs() =>
+ checkCompleteFunctionCallInsertText(
+ r'''
+ int myFunction(String a$a, int b, {String c}) {
+ var a = [[myFu^]]
+ }
+ ''',
+ 'myFunction(…)',
+ insertTextFormat: InsertTextFormat.Snippet,
+ // The dollar should have been escaped.
+ insertText: r'myFunction(${1:a\$a}, ${2:b})',
+ );
+
+ Future<void> test_completeFunctionCalls_escapesDollarName() =>
+ checkCompleteFunctionCallInsertText(
+ r'''
+ int myFunc$tion(String a, int b, {String c}) {
+ var a = [[myFu^]]
+ }
+ ''',
+ r'myFunc$tion(…)',
+ insertTextFormat: InsertTextFormat.Snippet,
+ // The dollar should have been escaped.
+ insertText: r'myFunc\$tion(${1:a}, ${2:b})',
+ );
+
Future<void> test_completeFunctionCalls_existingArgList_constructor() =>
checkCompleteFunctionCallInsertText(
'''
diff --git a/pkg/analyzer/CHANGELOG.md b/pkg/analyzer/CHANGELOG.md
index 6e3eab5..bd7f1e8 100644
--- a/pkg/analyzer/CHANGELOG.md
+++ b/pkg/analyzer/CHANGELOG.md
@@ -1,6 +1,9 @@
## 1.6.0-dev
* Deprecated `AnalysisDriver` default constructor. Added `tmp1`. The goal
is to allow deprecating and removing unused parameters.
+* Added AST structures and visit methods to support the upcoming "constructor
+ tearoffs" feature: `ConstructorReference`, `FunctionReference`, and
+ `TypeLiteral`.
## 1.5.0
* Support for the language version `2.14`.
diff --git a/pkg/analyzer/lib/dart/ast/ast.dart b/pkg/analyzer/lib/dart/ast/ast.dart
index a28b0ba..aafc82f 100644
--- a/pkg/analyzer/lib/dart/ast/ast.dart
+++ b/pkg/analyzer/lib/dart/ast/ast.dart
@@ -508,6 +508,8 @@
R? visitConstructorName(ConstructorName node);
+ R? visitConstructorReference(ConstructorReference node);
+
R? visitContinueStatement(ContinueStatement node);
R? visitDeclaredIdentifier(DeclaredIdentifier node);
@@ -566,6 +568,8 @@
R? visitFunctionExpressionInvocation(FunctionExpressionInvocation node);
+ R? visitFunctionReference(FunctionReference node);
+
R? visitFunctionTypeAlias(FunctionTypeAlias functionTypeAlias);
R? visitFunctionTypedFormalParameter(FunctionTypedFormalParameter node);
@@ -683,6 +687,8 @@
R? visitTypeArgumentList(TypeArgumentList node);
+ R? visitTypeLiteral(TypeLiteral node);
+
R? visitTypeName(TypeName node);
R? visitTypeParameter(TypeParameter node);
diff --git a/pkg/analyzer/lib/dart/ast/visitor.dart b/pkg/analyzer/lib/dart/ast/visitor.dart
index 1bbc19e..39e66dc 100644
--- a/pkg/analyzer/lib/dart/ast/visitor.dart
+++ b/pkg/analyzer/lib/dart/ast/visitor.dart
@@ -226,6 +226,10 @@
R? visitConstructorName(ConstructorName node) => visitNode(node);
@override
+ R? visitConstructorReference(ConstructorReference node) =>
+ visitExpression(node);
+
+ @override
R? visitContinueStatement(ContinueStatement node) => visitStatement(node);
R? visitDeclaration(Declaration node) => visitAnnotatedNode(node);
@@ -341,6 +345,9 @@
visitInvocationExpression(node);
@override
+ R? visitFunctionReference(FunctionReference node) => visitExpression(node);
+
+ @override
R? visitFunctionTypeAlias(FunctionTypeAlias node) => visitTypeAlias(node);
@override
@@ -570,6 +577,9 @@
R? visitTypedLiteral(TypedLiteral node) => visitLiteral(node);
@override
+ R? visitTypeLiteral(TypeLiteral node) => visitExpression(node);
+
+ @override
R? visitTypeName(TypeName node) => visitNode(node);
@override
@@ -767,6 +777,12 @@
}
@override
+ R? visitConstructorReference(ConstructorReference node) {
+ node.visitChildren(this);
+ return null;
+ }
+
+ @override
R? visitContinueStatement(ContinueStatement node) {
node.visitChildren(this);
return null;
@@ -941,6 +957,12 @@
}
@override
+ R? visitFunctionReference(FunctionReference node) {
+ node.visitChildren(this);
+ return null;
+ }
+
+ @override
R? visitFunctionTypeAlias(FunctionTypeAlias node) {
node.visitChildren(this);
return null;
@@ -1290,6 +1312,12 @@
}
@override
+ R? visitTypeLiteral(TypeLiteral node) {
+ node.visitChildren(this);
+ return null;
+ }
+
+ @override
R? visitTypeName(TypeName node) {
node.visitChildren(this);
return null;
@@ -1430,6 +1458,9 @@
R? visitConstructorName(ConstructorName node) => null;
@override
+ R? visitConstructorReference(ConstructorReference node) => null;
+
+ @override
R? visitContinueStatement(ContinueStatement node) => null;
@override
@@ -1519,6 +1550,9 @@
null;
@override
+ R? visitFunctionReference(FunctionReference node) => null;
+
+ @override
R? visitFunctionTypeAlias(FunctionTypeAlias node) => null;
@override
@@ -1696,6 +1730,9 @@
R? visitTypeArgumentList(TypeArgumentList node) => null;
@override
+ R? visitTypeLiteral(TypeLiteral node) => null;
+
+ @override
R? visitTypeName(TypeName node) => null;
@override
@@ -1812,6 +1849,9 @@
R? visitConstructorName(ConstructorName node) => _throw(node);
@override
+ R? visitConstructorReference(ConstructorReference node) => _throw(node);
+
+ @override
R? visitContinueStatement(ContinueStatement node) => _throw(node);
@override
@@ -1904,6 +1944,9 @@
_throw(node);
@override
+ R? visitFunctionReference(FunctionReference node) => _throw(node);
+
+ @override
R? visitFunctionTypeAlias(FunctionTypeAlias node) => _throw(node);
@override
@@ -2084,6 +2127,9 @@
R? visitTypeArgumentList(TypeArgumentList node) => _throw(node);
@override
+ R? visitTypeLiteral(TypeLiteral node) => _throw(node);
+
+ @override
R? visitTypeName(TypeName node) => _throw(node);
@override
@@ -2332,6 +2378,14 @@
}
@override
+ T? visitConstructorReference(ConstructorReference node) {
+ stopwatch.start();
+ T? result = _baseVisitor.visitConstructorReference(node);
+ stopwatch.stop();
+ return result;
+ }
+
+ @override
T? visitContinueStatement(ContinueStatement node) {
stopwatch.start();
T? result = _baseVisitor.visitContinueStatement(node);
@@ -2564,6 +2618,14 @@
}
@override
+ T? visitFunctionReference(FunctionReference node) {
+ stopwatch.start();
+ T? result = _baseVisitor.visitFunctionReference(node);
+ stopwatch.stop();
+ return result;
+ }
+
+ @override
T? visitFunctionTypeAlias(FunctionTypeAlias node) {
stopwatch.start();
T? result = _baseVisitor.visitFunctionTypeAlias(node);
@@ -3029,6 +3091,14 @@
}
@override
+ T? visitTypeLiteral(TypeLiteral node) {
+ stopwatch.start();
+ T? result = _baseVisitor.visitTypeLiteral(node);
+ stopwatch.stop();
+ return result;
+ }
+
+ @override
T? visitTypeName(TypeName node) {
stopwatch.start();
T? result = _baseVisitor.visitTypeName(node);
@@ -3194,6 +3264,9 @@
R? visitConstructorName(ConstructorName node) => visitNode(node);
@override
+ R? visitConstructorReference(ConstructorReference node) => visitNode(node);
+
+ @override
R? visitContinueStatement(ContinueStatement node) => visitNode(node);
@override
@@ -3290,6 +3363,9 @@
visitNode(node);
@override
+ R? visitFunctionReference(FunctionReference node) => visitNode(node);
+
+ @override
R? visitFunctionTypeAlias(FunctionTypeAlias node) => visitNode(node);
@override
@@ -3477,6 +3553,9 @@
R? visitTypeArgumentList(TypeArgumentList node) => visitNode(node);
@override
+ R? visitTypeLiteral(TypeLiteral node) => visitNode(node);
+
+ @override
R? visitTypeName(TypeName node) => visitNode(node);
@override
diff --git a/pkg/analyzer/lib/src/dart/ast/ast.dart b/pkg/analyzer/lib/src/dart/ast/ast.dart
index 060ada1..e02e41b 100644
--- a/pkg/analyzer/lib/src/dart/ast/ast.dart
+++ b/pkg/analyzer/lib/src/dart/ast/ast.dart
@@ -2715,10 +2715,8 @@
Precedence get precedence => Precedence.postfix;
@override
- E? accept<E>(AstVisitor<E> visitor) {
- throw UnimplementedError(
- 'Visitor support for ConstructorReference is not yet implemented');
- }
+ E? accept<E>(AstVisitor<E> visitor) =>
+ visitor.visitConstructorReference(this);
@override
void visitChildren(AstVisitor visitor) {
@@ -5064,10 +5062,7 @@
}
@override
- E? accept<E>(AstVisitor<E> visitor) {
- throw UnimplementedError(
- 'Visitor support for FunctionReference is not yet implemented');
- }
+ E? accept<E>(AstVisitor<E> visitor) => visitor.visitFunctionReference(this);
@override
void visitChildren(AstVisitor visitor) {
@@ -10106,10 +10101,7 @@
}
@override
- E? accept<E>(AstVisitor<E> visitor) {
- throw UnimplementedError(
- 'Visitor support for TypeLiteral is not yet implemented');
- }
+ E? accept<E>(AstVisitor<E> visitor) => visitor.visitTypeLiteral(this);
@override
void visitChildren(AstVisitor visitor) {
diff --git a/pkg/analyzer/lib/src/dart/ast/to_source_visitor.dart b/pkg/analyzer/lib/src/dart/ast/to_source_visitor.dart
index 0f5f371..a9d231b 100644
--- a/pkg/analyzer/lib/src/dart/ast/to_source_visitor.dart
+++ b/pkg/analyzer/lib/src/dart/ast/to_source_visitor.dart
@@ -351,6 +351,11 @@
}
@override
+ void visitConstructorReference(ConstructorReference node) {
+ safelyVisitNode(node.constructorName);
+ }
+
+ @override
void visitContinueStatement(ContinueStatement node) {
sink.write("continue");
safelyVisitNodeWithPrefix(" ", node.label);
@@ -614,6 +619,12 @@
}
@override
+ void visitFunctionReference(FunctionReference node) {
+ safelyVisitNode(node.function);
+ safelyVisitNode(node.typeArguments);
+ }
+
+ @override
void visitFunctionTypeAlias(FunctionTypeAlias node) {
safelyVisitNodeListWithSeparatorAndSuffix(node.metadata, " ", " ");
sink.write("typedef ");
@@ -1084,6 +1095,11 @@
}
@override
+ void visitTypeLiteral(TypeLiteral node) {
+ safelyVisitNode(node.typeName);
+ }
+
+ @override
void visitTypeName(TypeName node) {
safelyVisitNode(node.name);
safelyVisitNode(node.typeArguments);
diff --git a/pkg/analyzer/lib/src/dart/ast/utilities.dart b/pkg/analyzer/lib/src/dart/ast/utilities.dart
index 8989422..a2a8475 100644
--- a/pkg/analyzer/lib/src/dart/ast/utilities.dart
+++ b/pkg/analyzer/lib/src/dart/ast/utilities.dart
@@ -339,6 +339,10 @@
cloneNullableToken(node.period), cloneNullableNode(node.name));
@override
+ AstNode visitConstructorReference(ConstructorReference node) => astFactory
+ .constructorReference(constructorName: cloneNode(node.constructorName));
+
+ @override
ContinueStatement visitContinueStatement(ContinueStatement node) =>
astFactory.continueStatement(cloneToken(node.continueKeyword),
cloneNullableNode(node.label), cloneToken(node.semicolon));
@@ -581,6 +585,12 @@
cloneNullableNode(node.typeArguments), cloneNode(node.argumentList));
@override
+ AstNode visitFunctionReference(FunctionReference node) =>
+ astFactory.functionReference(
+ function: cloneNode(node.function),
+ typeArguments: cloneNullableNode(node.typeArguments));
+
+ @override
FunctionTypeAlias visitFunctionTypeAlias(FunctionTypeAlias node) =>
astFactory.functionTypeAlias(
cloneNullableNode(node.documentationComment),
@@ -1012,6 +1022,10 @@
cloneNodeList(node.arguments), cloneToken(node.rightBracket));
@override
+ TypeLiteral visitTypeLiteral(TypeLiteral node) =>
+ astFactory.typeLiteral(typeName: cloneNode(node.typeName));
+
+ @override
TypeNameImpl visitTypeName(TypeName node) => astFactory.typeName(
cloneNode(node.name), cloneNullableNode(node.typeArguments),
question: cloneNullableToken(node.question));
@@ -1450,6 +1464,12 @@
}
@override
+ bool visitConstructorReference(ConstructorReference node) {
+ ConstructorReference other = _other as ConstructorReference;
+ return isEqualNodes(node.constructorName, other.constructorName);
+ }
+
+ @override
bool visitContinueStatement(ContinueStatement node) {
ContinueStatement other = _other as ContinueStatement;
return isEqualTokens(node.continueKeyword, other.continueKeyword) &&
@@ -1722,6 +1742,13 @@
}
@override
+ bool visitFunctionReference(FunctionReference node) {
+ FunctionReference other = _other as FunctionReference;
+ return isEqualNodes(node.function, other.function) &&
+ isEqualNodes(node.typeArguments, other.typeArguments);
+ }
+
+ @override
bool visitFunctionTypeAlias(FunctionTypeAlias node) {
FunctionTypeAlias other = _other as FunctionTypeAlias;
return isEqualNodes(
@@ -2239,6 +2266,12 @@
}
@override
+ bool visitTypeLiteral(TypeLiteral node) {
+ TypeLiteral other = _other as TypeLiteral;
+ return isEqualNodes(node.typeName, other.typeName);
+ }
+
+ @override
bool visitTypeName(TypeName node) {
TypeName other = _other as TypeName;
return isEqualNodes(node.name, other.name) &&
@@ -2961,6 +2994,15 @@
}
@override
+ bool visitConstructorReference(covariant ConstructorReferenceImpl node) {
+ if (identical(node.constructorName, _oldNode)) {
+ node.constructorName = _newNode as ConstructorNameImpl;
+ return true;
+ }
+ return visitNode(node);
+ }
+
+ @override
bool visitContinueStatement(covariant ContinueStatementImpl node) {
if (identical(node.label, _oldNode)) {
node.label = _newNode as SimpleIdentifier;
@@ -3273,6 +3315,18 @@
}
@override
+ bool visitFunctionReference(covariant FunctionReferenceImpl node) {
+ if (identical(node.function, _oldNode)) {
+ node.function = _newNode as ExpressionImpl;
+ return true;
+ } else if (identical(node.typeArguments, _oldNode)) {
+ node.typeArguments = _newNode as TypeArgumentListImpl;
+ return true;
+ }
+ return visitNode(node);
+ }
+
+ @override
bool visitFunctionTypeAlias(covariant FunctionTypeAliasImpl node) {
if (identical(node.returnType, _oldNode)) {
node.returnType = _newNode as TypeAnnotation;
@@ -3871,6 +3925,15 @@
}
@override
+ bool visitTypeLiteral(covariant TypeLiteralImpl node) {
+ if (identical(node.typeName, _oldNode)) {
+ node.typeName = _newNode as TypeNameImpl;
+ return true;
+ }
+ return visitNode(node);
+ }
+
+ @override
bool visitTypeName(covariant TypeNameImpl node) {
if (identical(node.name, _oldNode)) {
node.name = _newNode as Identifier;
diff --git a/pkg/dartdev/lib/src/templates/console_full.dart b/pkg/dartdev/lib/src/templates/console_full.dart
index c610331..f17c05c 100644
--- a/pkg/dartdev/lib/src/templates/console_full.dart
+++ b/pkg/dartdev/lib/src/templates/console_full.dart
@@ -41,7 +41,7 @@
# path: ^1.8.0
dev_dependencies:
- lints: ^1.0.0-0
+ lints: ^1.0.0
test: ^1.16.0
''';
diff --git a/pkg/dartdev/lib/src/templates/console_simple.dart b/pkg/dartdev/lib/src/templates/console_simple.dart
index 991e3f6..22f9d9a 100644
--- a/pkg/dartdev/lib/src/templates/console_simple.dart
+++ b/pkg/dartdev/lib/src/templates/console_simple.dart
@@ -39,7 +39,7 @@
# path: ^1.8.0
dev_dependencies:
- lints: ^1.0.0-0
+ lints: ^1.0.0
''';
final String _readme = '''
diff --git a/pkg/dartdev/lib/src/templates/package_simple.dart b/pkg/dartdev/lib/src/templates/package_simple.dart
index ca4b820..586c799 100644
--- a/pkg/dartdev/lib/src/templates/package_simple.dart
+++ b/pkg/dartdev/lib/src/templates/package_simple.dart
@@ -55,7 +55,7 @@
# path: ^1.8.0
dev_dependencies:
- lints: ^1.0.0-0
+ lints: ^1.0.0
test: ^1.16.0
''';
diff --git a/pkg/dartdev/lib/src/templates/server_shelf.dart b/pkg/dartdev/lib/src/templates/server_shelf.dart
index 391a241..15f5898 100644
--- a/pkg/dartdev/lib/src/templates/server_shelf.dart
+++ b/pkg/dartdev/lib/src/templates/server_shelf.dart
@@ -36,7 +36,7 @@
# homepage: https://www.example.com
environment:
- sdk: ">=2.12.0 <3.0.0"
+ sdk: '>=2.12.0 <3.0.0'
dependencies:
args: ^2.0.0
@@ -45,7 +45,7 @@
dev_dependencies:
http: ^0.13.0
- lints: ^1.0.0-0
+ lints: ^1.0.0
test_process: ^2.0.0
test: ^1.15.0
''';
diff --git a/pkg/dartdev/lib/src/templates/web_simple.dart b/pkg/dartdev/lib/src/templates/web_simple.dart
index a42dbb2..836ec57 100644
--- a/pkg/dartdev/lib/src/templates/web_simple.dart
+++ b/pkg/dartdev/lib/src/templates/web_simple.dart
@@ -39,7 +39,7 @@
dev_dependencies:
build_runner: ^1.10.0
build_web_compilers: ^2.11.0
- lints: ^1.0.0-0
+ lints: ^1.0.0
''';
final String _readme = '''
diff --git a/pkg/frontend_server/lib/frontend_server.dart b/pkg/frontend_server/lib/frontend_server.dart
index 2c45796c..635cdd2 100644
--- a/pkg/frontend_server/lib/frontend_server.dart
+++ b/pkg/frontend_server/lib/frontend_server.dart
@@ -21,8 +21,6 @@
// that would replace api used below. This api was made private in
// an effort to discourage further use.
// ignore_for_file: implementation_imports
-import 'package:front_end/src/api_prototype/compiler_options.dart'
- show CompilerOptions, parseExperimentalFlags;
import 'package:front_end/src/api_unstable/vm.dart';
import 'package:front_end/widget_cache.dart';
import 'package:kernel/ast.dart' show Library, Procedure, LibraryDependency;
diff --git a/pkg/frontend_server/lib/src/to_string_transformer.dart b/pkg/frontend_server/lib/src/to_string_transformer.dart
index 9942dd4..c8c2ae2 100644
--- a/pkg/frontend_server/lib/src/to_string_transformer.dart
+++ b/pkg/frontend_server/lib/src/to_string_transformer.dart
@@ -3,7 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
import 'package:kernel/ast.dart';
-import 'package:kernel/visitor.dart';
import '../frontend_server.dart';
// Transformer/visitor for toString
diff --git a/runtime/vm/compiler/frontend/kernel_to_il.cc b/runtime/vm/compiler/frontend/kernel_to_il.cc
index 88c10b3..583a636 100644
--- a/runtime/vm/compiler/frontend/kernel_to_il.cc
+++ b/runtime/vm/compiler/frontend/kernel_to_il.cc
@@ -884,6 +884,7 @@
case MethodRecognizer::kFfiAbi:
case MethodRecognizer::kReachabilityFence:
case MethodRecognizer::kUtf8DecoderScan:
+ case MethodRecognizer::kHas63BitSmis:
return true;
default:
return false;
@@ -1480,6 +1481,13 @@
body += ConvertUntaggedToUnboxed(kUnboxedFfiIntPtr);
body += Box(kUnboxedFfiIntPtr);
} break;
+ case MethodRecognizer::kHas63BitSmis: {
+#if defined(TARGET_ARCH_IS_64_BIT) && !defined(DART_COMPRESSED_POINTERS)
+ body += Constant(Bool::True());
+#else
+ body += Constant(Bool::False());
+#endif // defined(ARCH_IS_64_BIT)
+ } break;
default: {
UNREACHABLE();
break;
diff --git a/runtime/vm/compiler/recognized_methods_list.h b/runtime/vm/compiler/recognized_methods_list.h
index 7e54439..228018f 100644
--- a/runtime/vm/compiler/recognized_methods_list.h
+++ b/runtime/vm/compiler/recognized_methods_list.h
@@ -203,6 +203,7 @@
V(Future, wait, FutureWait, 0xc71e731d) \
V(_RootZone, runUnary, RootZoneRunUnary, 0x966a802c) \
V(_FutureListener, handleValue, FutureListenerHandleValue, 0x165b47c4) \
+ V(::, has63BitSmis, Has63BitSmis, 0xf61b5ab2) \
// List of intrinsics:
// (class-name, function-name, intrinsification method, fingerprint).
diff --git a/runtime/vm/dart_api_impl.cc b/runtime/vm/dart_api_impl.cc
index 2e26b48..f26d236 100644
--- a/runtime/vm/dart_api_impl.cc
+++ b/runtime/vm/dart_api_impl.cc
@@ -1000,6 +1000,10 @@
// (no implementations or indirect subclasses are allowed).
const auto& klass = Class::Handle(Z, obj.clazz());
const auto& super_klass = Class::Handle(Z, klass.SuperClass());
+ if (super_klass.IsNull()) {
+ // This means klass is Object.
+ return false;
+ }
if (super_klass.Name() != Symbols::Struct().ptr() &&
super_klass.Name() != Symbols::Union().ptr()) {
return false;
diff --git a/sdk/lib/_internal/vm/lib/internal_patch.dart b/sdk/lib/_internal/vm/lib/internal_patch.dart
index 7ee1e6b..6cc2388 100644
--- a/sdk/lib/_internal/vm/lib/internal_patch.dart
+++ b/sdk/lib/_internal/vm/lib/internal_patch.dart
@@ -102,9 +102,9 @@
}
}
-final bool has63BitSmis = _has63BitSmis();
-
-bool _has63BitSmis() native "Internal_has63BitSmis";
+@pragma("vm:recognized", "other")
+@pragma('vm:prefer-inline')
+bool get has63BitSmis native "Internal_has63BitSmis";
@pragma("vm:recognized", "other")
@pragma("vm:entry-point", "call")
diff --git a/sdk/lib/core/double.dart b/sdk/lib/core/double.dart
index 337d5f9..29d36db 100644
--- a/sdk/lib/core/double.dart
+++ b/sdk/lib/core/double.dart
@@ -41,7 +41,7 @@
double abs();
- /// Returns the sign of the double's numerical value.
+ /// The sign of the double's numerical value.
///
/// Returns -1.0 if the value is less than zero,
/// +1.0 if the value is greater than zero,
diff --git a/sdk/lib/core/int.dart b/sdk/lib/core/int.dart
index a799e46..3e5bb4b 100644
--- a/sdk/lib/core/int.dart
+++ b/sdk/lib/core/int.dart
@@ -227,7 +227,10 @@
/// Returns the absolute value of this integer.
///
- /// For any integer `x`, the result is the same as `x < 0 ? -x : x`.
+ /// For any integer `value`,
+ /// the result is the same as `value < 0 ? -value : value`.
+ ///
+ /// Integer overflow may cause the result of `-value` to stay negative.
int abs();
/// Returns the sign of this integer.
diff --git a/sdk/lib/core/num.dart b/sdk/lib/core/num.dart
index b560025..21d1348 100644
--- a/sdk/lib/core/num.dart
+++ b/sdk/lib/core/num.dart
@@ -178,60 +178,89 @@
/// otherwise the result is a [double].
num remainder(num other);
- /// Whether [other] is numerically smaller than this number.
+ /// Whether this number is numerically smaller than [other].
///
- /// If either operand is the [double] NaN, the result is always false.
+ /// Returns `true` if this number is smaller than [other],
+ /// and returns `false` if this number is greater than or equal to [other]
+ /// or if either value is a NaN value like [double.nan].
bool operator <(num other);
- /// Whether [other] is numerically smaller than or equal to this number.
+ /// Whether this number is numerically smaller than or equal to [other].
///
- /// If either operand is the [double] NaN, the result is always false.
+ /// Returns `true` if this number is smaller than or equal to [other],
+ /// and returns `false` if this number is greater than [other]
+ /// or if either value is a NaN value like [double.nan].
bool operator <=(num other);
- /// Whether [other] is numerically greater than this number.
+ /// Whether this number is numerically greater than [other].
///
- /// If either operand is the [double] NaN, the result is always false.
+ /// Returns `true` if this number is greater than [other],
+ /// and returns `false` if this number is smaller than or equal to [other]
+ /// or if either value is a NaN value like [double.nan].
bool operator >(num other);
- /// Whether [other] is numerically greater than or equal to this number.
+ /// Whether this number is numerically greater than or equal to [other].
///
- /// If either operand is the [double] NaN, the result is always false.
+ /// Returns `true` if this number is greater than or equal to [other],
+ /// and returns `false` if this number is smaller than [other]
+ /// or if either value is a NaN value like [double.nan].
bool operator >=(num other);
- /// Whether the number is the double Not-a-Number value.
+ /// Whether the number is a Not-a-Number value.
+ ///
+ /// Is `true` if this number is the [double.nan] value
+ /// or any other of the possible [double] NaN values.
+ /// Is `false` if this number is an integer,
+ /// a finite double or an infinite double ([double.infinity]
+ /// or [double.negativeInfinity]).
+ ///
+ /// All numbers satisfy exacly one of of [isInfinite], [isFinite]
+ /// and `isNaN`.
bool get isNaN;
- /// Whether if the number is negative.
+ /// Whether this number is negative.
///
- /// Negative numbers are those less than zero, and the double `-0.0`.
+ /// A number is negative if it's smaller than zero,
+ /// or if it is the double `-0.0`.
+ /// This precludes a NaN value like [double.nan] from being negative.
bool get isNegative;
/// Whether the number is positive infinity or negative infinity.
+ ///
+ /// Only satisfied by [double.infinity] and [double.negativeInfinity].
+ ///
+ /// All numbers satisfy exacly one of of `isInfinite`, [isFinite]
+ /// and [isNaN].
bool get isInfinite;
/// Whether the number is finite.
///
- /// The only non-finite numbers are NaN, positive infinity, and
+ /// The only non-finite numbers are NaN values, positive infinity, and
/// negative infinity. All integers are finite.
+ ///
+ /// All numbers satisfy exacly one of of [isInfinite], `isFinite`
+ /// and [isNaN].
bool get isFinite;
/// The absolute value of this number.
///
/// The absolute value is the value itself, if the value is non-negative,
/// and `-value` if the value is negative.
+ ///
+ /// Integer overflow may cause the result of `-value` to stay negative.
num abs();
/// Negative one, zero or positive one depending on the sign and
- /// numerical value of the number.
+ /// numerical value of this number.
///
- /// Returns minus one if the number is less than zero,
- /// plus one if the number is greater than zero,
- /// and zero if the number is equal to zero.
+ /// The value minus one if this number is less than zero,
+ /// plus one if this number is greater than zero,
+ /// and zero if this number is equal to zero.
///
- /// Returns NaN if the number is the [double] NaN value.
+ /// Returns NaN if the number is a [double] NaN value.
///
/// Returns a number of the same type as this number.
- /// For doubles, `-0.0.sign == -0.0`.
+ /// For doubles, `(-0.0).sign` is `-0.0`.
///
/// The result satisfies:
/// ```dart
diff --git a/tests/language/generic_methods/generic_invocation_all_subexpression_types_test.dart b/tests/language/generic_methods/generic_invocation_all_subexpression_types_test.dart
new file mode 100644
index 0000000..fc34619
--- /dev/null
+++ b/tests/language/generic_methods/generic_invocation_all_subexpression_types_test.dart
@@ -0,0 +1,58 @@
+// 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.
+
+// This test verifies that `EXPR<typeArguments>(arguments)` is properly parsed
+// as a generic invocation, for all types of expressions that may appear as
+// EXPR. We try to pay extra attention to ambiguous expressions (that is, where
+// interpreting the `<` and `>` as operators would also have led to a valid
+// parse).
+
+import '../syntax_helper.dart';
+
+class C extends SyntaxTracker {
+ C([Object? x = absent, Object? y = absent])
+ : super('new C${SyntaxTracker.args(x, y)}');
+
+ C.syntax(String s) : super(s);
+}
+
+class ThisTest extends C {
+ ThisTest() : super.syntax('this');
+
+ void test() {
+ checkSyntax(f(this<C, C>(0)), 'f(this<C, C>(0))');
+ // Note: SyntaxTracker can't see the parens around `this` in the line below
+ checkSyntax(f((this)<C, C>(0)), 'f(this<C, C>(0))');
+ }
+}
+
+main() {
+ SyntaxTracker.known[C] = 'C';
+ SyntaxTracker.known[#x] = '#x';
+ checkSyntax(f(f<C, C>(0)), 'f(f<C, C>(0))');
+ checkSyntax(f(x.method<C, C>(0)), 'f(x.method<C, C>(0))');
+ checkSyntax(f(C()<C, C>(0)), 'f(new C()<C, C>(0))');
+ checkSyntax(f(new C()<C, C>(0)), 'f(new C()<C, C>(0))');
+ checkSyntax(f(f()<C, C>(0)), 'f(f()<C, C>(0))');
+ checkSyntax(f(x.method()<C, C>(0)), 'f(x.method()<C, C>(0))');
+ checkSyntax(f(x[0]()<C, C>(0)), 'f(x[0]()<C, C>(0))');
+ checkSyntax(f(#x<C, C>(0)), 'f(#x<C, C>(0))');
+ checkSyntax(f(null<C, C>(0)), 'f(null<C, C>(0))');
+ checkSyntax(f(0<C, C>(0)), 'f(0<C, C>(0))');
+ checkSyntax(f(0.5<C, C>(0)), 'f(0.5<C, C>(0))');
+ checkSyntax(f([]<C, C>(0)), 'f([]<C, C>(0))');
+ checkSyntax(f([0]<C, C>(0)), 'f([0]<C, C>(0))');
+ checkSyntax(f({}<C, C>(0)), 'f({}<C, C>(0))');
+ checkSyntax(f({0}<C, C>(0)), 'f({0}<C, C>(0))');
+ checkSyntax(f({0: 0}<C, C>(0)), 'f({ 0: 0 }<C, C>(0))');
+ checkSyntax(f(true<C, C>(0)), 'f(true<C, C>(0))');
+ checkSyntax(f("s"<C, C>(0)), 'f("s"<C, C>(0))');
+ checkSyntax(f(r"s"<C, C>(0)), 'f("s"<C, C>(0))');
+ checkSyntax(f(x[0]<C, C>(0)), 'f(x[0]<C, C>(0))');
+ // Note: SyntaxTracker can't see the `!` in the line below
+ checkSyntax(f(x!<C, C>(0)), 'f(x<C, C>(0))');
+ // Note: SyntaxTracker can't see the parens around `x` in the line below
+ checkSyntax(f((x)<C, C>(0)), 'f(x<C, C>(0))');
+ ThisTest().test();
+}
diff --git a/tests/language/generic_methods/non_generic_invocation_all_first_expression_types_test.dart b/tests/language/generic_methods/non_generic_invocation_all_first_expression_types_test.dart
new file mode 100644
index 0000000..35fde2d
--- /dev/null
+++ b/tests/language/generic_methods/non_generic_invocation_all_first_expression_types_test.dart
@@ -0,0 +1,88 @@
+// 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.
+
+// This test verifies that `EXPR<a,b>-x` is properly parsed as a pair of
+// expressions separated by a `,`, for all types of expressions that may appear
+// as EXPR. We try to pay extra attention to expressions that will become
+// ambiguous when the "constructor tearoffs" feature is enabled (that is, where
+// interpreting the `<` and `>` as delimiting a list of type arguments would
+// also have led to a valid parse).
+
+import '../syntax_helper.dart';
+
+class C extends SyntaxTracker {
+ C([Object? x = absent, Object? y = absent])
+ : super('new C${SyntaxTracker.args(x, y)}');
+
+ C.syntax(String s) : super(s);
+
+ Object? operator <(Object? other) =>
+ SyntaxTracker('(${syntax(this)} < ${syntax(other)})');
+}
+
+class ThisTest extends C {
+ ThisTest() : super.syntax('this');
+
+ void test() {
+ checkSyntax(f(this < C, C > -x), 'f((this < C), (C > (-x)))');
+ // Note: SyntaxTracker can't see the parens around `this` in the line below
+ checkSyntax(f((this) < C, C > -x), 'f((this < C), (C > (-x)))');
+ }
+}
+
+class SuperTest extends C {
+ SuperTest() : super.syntax('super');
+
+ void test() {
+ checkSyntax(f(super < C, C > -x), 'f((super < C), (C > (-x)))');
+ }
+}
+
+main() {
+ const y = 123;
+ SyntaxTracker.known[C] = 'C';
+ SyntaxTracker.known[#x] = '#x';
+ SyntaxTracker.known[y] = 'y';
+ checkSyntax(f(x < C, C > -x), 'f((x < C), (C > (-x)))');
+ checkSyntax(f(x.getter < C, C > -x), 'f((x.getter < C), (C > (-x)))');
+ checkSyntax(f(C() < C, C > -x), 'f((new C() < C), (C > (-x)))');
+ checkSyntax(f(new C() < C, C > -x), 'f((new C() < C), (C > (-x)))');
+ checkSyntax(f(f() < C, C > -x), 'f((f() < C), (C > (-x)))');
+ checkSyntax(f(x.method() < C, C > -x), 'f((x.method() < C), (C > (-x)))');
+ checkSyntax(f(x[0]() < C, C > -x), 'f((x[0]() < C), (C > (-x)))');
+ checkSyntax(f(#x < C, C > -x), 'f((#x < C), (C > (-x)))');
+ checkSyntax(f(null < C, C > -x), 'f((null < C), (C > (-x)))');
+ checkSyntax(f(0 < y, C > -x), 'f(true, (C > (-x)))');
+ checkSyntax(f(0.5 < y, C > -x), 'f(true, (C > (-x)))');
+ checkSyntax(f([] < C, C > -x), 'f(([] < C), (C > (-x)))');
+ checkSyntax(f([0] < C, C > -x), 'f(([0] < C), (C > (-x)))');
+ checkSyntax(f({} < C, C > -x), 'f(({} < C), (C > (-x)))');
+ checkSyntax(f({0} < C, C > -x), 'f(({0} < C), (C > (-x)))');
+ checkSyntax(f({0: 0} < C, C > -x), 'f(({ 0: 0 } < C), (C > (-x)))');
+ checkSyntax(f(true < C, C > -x), 'f((true < C), (C > (-x)))');
+ checkSyntax(f("s" < C, C > -x), 'f(("s" < C), (C > (-x)))');
+ checkSyntax(f(r"s" < C, C > -x), 'f(("s" < C), (C > (-x)))');
+ checkSyntax(f(x[0] < C, C > -x), 'f((x[0] < C), (C > (-x)))');
+ // Note: SyntaxTracker can't see the `!` in the line below
+ checkSyntax(f(x! < C, C > -x), 'f((x < C), (C > (-x)))');
+ // Note: SyntaxTracker can't see the parens around `x` in the line below
+ checkSyntax(f((x) < C, C > -x), 'f((x < C), (C > (-x)))');
+ checkSyntax(f(-x < C, C > -x), 'f(((-x) < C), (C > (-x)))');
+ checkSyntax(f(!true < C, C > -x), 'f((false < C), (C > (-x)))');
+ checkSyntax(f(!(true) < C, C > -x), 'f((false < C), (C > (-x)))');
+ checkSyntax(f(~x < C, C > -x), 'f(((~x) < C), (C > (-x)))');
+ checkSyntax(f(x * x < C, C > -x), 'f(((x * x) < C), (C > (-x)))');
+ checkSyntax(f(x / x < C, C > -x), 'f(((x / x) < C), (C > (-x)))');
+ checkSyntax(f(x ~/ x < C, C > -x), 'f(((x ~/ x) < C), (C > (-x)))');
+ checkSyntax(f(x % x < C, C > -x), 'f(((x % x) < C), (C > (-x)))');
+ checkSyntax(f(x + x < C, C > -x), 'f(((x + x) < C), (C > (-x)))');
+ checkSyntax(f(x - x < C, C > -x), 'f(((x - x) < C), (C > (-x)))');
+ checkSyntax(f(x << x < C, C > -x), 'f(((x << x) < C), (C > (-x)))');
+ checkSyntax(f(x >> x < C, C > -x), 'f(((x >> x) < C), (C > (-x)))');
+ checkSyntax(f(x & x < C, C > -x), 'f(((x & x) < C), (C > (-x)))');
+ checkSyntax(f(x ^ x < C, C > -x), 'f(((x ^ x) < C), (C > (-x)))');
+ checkSyntax(f(x | x < C, C > -x), 'f(((x | x) < C), (C > (-x)))');
+ ThisTest().test();
+ SuperTest().test();
+}
diff --git a/tests/language/generic_methods/non_generic_invocation_all_second_expression_types_test.dart b/tests/language/generic_methods/non_generic_invocation_all_second_expression_types_test.dart
new file mode 100644
index 0000000..3e698e4
--- /dev/null
+++ b/tests/language/generic_methods/non_generic_invocation_all_second_expression_types_test.dart
@@ -0,0 +1,73 @@
+// 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.
+
+// This test verifies that `f<a,b>EXPR` is properly parsed as a pair of
+// expressions separated by a `,`, for all types of expressions that may appear
+// as EXPR. We try to pay extra attention to expressions that will become
+// ambiguous when the "constructor tearoffs" feature is enabled (that is, where
+// interpreting the `<` and `>` as delimiting a list of type arguments would
+// also have led to a valid parse).
+
+import '../syntax_helper.dart';
+
+class C extends SyntaxTracker {
+ C([Object? x = absent, Object? y = absent])
+ : super('new C${SyntaxTracker.args(x, y)}');
+
+ C.syntax(String s) : super(s);
+
+ Object? operator <(Object? other) =>
+ SyntaxTracker('(${syntax(this)} < ${syntax(other)})');
+}
+
+class ThisTest extends C {
+ ThisTest() : super.syntax('this');
+
+ void test() {
+ checkSyntax(f(x < C, C > this), 'f((x < C), (C > this))');
+ }
+}
+
+main() {
+ SyntaxTracker.known[C] = 'C';
+ SyntaxTracker.known[#x] = '#x';
+ checkSyntax(f(x < C, C > x), 'f((x < C), (C > x))');
+ checkSyntax(f(x < C, C > x.getter), 'f((x < C), (C > x.getter))');
+ checkSyntax(f(x < C, C > C()), 'f((x < C), (C > new C()))');
+ checkSyntax(f(x < C, C > new C()), 'f((x < C), (C > new C()))');
+ checkSyntax(f(x < C, C > f()), 'f((x < C), (C > f()))');
+ checkSyntax(f(x < C, C > x.method()), 'f((x < C), (C > x.method()))');
+ checkSyntax(f(x < C, C > x[0]()), 'f((x < C), (C > x[0]()))');
+ checkSyntax(f(x < C, C > #x), 'f((x < C), (C > #x))');
+ checkSyntax(f(x < C, C > null), 'f((x < C), (C > null))');
+ checkSyntax(f(x < C, C > 0), 'f((x < C), (C > 0))');
+ checkSyntax(f(x < C, C > 0.5), 'f((x < C), (C > 0.5))');
+ checkSyntax(f(x < C, C > []), 'f((x < C), (C > []))');
+ checkSyntax(f(x < C, C > [0]), 'f((x < C), (C > [0]))');
+ checkSyntax(f(x < C, C > {}), 'f((x < C), (C > {}))');
+ checkSyntax(f(x < C, C > {0}), 'f((x < C), (C > {0}))');
+ checkSyntax(f(x < C, C > {0: 0}), 'f((x < C), (C > { 0: 0 }))');
+ checkSyntax(f(x < C, C > true), 'f((x < C), (C > true))');
+ checkSyntax(f(x < C, C > "s"), 'f((x < C), (C > "s"))');
+ checkSyntax(f(x < C, C > r"s"), 'f((x < C), (C > "s"))');
+ checkSyntax(f(x < C, C > x[0]), 'f((x < C), (C > x[0]))');
+ // Note: SyntaxTracker can't see the `!` in the line below
+ checkSyntax(f(x < C, C > x!), 'f((x < C), (C > x))');
+ checkSyntax(f(x < C, C > -x), 'f((x < C), (C > (-x)))');
+ checkSyntax(f(x < C, C > !true), 'f((x < C), (C > false))');
+ checkSyntax(f(x < C, C > !(true)), 'f((x < C), (C > false))');
+ checkSyntax(f(x < C, C > ~x), 'f((x < C), (C > (~x)))');
+ checkSyntax(f(x < C, C > x * x), 'f((x < C), (C > (x * x)))');
+ checkSyntax(f(x < C, C > x / x), 'f((x < C), (C > (x / x)))');
+ checkSyntax(f(x < C, C > x ~/ x), 'f((x < C), (C > (x ~/ x)))');
+ checkSyntax(f(x < C, C > x % x), 'f((x < C), (C > (x % x)))');
+ checkSyntax(f(x < C, C > x + x), 'f((x < C), (C > (x + x)))');
+ checkSyntax(f(x < C, C > x - x), 'f((x < C), (C > (x - x)))');
+ checkSyntax(f(x < C, C > x << x), 'f((x < C), (C > (x << x)))');
+ checkSyntax(f(x < C, C > x >> x), 'f((x < C), (C > (x >> x)))');
+ checkSyntax(f(x < C, C > x & x), 'f((x < C), (C > (x & x)))');
+ checkSyntax(f(x < C, C > x ^ x), 'f((x < C), (C > (x ^ x)))');
+ checkSyntax(f(x < C, C > x | x), 'f((x < C), (C > (x | x)))');
+ ThisTest().test();
+}
diff --git a/tests/language/syntax_helper.dart b/tests/language/syntax_helper.dart
new file mode 100644
index 0000000..7cf4147
--- /dev/null
+++ b/tests/language/syntax_helper.dart
@@ -0,0 +1,136 @@
+// 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:convert';
+
+import "package:expect/expect.dart";
+
+/// Helper class for determining when no argument is passed to a function.
+class Absent {
+ const Absent();
+}
+
+const absent = Absent();
+
+/// Returns an approximate representation of the syntax that was used to
+/// construct [x]. Extra parentheses are used around unary and binary
+/// expressions to disambiguate precedence.
+String syntax(Object? x) {
+ var knownSyntax = SyntaxTracker.known[x];
+ if (knownSyntax != null) return knownSyntax;
+ if (x is SyntaxTracker || x is num) {
+ return x.toString();
+ } else if (x is List) {
+ return '[${x.map(syntax).join(', ')}]';
+ } else if (x is Set) {
+ if (x.isEmpty) return 'Set()';
+ return '{${x.map(syntax).join(', ')}}';
+ } else if (x is Map) {
+ if (x.isEmpty) return '{}';
+ var entries = x.entries
+ .map((entry) => '${syntax(entry.key)}: ${syntax(entry.value)}');
+ return '{ ${entries.join(', ')} }';
+ } else if (x is String) {
+ return json.encode(x);
+ } else {
+ throw UnimplementedError('Unknown syntax for $x. '
+ 'Consider adding to `SyntaxTracker.known`.');
+ }
+}
+
+/// Instances of this class record the syntax of operations performed on them.
+class SyntaxTracker {
+ final String _syntax;
+
+ SyntaxTracker(this._syntax);
+
+ String toString() => _syntax;
+
+ static String args([Object? x = absent, Object? y = absent]) =>
+ '(${[x, y].where((v) => v is! Absent).join(', ')})';
+
+ static String typeArgs(Type T, Type U) =>
+ T.toString() == 'dynamic' ? '' : '<${syntax(T)}, ${syntax(U)}>';
+
+ /// Simple objects with known syntactic representations. Tests can add to
+ /// this map.
+ static Map<Object?, String> known = {
+ true: 'true',
+ false: 'false',
+ null: 'null'
+ };
+}
+
+/// Extension allowing us to detect the syntax of most operations performed on
+/// arbitrary types.
+extension SyntaxTrackingExtension on Object? {
+ Object? method<T, U>([Object? x = absent, Object? y = absent]) => SyntaxTracker(
+ '${syntax(this)}.method${SyntaxTracker.typeArgs(T, U)}${SyntaxTracker.args(x, y)}');
+
+ Object? call<T, U>([Object? x = absent, Object? y = absent]) => SyntaxTracker(
+ '${syntax(this)}${SyntaxTracker.typeArgs(T, U)}${SyntaxTracker.args(x, y)}');
+
+ Object? get getter => SyntaxTracker('${syntax(this)}.getter');
+
+ Object? operator [](Object? index) =>
+ SyntaxTracker('${syntax(this)}[${syntax(index)}]');
+
+ Object? operator -() => SyntaxTracker('(-${syntax(this)})');
+
+ Object? operator ~() => SyntaxTracker('(~${syntax(this)})');
+
+ Object? operator *(Object? other) =>
+ SyntaxTracker('(${syntax(this)} * ${syntax(other)})');
+
+ Object? operator /(Object? other) =>
+ SyntaxTracker('(${syntax(this)} / ${syntax(other)})');
+
+ Object? operator ~/(Object? other) =>
+ SyntaxTracker('(${syntax(this)} ~/ ${syntax(other)})');
+
+ Object? operator %(Object? other) =>
+ SyntaxTracker('(${syntax(this)} % ${syntax(other)})');
+
+ Object? operator +(Object? other) =>
+ SyntaxTracker('(${syntax(this)} + ${syntax(other)})');
+
+ Object? operator -(Object? other) =>
+ SyntaxTracker('(${syntax(this)} - ${syntax(other)})');
+
+ Object? operator <<(Object? other) =>
+ SyntaxTracker('(${syntax(this)} << ${syntax(other)})');
+
+ Object? operator >>(Object? other) =>
+ SyntaxTracker('(${syntax(this)} >> ${syntax(other)})');
+
+ Object? operator &(Object? other) =>
+ SyntaxTracker('(${syntax(this)} & ${syntax(other)})');
+
+ Object? operator ^(Object? other) =>
+ SyntaxTracker('(${syntax(this)} ^ ${syntax(other)})');
+
+ Object? operator |(Object? other) =>
+ SyntaxTracker('(${syntax(this)} | ${syntax(other)})');
+
+ Object? operator <(Object? other) =>
+ SyntaxTracker('(${syntax(this)} < ${syntax(other)})');
+
+ Object? operator >(Object? other) =>
+ SyntaxTracker('(${syntax(this)} > ${syntax(other)})');
+
+ Object? operator <=(Object? other) =>
+ SyntaxTracker('(${syntax(this)} <= ${syntax(other)})');
+
+ Object? operator >=(Object? other) =>
+ SyntaxTracker('(${syntax(this)} >= ${syntax(other)})');
+}
+
+void checkSyntax(Object? x, String expectedSyntax) {
+ Expect.equals(expectedSyntax, syntax(x));
+}
+
+Object? f<T, U>([Object? x = absent, Object? y = absent]) => SyntaxTracker(
+ 'f${SyntaxTracker.typeArgs(T, U)}${SyntaxTracker.args(x, y)}');
+
+Object? x = SyntaxTracker('x');
diff --git a/tests/language_2/generic_methods/generic_invocation_all_subexpression_types_test.dart b/tests/language_2/generic_methods/generic_invocation_all_subexpression_types_test.dart
new file mode 100644
index 0000000..c1e64c5
--- /dev/null
+++ b/tests/language_2/generic_methods/generic_invocation_all_subexpression_types_test.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.
+
+// This test verifies that `EXPR<typeArguments>(arguments)` is properly parsed
+// as a generic invocation, for all types of expressions that may appear as
+// EXPR. We try to pay extra attention to ambiguous expressions (that is, where
+// interpreting the `<` and `>` as operators would also have led to a valid
+// parse).
+
+import '../syntax_helper.dart';
+
+class C extends SyntaxTracker {
+ C([Object x = absent, Object y = absent])
+ : super('new C${SyntaxTracker.args(x, y)}');
+
+ C.syntax(String s) : super(s);
+}
+
+class ThisTest extends C {
+ ThisTest() : super.syntax('this');
+
+ void test() {
+ checkSyntax(f(this<C, C>(0)), 'f(this<C, C>(0))');
+ // Note: SyntaxTracker can't see the parens around `this` in the line below
+ checkSyntax(f((this)<C, C>(0)), 'f(this<C, C>(0))');
+ }
+}
+
+main() {
+ SyntaxTracker.known[C] = 'C';
+ SyntaxTracker.known[#x] = '#x';
+ checkSyntax(f(f<C, C>(0)), 'f(f<C, C>(0))');
+ checkSyntax(f(x.method<C, C>(0)), 'f(x.method<C, C>(0))');
+ checkSyntax(f(C()<C, C>(0)), 'f(new C()<C, C>(0))');
+ checkSyntax(f(new C()<C, C>(0)), 'f(new C()<C, C>(0))');
+ checkSyntax(f(f()<C, C>(0)), 'f(f()<C, C>(0))');
+ checkSyntax(f(x.method()<C, C>(0)), 'f(x.method()<C, C>(0))');
+ checkSyntax(f(x[0]()<C, C>(0)), 'f(x[0]()<C, C>(0))');
+ checkSyntax(f(#x<C, C>(0)), 'f(#x<C, C>(0))');
+ checkSyntax(f(null<C, C>(0)), 'f(null<C, C>(0))');
+ checkSyntax(f(0<C, C>(0)), 'f(0<C, C>(0))');
+ checkSyntax(f(0.5<C, C>(0)), 'f(0.5<C, C>(0))');
+ checkSyntax(f([]<C, C>(0)), 'f([]<C, C>(0))');
+ checkSyntax(f([0]<C, C>(0)), 'f([0]<C, C>(0))');
+ checkSyntax(f({}<C, C>(0)), 'f({}<C, C>(0))');
+ checkSyntax(f({0}<C, C>(0)), 'f({0}<C, C>(0))');
+ checkSyntax(f({0: 0}<C, C>(0)), 'f({ 0: 0 }<C, C>(0))');
+ checkSyntax(f(true<C, C>(0)), 'f(true<C, C>(0))');
+ checkSyntax(f("s"<C, C>(0)), 'f("s"<C, C>(0))');
+ checkSyntax(f(r"s"<C, C>(0)), 'f("s"<C, C>(0))');
+ checkSyntax(f(x[0]<C, C>(0)), 'f(x[0]<C, C>(0))');
+ // Note: SyntaxTracker can't see the parens around `x` in the line below
+ checkSyntax(f((x)<C, C>(0)), 'f(x<C, C>(0))');
+ ThisTest().test();
+}
diff --git a/tests/language_2/generic_methods/non_generic_invocation_all_first_expression_types_test.dart b/tests/language_2/generic_methods/non_generic_invocation_all_first_expression_types_test.dart
new file mode 100644
index 0000000..1b56800
--- /dev/null
+++ b/tests/language_2/generic_methods/non_generic_invocation_all_first_expression_types_test.dart
@@ -0,0 +1,86 @@
+// 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.
+
+// This test verifies that `EXPR<a,b>-x` is properly parsed as a pair of
+// expressions separated by a `,`, for all types of expressions that may appear
+// as EXPR. We try to pay extra attention to expressions that will become
+// ambiguous when the "constructor tearoffs" feature is enabled (that is, where
+// interpreting the `<` and `>` as delimiting a list of type arguments would
+// also have led to a valid parse).
+
+import '../syntax_helper.dart';
+
+class C extends SyntaxTracker {
+ C([Object x = absent, Object y = absent])
+ : super('new C${SyntaxTracker.args(x, y)}');
+
+ C.syntax(String s) : super(s);
+
+ Object operator <(Object other) =>
+ SyntaxTracker('(${syntax(this)} < ${syntax(other)})');
+}
+
+class ThisTest extends C {
+ ThisTest() : super.syntax('this');
+
+ void test() {
+ checkSyntax(f(this < C, C > -x), 'f((this < C), (C > (-x)))');
+ // Note: SyntaxTracker can't see the parens around `this` in the line below
+ checkSyntax(f((this) < C, C > -x), 'f((this < C), (C > (-x)))');
+ }
+}
+
+class SuperTest extends C {
+ SuperTest() : super.syntax('super');
+
+ void test() {
+ checkSyntax(f(super < C, C > -x), 'f((super < C), (C > (-x)))');
+ }
+}
+
+main() {
+ const y = 123;
+ SyntaxTracker.known[C] = 'C';
+ SyntaxTracker.known[#x] = '#x';
+ SyntaxTracker.known[y] = 'y';
+ checkSyntax(f(x < C, C > -x), 'f((x < C), (C > (-x)))');
+ checkSyntax(f(x.getter < C, C > -x), 'f((x.getter < C), (C > (-x)))');
+ checkSyntax(f(C() < C, C > -x), 'f((new C() < C), (C > (-x)))');
+ checkSyntax(f(new C() < C, C > -x), 'f((new C() < C), (C > (-x)))');
+ checkSyntax(f(f() < C, C > -x), 'f((f() < C), (C > (-x)))');
+ checkSyntax(f(x.method() < C, C > -x), 'f((x.method() < C), (C > (-x)))');
+ checkSyntax(f(x[0]() < C, C > -x), 'f((x[0]() < C), (C > (-x)))');
+ checkSyntax(f(#x < C, C > -x), 'f((#x < C), (C > (-x)))');
+ checkSyntax(f(null < C, C > -x), 'f((null < C), (C > (-x)))');
+ checkSyntax(f(0 < y, C > -x), 'f(true, (C > (-x)))');
+ checkSyntax(f(0.5 < y, C > -x), 'f(true, (C > (-x)))');
+ checkSyntax(f([] < C, C > -x), 'f(([] < C), (C > (-x)))');
+ checkSyntax(f([0] < C, C > -x), 'f(([0] < C), (C > (-x)))');
+ checkSyntax(f({} < C, C > -x), 'f(({} < C), (C > (-x)))');
+ checkSyntax(f({0} < C, C > -x), 'f(({0} < C), (C > (-x)))');
+ checkSyntax(f({0: 0} < C, C > -x), 'f(({ 0: 0 } < C), (C > (-x)))');
+ checkSyntax(f(true < C, C > -x), 'f((true < C), (C > (-x)))');
+ checkSyntax(f("s" < C, C > -x), 'f(("s" < C), (C > (-x)))');
+ checkSyntax(f(r"s" < C, C > -x), 'f(("s" < C), (C > (-x)))');
+ checkSyntax(f(x[0] < C, C > -x), 'f((x[0] < C), (C > (-x)))');
+ // Note: SyntaxTracker can't see the parens around `x` in the line below
+ checkSyntax(f((x) < C, C > -x), 'f((x < C), (C > (-x)))');
+ checkSyntax(f(-x < C, C > -x), 'f(((-x) < C), (C > (-x)))');
+ checkSyntax(f(!true < C, C > -x), 'f((false < C), (C > (-x)))');
+ checkSyntax(f(!(true) < C, C > -x), 'f((false < C), (C > (-x)))');
+ checkSyntax(f(~x < C, C > -x), 'f(((~x) < C), (C > (-x)))');
+ checkSyntax(f(x * x < C, C > -x), 'f(((x * x) < C), (C > (-x)))');
+ checkSyntax(f(x / x < C, C > -x), 'f(((x / x) < C), (C > (-x)))');
+ checkSyntax(f(x ~/ x < C, C > -x), 'f(((x ~/ x) < C), (C > (-x)))');
+ checkSyntax(f(x % x < C, C > -x), 'f(((x % x) < C), (C > (-x)))');
+ checkSyntax(f(x + x < C, C > -x), 'f(((x + x) < C), (C > (-x)))');
+ checkSyntax(f(x - x < C, C > -x), 'f(((x - x) < C), (C > (-x)))');
+ checkSyntax(f(x << x < C, C > -x), 'f(((x << x) < C), (C > (-x)))');
+ checkSyntax(f(x >> x < C, C > -x), 'f(((x >> x) < C), (C > (-x)))');
+ checkSyntax(f(x & x < C, C > -x), 'f(((x & x) < C), (C > (-x)))');
+ checkSyntax(f(x ^ x < C, C > -x), 'f(((x ^ x) < C), (C > (-x)))');
+ checkSyntax(f(x | x < C, C > -x), 'f(((x | x) < C), (C > (-x)))');
+ ThisTest().test();
+ SuperTest().test();
+}
diff --git a/tests/language_2/generic_methods/non_generic_invocation_all_second_expression_types_test.dart b/tests/language_2/generic_methods/non_generic_invocation_all_second_expression_types_test.dart
new file mode 100644
index 0000000..daead6d
--- /dev/null
+++ b/tests/language_2/generic_methods/non_generic_invocation_all_second_expression_types_test.dart
@@ -0,0 +1,71 @@
+// 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.
+
+// This test verifies that `f<a,b>EXPR` is properly parsed as a pair of
+// expressions separated by a `,`, for all types of expressions that may appear
+// as EXPR. We try to pay extra attention to expressions that will become
+// ambiguous when the "constructor tearoffs" feature is enabled (that is, where
+// interpreting the `<` and `>` as delimiting a list of type arguments would
+// also have led to a valid parse).
+
+import '../syntax_helper.dart';
+
+class C extends SyntaxTracker {
+ C([Object x = absent, Object y = absent])
+ : super('new C${SyntaxTracker.args(x, y)}');
+
+ C.syntax(String s) : super(s);
+
+ Object operator <(Object other) =>
+ SyntaxTracker('(${syntax(this)} < ${syntax(other)})');
+}
+
+class ThisTest extends C {
+ ThisTest() : super.syntax('this');
+
+ void test() {
+ checkSyntax(f(x < C, C > this), 'f((x < C), (C > this))');
+ }
+}
+
+main() {
+ SyntaxTracker.known[C] = 'C';
+ SyntaxTracker.known[#x] = '#x';
+ checkSyntax(f(x < C, C > x), 'f((x < C), (C > x))');
+ checkSyntax(f(x < C, C > x.getter), 'f((x < C), (C > x.getter))');
+ checkSyntax(f(x < C, C > C()), 'f((x < C), (C > new C()))');
+ checkSyntax(f(x < C, C > new C()), 'f((x < C), (C > new C()))');
+ checkSyntax(f(x < C, C > f()), 'f((x < C), (C > f()))');
+ checkSyntax(f(x < C, C > x.method()), 'f((x < C), (C > x.method()))');
+ checkSyntax(f(x < C, C > x[0]()), 'f((x < C), (C > x[0]()))');
+ checkSyntax(f(x < C, C > #x), 'f((x < C), (C > #x))');
+ checkSyntax(f(x < C, C > null), 'f((x < C), (C > null))');
+ checkSyntax(f(x < C, C > 0), 'f((x < C), (C > 0))');
+ checkSyntax(f(x < C, C > 0.5), 'f((x < C), (C > 0.5))');
+ checkSyntax(f(x < C, C > []), 'f((x < C), (C > []))');
+ checkSyntax(f(x < C, C > [0]), 'f((x < C), (C > [0]))');
+ checkSyntax(f(x < C, C > {}), 'f((x < C), (C > {}))');
+ checkSyntax(f(x < C, C > {0}), 'f((x < C), (C > {0}))');
+ checkSyntax(f(x < C, C > {0: 0}), 'f((x < C), (C > { 0: 0 }))');
+ checkSyntax(f(x < C, C > true), 'f((x < C), (C > true))');
+ checkSyntax(f(x < C, C > "s"), 'f((x < C), (C > "s"))');
+ checkSyntax(f(x < C, C > r"s"), 'f((x < C), (C > "s"))');
+ checkSyntax(f(x < C, C > x[0]), 'f((x < C), (C > x[0]))');
+ checkSyntax(f(x < C, C > -x), 'f((x < C), (C > (-x)))');
+ checkSyntax(f(x < C, C > !true), 'f((x < C), (C > false))');
+ checkSyntax(f(x < C, C > !(true)), 'f((x < C), (C > false))');
+ checkSyntax(f(x < C, C > ~x), 'f((x < C), (C > (~x)))');
+ checkSyntax(f(x < C, C > x * x), 'f((x < C), (C > (x * x)))');
+ checkSyntax(f(x < C, C > x / x), 'f((x < C), (C > (x / x)))');
+ checkSyntax(f(x < C, C > x ~/ x), 'f((x < C), (C > (x ~/ x)))');
+ checkSyntax(f(x < C, C > x % x), 'f((x < C), (C > (x % x)))');
+ checkSyntax(f(x < C, C > x + x), 'f((x < C), (C > (x + x)))');
+ checkSyntax(f(x < C, C > x - x), 'f((x < C), (C > (x - x)))');
+ checkSyntax(f(x < C, C > x << x), 'f((x < C), (C > (x << x)))');
+ checkSyntax(f(x < C, C > x >> x), 'f((x < C), (C > (x >> x)))');
+ checkSyntax(f(x < C, C > x & x), 'f((x < C), (C > (x & x)))');
+ checkSyntax(f(x < C, C > x ^ x), 'f((x < C), (C > (x ^ x)))');
+ checkSyntax(f(x < C, C > x | x), 'f((x < C), (C > (x | x)))');
+ ThisTest().test();
+}
diff --git a/tests/language_2/syntax_helper.dart b/tests/language_2/syntax_helper.dart
new file mode 100644
index 0000000..1c65097
--- /dev/null
+++ b/tests/language_2/syntax_helper.dart
@@ -0,0 +1,136 @@
+// 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:convert';
+
+import "package:expect/expect.dart";
+
+/// Helper class for determining when no argument is passed to a function.
+class Absent {
+ const Absent();
+}
+
+const absent = Absent();
+
+/// Returns an approximate representation of the syntax that was used to
+/// construct [x]. Extra parentheses are used around unary and binary
+/// expressions to disambiguate precedence.
+String syntax(Object x) {
+ var knownSyntax = SyntaxTracker.known[x];
+ if (knownSyntax != null) return knownSyntax;
+ if (x is SyntaxTracker || x is num) {
+ return x.toString();
+ } else if (x is List) {
+ return '[${x.map(syntax).join(', ')}]';
+ } else if (x is Set) {
+ if (x.isEmpty) return 'Set()';
+ return '{${x.map(syntax).join(', ')}}';
+ } else if (x is Map) {
+ if (x.isEmpty) return '{}';
+ var entries = x.entries
+ .map((entry) => '${syntax(entry.key)}: ${syntax(entry.value)}');
+ return '{ ${entries.join(', ')} }';
+ } else if (x is String) {
+ return json.encode(x);
+ } else {
+ throw UnimplementedError('Unknown syntax for $x. '
+ 'Consider adding to `SyntaxTracker.known`.');
+ }
+}
+
+/// Instances of this class record the syntax of operations performed on them.
+class SyntaxTracker {
+ final String _syntax;
+
+ SyntaxTracker(this._syntax);
+
+ String toString() => _syntax;
+
+ static String args([Object x = absent, Object y = absent]) =>
+ '(${[x, y].where((v) => v is! Absent).join(', ')})';
+
+ static String typeArgs(Type T, Type U) =>
+ T.toString() == 'dynamic' ? '' : '<${syntax(T)}, ${syntax(U)}>';
+
+ /// Simple objects with known syntactic representations. Tests can add to
+ /// this map.
+ static Map<Object, String> known = {
+ true: 'true',
+ false: 'false',
+ null: 'null'
+ };
+}
+
+/// Extension allowing us to detect the syntax of most operations performed on
+/// arbitrary types.
+extension SyntaxTrackingExtension on Object {
+ Object method<T, U>([Object x = absent, Object y = absent]) => SyntaxTracker(
+ '${syntax(this)}.method${SyntaxTracker.typeArgs(T, U)}${SyntaxTracker.args(x, y)}');
+
+ Object call<T, U>([Object x = absent, Object y = absent]) => SyntaxTracker(
+ '${syntax(this)}${SyntaxTracker.typeArgs(T, U)}${SyntaxTracker.args(x, y)}');
+
+ Object get getter => SyntaxTracker('${syntax(this)}.getter');
+
+ Object operator [](Object index) =>
+ SyntaxTracker('${syntax(this)}[${syntax(index)}]');
+
+ Object operator -() => SyntaxTracker('(-${syntax(this)})');
+
+ Object operator ~() => SyntaxTracker('(~${syntax(this)})');
+
+ Object operator *(Object other) =>
+ SyntaxTracker('(${syntax(this)} * ${syntax(other)})');
+
+ Object operator /(Object other) =>
+ SyntaxTracker('(${syntax(this)} / ${syntax(other)})');
+
+ Object operator ~/(Object other) =>
+ SyntaxTracker('(${syntax(this)} ~/ ${syntax(other)})');
+
+ Object operator %(Object other) =>
+ SyntaxTracker('(${syntax(this)} % ${syntax(other)})');
+
+ Object operator +(Object other) =>
+ SyntaxTracker('(${syntax(this)} + ${syntax(other)})');
+
+ Object operator -(Object other) =>
+ SyntaxTracker('(${syntax(this)} - ${syntax(other)})');
+
+ Object operator <<(Object other) =>
+ SyntaxTracker('(${syntax(this)} << ${syntax(other)})');
+
+ Object operator >>(Object other) =>
+ SyntaxTracker('(${syntax(this)} >> ${syntax(other)})');
+
+ Object operator &(Object other) =>
+ SyntaxTracker('(${syntax(this)} & ${syntax(other)})');
+
+ Object operator ^(Object other) =>
+ SyntaxTracker('(${syntax(this)} ^ ${syntax(other)})');
+
+ Object operator |(Object other) =>
+ SyntaxTracker('(${syntax(this)} | ${syntax(other)})');
+
+ Object operator <(Object other) =>
+ SyntaxTracker('(${syntax(this)} < ${syntax(other)})');
+
+ Object operator >(Object other) =>
+ SyntaxTracker('(${syntax(this)} > ${syntax(other)})');
+
+ Object operator <=(Object other) =>
+ SyntaxTracker('(${syntax(this)} <= ${syntax(other)})');
+
+ Object operator >=(Object other) =>
+ SyntaxTracker('(${syntax(this)} >= ${syntax(other)})');
+}
+
+void checkSyntax(Object x, String expectedSyntax) {
+ Expect.equals(expectedSyntax, syntax(x));
+}
+
+Object f<T, U>([Object x = absent, Object y = absent]) => SyntaxTracker(
+ 'f${SyntaxTracker.typeArgs(T, U)}${SyntaxTracker.args(x, y)}');
+
+Object x = SyntaxTracker('x');
diff --git a/tools/VERSION b/tools/VERSION
index c64b96a..317622d 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
MAJOR 2
MINOR 14
PATCH 0
-PRERELEASE 120
+PRERELEASE 121
PRERELEASE_PATCH 0
\ No newline at end of file