Version 3.10.0-95.0.dev Merge 922aff883be72d4918e8ec9ab27556c3866f5e4e into dev
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/feature_computer.dart b/pkg/analysis_server/lib/src/services/completion/dart/feature_computer.dart index ff96404..b0e80e3 100644 --- a/pkg/analysis_server/lib/src/services/completion/dart/feature_computer.dart +++ b/pkg/analysis_server/lib/src/services/completion/dart/feature_computer.dart
@@ -854,6 +854,11 @@ } @override + DartType? visitInstanceCreationExpression(InstanceCreationExpression node) { + return _visitParent(node); + } + + @override DartType? visitIsExpression(IsExpression node) { if (node.isOperator.end < offset) { return node.expression.staticType;
diff --git a/pkg/analysis_server/lib/src/services/correction/assist.dart b/pkg/analysis_server/lib/src/services/correction/assist.dart index 2007143..eb2ae7c 100644 --- a/pkg/analysis_server/lib/src/services/correction/assist.dart +++ b/pkg/analysis_server/lib/src/services/correction/assist.dart
@@ -111,6 +111,11 @@ DartAssistKindPriority.default_, 'Convert to use a URI', ); + static const convertToDotShorthand = AssistKind( + 'dart.assist.convert.ToDotShorthand', + DartAssistKindPriority.default_, + 'Convert to dot shorthand', + ); static const convertToDoubleQuotedString = AssistKind( 'dart.assist.convert.toDoubleQuotedString', DartAssistKindPriority.default_,
diff --git a/pkg/analysis_server/lib/src/services/correction/assist_internal.dart b/pkg/analysis_server/lib/src/services/correction/assist_internal.dart index 9de9ef0..cd9a573 100644 --- a/pkg/analysis_server/lib/src/services/correction/assist_internal.dart +++ b/pkg/analysis_server/lib/src/services/correction/assist_internal.dart
@@ -25,6 +25,7 @@ import 'package:analysis_server/src/services/correction/dart/convert_map_from_iterable_to_for_literal.dart'; import 'package:analysis_server/src/services/correction/dart/convert_part_of_to_uri.dart'; import 'package:analysis_server/src/services/correction/dart/convert_quotes.dart'; +import 'package:analysis_server/src/services/correction/dart/convert_to_dot_shorthand.dart'; import 'package:analysis_server/src/services/correction/dart/convert_to_expression_function_body.dart'; import 'package:analysis_server/src/services/correction/dart/convert_to_field_parameter.dart'; import 'package:analysis_server/src/services/correction/dart/convert_to_generic_function_syntax.dart'; @@ -105,6 +106,7 @@ ConvertMapFromIterableToForLiteral.new, ConvertPartOfToUri.new, ConvertSwitchExpressionToSwitchStatement.new, + ConvertToDotShorthand.new, ConvertToDoubleQuotes.new, ConvertToExpressionFunctionBody.new, ConvertToFieldParameter.new,
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/convert_to_dot_shorthand.dart b/pkg/analysis_server/lib/src/services/correction/dart/convert_to_dot_shorthand.dart new file mode 100644 index 0000000..c9a9a7f --- /dev/null +++ b/pkg/analysis_server/lib/src/services/correction/dart/convert_to_dot_shorthand.dart
@@ -0,0 +1,133 @@ +// Copyright (c) 2025, 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/src/services/completion/dart/feature_computer.dart'; +import 'package:analysis_server/src/services/correction/assist.dart'; +import 'package:analysis_server_plugin/edit/dart/correction_producer.dart'; +import 'package:analyzer/dart/analysis/features.dart'; +import 'package:analyzer/dart/ast/ast.dart'; +import 'package:analyzer/dart/element/element.dart'; +import 'package:analyzer/dart/element/type.dart'; +import 'package:analyzer_plugin/utilities/assist/assist.dart'; +import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart'; +import 'package:analyzer_plugin/utilities/range_factory.dart'; + +class ConvertToDotShorthand extends ResolvedCorrectionProducer { + ConvertToDotShorthand({required super.context}); + + @override + CorrectionApplicability get applicability => + CorrectionApplicability.automatically; + + @override + AssistKind get assistKind => DartAssistKind.convertToDotShorthand; + + @override + Future<void> compute(ChangeBuilder builder) async { + if (!isEnabled(Feature.dot_shorthands)) return; + await computeAssistForNode(builder, node.parent); + } + + Future<void> computeAssistForNode( + ChangeBuilder builder, + AstNode? node, + ) async { + switch (node) { + // e.g. `A.nam^ed()` + case ConstructorName constructorName: + // e.g. `pref^ix.A.named()` + case NamedType(parent: ConstructorName constructorName): + await convertFromConstructorName(builder, constructorName); + // e.g. `A.meth^od()` + case MethodInvocation(): + await convertFromMethodInvocation(builder, node); + // e.g. `A.gett^er` + case PrefixedIdentifier(): + await convertFromPrefixedIdentifier(builder, node); + } + } + + /// Whether the element of [node] matches the [typeElement], allowing us to + /// convert the typed identifier to a dot shorthand. + /// + /// In the following example, the node `B.getter` is unable to be converted + /// to a valid dot shorthand, and this method would return `false`. + /// + /// ```dart + /// class A {} + /// class B { + /// static A get getter => A(); + /// } + /// + /// A f() { + /// return B.getter; + /// } + /// ``` + bool contextTypeMatchesTypeElement(AstNode node, Element? typeElement) { + var featureComputer = FeatureComputer( + unitResult.libraryElement.typeSystem, + unitResult.libraryElement.typeProvider, + ); + var contextType = featureComputer.computeContextType(node, node.offset); + if (contextType is! InterfaceType) return false; + return contextType.element == typeElement; + } + + /// Converts a constructor to a dot shorthand. + /// (e.g. `E.named()` to `.named()` or `E()` to `.new()`) + Future<void> convertFromConstructorName( + ChangeBuilder builder, + ConstructorName node, + ) async { + if (!contextTypeMatchesTypeElement(node, node.type.element)) return; + + // Dot shorthand constructors don't have explicit type arguments. + // Disallow the assist if the user provided explicit arguments. + if (node.type.typeArguments != null) return; + + if (node.name != null) { + // Converts a named constructor e.g. `E.named()` to `.named()`. + await builder.addDartFileEdit(file, (builder) { + builder.addDeletion(range.node(node.type)); + }); + } else { + // Converts an unnamed constructor e.g. `E()` to `.new()`. + await builder.addDartFileEdit(file, (builder) { + builder.addSimpleReplacement(range.node(node.type), '.new'); + }); + } + } + + /// Converts a method invocation to a dot shorthand. + /// (e.g. `E.id()` to `.id()`) + Future<void> convertFromMethodInvocation( + ChangeBuilder builder, + MethodInvocation node, + ) async { + var target = node.target; + if (target is SimpleIdentifier) { + if (!contextTypeMatchesTypeElement(node, target.element)) return; + await builder.addDartFileEdit(file, (builder) { + builder.addDeletion(range.node(target)); + }); + } + } + + /// Converts a prefix identifier to a dot shorthand. (e.g. `E.id` to `.id`) + Future<void> convertFromPrefixedIdentifier( + ChangeBuilder builder, + PrefixedIdentifier node, + ) async { + Identifier prefix; + if (node.prefix.element is PrefixElement) { + prefix = node; + } else { + prefix = node.prefix; + } + if (!contextTypeMatchesTypeElement(node, prefix.element)) return; + await builder.addDartFileEdit(file, (builder) { + builder.addDeletion(range.node(prefix)); + }); + } +}
diff --git a/pkg/analysis_server/lib/src/utilities/process.dart b/pkg/analysis_server/lib/src/utilities/process.dart index 334850d..1d9ee7b 100644 --- a/pkg/analysis_server/lib/src/utilities/process.dart +++ b/pkg/analysis_server/lib/src/utilities/process.dart
@@ -23,6 +23,7 @@ environment: environment, includeParentEnvironment: includeParentEnvironment, runInShell: runInShell, + mode: mode, ); } }
diff --git a/pkg/analysis_server/test/src/services/correction/assist/convert_to_dot_shorthand_test.dart b/pkg/analysis_server/test/src/services/correction/assist/convert_to_dot_shorthand_test.dart new file mode 100644 index 0000000..5f5ff15 --- /dev/null +++ b/pkg/analysis_server/test/src/services/correction/assist/convert_to_dot_shorthand_test.dart
@@ -0,0 +1,768 @@ +// Copyright (c) 2025, 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/src/services/correction/assist.dart'; +import 'package:analyzer_plugin/utilities/assist/assist.dart'; +import 'package:test_reflective_loader/test_reflective_loader.dart'; + +import 'assist_processor.dart'; + +void main() { + defineReflectiveSuite(() { + defineReflectiveTests(ConvertToDotShorthandTest); + }); +} + +@reflectiveTest +class ConvertToDotShorthandTest extends AssistProcessorTest { + @override + AssistKind get kind => DartAssistKind.convertToDotShorthand; + + Future<void> test_chain_constructor_getter() async { + await resolveTestCode(''' +class A { + A get getter => A.named(); + A.named(); +} + +A f() { + return A.nam^ed().getter; +} +'''); + await assertHasAssist(''' +class A { + A get getter => A.named(); + A.named(); +} + +A f() { + return .named().getter; +} +'''); + } + + Future<void> test_chain_constructor_method() async { + await resolveTestCode(''' +class A { + A method() => A.named(); + A.named(); +} + +A f() { + return A.nam^ed().method(); +} +'''); + await assertHasAssist(''' +class A { + A method() => A.named(); + A.named(); +} + +A f() { + return .named().method(); +} +'''); + } + + Future<void> test_chain_getter_method() async { + await resolveTestCode(''' +class A { + static A get staticGetter => A(); + A method() => A(); +} + +A f() { + return A.static^Getter.method(); +} +'''); + await assertHasAssist(''' +class A { + static A get staticGetter => A(); + A method() => A(); +} + +A f() { + return .staticGetter.method(); +} +'''); + } + + Future<void> test_chain_method_getter() async { + await resolveTestCode(''' +class A { + A get getter => A(); + static A staticMethod() => A(); +} + +A f() { + return A.staticM^ethod().getter; +} +'''); + await assertHasAssist(''' +class A { + A get getter => A(); + static A staticMethod() => A(); +} + +A f() { + return .staticMethod().getter; +} +'''); + } + + Future<void> test_chain_method_method() async { + await resolveTestCode(''' +class A { + A method() => A(); + static A staticMethod() => A(); +} + +A f() { + return A.staticM^ethod().method(); +} +'''); + await assertHasAssist(''' +class A { + A method() => A(); + static A staticMethod() => A(); +} + +A f() { + return .staticMethod().method(); +} +'''); + } + + Future<void> test_chain_outer_getter() async { + await resolveTestCode(''' +class A { + A get getter => A(); + static A get staticGetter => A(); +} + +A f() { + return A.staticGetter.get^ter; +} +'''); + await assertNoAssist(); + } + + Future<void> test_chain_outer_method() async { + await resolveTestCode(''' +class A { + A method() => A(); + static A staticMethod() => A(); +} + +A f() { + return A.staticMethod().meth^od(); +} +'''); + await assertNoAssist(); + } + + Future<void> test_constructor_named_onNamedType() async { + await resolveTestCode(''' +class A { + A.named(); +} + +A f() { + return A^.named(); +} +'''); + await assertHasAssist(''' +class A { + A.named(); +} + +A f() { + return .named(); +} +'''); + } + + Future<void> test_constructor_named_onSimpleIdentifier() async { + await resolveTestCode(''' +class A { + A.named(); +} + +A f() { + return A.na^med(); +} +'''); + await assertHasAssist(''' +class A { + A.named(); +} + +A f() { + return .named(); +} +'''); + } + + Future<void> test_constructor_named_onSimpleIdentifier_typeArguments() async { + await resolveTestCode(''' +class A<T> { + A.named(); +} + +A f() { + return A^<int>.named(); +} +'''); + await assertNoAssist(); + } + + Future<void> test_constructor_unnamed() async { + await resolveTestCode(''' +class A {} + +A f() { + return A^(); +} +'''); + await assertHasAssist(''' +class A {} + +A f() { + return .new(); +} +'''); + } + + Future<void> test_constructor_unnamed_typeArguments() async { + await resolveTestCode(''' +class A<T> {} + +A f() { + return A^<int>(); +} +'''); + await assertNoAssist(); + } + + Future<void> test_constructorReference_onNamedType() async { + await resolveTestCode(''' +Object f() { + return Object^.new; +} +'''); + await assertHasAssist(''' +Object f() { + return .new; +} +'''); + } + + Future<void> test_constructorReference_onSimpleIdentifier() async { + await resolveTestCode(''' +Object f() { + return Object.n^ew; +} +'''); + await assertHasAssist(''' +Object f() { + return .new; +} +'''); + } + + Future<void> test_importPrefix_chain() async { + newFile('$testPackageLibPath/a.dart', ''' +class A { + A method() => A(); + A get getter => A(); + A.named(); +} +'''); + await resolveTestCode(''' +import 'a.dart' as prefix; + +prefix.A f() { + return prefix.A.named().method().ge^tter; +} +'''); + await assertNoAssist(); + } + + Future<void> test_importPrefix_constructor_onConstructor() async { + newFile('$testPackageLibPath/a.dart', ''' +class A { + A.named(); +} +'''); + await resolveTestCode(''' +import 'a.dart' as prefix; + +prefix.A f() { + return prefix.A.na^med(); +} +'''); + await assertHasAssist(''' +import 'a.dart' as prefix; + +prefix.A f() { + return .named(); +} +'''); + } + + Future<void> test_importPrefix_constructor_onPrefix() async { + newFile('$testPackageLibPath/a.dart', ''' +class A { + A.named(); +} +'''); + await resolveTestCode(''' +import 'a.dart' as prefix; + +prefix.A f() { + return prefi^x.A.named(); +} +'''); + await assertHasAssist(''' +import 'a.dart' as prefix; + +prefix.A f() { + return .named(); +} +'''); + } + + Future<void> test_importPrefix_constructor_onType() async { + newFile('$testPackageLibPath/a.dart', ''' +class A { + A.named(); +} +'''); + await resolveTestCode(''' +import 'a.dart' as prefix; + +prefix.A f() { + return prefix.A^.named(); +} +'''); + await assertHasAssist(''' +import 'a.dart' as prefix; + +prefix.A f() { + return .named(); +} +'''); + } + + Future<void> test_importPrefix_getter_onPrefix() async { + newFile('$testPackageLibPath/a.dart', ''' +class A { + static A get getter => A(); +} +'''); + await resolveTestCode(''' +import 'a.dart' as prefix; + +prefix.A f() { + return pref^ix.A.getter; +} +'''); + await assertHasAssist(''' +import 'a.dart' as prefix; + +prefix.A f() { + return .getter; +} +'''); + } + + Future<void> test_importPrefix_getter_onSimpleIdentifier() async { + newFile('$testPackageLibPath/a.dart', ''' +class A { + static A get getter => A(); +} +'''); + await resolveTestCode(''' +import 'a.dart' as prefix; + +prefix.A f() { + return prefix.A.get^ter; +} +'''); + await assertNoAssist(); + } + + Future<void> test_importPrefix_getter_onType() async { + newFile('$testPackageLibPath/a.dart', ''' +class A { + static A get getter => A(); +} +'''); + await resolveTestCode(''' +import 'a.dart' as prefix; + +prefix.A f() { + return prefix.A^.getter; +} +'''); + await assertHasAssist(''' +import 'a.dart' as prefix; + +prefix.A f() { + return .getter; +} +'''); + } + + Future<void> test_importPrefix_method_onMethodInvocation() async { + newFile('$testPackageLibPath/a.dart', ''' +class A { + static A method() => A(); +} +'''); + await resolveTestCode(''' +import 'a.dart' as prefix; + +prefix.A f() { + return prefix.A.m^ethod(); +} +'''); + await assertNoAssist(); + } + + Future<void> test_importPrefix_method_onPrefix() async { + newFile('$testPackageLibPath/a.dart', ''' +class A { + static A method() => A(); +} +'''); + await resolveTestCode(''' +import 'a.dart' as prefix; + +prefix.A f() { + return pre^fix.A.method(); +} +'''); + await assertHasAssist(''' +import 'a.dart' as prefix; + +prefix.A f() { + return .method(); +} +'''); + } + + Future<void> test_importPrefix_method_onType() async { + newFile('$testPackageLibPath/a.dart', ''' +class A { + static A method() => A(); +} +'''); + await resolveTestCode(''' +import 'a.dart' as prefix; + +prefix.A f() { + return prefix.A^.method(); +} +'''); + await assertHasAssist(''' +import 'a.dart' as prefix; + +prefix.A f() { + return .method(); +} +'''); + } + + Future<void> test_methodInvocation_class_onNamedType() async { + await resolveTestCode(''' +class C { + static C method() => C(); +} + +C f() { + return C^.method(); +} +'''); + await assertHasAssist(''' +class C { + static C method() => C(); +} + +C f() { + return .method(); +} +'''); + } + + Future<void> test_methodInvocation_class_onNamedType_typeArguments() async { + await resolveTestCode(''' +class C { + static C method<T>(T t) { + print(t); + return C(); + } +} + +C f() { + return C^.method<int>(1); +} +'''); + await assertHasAssist(''' +class C { + static C method<T>(T t) { + print(t); + return C(); + } +} + +C f() { + return .method<int>(1); +} +'''); + } + + Future<void> test_methodInvocation_class_onSimpleIdentifier() async { + await resolveTestCode(''' +class C { + static C method() => C(); +} + +C f() { + return C.^method(); +} +'''); + await assertHasAssist(''' +class C { + static C method() => C(); +} + +C f() { + return .method(); +} +'''); + } + + Future<void> + test_methodInvocation_class_onSimpleIdentifier_typeArguments() async { + await resolveTestCode(''' +class C { + static C method<T>(T t) { + print(t); + return C(); + } +} + +C f() { + return C.meth^od<int>(1); +} +'''); + await assertHasAssist(''' +class C { + static C method<T>(T t) { + print(t); + return C(); + } +} + +C f() { + return .method<int>(1); +} +'''); + } + + Future<void> test_methodInvocation_extensionType_onNamedType() async { + await resolveTestCode(''' +extension type C(int x) { + static C method() => C(1); +} + +C f() { + return C^.method(); +} +'''); + await assertHasAssist(''' +extension type C(int x) { + static C method() => C(1); +} + +C f() { + return .method(); +} +'''); + } + + Future<void> test_methodInvocation_extensionType_onSimpleIdentifier() async { + await resolveTestCode(''' +extension type C(int x) { + static C method() => C(1); +} + +C f() { + return C.^method(); +} +'''); + await assertHasAssist(''' +extension type C(int x) { + static C method() => C(1); +} + +C f() { + return .method(); +} +'''); + } + + Future<void> test_prefixedIdentifier_class_field_onNamedType() async { + await resolveTestCode(''' +class C { + static C? field; +} + +C? f() { + return C^.field; +} +'''); + await assertHasAssist(''' +class C { + static C? field; +} + +C? f() { + return .field; +} +'''); + } + + Future<void> test_prefixedIdentifier_class_field_onSimpleIdentifier() async { + await resolveTestCode(''' +class C { + static C? field; +} + +C? f() { + return C.fi^eld; +} +'''); + await assertHasAssist(''' +class C { + static C? field; +} + +C? f() { + return .field; +} +'''); + } + + Future<void> test_prefixedIdentifier_class_getter_onNamedType() async { + await resolveTestCode(''' +class C { + static C get getter => C(); +} + +C f() { + return C^.getter; +} +'''); + await assertHasAssist(''' +class C { + static C get getter => C(); +} + +C f() { + return .getter; +} +'''); + } + + Future<void> + test_prefixedIdentifier_class_getter_onNamedType_otherClass() async { + await resolveTestCode(''' +class A {} + +class B { + static A get getter => A(); +} + +A f() { + return B^.getter; +} + +'''); + await assertNoAssist(); + } + + Future<void> test_prefixedIdentifier_class_getter_onSimpleIdentifier() async { + await resolveTestCode(''' +class C { + static C get getter => C(); +} + +C f() { + return C.get^ter; +} +'''); + await assertHasAssist(''' +class C { + static C get getter => C(); +} + +C f() { + return .getter; +} +'''); + } + + Future<void> + test_prefixedIdentifier_class_getter_onSimpleIdentifier_otherClass() async { + await resolveTestCode(''' +class A {} + +class B { + static A get getter => A(); +} + +A f() { + return B.g^etter; +} + +'''); + await assertNoAssist(); + } + + Future<void> test_prefixedIdentifier_enum_onNamedType() async { + await resolveTestCode(''' +enum E { a } + +E f() { + return E^.a; +} +'''); + await assertHasAssist(''' +enum E { a } + +E f() { + return .a; +} +'''); + } + + Future<void> test_prefixedIdentifier_enum_onSimpleIdentifier() async { + await resolveTestCode(''' +enum E { a } + +E f() { + return E.a^; +} +'''); + await assertHasAssist(''' +enum E { a } + +E f() { + return .a; +} +'''); + } +}
diff --git a/pkg/analysis_server/test/src/services/correction/assist/test_all.dart b/pkg/analysis_server/test/src/services/correction/assist/test_all.dart index 43648da..a3e162e 100644 --- a/pkg/analysis_server/test/src/services/correction/assist/test_all.dart +++ b/pkg/analysis_server/test/src/services/correction/assist/test_all.dart
@@ -29,6 +29,7 @@ import 'convert_into_is_not_empty_test.dart' as convert_into_is_not_empty; import 'convert_into_is_not_test.dart' as convert_into_is_not; import 'convert_part_of_to_uri_test.dart' as convert_part_of_to_uri; +import 'convert_to_dot_shorthand_test.dart' as convert_to_dot_shorthand; import 'convert_to_double_quoted_string_test.dart' as convert_to_double_quoted_string; import 'convert_to_field_parameter_test.dart' as convert_to_field_parameter; @@ -133,6 +134,7 @@ convert_into_is_not.main(); convert_into_is_not_empty.main(); convert_part_of_to_uri.main(); + convert_to_dot_shorthand.main(); convert_to_double_quoted_string.main(); convert_to_field_parameter.main(); convert_to_for_element.main();
diff --git a/pkg/analysis_server_plugin/CHANGELOG.md b/pkg/analysis_server_plugin/CHANGELOG.md index e19ff05..769991e 100644 --- a/pkg/analysis_server_plugin/CHANGELOG.md +++ b/pkg/analysis_server_plugin/CHANGELOG.md
@@ -1,3 +1,11 @@ +## 0.2.3-dev + +- Require version `8.2.0` of the `analyzer` package. + +## 0.2.2 + +- Require version `8.1.1` of the `analyzer` package. + ## 0.2.1 - Require version `^8.1.0` of the `analyzer` package.
diff --git a/pkg/analysis_server_plugin/pubspec.yaml b/pkg/analysis_server_plugin/pubspec.yaml index 9a523b6..686177c 100644 --- a/pkg/analysis_server_plugin/pubspec.yaml +++ b/pkg/analysis_server_plugin/pubspec.yaml
@@ -1,6 +1,6 @@ name: analysis_server_plugin description: A framework and support code for building plugins for the analysis server. -version: 0.2.1 +version: 0.2.3-dev repository: https://github.com/dart-lang/sdk/tree/main/pkg/analysis_server_plugin environment: @@ -11,8 +11,8 @@ dependencies: # See the release policy for managing this dependency at # pkg/analyzer/doc/implementation/releasing.md. - analyzer: ^8.1.0 - analyzer_plugin: ^0.13.2 + analyzer: 8.2.0-dev + analyzer_plugin: 0.13.8-dev meta: ^1.16.0 yaml: ^3.1.0 yaml_edit: ^2.2.0
diff --git a/pkg/analyzer/doc/implementation/releasing.md b/pkg/analyzer/doc/implementation/releasing.md index 6c814a7..12acc92 100644 --- a/pkg/analyzer/doc/implementation/releasing.md +++ b/pkg/analyzer/doc/implementation/releasing.md
@@ -36,7 +36,7 @@ `_fe_analyzer_shared` package a breaking release. The `_fe_analyzer_shared` package must be released each time the `analyzer` package is released, with a major version bump. The version of the `analyzer` package being released must -then depend on exactly that new version of the `_fe_analyzer_shared` package. +then depend on _exactly_ that new version of the `_fe_analyzer_shared` package. (Technically a caret dependency, like `_fe_analyzer_shared: ^82.0.0` would work, but it doesn't really make sense given that we only version the `_fe_analyzer_shared` package with major version releases.) @@ -65,9 +65,8 @@ The versioning of the `analyzer_plugin` package can follow basic semantic versioning based on its own API. -TODO: Should we also have an "exact version dependency" policy for the -`analyzer_plugin` package's dependency on the `analyzer` package? I believe so, -given the "freely use private implementation" situation. +The `analyzer_plugin` package must depend on the `analyzer` package with an +_exact_ version constraint. ## The `analyzer_testing` and `analyzer` packages @@ -79,9 +78,8 @@ The versioning of the `analyzer_testing` package can follow basic semantic versioning based on its own API. -TODO: Should we also have an "exact version dependency" policy for the -`analyzer_testing` package's dependency on the `analyzer` package? I believe -so, given the "freely use private implementation" situation. +The `analyzer_testing` package must depend on the `analyzer` package with an +_exact_ version constraint. ## The `analysis_server_plugin` and `analyzer` packages @@ -93,9 +91,9 @@ The versioning of the `analysis_server_plugin` package can follow basic semantic versioning based on its own API. -TODO: Should we also have an "exact version dependency" policy for the -`analysis_server_plugin` package's dependency on the `analyzer` package? I -believe so, given the "freely use private implementation" situation. +The `analysis_server_plugin` package must depend on the `analyzer` package with +an _exact_ version constraint. It must also depend on the `analyzer_plugin` +package with an _exact_ version constraint. [_fe_analyzer_shared source]: https://github.com/dart-lang/sdk/tree/main/pkg/_fe_analyzer_shared [_fe_analyzer_shared pub package]: https://pub.dev/packages/_fe_analyzer_shared
diff --git a/pkg/analyzer/lib/src/dart/element/element.dart b/pkg/analyzer/lib/src/dart/element/element.dart index 5bbbe469..bb76358 100644 --- a/pkg/analyzer/lib/src/dart/element/element.dart +++ b/pkg/analyzer/lib/src/dart/element/element.dart
@@ -132,7 +132,7 @@ @elementClass class ClassElementImpl extends InterfaceElementImpl implements ClassElement { @override - @trackedIncludedIntoId + @trackedIncludedInId final Reference reference; final ClassFragmentImpl _firstFragment; @@ -204,14 +204,14 @@ } @override - @trackedDirectlyDisable + @trackedDirectlyOpaque ClassFragmentImpl get firstFragment { globalResultRequirements?.recordOpaqueApiUse(this, 'firstFragment'); return _firstFragment; } @override - @trackedDirectlyDisable + @trackedDirectlyOpaque List<ClassFragmentImpl> get fragments { globalResultRequirements?.recordOpaqueApiUse(this, 'fragments'); return [ @@ -266,7 +266,7 @@ } @override - @trackedIncludedIntoId + @trackedIncludedInId bool get isAbstract { return hasModifier(Modifier.ABSTRACT); } @@ -276,7 +276,7 @@ } @override - @trackedIncludedIntoId + @trackedIncludedInId bool get isBase { return hasModifier(Modifier.BASE); } @@ -286,11 +286,11 @@ } @override - @trackedIncludedIntoId + @trackedIncludedInId bool get isConstructable => firstFragment.isConstructable; @override - @trackedIncludedIntoId + @trackedIncludedInId bool get isDartCoreEnum { return name == 'Enum' && library.isDartCore; } @@ -302,12 +302,12 @@ } @override - @trackedIncludedIntoId + @trackedIncludedInId bool get isDartCoreObject { return name == 'Object' && library.isDartCore; } - @trackedIncludedIntoId + @trackedIncludedInId bool get isDartCoreRecord { return name == 'Record' && library.isDartCore; } @@ -350,14 +350,14 @@ } @override - @trackedIncludedIntoId + @trackedIncludedInId bool get isExhaustive => firstFragment.isExhaustive; @override bool get isExtendableOutside => !isInterface && !isFinal && !isSealed; @override - @trackedIncludedIntoId + @trackedIncludedInId bool get isFinal { return hasModifier(Modifier.FINAL); } @@ -370,7 +370,7 @@ bool get isImplementableOutside => !isBase && !isFinal && !isSealed; @override - @trackedIncludedIntoId + @trackedIncludedInId bool get isInterface { return hasModifier(Modifier.INTERFACE); } @@ -388,7 +388,7 @@ } @override - @trackedIncludedIntoId + @trackedIncludedInId bool get isMixinApplication { return globalResultRequirements.includedInId(() { return firstFragment.isMixinApplication; @@ -396,7 +396,7 @@ } @override - @trackedIncludedIntoId + @trackedIncludedInId bool get isMixinClass { return hasModifier(Modifier.MIXIN_CLASS); } @@ -406,7 +406,7 @@ } @override - @trackedIncludedIntoId + @trackedIncludedInId bool get isSealed { return hasModifier(Modifier.SEALED); } @@ -416,14 +416,14 @@ } @override - @trackedIncludedIntoId + @trackedIncludedInId bool get isValidMixin => firstFragment.isValidMixin; @override ElementKind get kind => ElementKind.CLASS; @override - @trackedDirectlyDisable + @trackedDirectlyOpaque T? accept<T>(ElementVisitor2<T> visitor) { globalResultRequirements?.recordOpaqueApiUse(this, 'accept2'); return visitor.visitClassElement(this); @@ -431,7 +431,7 @@ @Deprecated('Use accept instead') @override - @trackedDirectlyDisable + @trackedDirectlyOpaque T? accept2<T>(ElementVisitor2<T> visitor) { return accept(visitor); } @@ -1790,7 +1790,7 @@ bool get isPublic => !isPrivate; @override - @trackedIncludedIntoId + @trackedIncludedInId LibraryElementImpl? get library { return globalResultRequirements.includedInId(() { return firstFragment.libraryFragment?.element as LibraryElementImpl?; @@ -4033,7 +4033,7 @@ } @override - @trackedIncludedIntoId + @trackedIncludedInId String get displayName { return globalResultRequirements.includedInId(() { return firstFragment.displayName; @@ -4090,7 +4090,7 @@ bool get isPublic => firstFragment.isPublic; @override - @trackedIncludedIntoId + @trackedIncludedInId bool get isSimplyBounded { return globalResultRequirements.includedInId(() { return firstFragment.isSimplyBounded; @@ -4098,7 +4098,7 @@ } @override - @trackedIncludedIntoId + @trackedIncludedInId bool get isSynthetic { return globalResultRequirements.includedInId(() { return firstFragment.isSynthetic; @@ -4113,7 +4113,7 @@ LibraryElementImpl get library2 => library; @override - @trackedIncludedIntoId + @trackedIncludedInId MetadataImpl get metadata { return globalResultRequirements.includedInId(() { return firstFragment.metadata; @@ -4141,7 +4141,7 @@ List<MethodElementImpl> get methods2 => methods; @override - @trackedIncludedIntoId + @trackedIncludedInId String? get name { return globalResultRequirements.includedInId(() { return firstFragment.name; @@ -4174,7 +4174,7 @@ List<SetterElementImpl> get setters2 => setters; @override - @trackedIncludedIntoId + @trackedIncludedInId List<TypeParameterElementImpl> get typeParameters { return globalResultRequirements.includedInId(() { return firstFragment.typeParameters @@ -4723,7 +4723,7 @@ .map; @override - @trackedIncludedIntoId + @trackedIncludedInId List<InterfaceTypeImpl> get interfaces { return globalResultRequirements.includedInId(() { return firstFragment.interfaces; @@ -4743,7 +4743,7 @@ } @override - @trackedIncludedIntoId + @trackedIncludedInId List<InterfaceTypeImpl> get mixins { return globalResultRequirements.includedInId(() { return firstFragment.mixins; @@ -4751,7 +4751,7 @@ } @override - @trackedIncludedIntoId + @trackedIncludedInId InterfaceTypeImpl? get supertype { return globalResultRequirements.includedInId(() { return firstFragment.supertype;
diff --git a/pkg/analyzer/lib/src/fine/annotations.dart b/pkg/analyzer/lib/src/fine/annotations.dart index 315fa6b..993b90f 100644 --- a/pkg/analyzer/lib/src/fine/annotations.dart +++ b/pkg/analyzer/lib/src/fine/annotations.dart
@@ -8,7 +8,7 @@ /// annotated, as follows: /// /// Public getters and fields must be annotated with one of: -/// [trackedIncludedIntoId] +/// [trackedIncludedInId] /// [trackedDirectly] /// [trackedIndirectly] /// @@ -19,24 +19,13 @@ /// Annotation for methods that must record the fact of the invocation. /// -/// In contrast to [trackedIncludedIntoId], the result of the method is not +/// In contrast to [trackedIncludedInId], the result of the method is not /// reflected in the ID of the target class, so we need to record this /// requirement separately. /// /// Examples: `getMethod(name)`, `getNamedConstructor(name)`. const trackedDirectly = _TrackedDirectly(); -/// Annotation for methods that are too invasive, and if invoked for anything -/// other than the current library, disable fine-grained dependencies -/// optimizations. So, the result will be re-computed if just anything in -/// its transitive closure of imported libraries changed. -/// -/// Examples: `fragments`, `visitChildren()`. -/// -/// We must make sure that the analyzer itself does not use such APIs. -/// We should try to fix any popular lint rules that use such APIs. -const trackedDirectlyDisable = _TrackedDirectlyDisable(); - /// As [trackedDirectly], but for methods that are expensive. /// /// For example, `get methods` adds dependency on all declared methods, but @@ -49,6 +38,17 @@ /// We should try to fix any popular lint rules that use such APIs. const trackedDirectlyExpensive = _TrackedDirectlyExpensive(); +/// Annotation for methods that are too invasive, and if invoked for anything +/// other than the current library, disable fine-grained dependencies +/// optimizations. So, the result will be re-computed if just anything in +/// its transitive closure of imported libraries changed. +/// +/// Examples: `fragments`, `visitChildren()`. +/// +/// We must make sure that the analyzer itself does not use such APIs. +/// We should try to fix any popular lint rules that use such APIs. +const trackedDirectlyOpaque = _TrackedDirectlyOpaque(); + /// Annotation for getters that don't require recording. /// /// The library manifest builder must compare the value of the getter with @@ -56,7 +56,7 @@ /// value is different. /// /// Examples: `isAbstract`, `interfaces`, `returnType`. -const trackedIncludedIntoId = _TrackedIncludedIntoId(); +const trackedIncludedInId = _TrackedIncludedInId(); /// Annotation for methods that don't require recording. /// @@ -73,16 +73,16 @@ const _TrackedDirectly(); } -final class _TrackedDirectlyDisable extends _TrackedDirectly { - const _TrackedDirectlyDisable(); -} - final class _TrackedDirectlyExpensive extends _TrackedDirectly { const _TrackedDirectlyExpensive(); } -final class _TrackedIncludedIntoId extends _TrackedKind { - const _TrackedIncludedIntoId(); +final class _TrackedDirectlyOpaque extends _TrackedDirectly { + const _TrackedDirectlyOpaque(); +} + +final class _TrackedIncludedInId extends _TrackedKind { + const _TrackedIncludedInId(); } final class _TrackedIndirectly extends _TrackedKind {
diff --git a/pkg/analyzer/lib/src/generated/testing/test_type_provider.dart b/pkg/analyzer/lib/src/generated/testing/test_type_provider.dart index db6a1ee..3174ef7 100644 --- a/pkg/analyzer/lib/src/generated/testing/test_type_provider.dart +++ b/pkg/analyzer/lib/src/generated/testing/test_type_provider.dart
@@ -9,6 +9,7 @@ import 'package:analyzer/src/dart/element/type_provider.dart'; import 'package:analyzer/src/generated/engine.dart' show AnalysisContext; import 'package:analyzer/src/generated/source.dart' show SourceFactory; +import 'package:analyzer/src/summary2/reference.dart'; import 'package:analyzer/src/test_utilities/mock_sdk_elements.dart'; import 'package:analyzer/src/utilities/uri_cache.dart'; @@ -18,7 +19,11 @@ factory TestTypeProvider() { var analysisContext = _MockAnalysisContext(); var analysisSession = _MockAnalysisSession(); - var sdkElements = MockSdkElements(analysisContext, analysisSession); + var sdkElements = MockSdkElements( + analysisContext, + Reference.root(), + analysisSession, + ); return TestTypeProvider._( sdkElements.coreLibrary, sdkElements.asyncLibrary,
diff --git a/pkg/analyzer/lib/src/test_utilities/mock_sdk_elements.dart b/pkg/analyzer/lib/src/test_utilities/mock_sdk_elements.dart index cc078d4..bd0e160 100644 --- a/pkg/analyzer/lib/src/test_utilities/mock_sdk_elements.dart +++ b/pkg/analyzer/lib/src/test_utilities/mock_sdk_elements.dart
@@ -2,18 +2,141 @@ // 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/analysis/features.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/source/line_info.dart'; import 'package:analyzer/src/dart/analysis/session.dart'; import 'package:analyzer/src/dart/element/element.dart'; -import 'package:analyzer/src/dart/element/type.dart'; import 'package:analyzer/src/generated/engine.dart' as engine; -import 'package:analyzer/src/generated/utilities_dart.dart'; import 'package:analyzer/src/summary2/reference.dart'; -import 'package:analyzer/src/utilities/extensions/string.dart'; +import 'package:analyzer/src/test_utilities/test_library_builder.dart'; + +const _sdkSpec = { + 'dart:core': LibrarySpec( + uri: 'dart:core', + classes: [ + ClassSpec( + name: 'bool', + supertype: 'Object', + constructors: [ + ConstructorSpec( + name: 'fromEnvironment', + formalParameters: 'String name, {bool defaultValue}', + isConst: true, + isFactory: true, + ), + ], + ), + ClassSpec(name: 'double', supertype: 'num'), + ClassSpec( + name: 'int', + supertype: 'num', + constructors: [ + ConstructorSpec( + name: 'fromEnvironment', + formalParameters: 'String name, {int defaultValue}', + isConst: true, + isFactory: true, + ), + ], + ), + ClassSpec( + name: 'num', + supertype: 'Object', + interfaces: ['Comparable<num>'], + ), + ClassSpec(name: 'Comparable', typeParameters: ['T'], isAbstract: true), + ClassSpec(name: 'Function', isAbstract: true, supertype: 'Object'), + ClassSpec( + name: 'Iterable', + typeParameters: ['E'], + isAbstract: true, + supertype: 'Object', + ), + ClassSpec( + name: 'Iterator', + typeParameters: ['E'], + isAbstract: true, + supertype: 'Object', + ), + ClassSpec( + name: 'List', + typeParameters: ['E'], + isAbstract: true, + supertype: 'Object', + interfaces: ['Iterable<E>'], + ), + ClassSpec( + name: 'Map', + typeParameters: ['K', 'V'], + isAbstract: true, + supertype: 'Object', + ), + ClassSpec(name: 'Null', supertype: 'Object'), + ClassSpec( + name: 'Object', + methods: [ + MethodSpec(name: 'toString', returnType: 'String'), + MethodSpec( + name: '==', + returnType: 'bool', + formalParameters: 'Object other', + ), + ], + ), + ClassSpec(name: 'Record', isAbstract: true, supertype: 'Object'), + ClassSpec( + name: 'Set', + typeParameters: ['E'], + isAbstract: true, + supertype: 'Object', + interfaces: ['Iterable<E>'], + ), + ClassSpec( + name: 'String', + supertype: 'Object', + constructors: [ + ConstructorSpec( + name: 'fromEnvironment', + formalParameters: 'String name, {String defaultValue}', + isConst: true, + isFactory: true, + ), + ], + methods: [ + MethodSpec(name: 'toLowerCase', returnType: 'String'), + MethodSpec( + name: '+', + returnType: 'String', + formalParameters: 'String other', + ), + ], + ), + ClassSpec( + name: 'Symbol', + isAbstract: true, + supertype: 'Object', + constructors: [ + ConstructorSpec( + isConst: true, + isFactory: true, + formalParameters: 'String name', + ), + ], + ), + ClassSpec(name: 'Type', isAbstract: true, supertype: 'Object'), + ], + ), + 'dart:async': LibrarySpec( + uri: 'dart:async', + classes: [ + ClassSpec( + name: 'Future', + typeParameters: ['T'], + isAbstract: true, + supertype: 'Object', + ), + ClassSpec(name: 'FutureOr', typeParameters: ['T'], supertype: 'Object'), + ], + ), +}; class MockSdkElements { final LibraryElementImpl coreLibrary; @@ -21,1282 +144,20 @@ factory MockSdkElements( engine.AnalysisContext analysisContext, + Reference rootReference, AnalysisSessionImpl analysisSession, ) { - var builder = _MockSdkElementsBuilder(analysisContext, analysisSession); - var coreLibrary = builder._buildCore(); - var asyncLibrary = builder._buildAsync(); - builder._populateCore(); - builder._populateAsync(); - return MockSdkElements._(coreLibrary, asyncLibrary); - } - - MockSdkElements._(this.coreLibrary, this.asyncLibrary); -} - -class _MockSdkElementsBuilder { - final engine.AnalysisContext analysisContext; - final AnalysisSessionImpl analysisSession; - - ClassFragmentImpl? _boolElement; - ClassFragmentImpl? _comparableElement; - ClassFragmentImpl? _completerElement; - ClassFragmentImpl? _deprecatedElement; - ClassFragmentImpl? _doubleElement; - ClassFragmentImpl? _functionElement; - ClassFragmentImpl? _futureElement; - ClassFragmentImpl? _futureOrElement; - ClassFragmentImpl? _intElement; - ClassFragmentImpl? _iterableElement; - ClassFragmentImpl? _iteratorElement; - ClassFragmentImpl? _listElement; - ClassFragmentImpl? _mapElement; - ClassFragmentImpl? _nullElement; - ClassFragmentImpl? _numElement; - ClassFragmentImpl? _objectElement; - ClassFragmentImpl? _overrideElement; - ClassFragmentImpl? _recordElement; - ClassFragmentImpl? _setElement; - ClassFragmentImpl? _stackTraceElement; - ClassFragmentImpl? _streamElement; - ClassFragmentImpl? _streamSubscriptionElement; - ClassFragmentImpl? _stringElement; - ClassFragmentImpl? _symbolElement; - ClassFragmentImpl? _typeElement; - - InterfaceTypeImpl? _boolType; - InterfaceTypeImpl? _doubleType; - InterfaceTypeImpl? _intType; - InterfaceTypeImpl? _numType; - InterfaceTypeImpl? _objectType; - InterfaceTypeImpl? _stringType; - InterfaceTypeImpl? _typeType; - - late LibraryElementImpl _asyncLibrary; - late LibraryFragmentImpl _asyncUnit; - - late LibraryElementImpl _coreLibrary; - late LibraryFragmentImpl _coreUnit; - - _MockSdkElementsBuilder(this.analysisContext, this.analysisSession); - - ClassFragmentImpl get boolElement { - var boolElement = _boolElement; - if (boolElement != null) return boolElement; - - _boolElement = boolElement = _class(name: 'bool', unit: _coreUnit); - boolElement.supertype = objectType; - - boolElement.constructors = [ - _constructor( - name: 'fromEnvironment', - isConst: true, - isFactory: true, - parameters: [ - _requiredParameter('name', stringType), - _namedParameter('defaultValue', boolType), - ], - ), - ]; - - _buildClassElement(boolElement); - return boolElement; - } - - InterfaceTypeImpl get boolType { - return _boolType ??= _interfaceType(boolElement); - } - - ClassFragmentImpl get comparableElement { - var comparableElement = _comparableElement; - if (comparableElement != null) return comparableElement; - - var tElement = _typeParameter('T'); - _comparableElement = - comparableElement = _class( - name: 'Comparable', - isAbstract: true, - typeParameters: [tElement], - unit: _coreUnit, - ); - comparableElement.supertype = objectType; - - _buildClassElement(comparableElement); - return comparableElement; - } - - ClassFragmentImpl get completerElement { - var completerElement = _completerElement; - if (completerElement != null) return completerElement; - - var tElement = _typeParameter('T'); - _completerElement = - completerElement = _class( - name: 'Completer', - isAbstract: true, - typeParameters: [tElement], - unit: _asyncUnit, - ); - completerElement.supertype = objectType; - - _buildClassElement(completerElement); - return completerElement; - } - - ClassFragmentImpl get deprecatedElement { - var deprecatedElement = _deprecatedElement; - if (deprecatedElement != null) return deprecatedElement; - - _deprecatedElement = - deprecatedElement = _class(name: 'Deprecated', unit: _coreUnit); - deprecatedElement.supertype = objectType; - - deprecatedElement.fields = [_field('message', stringType, isFinal: true)]; - - deprecatedElement.getters = - deprecatedElement.fields - .map((f) => f.element.getter!.firstFragment) - .toList(); - - deprecatedElement.constructors = [ - _constructor( - isConst: true, - parameters: [_requiredParameter('message', stringType)], - ), - ]; - - _buildClassElement(deprecatedElement); - return deprecatedElement; - } - - ClassFragmentImpl get doubleElement { - var doubleElement = _doubleElement; - if (doubleElement != null) return doubleElement; - - _doubleElement = - doubleElement = _class( - name: 'double', - isAbstract: true, - unit: _coreUnit, - ); - doubleElement.supertype = numType; - - FieldFragmentImpl staticConstDoubleField(String name) { - return _field(name, doubleType, isStatic: true, isConst: true); - } - - doubleElement.fields = <FieldFragmentImpl>[ - staticConstDoubleField('nan'), - staticConstDoubleField('infinity'), - staticConstDoubleField('negativeInfinity'), - staticConstDoubleField('minPositive'), - staticConstDoubleField('maxFinite'), - ]; - - doubleElement.getters = - doubleElement.fields - .map((field) => field.element.getter!.firstFragment) - .toList(); - - doubleElement.methods = [ - _method( - '+', - doubleType, - parameters: [_requiredParameter('other', numType)], - ), - _method( - '*', - doubleType, - parameters: [_requiredParameter('other', numType)], - ), - _method( - '-', - doubleType, - parameters: [_requiredParameter('other', numType)], - ), - _method( - '%', - doubleType, - parameters: [_requiredParameter('other', numType)], - ), - _method( - '/', - doubleType, - parameters: [_requiredParameter('other', numType)], - ), - _method( - '~/', - intType, - parameters: [_requiredParameter('other', numType)], - ), - _method( - '-', - doubleType, - parameters: [_requiredParameter('other', numType)], - ), - _method('abs', doubleType), - _method('ceil', doubleType), - _method('floor', doubleType), - _method( - 'remainder', - doubleType, - parameters: [_requiredParameter('other', numType)], - ), - _method('round', doubleType), - _method('toString', stringType), - _method('truncate', doubleType), - ]; - - _buildClassElement(doubleElement); - return doubleElement; - } - - InterfaceTypeImpl get doubleType { - return _doubleType ??= _interfaceType(doubleElement); - } - - DynamicTypeImpl get dynamicType => DynamicTypeImpl.instance; - - ClassFragmentImpl get functionElement { - var functionElement = _functionElement; - if (functionElement != null) return functionElement; - - _functionElement = - functionElement = _class( - name: 'Function', - isAbstract: true, - unit: _coreUnit, - ); - functionElement.supertype = objectType; - - _buildClassElement(functionElement); - return functionElement; - } - - InterfaceTypeImpl get functionType { - return _interfaceType(functionElement); - } - - ClassFragmentImpl get futureElement { - var futureElement = _futureElement; - if (futureElement != null) return futureElement; - - var tElement = _typeParameter('T'); - var tType = _typeParameterType(tElement); - - _futureElement = - futureElement = _class( - name: 'Future', - isAbstract: true, - typeParameters: [tElement], - unit: _asyncUnit, - ); - futureElement.supertype = objectType; - - // factory Future.value([FutureOr<T> value]) - futureElement.constructors = [ - _constructor( - isFactory: true, - parameters: [_positionalParameter('value', futureOrType(tType))], - ), - ]; - - // Future<R> then<R>(FutureOr<R> onValue(T value), {Function onError}) - var rElement = _typeParameter('R'); - var rType = _typeParameterType(rElement); - futureElement.methods = [ - _method( - 'then', - futureType(rType), - parameters: [ - _requiredParameter( - 'onValue', - _functionType( - returnType: futureOrType(rType), - parameters: [_requiredParameter('value', tType)], - ), - ), - _positionalParameter('onError', functionType), - ], - ), - ]; - - _buildClassElement(futureElement); - return futureElement; - } - - ClassFragmentImpl get futureOrElement { - var futureOrElement = _futureOrElement; - if (futureOrElement != null) return futureOrElement; - - var tElement = _typeParameter('T'); - _futureOrElement = - futureOrElement = _class( - name: 'FutureOr', - typeParameters: [tElement], - unit: _asyncUnit, - ); - futureOrElement.supertype = objectType; - - _buildClassElement(futureOrElement); - return futureOrElement; - } - - ClassFragmentImpl get intElement { - var intElement = _intElement; - if (intElement != null) return intElement; - - _intElement = - intElement = _class(name: 'int', isAbstract: true, unit: _coreUnit); - intElement.supertype = numType; - - intElement.constructors = [ - _constructor( - name: 'fromEnvironment', - isConst: true, - isFactory: true, - parameters: [ - _requiredParameter('name', stringType), - _namedParameter('defaultValue', intType), - ], - ), - ]; - - intElement.methods = [ - _method('&', intType, parameters: [_requiredParameter('other', intType)]), - _method('|', intType, parameters: [_requiredParameter('other', intType)]), - _method('^', intType, parameters: [_requiredParameter('other', intType)]), - _method('~', intType), - _method( - '<<', - intType, - parameters: [_requiredParameter('shiftAmount', intType)], - ), - _method( - '>>', - intType, - parameters: [_requiredParameter('shiftAmount', intType)], - ), - _method('-', intType), - _method('abs', intType), - _method('round', intType), - _method('floor', intType), - _method('ceil', intType), - _method('truncate', intType), - _method('toString', stringType), - ]; - - _buildClassElement(intElement); - return intElement; - } - - InterfaceTypeImpl get intType { - return _intType ??= _interfaceType(intElement); - } - - ClassFragmentImpl get iterableElement { - var iterableElement = _iterableElement; - if (iterableElement != null) return iterableElement; - - var eElement = _typeParameter('E'); - var eType = _typeParameterType(eElement); - - _iterableElement = - iterableElement = _class( - name: 'Iterable', - isAbstract: true, - typeParameters: [eElement], - unit: _coreUnit, - ); - iterableElement.supertype = objectType; - - iterableElement.constructors = [_constructor(isConst: true)]; - - _setGetters(iterableElement, [ - _getter('iterator', iteratorType(eType)), - _getter('last', eType), - ]); - - _buildClassElement(iterableElement); - return iterableElement; - } - - ClassFragmentImpl get iteratorElement { - var iteratorElement = _iteratorElement; - if (iteratorElement != null) return iteratorElement; - - var eElement = _typeParameter('E'); - var eType = _typeParameterType(eElement); - - _iteratorElement = - iteratorElement = _class( - name: 'Iterator', - isAbstract: true, - typeParameters: [eElement], - unit: _coreUnit, - ); - iteratorElement.supertype = objectType; - - _setGetters(iterableElement, [_getter('current', eType)]); - - _buildClassElement(iteratorElement); - return iteratorElement; - } - - ClassFragmentImpl get listElement { - var listElement = _listElement; - if (listElement != null) return listElement; - - var eElement = _typeParameter('E'); - var eType = _typeParameterType(eElement); - - _listElement = - listElement = _class( - name: 'List', - isAbstract: true, - typeParameters: [eElement], - unit: _coreUnit, - ); - listElement.supertype = objectType; - listElement.interfaces = [iterableType(eType)]; - - listElement.constructors = [ - _constructor( - isFactory: true, - parameters: [_positionalParameter('length', intType)], - ), - ]; - - _setGetters(listElement, [_getter('length', intType)]); - - listElement.methods = [ - _method('[]', eType, parameters: [_requiredParameter('index', intType)]), - _method( - '[]=', - voidType, - parameters: [ - _requiredParameter('index', intType), - _requiredParameter('value', eType), - ], - ), - _method( - 'add', - voidType, - parameters: [_requiredParameter('value', eType)], - ), - ]; - - _buildClassElement(listElement); - return listElement; - } - - ClassFragmentImpl get mapElement { - var mapElement = _mapElement; - if (mapElement != null) return mapElement; - - var kElement = _typeParameter('K'); - var vElement = _typeParameter('V'); - var kType = _typeParameterType(kElement); - var vType = _typeParameterType(vElement); - - _mapElement = - mapElement = _class( - name: 'Map', - isAbstract: true, - typeParameters: [kElement, vElement], - unit: _coreUnit, - ); - mapElement.supertype = objectType; - - _setGetters(mapElement, [_getter('length', intType)]); - - mapElement.methods = [ - _method('[]', vType, parameters: [_requiredParameter('key', objectType)]), - _method( - '[]=', - voidType, - parameters: [ - _requiredParameter('key', kType), - _requiredParameter('value', vType), - ], - ), - ]; - - _buildClassElement(mapElement); - return mapElement; - } - - ClassFragmentImpl get nullElement { - var nullElement = _nullElement; - if (nullElement != null) return nullElement; - - _nullElement = nullElement = _class(name: 'Null', unit: _coreUnit); - nullElement.supertype = objectType; - - nullElement.constructors = [ - _constructor(name: '_uninstantiatable', isFactory: true), - ]; - - _buildClassElement(nullElement); - return nullElement; - } - - ClassFragmentImpl get numElement { - var numElement = _numElement; - if (numElement != null) return numElement; - - _numElement = - numElement = _class(name: 'num', isAbstract: true, unit: _coreUnit); - numElement.supertype = objectType; - numElement.interfaces = [ - _interfaceType(comparableElement, typeArguments: [numType]), - ]; - - numElement.methods = [ - _method('+', numType, parameters: [_requiredParameter('other', numType)]), - _method('-', numType, parameters: [_requiredParameter('other', numType)]), - _method('*', numType, parameters: [_requiredParameter('other', numType)]), - _method('%', numType, parameters: [_requiredParameter('other', numType)]), - _method( - '/', - doubleType, - parameters: [_requiredParameter('other', numType)], - ), - _method( - '~/', - intType, - parameters: [_requiredParameter('other', numType)], - ), - _method('-', numType, parameters: [_requiredParameter('other', numType)]), - _method( - 'remainder', - numType, - parameters: [_requiredParameter('other', numType)], - ), - _method( - '<', - boolType, - parameters: [_requiredParameter('other', numType)], - ), - _method( - '<=', - boolType, - parameters: [_requiredParameter('other', numType)], - ), - _method( - '>', - boolType, - parameters: [_requiredParameter('other', numType)], - ), - _method( - '>=', - boolType, - parameters: [_requiredParameter('other', numType)], - ), - _method( - '==', - boolType, - parameters: [_requiredParameter('other', objectType)], - ), - _method('abs', numType), - _method('floor', numType), - _method('ceil', numType), - _method('round', numType), - _method('truncate', numType), - _method('toInt', intType), - _method('toDouble', doubleType), - _method( - 'toStringAsFixed', - stringType, - parameters: [_requiredParameter('fractionDigits', intType)], - ), - _method( - 'toStringAsExponential', - stringType, - parameters: [_requiredParameter('fractionDigits', intType)], - ), - _method( - 'toStringAsPrecision', - stringType, - parameters: [_requiredParameter('precision', intType)], - ), - ]; - - _setGetters(numElement, [ - _getter('isInfinite', boolType), - _getter('isNaN', boolType), - _getter('isNegative', boolType), - ]); - - _buildClassElement(numElement); - return numElement; - } - - InterfaceTypeImpl get numType { - return _numType ??= _interfaceType(numElement); - } - - ClassFragmentImpl get objectElement { - var objectElement = _objectElement; - if (objectElement != null) return objectElement; - - _objectElement = objectElement = _class(name: 'Object', unit: _coreUnit); - _coreUnit.encloseElement(objectElement); - objectElement.interfaces = const <InterfaceType>[]; - objectElement.mixins = const <InterfaceType>[]; - objectElement.typeParameters = const <TypeParameterFragmentImpl>[]; - objectElement.constructors = [_constructor(isConst: true)]; - - objectElement.methods = [ - _method('toString', stringType), - _method( - '==', - boolType, - parameters: [_requiredParameter('other', objectType)], - ), - _method( - 'noSuchMethod', - dynamicType, - parameters: [_requiredParameter('other', dynamicType)], - ), - ]; - - _setGetters(objectElement, [ - _getter('hashCode', intType), - _getter('runtimeType', typeType), - ]); - - _buildClassElement(objectElement); - return objectElement; - } - - InterfaceTypeImpl get objectType { - return _objectType ??= _interfaceType(objectElement); - } - - ClassFragmentImpl get overrideElement { - var overrideElement = _overrideElement; - if (overrideElement != null) return overrideElement; - - _overrideElement = - overrideElement = _class(name: '_Override', unit: _coreUnit); - overrideElement.supertype = objectType; - - overrideElement.constructors = [_constructor(isConst: true)]; - - _buildClassElement(overrideElement); - return overrideElement; - } - - ClassFragmentImpl get recordElement { - var recordElement = _recordElement; - if (recordElement != null) return recordElement; - - _recordElement = - recordElement = _class( - name: 'Record', - isAbstract: true, - unit: _coreUnit, - ); - recordElement.supertype = objectType; - - _buildClassElement(recordElement); - return recordElement; - } - - InterfaceTypeImpl get recordType { - return _interfaceType(recordElement); - } - - ClassFragmentImpl get setElement { - var setElement = _setElement; - if (setElement != null) return setElement; - - var eElement = _typeParameter('E'); - var eType = _typeParameterType(eElement); - - _setElement = - setElement = _class( - name: 'Set', - isAbstract: true, - typeParameters: [eElement], - unit: _coreUnit, - ); - setElement.supertype = objectType; - setElement.interfaces = [iterableType(eType)]; - - _buildClassElement(setElement); - return setElement; - } - - ClassFragmentImpl get stackTraceElement { - var stackTraceElement = _stackTraceElement; - if (stackTraceElement != null) return stackTraceElement; - - _stackTraceElement = - stackTraceElement = _class( - name: 'StackTrace', - isAbstract: true, - unit: _coreUnit, - ); - stackTraceElement.supertype = objectType; - - _buildClassElement(stackTraceElement); - return stackTraceElement; - } - - ClassFragmentImpl get streamElement { - var streamElement = _streamElement; - if (streamElement != null) return streamElement; - - var tElement = _typeParameter('T'); - var tType = _typeParameterType(tElement); - - _streamElement = - streamElement = _class( - name: 'Stream', - isAbstract: true, - typeParameters: [tElement], - unit: _asyncUnit, - ); - streamElement.isAbstract = true; - streamElement.supertype = objectType; - - // StreamSubscription<T> listen(void onData(T event), - // {Function onError, void onDone(), bool cancelOnError}); - streamElement.methods = [ - _method( - 'listen', - streamSubscriptionType(tType), - parameters: [ - _requiredParameter( - 'onData', - _functionType( - returnType: voidType, - parameters: [_requiredParameter('event', tType)], - ), - ), - _namedParameter('onError', functionType), - _namedParameter('onDone', _functionType(returnType: voidType)), - _namedParameter('cancelOnError', boolType), - ], - ), - ]; - - _buildClassElement(streamElement); - return streamElement; - } - - ClassFragmentImpl get streamSubscriptionElement { - var streamSubscriptionElement = _streamSubscriptionElement; - if (streamSubscriptionElement != null) return streamSubscriptionElement; - - var tElement = _typeParameter('T'); - _streamSubscriptionElement = - streamSubscriptionElement = _class( - name: 'StreamSubscription', - isAbstract: true, - typeParameters: [tElement], - unit: _asyncUnit, - ); - streamSubscriptionElement.supertype = objectType; - - _buildClassElement(streamSubscriptionElement); - return streamSubscriptionElement; - } - - ClassFragmentImpl get stringElement { - var stringElement = _stringElement; - if (stringElement != null) return stringElement; - - _stringElement = - stringElement = _class( - name: 'String', - isAbstract: true, - unit: _coreUnit, - ); - stringElement.supertype = objectType; - - stringElement.constructors = [ - _constructor( - name: 'fromEnvironment', - isConst: true, - isFactory: true, - parameters: [ - _requiredParameter('name', stringType), - _namedParameter('defaultValue', stringType), - ], - ), - ]; - - _setGetters(stringElement, [ - _getter('isEmpty', boolType), - _getter('length', intType), - _getter('codeUnits', listType(intType)), - ]); - - stringElement.methods = [ - _method( - '+', - stringType, - parameters: [_requiredParameter('other', stringType)], - ), - _method('toLowerCase', stringType), - _method('toUpperCase', stringType), - ]; - - _buildClassElement(stringElement); - return stringElement; - } - - InterfaceTypeImpl get stringType { - return _stringType ??= _interfaceType(stringElement); - } - - ClassFragmentImpl get symbolElement { - var symbolElement = _symbolElement; - if (symbolElement != null) return symbolElement; - - _symbolElement = - symbolElement = _class( - name: 'Symbol', - isAbstract: true, - unit: _coreUnit, - ); - symbolElement.supertype = objectType; - - symbolElement.constructors = [ - _constructor( - isConst: true, - isFactory: true, - parameters: [_requiredParameter('name', stringType)], - ), - ]; - - _buildClassElement(symbolElement); - return symbolElement; - } - - ClassFragmentImpl get typeElement { - var typeElement = _typeElement; - if (typeElement != null) return typeElement; - - _typeElement = - typeElement = _class(name: 'Type', isAbstract: true, unit: _coreUnit); - typeElement.supertype = objectType; - - _buildClassElement(typeElement); - return typeElement; - } - - InterfaceTypeImpl get typeType { - return _typeType ??= _interfaceType(typeElement); - } - - VoidTypeImpl get voidType => VoidTypeImpl.instance; - - InterfaceTypeImpl futureOrType(TypeImpl elementType) { - return _interfaceType(futureOrElement, typeArguments: [elementType]); - } - - InterfaceTypeImpl futureType(TypeImpl elementType) { - return _interfaceType(futureElement, typeArguments: [elementType]); - } - - InterfaceTypeImpl iterableType(TypeImpl elementType) { - return _interfaceType(iterableElement, typeArguments: [elementType]); - } - - InterfaceTypeImpl iteratorType(TypeImpl elementType) { - return _interfaceType(iteratorElement, typeArguments: [elementType]); - } - - InterfaceTypeImpl listType(TypeImpl elementType) { - return _interfaceType(listElement, typeArguments: [elementType]); - } - - InterfaceTypeImpl streamSubscriptionType(TypeImpl valueType) { - return _interfaceType( - streamSubscriptionElement, - typeArguments: [valueType], - ); - } - - LibraryElementImpl _buildAsync() { - var asyncSource = analysisContext.sourceFactory.forUri('dart:async')!; - _asyncLibrary = LibraryElementImpl( + var libraries = buildLibrariesFromSpec( analysisContext, + rootReference, analysisSession, - 'dart.async', - 0, - 0, - FeatureSet.latestLanguageVersion(), + _sdkSpec, ); - - _asyncUnit = LibraryFragmentImpl( - library: _asyncLibrary, - source: asyncSource, - lineInfo: LineInfo([0]), - ); - - _asyncLibrary.definingCompilationUnit = _asyncUnit; - return _asyncLibrary; - } - - void _buildClassElement(ClassFragmentImpl classFragment) { - var classElement = classFragment.element; - classElement.methods = classFragment.methods.map((f) => f.element).toList(); - classElement.constructors = - classFragment.constructors.map((f) => f.element).toList(); - // TODO(scheglov): other members - // classElement.fields = classFragment.fields.map((f) => f.element).toList(); - // classElement.getters = classFragment.getters.map((f) => f.element).toList(); - // classElement.setters = classFragment.setters.map((f) => f.element).toList(); - } - - LibraryElementImpl _buildCore() { - var coreSource = analysisContext.sourceFactory.forUri('dart:core')!; - _coreLibrary = LibraryElementImpl( - analysisContext, - analysisSession, - 'dart.core', - 0, - 0, - FeatureSet.latestLanguageVersion(), - ); - - _coreUnit = LibraryFragmentImpl( - library: _coreLibrary, - source: coreSource, - lineInfo: LineInfo([0]), - ); - - _coreLibrary.definingCompilationUnit = _coreUnit; - return _coreLibrary; - } - - ClassFragmentImpl _class({ - required String name, - bool isAbstract = false, - List<TypeParameterElementImpl> typeParameters = const [], - required LibraryFragmentImpl unit, - }) { - var fragment = ClassFragmentImpl(name: name); - ClassElementImpl(Reference.root(), fragment); - fragment.typeParameters = - typeParameters.map((tp) => tp.firstFragment).toList(); - fragment.constructors = <ConstructorFragmentImpl>[_constructor()]; - unit.encloseElement(fragment); - return fragment; - } - - ConstructorFragmentImpl _constructor({ - String name = 'new', - bool isConst = false, - bool isFactory = false, - List<FormalParameterElement> parameters = const [], - }) { - var fragment = ConstructorFragmentImpl(name: name); - fragment.isFactory = isFactory; - fragment.isConst = isConst; - fragment.formalParameters = - parameters - .map((p) => p.firstFragment as FormalParameterFragmentImpl) - .toList(); - - ConstructorElementImpl( - name: fragment.name, - reference: Reference.root(), - firstFragment: fragment, - ); - - return fragment; - } - - FieldFragmentImpl _field( - String name, - TypeImpl type, { - bool isConst = false, - bool isFinal = false, - bool isStatic = false, - }) { - var fragment = FieldFragmentImpl(name: name); - var element = FieldElementImpl( - reference: Reference.root(), - firstFragment: fragment, - ); - fragment.isConst = isConst; - fragment.isFinal = isFinal; - fragment.isStatic = isStatic; - - var getterFragment = GetterFragmentImpl(name: name)..isSynthetic = true; - var getterElement = GetterElementImpl(Reference.root(), getterFragment); - element.getter = getterElement; - - if (!isConst && !isFinal) { - var valueFragment = FormalParameterFragmentImpl( - name: null, - nameOffset: null, - parameterKind: ParameterKind.REQUIRED, - ); - var setterFragment = - SetterFragmentImpl(name: name) - ..isSynthetic = true - ..formalParameters = [valueFragment]; - var setterElement = SetterElementImpl(Reference.root(), setterFragment); - element.setter = setterElement; - } - - element.type = type; - return fragment; - } - - TopLevelFunctionFragmentImpl _function( - String name, - TypeImpl returnType, { - List<TypeParameterFragmentImpl> typeFormals = const [], - List<FormalParameterElement> parameters = const [], - }) { - var fragment = - TopLevelFunctionFragmentImpl(name: name) - ..formalParameters = - parameters - .map((p) => p.firstFragment as FormalParameterFragmentImpl) - .toList() - ..typeParameters = typeFormals; - - var element = TopLevelFunctionElementImpl(Reference.root(), fragment); - element.returnType = returnType; - - return fragment; - } - - FunctionTypeImpl _functionType({ - required TypeImpl returnType, - List<TypeParameterElementImpl> typeFormals = const [], - List<FormalParameterElement> parameters = const [], - }) { - return FunctionTypeImpl.v2( - typeParameters: typeFormals, - formalParameters: parameters.cast(), - returnType: returnType, - nullabilitySuffix: NullabilitySuffix.none, + return MockSdkElements._( + coreLibrary: libraries['dart:core']!, + asyncLibrary: libraries['dart:async']!, ); } - GetterFragmentImpl _getter( - String name, - TypeImpl type, { - bool isStatic = false, - }) { - var fieldFragment = FieldFragmentImpl(name: name); - var fieldElement = FieldElementImpl( - reference: Reference.root(), - firstFragment: fieldFragment, - ); - fieldFragment.isStatic = isStatic; - fieldFragment.isSynthetic = true; - fieldElement.type = type; - - var getterFragment = GetterFragmentImpl(name: name); - getterFragment.isStatic = isStatic; - getterFragment.isSynthetic = false; - - var getterElement = GetterElementImpl(Reference.root(), getterFragment); - fieldElement.getter = getterElement; - getterElement.variable = fieldElement; - getterElement.returnType = type; - - return getterFragment; - } - - InterfaceTypeImpl _interfaceType( - InterfaceFragmentImpl element, { - List<TypeImpl> typeArguments = const [], - }) { - return InterfaceTypeImpl( - element: element.element, - typeArguments: typeArguments, - nullabilitySuffix: NullabilitySuffix.none, - ); - } - - MethodFragmentImpl _method( - String name, - TypeImpl returnType, { - List<TypeParameterFragmentImpl> typeFormals = const [], - List<FormalParameterElement> parameters = const [], - }) { - var fragment = - MethodFragmentImpl(name: name) - ..formalParameters = - parameters - .map((p) => p.firstFragment as FormalParameterFragmentImpl) - .toList() - ..typeParameters = typeFormals; - - var element = MethodElementImpl( - name: name, - reference: Reference.root(), - firstFragment: fragment, - ); - element.returnType = returnType; - - return fragment; - } - - FormalParameterElement _namedParameter(String name, TypeImpl type) { - var fragment = FormalParameterFragmentImpl( - name: name, - nameOffset: 0, - parameterKind: ParameterKind.NAMED, - ); - return FormalParameterElementImpl(fragment)..type = type; - } - - void _populateAsync() { - _asyncUnit.classes = <ClassFragmentImpl>[ - completerElement, - futureElement, - futureOrElement, - streamElement, - streamSubscriptionElement, - ]; - - _fillLibraryFromFragment(_asyncLibrary, _asyncUnit); - } - - void _populateCore() { - _coreUnit.classes = <ClassFragmentImpl>[ - boolElement, - comparableElement, - deprecatedElement, - doubleElement, - functionElement, - intElement, - iterableElement, - iteratorElement, - listElement, - mapElement, - nullElement, - numElement, - objectElement, - overrideElement, - recordElement, - setElement, - stackTraceElement, - stringElement, - symbolElement, - typeElement, - ]; - - _coreUnit.functions = <TopLevelFunctionFragmentImpl>[ - _function( - 'identical', - boolType, - parameters: [ - _requiredParameter('a', objectType), - _requiredParameter('b', objectType), - ], - ), - _function( - 'print', - voidType, - parameters: [_requiredParameter('object', objectType)], - ), - ]; - - var deprecatedVariable = _topLevelVariableConst( - 'deprecated', - _interfaceType(deprecatedElement), - ); - - var overrideVariable = _topLevelVariableConst( - 'override', - _interfaceType(overrideElement), - ); - - _coreUnit.getters = <GetterFragmentImpl>[ - deprecatedVariable.element.getter!.firstFragment, - overrideVariable.element.getter!.firstFragment, - ]; - - _coreUnit.topLevelVariables = <TopLevelVariableFragmentImpl>[ - deprecatedVariable, - overrideVariable, - ]; - - _fillLibraryFromFragment(_coreLibrary, _coreUnit); - } - - FormalParameterElement _positionalParameter(String name, TypeImpl type) { - var fragment = FormalParameterFragmentImpl( - name: name, - nameOffset: 0, - parameterKind: ParameterKind.POSITIONAL, - ); - return FormalParameterElementImpl(fragment)..type = type; - } - - FormalParameterElement _requiredParameter(String name, TypeImpl type) { - var fragment = FormalParameterFragmentImpl( - name: name, - nameOffset: 0, - parameterKind: ParameterKind.REQUIRED, - ); - return FormalParameterElementImpl(fragment)..type = type; - } - - /// Set the [getters] and the corresponding fields for the [classElement]. - void _setGetters( - ClassFragmentImpl classElement, - List<GetterFragmentImpl> getters, - ) { - classElement.getters = getters; - classElement.fields = - getters - .map((accessor) => accessor.element.variable.firstFragment) - .cast<FieldFragmentImpl>() - .toList(); - } - - TopLevelVariableFragmentImpl _topLevelVariableConst( - String name, - TypeImpl type, - ) { - var fragment = TopLevelVariableFragmentImpl(name: name)..isConst = true; - var element = TopLevelVariableElementImpl(Reference.root(), fragment); - var getterFragment = GetterFragmentImpl(name: name)..isSynthetic = true; - var getterElement = GetterElementImpl(Reference.root(), getterFragment); - element.getter = getterElement; - element.type = type; - return fragment; - } - - TypeParameterElementImpl _typeParameter(String name) { - return TypeParameterElementImpl( - firstFragment: TypeParameterFragmentImpl(name: name), - name: name.nullIfEmpty, - ); - } - - TypeParameterTypeImpl _typeParameterType(TypeParameterElementImpl element) { - return TypeParameterTypeImpl( - element: element, - nullabilitySuffix: NullabilitySuffix.none, - ); - } - - static void _fillLibraryFromFragment( - LibraryElementImpl library, - LibraryFragmentImpl fragment, - ) { - library.classes = fragment.classes.map((f) => f.element).toList(); - - library.topLevelFunctions = - fragment.functions.map((f) => f.element).toList(); - - library.topLevelVariables = - fragment.topLevelVariables.map((f) => f.element).toList(); - } + MockSdkElements._({required this.coreLibrary, required this.asyncLibrary}); }
diff --git a/pkg/analyzer/lib/src/test_utilities/test_library_builder.dart b/pkg/analyzer/lib/src/test_utilities/test_library_builder.dart new file mode 100644 index 0000000..8c62bcf --- /dev/null +++ b/pkg/analyzer/lib/src/test_utilities/test_library_builder.dart
@@ -0,0 +1,723 @@ +// Copyright (c) 2025, 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/dart/analysis/features.dart'; +import 'package:analyzer/dart/element/nullability_suffix.dart'; +import 'package:analyzer/source/line_info.dart'; +import 'package:analyzer/src/dart/analysis/session.dart'; +import 'package:analyzer/src/dart/element/element.dart'; +import 'package:analyzer/src/dart/element/type.dart'; +import 'package:analyzer/src/generated/engine.dart' as engine; +import 'package:analyzer/src/generated/utilities_dart.dart'; +import 'package:analyzer/src/summary2/reference.dart'; + +/// Creates a set of mock libraries from the given specifications. +Map<String, LibraryElementImpl> buildLibrariesFromSpec( + engine.AnalysisContext analysisContext, + Reference rootReference, + AnalysisSessionImpl analysisSession, + Map<String, LibrarySpec> specs, +) { + var builder = _LibraryBuilder( + rootReference, + analysisContext, + analysisSession, + specs, + ); + return builder.build(); +} + +class ClassSpec { + final String name; + final List<String> typeParameters; + final String? supertype; + final List<String> interfaces; + final List<ConstructorSpec> constructors; + final List<MethodSpec> methods; + final bool isAbstract; + + const ClassSpec({ + required this.name, + this.typeParameters = const [], + this.supertype, + this.interfaces = const [], + this.constructors = const [], + this.methods = const [], + this.isAbstract = false, + }); +} + +class ConstructorSpec { + final String name; + final String formalParameters; + final bool isConst; + final bool isFactory; + + const ConstructorSpec({ + this.name = '', + this.formalParameters = '', + this.isConst = false, + this.isFactory = false, + }); +} + +class FunctionSpec { + final String name; + final List<String> typeParameters; + final String formalParameters; + final String returnType; + + const FunctionSpec({ + required this.name, + this.typeParameters = const [], + this.formalParameters = '', + required this.returnType, + }); +} + +class LibrarySpec { + final String uri; + final List<ClassSpec> classes; + final List<FunctionSpec> functions; + + const LibrarySpec({ + required this.uri, + this.classes = const [], + this.functions = const [], + }); +} + +class MethodSpec { + final String name; + final List<String> typeParameters; + final String formalParameters; + final String returnType; + + const MethodSpec({ + required this.name, + this.typeParameters = const [], + this.formalParameters = '', + required this.returnType, + }); +} + +/// Builds a set of libraries from a collection of [LibrarySpec]s. +/// +/// The builder uses a two-pass process: +/// +/// 1. **Shell Creation**: Create all `LibraryElementImpl`, `ClassElementImpl`, +/// etc "shells". This populates the namespace so that type lookups can +/// succeed across library boundaries in the next step. +/// +/// 2. **Element Population**: Fill in the details of each element, such as +/// supertypes, interfaces, methods, and parameters. This step resolves +/// all type strings using the shells created in the first pass. +/// +/// This two-pass approach allows for resolving inter-library dependencies and +/// cycles (e.g., `dart:core` and `dart:async` referencing each other). +class _LibraryBuilder { + final Reference rootReference; + final engine.AnalysisContext analysisContext; + final AnalysisSessionImpl analysisSession; + final Map<String, LibrarySpec> specs; + + final Map<String, LibraryElementImpl> _libraryElements = {}; + final Map<String, LibraryFragmentImpl> _libraryFragments = {}; + final Map<String, ClassElementImpl> _classElements = {}; + + late final _Scope _rootScope; + final _TypeParser _typeParser = _TypeParser(); + + _LibraryBuilder( + this.rootReference, + this.analysisContext, + this.analysisSession, + this.specs, + ) { + _rootScope = _Scope.root(_classElements); + } + + /// Builds all libraries specified in [specs] and returns them in a map. + Map<String, LibraryElementImpl> build() { + _buildElementShells(); + _populateElements(); + return _libraryElements; + } + + /// Create empty shells for all libraries and elements. + void _buildElementShells() { + for (var libSpec in specs.values) { + var libraryUriStr = libSpec.uri; + var librarySource = analysisContext.sourceFactory.forUri(libraryUriStr)!; + var libraryElement = LibraryElementImpl( + analysisContext, + analysisSession, + libraryUriStr.replaceAll(':', '.'), + 0, + 0, + FeatureSet.latestLanguageVersion(), + ); + var libraryFragment = LibraryFragmentImpl( + library: libraryElement, + source: librarySource, + lineInfo: LineInfo([0]), + ); + libraryElement.definingCompilationUnit = libraryFragment; + _libraryElements[libraryUriStr] = libraryElement; + _libraryFragments[libraryUriStr] = libraryFragment; + + for (var classSpec in libSpec.classes) { + var fragment = ClassFragmentImpl(name: classSpec.name); + var element = ClassElementImpl( + rootReference.getChild('@class').getChild(classSpec.name), + fragment, + ); + + libraryFragment.encloseElement(fragment); + libraryElement.addClass(element); + _classElements[classSpec.name] = element; + } + } + } + + ConstructorFragmentImpl _createConstructorFragment( + ConstructorSpec spec, { + required ClassElementImpl classElement, + required _Scope classScope, + }) { + var fragment = ConstructorFragmentImpl(name: spec.name); + fragment.isConst = spec.isConst; + fragment.isFactory = spec.isFactory; + + var element = ConstructorElementImpl( + name: spec.name, + reference: classElement.reference + .getChild('@constructor') + .getChild(spec.name), + firstFragment: fragment, + ); + classElement.addConstructor(element); + + var formalParameters = _typeParser.parseFormalParameters( + classScope, + spec.formalParameters, + ); + fragment.formalParameters = + formalParameters.map((formalParameterElement) { + return formalParameterElement.firstFragment; + }).toList(); + + return fragment; + } + + TopLevelFunctionFragmentImpl _createFunctionFragment( + LibraryElementImpl library, + FunctionSpec spec, + ) { + var fragment = TopLevelFunctionFragmentImpl(name: spec.name); + var scope = _Scope.child(_rootScope); + + var element = TopLevelFunctionElementImpl( + rootReference.getChild('@function').getChild(spec.name), + fragment, + ); + library.addTopLevelFunction(element); + + for (var name in spec.typeParameters) { + var tpElement = _createTypeParameterElement(name); + scope.addTypeParameter(name, tpElement); + fragment.typeParameters.add(tpElement.firstFragment); + } + + var formalParameterElements = _typeParser.parseFormalParameters( + scope, + spec.formalParameters, + ); + fragment.formalParameters = + formalParameterElements.map((formalParameterElement) { + return formalParameterElement.firstFragment; + }).toList(); + + element.returnType = _typeParser.parse(spec.returnType, scope); + return fragment; + } + + MethodFragmentImpl _createMethodFragment( + ClassElementImpl classElement, + MethodSpec spec, + _Scope parentScope, + ) { + var fragment = MethodFragmentImpl(name: spec.name); + var scope = _Scope.child(parentScope); + + var element = MethodElementImpl( + name: spec.name, + reference: classElement.reference.getChild('@method').getChild(spec.name), + firstFragment: fragment, + ); + + fragment.typeParameters = + spec.typeParameters.map((name) { + var tpElement = _createTypeParameterElement(name); + scope.addTypeParameter(name, tpElement); + return tpElement.firstFragment; + }).toList(); + + var formalParameterElements = _typeParser.parseFormalParameters( + scope, + spec.formalParameters, + ); + fragment.formalParameters = + formalParameterElements.map((formalParameterElement) { + return formalParameterElement.firstFragment; + }).toList(); + + element.returnType = _typeParser.parse(spec.returnType, scope); + return fragment; + } + + TypeParameterElementImpl _createTypeParameterElement(String name) { + var fragment = TypeParameterFragmentImpl(name: name); + return TypeParameterElementImpl(firstFragment: fragment, name: name); + } + + void _populateClasses(LibrarySpec libSpec) { + for (var classSpec in libSpec.classes) { + var element = _classElements[classSpec.name]!; + var fragment = element.firstFragment; + var scope = _Scope.child(_rootScope); + + fragment.typeParameters = + classSpec.typeParameters.map((name) { + var tpElement = _createTypeParameterElement(name); + scope.addTypeParameter(name, tpElement); + return tpElement.firstFragment; + }).toList(); + + if (classSpec.supertype case var supertypeStr?) { + var supertype = _typeParser.parse(supertypeStr, scope); + fragment.supertype = supertype as InterfaceTypeImpl?; + } + + fragment.interfaces = + classSpec.interfaces.map((interfaceStr) { + var interface = _typeParser.parse(interfaceStr, scope); + return interface as InterfaceTypeImpl; + }).toList(); + + for (var constructorSpec in classSpec.constructors) { + var constructorFragment = _createConstructorFragment( + classElement: element, + classScope: scope, + constructorSpec, + ); + fragment.addConstructor(constructorFragment); + } + + for (var methodSpec in classSpec.methods) { + var methodFragment = _createMethodFragment(element, methodSpec, scope); + fragment.addMethod(methodFragment); + } + + element.methods = fragment.methods.map((f) => f.element).toList(); + element.constructors = + fragment.constructors.map((f) => f.element).toList(); + } + } + + /// Populate the shells with types, methods, and other details. + void _populateElements() { + for (var libSpec in specs.values) { + _populateClasses(libSpec); + _populateTopLevelFunctions(libSpec); + } + } + + void _populateTopLevelFunctions(LibrarySpec libSpec) { + var libraryElement = _libraryElements[libSpec.uri]!; + var libraryFragment = _libraryFragments[libSpec.uri]!; + + for (var functionSpec in libSpec.functions) { + var functionFragment = _createFunctionFragment( + libraryElement, + functionSpec, + ); + libraryFragment.addFunction(functionFragment); + } + libraryElement.topLevelFunctions = + libraryFragment.functions.map((element) { + return element.element; + }).toList(); + } +} + +/// Representation of direct type like `void` or `dynamic`. +class _PreExplicitType implements _PreType { + final TypeImpl type; + + _PreExplicitType({required this.type}); + + @override + TypeImpl materialize(_Scope scope) => type; +} + +class _PreFormalParameter { + final _PreType type; + final String? name; + final ParameterKind kind; + + _PreFormalParameter({ + required this.type, + required this.name, + required this.kind, + }); + + FormalParameterElementImpl materialize(_Scope scope) { + var fragment = FormalParameterFragmentImpl( + name: name, + nameOffset: 0, + parameterKind: kind, + ); + + var element = FormalParameterElementImpl(fragment); + element.type = type.materialize(scope); + return element; + } +} + +class _PreFunctionType implements _PreType { + final List<_PreTypeParameter> typeParameters; + final List<_PreFormalParameter> formalParameters; + final _PreType returnType; + + _PreFunctionType({ + required this.typeParameters, + required this.formalParameters, + required this.returnType, + }); + + @override + TypeImpl materialize(_Scope scope) { + // Create a new scope for the function's own type parameters. + var functionScope = _Scope.child(scope); + + // Create elements for the function's own type parameters and add to scope. + var typeParameters = + this.typeParameters.map((pre) { + var fragment = TypeParameterFragmentImpl(name: pre.name); + var element = TypeParameterElementImpl( + firstFragment: fragment, + name: pre.name, + ); + functionScope.addTypeParameter(pre.name, element); + return element; + }).toList(); + + // Now that the type parameters are in scope, materialize their bounds. + for (var i = 0; i < typeParameters.length; i++) { + var pre = this.typeParameters[i]; + if (pre.bound case var bound?) { + typeParameters[i].bound = bound.materialize(functionScope); + } + } + + var returnType = this.returnType.materialize(functionScope); + var formalParameters = + this.formalParameters.map((p) => p.materialize(functionScope)).toList(); + + return FunctionTypeImpl.v2( + returnType: returnType, + typeParameters: typeParameters, + formalParameters: formalParameters, + nullabilitySuffix: NullabilitySuffix.none, + ); + } +} + +class _PreNamedType implements _PreType { + final String name; + final List<_PreType> args; + + _PreNamedType({required this.name, required this.args}); + + @override + TypeImpl materialize(_Scope scope) { + if (scope.lookupTypeParameter(name) case var element?) { + assert(args.isEmpty); + return TypeParameterTypeImpl( + element: element, + nullabilitySuffix: NullabilitySuffix.none, + ); + } + + var element = scope.lookupInterface(name); + if (element == null) throw StateError('Unknown type: $name'); + + return InterfaceTypeImpl( + element: element, + typeArguments: args.map((arg) => arg.materialize(scope)).toList(), + nullabilitySuffix: NullabilitySuffix.none, + ); + } +} + +/// Makes [inner] type nullable. +class _PreNullableType implements _PreType { + final _PreType inner; + + _PreNullableType({required this.inner}); + + @override + TypeImpl materialize(_Scope scope) { + return inner.materialize(scope).withNullability(NullabilitySuffix.question); + } +} + +/// Representation of a [TypeImpl] that has been parsed but hasn't had meaning +/// assigned to its identifiers yet. +abstract class _PreType { + /// Translates `this` into a [TypeImpl]. + /// + /// The meaning of identifiers in `this` is determined by looking them up + /// in the provided [scope]. + TypeImpl materialize(_Scope scope); +} + +class _PreTypeParameter { + final String name; + final _PreType? bound; + + _PreTypeParameter({required this.name, required this.bound}) + : assert(name.isNotEmpty); +} + +/// A unified scope for looking up named elements. +/// +/// It can look up type parameters from the current (and parent) scopes, +/// as well as named types (like classes and mixins) from the global scope. +class _Scope { + final _Scope? parent; + final Map<String, TypeParameterElementImpl> _typeParameters = {}; + final Map<String, InterfaceElementImpl> _interfaces; + + /// Creates a nested scope that inherits its parent's context. + /// + /// This is used to add type parameters of a class, method, etc. + _Scope.child(_Scope this.parent) : _interfaces = parent._interfaces; + + /// Creates a root scope containing all known interfaces. + _Scope.root(this._interfaces) : parent = null; + + /// Adds a type parameter to the current scope. + void addTypeParameter(String name, TypeParameterElementImpl element) { + _typeParameters[name] = element; + } + + /// Looks up an interface by name. + InterfaceElementImpl? lookupInterface(String name) { + return _interfaces[name]; + } + + /// Looks up a type parameter by name, walking up the scope chain. + TypeParameterElementImpl? lookupTypeParameter(String name) { + return _typeParameters[name] ?? parent?.lookupTypeParameter(name); + } +} + +class _TokenStream { + static final RegExp _tokenizer = RegExp( + r'[a-zA-Z_]\w*|<|>|,|\?|\(|\)|\{|\}|\[|\]|extends', + ); + + final List<String> _tokens; + int _index = 0; + + factory _TokenStream.fromString(String input) { + var tokens = _tokenizer.allMatches(input).map((m) => m.group(0)!).toList(); + return _TokenStream._(tokens); + } + + _TokenStream._(this._tokens); + + bool get isAtEnd => _index >= _tokens.length; + + String consume() { + if (isAtEnd) throw StateError('Unexpected end of token stream.'); + return _tokens[_index++]; + } + + void expect(String expected) { + if (isAtEnd) { + throw StateError('Expected "$expected" but found end of stream.'); + } + var token = consume(); + if (token != expected) { + throw StateError('Expected "$expected" but found "$token".'); + } + } + + bool match(String expected) { + if (peekIs(expected)) { + _index++; + return true; + } + return false; + } + + String peek() { + if (isAtEnd) throw StateError('Unexpected end of token stream.'); + return _tokens[_index]; + } + + bool peekIs(String token) { + return !isAtEnd && peek() == token; + } + + bool peekIsAnyOf(Set<String> tokens) { + return !isAtEnd && tokens.contains(peek()); + } +} + +class _TypeParser { + TypeImpl parse(String input, _Scope scope) { + var stream = _TokenStream.fromString(input); + var preType = _parseType(stream); + if (!stream.isAtEnd) { + throw StateError('Unexpected trailing tokens'); + } + return preType.materialize(scope); + } + + /// Parse formal parameters, without enclosing `()`. + List<FormalParameterElementImpl> parseFormalParameters( + _Scope scope, + String input, + ) { + var stream = _TokenStream.fromString(input); + var preList = _parseFormalParameters(stream); + return preList.map((pre) => pre.materialize(scope)).toList(); + } + + _PreFormalParameter _parseFormalParameter( + _TokenStream stream, + ParameterKind kind, + ) { + if (kind == ParameterKind.NAMED) { + kind = ParameterKind.NAMED_REQUIRED; + } + + var type = _parseType(stream); + + String? name; + if (!stream.isAtEnd && !stream.peekIsAnyOf(const {',', ')', ']', '}'})) { + name = stream.consume(); + } + + return _PreFormalParameter(type: type, name: name, kind: kind); + } + + /// Parse formal parameters, without enclosing `()`. + List<_PreFormalParameter> _parseFormalParameters(_TokenStream stream) { + var formalParameters = <_PreFormalParameter>[]; + if (stream.isAtEnd || stream.peekIs(')')) { + return formalParameters; + } + + // Parse required positional formal parameters. + while (!stream.isAtEnd && !stream.peekIsAnyOf(const {')', '[', '{'})) { + formalParameters.add( + _parseFormalParameter(stream, ParameterKind.REQUIRED), + ); + stream.match(','); + } + + // Parse optional positional formal parameters. + if (stream.match('[')) { + while (!stream.isAtEnd && !stream.peekIs(']')) { + formalParameters.add( + _parseFormalParameter(stream, ParameterKind.POSITIONAL), + ); + stream.match(','); + } + stream.expect(']'); + } + + // Parse named formal parameters. + if (stream.match('{')) { + while (!stream.isAtEnd && !stream.peekIs('}')) { + formalParameters.add( + _parseFormalParameter(stream, ParameterKind.NAMED), + ); + stream.match(','); + } + stream.expect('}'); + } + + return formalParameters; + } + + _PreType _parsePrimaryType(_TokenStream stream) { + var name = stream.consume(); + switch (name) { + case 'dynamic': + return _PreExplicitType(type: DynamicTypeImpl.instance); + case 'void': + return _PreExplicitType(type: VoidTypeImpl.instance); + } + + var args = <_PreType>[]; + if (stream.match('<')) { + while (!stream.isAtEnd && !stream.peekIs('>')) { + args.add(_parseType(stream)); + stream.match(','); + } + stream.expect('>'); + } + return _PreNamedType(name: name, args: args); + } + + _PreType _parseType(_TokenStream stream) { + var type = _parsePrimaryType(stream); + + // Check for function type. + if (stream.match('Function')) { + var typeParameters = _parseTypeParameters(stream); + + stream.expect('('); + var formalParameters = _parseFormalParameters(stream); + stream.expect(')'); + type = _PreFunctionType( + returnType: type, + formalParameters: formalParameters, + typeParameters: typeParameters, + ); + } + + if (stream.match('?')) { + return _PreNullableType(inner: type); + } + return type; + } + + List<_PreTypeParameter> _parseTypeParameters(_TokenStream stream) { + var typeParameters = <_PreTypeParameter>[]; + if (stream.isAtEnd || !stream.match('<')) { + return typeParameters; + } + + while (!stream.isAtEnd && !stream.peekIs('>')) { + var name = stream.consume(); + _PreType? bound; + if (stream.match('extends')) { + bound = _parseType(stream); + } + typeParameters.add(_PreTypeParameter(name: name, bound: bound)); + + stream.match(','); + } + + stream.expect('>'); + return typeParameters; + } +}
diff --git a/pkg/analyzer/lib/src/utilities/extensions/string.dart b/pkg/analyzer/lib/src/utilities/extensions/string.dart index f9cf549..07580c9 100644 --- a/pkg/analyzer/lib/src/utilities/extensions/string.dart +++ b/pkg/analyzer/lib/src/utilities/extensions/string.dart
@@ -160,6 +160,33 @@ } } + /// Converts `SCREAMING_SNAKE_CASE` or `snake_case` to `camelCase`. + String toCamelCase() { + var parts = toLowerCase().split('_'); + var buffer = StringBuffer(); + var i = 0; + // Preserve initial '_'s + while (i < parts.length - 1 && parts[i].isEmpty) { + buffer.write('_'); + ++i; + } + if (i < parts.length) { + // Convert first word to lower case + buffer.write(parts[i].toLowerCase()); + ++i; + // Convert remaining words to initial upper case + while (i < parts.length) { + var part = parts[i]; + if (part.isNotEmpty) { + buffer.write(part[0].toUpperCase()); + buffer.write(part.substring(1)); + } + ++i; + } + } + return buffer.toString(); + } + /// Converts `camelCase` / `PascalCase` to `SCREAMING_SNAKE_CASE`. /// Examples: /// - camelCase -> CAMEL_CASE
diff --git a/pkg/analyzer/pubspec.yaml b/pkg/analyzer/pubspec.yaml index e16325a..e9ddc21 100644 --- a/pkg/analyzer/pubspec.yaml +++ b/pkg/analyzer/pubspec.yaml
@@ -31,6 +31,7 @@ # See also https://dart.dev/tools/pub/dependencies. dev_dependencies: analysis_server_client: any + analyzer_plugin: any analyzer_testing: any analyzer_utilities: any args: any
diff --git a/pkg/analyzer/test/generated/test_analysis_context.dart b/pkg/analyzer/test/generated/test_analysis_context.dart index b791af6..e0a7469 100644 --- a/pkg/analyzer/test/generated/test_analysis_context.dart +++ b/pkg/analyzer/test/generated/test_analysis_context.dart
@@ -13,9 +13,12 @@ import 'package:analyzer/src/dart/element/type_system.dart'; import 'package:analyzer/src/generated/engine.dart' show AnalysisContext; import 'package:analyzer/src/generated/source.dart' show SourceFactory; +import 'package:analyzer/src/summary2/reference.dart'; import 'package:analyzer/src/test_utilities/mock_sdk_elements.dart'; class TestAnalysisContext implements AnalysisContext { + final Reference rootReference = Reference.root(); + @override final SourceFactory sourceFactory = _MockSourceFactory(); @@ -26,7 +29,7 @@ late TypeSystemImpl _typeSystem; TestAnalysisContext() { - var sdkElements = MockSdkElements(this, _analysisSession); + var sdkElements = MockSdkElements(this, rootReference, _analysisSession); _typeProvider = TypeProviderImpl( coreLibrary: sdkElements.coreLibrary,
diff --git a/pkg/analyzer/test/src/dart/constant/value_test.dart b/pkg/analyzer/test/src/dart/constant/value_test.dart index 0105c55..d029d49 100644 --- a/pkg/analyzer/test/src/dart/constant/value_test.dart +++ b/pkg/analyzer/test/src/dart/constant/value_test.dart
@@ -1152,7 +1152,7 @@ void test_identical_Type_functionType() { var toStringType = - _typeProvider.intType.methods + _typeProvider.objectType.methods .firstWhere((e) => e.name == 'toString') .type;
diff --git a/pkg/analyzer/test/src/utilities/extensions/string_test.dart b/pkg/analyzer/test/src/utilities/extensions/string_test.dart index 1fd022b..c00b3d6 100644 --- a/pkg/analyzer/test/src/utilities/extensions/string_test.dart +++ b/pkg/analyzer/test/src/utilities/extensions/string_test.dart
@@ -197,6 +197,18 @@ expect('01234'.removeSuffix('5'), isNull); } + void test_toCamelCase() { + expect('CAMEL_CASE'.toCamelCase(), 'camelCase'); + expect('alreadyCamel_case'.toCamelCase(), 'alreadycamelCase'); + expect('FOO_123_BAR'.toCamelCase(), 'foo123Bar'); + expect('FOO'.toCamelCase(), 'foo'); + expect('___'.toCamelCase(), '___'); + expect(''.toCamelCase(), ''); + expect('_FOO_BAR'.toCamelCase(), '_fooBar'); + expect('FOO__BAR'.toCamelCase(), 'fooBar'); + expect('FOO_BAR_'.toCamelCase(), 'fooBar'); + } + void test_toScreamingSnake() { expect('camelCase'.toScreamingSnake(), 'CAMEL_CASE'); expect('HTTPRequest'.toScreamingSnake(), 'HTTP_REQUEST');
diff --git a/pkg/analyzer/tool/messages/error_code_info.dart b/pkg/analyzer/tool/messages/error_code_info.dart index 6667cc0..e06cdd0 100644 --- a/pkg/analyzer/tool/messages/error_code_info.dart +++ b/pkg/analyzer/tool/messages/error_code_info.dart
@@ -34,6 +34,13 @@ file: codesFile, name: 'CompileTimeErrorCode', type: 'COMPILE_TIME_ERROR', + deprecatedSnakeCaseNames: { + 'IMPORT_INTERNAL_LIBRARY', // Referenced by `verify_docs.dart`. + 'INSTANCE_ACCESS_TO_STATIC_MEMBER', // Referenced by `messages.yaml`. + 'INVALID_OVERRIDE', // Referenced by `messages.yaml`. + 'TYPE_ARGUMENT_NOT_MATCHING_BOUNDS', // Referenced by `messages.yaml`. + 'UNDEFINED_CLASS', // Referenced by `messages.yaml`. + }, ), ErrorClassInfo( file: scannerErrorFile, @@ -51,19 +58,35 @@ name: 'WarningCode', type: 'STATIC_WARNING', severity: 'WARNING', + deprecatedSnakeCaseNames: { + 'UNUSED_ELEMENT', // Referenced by `verify_docs.dart`. + 'UNUSED_IMPORT', // Referenced by `verify_docs.dart`. + 'UNUSED_LOCAL_VARIABLE', // Referenced by `verify_docs.dart`. + }, ), ErrorClassInfo( file: ffiCodesFile, name: 'FfiCode', type: 'COMPILE_TIME_ERROR', ), - ErrorClassInfo(file: hintCodesFile, name: 'HintCode', type: 'HINT'), + ErrorClassInfo( + file: hintCodesFile, + name: 'HintCode', + type: 'HINT', + deprecatedSnakeCaseNames: { + 'UNNECESSARY_IMPORT', // Referenced by `verify_docs.dart`. + }, + ), ErrorClassInfo( file: syntacticErrorsFile, name: 'ParserErrorCode', type: 'SYNTACTIC_ERROR', severity: 'ERROR', includeCfeMessages: true, + deprecatedSnakeCaseNames: { + 'EXPERIMENT_NOT_ENABLED', // Referenced by `messages.yaml`. + 'UNEXPECTED_TOKEN', // Referenced by `package:dart_style`. + }, ), ErrorClassInfo( file: manifestWarningCodeFile, @@ -492,6 +515,11 @@ /// The type of errors in this class. final String type; + /// The names of any errors which are relied upon by analyzer clients, and + /// therefore will need their "snake case" form preserved (with a deprecation + /// notice) after migration to camel case error codes. + final Set<String> deprecatedSnakeCaseNames; + const ErrorClassInfo({ this.extraImports = const [], required this.file, @@ -500,6 +528,7 @@ this.severity, this.superclass = 'DiagnosticCode', required this.type, + this.deprecatedSnakeCaseNames = const {}, }); /// Generates the code to compute the severity of errors of this class.
diff --git a/pkg/analyzer/tool/messages/generate.dart b/pkg/analyzer/tool/messages/generate.dart index c68111a..86e3e5d 100644 --- a/pkg/analyzer/tool/messages/generate.dart +++ b/pkg/analyzer/tool/messages/generate.dart
@@ -19,6 +19,7 @@ import 'dart:convert'; import 'dart:io'; +import 'package:analyzer/src/utilities/extensions/string.dart'; import 'package:analyzer_testing/package_root.dart' as pkg_root; import 'package:analyzer_utilities/tools.dart'; import 'package:collection/collection.dart'; @@ -34,6 +35,14 @@ ..printSummary(); } +/// Whether the error constants that should be generated should use camel-case +/// names. +/// +/// This is a temporary flag to allow the codebase to be transitioned to using +/// camel-case error constants. TODO(paulberry): once the transition is +/// complete, remove this constant. +const _useLowerCamelCaseNames = false; + /// A list of all targets generated by this code generator. final List<GeneratedContent> allTargets = _analyzerGeneratedFiles(); @@ -44,7 +53,7 @@ for (var errorClassInfo in errorClasses) { (classesByFile[errorClassInfo.file] ??= []).add(errorClassInfo); } - var generatedCodes = <String>[]; + var generatedCodes = <(String, String)>[]; return [ for (var entry in classesByFile.entries) GeneratedFile(entry.key.path, (pkgRoot) async { @@ -72,7 +81,7 @@ final List<ErrorClassInfo> errorClasses; - final List<String> generatedCodes; + final List<(String, String)> generatedCodes; final StringBuffer out = StringBuffer(''' // Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file @@ -154,8 +163,10 @@ ); out.writeln('${errorCodeInfo.aliasFor};'); } else { - generatedCodes.add('${errorClass.name}.$errorName'); - out.writeln(' static const ${errorClass.name} $errorName ='); + generatedCodes.add((errorClass.name, errorName)); + var constantName = + _useLowerCamelCaseNames ? errorName.toCamelCase() : errorName; + out.writeln(' static const ${errorClass.name} $constantName ='); out.writeln( errorCodeInfo.toAnalyzerCode( errorClass, @@ -163,6 +174,15 @@ useExplicitConst: file.shouldUseExplicitConst, ), ); + + if (_useLowerCamelCaseNames && + errorClass.deprecatedSnakeCaseNames.contains(errorName)) { + out.writeln(); + out.writeln(' @Deprecated("Please use $constantName")'); + out.writeln( + ' static const ${errorClass.name} $errorName = $constantName;', + ); + } } } catch (e, st) { Error.throwWithStackTrace( @@ -205,6 +225,9 @@ out.writeln('final fastaAnalyzerErrorCodes = <DiagnosticCode?>['); for (var entry in cfeToAnalyzerErrorCodeTables.indexToInfo) { var name = cfeToAnalyzerErrorCodeTables.infoToAnalyzerCode[entry]; + if (_useLowerCamelCaseNames) { + name = name?.toCamelCase(); + } out.writeln('${name == null ? 'null' : 'ParserErrorCode.$name'},'); } out.writeln('];'); @@ -212,7 +235,7 @@ } class _DiagnosticCodeValuesGenerator { - final List<String> generatedCodes; + final List<(String, String)> generatedCodes; final StringBuffer out = StringBuffer(''' // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file @@ -239,12 +262,12 @@ // The scanner error codes are not yet being generated, so we need to add // them to the list explicitly. generatedCodes.addAll([ - 'TodoCode.TODO', - 'TodoCode.FIXME', - 'TodoCode.HACK', - 'TodoCode.UNDONE', + ('TodoCode', 'TODO'), + ('TodoCode', 'FIXME'), + ('TodoCode', 'HACK'), + ('TodoCode', 'UNDONE'), ]); - generatedCodes.sort(); + generatedCodes.sortBy((x) => '${x.$1}.${x.$2}'); out.writeln(); out.writeln(r''' @@ -261,8 +284,11 @@ "@AnalyzerPublicApi(message: 'exported by lib/error/error.dart')", ); out.writeln('const List<DiagnosticCode> diagnosticCodeValues = ['); - for (var name in generatedCodes) { - out.writeln(' $name,'); + for (var (className, errorName) in generatedCodes) { + if (_useLowerCamelCaseNames) { + errorName = errorName.toCamelCase(); + } + out.writeln(' $className.$errorName,'); } out.writeln('];'); out.writeln();
diff --git a/pkg/analyzer/tool/messages/rename_error_constants.dart b/pkg/analyzer/tool/messages/rename_error_constants.dart new file mode 100644 index 0000000..b3e2df0 --- /dev/null +++ b/pkg/analyzer/tool/messages/rename_error_constants.dart
@@ -0,0 +1,152 @@ +// Copyright (c) 2025, 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. + +/// Temporary script to convert the analyzer's error message constants to +/// camelCase. +/// +/// This script analyzes `pkg/analyzer` (and related packages), and renames +/// every static const error code declaration from SCREAMING_CAPS format to +/// camelCase format. It also flips the constant `_useLowerCamelCaseNames` +/// (in `messages/generate.dart`) from `false` to `true`, so that error message +/// code generation will start generating error codes in camelCase format. +/// +/// This script will be run once to change the format of all the analyzer error +/// codes, and then it will be removed from the codebase. +library; + +// TODO(paulberry): delete this script once it is no longer needed. + +import 'dart:io'; + +import 'package:analyzer/dart/analysis/analysis_context_collection.dart'; +import 'package:analyzer/dart/analysis/results.dart'; +import 'package:analyzer/dart/ast/ast.dart'; +import 'package:analyzer/dart/ast/visitor.dart'; +import 'package:analyzer/dart/element/element.dart'; +import 'package:analyzer/dart/element/type_system.dart'; +import 'package:analyzer/file_system/physical_file_system.dart'; +import 'package:analyzer/src/utilities/extensions/string.dart'; +import 'package:analyzer_plugin/protocol/protocol_common.dart' hide Element; +import 'package:analyzer_testing/package_root.dart'; +import 'package:collection/collection.dart'; +import 'package:path/path.dart' as path; + +void main() async { + var provider = PhysicalResourceProvider.INSTANCE; + var packages = [ + '_fe_analyzer_shared', + 'analyzer', + 'analysis_server', + 'analysis_server_plugin', + 'analyzer_cli', + 'analyzer_plugin', + 'analyzer_testing', + 'front_end', + 'linter', + ]; + var collection = AnalysisContextCollection( + includedPaths: [ + for (var package in packages) path.join(packageRoot, package), + ], + resourceProvider: provider, + ); + // Use `.single` to make sure that `collection` just contains a single + // context. This ensures that `publicApi.build` will see all the files in + // the package. + var context = collection.contexts.single; + var errorsLibrary = + ((await context.currentSession.getLibraryByUri( + 'package:_fe_analyzer_shared/src/base/errors.dart', + )) + as LibraryElementResult) + .element; + var elements = _Elements( + diagnosticCodeClass: errorsLibrary.getClass('DiagnosticCode')!, + typeSystem: errorsLibrary.typeSystem, + ); + var analyzedFiles = context.contextRoot.analyzedFiles().toList(); + for (var (index, file) in analyzedFiles.indexed) { + if (!file.endsWith('.dart')) continue; + var fileResult = + (await context.currentSession.getResolvedUnit(file)) + as ResolvedUnitResult; + var edits = <SourceEdit>[]; + fileResult.unit.accept(_Visitor(file, edits, elements)); + var percent = (index / analyzedFiles.length * 100).floor(); + print('$percent%: $file: ${edits.length} edits'); + if (edits.isNotEmpty) { + File(file).writeAsStringSync( + SourceEdit.applySequence( + fileResult.content, + edits..sortBy((e) => -e.offset), + ), + ); + } + } +} + +final _screamingCapsRegExp = RegExp(r'^[A-Z0-9_]+$'); + +class _Elements { + final ClassElement diagnosticCodeClass; + final TypeSystem typeSystem; + + _Elements({required this.diagnosticCodeClass, required this.typeSystem}); +} + +class _Visitor extends RecursiveAstVisitor { + final String _file; + final List<SourceEdit> _edits; + final _Elements _elements; + + _Visitor(this._file, this._edits, this._elements); + + @override + visitSimpleIdentifier(SimpleIdentifier node) { + if (_isScreamingCaps(node.name) && + _isDiagnosticCodeConstant(node.element)) { + _edits.add(SourceEdit(node.offset, node.length, node.name.toCamelCase())); + } + return super.visitSimpleIdentifier(node); + } + + @override + visitVariableDeclaration(VariableDeclaration node) { + if (node.name.lexeme == '_useLowerCamelCaseNames' && + path.basename(_file) == 'generate.dart') { + _edits.add( + SourceEdit(node.initializer!.offset, node.initializer!.length, 'true'), + ); + } else if (_isScreamingCaps(node.name.lexeme) && + _isDiagnosticCodeConstant(node.declaredFragment?.element)) { + _edits.add( + SourceEdit( + node.name.offset, + node.name.length, + node.name.lexeme.toCamelCase(), + ), + ); + } + return super.visitVariableDeclaration(node); + } + + bool _isDiagnosticCodeConstant(Element? element) { + if (element is GetterElement) { + element = element.variable; + } + return element is FieldElement && + element.isStatic && + element.isConst && + _isDiagnosticSubclass(element.enclosingElement); + } + + bool _isDiagnosticSubclass(InstanceElement class_) { + return _elements.typeSystem.isSubtypeOf( + class_.thisType, + _elements.diagnosticCodeClass.thisType, + ); + } + + bool _isScreamingCaps(String s) => _screamingCapsRegExp.hasMatch(s); +}
diff --git a/pkg/analyzer_plugin/CHANGELOG.md b/pkg/analyzer_plugin/CHANGELOG.md index 37104de..c92a339 100644 --- a/pkg/analyzer_plugin/CHANGELOG.md +++ b/pkg/analyzer_plugin/CHANGELOG.md
@@ -1,3 +1,11 @@ +## 0.13.8-dev + +- Require version `8.2.0` of the `analyzer` package. + +## 0.13.7 + +- Require version `8.1.1` of the `analyzer` package. + ## 0.13.6 - Require version `^8.1.0` of the `analyzer` package.
diff --git a/pkg/analyzer_plugin/pubspec.yaml b/pkg/analyzer_plugin/pubspec.yaml index 4e169bd..3e7e4dc 100644 --- a/pkg/analyzer_plugin/pubspec.yaml +++ b/pkg/analyzer_plugin/pubspec.yaml
@@ -1,6 +1,6 @@ name: analyzer_plugin description: A framework and support code for building plugins for the analysis server. -version: 0.13.6 +version: 0.13.8-dev repository: https://github.com/dart-lang/sdk/tree/main/pkg/analyzer_plugin environment: @@ -11,9 +11,9 @@ dependencies: # See the release policy for managing this dependency at # pkg/analyzer/doc/implementation/releasing.md. - analyzer: ^8.1.0 + analyzer: 8.2.0-dev collection: ^1.15.0 - dart_style: '>=2.3.7 <4.0.0' + dart_style: ^3.0.0 pub_semver: ^2.1.0 yaml: ^3.1.0 path: ^1.9.0
diff --git a/pkg/analyzer_testing/CHANGELOG.md b/pkg/analyzer_testing/CHANGELOG.md index 5b048d0..2834eb0 100644 --- a/pkg/analyzer_testing/CHANGELOG.md +++ b/pkg/analyzer_testing/CHANGELOG.md
@@ -1,3 +1,11 @@ +## 0.1.3-dev + +- Require version `8.2.0` of the `analyzer` package. + +## 0.1.2 + +- Require version `8.1.1` of the `analyzer` package. + ## 0.1.1 - Require version `^8.1.0` of the `analyzer` package.
diff --git a/pkg/analyzer_testing/pubspec.yaml b/pkg/analyzer_testing/pubspec.yaml index e227f9f..0467c3c 100644 --- a/pkg/analyzer_testing/pubspec.yaml +++ b/pkg/analyzer_testing/pubspec.yaml
@@ -1,6 +1,6 @@ name: analyzer_testing description: Testing utilities related to the analyzer and analysis_server_plugin packages. -version: 0.1.1 +version: 0.1.3-dev repository: https://github.com/dart-lang/sdk/tree/main/pkg/analyzer_testing environment: @@ -11,7 +11,7 @@ dependencies: # See the release policy for managing this dependency at # pkg/analyzer/doc/implementation/releasing.md. - analyzer: ^8.1.0 + analyzer: 8.2.0-dev meta: ^1.10.0 path: ^1.9.0 test: ^1.25.0
diff --git a/runtime/tests/vm/dart/.gitignore b/runtime/tests/vm/dart/.gitignore deleted file mode 100644 index 10a983d..0000000 --- a/runtime/tests/vm/dart/.gitignore +++ /dev/null
@@ -1,3 +0,0 @@ -# Auto-generated tests -many_functions_test.dart -
diff --git a/runtime/tests/vm/dart/generated/.gitignore b/runtime/tests/vm/dart/generated/.gitignore new file mode 100644 index 0000000..5c9add0 --- /dev/null +++ b/runtime/tests/vm/dart/generated/.gitignore
@@ -0,0 +1,2 @@ +# Tests generated by `gclient runhooks`. +*_test.dart
diff --git a/runtime/tests/vm/dart/generated/deeply_nested_closure_expression_generator.dart b/runtime/tests/vm/dart/generated/deeply_nested_closure_expression_generator.dart new file mode 100644 index 0000000..6484cfa --- /dev/null +++ b/runtime/tests/vm/dart/generated/deeply_nested_closure_expression_generator.dart
@@ -0,0 +1,33 @@ +// Copyright (c) 2025, 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. + +main() { + const n = 64; + // TODO(https://github.com/dart-lang/sdk/issues/61310): n = 256 + + print("class C {"); + print(" String field;"); + print(" C(this.field);"); + + print(" createClosure() {"); + print(" return"); + for (var i = 0; i < n; i++) { + print(" () =>"); + } + print(" field;"); + print(" }"); + print("}"); + + print(""" +main() { + const n = $n; + dynamic c = C("42").createClosure(); + for (var i = 0; i < n; i++) { + if (c is! Function) throw "Wrong!"; + c = c.call(); + } + if (c != "42") throw "Wrong!"; +} +"""); +}
diff --git a/runtime/tests/vm/dart/generated/deeply_nested_list_literal_generator.dart b/runtime/tests/vm/dart/generated/deeply_nested_list_literal_generator.dart new file mode 100644 index 0000000..fb38da3 --- /dev/null +++ b/runtime/tests/vm/dart/generated/deeply_nested_list_literal_generator.dart
@@ -0,0 +1,30 @@ +// Copyright (c) 2025, 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. + +main() { + const n = 1 << 8; + // TODO(https://github.com/dart-lang/sdk/issues/61310): n = 1 << 16 + + print("var list ="); + for (var i = 0; i < n; i++) { + print("["); + } + print("null"); + for (var i = 0; i < n; i++) { + print("]"); + } + print(";"); + + print(""" +main() { + const n = $n; + dynamic l = list; + for (var i = 0; i < n; i++) { + if (l is! List) throw "wrong"; + l = l[0]; + } + if (l != null) throw "wrong"; +} +"""); +}
diff --git a/runtime/tests/vm/dart/generated/deeply_nested_logical_expression_generator.dart b/runtime/tests/vm/dart/generated/deeply_nested_logical_expression_generator.dart new file mode 100644 index 0000000..8e75191 --- /dev/null +++ b/runtime/tests/vm/dart/generated/deeply_nested_logical_expression_generator.dart
@@ -0,0 +1,25 @@ +// Copyright (c) 2025, 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. + +main() { + const n = 512; + + print("@pragma('vm:never-inline')"); + print("@pragma('vm:entry-point') // Stop TFA"); + print("@pragma('dart2js:noInline')"); + print("dynamic check(List<int> x, int y) {"); + print(" return x[0] == y"); + for (var i = 1; i < n; i++) { + print(" && x[$i] == y"); + } + print(" ;"); + print("}"); + + print(""" +main() { + var x = new List<int>.filled($n, 42); + if (!check(x, 42)) throw "Wrong!"; +} +"""); +}
diff --git a/runtime/tests/vm/dart/generated/deeply_nested_map_literal_generator.dart b/runtime/tests/vm/dart/generated/deeply_nested_map_literal_generator.dart new file mode 100644 index 0000000..f9ebeb3 --- /dev/null +++ b/runtime/tests/vm/dart/generated/deeply_nested_map_literal_generator.dart
@@ -0,0 +1,30 @@ +// Copyright (c) 2025, 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. + +main() { + const n = 1 << 8; + // TODO(https://github.com/dart-lang/sdk/issues/61310): n = 1 << 16 + + print("var map ="); + for (var i = 0; i < n; i++) { + print("{0:"); + } + print("null"); + for (var i = 0; i < n; i++) { + print("}"); + } + print(";"); + + print(""" +main() { + const n = $n; + dynamic m = map; + for (var i = 0; i < n; i++) { + if (m is! Map) throw "wrong"; + m = m[0]; + } + if (m != null) throw "wrong"; +} +"""); +}
diff --git a/runtime/tests/vm/dart/generated/deeply_nested_set_literal_generator.dart b/runtime/tests/vm/dart/generated/deeply_nested_set_literal_generator.dart new file mode 100644 index 0000000..a9232e6 --- /dev/null +++ b/runtime/tests/vm/dart/generated/deeply_nested_set_literal_generator.dart
@@ -0,0 +1,30 @@ +// Copyright (c) 2025, 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. + +main() { + const n = 1 << 8; + // TODO(https://github.com/dart-lang/sdk/issues/61310): n = 1 << 16 + + print("var set ="); + for (var i = 0; i < n; i++) { + print("{"); + } + print("null"); + for (var i = 0; i < n; i++) { + print("}"); + } + print(";"); + + print(""" +main() { + const n = $n; + dynamic s = set; + for (var i = 0; i < n; i++) { + if (s is! Set) throw "wrong"; + s = s.single; + } + if (s != null) throw "wrong"; +} +"""); +}
diff --git a/runtime/tests/vm/dart/generated/deeply_nested_type_parameters_generator.dart b/runtime/tests/vm/dart/generated/deeply_nested_type_parameters_generator.dart new file mode 100644 index 0000000..8a42516 --- /dev/null +++ b/runtime/tests/vm/dart/generated/deeply_nested_type_parameters_generator.dart
@@ -0,0 +1,62 @@ +// Copyright (c) 2025, 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. + +main() { + const n = 1 << 8; + // TODO(https://github.com/dart-lang/sdk/issues/61310): n = 1 << 16 + + print("class C<T> {}"); + + for (var i = 0; i < n; i++) { + print("C<"); + } + print("int"); + for (var i = 0; i < n; i++) { + print(">"); + } + print(" c = new"); + for (var i = 0; i < n; i++) { + print("C<"); + } + print("int"); + for (var i = 0; i < n; i++) { + print(">"); + } + print("();"); + + print("@pragma('vm:never-inline')"); + print("@pragma('vm:entry-point') // Stop TFA"); + print("@pragma('dart2js:noInline')"); + print("bool isCheck(dynamic c) {"); + print(" return c is "); + for (var i = 0; i < n; i++) { + print("C<"); + } + print("int"); + for (var i = 0; i < n; i++) { + print(">"); + } + print(";"); + print("}"); + + print("@pragma('vm:never-inline')"); + print("@pragma('vm:entry-point') // Stop TFA"); + print("@pragma('dart2js:noInline')"); + print("void asCheck(dynamic c) {"); + print(" c as "); + for (var i = 0; i < n; i++) { + print("C<"); + } + print("int"); + for (var i = 0; i < n; i++) { + print(">"); + } + print(";"); + print("}"); + + print("main() {"); + print(" if (!isCheck(c)) throw 'Wrong!';"); + print(" asCheck(c);"); + print("}"); +}
diff --git a/runtime/tests/vm/dart/generated/large_offset_type_parameter_generator.dart b/runtime/tests/vm/dart/generated/large_offset_type_parameter_generator.dart new file mode 100644 index 0000000..c6b281e --- /dev/null +++ b/runtime/tests/vm/dart/generated/large_offset_type_parameter_generator.dart
@@ -0,0 +1,68 @@ +// Copyright (c) 2025, 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. + +main() { + const n = 1 << 10; + + print( + "// VMOptions=--optimization-counter-threshold=100 --no-use-osr --no-background-compilation", + ); + + print("class C <"); + print(" T0"); + for (var i = 1; i < n; i++) { + print(" ,T${i}"); + } + print("> {"); + + print(" @pragma('vm:never-inline')"); + print(" @pragma('vm:entry-point') // Stop TFA"); + print(" @pragma('dart2js:noInline')"); + print(" static create<T>() {"); + print(" return new C<"); + for (var i = 0; i < (n - 1); i++) { + print(" double,"); + } + print(" T>();"); + print(" }"); + + print(""" + @pragma('vm:never-inline') + @pragma('vm:entry-point') // Stop TFA + @pragma('dart2js:noInline') + dynamic checkIs(dynamic x) => x is T${n - 1}; + + @pragma('vm:never-inline') + @pragma('vm:entry-point') // Stop TFA + @pragma('dart2js:noInline') + dynamic checkAs(dynamic x) => x as T${n - 1}; + + @pragma('vm:never-inline') + @pragma('vm:entry-point') // Stop TFA + @pragma('dart2js:noInline') + dynamic checkInstantiate() => new G<G<T${n - 1}>>(); +} + +class G<T> {} + +main() { + for (var i = 0; i < 101; i++) { + var c1 = C.create<int>(); + if (c1.checkIs(42) != true) throw "Wrong is"; + if (c1.checkAs(42) != 42) throw "Wrong as"; + if (c1.checkInstantiate() is! G<G<int>>) throw "Wrong instantiate"; + + var c2 = C.create<double>(); + if (c2.checkIs(42.0) != true) throw "Wrong is"; + if (c2.checkAs(42.0) != 42.0) throw "Wrong as"; + if (c2.checkInstantiate() is! G<G<double>>) throw "Wrong instantiate"; + + var c3 = C.create<String>(); + if (c3.checkIs("42") != true) throw "Wrong is"; + if (c3.checkAs("42") != "42") throw "Wrong as"; + if (c3.checkInstantiate() is! G<G<String>>) throw "Wrong instantiate"; + } +} +"""); +}
diff --git a/runtime/tests/vm/dart/generated/large_offset_type_vector_generator.dart b/runtime/tests/vm/dart/generated/large_offset_type_vector_generator.dart new file mode 100644 index 0000000..9928160 --- /dev/null +++ b/runtime/tests/vm/dart/generated/large_offset_type_vector_generator.dart
@@ -0,0 +1,80 @@ +// Copyright (c) 2025, 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. + +main() { + const n = 1 << 10; + + print( + "// VMOptions=--optimization-counter-threshold=100 --no-use-osr --no-background-compilation", + ); + + print("class NotGeneric {"); + for (var i = 0; i < n; i++) { + print(" dynamic field${i};"); + } + print(" @pragma('vm:never-inline')"); + print(" @pragma('vm:entry-point') // Stop TFA"); + print(" @pragma('dart2js:noInline')"); + print(" NotGeneric(x) :"); + for (var i = 0; i < (n - 1); i++) { + print(" field${i} = x,"); + } + print(" field${n - 1} = x {}"); + + print(" @pragma('vm:never-inline')"); + print(" @pragma('vm:entry-point') // Stop TFA"); + print(" @pragma('dart2js:noInline')"); + print(" dynamic checkFields(dynamic x) {"); + for (var i = 0; i < n; i++) { + print(" if (field${i} != x) return false;"); + } + print(" return true;"); + print(" }"); + print("}"); + + print(""" +class C<T> extends NotGeneric { + C(x) : super(x); + + @pragma('vm:never-inline') + @pragma('vm:entry-point') // Stop TFA + @pragma('dart2js:noInline') + dynamic checkIs(dynamic x) => x is T; + + @pragma('vm:never-inline') + @pragma('vm:entry-point') // Stop TFA + @pragma('dart2js:noInline') + dynamic checkAs(dynamic x) => x as T; + + @pragma('vm:never-inline') + @pragma('vm:entry-point') // Stop TFA + @pragma('dart2js:noInline') + dynamic checkInstantiate() => new G<G<T>>(); +} + +class G<T> {} + +main() { + for (var i = 0; i < 101; i++) { + var c1 = new C<int>(10); + if (c1.checkIs(42) != true) throw "Wrong is"; + if (c1.checkAs(42) != 42) throw "Wrong as"; + if (c1.checkInstantiate() is! G<G<int>>) throw "Wrong instanitate"; + if (!c1.checkFields(10)) throw "Wrong fields"; + + var c2 = new C<double>(10); + if (c2.checkIs(42.0) != true) throw "Wrong is"; + if (c2.checkAs(42.0) != 42.0) throw "Wrong as"; + if (c2.checkInstantiate() is! G<G<double>>) throw "Wrong instanitate"; + if (!c2.checkFields(10)) throw "Wrong fields"; + + var c3 = new C<String>(10); + if (c3.checkIs("42") != true) throw "Wrong is"; + if (c3.checkAs("42") != "42") throw "Wrong as"; + if (c3.checkInstantiate() is! G<G<String>>) throw "Wrong instanitate"; + if (!c3.checkFields(10)) throw "Wrong fields"; + } +} +"""); +}
diff --git a/runtime/tests/vm/dart/generated/long_interface_chain_generator.dart b/runtime/tests/vm/dart/generated/long_interface_chain_generator.dart new file mode 100644 index 0000000..d939c65 --- /dev/null +++ b/runtime/tests/vm/dart/generated/long_interface_chain_generator.dart
@@ -0,0 +1,20 @@ +// Copyright (c) 2025, 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. + +main() { + const n = 16; + // TODO(https://github.com/dart-lang/sdk/issues/61310): n = 256 + + print("class C0 {}"); + for (var i = 1; i < n; i++) { + print("class C${i} implements C${i - 1} {}"); + } + + print("main() {"); + print(" var c = new C${n - 1}();"); + for (var i = 1; i < n; i++) { + print(" if (c is! C${i}) throw 'Wrong!';"); + } + print("}"); +}
diff --git a/runtime/tests/vm/dart/generated/long_superclass_chain_generator.dart b/runtime/tests/vm/dart/generated/long_superclass_chain_generator.dart new file mode 100644 index 0000000..5653690 --- /dev/null +++ b/runtime/tests/vm/dart/generated/long_superclass_chain_generator.dart
@@ -0,0 +1,19 @@ +// Copyright (c) 2025, 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. + +main() { + const n = 256; + + print("class C0 {}"); + for (var i = 1; i < n; i++) { + print("class C${i} extends C${i - 1} {}"); + } + + print("main() {"); + print(" var c = new C${n - 1}();"); + for (var i = 1; i < n; i++) { + print(" if (c is! C${i}) throw 'Wrong!';"); + } + print("}"); +}
diff --git a/runtime/tests/vm/dart/generated/many_double_literals_generator.dart b/runtime/tests/vm/dart/generated/many_double_literals_generator.dart new file mode 100644 index 0000000..e89dcd8 --- /dev/null +++ b/runtime/tests/vm/dart/generated/many_double_literals_generator.dart
@@ -0,0 +1,28 @@ +// Copyright (c) 2025, 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. + +main() { + const n = 1 << 20; + const m = 1 << 10; + + print("double sum = 0.0;"); + print("@pragma('vm:never-inline')"); + print("@pragma('dart2js:noInline')"); + print("add(double x) { sum += x; }"); + + for (var i = 0; i < m; i++) { + print("chunk${i}() {"); + for (var j = 0; j < n / m; j++) { + print(" add(${n / m * i + j}.0);"); + } + print("}"); + } + + print("main() {"); + for (var i = 0; i < m; i++) { + print(" chunk${i}();"); + } + print(" if (sum != ${n * (n - 1) ~/ 2}.0) throw 'Wrong!';"); + print("}"); +}
diff --git a/runtime/tests/vm/dart/generated/many_fields_flat_generator.dart b/runtime/tests/vm/dart/generated/many_fields_flat_generator.dart new file mode 100644 index 0000000..4a76cc0 --- /dev/null +++ b/runtime/tests/vm/dart/generated/many_fields_flat_generator.dart
@@ -0,0 +1,38 @@ +// Copyright (c) 2025, 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. + +main() { + const n = 1 << 12; + // TODO(https://github.com/dart-lang/sdk/issues/61310): n = 1 << 16 + + print("class C {"); + for (var i = 0; i < n; i++) { + print(" dynamic f$i = ${i * 2};"); + } + print(" @pragma('vm:never-inline') C();"); + print("}"); + + print("@pragma('vm:never-inline') setDouble(c) {"); + for (var i = 0; i < n; i++) { + print(" c.f$i = ${i * 2.0};"); + } + print(" return c;"); + print("}"); + + print("@pragma('vm:never-inline') check(c) {"); + for (var i = 0; i < n; i++) { + print(" if (c.f$i != ${i * 2}) throw 'Wrong!';"); + } + print(" return c;"); + print("}"); + + print(""" +main() { + var c = new C(); // Initialized to Smi. + check(c); + setDouble(c); // Storm of field-guard updates + check(c); +} +"""); +}
diff --git a/runtime/tests/vm/dart/generated/many_fields_staggered_generator.dart b/runtime/tests/vm/dart/generated/many_fields_staggered_generator.dart new file mode 100644 index 0000000..c0a4441 --- /dev/null +++ b/runtime/tests/vm/dart/generated/many_fields_staggered_generator.dart
@@ -0,0 +1,48 @@ +// Copyright (c) 2025, 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. + +// Will exhibit quadratic complexity if an implementation revisits superclass +// fields when processing each class's fields. + +main() { + const k = 64; + const n = (1 << 12) ~/ k; + // TODO(https://github.com/dart-lang/sdk/issues/61310): n = (1 << 16) ~/ k + + for (var i = 0; i < n; i++) { + if (i == 0) + print("class C${i} {"); + else + print("class C${i} extends C${i - 1} {"); + + for (var j = 0; j < k; j++) { + print(" dynamic f${i * k + j} = ${(i * k + j) * 2};"); + } + print(" @pragma('vm:never-inline') C${i}();"); + print("}"); + } + + print("@pragma('vm:never-inline') setDouble(c) {"); + for (var i = 0; i < (n * k); i++) { + print(" c.f$i = ${i * 2.0};"); + } + print(" return c;"); + print("}"); + + print("@pragma('vm:never-inline') check(c) {"); + for (var i = 0; i < (n * k); i++) { + print(" if (c.f$i != ${i * 2}) throw 'Wrong!';"); + } + print(" return c;"); + print("}"); + + print(""" +main() { + var c = new C${n - 1}(); // Initialized to Smi. + check(c); + setDouble(c); // Storm of field-guard updates + check(c); +} +"""); +}
diff --git a/runtime/tests/vm/dart/many_functions_generator.dart b/runtime/tests/vm/dart/generated/many_functions_generator.dart similarity index 100% rename from runtime/tests/vm/dart/many_functions_generator.dart rename to runtime/tests/vm/dart/generated/many_functions_generator.dart
diff --git a/runtime/tests/vm/dart/generated/many_interfaces_flat_generator.dart b/runtime/tests/vm/dart/generated/many_interfaces_flat_generator.dart new file mode 100644 index 0000000..41efd53 --- /dev/null +++ b/runtime/tests/vm/dart/generated/many_interfaces_flat_generator.dart
@@ -0,0 +1,25 @@ +// Copyright (c) 2025, 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. + +main() { + const n = 1 << 14; + // TODO(https://github.com/dart-lang/sdk/issues/61310): n = 1 << 16 + + for (var i = 0; i < n; i++) { + print("class I${i} {}"); + } + + print("class C implements I0"); + for (var i = 1; i < n; i++) { + print(" , I${i}"); + } + print("{}"); + + print("main() {"); + print(" var c = new C();"); + for (var i = 1; i < n; i++) { + print(" if (c is! I${i}) throw 'Wrong!';"); + } + print("}"); +}
diff --git a/runtime/tests/vm/dart/generated/many_object_literals_generator.dart b/runtime/tests/vm/dart/generated/many_object_literals_generator.dart new file mode 100644 index 0000000..0f0dc85 --- /dev/null +++ b/runtime/tests/vm/dart/generated/many_object_literals_generator.dart
@@ -0,0 +1,31 @@ +// Copyright (c) 2025, 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. + +main() { + const n = 1 << 16; + // TODO(https://github.com/dart-lang/sdk/issues/61310): n = 1 << 20 + const m = 1 << 10; + + print("class Box { final int value; const Box(this.value); }"); + + print("int sum = 0;"); + print("@pragma('vm:never-inline')"); + print("@pragma('dart2js:noInline')"); + print("add(Box x) { sum += x.value; }"); + + for (var i = 0; i < m; i++) { + print("chunk${i}() {"); + for (var j = 0; j < n / m; j++) { + print(" add(const Box(${n ~/ m * i + j}));"); + } + print("}"); + } + + print("main() {"); + for (var i = 0; i < m; i++) { + print(" chunk${i}();"); + } + print(" if (sum != ${n * (n - 1) ~/ 2}) throw 'Wrong!';"); + print("}"); +}
diff --git a/runtime/tests/vm/dart/generated/megamorphic_as_via_function_generator.dart b/runtime/tests/vm/dart/generated/megamorphic_as_via_function_generator.dart new file mode 100644 index 0000000..e55a93a --- /dev/null +++ b/runtime/tests/vm/dart/generated/megamorphic_as_via_function_generator.dart
@@ -0,0 +1,25 @@ +// Copyright (c) 2025, 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. + +main() { + const n = 1 << 12; + // TODO(https://github.com/dart-lang/sdk/issues/61310): n = 1 << 16 + + for (var i = 0; i < n; i++) { + print("class C${i} {}"); + } + + print("@pragma('vm:never-inline')"); + print("@pragma('vm:entry-point') // Stop TFA"); + print("@pragma('dart2js:noInline')"); + print("check<T>(dynamic c) {"); + print(" c as T;"); + print("}"); + + print("main() {"); + for (var i = 0; i < n; i++) { + print(" check<C${i}>(new C${i}());"); + } + print("}"); +}
diff --git a/runtime/tests/vm/dart/generated/megamorphic_as_via_instance_generator.dart b/runtime/tests/vm/dart/generated/megamorphic_as_via_instance_generator.dart new file mode 100644 index 0000000..2afa205 --- /dev/null +++ b/runtime/tests/vm/dart/generated/megamorphic_as_via_instance_generator.dart
@@ -0,0 +1,27 @@ +// Copyright (c) 2025, 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. + +main() { + const n = 1 << 12; + // TODO(https://github.com/dart-lang/sdk/issues/61310): n = 1 << 16 + + for (var i = 0; i < n; i++) { + print("class C${i} {}"); + } + + print("class Check<T> {"); + print(" @pragma('vm:never-inline')"); + print(" @pragma('vm:entry-point') // Stop TFA"); + print(" @pragma('dart2js:noInline')"); + print(" check(dynamic c) {"); + print(" c as T;"); + print(" }"); + print("}"); + + print("main() {"); + for (var i = 0; i < n; i++) { + print(" Check<C${i}>().check(new C${i}());"); + } + print("}"); +}
diff --git a/runtime/tests/vm/dart/generated/megamorphic_instantiate_via_function_generator.dart b/runtime/tests/vm/dart/generated/megamorphic_instantiate_via_function_generator.dart new file mode 100644 index 0000000..6deda60 --- /dev/null +++ b/runtime/tests/vm/dart/generated/megamorphic_instantiate_via_function_generator.dart
@@ -0,0 +1,27 @@ +// Copyright (c) 2025, 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. + +main() { + const n = 1 << 12; + // TODO(https://github.com/dart-lang/sdk/issues/61310): n = 1 << 16 + + for (var i = 0; i < n; i++) { + print("class C${i} {}"); + } + + print("class G<T> {}"); + + print("@pragma('vm:never-inline')"); + print("@pragma('vm:entry-point') // Stop TFA"); + print("@pragma('dart2js:noInline')"); + print("check<T>(dynamic c) {"); + print(" return new G<G<T>>();"); + print("}"); + + print("main() {"); + for (var i = 0; i < n; i++) { + print(" check<C${i}>(new C${i}());"); + } + print("}"); +}
diff --git a/runtime/tests/vm/dart/generated/megamorphic_instantiate_via_instance_generator.dart b/runtime/tests/vm/dart/generated/megamorphic_instantiate_via_instance_generator.dart new file mode 100644 index 0000000..04c785b --- /dev/null +++ b/runtime/tests/vm/dart/generated/megamorphic_instantiate_via_instance_generator.dart
@@ -0,0 +1,29 @@ +// Copyright (c) 2025, 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. + +main() { + const n = 1 << 12; + // TODO(https://github.com/dart-lang/sdk/issues/61310): n = 1 << 16 + + for (var i = 0; i < n; i++) { + print("class C${i} {}"); + } + + print("class G<T> {}"); + + print("class Check<T> {"); + print(" @pragma('vm:never-inline')"); + print(" @pragma('vm:entry-point') // Stop TFA"); + print(" @pragma('dart2js:noInline')"); + print(" check(dynamic c) {"); + print(" return new G<G<T>>();"); + print(" }"); + print("}"); + + print("main() {"); + for (var i = 0; i < n; i++) { + print(" Check<C${i}>().check(new C${i}());"); + } + print("}"); +}
diff --git a/runtime/tests/vm/dart/generated/megamorphic_is_via_function_generator.dart b/runtime/tests/vm/dart/generated/megamorphic_is_via_function_generator.dart new file mode 100644 index 0000000..fa04f61 --- /dev/null +++ b/runtime/tests/vm/dart/generated/megamorphic_is_via_function_generator.dart
@@ -0,0 +1,25 @@ +// Copyright (c) 2025, 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. + +main() { + const n = 1 << 12; + // TODO(https://github.com/dart-lang/sdk/issues/61310): n = 1 << 16 + + for (var i = 0; i < n; i++) { + print("class C${i} {}"); + } + + print("@pragma('vm:never-inline')"); + print("@pragma('vm:entry-point') // Stop TFA"); + print("@pragma('dart2js:noInline')"); + print("check<T>(dynamic c) {"); + print(" if (c is! T) throw 'Wrong!';"); + print("}"); + + print("main() {"); + for (var i = 0; i < n; i++) { + print(" check<C${i}>(new C${i}());"); + } + print("}"); +}
diff --git a/runtime/tests/vm/dart/generated/megamorphic_is_via_instance_generator.dart b/runtime/tests/vm/dart/generated/megamorphic_is_via_instance_generator.dart new file mode 100644 index 0000000..78d0cd2 --- /dev/null +++ b/runtime/tests/vm/dart/generated/megamorphic_is_via_instance_generator.dart
@@ -0,0 +1,27 @@ +// Copyright (c) 2025, 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. + +main() { + const n = 1 << 12; + // TODO(https://github.com/dart-lang/sdk/issues/61310): n = 1 << 16 + + for (var i = 0; i < n; i++) { + print("class C${i} {}"); + } + + print("class Check<T> {"); + print(" @pragma('vm:never-inline')"); + print(" @pragma('vm:entry-point') // Stop TFA"); + print(" @pragma('dart2js:noInline')"); + print(" check(dynamic c) {"); + print(" if (c is! T) throw 'Wrong!';"); + print(" }"); + print("}"); + + print("main() {"); + for (var i = 0; i < n; i++) { + print(" Check<C${i}>().check(new C${i}());"); + } + print("}"); +}
diff --git a/runtime/tests/vm/dart/generated/weave_const_array_generator.dart b/runtime/tests/vm/dart/generated/weave_const_array_generator.dart new file mode 100644 index 0000000..a27e30d --- /dev/null +++ b/runtime/tests/vm/dart/generated/weave_const_array_generator.dart
@@ -0,0 +1,32 @@ +// Copyright (c) 2025, 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. + +main() { + const n = 100; + + for (var j = 1; j <= 2; j++) { + print("const array${j}_0 = const <Object?>[null, null];"); + for (var i = 1; i <= n; i++) { + print( + "const array${j}_${i} = const <Object?>[array${j}_${i - 1}, array${j}_${i - 1}];", + ); + } + } + + print("@pragma('vm:never-inline')"); + print("@pragma('vm:entry-point')"); + print("@pragma('dart2js:noInline')"); + print("confuse(x) {"); + print(" try {"); + print(" throw x;"); + print(" } catch (e) {"); + print(" return e;"); + print(" }"); + print("}"); + + print("main() {"); + print(" if (!identical(confuse(array1_${n}),"); + print(" confuse(array2_${n}))) throw 'Wrong!';"); + print("}"); +}
diff --git a/runtime/tests/vm/dart/generated/weave_const_instance_generator.dart b/runtime/tests/vm/dart/generated/weave_const_instance_generator.dart new file mode 100644 index 0000000..9220cd2 --- /dev/null +++ b/runtime/tests/vm/dart/generated/weave_const_instance_generator.dart
@@ -0,0 +1,36 @@ +// Copyright (c) 2025, 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. + +main() { + const n = 100; + + print("class Box {"); + print(" final Object? content1;"); + print(" final Object? content2;"); + print(" const Box(this.content1, this.content2);"); + print("}"); + + for (var j = 1; j <= 2; j++) { + print("const box${j}_0 = Box(null, null);"); + for (var i = 1; i <= n; i++) { + print("const box${j}_${i} = Box(box${j}_${i - 1}, box${j}_${i - 1});"); + } + } + + print("@pragma('vm:never-inline')"); + print("@pragma('vm:entry-point')"); + print("@pragma('dart2js:noInline')"); + print("confuse(x) {"); + print(" try {"); + print(" throw x;"); + print(" } catch (e) {"); + print(" return e;"); + print(" }"); + print("}"); + + print("main() {"); + print(" if (!identical(confuse(box1_${n}),"); + print(" confuse(box2_${n}))) throw 'Wrong!';"); + print("}"); +}
diff --git a/runtime/tests/vm/dart/generated/weave_const_record_generator.dart b/runtime/tests/vm/dart/generated/weave_const_record_generator.dart new file mode 100644 index 0000000..58815aa --- /dev/null +++ b/runtime/tests/vm/dart/generated/weave_const_record_generator.dart
@@ -0,0 +1,33 @@ +// Copyright (c) 2025, 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. + +main() { + const n = 8; + // TODO(https://github.com/dart-lang/sdk/issues/61310): n = 100 + + for (var j = 1; j <= 2; j++) { + print("const (Object?, Object?) record${j}_0 = (null, null);"); + for (var i = 1; i <= n; i++) { + print( + "const (Object?, Object?) record${j}_${i} = (record${j}_${i - 1}, record${j}_${i - 1});", + ); + } + } + + print("@pragma('vm:never-inline')"); + print("@pragma('vm:entry-point')"); + print("@pragma('dart2js:noInline')"); + print("confuse(x) {"); + print(" try {"); + print(" throw x;"); + print(" } catch (e) {"); + print(" return e;"); + print(" }"); + print("}"); + + print("main() {"); + print(" if (!identical(confuse(record1_${n}),"); + print(" confuse(record2_${n}))) throw 'Wrong!';"); + print("}"); +}
diff --git a/runtime/tests/vm/dart/generated/weave_implements_generator.dart b/runtime/tests/vm/dart/generated/weave_implements_generator.dart new file mode 100644 index 0000000..52a055c --- /dev/null +++ b/runtime/tests/vm/dart/generated/weave_implements_generator.dart
@@ -0,0 +1,24 @@ +// Copyright (c) 2025, 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. + +// Cf. hashing_memoize_instance_test.dart. + +main() { + const n = 8; + // TODO(https://github.com/dart-lang/sdk/issues/61310): n = 100 + + print("class C0A {}"); + print("class C0B {}"); + for (var i = 1; i <= n; i++) { + print("class C${i}A implements C${i - 1}A, C${i - 1}B {}"); + print("class C${i}B implements C${i - 1}A, C${i - 1}B {}"); + } + + print("main() {"); + print(" if (new C${n}A() is! C0A) throw 'Wrong!';"); + print(" if (new C${n}A() is! C0B) throw 'Wrong!';"); + print(" if (new C${n}B() is! C0A) throw 'Wrong!';"); + print(" if (new C${n}B() is! C0B) throw 'Wrong!';"); + print("}"); +}
diff --git a/runtime/tests/vm/vm.status b/runtime/tests/vm/vm.status index 445db8e..5d2757a 100644 --- a/runtime/tests/vm/vm.status +++ b/runtime/tests/vm/vm.status
@@ -138,7 +138,6 @@ dart/appjit_cha_deopt_test: Pass, Slow # Quite slow in debug mode, uses --optimization-counter-threshold=100 dart/b162922506_test: SkipSlow # Generates very large input file dart/hash_map_probes_limit_test: SkipSlow # Test includes large program compilation. -dart/many_functions_test: SkipSlow # Too slow in debug mode. dart/minimal_kernel_test: SkipSlow # gen_kernel is too slow in debug mode dart/regress47472_test: Pass, Slow # Slow due to throwing 1 million exceptions. dart/spawn_shutdown_test: Pass, Slow # VM Shutdown test, It can take some time for all the isolates to shutdown in a Debug build. @@ -382,6 +381,9 @@ dart/split_aot_kernel_generation2_test: SkipByDesign # This test is for VM AOT only and is quite slow (so we don't run it in debug mode). dart/split_aot_kernel_generation_test: SkipByDesign # This test is for VM AOT only and is quite slow (so we don't run it in debug mode). +[ $mode == debug || $sanitizer != none || $system == android || $system == fuchsia || $hot_reload || $qemu || $simulator ] +dart/generated/*: SkipSlow + # It makes no sense to run any test that uses spawnURI under the simulator # as that would involve running CFE (the front end) in simulator mode # to compile the URI file specified in spawnURI code.
diff --git a/samples/ffi/httpIG/lib/http.dart b/samples/ffi/httpIG/lib/http.dart index 0a1024d..4b3798d 100644 --- a/samples/ffi/httpIG/lib/http.dart +++ b/samples/ffi/httpIG/lib/http.dart
@@ -12,7 +12,7 @@ import 'dylib_utils.dart'; -Function (Pointer<Utf8>) createGetSender(SendPort sendPort) { +Function (Pointer<Utf8>) createGetSender(final SendPort sendPort) { return (Pointer<Utf8> responsePointer) { final typedList = responsePointer.cast<Uint8>().asTypedList( responsePointer.length, @@ -57,7 +57,7 @@ @pragma('vm:shared') late int counter; -Function (Pointer<Utf8>) createServeSender(SendPort sendPort) { +Function (Pointer<Utf8>) createServeSender(final SendPort sendPort) { return (Pointer<Utf8> requestPointer) { counter++; final typedList = requestPointer.cast<Uint8>().asTypedList(
diff --git a/samples/samples.status b/samples/samples.status index 26d9087..0cea15b 100644 --- a/samples/samples.status +++ b/samples/samples.status
@@ -15,8 +15,6 @@ *: Skip # Not yet triaged. [ $arch != x64 || $compiler != dartk || $system != linux || $hot_reload || $hot_reload_rollback ] -ffi/http/test/http_test: SkipByDesign -ffi/httpIG/test/http_test: SkipByDesign ffi/sqlite/test/sqlite_test: SkipByDesign # FFI not supported or libsqlite3.so not available. [ $runtime == d8 || $browser ]
diff --git a/tests/ffi/ffi.status b/tests/ffi/ffi.status index 4fc7624..937c929 100644 --- a/tests/ffi/ffi.status +++ b/tests/ffi/ffi.status
@@ -58,6 +58,7 @@ function_callbacks_structs_by_value_native_callable_generated_test/*: Skip # Test harness doesn't support multitest with Fuchsia function_callbacks_subtype_test/*: Skip # Test harness doesn't support multitest with Fuchsia isolate_group_bound_callback_test: Skip # gen_snapshot requires experimental-shared-data flag +isolate_group_bound_captured_local_test: Skip # gen_snapshot requires experimental-shared-data flag isolate_group_bound_init_test: Skip # gen_snapshot requires experimental-shared-data flag isolate_group_bound_send_test: Skip # gen_snapshot requires experimental-shared-data flag native_assets/*: Skip # Source not available in the emulator
diff --git a/tools/VERSION b/tools/VERSION index f24c007..bf81143 100644 --- a/tools/VERSION +++ b/tools/VERSION
@@ -27,5 +27,5 @@ MAJOR 3 MINOR 10 PATCH 0 -PRERELEASE 94 +PRERELEASE 95 PRERELEASE_PATCH 0
diff --git a/tools/generate_large_tests.py b/tools/generate_large_tests.py index 77c9ba05..08edffe 100755 --- a/tools/generate_large_tests.py +++ b/tools/generate_large_tests.py
@@ -30,14 +30,19 @@ def generate_tests(tools_dir, tests_dir): + # Remove all previously existing tests in case the corresponding generator + # is deleted or renamed. + for file in os.listdir(tests_dir): + if file.endswith('_test.dart'): + os.remove(os.path.join(tests_dir, file)) + + # Create each xyz_test.dart as the output of xyz_generator.dart. for file in os.listdir(tests_dir): if not file.endswith('_generator.dart'): continue gen_file = os.path.join(tests_dir, file) test_file = gen_file.replace('_generator.dart', '_test.dart') - if os.path.exists(test_file): - os.remove(test_file) process = subprocess.run([ checked_in_sdk_executable(), '--packages=%s' % @@ -46,8 +51,15 @@ ], capture_output=True) if process.returncode != 0: + print("%s failed" % os.path.join(tests_dir, file)) + print(process.stdout) + print(process.stderr) sys.exit(process.returncode) f = open(test_file, 'wb') + f.write(b'// GENERATED FILE: DO NOT EDIT\n') + f.write(('// Edit %s instead and re-run `gclient runhooks`\n' % + file).encode('utf-8')) + f.write(b'\n') f.write(process.stdout) f.close() @@ -55,7 +67,7 @@ def Main(): tools_dir = os.path.dirname(os.path.realpath(__file__)) vm_tests_dir = os.path.join(tools_dir, '..', 'runtime', 'tests', 'vm', - 'dart') + 'dart', 'generated') generate_tests(tools_dir, vm_tests_dir)
diff --git a/tools/verify_docs/bin/verify_docs.dart b/tools/verify_docs/bin/verify_docs.dart index c65862c..0f7e600 100755 --- a/tools/verify_docs/bin/verify_docs.dart +++ b/tools/verify_docs/bin/verify_docs.dart
@@ -4,6 +4,13 @@ // Read the ../README.md file for the recognized syntax. +// This file uses some SCREAMING_CAPS diagnostic names that are private to the +// analyzer package. Those names will soon be deprecated since the analyzer will +// switch to using camelCase diagnostic names. This "ignore" comment will +// prevent a bot failure when the deprecation happens. TODO(paulberry): clean +// this up. +// ignore_for_file: deprecated_member_use + import 'dart:collection'; import 'dart:io';