Version 2.17.6
* Cherry-pick refs/changes/80/248080/4 to stable
* Cherry-pick ec9a660ccca94edaf04051192282c7ecaf2bcccc to stable
* Cherry-pick d3ea8bff5c56e557bf5b4310fe2538feeda7b84c to stable
* Cherry-pick 3224487a07d7c23560228e5159e21fed28781b3a to stable
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c146e53..147bc50 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,9 +1,21 @@
+## 2.17.6 - 2022-07-13
+
+This is a patch release that:
+
+- Improves code completion for Flutter (issue [#49054][]).
+- Fixes a crash on ARM (issue [#106510][]).
+- Fixes a compiler crash with Finalizable parameters (issue [#49402][]).
+
+[#49054]: https://github.com/dart-lang/sdk/issues/49054
+[#106510]: https://github.com/flutter/flutter/issues/106510
+[#49402]: https://github.com/dart-lang/sdk/issues/49402
+
## 2.17.5 - 2022-06-22
-This is a patch release that fixes:
+This is a patch release that:
-- Improve analysis of enums and switch (issue [#49188]).
-- Fix compiler crash when initializing Finalizable objects (issue [#49075]).
+- Improves analysis of enums and switch (issue [#49188][]).
+- Fixes a compiler crash when initializing Finalizable objects (issue [#49075][]).
[#49188]: https://github.com/dart-lang/sdk/issues/49188
[#49075]: https://github.com/dart-lang/sdk/issues/49075
diff --git a/pkg/analysis_server/lib/src/domain_completion.dart b/pkg/analysis_server/lib/src/domain_completion.dart
index 3db8862..a35a758 100644
--- a/pkg/analysis_server/lib/src/domain_completion.dart
+++ b/pkg/analysis_server/lib/src/domain_completion.dart
@@ -31,7 +31,6 @@
import 'package:analyzer_plugin/protocol/protocol.dart' as plugin;
import 'package:analyzer_plugin/protocol/protocol_common.dart';
import 'package:analyzer_plugin/protocol/protocol_generated.dart' as plugin;
-import 'package:collection/collection.dart';
/// Instances of the class [CompletionDomainHandler] implement a
/// [RequestHandler] that handles requests in the completion domain.
@@ -208,8 +207,7 @@
performanceList.add(completionPerformance);
var analysisSession = resolvedUnit.analysisSession;
- var enclosingNode =
- resolvedUnit.resolvedNodes.lastOrNull ?? resolvedUnit.parsedUnit;
+ var enclosingNode = resolvedUnit.parsedUnit;
var completionRequest = DartCompletionRequest(
analysisSession: analysisSession,
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/completion_manager.dart b/pkg/analysis_server/lib/src/services/completion/dart/completion_manager.dart
index 4fbeca3..80b2128 100644
--- a/pkg/analysis_server/lib/src/services/completion/dart/completion_manager.dart
+++ b/pkg/analysis_server/lib/src/services/completion/dart/completion_manager.dart
@@ -475,6 +475,19 @@
}
}
+ if (entity is Token &&
+ entity.type == TokenType.STRING &&
+ entity.offset < offset &&
+ offset < entity.end) {
+ final uriNode = target.containingNode;
+ if (uriNode is SimpleStringLiteral && uriNode.literal == entity) {
+ final directive = uriNode.parent;
+ if (directive is UriBasedDirective && directive.uri == uriNode) {
+ return uriNode.value.substring(0, offset - uriNode.contentsOffset);
+ }
+ }
+ }
+
while (entity is AstNode) {
if (entity is SimpleIdentifier) {
var identifier = entity.name;
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 983ff52..0996659 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
@@ -540,10 +540,12 @@
if (offset <= argument.offset) {
return typeOfIndexPositionalParameter();
}
- if (argument.contains(offset) && offset >= argument.name.end) {
- return argument.staticParameterElement?.type;
+ if (argument.contains(offset)) {
+ if (offset >= argument.name.end) {
+ return argument.staticParameterElement?.type;
+ }
+ return null;
}
- return null;
} else {
if (previousArgument == null || previousArgument.end < offset) {
if (offset <= argument.end) {
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/fuzzy_filter_sort.dart b/pkg/analysis_server/lib/src/services/completion/dart/fuzzy_filter_sort.dart
index 1fbda62..f410bf1 100644
--- a/pkg/analysis_server/lib/src/services/completion/dart/fuzzy_filter_sort.dart
+++ b/pkg/analysis_server/lib/src/services/completion/dart/fuzzy_filter_sort.dart
@@ -5,6 +5,7 @@
import 'package:analysis_server/src/protocol_server.dart';
import 'package:analysis_server/src/services/completion/dart/suggestion_builder.dart';
import 'package:analysis_server/src/services/completion/filtering/fuzzy_matcher.dart';
+import 'package:collection/collection.dart';
final _identifierPattern = RegExp(r'([_a-zA-Z][_a-zA-Z0-9]*)');
@@ -14,7 +15,11 @@
required String pattern,
required List<CompletionSuggestionBuilder> suggestions,
}) {
- var matcher = FuzzyMatcher(pattern, matchStyle: MatchStyle.SYMBOL);
+ final matchStyle =
+ suggestions.firstOrNull?.kind == CompletionSuggestionKind.IMPORT
+ ? MatchStyle.FILENAME
+ : MatchStyle.SYMBOL;
+ final matcher = FuzzyMatcher(pattern, matchStyle: matchStyle);
double score(CompletionSuggestionBuilder suggestion) {
var textToMatch = suggestion.textToMatch;
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/local_library_contributor.dart b/pkg/analysis_server/lib/src/services/completion/dart/local_library_contributor.dart
index 7659c35..1b2a1dd 100644
--- a/pkg/analysis_server/lib/src/services/completion/dart/local_library_contributor.dart
+++ b/pkg/analysis_server/lib/src/services/completion/dart/local_library_contributor.dart
@@ -9,6 +9,7 @@
import 'package:analysis_server/src/services/completion/dart/suggestion_builder.dart'
show SuggestionBuilder;
import 'package:analyzer/dart/element/element.dart';
+import 'package:analyzer/dart/element/type.dart';
import 'package:analyzer/dart/element/visitor.dart';
import 'package:analyzer_plugin/src/utilities/completion/optype.dart';
@@ -52,10 +53,14 @@
_addConstructorSuggestions(element);
}
if (opType.includeReturnValueSuggestions) {
- if (element.isEnum) {
- for (var field in element.fields) {
- if (field.isEnumConstant) {
- builder.suggestEnumConstant(field, prefix: prefix);
+ final typeSystem = request.libraryElement.typeSystem;
+ final contextType = request.contextType;
+ if (contextType is InterfaceType) {
+ // TODO(scheglov) This looks not ideal - we should suggest getters.
+ for (final field in element.fields) {
+ if (field.isStatic &&
+ typeSystem.isSubtypeOf(field.type, contextType)) {
+ builder.suggestStaticField(field, prefix: prefix);
}
}
}
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/local_reference_contributor.dart b/pkg/analysis_server/lib/src/services/completion/dart/local_reference_contributor.dart
index b3a1499..d3f7a95 100644
--- a/pkg/analysis_server/lib/src/services/completion/dart/local_reference_contributor.dart
+++ b/pkg/analysis_server/lib/src/services/completion/dart/local_reference_contributor.dart
@@ -210,24 +210,7 @@
@override
void declaredClass(ClassDeclaration declaration) {
- var classElt = declaration.declaredElement;
- if (classElt != null && visibilityTracker._isVisible(classElt)) {
- if (opType.includeTypeNameSuggestions) {
- builder.suggestClass(classElt);
- }
-
- // Generate the suggestions for the constructors. We are required to loop
- // through elements here instead of using declaredConstructor() due to
- // implicit constructors (i.e. there is no AstNode for an implicit
- // constructor)
- if (!opType.isPrefixed && opType.includeConstructorSuggestions) {
- for (var constructor in classElt.constructors) {
- if (!classElt.isAbstract || constructor.isFactory) {
- builder.suggestConstructor(constructor);
- }
- }
- }
- }
+ _declaredClassElement(declaration.declaredElement);
}
@override
@@ -245,20 +228,7 @@
@override
void declaredEnum(EnumDeclaration declaration) {
- var declaredElement = declaration.declaredElement;
- if (declaredElement != null &&
- visibilityTracker._isVisible(declaredElement) &&
- opType.includeTypeNameSuggestions) {
- builder.suggestClass(declaredElement);
- for (var enumConstant in declaration.constants) {
- if (!enumConstant.isSynthetic) {
- var constantElement = enumConstant.declaredElement;
- if (constantElement is FieldElement) {
- builder.suggestEnumConstant(constantElement);
- }
- }
- }
- }
+ _declaredClassElement(declaration.declaredElement);
}
@override
@@ -434,6 +404,38 @@
super.visitExtendsClause(node);
}
+ void _declaredClassElement(ClassElement? class_) {
+ if (class_ != null && visibilityTracker._isVisible(class_)) {
+ if (opType.includeTypeNameSuggestions) {
+ builder.suggestClass(class_);
+ }
+
+ if (!opType.isPrefixed &&
+ opType.includeConstructorSuggestions &&
+ !class_.isEnum) {
+ for (final constructor in class_.constructors) {
+ if (!class_.isAbstract || constructor.isFactory) {
+ builder.suggestConstructor(constructor);
+ }
+ }
+ }
+
+ if (!opType.isPrefixed && opType.includeReturnValueSuggestions) {
+ final typeSystem = request.libraryElement.typeSystem;
+ final contextType = request.contextType;
+ if (contextType is InterfaceType) {
+ // TODO(scheglov) This looks not ideal - we should suggest getters.
+ for (final field in class_.fields) {
+ if (field.isStatic &&
+ typeSystem.isSubtypeOf(field.type, contextType)) {
+ builder.suggestStaticField(field);
+ }
+ }
+ }
+ }
+ }
+ }
+
/// Return `true` if the [identifier] is composed of one or more underscore
/// characters and nothing else.
bool _isUnused(String identifier) => RegExp(r'^_+$').hasMatch(identifier);
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/suggestion_builder.dart b/pkg/analysis_server/lib/src/services/completion/dart/suggestion_builder.dart
index a784e8a..e401ba4 100644
--- a/pkg/analysis_server/lib/src/services/completion/dart/suggestion_builder.dart
+++ b/pkg/analysis_server/lib/src/services/completion/dart/suggestion_builder.dart
@@ -335,10 +335,17 @@
/// invocation. The [inheritanceDistance] is the value of the inheritance
/// distance feature computed for the accessor or `-1.0` if the accessor is a
/// static accessor.
- void suggestAccessor(PropertyAccessorElement accessor,
- {required double inheritanceDistance}) {
- assert(accessor.enclosingElement is ClassElement ||
- accessor.enclosingElement is ExtensionElement);
+ void suggestAccessor(
+ PropertyAccessorElement accessor, {
+ required double inheritanceDistance,
+ bool withEnclosingName = false,
+ }) {
+ var enclosingPrefix = '';
+ var enclosingName = _enclosingClassOrExtensionName(accessor);
+ if (withEnclosingName && enclosingName != null) {
+ enclosingPrefix = '$enclosingName.';
+ }
+
if (accessor.isSynthetic) {
// Avoid visiting a field twice. All fields induce a getter, but only
// non-final fields induce a setter, so we don't add a suggestion for a
@@ -378,6 +385,7 @@
_addBuilder(
_createCompletionSuggestionBuilder(
accessor,
+ completion: enclosingPrefix + accessor.displayName,
kind: CompletionSuggestionKind.IDENTIFIER,
relevance: relevance,
isNotImported: isNotImportedLibrary,
@@ -825,6 +833,7 @@
// Let the user know that we are going to insert a complete statement.
displayText: 'setState(() {});',
),
+ textToMatchOverride: 'setState',
);
return;
}
@@ -1025,6 +1034,43 @@
);
}
+ /// Add a suggestion for a static field declared within a class or extension.
+ /// If the field is synthetic, add the corresponding getter instead.
+ ///
+ /// If the enclosing element can only be referenced using a prefix, then
+ /// the [prefix] should be provided.
+ void suggestStaticField(FieldElement element, {String? prefix}) {
+ assert(element.isStatic);
+ if (element.isSynthetic) {
+ var getter = element.getter;
+ if (getter != null) {
+ suggestAccessor(
+ getter,
+ inheritanceDistance: 0.0,
+ withEnclosingName: true,
+ );
+ }
+ } else {
+ var enclosingPrefix = '';
+ var enclosingName = _enclosingClassOrExtensionName(element);
+ if (enclosingName != null) {
+ enclosingPrefix = '$enclosingName.';
+ }
+ var relevance =
+ _computeTopLevelRelevance(element, elementType: element.type);
+ _addBuilder(
+ _createCompletionSuggestionBuilder(
+ element,
+ completion: enclosingPrefix + element.name,
+ kind: CompletionSuggestionKind.IDENTIFIER,
+ prefix: prefix,
+ relevance: relevance,
+ isNotImported: isNotImportedLibrary,
+ ),
+ );
+ }
+ }
+
/// Add a suggestion to reference a [parameter] in a super formal parameter.
void suggestSuperFormalParameter(ParameterElement parameter) {
_addBuilder(
@@ -1392,6 +1438,22 @@
);
}
+ /// Return the name of the enclosing class or extension.
+ ///
+ /// The enclosing element must be either a class, or extension; otherwise
+ /// we either fail with assertion, or return `null`.
+ String? _enclosingClassOrExtensionName(Element element) {
+ var enclosing = element.enclosingElement;
+ if (enclosing is ClassElement) {
+ return enclosing.name;
+ } else if (enclosing is ExtensionElement) {
+ return enclosing.name;
+ } else {
+ assert(false, 'Expected ClassElement or ExtensionElement');
+ return null;
+ }
+ }
+
/// If the [element] has a documentation comment, return it.
_ElementDocumentation? _getDocumentation(Element element) {
var documentationCache = request.documentationCache;
diff --git a/pkg/analysis_server/test/analysis_server_base.dart b/pkg/analysis_server/test/analysis_server_base.dart
index 804f079..43f7da3 100644
--- a/pkg/analysis_server/test/analysis_server_base.dart
+++ b/pkg/analysis_server/test/analysis_server_base.dart
@@ -22,6 +22,7 @@
import 'package:test/test.dart';
import 'mocks.dart';
+import 'src/utilities/mock_packages.dart';
/// TODO(scheglov) this is duplicate
class AnalysisOptionsFileConfig {
@@ -91,6 +92,9 @@
EnableString.super_parameters,
];
+ /// The path that is not in [workspaceRootPath], contains external packages.
+ String get packagesRootPath => '/packages';
+
Folder get sdkRoot => newFolder('/sdk');
File get testFile => getFile(testFilePath);
@@ -280,6 +284,8 @@
void writeTestPackageConfig({
PackageConfigFileBuilder? config,
String? languageVersion,
+ bool flutter = false,
+ bool meta = false,
}) {
if (config == null) {
config = PackageConfigFileBuilder();
@@ -293,6 +299,22 @@
languageVersion: languageVersion,
);
+ if (meta || flutter) {
+ var libFolder = MockPackages.instance.addMeta(resourceProvider);
+ config.add(name: 'meta', rootPath: libFolder.parent.path);
+ }
+
+ if (flutter) {
+ {
+ var libFolder = MockPackages.instance.addUI(resourceProvider);
+ config.add(name: 'ui', rootPath: libFolder.parent.path);
+ }
+ {
+ var libFolder = MockPackages.instance.addFlutter(resourceProvider);
+ config.add(name: 'flutter', rootPath: libFolder.parent.path);
+ }
+ }
+
writePackageConfig(testPackageRoot, config);
}
diff --git a/pkg/analysis_server/test/client/completion_driver_test.dart b/pkg/analysis_server/test/client/completion_driver_test.dart
index c8ee38b..1b470b3 100644
--- a/pkg/analysis_server/test/client/completion_driver_test.dart
+++ b/pkg/analysis_server/test/client/completion_driver_test.dart
@@ -267,7 +267,7 @@
@override
TestingCompletionProtocol get protocol => TestingCompletionProtocol.version1;
- @failingTest
+ @FailingTest(reason: 'This test fails with available suggestions')
@override
Future<void> test_project_lib_multipleExports() async {
return super.test_project_lib_multipleExports();
@@ -343,7 +343,7 @@
await addTestFile('''
import 'a.dart';
void f() {
- ^
+ E v = ^
}
''');
assertSuggestion(
@@ -665,7 +665,7 @@
Future<void> test_project_lib_setters_static() async {
newFile2('$testPackageLibPath/a.dart', r'''
class A {
- static set g(int g) {}
+ static set foo(int _) {}
}
''');
@@ -679,7 +679,7 @@
}
''');
- assertNoSuggestion(completion: 'A.g');
+ assertNoSuggestion(completion: 'A.foo');
}
/// See: https://github.com/dart-lang/sdk/issues/40626
diff --git a/pkg/analysis_server/test/client/impl/completion_driver.dart b/pkg/analysis_server/test/client/impl/completion_driver.dart
index df8faf3..c29265e 100644
--- a/pkg/analysis_server/test/client/impl/completion_driver.dart
+++ b/pkg/analysis_server/test/client/impl/completion_driver.dart
@@ -276,6 +276,8 @@
} else if (notification.event == ANALYSIS_NOTIFICATION_ERRORS) {
var decoded = AnalysisErrorsParams.fromNotification(notification);
filesErrors[decoded.file] = decoded.errors;
+ } else if (notification.event == ANALYSIS_NOTIFICATION_FLUSH_RESULTS) {
+ // Ignored.
} else if (notification.event == SERVER_NOTIFICATION_ERROR) {
throw Exception('server error: ${notification.toJson()}');
} else if (notification.event == SERVER_NOTIFICATION_CONNECTED) {
diff --git a/pkg/analysis_server/test/domain_completion_test.dart b/pkg/analysis_server/test/domain_completion_test.dart
index 03ec19a..2fa55a9 100644
--- a/pkg/analysis_server/test/domain_completion_test.dart
+++ b/pkg/analysis_server/test/domain_completion_test.dart
@@ -391,44 +391,6 @@
..suggestions.withElementClass.isEmpty;
}
- Future<void> test_notImported_lowerRelevance_enumConstant() async {
- newFile2('$testPackageLibPath/a.dart', '''
-enum E1 {
- foo01
-}
-''');
-
- newFile2('$testPackageLibPath/b.dart', '''
-enum E2 {
- foo02
-}
-''');
-
- await _configureWithWorkspaceRoot();
-
- var response = await _getTestCodeSuggestions('''
-import 'b.dart';
-
-void f() {
- foo0^
-}
-''');
-
- check(response)
- ..assertComplete()
- ..hasReplacement(left: 4);
-
- // `foo01` relevance is decreased because it is not yet imported.
- check(response).suggestions.matches([
- (suggestion) => suggestion
- ..completion.isEqualTo('E2.foo02')
- ..libraryUriToImport.isNull,
- (suggestion) => suggestion
- ..completion.isEqualTo('E1.foo01')
- ..libraryUriToImport.isEqualTo('package:test/a.dart'),
- ]);
- }
-
Future<void> test_notImported_lowerRelevance_extension_getter() async {
await _configureWithWorkspaceRoot();
diff --git a/pkg/analysis_server/test/services/completion/dart/completion_check.dart b/pkg/analysis_server/test/services/completion/dart/completion_check.dart
index ff98794..313959e 100644
--- a/pkg/analysis_server/test/services/completion/dart/completion_check.dart
+++ b/pkg/analysis_server/test/services/completion/dart/completion_check.dart
@@ -185,6 +185,14 @@
);
}
+ @useResult
+ CheckTarget<bool?> get hasNamedParameters {
+ return nest(
+ value.suggestion.hasNamedParameters,
+ (selected) => 'has hasNamedParameters ${valueStr(selected)}',
+ );
+ }
+
void get isClass {
kind.isIdentifier;
element.isNotNull.kind.isClass;
@@ -220,6 +228,10 @@
element.isNotNull.kind.isGetter;
}
+ void get isImport {
+ kind.isImport;
+ }
+
void get isImportPrefix {
kind.isIdentifier;
element.isNotNull.kind.isPrefix;
@@ -262,6 +274,20 @@
element.isNotNull.kind.isSetter;
}
+ void get isStatic {
+ element.isNotNull.isStatic.isTrue;
+ }
+
+ void get isStaticField {
+ isStatic;
+ isField;
+ }
+
+ void get isStaticGetter {
+ isStatic;
+ isGetter;
+ }
+
void get isTopLevelVariable {
kind.isIdentifier;
element.isNotNull.kind.isTopLevelVariable;
@@ -294,6 +320,14 @@
}
@useResult
+ CheckTarget<List<String>?> get parameterNames {
+ return nest(
+ value.suggestion.parameterNames,
+ (selected) => 'has parameterNames ${valueStr(selected)}',
+ );
+ }
+
+ @useResult
CheckTarget<String?> get parameterType {
return nest(
value.suggestion.parameterType,
@@ -301,6 +335,14 @@
);
}
+ @useResult
+ CheckTarget<List<String>?> get parameterTypes {
+ return nest(
+ value.suggestion.parameterTypes,
+ (selected) => 'has parameterTypes ${valueStr(selected)}',
+ );
+ }
+
/// Return the effective replacement length.
@useResult
CheckTarget<int> get replacementLength {
@@ -320,6 +362,14 @@
}
@useResult
+ CheckTarget<int?> get requiredParameterCount {
+ return nest(
+ value.suggestion.requiredParameterCount,
+ (selected) => 'has requiredParameterCount ${valueStr(selected)}',
+ );
+ }
+
+ @useResult
CheckTarget<String?> get returnType {
return nest(
value.suggestion.returnType,
@@ -373,6 +423,10 @@
isEqualTo(CompletionSuggestionKind.IDENTIFIER);
}
+ void get isImport {
+ isEqualTo(CompletionSuggestionKind.IMPORT);
+ }
+
void get isInvocation {
isEqualTo(CompletionSuggestionKind.INVOCATION);
}
@@ -397,6 +451,45 @@
}
@useResult
+ CheckTarget<Iterable<CompletionSuggestionForTesting>> get fields {
+ var result = value
+ .where((suggestion) =>
+ suggestion.suggestion.kind == CompletionSuggestionKind.IDENTIFIER &&
+ suggestion.suggestion.element?.kind == ElementKind.FIELD)
+ .toList();
+ return nest(
+ result,
+ (selected) => 'fields ${valueStr(selected)}',
+ );
+ }
+
+ @useResult
+ CheckTarget<Iterable<CompletionSuggestionForTesting>> get getters {
+ var result = value
+ .where((suggestion) =>
+ suggestion.suggestion.kind == CompletionSuggestionKind.IDENTIFIER &&
+ suggestion.suggestion.element?.kind == ElementKind.GETTER)
+ .toList();
+ return nest(
+ result,
+ (selected) => 'getters ${valueStr(selected)}',
+ );
+ }
+
+ @useResult
+ CheckTarget<Iterable<CompletionSuggestionForTesting>> get methods {
+ var result = value
+ .where((suggestion) =>
+ suggestion.suggestion.kind == CompletionSuggestionKind.IDENTIFIER &&
+ suggestion.suggestion.element?.kind == ElementKind.METHOD)
+ .toList();
+ return nest(
+ result,
+ (selected) => 'setters ${valueStr(selected)}',
+ );
+ }
+
+ @useResult
CheckTarget<Iterable<CompletionSuggestionForTesting>> get namedArguments {
var result = value
.where((suggestion) =>
@@ -422,6 +515,19 @@
}
@useResult
+ CheckTarget<Iterable<CompletionSuggestionForTesting>> get setters {
+ var result = value
+ .where((suggestion) =>
+ suggestion.suggestion.kind == CompletionSuggestionKind.IDENTIFIER &&
+ suggestion.suggestion.element?.kind == ElementKind.SETTER)
+ .toList();
+ return nest(
+ result,
+ (selected) => 'setters ${valueStr(selected)}',
+ );
+ }
+
+ @useResult
CheckTarget<Iterable<CompletionSuggestionForTesting>> get withElementClass {
return nest(
value.where((e) {
@@ -446,6 +552,14 @@
extension ElementExtension on CheckTarget<Element> {
@useResult
+ CheckTarget<bool> get isStatic {
+ return nest(
+ value.isStatic,
+ (selected) => 'isStatic ${valueStr(selected)}',
+ );
+ }
+
+ @useResult
CheckTarget<ElementKind> get kind {
return nest(
value.kind,
diff --git a/pkg/analysis_server/test/services/completion/dart/declaration/class_test.dart b/pkg/analysis_server/test/services/completion/dart/declaration/class_test.dart
new file mode 100644
index 0000000..83f7e10
--- /dev/null
+++ b/pkg/analysis_server/test/services/completion/dart/declaration/class_test.dart
@@ -0,0 +1,249 @@
+// Copyright (c) 2022, 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_utilities/check/check.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import '../../../../client/completion_driver_test.dart';
+import '../completion_check.dart';
+
+void main() {
+ defineReflectiveSuite(() {
+ defineReflectiveTests(ClassStaticMembersWithoutClassNameTest);
+ });
+}
+
+@reflectiveTest
+class ClassStaticMembersWithoutClassNameTest
+ extends AbstractCompletionDriverTest with _Helpers {
+ @override
+ TestingCompletionProtocol get protocol => TestingCompletionProtocol.version2;
+
+ Future<void> test_field_hasContextType_exact() async {
+ await _checkLocations(
+ classCode: r'''
+class A {
+ static final int foo01 = 0;
+ static final num foo02 = 0;
+ static final double foo03 = 0;
+ final int foo04 = 0;
+}
+''',
+ contextCode: r'''
+void f() {
+ int a = foo0^
+}
+''',
+ validator: (response) {
+ check(response).suggestions.fields.completions.matchesInAnyOrder([
+ (e) => e.isEqualTo('A.foo01'),
+ ]);
+ },
+ );
+ }
+
+ Future<void> test_field_hasContextType_subtypes() async {
+ await _checkLocations(
+ classCode: r'''
+class A {
+ static final int foo01 = 0;
+ static final double foo02 = 0;
+ static final num foo03 = 0;
+ static final Object foo04 = '';
+}
+''',
+ contextCode: r'''
+void f() {
+ num a = foo0^
+}
+''',
+ validator: (response) {
+ check(response).suggestions.fields.completions.matchesInAnyOrder([
+ (e) => e.isEqualTo('A.foo01'),
+ (e) => e.isEqualTo('A.foo02'),
+ (e) => e.isEqualTo('A.foo03'),
+ ]);
+ },
+ );
+ }
+
+ Future<void> test_field_noContextType() async {
+ await _checkLocations(
+ classCode: r'''
+class A {
+ static final foo01 = 0;
+ static final foo02 = 0;
+ final foo03 = 0;
+}
+''',
+ contextCode: r'''
+void f() {
+ foo0^
+}
+''',
+ validator: (response) {
+ check(response).suggestions.fields.isEmpty;
+ },
+ );
+ }
+
+ Future<void> test_getter_hasContextType_exact() async {
+ await _checkLocations(
+ classCode: r'''
+class A {
+ static int get foo01 => 0;
+ static num get foo02 => 0;
+ static double get foo03 => 0;
+}
+''',
+ contextCode: r'''
+void f() {
+ int a = foo0^
+}
+''',
+ validator: (response) {
+ check(response).suggestions.getters.completions.matchesInAnyOrder([
+ (e) => e.isEqualTo('A.foo01'),
+ ]);
+ },
+ );
+ }
+
+ Future<void> test_getter_hasContextType_subtypes() async {
+ await _checkLocations(
+ classCode: r'''
+class A {
+ static int get foo01 => 0;
+ static double get foo02 => 0;
+ static num get foo03 => 0;
+ static Object get foo04 => '';
+}
+''',
+ contextCode: r'''
+void f() {
+ num a = foo0^
+}
+''',
+ validator: (response) {
+ check(response).suggestions.getters.completions.matchesInAnyOrder([
+ (e) => e.isEqualTo('A.foo01'),
+ (e) => e.isEqualTo('A.foo02'),
+ (e) => e.isEqualTo('A.foo03'),
+ ]);
+ },
+ );
+ }
+
+ Future<void> test_getter_noContextType() async {
+ await _checkLocations(
+ classCode: r'''
+class A {
+ static int get foo01 => 0;
+}
+''',
+ contextCode: r'''
+void f() {
+ foo0^
+}
+''',
+ validator: (response) {
+ check(response).suggestions.getters.isEmpty;
+ },
+ );
+ }
+
+ Future<void> test_method() async {
+ await _checkLocations(
+ classCode: r'''
+class A {
+ static void foo01() {}
+}
+''',
+ contextCode: r'''
+void f() {
+ foo0^
+}
+''',
+ validator: (response) {
+ check(response).suggestions.methods.isEmpty;
+ },
+ );
+ }
+
+ Future<void> test_setter_hasContextType() async {
+ await _checkLocations(
+ classCode: r'''
+class A {
+ static set foo01(int _) {}
+ static set foo02(num _) {}
+ static set foo03(double _) {}
+}
+''',
+ contextCode: r'''
+void f() {
+ int a = foo0^
+}
+''',
+ validator: (response) {
+ check(response).suggestions.setters.isEmpty;
+ },
+ );
+ }
+
+ Future<void> test_setter_noContextType() async {
+ await _checkLocations(
+ classCode: r'''
+class A {
+ static set foo01(int _) {}
+}
+''',
+ contextCode: r'''
+void f() {
+ foo0^
+}
+''',
+ validator: (response) {
+ check(response).suggestions.setters.isEmpty;
+ },
+ );
+ }
+}
+
+mixin _Helpers on AbstractCompletionDriverTest {
+ Future<void> _checkLocations({
+ required String classCode,
+ required String contextCode,
+ required void Function(CompletionResponseForTesting response) validator,
+ }) async {
+ // local
+ {
+ final response = await getTestCodeSuggestions('''
+$classCode
+
+$contextCode
+''');
+ validator(response);
+ }
+
+ // imported, without prefix
+ {
+ newFile2('$testPackageLibPath/a.dart', classCode);
+ final response = await getTestCodeSuggestions('''
+import 'a.dart';
+
+$contextCode
+''');
+ validator(response);
+ }
+
+ // not imported
+ {
+ newFile2('$testPackageLibPath/a.dart', classCode);
+ final response = await getTestCodeSuggestions('''
+$contextCode
+''');
+ validator(response);
+ }
+ }
+}
diff --git a/pkg/analysis_server/test/services/completion/dart/declaration/enum_test.dart b/pkg/analysis_server/test/services/completion/dart/declaration/enum_test.dart
index e06f764..af01add 100644
--- a/pkg/analysis_server/test/services/completion/dart/declaration/enum_test.dart
+++ b/pkg/analysis_server/test/services/completion/dart/declaration/enum_test.dart
@@ -30,8 +30,12 @@
mixin EnumTestCases on AbstractCompletionDriverTest {
Future<void> test_enumConstantName() async {
await _check_locations(
- declaration: 'enum MyEnum { foo01 }',
- codeAtCompletion: 'foo0^',
+ declaration: '''
+enum MyEnum { foo01 }
+enum OtherEnum { foo02 }
+''',
+ declarationForContextType: 'void useMyEnum(MyEnum _) {}',
+ codeAtCompletion: 'useMyEnum(foo0^);',
validator: (response) {
check(response).hasReplacement(left: 4);
@@ -61,6 +65,7 @@
Future<void> test_enumConstantName_imported_withPrefix() async {
newFile2('$testPackageLibPath/a.dart', r'''
enum MyEnum { foo01 }
+enum OtherEnum { foo02 }
''');
if (isProtocolVersion1) {
@@ -70,8 +75,10 @@
var response = await getTestCodeSuggestions('''
import 'a.dart' as prefix;
+void useMyEnum(prefix.MyEnum _) {}
+
void f() {
- foo0^
+ useMyEnum(foo0^);
}
''');
@@ -223,7 +230,8 @@
Future<void> test_nothing() async {
await _check_locations(
declaration: 'enum MyEnum { v }',
- codeAtCompletion: '^',
+ declarationForContextType: 'void useMyEnum(MyEnum _) {}',
+ codeAtCompletion: 'useMyEnum(^);',
validator: (response) {
check(response).hasEmptyReplacement();
@@ -257,8 +265,10 @@
var response = await getTestCodeSuggestions('''
import 'a.dart' as prefix;
+void useMyEnum(prefix.MyEnum _) {}
+
void f() {
- ^
+ useMyEnum(^);
}
''');
@@ -288,6 +298,7 @@
Future<void> _check_locations({
required String declaration,
+ String declarationForContextType = '',
required String codeAtCompletion,
required void Function(CompletionResponseForTesting response) validator,
}) async {
@@ -295,6 +306,7 @@
{
var response = await getTestCodeSuggestions('''
$declaration
+$declarationForContextType
void f() {
$codeAtCompletion
}
@@ -312,6 +324,7 @@
}
var response = await getTestCodeSuggestions('''
import 'a.dart';
+$declarationForContextType
void f() {
$codeAtCompletion
}
@@ -324,10 +337,15 @@
newFile2('$testPackageLibPath/a.dart', '''
$declaration
''');
+ newFile2('$testPackageLibPath/context_type.dart', '''
+import 'a.dart'; // ignore: unused_import
+$declarationForContextType
+''');
if (isProtocolVersion1) {
await waitForSetWithUri('package:test/a.dart');
}
var response = await getTestCodeSuggestions('''
+import 'context_type.dart';
void f() {
$codeAtCompletion
}
diff --git a/pkg/analysis_server/test/services/completion/dart/declaration/test_all.dart b/pkg/analysis_server/test/services/completion/dart/declaration/test_all.dart
index 12cf693..c74d1a0 100644
--- a/pkg/analysis_server/test/services/completion/dart/declaration/test_all.dart
+++ b/pkg/analysis_server/test/services/completion/dart/declaration/test_all.dart
@@ -4,12 +4,14 @@
import 'package:test_reflective_loader/test_reflective_loader.dart';
+import 'class_test.dart' as class_;
import 'enum_test.dart' as enum_;
import 'library_test.dart' as library_;
/// Tests suggestions produced for various kinds of declarations.
void main() {
defineReflectiveSuite(() {
+ class_.main();
enum_.main();
library_.main();
});
diff --git a/pkg/analysis_server/test/services/completion/dart/imported_reference_contributor_test.dart b/pkg/analysis_server/test/services/completion/dart/imported_reference_contributor_test.dart
index 4305c13..91d59b2 100644
--- a/pkg/analysis_server/test/services/completion/dart/imported_reference_contributor_test.dart
+++ b/pkg/analysis_server/test/services/completion/dart/imported_reference_contributor_test.dart
@@ -1923,8 +1923,8 @@
assertSuggestEnumConst('E.two');
assertSuggestEnum('F');
- assertSuggestEnumConst('F.three');
- assertSuggestEnumConst('F.four');
+ assertNotSuggested('F.three');
+ assertNotSuggested('F.four');
}
Future<void> test_ExpressionStatement_identifier() async {
diff --git a/pkg/analysis_server/test/services/completion/dart/local_reference_contributor_test.dart b/pkg/analysis_server/test/services/completion/dart/local_reference_contributor_test.dart
index 45e5c5d..a95020e 100644
--- a/pkg/analysis_server/test/services/completion/dart/local_reference_contributor_test.dart
+++ b/pkg/analysis_server/test/services/completion/dart/local_reference_contributor_test.dart
@@ -2312,7 +2312,7 @@
}
Future<void> test_enum() async {
- addTestSource('enum E { one, two } void f() {^}');
+ addTestSource('enum E { one, two } void f() {E v = ^}');
await computeSuggestions();
assertSuggestEnum('E');
assertSuggestEnumConst('E.one');
@@ -2322,7 +2322,7 @@
}
Future<void> test_enum_deprecated() async {
- addTestSource('@deprecated enum E { one, two } void f() {^}');
+ addTestSource('@deprecated enum E { one, two } void f() {E v = ^}');
await computeSuggestions();
assertSuggestEnum('E', isDeprecated: true);
assertSuggestEnumConst('E.one', isDeprecated: true);
@@ -2349,8 +2349,8 @@
assertSuggestEnumConst('E.two');
assertSuggestEnum('F');
- assertSuggestEnumConst('F.three');
- assertSuggestEnumConst('F.four');
+ assertNotSuggested('F.three');
+ assertNotSuggested('F.four');
}
Future<void> test_enum_filter_assignment() async {
@@ -2370,8 +2370,8 @@
assertSuggestEnumConst('E.two');
assertSuggestEnum('F');
- assertSuggestEnumConst('F.three');
- assertSuggestEnumConst('F.four');
+ assertNotSuggested('F.three');
+ assertNotSuggested('F.four');
}
Future<void> test_enum_filter_binaryEquals() async {
@@ -2412,8 +2412,8 @@
assertSuggestEnumConst('E.two');
assertSuggestEnum('F');
- assertSuggestEnumConst('F.three');
- assertSuggestEnumConst('F.four');
+ assertNotSuggested('F.three');
+ assertNotSuggested('F.four');
}
Future<void> test_enum_filter_variableDeclaration() async {
@@ -2432,8 +2432,8 @@
assertSuggestEnumConst('E.two');
assertSuggestEnum('F');
- assertSuggestEnumConst('F.three');
- assertSuggestEnumConst('F.four');
+ assertNotSuggested('F.three');
+ assertNotSuggested('F.four');
}
Future<void> test_enum_shadowed() async {
@@ -2771,42 +2771,6 @@
assertNoSuggestions();
}
- Future<void> test_flutter_setState_hasPrefix() async {
- var spaces_4 = ' ' * 4;
- var spaces_6 = ' ' * 6;
- await _check_flutter_setState(
- ' setSt',
- '''
-setState(() {
-$spaces_6
-$spaces_4});''',
- 20);
- }
-
- Future<void> test_flutter_setState_longPrefix() async {
- var spaces_6 = ' ' * 6;
- var spaces_8 = ' ' * 8;
- await _check_flutter_setState(
- ' setSt',
- '''
-setState(() {
-$spaces_8
-$spaces_6});''',
- 22);
- }
-
- Future<void> test_flutter_setState_noPrefix() async {
- var spaces_4 = ' ' * 4;
- var spaces_6 = ' ' * 6;
- await _check_flutter_setState(
- ' ',
- '''
-setState(() {
-$spaces_6
-$spaces_4});''',
- 20);
- }
-
Future<void> test_forEachPartsWithIdentifier_class() async {
addTestSource('''
class C {}
@@ -6322,36 +6286,4 @@
assertSuggestLocalVariable('value', null);
}
-
- Future<void> _check_flutter_setState(
- String line, String completion, int selectionOffset) async {
- writeTestPackageConfig(flutter: true);
- addTestSource('''
-import 'package:flutter/widgets.dart';
-
-class TestWidget extends StatefulWidget {
- @override
- State<TestWidget> createState() {
- return new TestWidgetState();
- }
-}
-
-class TestWidgetState extends State<TestWidget> {
- @override
- Widget build(BuildContext context) {
-$line^
- }
-}
-''');
- await computeSuggestions();
- var cs = assertSuggest(completion, selectionOffset: selectionOffset);
- expect(cs.selectionLength, 0);
-
- // It is an invocation, but we don't need any additional info for it.
- // So, all parameter information is absent.
- expect(cs.parameterNames, isNull);
- expect(cs.parameterTypes, isNull);
- expect(cs.requiredParameterCount, isNull);
- expect(cs.hasNamedParameters, isNull);
- }
}
diff --git a/pkg/analysis_server/test/services/completion/dart/location/block_test.dart b/pkg/analysis_server/test/services/completion/dart/location/block_test.dart
new file mode 100644
index 0000000..bf80f1b
--- /dev/null
+++ b/pkg/analysis_server/test/services/completion/dart/location/block_test.dart
@@ -0,0 +1,107 @@
+// Copyright (c) 2022, 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_utilities/check/check.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import '../../../../client/completion_driver_test.dart';
+import '../completion_check.dart';
+
+void main() {
+ defineReflectiveSuite(() {
+ defineReflectiveTests(BlockTest1);
+ defineReflectiveTests(BlockTest2);
+ });
+}
+
+@reflectiveTest
+class BlockTest1 extends AbstractCompletionDriverTest with BlockTestCases {
+ @override
+ TestingCompletionProtocol get protocol => TestingCompletionProtocol.version1;
+}
+
+@reflectiveTest
+class BlockTest2 extends AbstractCompletionDriverTest with BlockTestCases {
+ @override
+ TestingCompletionProtocol get protocol => TestingCompletionProtocol.version2;
+}
+
+mixin BlockTestCases on AbstractCompletionDriverTest {
+ static final spaces_4 = ' ' * 4;
+ static final spaces_6 = ' ' * 6;
+ static final spaces_8 = ' ' * 8;
+
+ Future<void> test_flutter_setState_indent6_hasPrefix() async {
+ await _check_flutter_setState(
+ line: '${spaces_6}setSt^',
+ completion: '''
+setState(() {
+$spaces_8
+$spaces_6});''',
+ selectionOffset: 22,
+ );
+ }
+
+ Future<void> test_flutter_setState_indent_hasPrefix() async {
+ await _check_flutter_setState(
+ line: '${spaces_4}setSt^',
+ completion: '''
+setState(() {
+$spaces_6
+$spaces_4});''',
+ selectionOffset: 20,
+ );
+ }
+
+ Future<void> test_flutter_setState_indent_noPrefix() async {
+ await _check_flutter_setState(
+ line: '$spaces_4^',
+ completion: '''
+setState(() {
+$spaces_6
+$spaces_4});''',
+ selectionOffset: 20,
+ );
+ }
+
+ Future<void> _check_flutter_setState({
+ required String line,
+ required String completion,
+ required int selectionOffset,
+ }) async {
+ writeTestPackageConfig(flutter: true);
+
+ var response = await getTestCodeSuggestions('''
+import 'package:flutter/widgets.dart';
+
+class TestWidget extends StatefulWidget {
+ @override
+ State<TestWidget> createState() {
+ return TestWidgetState();
+ }
+}
+
+class TestWidgetState extends State<TestWidget> {
+ @override
+ Widget build(BuildContext context) {
+$line
+ }
+}
+''');
+
+ check(response).suggestions.includesAll([
+ (suggestion) => suggestion
+ ..completion.startsWith('setState')
+ ..completion.isEqualTo(completion)
+ ..hasSelection(offset: selectionOffset)
+ // It is an invocation, but we don't need any additional info for it.
+ // So, all parameter information is absent.
+ ..kind.isInvocation
+ ..parameterNames.isNull
+ ..parameterTypes.isNull
+ ..requiredParameterCount.isNull
+ ..hasNamedParameters.isNull,
+ ]);
+ }
+}
diff --git a/pkg/analysis_server/test/services/completion/dart/location/directive_uri_test.dart b/pkg/analysis_server/test/services/completion/dart/location/directive_uri_test.dart
new file mode 100644
index 0000000..bb87a74
--- /dev/null
+++ b/pkg/analysis_server/test/services/completion/dart/location/directive_uri_test.dart
@@ -0,0 +1,95 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:analyzer/src/test_utilities/package_config_file_builder.dart';
+import 'package:analyzer_utilities/check/check.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import '../../../../client/completion_driver_test.dart';
+import '../completion_check.dart';
+
+void main() {
+ defineReflectiveSuite(() {
+ defineReflectiveTests(DirectiveUriTest);
+ });
+}
+
+@reflectiveTest
+class DirectiveUriTest extends AbstractCompletionDriverTest {
+ @override
+ TestingCompletionProtocol get protocol => TestingCompletionProtocol.version2;
+
+ Future<void> test_uri_end() async {
+ await _checkDirectives(
+ uriContent: 'foo0^',
+ validator: (response) {
+ // We have both `foo0x`, but no `bar`.
+ check(response).suggestions.matchesInAnyOrder([
+ (suggestion) => suggestion
+ ..isImport
+ ..completion.isEqualTo('package:foo/foo01.dart'),
+ (suggestion) => suggestion
+ ..isImport
+ ..completion.isEqualTo('package:foo/foo02.dart'),
+ ]);
+ },
+ );
+ }
+
+ Future<void> test_uri_notEnd() async {
+ await _checkDirectives(
+ uriContent: 'foo0^xyz',
+ validator: (response) {
+ // We ignore 'xyz' after the caret.
+ check(response).suggestions.matchesInAnyOrder([
+ (suggestion) => suggestion
+ ..isImport
+ ..completion.isEqualTo('package:foo/foo01.dart'),
+ (suggestion) => suggestion
+ ..isImport
+ ..completion.isEqualTo('package:foo/foo02.dart'),
+ ]);
+ },
+ );
+ }
+
+ Future<void> _checkDirectives({
+ required String uriContent,
+ required void Function(CompletionResponseForTesting response) validator,
+ }) async {
+ _configurePackagesFooBar();
+
+ {
+ var response = await getTestCodeSuggestions('''
+export '$uriContent';
+''');
+ validator(response);
+ }
+
+ {
+ var response = await getTestCodeSuggestions('''
+import '$uriContent';
+''');
+ validator(response);
+ }
+ }
+
+ void _configurePackagesFooBar() {
+ final fooPackageRoot = getFolder('$packagesRootPath/foo');
+ newFile2('$packagesRootPath/foo/lib/foo01.dart', '');
+ newFile2('$packagesRootPath/foo/lib/foo02.dart', '');
+ // We use this file to check that exactly `foo0` is used as prefix.
+ // So, we don't have one-off and don't use just `foo`.
+ newFile2('$packagesRootPath/foo/lib/foo11.dart', '');
+
+ final barPackageRoot = getFolder('$packagesRootPath/bar');
+ newFile2('$packagesRootPath/bar/lib/bar01.dart', '');
+
+ writeTestPackageConfig(
+ config: PackageConfigFileBuilder()
+ ..add(name: 'foo', rootPath: fooPackageRoot.path)
+ ..add(name: 'bar', rootPath: barPackageRoot.path),
+ );
+ }
+}
diff --git a/pkg/analysis_server/test/services/completion/dart/location/test_all.dart b/pkg/analysis_server/test/services/completion/dart/location/test_all.dart
index b0fe488..139de2f 100644
--- a/pkg/analysis_server/test/services/completion/dart/location/test_all.dart
+++ b/pkg/analysis_server/test/services/completion/dart/location/test_all.dart
@@ -4,8 +4,10 @@
import 'package:test_reflective_loader/test_reflective_loader.dart';
+import 'block_test.dart' as block;
import 'class_body_test.dart' as class_body;
import 'compilation_unit_test.dart' as compilation_unit;
+import 'directive_uri_test.dart' as directive_uri;
import 'enum_constant_test.dart' as enum_constant;
import 'enum_test.dart' as enum_;
import 'field_formal_parameter_test.dart' as field_formal_parameter;
@@ -15,8 +17,10 @@
/// Tests suggestions produced at specific locations.
void main() {
defineReflectiveSuite(() {
+ block.main();
class_body.main();
compilation_unit.main();
+ directive_uri.main();
enum_constant.main();
enum_.main();
field_formal_parameter.main();
diff --git a/pkg/analysis_server/test/src/cider/completion_test.dart b/pkg/analysis_server/test/src/cider/completion_test.dart
index 06ee178..4f3945b 100644
--- a/pkg/analysis_server/test/src/cider/completion_test.dart
+++ b/pkg/analysis_server/test/src/cider/completion_test.dart
@@ -401,7 +401,7 @@
_assertHasClass(text: 'String');
_assertHasConstructor(text: 'A');
_assertHasConstructor(text: 'B');
- _assertHasEnumConstant(text: 'E.e');
+ _assertHasEnum(text: 'E');
_assertHasMethod(text: 'foo');
_assertHasMethod(text: 'bar');
_assertHasParameter(text: 'a');
@@ -661,10 +661,10 @@
return matching.single;
}
- CompletionSuggestion _assertHasEnumConstant({required String text}) {
+ CompletionSuggestion _assertHasEnum({required String text}) {
var matching = _matchingCompletions(
text: text,
- elementKind: ElementKind.ENUM_CONSTANT,
+ elementKind: ElementKind.ENUM,
);
expect(matching, hasLength(1), reason: 'Expected exactly one completion');
return matching.single;
diff --git a/pkg/analysis_server/test/src/services/completion/dart/feature_computer_test.dart b/pkg/analysis_server/test/src/services/completion/dart/feature_computer_test.dart
index bfa63e0..f866620 100644
--- a/pkg/analysis_server/test/src/services/completion/dart/feature_computer_test.dart
+++ b/pkg/analysis_server/test/src/services/completion/dart/feature_computer_test.dart
@@ -109,6 +109,16 @@
''', 'int');
}
+ Future<void> test_argumentList_named_second() async {
+ await assertContextType('''
+void f({String p1, int p2}) {}
+
+void g() {
+ f(p1: '', p2: ^);
+}
+''', 'int');
+ }
+
Future<void> test_argumentList_named_unresolved_hasNamedParameters() async {
await assertContextType('''
void f({int i}) {}
diff --git a/runtime/tests/vm/dart/flutter_regress_106510_test.dart b/runtime/tests/vm/dart/flutter_regress_106510_test.dart
new file mode 100644
index 0000000..7c41c20
--- /dev/null
+++ b/runtime/tests/vm/dart/flutter_regress_106510_test.dart
@@ -0,0 +1,49 @@
+// Copyright (c) 2022, 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.
+
+// VMOptions=--use_slow_path --stress_write_barrier_elimination
+
+// Test that array allocation in noSuchMethod dispatcher is preserving the
+// length register correctly.
+
+import 'dart:async';
+
+import 'package:expect/expect.dart';
+
+final List<dynamic> list = <dynamic>[];
+
+final List<int> invocations = <int>[];
+
+abstract class B {
+ void someMethod(int v);
+}
+
+class A {
+ noSuchMethod(Invocation i) {
+ Expect.equals(#someMethod, i.memberName);
+ invocations.add(i.positionalArguments[0] as int);
+ }
+}
+
+class C implements B {
+ void someMethod(int v) {
+ invocations.add(v);
+ }
+}
+
+void main() {
+ for (var i = 0; i < 10; i++) {
+ list.add(A());
+ list.add(C());
+ }
+
+ for (var i = 0; i < list.length; i++) {
+ list[i].someMethod(i);
+ }
+
+ Expect.equals(list.length, invocations.length);
+ for (var i = 0; i < list.length; i++) {
+ Expect.equals(i, invocations[i]);
+ }
+}
diff --git a/runtime/tests/vm/dart_2/flutter_regress_106510_test.dart b/runtime/tests/vm/dart_2/flutter_regress_106510_test.dart
new file mode 100644
index 0000000..5139e57
--- /dev/null
+++ b/runtime/tests/vm/dart_2/flutter_regress_106510_test.dart
@@ -0,0 +1,50 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+// @dart=2.9
+
+// VMOptions=--use_slow_path --stress_write_barrier_elimination
+
+// Test that array allocation in noSuchMethod dispatcher is preserving the
+// length register correctly.
+
+import 'dart:async';
+
+import 'package:expect/expect.dart';
+
+final List<dynamic> list = <dynamic>[];
+
+final List<int> invocations = <int>[];
+
+abstract class B {
+ void someMethod(int v);
+}
+
+class A {
+ noSuchMethod(Invocation i) {
+ Expect.equals(#someMethod, i.memberName);
+ invocations.add(i.positionalArguments[0] as int);
+ }
+}
+
+class C implements B {
+ void someMethod(int v) {
+ invocations.add(v);
+ }
+}
+
+void main() {
+ for (var i = 0; i < 10; i++) {
+ list.add(A());
+ list.add(C());
+ }
+
+ for (var i = 0; i < list.length; i++) {
+ list[i].someMethod(i);
+ }
+
+ Expect.equals(list.length, invocations.length);
+ for (var i = 0; i < list.length; i++) {
+ Expect.equals(i, invocations[i]);
+ }
+}
diff --git a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
index 7e93579..341408a 100644
--- a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
+++ b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
@@ -1104,7 +1104,9 @@
return BuildStatement(); // read statement.
}
-Fragment StreamingFlowGraphBuilder::BuildExpression(TokenPosition* position) {
+Fragment StreamingFlowGraphBuilder::BuildExpression(
+ TokenPosition* position,
+ bool allow_late_uninitialized) {
++num_ast_nodes_;
uint8_t payload = 0;
Tag tag = ReadTag(&payload); // read tag.
@@ -1112,9 +1114,9 @@
case kInvalidExpression:
return BuildInvalidExpression(position);
case kVariableGet:
- return BuildVariableGet(position);
+ return BuildVariableGet(position, allow_late_uninitialized);
case kSpecializedVariableGet:
- return BuildVariableGet(payload, position);
+ return BuildVariableGet(payload, position, allow_late_uninitialized);
case kVariableSet:
return BuildVariableSet(position);
case kSpecializedVariableSet:
@@ -5568,26 +5570,12 @@
ASSERT(positional_count == 1);
// The CFE transform only generates a subset of argument expressions:
- // either variable get or `this`.
- uint8_t payload = 0;
- Tag tag = ReadTag(&payload);
+ // either variable get or `this`. However, subsequent transforms can
+ // generate different expressions, including: constant expressions.
+ // So, build an arbitrary expression here instead.
TokenPosition* position = nullptr;
const bool allow_late_uninitialized = true;
- Fragment code;
- switch (tag) {
- case kVariableGet:
- code = BuildVariableGet(position, allow_late_uninitialized);
- break;
- case kSpecializedVariableGet:
- code = BuildVariableGet(payload, position, allow_late_uninitialized);
- break;
- case kThisExpression:
- code = BuildThisExpression(position);
- break;
- default:
- // The transformation should not be generating anything else.
- FATAL1("Unexpected tag %i", tag);
- }
+ Fragment code = BuildExpression(position, allow_late_uninitialized);
const intptr_t named_args_len = ReadListLength();
ASSERT(named_args_len == 0);
diff --git a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.h b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.h
index 29adbcd..0ae3163 100644
--- a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.h
+++ b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.h
@@ -70,7 +70,8 @@
Fragment BuildInitializers(const Class& parent_class);
FlowGraph* BuildGraphOfFunction(bool constructor);
- Fragment BuildExpression(TokenPosition* position = nullptr);
+ Fragment BuildExpression(TokenPosition* position = nullptr,
+ bool allow_late_uninitialized = false);
Fragment BuildStatement(TokenPosition* position = nullptr);
Fragment BuildStatementWithBranchCoverage(TokenPosition* position = nullptr);
diff --git a/runtime/vm/compiler/stub_code_compiler_arm.cc b/runtime/vm/compiler/stub_code_compiler_arm.cc
index dda5901..b3aef4f 100644
--- a/runtime/vm/compiler/stub_code_compiler_arm.cc
+++ b/runtime/vm/compiler/stub_code_compiler_arm.cc
@@ -46,7 +46,7 @@
{
LeafRuntimeScope rt(assembler,
/*frame_size=*/0,
- /*preserve_registers=*/false);
+ /*preserve_registers=*/preserve_registers);
// [R0] already contains first argument.
__ mov(R1, Operand(THR));
rt.Call(kEnsureRememberedAndMarkingDeferredRuntimeEntry, 2);
diff --git a/tests/ffi/regress_49402_test.dart b/tests/ffi/regress_49402_test.dart
new file mode 100644
index 0000000..e31384b
--- /dev/null
+++ b/tests/ffi/regress_49402_test.dart
@@ -0,0 +1,13 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:ffi';
+
+class A implements Finalizable {}
+
+void b([A? a]) {}
+
+void main() {
+ b();
+}
diff --git a/tests/ffi_2/regress_49402_test.dart b/tests/ffi_2/regress_49402_test.dart
new file mode 100644
index 0000000..e9a71ba
--- /dev/null
+++ b/tests/ffi_2/regress_49402_test.dart
@@ -0,0 +1,15 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// @dart=2.9
+
+import 'dart:ffi';
+
+class A implements Finalizable {}
+
+void b([A a]) {}
+
+void main() {
+ b();
+}
diff --git a/tools/VERSION b/tools/VERSION
index 7e91410..7f52416 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -26,6 +26,6 @@
CHANNEL stable
MAJOR 2
MINOR 17
-PATCH 5
+PATCH 6
PRERELEASE 0
PRERELEASE_PATCH 0
\ No newline at end of file