Version 2.12.0-265.0.dev
Merge commit '934980be801c8007189500ee6980fd81b9eca079' into 'dev'
diff --git a/pkg/analysis_server/lib/src/context_manager.dart b/pkg/analysis_server/lib/src/context_manager.dart
index d552d08..aacaaeed 100644
--- a/pkg/analysis_server/lib/src/context_manager.dart
+++ b/pkg/analysis_server/lib/src/context_manager.dart
@@ -1324,9 +1324,6 @@
if (info.excludes(path)) {
return;
}
- if (info.ignored(path)) {
- return;
- }
// handle the change
switch (type) {
case ChangeType.ADD:
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/library_member_contributor.dart b/pkg/analysis_server/lib/src/services/completion/dart/library_member_contributor.dart
index 4801bf8..151f658 100644
--- a/pkg/analysis_server/lib/src/services/completion/dart/library_member_contributor.dart
+++ b/pkg/analysis_server/lib/src/services/completion/dart/library_member_contributor.dart
@@ -62,7 +62,7 @@
} else {
if (element is ClassElement ||
element is ExtensionElement ||
- element is FunctionTypeAliasElement) {
+ element is TypeAliasElement) {
builder.suggestElement(element,
kind: CompletionSuggestionKind.INVOCATION);
} else if (!typesOnly &&
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 4b1b121..32e454f 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
@@ -96,7 +96,7 @@
@override
void visitFunctionTypeAliasElement(FunctionTypeAliasElement element) {
if (opType.includeTypeNameSuggestions) {
- builder.suggestFunctionTypeAlias(element, prefix: prefix);
+ builder.suggestTypeAlias(element, prefix: prefix);
}
}
@@ -126,6 +126,13 @@
}
}
+ @override
+ void visitTypeAliasElement(TypeAliasElement element) {
+ if (opType.includeTypeNameSuggestions) {
+ builder.suggestTypeAlias(element, prefix: prefix);
+ }
+ }
+
/// Add constructor suggestions for the given class.
void _addConstructorSuggestions(ClassElement classElem) {
for (var constructor in classElem.constructors) {
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 4c87bce..53fed10 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
@@ -310,16 +310,14 @@
@override
void declaredFunctionTypeAlias(FunctionTypeAlias declaration) {
if (opType.includeTypeNameSuggestions) {
- builder.suggestFunctionTypeAlias(declaration.declaredElement,
- kind: _defaultKind);
+ builder.suggestTypeAlias(declaration.declaredElement, kind: _defaultKind);
}
}
@override
void declaredGenericTypeAlias(GenericTypeAlias declaration) {
if (opType.includeTypeNameSuggestions) {
- builder.suggestFunctionTypeAlias(declaration.declaredElement,
- kind: _defaultKind);
+ builder.suggestTypeAlias(declaration.declaredElement, kind: _defaultKind);
}
}
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 0cb3b8b..276c615 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
@@ -382,11 +382,11 @@
} else if (element is FunctionElement &&
element.enclosingElement is CompilationUnitElement) {
suggestTopLevelFunction(element, kind: kind);
- } else if (element is FunctionTypeAliasElement) {
- suggestFunctionTypeAlias(element, kind: kind);
} else if (element is PropertyAccessorElement &&
element.enclosingElement is CompilationUnitElement) {
suggestTopLevelPropertyAccessor(element, kind: kind);
+ } else if (element is TypeAliasElement) {
+ suggestTypeAlias(element, kind: kind);
} else {
throw ArgumentError('Cannot suggest a ${element.runtimeType}');
}
@@ -487,19 +487,6 @@
));
}
- /// Add a suggestion for a [functionTypeAlias]. If a [kind] is provided it
- /// will be used as the kind for the suggestion. If the alias can only be
- /// referenced using a prefix, then the [prefix] should be provided.
- void suggestFunctionTypeAlias(FunctionTypeAliasElement functionTypeAlias,
- {CompletionSuggestionKind kind = CompletionSuggestionKind.INVOCATION,
- String prefix}) {
- var relevance = _computeTopLevelRelevance(functionTypeAlias,
- defaultRelevance: 750,
- elementType: _instantiateFunctionTypeAlias(functionTypeAlias));
- _add(_createSuggestion(functionTypeAlias,
- kind: kind, prefix: prefix, relevance: relevance));
- }
-
/// Add a suggestion for a [keyword]. The [offset] is the offset from the
/// beginning of the keyword where the cursor will be left.
void suggestKeyword(String keyword, {int offset}) {
@@ -863,6 +850,18 @@
kind: kind, prefix: prefix, relevance: relevance));
}
+ /// Add a suggestion for a [typeAlias]. If a [kind] is provided it
+ /// will be used as the kind for the suggestion. If the alias can only be
+ /// referenced using a prefix, then the [prefix] should be provided.
+ void suggestTypeAlias(TypeAliasElement typeAlias,
+ {CompletionSuggestionKind kind = CompletionSuggestionKind.INVOCATION,
+ String prefix}) {
+ var relevance = _computeTopLevelRelevance(typeAlias,
+ defaultRelevance: 750, elementType: _instantiateTypeAlias(typeAlias));
+ _add(_createSuggestion(typeAlias,
+ kind: kind, prefix: prefix, relevance: relevance));
+ }
+
/// Add a suggestion for a type [parameter].
void suggestTypeParameter(TypeParameterElement parameter) {
var elementKind = _computeElementKind(parameter);
@@ -1083,7 +1082,7 @@
);
}
- FunctionType _instantiateFunctionTypeAlias(FunctionTypeAliasElement element) {
+ DartType _instantiateTypeAlias(TypeAliasElement element) {
var typeParameters = element.typeParameters;
var typeArguments = const <DartType>[];
if (typeParameters.isNotEmpty) {
diff --git a/pkg/analysis_server/lib/src/services/refactoring/naming_conventions.dart b/pkg/analysis_server/lib/src/services/refactoring/naming_conventions.dart
index d139ce5..3cd4fe3 100644
--- a/pkg/analysis_server/lib/src/services/refactoring/naming_conventions.dart
+++ b/pkg/analysis_server/lib/src/services/refactoring/naming_conventions.dart
@@ -46,14 +46,6 @@
/// - OK if the name is valid;
/// - WARNING if the name is discouraged;
/// - FATAL if the name is illegal.
-RefactoringStatus validateFunctionTypeAliasName(String name) {
- return _validateUpperCamelCase(name, 'Function type alias');
-}
-
-/// Returns the [RefactoringStatus] with severity:
-/// - OK if the name is valid;
-/// - WARNING if the name is discouraged;
-/// - FATAL if the name is illegal.
RefactoringStatus validateImportPrefixName(String name) {
if (name != null && name.isEmpty) {
return RefactoringStatus();
@@ -124,6 +116,14 @@
/// - OK if the name is valid;
/// - WARNING if the name is discouraged;
/// - FATAL if the name is illegal.
+RefactoringStatus validateTypeAliasName(String name) {
+ return _validateUpperCamelCase(name, 'Type alias');
+}
+
+/// Returns the [RefactoringStatus] with severity:
+/// - OK if the name is valid;
+/// - WARNING if the name is discouraged;
+/// - FATAL if the name is illegal.
RefactoringStatus validateVariableName(String name) {
return _validateLowerCamelCase(name, 'Variable', allowBuiltIn: true);
}
diff --git a/pkg/analysis_server/lib/src/services/refactoring/rename_unit_member.dart b/pkg/analysis_server/lib/src/services/refactoring/rename_unit_member.dart
index 0838f89..05cf609 100644
--- a/pkg/analysis_server/lib/src/services/refactoring/rename_unit_member.dart
+++ b/pkg/analysis_server/lib/src/services/refactoring/rename_unit_member.dart
@@ -54,12 +54,12 @@
if (element is FunctionElement) {
return 'Rename Top-Level Function';
}
- if (element is FunctionTypeAliasElement) {
- return 'Rename Function Type Alias';
- }
if (element is TopLevelVariableElement) {
return 'Rename Top-Level Variable';
}
+ if (element is TypeAliasElement) {
+ return 'Rename Type Alias';
+ }
return 'Rename Class';
}
@@ -94,8 +94,8 @@
if (element is FunctionElement) {
result.addStatus(validateFunctionName(newName));
}
- if (element is FunctionTypeAliasElement) {
- result.addStatus(validateFunctionTypeAliasName(newName));
+ if (element is TypeAliasElement) {
+ result.addStatus(validateTypeAliasName(newName));
}
if (element is ClassElement) {
result.addStatus(validateClassName(newName));
diff --git a/pkg/analysis_server/test/abstract_context.dart b/pkg/analysis_server/test/abstract_context.dart
index 25a3347..f6cf3a5 100644
--- a/pkg/analysis_server/test/abstract_context.dart
+++ b/pkg/analysis_server/test/abstract_context.dart
@@ -282,7 +282,6 @@
@override
String get testPackageLanguageVersion => null;
- @nonVirtual
@override
void setUp() {
super.setUp();
diff --git a/pkg/analysis_server/test/context_manager_test.dart b/pkg/analysis_server/test/context_manager_test.dart
index 61e3ffa..fea3b43 100644
--- a/pkg/analysis_server/test/context_manager_test.dart
+++ b/pkg/analysis_server/test/context_manager_test.dart
@@ -227,32 +227,6 @@
});
}
- Future<void> test_path_filter() async {
- // Setup context.
- var root = newFolder(projPath);
- manager.setRoots(<String>[projPath], <String>[]);
- expect(callbacks.currentFilePaths, isEmpty);
- // Set ignore patterns for context.
- var rootInfo = manager.getContextInfoFor(root);
- manager.setIgnorePatternsForContext(
- rootInfo, ['sdk_ext/**', 'lib/ignoreme.dart']);
- // Start creating files.
- newFile('$projPath/${ContextManagerImpl.PUBSPEC_NAME}');
- var libPath = '$projPath/${ContextManagerTest.LIB_NAME}';
- newFile('$libPath/main.dart');
- newFile('$libPath/ignoreme.dart');
- var sdkExtPath = '$projPath/sdk_ext';
- newFile('$sdkExtPath/entry.dart');
- var sdkExtSrcPath = '$projPath/sdk_ext/src';
- newFile('$sdkExtSrcPath/part.dart');
- // Pump event loop so new files are discovered and added to context.
- await pumpEventQueue();
- // Verify that ignored files were ignored.
- var filePaths = callbacks.currentFilePaths;
- expect(filePaths, hasLength(1));
- expect(filePaths, contains(convertPath('/my/proj/lib/main.dart')));
- }
-
Future<void> test_refresh_folder_with_packagespec() {
// create a context with a .packages file
var packagespecFile = join(projPath, '.packages');
diff --git a/pkg/analysis_server/test/domain_analysis_test.dart b/pkg/analysis_server/test/domain_analysis_test.dart
index 9b966d2..06c2e37 100644
--- a/pkg/analysis_server/test/domain_analysis_test.dart
+++ b/pkg/analysis_server/test/domain_analysis_test.dart
@@ -305,6 +305,18 @@
class AnalysisDomainTest extends AbstractAnalysisTest {
Map<String, List<AnalysisError>> filesErrors = {};
+ void assertHasErrors(String path) {
+ expect(filesErrors[path], isNotEmpty);
+ }
+
+ void assertNoErrors(String path) {
+ expect(filesErrors[path], isEmpty);
+ }
+
+ void assertNoErrorsNotification(String path) {
+ expect(filesErrors[path], isNull);
+ }
+
@override
void processNotification(Notification notification) {
if (notification.event == ANALYSIS_NOTIFICATION_ERRORS) {
@@ -313,6 +325,117 @@
}
}
+ Future<void> test_fileSystem_addFile_excluded() async {
+ var a_path = convertPath('$projectPath/lib/a.dart');
+ var b_path = convertPath('$projectPath/lib/b.dart');
+
+ newFile('$projectPath/analysis_options.yaml', content: r'''
+analyzer:
+ exclude:
+ - "**/a.dart"
+''');
+
+ newFile(b_path, content: r'''
+import 'a.dart';
+void f(A a) {}
+''');
+
+ createProject();
+ await pumpEventQueue();
+ await server.onAnalysisComplete;
+
+ // We don't have a.dart, so the import cannot be resolved.
+ assertHasErrors(b_path);
+
+ newFile(a_path, content: r'''
+class A {}
+''');
+ await pumpEventQueue();
+ await server.onAnalysisComplete;
+
+ // We excluded 'a.dart' from analysis, no errors notification for it.
+ assertNoErrorsNotification(a_path);
+
+ // We added a.dart with `A`, so no errors.
+ assertNoErrors(b_path);
+ }
+
+ Future<void> test_fileSystem_changeFile_excluded() async {
+ var a_path = convertPath('$projectPath/lib/a.dart');
+ var b_path = convertPath('$projectPath/lib/b.dart');
+
+ newFile('$projectPath/analysis_options.yaml', content: r'''
+analyzer:
+ exclude:
+ - "**/a.dart"
+''');
+
+ newFile(a_path, content: r'''
+class B {}
+''');
+
+ newFile(b_path, content: r'''
+import 'a.dart';
+void f(A a) {}
+''');
+
+ createProject();
+ await pumpEventQueue();
+ await server.onAnalysisComplete;
+
+ // We excluded 'a.dart' from analysis, no errors notification for it.
+ assertNoErrorsNotification(a_path);
+
+ // We have `B`, not `A`, in a.dart, so has errors.
+ assertHasErrors(b_path);
+
+ newFile(a_path, content: r'''
+class A {}
+''');
+ await pumpEventQueue();
+ await server.onAnalysisComplete;
+
+ // We changed a.dart, to have `A`, so no errors.
+ assertNoErrors(b_path);
+ }
+
+ Future<void> test_fileSystem_deleteFile_excluded() async {
+ var a_path = convertPath('$projectPath/lib/a.dart');
+ var b_path = convertPath('$projectPath/lib/b.dart');
+
+ newFile('$projectPath/analysis_options.yaml', content: r'''
+analyzer:
+ exclude:
+ - "**/a.dart"
+''');
+
+ newFile(a_path, content: r'''
+class A {}
+''');
+
+ newFile(b_path, content: r'''
+import 'a.dart';
+void f(A a) {}
+''');
+
+ createProject();
+ await pumpEventQueue();
+ await server.onAnalysisComplete;
+
+ // We excluded 'a.dart' from analysis, no errors notification for it.
+ assertNoErrorsNotification(a_path);
+
+ // We have `A` in a.dart, so no errors.
+ assertNoErrors(b_path);
+
+ deleteFile(a_path);
+ await pumpEventQueue();
+ await server.onAnalysisComplete;
+
+ // We deleted a.dart, so `A` cannot be resolved.
+ assertHasErrors(b_path);
+ }
+
Future<void> test_setRoots_packages() {
// prepare package
var pkgFile = newFile('/packages/pkgA/libA.dart', content: '''
diff --git a/pkg/analysis_server/test/services/completion/dart/arglist_contributor_test.dart b/pkg/analysis_server/test/services/completion/dart/arglist_contributor_test.dart
index f91b4f7..0f6e004 100644
--- a/pkg/analysis_server/test/services/completion/dart/arglist_contributor_test.dart
+++ b/pkg/analysis_server/test/services/completion/dart/arglist_contributor_test.dart
@@ -715,8 +715,8 @@
addTestSource('main() { int.parse("16", ^);}');
await computeSuggestions();
assertSuggestArgumentsAndTypes(namedArgumentsWithTypes: {
- 'radix': 'int',
- 'onError': 'int Function(String)'
+ 'radix': 'int?',
+ 'onError': 'int Function(String)?'
});
}
@@ -725,8 +725,8 @@
addTestSource('main() { int.parse("16", r^);}');
await computeSuggestions();
assertSuggestArgumentsAndTypes(namedArgumentsWithTypes: {
- 'radix': 'int',
- 'onError': 'int Function(String)'
+ 'radix': 'int?',
+ 'onError': 'int Function(String)?'
});
}
@@ -735,7 +735,7 @@
addTestSource('main() { int.parse("16", radix: 7, ^);}');
await computeSuggestions();
assertSuggestArgumentsAndTypes(
- namedArgumentsWithTypes: {'onError': 'int Function(String)'});
+ namedArgumentsWithTypes: {'onError': 'int Function(String)?'});
}
Future<void> test_ArgumentList_imported_function_named_param2a() async {
@@ -750,8 +750,8 @@
addTestSource('main() { int.parse("16", r^: 16);}');
await computeSuggestions();
assertSuggestArgumentsAndTypes(namedArgumentsWithTypes: {
- 'radix': 'int',
- 'onError': 'int Function(String)'
+ 'radix': 'int?',
+ 'onError': 'int Function(String)?'
}, includeColon: false);
}
@@ -767,8 +767,8 @@
addTestSource('main() { int.parse("16", ^: 16);}');
await computeSuggestions();
assertSuggestArgumentsAndTypes(namedArgumentsWithTypes: {
- 'radix': 'int',
- 'onError': 'int Function(String)'
+ 'radix': 'int?',
+ 'onError': 'int Function(String)?'
});
}
diff --git a/pkg/analysis_server/test/services/completion/dart/combinator_contributor_test.dart b/pkg/analysis_server/test/services/completion/dart/combinator_contributor_test.dart
index f5faa6a..ae98d85 100644
--- a/pkg/analysis_server/test/services/completion/dart/combinator_contributor_test.dart
+++ b/pkg/analysis_server/test/services/completion/dart/combinator_contributor_test.dart
@@ -107,7 +107,7 @@
assertSuggestFunction('F1', 'PB',
kind: CompletionSuggestionKind.IDENTIFIER);
assertSuggestClass('Clz', kind: CompletionSuggestionKind.IDENTIFIER);
- assertSuggestFunctionTypeAlias('F2', null,
+ assertSuggestTypeAlias('F2', null,
kind: CompletionSuggestionKind.IDENTIFIER);
assertNotSuggested('C');
assertNotSuggested('D');
diff --git a/pkg/analysis_server/test/services/completion/dart/completion_contributor_util.dart b/pkg/analysis_server/test/services/completion/dart/completion_contributor_util.dart
index 89353f4..781fb5e 100644
--- a/pkg/analysis_server/test/services/completion/dart/completion_contributor_util.dart
+++ b/pkg/analysis_server/test/services/completion/dart/completion_contributor_util.dart
@@ -68,7 +68,8 @@
}
}
-abstract class _BaseDartCompletionContributorTest extends AbstractContextTest {
+abstract class _BaseDartCompletionContributorTest extends AbstractContextTest
+ with WithNonFunctionTypeAliasesMixin {
static const String _UNCHECKED = '__UNCHECKED__';
String testFile;
int completionOffset;
@@ -354,36 +355,6 @@
return cs;
}
- CompletionSuggestion assertSuggestFunctionTypeAlias(
- String name,
- String returnType, {
- bool isDeprecated = false,
- CompletionSuggestionKind kind = CompletionSuggestionKind.INVOCATION,
- }) {
- var cs = assertSuggest(name, csKind: kind, isDeprecated: isDeprecated);
- if (returnType != null) {
- expect(cs.returnType, returnType);
- } else if (isNullExpectedReturnTypeConsideredDynamic) {
- expect(cs.returnType, 'dynamic');
- } else {
- expect(cs.returnType, isNull);
- }
- var element = cs.element;
- expect(element, isNotNull);
- expect(element.kind, equals(ElementKind.FUNCTION_TYPE_ALIAS));
- expect(element.name, equals(name));
- expect(element.isDeprecated, equals(isDeprecated));
- // TODO (danrubel) Determine why params are null
- // String param = element.parameters;
- // expect(param, isNotNull);
- // expect(param[0], equals('('));
- // expect(param[param.length - 1], equals(')'));
- expect(element.returnType, equals(returnType ?? 'dynamic'));
- // TODO (danrubel) Determine why param info is missing
- // assertHasParameterInfo(cs);
- return cs;
- }
-
CompletionSuggestion assertSuggestGetter(String name, String returnType,
{CompletionSuggestionKind kind = CompletionSuggestionKind.INVOCATION,
bool isDeprecated = false}) {
@@ -529,6 +500,36 @@
return cs;
}
+ CompletionSuggestion assertSuggestTypeAlias(
+ String name,
+ String returnType, {
+ bool isDeprecated = false,
+ CompletionSuggestionKind kind = CompletionSuggestionKind.INVOCATION,
+ }) {
+ var cs = assertSuggest(name, csKind: kind, isDeprecated: isDeprecated);
+ if (returnType != null) {
+ expect(cs.returnType, returnType);
+ } else if (isNullExpectedReturnTypeConsideredDynamic) {
+ expect(cs.returnType, 'dynamic');
+ } else {
+ expect(cs.returnType, isNull);
+ }
+ var element = cs.element;
+ expect(element, isNotNull);
+ expect(element.kind, equals(ElementKind.TYPE_ALIAS));
+ expect(element.name, equals(name));
+ expect(element.isDeprecated, equals(isDeprecated));
+ // TODO (danrubel) Determine why params are null
+ // String param = element.parameters;
+ // expect(param, isNotNull);
+ // expect(param[0], equals('('));
+ // expect(param[param.length - 1], equals(')'));
+ expect(element.returnType, equals(returnType ?? 'dynamic'));
+ // TODO (danrubel) Determine why param info is missing
+ // assertHasParameterInfo(cs);
+ return cs;
+ }
+
CompletionSuggestion assertSuggestTypeParameter(String name) {
var cs = assertSuggest(name, csKind: CompletionSuggestionKind.IDENTIFIER);
expect(cs.returnType, isNull);
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 a83e323..e6b02e7 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
@@ -2400,7 +2400,7 @@
assertSuggestClass('Object');
assertNotSuggested('T1');
assertNotSuggested('F1');
- assertSuggestFunctionTypeAlias('D1', 'dynamic');
+ assertSuggestTypeAlias('D1', 'dynamic');
assertSuggestClass('C1');
assertNotSuggested('T2');
assertNotSuggested('F2');
@@ -2430,7 +2430,7 @@
assertSuggestClass('Object');
assertNotSuggested('T1');
assertNotSuggested('F1');
- assertSuggestFunctionTypeAlias('D1', 'dynamic');
+ assertSuggestTypeAlias('D1', 'dynamic');
assertSuggestClass('C1');
assertNotSuggested('T2');
assertNotSuggested('F2');
@@ -2461,7 +2461,7 @@
assertSuggestClass('Object');
assertNotSuggested('T1');
assertNotSuggested('F1');
- assertSuggestFunctionTypeAlias('D1', 'dynamic');
+ assertSuggestTypeAlias('D1', 'dynamic');
assertSuggestClass('C1');
assertNotSuggested('T2');
assertNotSuggested('F2');
@@ -2498,7 +2498,7 @@
}
''');
await computeSuggestions();
- assertSuggestFunctionTypeAlias('F', 'void');
+ assertSuggestTypeAlias('F', 'void');
}
Future<void> test_functionTypeAlias_old() async {
@@ -2513,7 +2513,7 @@
}
''');
await computeSuggestions();
- assertSuggestFunctionTypeAlias('F', 'void');
+ assertSuggestTypeAlias('F', 'void');
}
Future<void> test_IfStatement() async {
@@ -2914,7 +2914,7 @@
// in which case suggestions will have null (unresolved) returnType
assertSuggestTopLevelVar('T1', null);
assertSuggestFunction('F1', null);
- assertSuggestFunctionTypeAlias('D1', 'dynamic');
+ assertSuggestTypeAlias('D1', 'dynamic');
assertSuggestClass('C1');
assertNotSuggested('T2');
assertNotSuggested('F2');
@@ -3178,7 +3178,7 @@
// to be resolved.
assertSuggestTopLevelVar('T1', /* null */ 'int');
assertSuggestFunction('F1', /* null */ 'dynamic');
- assertSuggestFunctionTypeAlias('D1', /* null */ 'dynamic');
+ assertSuggestTypeAlias('D1', /* null */ 'dynamic');
assertSuggestClass('C1');
assertNotSuggested('T2');
assertNotSuggested('F2');
@@ -3477,7 +3477,7 @@
assertSuggestClass('Object');
assertNotSuggested('T1');
assertNotSuggested('F1');
- assertSuggestFunctionTypeAlias('D1', 'dynamic');
+ assertSuggestTypeAlias('D1', 'dynamic');
assertSuggestClass('C1');
assertNotSuggested('T2');
assertNotSuggested('F2');
@@ -3506,7 +3506,7 @@
assertSuggestClass('Object');
assertNotSuggested('T1');
assertNotSuggested('F1');
- assertSuggestFunctionTypeAlias('D1', 'dynamic');
+ assertSuggestTypeAlias('D1', 'dynamic');
assertSuggestClass('C1');
assertNotSuggested('T2');
assertNotSuggested('F2');
@@ -3535,7 +3535,7 @@
assertSuggestClass('Object');
assertNotSuggested('T1');
assertNotSuggested('F1');
- assertSuggestFunctionTypeAlias('D1', 'dynamic');
+ assertSuggestTypeAlias('D1', 'dynamic');
assertSuggestClass('C1');
assertNotSuggested('T2');
assertNotSuggested('F2');
@@ -3566,7 +3566,7 @@
assertSuggestClass('Object');
assertNotSuggested('T1');
assertNotSuggested('F1');
- assertSuggestFunctionTypeAlias('D1', 'dynamic');
+ assertSuggestTypeAlias('D1', 'dynamic');
assertSuggestClass('C1');
assertNotSuggested('T2');
assertNotSuggested('F2');
@@ -4564,7 +4564,7 @@
expect(replacementLength, 0);
assertSuggestClass('Object');
assertSuggestClass('C1');
- assertSuggestFunctionTypeAlias('T1', 'String');
+ assertSuggestTypeAlias('T1', 'String');
assertNotSuggested('C2');
assertNotSuggested('T2');
assertNotSuggested('F1');
diff --git a/pkg/analysis_server/test/services/completion/dart/library_member_contributor_test.dart b/pkg/analysis_server/test/services/completion/dart/library_member_contributor_test.dart
index 95a7d99..f73cdd7 100644
--- a/pkg/analysis_server/test/services/completion/dart/library_member_contributor_test.dart
+++ b/pkg/analysis_server/test/services/completion/dart/library_member_contributor_test.dart
@@ -224,7 +224,10 @@
lib B;
var T1;
class X { }
- class Y { }''');
+ class Y { }
+ typedef void TypeAliasLegacy();
+ typedef TypeAliasFunctionType = void Function();
+ typedef TypeAliasInterfaceType = List<int>;''');
addTestSource('''
import "b.dart" as b;
var T2;
@@ -235,6 +238,9 @@
expect(replacementLength, 0);
assertSuggestClass('X');
assertSuggestClass('Y');
+ assertSuggestTypeAlias('TypeAliasLegacy', 'void');
+ assertSuggestTypeAlias('TypeAliasFunctionType', 'void');
+ assertSuggestTypeAlias('TypeAliasInterfaceType', 'List<int>');
assertNotSuggested('T1');
assertNotSuggested('T2');
assertNotSuggested('Object');
diff --git a/pkg/analysis_server/test/services/completion/dart/local_library_contributor_test.dart b/pkg/analysis_server/test/services/completion/dart/local_library_contributor_test.dart
index 08f9a72..ebf0fdf 100644
--- a/pkg/analysis_server/test/services/completion/dart/local_library_contributor_test.dart
+++ b/pkg/analysis_server/test/services/completion/dart/local_library_contributor_test.dart
@@ -212,6 +212,8 @@
class A { var a1; a2(){}}
var m;
typedef t1(int blue);
+ typedef t2 = void Function(int blue);
+ typedef t3 = List<int>;
int af() {return 0;}''');
addTestSource('''
part of libA;
@@ -227,7 +229,9 @@
}
assertSuggestFunction('af', 'int');
assertSuggestTopLevelVar('m', null);
- assertSuggestFunctionTypeAlias('t1', null);
+ assertSuggestTypeAlias('t1', null);
+ assertSuggestTypeAlias('t2', 'void');
+ assertSuggestTypeAlias('t3', 'List<int>');
assertNotSuggested('a1');
assertNotSuggested('a2');
// Suggested by LocalConstructorContributor
@@ -270,7 +274,7 @@
}
assertSuggestFunction('bf', 'int');
assertSuggestTopLevelVar('n', null);
- assertSuggestFunctionTypeAlias('t1', null);
+ assertSuggestTypeAlias('t1', null);
assertNotSuggested('b1');
assertNotSuggested('b2');
// Suggested by ConstructorContributor
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 571d7b2..5475a78 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
@@ -1667,7 +1667,7 @@
expect(replacementOffset, completionOffset);
expect(replacementLength, 0);
- assertSuggestParameter('e', null);
+ assertSuggestParameter('e', 'Object');
assertSuggestParameter('s', 'StackTrace');
assertSuggestMethod('a', 'A', null);
assertNotSuggested('Object');
@@ -2588,7 +2588,7 @@
assertSuggestClass('C');
assertSuggestMethod('foo', 'C', null);
assertSuggestMethod('bar', 'C', 'void');
- assertSuggestFunctionTypeAlias('F2', 'int');
+ assertSuggestTypeAlias('F2', 'int');
assertSuggestClass('Clz');
assertSuggestClass('C');
assertNotSuggested('x');
@@ -3242,7 +3242,7 @@
assertNotSuggested('C1');
assertNotSuggested('T2');
assertNotSuggested('F2');
- assertSuggestFunctionTypeAlias('D2', 'dynamic');
+ assertSuggestTypeAlias('D2', 'dynamic');
assertSuggestClass('C2');
assertNotSuggested('name');
}
@@ -3272,7 +3272,7 @@
assertNotSuggested('C1');
assertNotSuggested('T2');
assertNotSuggested('F2');
- assertSuggestFunctionTypeAlias('D2', 'dynamic');
+ assertSuggestTypeAlias('D2', 'dynamic');
assertSuggestClass('C2');
assertNotSuggested('name');
}
@@ -3303,7 +3303,7 @@
assertNotSuggested('C1');
assertNotSuggested('T2');
assertNotSuggested('F2');
- assertSuggestFunctionTypeAlias('D2', 'dynamic');
+ assertSuggestTypeAlias('D2', 'dynamic');
assertSuggestClass('C2');
assertNotSuggested('name');
}
@@ -3387,39 +3387,6 @@
assertSuggestTypeParameter('T');
}
- Future<void> test_functionTypeAlias_genericTypeAlias() async {
- addTestSource(r'''
-typedef F = void Function();
-main() {
- ^
-}
-''');
- await computeSuggestions();
- assertSuggestFunctionTypeAlias('F', 'void');
- }
-
- Future<void> test_functionTypeAlias_genericTypeAlias_incomplete() async {
- addTestSource(r'''
-typedef F = int;
-main() {
- ^
-}
-''');
- await computeSuggestions();
- assertSuggestFunctionTypeAlias('F', 'dynamic');
- }
-
- Future<void> test_functionTypeAlias_old() async {
- addTestSource(r'''
-typedef void F();
-main() {
- ^
-}
-''');
- await computeSuggestions();
- assertSuggestFunctionTypeAlias('F', 'void');
- }
-
Future<void> test_genericFunctionType_parameterList() async {
addTestSource('''
void f(int Function<T>(^) g) {}
@@ -3903,7 +3870,7 @@
assertNotSuggested('C1');
assertSuggestTopLevelVar('T2', 'int');
assertSuggestFunction('F2', null);
- assertSuggestFunctionTypeAlias('D2', 'dynamic');
+ assertSuggestTypeAlias('D2', 'dynamic');
assertSuggestClass('C2');
assertSuggestLocalVariable('name', 'String');
}
@@ -4251,7 +4218,7 @@
assertNotSuggested('C1');
assertSuggestTopLevelVar('T2', 'int');
assertSuggestFunction('F2', null);
- assertSuggestFunctionTypeAlias('D2', 'dynamic');
+ assertSuggestTypeAlias('D2', 'dynamic');
assertSuggestClass('C2');
}
@@ -4800,7 +4767,7 @@
assertNotSuggested('C1');
assertNotSuggested('T2');
assertNotSuggested('F2');
- assertSuggestFunctionTypeAlias('D2', 'dynamic');
+ assertSuggestTypeAlias('D2', 'dynamic');
assertSuggestClass('C2');
assertNotSuggested('name');
}
@@ -4829,7 +4796,7 @@
assertNotSuggested('C1');
assertNotSuggested('T2');
assertNotSuggested('F2');
- assertSuggestFunctionTypeAlias('D2', 'dynamic');
+ assertSuggestTypeAlias('D2', 'dynamic');
assertSuggestClass('C2');
assertNotSuggested('name');
}
@@ -4858,7 +4825,7 @@
assertNotSuggested('C1');
assertNotSuggested('T2');
assertNotSuggested('F2');
- assertSuggestFunctionTypeAlias('D2', 'dynamic');
+ assertSuggestTypeAlias('D2', 'dynamic');
assertSuggestClass('C2');
assertNotSuggested('name');
}
@@ -4889,7 +4856,7 @@
assertNotSuggested('C1');
assertNotSuggested('T2');
assertNotSuggested('F2');
- assertSuggestFunctionTypeAlias('D2', 'dynamic');
+ assertSuggestTypeAlias('D2', 'dynamic');
assertSuggestClass('C2');
assertNotSuggested('name');
}
@@ -6060,6 +6027,39 @@
assertNoSuggestions();
}
+ Future<void> test_typeAlias_functionType() async {
+ addTestSource(r'''
+typedef F = void Function();
+main() {
+ ^
+}
+''');
+ await computeSuggestions();
+ assertSuggestTypeAlias('F', 'void');
+ }
+
+ Future<void> test_typeAlias_interfaceType() async {
+ addTestSource(r'''
+typedef F = List<int>;
+main() {
+ ^
+}
+''');
+ await computeSuggestions();
+ assertSuggestTypeAlias('F', 'List<int>');
+ }
+
+ Future<void> test_typeAlias_legacy() async {
+ addTestSource(r'''
+typedef void F();
+main() {
+ ^
+}
+''');
+ await computeSuggestions();
+ assertSuggestTypeAlias('F', 'void');
+ }
+
Future<void> test_TypeArgumentList() async {
// SimpleIdentifier BinaryExpression ExpressionStatement
addSource('/home/test/lib/a.dart', '''
@@ -6081,7 +6081,7 @@
assertNotSuggested('C1');
assertNotSuggested('T1');
assertSuggestClass('C2');
- assertSuggestFunctionTypeAlias('T2', 'int');
+ assertSuggestTypeAlias('T2', 'int');
assertNotSuggested('F1');
assertNotSuggested('F2');
}
diff --git a/pkg/analysis_server/test/services/refactoring/naming_conventions_test.dart b/pkg/analysis_server/test/services/refactoring/naming_conventions_test.dart
index 8c9ec86..ea21e44 100644
--- a/pkg/analysis_server/test/services/refactoring/naming_conventions_test.dart
+++ b/pkg/analysis_server/test/services/refactoring/naming_conventions_test.dart
@@ -268,74 +268,6 @@
expectedMessage: 'Function name must not start or end with a blank.');
}
- void test_validateFunctionTypeAliasName_doesNotStartWithLowerCase() {
- assertRefactoringStatus(validateFunctionTypeAliasName('newName'),
- RefactoringProblemSeverity.WARNING,
- expectedMessage:
- 'Function type alias name should start with an uppercase letter.');
- }
-
- void test_validateFunctionTypeAliasName_empty() {
- assertRefactoringStatus(
- validateFunctionTypeAliasName(''), RefactoringProblemSeverity.FATAL,
- expectedMessage: 'Function type alias name must not be empty.');
- }
-
- void test_validateFunctionTypeAliasName_invalidCharacters() {
- assertRefactoringStatus(validateFunctionTypeAliasName('New-Name'),
- RefactoringProblemSeverity.FATAL,
- expectedMessage: "Function type alias name must not contain \'-\'.");
- }
-
- void test_validateFunctionTypeAliasName_leadingBlanks() {
- assertRefactoringStatus(validateFunctionTypeAliasName(' NewName'),
- RefactoringProblemSeverity.FATAL,
- expectedMessage:
- 'Function type alias name must not start or end with a blank.');
- }
-
- void test_validateFunctionTypeAliasName_notIdentifierMiddle() {
- assertRefactoringStatus(validateFunctionTypeAliasName('New-Name'),
- RefactoringProblemSeverity.FATAL,
- expectedMessage: "Function type alias name must not contain '-'.");
- }
-
- void test_validateFunctionTypeAliasName_notIdentifierStart() {
- assertRefactoringStatus(validateFunctionTypeAliasName('newName'),
- RefactoringProblemSeverity.WARNING,
- expectedMessage:
- 'Function type alias name should start with an uppercase letter.');
- }
-
- void test_validateFunctionTypeAliasName_null() {
- assertRefactoringStatus(
- validateFunctionTypeAliasName(null), RefactoringProblemSeverity.FATAL,
- expectedMessage: 'Function type alias name must not be null.');
- }
-
- void test_validateFunctionTypeAliasName_OK() {
- assertRefactoringStatusOK(validateFunctionTypeAliasName('NewName'));
- }
-
- void test_validateFunctionTypeAliasName_OK_leadingDollar() {
- assertRefactoringStatusOK(validateFunctionTypeAliasName('\$NewName'));
- }
-
- void test_validateFunctionTypeAliasName_OK_leadingUnderscore() {
- assertRefactoringStatusOK(validateFunctionTypeAliasName('_NewName'));
- }
-
- void test_validateFunctionTypeAliasName_OK_middleDollar() {
- assertRefactoringStatusOK(validateFunctionTypeAliasName('New\$Name'));
- }
-
- void test_validateFunctionTypeAliasName_trailingBlanks() {
- assertRefactoringStatus(validateFunctionTypeAliasName('NewName '),
- RefactoringProblemSeverity.FATAL,
- expectedMessage:
- 'Function type alias name must not start or end with a blank.');
- }
-
void test_validateImportPrefixName_doesNotStartWithLowerCase() {
assertRefactoringStatus(
validateImportPrefixName('NewName'), RefactoringProblemSeverity.WARNING,
@@ -681,6 +613,72 @@
expectedMessage: 'Parameter name must not start or end with a blank.');
}
+ void test_validateTypeAliasName_doesNotStartWithLowerCase() {
+ assertRefactoringStatus(
+ validateTypeAliasName('newName'), RefactoringProblemSeverity.WARNING,
+ expectedMessage:
+ 'Type alias name should start with an uppercase letter.');
+ }
+
+ void test_validateTypeAliasName_empty() {
+ assertRefactoringStatus(
+ validateTypeAliasName(''), RefactoringProblemSeverity.FATAL,
+ expectedMessage: 'Type alias name must not be empty.');
+ }
+
+ void test_validateTypeAliasName_invalidCharacters() {
+ assertRefactoringStatus(
+ validateTypeAliasName('New-Name'), RefactoringProblemSeverity.FATAL,
+ expectedMessage: "Type alias name must not contain \'-\'.");
+ }
+
+ void test_validateTypeAliasName_leadingBlanks() {
+ assertRefactoringStatus(
+ validateTypeAliasName(' NewName'), RefactoringProblemSeverity.FATAL,
+ expectedMessage: 'Type alias name must not start or end with a blank.');
+ }
+
+ void test_validateTypeAliasName_notIdentifierMiddle() {
+ assertRefactoringStatus(
+ validateTypeAliasName('New-Name'), RefactoringProblemSeverity.FATAL,
+ expectedMessage: "Type alias name must not contain '-'.");
+ }
+
+ void test_validateTypeAliasName_notIdentifierStart() {
+ assertRefactoringStatus(
+ validateTypeAliasName('newName'), RefactoringProblemSeverity.WARNING,
+ expectedMessage:
+ 'Type alias name should start with an uppercase letter.');
+ }
+
+ void test_validateTypeAliasName_null() {
+ assertRefactoringStatus(
+ validateTypeAliasName(null), RefactoringProblemSeverity.FATAL,
+ expectedMessage: 'Type alias name must not be null.');
+ }
+
+ void test_validateTypeAliasName_OK() {
+ assertRefactoringStatusOK(validateTypeAliasName('NewName'));
+ }
+
+ void test_validateTypeAliasName_OK_leadingDollar() {
+ assertRefactoringStatusOK(validateTypeAliasName('\$NewName'));
+ }
+
+ void test_validateTypeAliasName_OK_leadingUnderscore() {
+ assertRefactoringStatusOK(validateTypeAliasName('_NewName'));
+ }
+
+ void test_validateTypeAliasName_OK_middleDollar() {
+ assertRefactoringStatusOK(validateTypeAliasName('New\$Name'));
+ }
+
+ void test_validateTypeAliasName_trailingBlanks() {
+ assertRefactoringStatus(
+ validateTypeAliasName('NewName '), RefactoringProblemSeverity.FATAL,
+ expectedMessage: 'Type alias name must not start or end with a blank.');
+ }
+
void test_validateVariableName_builtIn() {
_assertWarningBuiltIn(validateVariableName('abstract'));
}
diff --git a/pkg/analysis_server/test/services/refactoring/rename_unit_member_test.dart b/pkg/analysis_server/test/services/refactoring/rename_unit_member_test.dart
index ab7d05d..5aa122f 100644
--- a/pkg/analysis_server/test/services/refactoring/rename_unit_member_test.dart
+++ b/pkg/analysis_server/test/services/refactoring/rename_unit_member_test.dart
@@ -6,6 +6,7 @@
import 'package:test/test.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
+import '../../abstract_context.dart';
import 'abstract_rename.dart';
void main() {
@@ -15,7 +16,8 @@
}
@reflectiveTest
-class RenameUnitMemberTest extends RenameRefactoringTest {
+class RenameUnitMemberTest extends RenameRefactoringTest
+ with WithNonFunctionTypeAliasesMixin {
Future<void> test_checkFinalConditions_hasTopLevel_ClassElement() async {
await indexTestUnit('''
class Test {}
@@ -42,7 +44,7 @@
var status = await refactoring.checkFinalConditions();
assertRefactoringStatus(status, RefactoringProblemSeverity.ERROR,
expectedMessage:
- "Library already declares function type alias with name 'NewName'.",
+ "Library already declares type alias with name 'NewName'.",
expectedContextSearch: 'NewName(); // existing');
}
@@ -306,21 +308,6 @@
assertRefactoringStatusOK(refactoring.checkNewName());
}
- Future<void> test_checkNewName_FunctionTypeAliasElement() async {
- await indexTestUnit('''
-typedef Test();
-''');
- createRenameRefactoringAtString('Test();');
- // null
- refactoring.newName = null;
- assertRefactoringStatus(
- refactoring.checkNewName(), RefactoringProblemSeverity.FATAL,
- expectedMessage: 'Function type alias name must not be null.');
- // OK
- refactoring.newName = 'NewName';
- assertRefactoringStatusOK(refactoring.checkNewName());
- }
-
Future<void> test_checkNewName_TopLevelVariableElement() async {
await indexTestUnit('''
var test;
@@ -341,6 +328,51 @@
assertRefactoringStatusOK(refactoring.checkNewName());
}
+ Future<void> test_checkNewName_TypeAliasElement_functionType() async {
+ await indexTestUnit('''
+typedef Test = void Function();
+''');
+ createRenameRefactoringAtString('Test =');
+ // null
+ refactoring.newName = null;
+ assertRefactoringStatus(
+ refactoring.checkNewName(), RefactoringProblemSeverity.FATAL,
+ expectedMessage: 'Type alias name must not be null.');
+ // OK
+ refactoring.newName = 'NewName';
+ assertRefactoringStatusOK(refactoring.checkNewName());
+ }
+
+ Future<void> test_checkNewName_TypeAliasElement_interfaceType() async {
+ await indexTestUnit('''
+typedef Test = List<int>;
+''');
+ createRenameRefactoringAtString('Test =');
+ // null
+ refactoring.newName = null;
+ assertRefactoringStatus(
+ refactoring.checkNewName(), RefactoringProblemSeverity.FATAL,
+ expectedMessage: 'Type alias name must not be null.');
+ // OK
+ refactoring.newName = 'NewName';
+ assertRefactoringStatusOK(refactoring.checkNewName());
+ }
+
+ Future<void> test_checkNewName_TypeAliasElement_legacy() async {
+ await indexTestUnit('''
+typedef Test();
+''');
+ createRenameRefactoringAtString('Test();');
+ // null
+ refactoring.newName = null;
+ assertRefactoringStatus(
+ refactoring.checkNewName(), RefactoringProblemSeverity.FATAL,
+ expectedMessage: 'Type alias name must not be null.');
+ // OK
+ refactoring.newName = 'NewName';
+ assertRefactoringStatusOK(refactoring.checkNewName());
+ }
+
Future<void> test_createChange_ClassElement() async {
await indexTestUnit('''
class Test implements Other {
@@ -393,7 +425,7 @@
class TestPageState extends State<TestPage> {
@override
- Widget build(BuildContext context) => null;
+ Widget build(BuildContext context) => throw 0;
}
''');
createRenameRefactoringAtString('TestPage extends');
@@ -415,7 +447,7 @@
class NewPageState extends State<NewPage> {
@override
- Widget build(BuildContext context) => null;
+ Widget build(BuildContext context) => throw 0;
}
''');
}
@@ -435,7 +467,7 @@
class _TestPageState extends State<_TestPage> {
@override
- Widget build(BuildContext context) => null;
+ Widget build(BuildContext context) => throw 0;
}
''');
createRenameRefactoringAtString('_TestPage extends');
@@ -457,7 +489,7 @@
class _NewPageState extends State<_NewPage> {
@override
- Widget build(BuildContext context) => null;
+ Widget build(BuildContext context) => throw 0;
}
''');
}
@@ -477,7 +509,7 @@
class _TestPageState extends State<TestPage> {
@override
- Widget build(BuildContext context) => null;
+ Widget build(BuildContext context) => throw 0;
}
''');
createRenameRefactoringAtString('TestPage extends');
@@ -499,7 +531,7 @@
class _NewPageState extends State<NewPage> {
@override
- Widget build(BuildContext context) => null;
+ Widget build(BuildContext context) => throw 0;
}
''');
}
@@ -531,10 +563,8 @@
Future<void> test_createChange_ClassElement_parameterTypeNested() async {
await indexTestUnit('''
-class Test {
-}
-main(f(Test p)) {
-}
+class Test {}
+void f(g(Test p)) {}
''');
// configure refactoring
createRenameRefactoringAtString('Test {');
@@ -543,10 +573,8 @@
refactoring.newName = 'NewName';
// validate change
return assertSuccessfulRefactoring('''
-class NewName {
-}
-main(f(NewName p)) {
-}
+class NewName {}
+void f(g(NewName p)) {}
''');
}
@@ -554,8 +582,7 @@
await indexTestUnit('''
class A {}
class Test = Object with A;
-main(Test t) {
-}
+void f(Test t) {}
''');
// configure refactoring
createRenameRefactoringAtString('Test =');
@@ -567,8 +594,7 @@
return assertSuccessfulRefactoring('''
class A {}
class NewName = Object with A;
-main(NewName t) {
-}
+void f(NewName t) {}
''');
}
@@ -634,30 +660,6 @@
''');
}
- Future<void> test_createChange_FunctionTypeAliasElement() async {
- await indexTestUnit('''
-typedef void F();
-void foo<T>() {}
-void main() {
- foo<F>();
-}
-''');
- // configure refactoring
- createRenameRefactoringAtString('F()');
- expect(refactoring.refactoringName, 'Rename Function Type Alias');
- expect(refactoring.elementKindName, 'function type alias');
- expect(refactoring.oldName, 'F');
- refactoring.newName = 'G';
- // validate change
- return assertSuccessfulRefactoring('''
-typedef void G();
-void foo<T>() {}
-void main() {
- foo<G>();
-}
-''');
- }
-
Future<void> test_createChange_outsideOfProject_referenceInPart() async {
newFile('/home/part.dart', content: r'''
part of test;
@@ -675,7 +677,7 @@
class Test {}
-Test test;
+void f(Test a) {}
''');
createRenameRefactoringAtString('Test {}');
refactoring.newName = 'NewName';
@@ -687,7 +689,7 @@
class NewName {}
-NewName test;
+void f(NewName a) {}
''');
expect(refactoringChange.edits, hasLength(1));
@@ -732,6 +734,60 @@
await _test_createChange_TopLevelVariableElement('test = 1');
}
+ Future<void> test_createChange_typeAlias_functionType() async {
+ await indexTestUnit('''
+typedef F = void Function();
+void f(F a) {}
+''');
+ // configure refactoring
+ createRenameRefactoringAtString('F =');
+ expect(refactoring.refactoringName, 'Rename Type Alias');
+ expect(refactoring.elementKindName, 'type alias');
+ expect(refactoring.oldName, 'F');
+ refactoring.newName = 'NewName';
+ // validate change
+ return assertSuccessfulRefactoring('''
+typedef NewName = void Function();
+void f(NewName a) {}
+''');
+ }
+
+ Future<void> test_createChange_typeAlias_interfaceType() async {
+ await indexTestUnit('''
+typedef A<T> = Map<int, T>;
+void f(A<String> a) {}
+''');
+ // configure refactoring
+ createRenameRefactoringAtString('A<T>');
+ expect(refactoring.refactoringName, 'Rename Type Alias');
+ expect(refactoring.elementKindName, 'type alias');
+ expect(refactoring.oldName, 'A');
+ refactoring.newName = 'NewName';
+ // validate change
+ return assertSuccessfulRefactoring('''
+typedef NewName<T> = Map<int, T>;
+void f(NewName<String> a) {}
+''');
+ }
+
+ Future<void> test_createChange_typeAlias_legacy() async {
+ await indexTestUnit('''
+typedef void F();
+void f(F a) {}
+''');
+ // configure refactoring
+ createRenameRefactoringAtString('F()');
+ expect(refactoring.refactoringName, 'Rename Type Alias');
+ expect(refactoring.elementKindName, 'type alias');
+ expect(refactoring.oldName, 'F');
+ refactoring.newName = 'G';
+ // validate change
+ return assertSuccessfulRefactoring('''
+typedef void G();
+void f(G a) {}
+''');
+ }
+
Future<void> _test_createChange_PropertyAccessorElement(String search) async {
await indexTestUnit('''
get test {}
diff --git a/pkg/analysis_server/tool/code_completion/completion_metrics.dart b/pkg/analysis_server/tool/code_completion/completion_metrics.dart
index 1d1665c..3cffbee 100644
--- a/pkg/analysis_server/tool/code_completion/completion_metrics.dart
+++ b/pkg/analysis_server/tool/code_completion/completion_metrics.dart
@@ -2,7 +2,6 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
-import 'dart:convert';
import 'dart:io' as io;
import 'dart:math' as math;
@@ -14,18 +13,26 @@
import 'package:analysis_server/src/services/completion/dart/completion_manager.dart';
import 'package:analysis_server/src/services/completion/dart/suggestion_builder.dart';
import 'package:analysis_server/src/services/completion/dart/utilities.dart';
+import 'package:analysis_server/src/status/pages.dart';
import 'package:analyzer/dart/analysis/analysis_context_collection.dart';
import 'package:analyzer/dart/analysis/context_root.dart';
import 'package:analyzer/dart/analysis/results.dart';
import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer/dart/ast/token.dart';
import 'package:analyzer/dart/element/element.dart'
show
ClassElement,
- Element,
- ExtensionElement,
ClassMemberElement,
+ CompilationUnitElement,
+ Element,
ExecutableElement,
+ ExtensionElement,
FieldElement,
+ FunctionElement,
+ LocalVariableElement,
+ ParameterElement,
+ PrefixElement,
+ TypeParameterElement,
VariableElement;
import 'package:analyzer/diagnostic/diagnostic.dart';
import 'package:analyzer/error/error.dart' as err;
@@ -37,7 +44,6 @@
import 'package:analyzer/src/generated/engine.dart';
import 'package:analyzer/src/services/available_declarations.dart';
import 'package:analyzer/src/util/performance/operation_performance.dart';
-import 'package:analyzer_plugin/protocol/protocol_common.dart' show ElementKind;
import 'package:analyzer_plugin/src/utilities/completion/optype.dart';
import 'package:args/args.dart';
import 'package:meta/meta.dart';
@@ -54,72 +60,21 @@
return io.exit(1);
}
- var options = CompletionMetricsOptions(
- availableSuggestions: result[AVAILABLE_SUGGESTIONS],
- overlay: result[OVERLAY],
- printMissedCompletionDetails: result[PRINT_MISSED_COMPLETION_DETAILS],
- printMissedCompletionSummary: result[PRINT_MISSED_COMPLETION_SUMMARY],
- printMissingInformation: result[PRINT_MISSING_INFORMATION],
- printMrrByLocation: result[PRINT_MRR_BY_LOCATION],
- printSlowestResults: result[PRINT_SLOWEST_RESULTS],
- printWorstResults: result[PRINT_WORST_RESULTS]);
+ var options = CompletionMetricsOptions(result);
var root = result.rest[0];
print('Analyzing root: "$root"');
var stopwatch = Stopwatch()..start();
- var code = await CompletionMetricsComputer(root, options).compute();
+ var computer = CompletionMetricsComputer(root, options);
+ var code = await computer.computeMetrics();
stopwatch.stop();
var duration = Duration(milliseconds: stopwatch.elapsedMilliseconds);
print('');
print('Metrics computed in $duration');
+ computer.printResults();
return io.exit(code);
}
-const String AVAILABLE_SUGGESTIONS = 'available-suggestions';
-
-/// An option to control whether and how overlays should be produced.
-const String OVERLAY = 'overlay';
-
-/// A mode indicating that no overlays should be produced.
-const String OVERLAY_NONE = 'none';
-
-/// A mode indicating that everything from the completion offset to the end of
-/// the file should be removed.
-const String OVERLAY_REMOVE_REST_OF_FILE = 'remove-rest-of-file';
-
-/// A mode indicating that the token whose offset is the same as the
-/// completion offset should be removed.
-const String OVERLAY_REMOVE_TOKEN = 'remove-token';
-
-/// A flag that causes detailed information to be printed every time a
-/// completion request fails to produce a suggestions matching the expected
-/// suggestion.
-const String PRINT_MISSED_COMPLETION_DETAILS =
- 'print-missed-completion-details';
-
-/// A flag that causes summary information to be printed about the times that a
-/// completion request failed to produce a suggestions matching the expected
-/// suggestion.
-const String PRINT_MISSED_COMPLETION_SUMMARY =
- 'print-missed-completion-summary';
-
-/// A flag that causes information to be printed about places where no
-/// completion location was computed and about information that's missing in the
-/// completion tables.
-const String PRINT_MISSING_INFORMATION = 'print-missing-information';
-
-/// A flag that causes information to be printed about the mrr score achieved at
-/// each completion location.
-const String PRINT_MRR_BY_LOCATION = 'print-mrr-by-location';
-
-/// A flag that causes information to be printed about the completion requests
-/// that were the slowest to return suggestions.
-const String PRINT_SLOWEST_RESULTS = 'print-slowest-results';
-
-/// A flag that causes information to be printed about the completion requests
-/// that had the worst mrr scores.
-const String PRINT_WORST_RESULTS = 'print-worst-results';
-
/// A [Counter] to track the performance of each of the completion strategies
/// that are being compared.
Counter rankComparison = Counter('relevance rank comparison');
@@ -127,7 +82,7 @@
/// Create a parser that can be used to parse the command-line arguments.
ArgParser createArgParser() {
return ArgParser()
- ..addFlag(AVAILABLE_SUGGESTIONS,
+ ..addFlag(CompletionMetricsOptions.AVAILABLE_SUGGESTIONS,
abbr: 'a',
help:
'Use the available suggestions feature in the Analysis Server when '
@@ -137,42 +92,46 @@
'support in LSP.',
defaultsTo: false,
negatable: false)
+ ..addFlag(CompletionMetricsOptions.MD,
+ help: 'Use markdown as the output format.',
+ defaultsTo: false,
+ negatable: false)
..addOption(
'help',
abbr: 'h',
help: 'Print this help message.',
)
- ..addOption(OVERLAY,
+ ..addOption(CompletionMetricsOptions.OVERLAY,
allowed: [
- OVERLAY_NONE,
- OVERLAY_REMOVE_TOKEN,
- OVERLAY_REMOVE_REST_OF_FILE
+ CompletionMetricsOptions.OVERLAY_NONE,
+ CompletionMetricsOptions.OVERLAY_REMOVE_TOKEN,
+ CompletionMetricsOptions.OVERLAY_REMOVE_REST_OF_FILE
],
- defaultsTo: OVERLAY_NONE,
+ defaultsTo: CompletionMetricsOptions.OVERLAY_NONE,
help:
'Before attempting a completion at the location of each token, the '
'token can be removed, or the rest of the file can be removed to '
'test code completion with diverse methods. The default mode is to '
'complete at the start of the token without modifying the file.')
- ..addFlag(PRINT_MISSED_COMPLETION_DETAILS,
+ ..addFlag(CompletionMetricsOptions.PRINT_MISSED_COMPLETION_DETAILS,
defaultsTo: false,
help:
'Print detailed information every time a completion request fails '
'to produce a suggestions matching the expected suggestion.',
negatable: false)
- ..addFlag(PRINT_MISSED_COMPLETION_SUMMARY,
+ ..addFlag(CompletionMetricsOptions.PRINT_MISSED_COMPLETION_SUMMARY,
defaultsTo: false,
help: 'Print summary information about the times that a completion '
'request failed to produce a suggestions matching the expected '
'suggestion.',
negatable: false)
- ..addFlag(PRINT_MISSING_INFORMATION,
+ ..addFlag(CompletionMetricsOptions.PRINT_MISSING_INFORMATION,
defaultsTo: false,
help: 'Print information about places where no completion location was '
'computed and about information that is missing in the completion '
'tables.',
negatable: false)
- ..addFlag(PRINT_MRR_BY_LOCATION,
+ ..addFlag(CompletionMetricsOptions.PRINT_MRR_BY_LOCATION,
defaultsTo: false,
help:
'Print information about the mrr score achieved at each completion '
@@ -180,16 +139,15 @@
'score by pointing out the locations that are causing the biggest '
'impact.',
negatable: false)
- ..addFlag(PRINT_SLOWEST_RESULTS,
+ ..addFlag(CompletionMetricsOptions.PRINT_SLOWEST_RESULTS,
defaultsTo: false,
help: 'Print information about the completion requests that were the '
'slowest to return suggestions.',
negatable: false)
- ..addFlag(PRINT_WORST_RESULTS,
+ ..addFlag(CompletionMetricsOptions.PRINT_WORST_RESULTS,
defaultsTo: false,
- help:
- 'Print information about the completion requests that had the worst '
- 'mrr scores.',
+ help: 'Print information about the completion requests that had the '
+ 'worst mrr scores.',
negatable: false);
}
@@ -227,12 +185,34 @@
/// An indication of the group in which the completion falls for the purposes of
/// subdividing the results.
enum CompletionGroup {
+ classElement,
+ constructorElement,
+ enumElement,
+ extensionElement,
+
+ /// An instance member of a class, enum, mixin or extension.
instanceMember,
+
+ labelElement,
+ localFunctionElement,
+ localVariableElement,
+ mixinElement,
+ parameterElement,
+ prefixElement,
+
+ /// A static member of a class, enum, mixin or extension.
staticMember,
- typeReference,
- localReference,
- paramReference,
- topLevel
+
+ topLevelMember,
+ typeParameterElement,
+
+ // Groups for keywords.
+
+ keywordDynamic,
+ keywordVoid,
+
+ /// Anything that doesn't fit in one of the other groups.
+ unknown,
}
/// A wrapper for the collection of [Counter] and [MeanReciprocalRankComputer]
@@ -253,7 +233,7 @@
/// The function to be executed when this metrics collector is disabled.
final void Function() disableFunction;
- Counter completionCounter = Counter('successful/ unsuccessful completions');
+ Counter completionCounter = Counter('all completions');
Counter completionMissedTokenCounter =
Counter('unsuccessful completion token counter');
@@ -268,29 +248,17 @@
ArithmeticMeanComputer('ms per completion');
MeanReciprocalRankComputer mrrComputer =
- MeanReciprocalRankComputer('successful/ unsuccessful completions');
+ MeanReciprocalRankComputer('all completions');
MeanReciprocalRankComputer successfulMrrComputer =
MeanReciprocalRankComputer('successful completions');
- MeanReciprocalRankComputer instanceMemberMrrComputer =
- MeanReciprocalRankComputer('instance member completions');
+ /// A table mapping completion groups to the mrr computer used to track the
+ /// quality of suggestions for those groups.
+ Map<CompletionGroup, MeanReciprocalRankComputer> groupMrrComputers = {};
- MeanReciprocalRankComputer staticMemberMrrComputer =
- MeanReciprocalRankComputer('static member completions');
-
- MeanReciprocalRankComputer typeRefMrrComputer =
- MeanReciprocalRankComputer('type reference completions');
-
- MeanReciprocalRankComputer localRefMrrComputer =
- MeanReciprocalRankComputer('local reference completions');
-
- MeanReciprocalRankComputer paramRefMrrComputer =
- MeanReciprocalRankComputer('param reference completions');
-
- MeanReciprocalRankComputer topLevelMrrComputer =
- MeanReciprocalRankComputer('non-type member completions');
-
+ /// A table mapping locations to the mrr computer used to track the quality of
+ /// suggestions for those locations.
Map<String, MeanReciprocalRankComputer> locationMrrComputers = {};
ArithmeticMeanComputer charsBeforeTop =
@@ -309,55 +277,11 @@
/// The completion locations for which no relevance table was available.
Set<String> missingCompletionLocationTables = {};
- /// A list of the top [maxWorstResults] completion results with the highest
- /// (worst) ranks for completing to instance members.
- List<CompletionResult> instanceMemberWorstResults = [];
+ Map<CompletionGroup, List<CompletionResult>> slowestResults = {};
- /// A list of the top [maxWorstResults] completion results with the highest
- /// (worst) ranks for completing to static members.
- List<CompletionResult> staticMemberWorstResults = [];
+ Map<CompletionGroup, List<CompletionResult>> worstResults = {};
- /// A list of the top [maxWorstResults] completion results with the highest
- /// (worst) ranks for completing to type references.
- List<CompletionResult> typeRefWorstResults = [];
-
- /// A list of the top [maxWorstResults] completion results with the highest
- /// (worst) ranks for completing to local references.
- List<CompletionResult> localRefWorstResults = [];
-
- /// A list of the top [maxWorstResults] completion results with the highest
- /// (worst) ranks for completing to parameter references.
- List<CompletionResult> paramRefWorstResults = [];
-
- /// A list of the top [maxWorstResults] completion results with the highest
- /// (worst) ranks for completing to top-level declarations.
- List<CompletionResult> topLevelWorstResults = [];
-
- /// A list of the top [maxSlowestResults] completion results that took the
- /// longest top compute for instance members.
- List<CompletionResult> instanceMemberSlowestResults = [];
-
- /// A list of the top [maxSlowestResults] completion results that took the
- /// longest top compute for static members.
- List<CompletionResult> staticMemberSlowestResults = [];
-
- /// A list of the top [maxSlowestResults] completion results that took the
- /// longest top compute for type references.
- List<CompletionResult> typeRefSlowestResults = [];
-
- /// A list of the top [maxSlowestResults] completion results that took the
- /// longest top compute for local references.
- List<CompletionResult> localRefSlowestResults = [];
-
- /// A list of the top [maxSlowestResults] completion results that took the
- /// longest top compute for parameter references.
- List<CompletionResult> paramRefSlowestResults = [];
-
- /// A list of the top [maxSlowestResults] completion results that took the
- /// longest top compute for top-level declarations.
- List<CompletionResult> topLevelSlowestResults = [];
-
- CompletionMetrics(this.name, this.enableFunction, this.disableFunction);
+ CompletionMetrics(this.name, {this.enableFunction, this.disableFunction});
/// Perform any operations required in order to revert computing the kind of
/// completions represented by this metrics collector.
@@ -375,24 +299,25 @@
}
}
- /// Record this completion result, this method handles the worst ranked items
+ /// Record the completion [result]. This method handles the worst ranked items
/// as well as the longest sets of results to compute.
- void recordCompletionResult(CompletionResult result) {
+ void recordCompletionResult(
+ CompletionResult result, MetricsSuggestionListener listener) {
_recordTime(result);
_recordMrr(result);
_recordWorstResult(result);
_recordSlowestResult(result);
- _recordMissingInformation(result);
+ _recordMissingInformation(listener);
}
/// If the completion location was requested but missing when computing the
/// [result], then record where that happened.
- void _recordMissingInformation(CompletionResult result) {
- var location = result.listener?.missingCompletionLocation;
+ void _recordMissingInformation(MetricsSuggestionListener listener) {
+ var location = listener?.missingCompletionLocation;
if (location != null) {
missingCompletionLocations.add(location);
} else {
- location = result.listener?.missingCompletionLocationTable;
+ location = listener?.missingCompletionLocationTable;
if (location != null) {
missingCompletionLocationTables.add(location);
}
@@ -405,26 +330,10 @@
// Record globally.
successfulMrrComputer.addRank(rank);
// Record by group.
- switch (result.group) {
- case CompletionGroup.instanceMember:
- instanceMemberMrrComputer.addRank(rank);
- break;
- case CompletionGroup.staticMember:
- staticMemberMrrComputer.addRank(rank);
- break;
- case CompletionGroup.typeReference:
- typeRefMrrComputer.addRank(rank);
- break;
- case CompletionGroup.localReference:
- localRefMrrComputer.addRank(rank);
- break;
- case CompletionGroup.paramReference:
- paramRefMrrComputer.addRank(rank);
- break;
- case CompletionGroup.topLevel:
- topLevelMrrComputer.addRank(rank);
- break;
- }
+ var group = result.group;
+ groupMrrComputers
+ .putIfAbsent(group, () => MeanReciprocalRankComputer(group.name))
+ .addRank(rank);
// Record by completion location.
var location = result.completionLocation;
if (location != null) {
@@ -437,33 +346,15 @@
/// If the [result] is took longer than any previously recorded results,
/// record it.
void _recordSlowestResult(CompletionResult result) {
- List<CompletionResult> getSlowestResults() {
- switch (result.group) {
- case CompletionGroup.instanceMember:
- return instanceMemberSlowestResults;
- case CompletionGroup.staticMember:
- return staticMemberSlowestResults;
- case CompletionGroup.typeReference:
- return typeRefSlowestResults;
- case CompletionGroup.localReference:
- return localRefSlowestResults;
- case CompletionGroup.paramReference:
- return paramRefSlowestResults;
- case CompletionGroup.topLevel:
- return topLevelSlowestResults;
- }
- return const <CompletionResult>[];
- }
-
- var slowestResults = getSlowestResults();
- if (slowestResults.length >= maxSlowestResults) {
- if (result.elapsedMS <= slowestResults.last.elapsedMS) {
+ var results = slowestResults.putIfAbsent(result.group, () => []);
+ if (results.length >= maxSlowestResults) {
+ if (result.elapsedMS <= results.last.elapsedMS) {
return;
}
- slowestResults.removeLast();
+ results.removeLast();
}
- slowestResults.add(result);
- slowestResults.sort((first, second) => second.elapsedMS - first.elapsedMS);
+ results.add(result);
+ results.sort((first, second) => second.elapsedMS - first.elapsedMS);
}
/// Record this elapsed ms count for the average ms count.
@@ -473,33 +364,15 @@
/// If the [result] is worse than any previously recorded results, record it.
void _recordWorstResult(CompletionResult result) {
- List<CompletionResult> getWorstResults() {
- switch (result.group) {
- case CompletionGroup.instanceMember:
- return instanceMemberWorstResults;
- case CompletionGroup.staticMember:
- return staticMemberWorstResults;
- case CompletionGroup.typeReference:
- return typeRefWorstResults;
- case CompletionGroup.localReference:
- return localRefWorstResults;
- case CompletionGroup.paramReference:
- return paramRefWorstResults;
- case CompletionGroup.topLevel:
- return topLevelWorstResults;
- }
- return const <CompletionResult>[];
- }
-
- var worstResults = getWorstResults();
- if (worstResults.length >= maxWorstResults) {
- if (result.place.rank <= worstResults.last.place.rank) {
+ var results = worstResults.putIfAbsent(result.group, () => []);
+ if (results.length >= maxWorstResults) {
+ if (result.place.rank <= results.last.place.rank) {
return;
}
- worstResults.removeLast();
+ results.removeLast();
}
- worstResults.add(result);
- worstResults.sort((first, second) => second.place.rank - first.place.rank);
+ results.add(result);
+ results.sort((first, second) => second.place.rank - first.place.rank);
}
}
@@ -513,7 +386,7 @@
ResolvedUnitResult _resolvedUnitResult;
- /// The int to be returned from the [compute] call.
+ /// The int to be returned from the [computeMetrics] call.
int resultCode;
/// A list of the metrics to be computed.
@@ -526,11 +399,13 @@
CompletionMetricsComputer(this.rootPath, this.options);
- Future<int> compute() async {
+ Future<int> computeMetrics() async {
resultCode = 0;
// To compare two or more changes to completions, add a `CompletionMetrics`
// object with enable and disable functions to the list of `targetMetrics`.
- targetMetrics.add(CompletionMetrics('shipping', null, null));
+ targetMetrics.add(CompletionMetrics('shipping',
+ enableFunction: null, disableFunction: null));
+
final collection = AnalysisContextCollection(
includedPaths: [rootPath],
resourceProvider: PhysicalResourceProvider.INSTANCE,
@@ -538,25 +413,6 @@
for (var context in collection.contexts) {
await _computeInContext(context.contextRoot);
}
- printComparison();
- for (var metrics in targetMetrics) {
- printMetrics(metrics);
-
- print('');
- print('====================');
- rankComparison.printCounterValues();
- print('====================');
-
- if (options.printMissingInformation) {
- printMissingInformation(metrics);
- }
- if (options.printSlowestResults) {
- printSlowestResults(metrics);
- }
- if (options.printWorstResults) {
- printWorstResults(metrics);
- }
- }
return resultCode;
}
@@ -567,8 +423,7 @@
String completionLocation,
List<protocol.CompletionSuggestion> suggestions,
CompletionMetrics metrics,
- int elapsedMS,
- bool doPrintMissedCompletions) {
+ int elapsedMS) {
assert(suggestions != null);
var place = placementInSuggestionList(suggestions, expectedCompletion);
@@ -578,8 +433,36 @@
if (place.denominator != 0) {
metrics.completionCounter.count('successful');
- metrics.recordCompletionResult(CompletionResult(place, request, listener,
- suggestions, expectedCompletion, completionLocation, elapsedMS));
+ var rank = place.rank;
+ var suggestion = suggestions[rank - 1];
+ var actualSuggestion =
+ SuggestionData(suggestion, listener.featureMap[suggestion]);
+ List<SuggestionData> topSuggestions;
+ Map<int, int> precedingRelevanceCounts;
+ if (options.printWorstResults) {
+ topSuggestions = suggestions
+ .sublist(0, math.min(10, suggestions.length))
+ .map((suggestion) =>
+ SuggestionData(suggestion, listener.featureMap[suggestion]))
+ .toList();
+ precedingRelevanceCounts = <int, int>{};
+ for (var i = 0; i < rank - 1; i++) {
+ var relevance = suggestions[i].relevance;
+ precedingRelevanceCounts[relevance] =
+ (precedingRelevanceCounts[relevance] ?? 0) + 1;
+ }
+ }
+ metrics.recordCompletionResult(
+ CompletionResult(
+ place,
+ request,
+ actualSuggestion,
+ topSuggestions,
+ precedingRelevanceCounts,
+ expectedCompletion,
+ completionLocation,
+ elapsedMS),
+ listener);
var charsBeforeTop =
_computeCharsBeforeTop(expectedCompletion, suggestions);
@@ -598,7 +481,7 @@
metrics.completionElementKindCounter
.count(expectedCompletion.elementKind.toString());
- if (doPrintMissedCompletions) {
+ if (options.printMissedCompletionDetails) {
protocol.CompletionSuggestion closeMatchSuggestion;
for (var suggestion in suggestions) {
if (suggestion.completion == expectedCompletion.completion) {
@@ -619,50 +502,64 @@
}
}
- void printComparison() {
- List<String> toRow(Iterable<MeanReciprocalRankComputer> computers) {
- return [
- computers.first.name,
- for (var computer in computers) (1 / computer.mrr).toStringAsFixed(3),
- ];
+ void printComparisons() {
+ printHeading(1, 'Comparison of experiments');
+ printMrrComparison();
+ printCounter(rankComparison);
+ printOtherMetrics();
+ printCompletionCounts();
+ }
+
+ void printCompletionCounts() {
+ String toString(int count, int totalCount) {
+ return '$count (${printPercentage(count / totalCount, 2)})';
}
- var buffer = StringBuffer();
+ var counters = targetMetrics.map((metrics) => metrics.completionCounter);
var table = [
['', for (var metrics in targetMetrics) metrics.name],
- toRow(targetMetrics.map((metrics) => metrics.mrrComputer)),
- toRow(targetMetrics.map((metrics) => metrics.successfulMrrComputer)),
- toRow(targetMetrics.map((metrics) => metrics.instanceMemberMrrComputer)),
- toRow(targetMetrics.map((metrics) => metrics.staticMemberMrrComputer)),
- toRow(targetMetrics.map((metrics) => metrics.typeRefMrrComputer)),
- toRow(targetMetrics.map((metrics) => metrics.localRefMrrComputer)),
- toRow(targetMetrics.map((metrics) => metrics.paramRefMrrComputer)),
- toRow(targetMetrics.map((metrics) => metrics.topLevelMrrComputer)),
+ ['total', for (var counter in counters) counter.totalCount.toString()],
+ [
+ 'successful',
+ for (var counter in counters)
+ toString(counter.getCountOf('successful'), counter.totalCount)
+ ],
+ [
+ 'unsuccessful',
+ for (var counter in counters)
+ toString(counter.getCountOf('unsuccessful'), counter.totalCount)
+ ],
];
- for (var i = 1; i < table[0].length; i++) {
- rightJustifyColumn(i, table);
- }
- buffer.writeTable(table);
+ rightJustifyColumns(table, range(1, table[0].length));
+ printHeading(2, 'Comparison of completion counts');
+ printTable(table);
+ }
+
+ void printCounter(Counter counter) {
+ var name = counter.name;
+ var total = counter.totalCount;
+ printHeading(2, "Counts for '$name' (total = $total)");
+ counter.printCounterValues();
+ }
+
+ void printHeading(int level, String heading) {
+ if (options.markdown) {
+ var prefix = '#' * level;
+ print('$prefix $heading');
+ } else {
+ print(heading);
+ print(((level == 1) ? '=' : '-') * heading.length);
+ }
print('');
- print('Comparison of inverse mean reciprocal ranks (lower is better)');
- print('');
- print(buffer.toString());
}
void printMetrics(CompletionMetrics metrics) {
- print('====================');
- print('Completion metrics for ${metrics.name}:');
- print('');
+ printHeading(1, 'Completion metrics for ${metrics.name}');
if (options.printMissedCompletionSummary) {
- metrics.completionMissedTokenCounter.printCounterValues();
- print('');
-
- metrics.completionKindCounter.printCounterValues();
- print('');
-
- metrics.completionElementKindCounter.printCounterValues();
- print('');
+ printCounter(metrics.completionMissedTokenCounter);
+ printCounter(metrics.completionKindCounter);
+ printCounter(metrics.completionElementKindCounter);
}
List<String> toRow(MeanReciprocalRankComputer computer) {
@@ -676,26 +573,18 @@
];
}
- var buffer = StringBuffer();
+ var groups = metrics.groupMrrComputers.keys.toList();
+ groups.sort((first, second) => first.name.compareTo(second.name));
var table = [
['', 'mrr', 'inverse mrr', 'mrr_5', 'inverse mrr_5', 'count'],
toRow(metrics.mrrComputer),
toRow(metrics.successfulMrrComputer),
- toRow(metrics.instanceMemberMrrComputer),
- toRow(metrics.staticMemberMrrComputer),
- toRow(metrics.typeRefMrrComputer),
- toRow(metrics.localRefMrrComputer),
- toRow(metrics.paramRefMrrComputer),
- toRow(metrics.topLevelMrrComputer),
+ for (var group in groups) toRow(metrics.groupMrrComputers[group]),
];
- rightJustifyColumn(2, table);
- rightJustifyColumn(4, table);
- rightJustifyColumn(5, table);
- buffer.writeTable(table);
+ rightJustifyColumns(table, [2, 4, 5]);
- print('Mean Reciprocal Rank');
- print('');
- print(buffer.toString());
+ printHeading(2, 'Mean Reciprocal Rank');
+ printTable(table);
if (options.printMrrByLocation) {
var lines = <LocationTableLine>[];
@@ -722,69 +611,158 @@
var mrr_5 = line.mrr_5.toStringAsFixed(3);
table.add([location, product, count, mrr, mrr_5]);
}
- var buffer = StringBuffer();
- buffer.writeTable(table);
- print(buffer.toString());
- print('');
+ printTable(table);
}
-
- metrics.charsBeforeTop.printMean();
- metrics.charsBeforeTopFive.printMean();
- metrics.insertionLengthTheoretical.printMean();
- print('');
-
- print('Summary for $rootPath:');
- metrics.meanCompletionMS.printMean();
- metrics.completionCounter.printCounterValues();
- print('====================');
}
void printMissingInformation(CompletionMetrics metrics) {
var locations = metrics.missingCompletionLocations;
if (locations.isNotEmpty) {
print('');
- print('====================');
- print('Missing completion location in the following places:');
+ printHeading(2, 'Missing completion location in the following places');
for (var location in locations.toList()..sort()) {
- print(' $location');
+ print('- $location');
}
}
var tables = metrics.missingCompletionLocationTables;
if (tables.isNotEmpty) {
print('');
- print('====================');
- print('Missing tables for the following completion locations:');
+ printHeading(2, 'Missing tables for the following completion locations');
for (var table in tables.toList()..sort()) {
- print(' $table');
+ print('- $table');
+ }
+ }
+ }
+
+ void printMrrComparison() {
+ List<String> toRow(Iterable<MeanReciprocalRankComputer> sources) {
+ var computers = sources.toList();
+ var baseComputer = computers.first;
+ var row = [baseComputer.name];
+ var baseInverseMrr = 1 / baseComputer.mrr;
+ row.add(baseInverseMrr.toStringAsFixed(3));
+ for (var i = 1; i < computers.length; i++) {
+ var inverseMrr = 1 / computers[i].mrr;
+ var delta = inverseMrr - baseInverseMrr;
+ row.add('|');
+ row.add(inverseMrr.toStringAsFixed(3));
+ row.add(delta.toStringAsFixed(3));
+ }
+ return row;
+ }
+
+ var columnHeaders = [' ', targetMetrics[0].name];
+ for (var i = 1; i < targetMetrics.length; i++) {
+ columnHeaders.add('|');
+ columnHeaders.add('${targetMetrics[i].name}');
+ columnHeaders.add('delta');
+ }
+ var blankRow = [for (int i = 0; i < columnHeaders.length; i++) ''];
+ var table = [
+ columnHeaders,
+ toRow(targetMetrics.map((metrics) => metrics.mrrComputer)),
+ toRow(targetMetrics.map((metrics) => metrics.successfulMrrComputer)),
+ blankRow,
+ ];
+ var elementKinds = targetMetrics
+ .expand((metrics) => metrics.groupMrrComputers.keys)
+ .toSet()
+ .toList();
+ elementKinds.sort((first, second) => first.name.compareTo(second.name));
+ for (var kind in elementKinds) {
+ table.add(toRow(
+ targetMetrics.map((metrics) => metrics.groupMrrComputers[kind])));
+ }
+ if (options.printMrrByLocation) {
+ table.add(blankRow);
+ var locations = targetMetrics
+ .expand((metrics) => metrics.locationMrrComputers.keys)
+ .toSet()
+ .toList();
+ locations.sort();
+ for (var location in locations) {
+ table.add(toRow(targetMetrics
+ .map((metrics) => metrics.locationMrrComputers[location])));
+ }
+ }
+ rightJustifyColumns(table, range(1, table[0].length));
+
+ printHeading(2, 'Comparison of inverse mean reciprocal ranks');
+ print('A lower value is better, so a negative delta is good.');
+ print('');
+ printTable(table);
+ }
+
+ void printOtherMetrics() {
+ List<String> toRow(Iterable<ArithmeticMeanComputer> sources) {
+ var computers = sources.toList();
+ var row = [computers.first.name];
+ for (var computer in computers) {
+ row.add(computer.mean.toStringAsFixed(6));
+ }
+ return row;
+ }
+
+ var table = [
+ ['', for (var metrics in targetMetrics) metrics.name],
+ toRow(targetMetrics.map((metrics) => metrics.meanCompletionMS)),
+ toRow(targetMetrics.map((metrics) => metrics.charsBeforeTop)),
+ toRow(targetMetrics.map((metrics) => metrics.charsBeforeTopFive)),
+ toRow(targetMetrics.map((metrics) => metrics.insertionLengthTheoretical)),
+ ];
+ rightJustifyColumns(table, range(1, table[0].length));
+
+ printHeading(2, 'Comparison of other metrics');
+ printTable(table);
+ }
+
+ void printResults() {
+ if (targetMetrics.length > 1) {
+ print('');
+ printComparisons();
+ }
+ var needsBlankLine = false;
+ for (var metrics in targetMetrics) {
+ if (needsBlankLine) {
+ print('');
+ } else {
+ needsBlankLine = true;
+ }
+ printMetrics(metrics);
+
+ if (options.printMissingInformation) {
+ printMissingInformation(metrics);
+ }
+ if (options.printSlowestResults) {
+ printSlowestResults(metrics);
+ }
+ if (options.printWorstResults) {
+ printWorstResults(metrics);
}
}
}
void printSlowestResults(CompletionMetrics metrics) {
+ var slowestResults = metrics.slowestResults;
+ var groups = slowestResults.keys.toList();
+ groups.sort((first, second) => first.name.compareTo(second.name));
print('');
- print('====================');
- print('The slowest completion results to compute');
- _printSlowestResults(
- 'Instance members', metrics.instanceMemberSlowestResults);
- _printSlowestResults('Static members', metrics.staticMemberSlowestResults);
- _printSlowestResults('Type references', metrics.typeRefSlowestResults);
- _printSlowestResults('Local references', metrics.localRefSlowestResults);
- _printSlowestResults(
- 'Parameter references', metrics.paramRefSlowestResults);
- _printSlowestResults('Top level', metrics.topLevelSlowestResults);
+ printHeading(2, 'The slowest completion results to compute');
+ for (var group in groups) {
+ _printSlowestResults('In ${group.name}', slowestResults[group]);
+ }
}
void printWorstResults(CompletionMetrics metrics) {
+ var worstResults = metrics.worstResults;
+ var groups = worstResults.keys.toList();
+ groups.sort((first, second) => first.name.compareTo(second.name));
print('');
- print('====================');
- print('The worst completion results');
- _printWorstResults('Instance members', metrics.instanceMemberWorstResults);
- _printWorstResults('Static members', metrics.staticMemberWorstResults);
- _printWorstResults('Type references', metrics.topLevelWorstResults);
- _printWorstResults('Local references', metrics.localRefWorstResults);
- _printWorstResults('Parameter references', metrics.paramRefWorstResults);
- _printWorstResults('Top level', metrics.topLevelWorstResults);
+ printHeading(2, 'The worst completion results');
+ for (var group in groups) {
+ _printWorstResults('In ${group.name}', worstResults[group]);
+ }
}
int _computeCharsBeforeTop(ExpectedCompletion target,
@@ -948,11 +926,9 @@
// If an overlay option is being used, compute the overlay file, and
// have the context reanalyze the file
- if (options.overlay != OVERLAY_NONE) {
+ if (options.overlay != CompletionMetricsOptions.OVERLAY_NONE) {
var overlayContents = _getOverlayContents(
- _resolvedUnitResult.content,
- expectedCompletion,
- options.overlay);
+ _resolvedUnitResult.content, expectedCompletion);
_provider.setOverlay(filePath,
content: overlayContents,
@@ -970,8 +946,7 @@
Future<int> handleExpectedCompletion(
{MetricsSuggestionListener listener,
- @required CompletionMetrics metrics,
- @required bool printMissedCompletions}) async {
+ @required CompletionMetrics metrics}) async {
var stopwatch = Stopwatch()..start();
var request = CompletionRequestImpl(
resolvedUnitResult,
@@ -1006,8 +981,7 @@
opType.completionLocation,
suggestions,
metrics,
- stopwatch.elapsedMilliseconds,
- printMissedCompletions);
+ stopwatch.elapsedMilliseconds);
}
var bestRank = -1;
@@ -1015,11 +989,14 @@
for (var metrics in targetMetrics) {
// Compute the completions.
metrics.enable();
+ // if (FeatureComputer.noDisabledFeatures) {
+ // var line = expectedCompletion.lineNumber;
+ // var column = expectedCompletion.columnNumber;
+ // print('$filePath:$line:$column');
+ // }
var listener = MetricsSuggestionListener();
var rank = await handleExpectedCompletion(
- listener: listener,
- metrics: metrics,
- printMissedCompletions: options.printMissedCompletionDetails);
+ listener: listener, metrics: metrics);
if (bestRank < 0 || rank < bestRank) {
bestRank = rank;
bestName = metrics.name;
@@ -1030,7 +1007,7 @@
// If an overlay option is being used, remove the overlay applied
// earlier
- if (options.overlay != OVERLAY_NONE) {
+ if (options.overlay != CompletionMetricsOptions.OVERLAY_NONE) {
_provider.removeOverlay(filePath);
}
}
@@ -1046,38 +1023,46 @@
List<protocol.CompletionSuggestion> _filterSuggestions(
String prefix, List<protocol.CompletionSuggestion> suggestions) {
- // TODO(brianwilkerson) Replace this with a more realistic filtering algorithm.
+ // TODO(brianwilkerson) Replace this with a more realistic filtering
+ // algorithm.
return suggestions
.where((suggestion) => suggestion.completion.startsWith(prefix))
.toList();
}
- String _getOverlayContents(String contents,
- ExpectedCompletion expectedCompletion, String overlayMode) {
+ String _getOverlayContents(
+ String contents, ExpectedCompletion expectedCompletion) {
assert(contents.isNotEmpty);
var offset = expectedCompletion.offset;
var length = expectedCompletion.syntacticEntity.length;
assert(offset >= 0);
assert(length > 0);
- if (overlayMode == OVERLAY_REMOVE_TOKEN) {
+ if (options.overlay == CompletionMetricsOptions.OVERLAY_REMOVE_TOKEN) {
return contents.substring(0, offset) +
contents.substring(offset + length);
- } else if (overlayMode == OVERLAY_REMOVE_REST_OF_FILE) {
+ } else if (options.overlay ==
+ CompletionMetricsOptions.OVERLAY_REMOVE_REST_OF_FILE) {
return contents.substring(0, offset);
} else {
+ var removeToken = CompletionMetricsOptions.OVERLAY_REMOVE_TOKEN;
+ var removeRest = CompletionMetricsOptions.OVERLAY_REMOVE_REST_OF_FILE;
throw Exception('\'_getOverlayContents\' called with option other than'
- '$OVERLAY_REMOVE_TOKEN and $OVERLAY_REMOVE_REST_OF_FILE: $overlayMode');
+ '$removeToken and $removeRest: ${options.overlay}');
}
}
void _printSlowestResults(
String title, List<CompletionResult> slowestResults) {
- print('');
- print(title);
+ printHeading(3, title);
+ var needsBlankLine = false;
for (var result in slowestResults) {
var elapsedMS = result.elapsedMS;
var expected = result.expectedCompletion;
- print('');
+ if (needsBlankLine) {
+ print('');
+ } else {
+ needsBlankLine = true;
+ }
print(' Elapsed ms: $elapsedMS');
print(' Completion: ${expected.completion}');
print(' Completion kind: ${expected.kind}');
@@ -1087,45 +1072,79 @@
}
void _printWorstResults(String title, List<CompletionResult> worstResults) {
- print('');
- print(title);
+ List<String> suggestionRow(int rank, SuggestionData data) {
+ var suggestion = data.suggestion;
+ return [
+ rank.toString(),
+ suggestion.relevance.toString(),
+ suggestion.completion,
+ suggestion.kind.toString()
+ ];
+ }
+
+ List<String> featuresRow(int rank, SuggestionData data) {
+ var features = data.features;
+ return [
+ rank.toString(),
+ for (var feature in features) feature.toStringAsFixed(4)
+ ];
+ }
+
+ printHeading(3, title);
+ var needsBlankLine = false;
for (var result in worstResults) {
var rank = result.place.rank;
+ var actualSuggestion = result.actualSuggestion;
var expected = result.expectedCompletion;
- var suggestions = result.suggestions;
- var suggestion = suggestions[rank - 1];
- var features = result.listener?.featureMap[suggestion];
- var topSuggestions =
- suggestions.sublist(0, math.min(10, suggestions.length));
+ var topSuggestions = result.topSuggestions;
var topSuggestionCount = topSuggestions.length;
- var preceding = <int, int>{};
- for (var i = 0; i < rank - 1; i++) {
- var relevance = suggestions[i].relevance;
- preceding[relevance] = (preceding[relevance] ?? 0) + 1;
- }
+ var preceding = result.precedingRelevanceCounts;
var precedingRelevances = preceding.keys.toList();
precedingRelevances.sort();
- print('');
+ var suggestionsTable = [
+ ['Rank', 'Relevance', 'Completion', 'Kind']
+ ];
+ for (var i = 0; i < topSuggestionCount; i++) {
+ suggestionsTable.add(suggestionRow(i, topSuggestions[i]));
+ }
+ suggestionsTable.add(suggestionRow(rank, actualSuggestion));
+ rightJustifyColumns(suggestionsTable, [0, 1]);
+
+ var featuresTable = [
+ [
+ 'Rank',
+ 'contextType',
+ 'elementKind',
+ 'hasDeprecated',
+ 'inheritanceDistance',
+ 'isConstant',
+ 'keyword',
+ 'localVariableDistance',
+ 'startsWithDollar',
+ 'superMatches'
+ ]
+ ];
+ for (var i = 0; i < topSuggestionCount; i++) {
+ featuresTable.add(featuresRow(i, topSuggestions[i]));
+ }
+ featuresTable.add(featuresRow(rank, actualSuggestion));
+ rightJustifyColumns(featuresTable, range(0, featuresTable[0].length));
+
+ if (needsBlankLine) {
+ print('');
+ } else {
+ needsBlankLine = true;
+ }
print(' Rank: $rank');
print(' Location: ${expected.location}');
- print(' Suggestion: ${suggestion.description}');
- print(' Features: $features');
- print(' Top $topSuggestionCount suggestions:');
- for (var i = 0; i < topSuggestionCount; i++) {
- var topSuggestion = topSuggestions[i];
- print(' $i Suggestion: ${topSuggestion.description}');
- if (result.listener != null) {
- var feature = result.listener.featureMap[topSuggestion];
- if (feature == null || feature.isEmpty) {
- print(' Features: <none>');
- } else {
- print(' Features: $feature');
- }
- }
- }
+ print(' Comparison with the top $topSuggestionCount suggestions:');
+ printTable(suggestionsTable);
+ print(' Comparison of features with the top $topSuggestionCount '
+ 'suggestions:');
+ printTable(featuresTable);
print(' Preceding relevance scores and counts:');
for (var relevance in precedingRelevances.reversed) {
print(' $relevance: ${preceding[relevance]}');
@@ -1161,10 +1180,63 @@
/// The options specified on the command-line.
class CompletionMetricsOptions {
+ /// A flag that causes the available suggestion sets to be used while
+ /// computing suggestions.
+ static const String AVAILABLE_SUGGESTIONS = 'available-suggestions';
+
+ /// A flag that causes the output to be in markdown format.
+ static const String MD = 'md';
+
+ /// An option to control whether and how overlays should be produced.
+ static const String OVERLAY = 'overlay';
+
+ /// A mode indicating that no overlays should be produced.
+ static const String OVERLAY_NONE = 'none';
+
+ /// A mode indicating that everything from the completion offset to the end of
+ /// the file should be removed.
+ static const String OVERLAY_REMOVE_REST_OF_FILE = 'remove-rest-of-file';
+
+ /// A mode indicating that the token whose offset is the same as the
+ /// completion offset should be removed.
+ static const String OVERLAY_REMOVE_TOKEN = 'remove-token';
+
+ /// A flag that causes detailed information to be printed every time a
+ /// completion request fails to produce a suggestions matching the expected
+ /// suggestion.
+ static const String PRINT_MISSED_COMPLETION_DETAILS =
+ 'print-missed-completion-details';
+
+ /// A flag that causes summary information to be printed about the times that
+ /// a completion request failed to produce a suggestions matching the expected
+ /// suggestion.
+ static const String PRINT_MISSED_COMPLETION_SUMMARY =
+ 'print-missed-completion-summary';
+
+ /// A flag that causes information to be printed about places where no
+ /// completion location was computed and about information that's missing in
+ /// the completion tables.
+ static const String PRINT_MISSING_INFORMATION = 'print-missing-information';
+
+ /// A flag that causes information to be printed about the mrr score achieved
+ /// at each completion location.
+ static const String PRINT_MRR_BY_LOCATION = 'print-mrr-by-location';
+
+ /// A flag that causes information to be printed about the completion requests
+ /// that were the slowest to return suggestions.
+ static const String PRINT_SLOWEST_RESULTS = 'print-slowest-results';
+
+ /// A flag that causes information to be printed about the completion requests
+ /// that had the worst mrr scores.
+ static const String PRINT_WORST_RESULTS = 'print-worst-results';
+
/// A flag indicating whether available suggestions should be enabled for this
/// run.
final bool availableSuggestions;
+ /// A flag indicating whether the output should use markdown.
+ final bool markdown;
+
/// The overlay mode that should be used.
final String overlay;
@@ -1195,8 +1267,22 @@
/// completion requests that had the worst mrr scores.
final bool printWorstResults;
- CompletionMetricsOptions(
+ factory CompletionMetricsOptions(results) {
+ return CompletionMetricsOptions._(
+ availableSuggestions: results[AVAILABLE_SUGGESTIONS],
+ markdown: results[MD],
+ overlay: results[OVERLAY],
+ printMissedCompletionDetails: results[PRINT_MISSED_COMPLETION_DETAILS],
+ printMissedCompletionSummary: results[PRINT_MISSED_COMPLETION_SUMMARY],
+ printMissingInformation: results[PRINT_MISSING_INFORMATION],
+ printMrrByLocation: results[PRINT_MRR_BY_LOCATION],
+ printSlowestResults: results[PRINT_SLOWEST_RESULTS],
+ printWorstResults: results[PRINT_WORST_RESULTS]);
+ }
+
+ CompletionMetricsOptions._(
{@required this.availableSuggestions,
+ @required this.markdown,
@required this.overlay,
@required this.printMissedCompletionDetails,
@required this.printMissedCompletionSummary,
@@ -1215,9 +1301,9 @@
final CompletionRequestImpl request;
- final MetricsSuggestionListener listener;
+ final SuggestionData actualSuggestion;
- final List<protocol.CompletionSuggestion> suggestions;
+ final List<SuggestionData> topSuggestions;
final ExpectedCompletion expectedCompletion;
@@ -1225,13 +1311,23 @@
final int elapsedMS;
- CompletionResult(this.place, this.request, this.listener, this.suggestions,
- this.expectedCompletion, this.completionLocation, this.elapsedMS);
+ final Map<int, int> precedingRelevanceCounts;
+
+ CompletionResult(
+ this.place,
+ this.request,
+ this.actualSuggestion,
+ this.topSuggestions,
+ this.precedingRelevanceCounts,
+ this.expectedCompletion,
+ this.completionLocation,
+ this.elapsedMS);
/// Return the completion group for the location at which completion was
/// requested.
CompletionGroup get group {
- var element = _getElement(expectedCompletion.syntacticEntity);
+ var entity = expectedCompletion.syntacticEntity;
+ var element = _getElement(entity);
if (element != null) {
var parent = element.enclosingElement;
if (parent is ClassElement || parent is ExtensionElement) {
@@ -1240,25 +1336,75 @@
} else {
return CompletionGroup.instanceMember;
}
- } else if (expectedCompletion.elementKind == ElementKind.CLASS ||
- expectedCompletion.elementKind == ElementKind.MIXIN ||
- expectedCompletion.elementKind == ElementKind.ENUM ||
- expectedCompletion.elementKind == ElementKind.TYPE_PARAMETER) {
- return CompletionGroup.typeReference;
- } else if (expectedCompletion.elementKind == ElementKind.LOCAL_VARIABLE) {
- return CompletionGroup.localReference;
- } else if (expectedCompletion.elementKind == ElementKind.PARAMETER) {
- return CompletionGroup.paramReference;
+ } else if (parent is CompilationUnitElement &&
+ element is! ClassElement &&
+ element is! ExtensionElement) {
+ return CompletionGroup.topLevelMember;
+ }
+ if (element is ClassElement) {
+ if (element.isEnum) {
+ return CompletionGroup.enumElement;
+ } else if (element.isMixin) {
+ return CompletionGroup.mixinElement;
+ }
+ if (entity is SimpleIdentifier &&
+ entity.parent is TypeName &&
+ entity.parent.parent is ConstructorName &&
+ entity.parent.parent.parent is InstanceCreationExpression) {
+ return CompletionGroup.constructorElement;
+ }
+ return CompletionGroup.classElement;
+ } else if (element is ExtensionElement) {
+ return CompletionGroup.extensionElement;
+ } else if (element is FunctionElement) {
+ return CompletionGroup.localFunctionElement;
+ } else if (element is LocalVariableElement) {
+ return CompletionGroup.localVariableElement;
+ } else if (element is ParameterElement) {
+ return CompletionGroup.parameterElement;
+ } else if (element is PrefixElement) {
+ return CompletionGroup.prefixElement;
+ } else if (element is TypeParameterElement) {
+ return CompletionGroup.typeParameterElement;
}
}
- return CompletionGroup.topLevel;
+ if (entity is SimpleIdentifier) {
+ var name = entity.name;
+ if (name == 'void') {
+ return CompletionGroup.keywordVoid;
+ } else if (name == 'dynamic') {
+ return CompletionGroup.keywordDynamic;
+ }
+ }
+ return CompletionGroup.unknown;
}
/// Return the element associated with the syntactic [entity], or `null` if
/// there is no such element.
Element _getElement(SyntacticEntity entity) {
if (entity is SimpleIdentifier) {
- return entity.staticElement;
+ var element = entity.staticElement;
+ if (element != null) {
+ return element;
+ }
+ AstNode node = entity;
+ while (node != null) {
+ var parent = node.parent;
+ if (parent is AssignmentExpression) {
+ if (node == parent.leftHandSide) {
+ return parent.readElement ?? parent.writeElement;
+ }
+ return null;
+ } else if (parent is PrefixExpression) {
+ if (parent.operator.type == TokenType.PLUS_PLUS ||
+ parent.operator.type == TokenType.MINUS_MINUS) {
+ return parent.readElement ?? parent.writeElement;
+ }
+ } else if (parent is PostfixExpression) {
+ return parent.readElement ?? parent.writeElement;
+ }
+ node = parent;
+ }
}
return null;
}
@@ -1297,48 +1443,52 @@
}
class MetricsSuggestionListener implements SuggestionListener {
- Map<protocol.CompletionSuggestion, String> featureMap = {};
+ Map<protocol.CompletionSuggestion, List<double>> featureMap = {};
- String cachedFeatures = '';
+ List<double> cachedFeatures = const [
+ 0.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ 0.0
+ ];
String missingCompletionLocation;
+
String missingCompletionLocationTable;
@override
void builtSuggestion(protocol.CompletionSuggestion suggestion) {
featureMap[suggestion] = cachedFeatures;
- cachedFeatures = '';
+ cachedFeatures = const [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0];
}
@override
void computedFeatures(
- {double contextType,
- double elementKind,
- double hasDeprecated,
- double inheritanceDistance,
- double startsWithDollar,
- double superMatches}) {
- var buffer = StringBuffer();
-
- bool write(String label, double value, bool needsComma) {
- if (value != null) {
- if (needsComma) {
- buffer.write(', ');
- }
- buffer.write('$label: $value');
- return true;
- }
- return needsComma;
- }
-
- var needsComma = false;
- needsComma = write('contextType', contextType, needsComma);
- needsComma = write('elementKind', elementKind, needsComma);
- needsComma = write('hasDeprecated', hasDeprecated, needsComma);
- needsComma = write('inheritanceDistance', inheritanceDistance, needsComma);
- needsComma = write('startsWithDollar', startsWithDollar, needsComma);
- needsComma = write('superMatches', superMatches, needsComma);
- cachedFeatures = buffer.toString();
+ {double contextType = 0.0,
+ double elementKind = 0.0,
+ double hasDeprecated = 0.0,
+ double inheritanceDistance = 0.0,
+ double isConstant = 0.0,
+ double keyword = 0.0,
+ double localVariableDistance = 0.0,
+ double startsWithDollar = 0.0,
+ double superMatches = 0.0}) {
+ cachedFeatures = [
+ contextType,
+ elementKind,
+ hasDeprecated,
+ inheritanceDistance,
+ isConstant,
+ keyword,
+ localVariableDistance,
+ startsWithDollar,
+ superMatches
+ ];
}
@override
@@ -1364,10 +1514,59 @@
}
}
-extension on protocol.CompletionSuggestion {
- /// A shorter description of the suggestion than [toString] provides.
- String get description =>
- json.encode(toJson()..remove('docSummary')..remove('docComplete'));
+/// The information being remembered about an individual suggestion.
+class SuggestionData {
+ /// The suggestion that was produced.
+ protocol.CompletionSuggestion suggestion;
+
+ /// The values of the features used to compute the suggestion.
+ List<double> features;
+
+ SuggestionData(this.suggestion, this.features);
+}
+
+extension on CompletionGroup {
+ String get name {
+ switch (this) {
+ case CompletionGroup.classElement:
+ return 'class';
+ case CompletionGroup.constructorElement:
+ return 'constructor';
+ case CompletionGroup.enumElement:
+ return 'enum';
+ case CompletionGroup.extensionElement:
+ return 'extension';
+ case CompletionGroup.instanceMember:
+ return 'instance member';
+ case CompletionGroup.labelElement:
+ return 'label';
+ case CompletionGroup.localFunctionElement:
+ return 'local function';
+ case CompletionGroup.localVariableElement:
+ return 'local variable';
+ case CompletionGroup.mixinElement:
+ return 'mixin';
+ case CompletionGroup.parameterElement:
+ return 'parameter';
+ case CompletionGroup.prefixElement:
+ return 'prefix';
+ case CompletionGroup.staticMember:
+ return 'static member';
+ case CompletionGroup.topLevelMember:
+ return 'top level member';
+ case CompletionGroup.typeParameterElement:
+ return 'type parameter';
+
+ case CompletionGroup.keywordDynamic:
+ return 'keyword dynamic';
+ case CompletionGroup.keywordVoid:
+ return 'keyword void';
+
+ case CompletionGroup.unknown:
+ return 'unknown';
+ }
+ return '<unknown>';
+ }
}
extension AvailableSuggestionsExtension on protocol.AvailableSuggestion {
diff --git a/pkg/analysis_server/tool/code_completion/metrics_util.dart b/pkg/analysis_server/tool/code_completion/metrics_util.dart
index 6078af8d..98acfce 100644
--- a/pkg/analysis_server/tool/code_completion/metrics_util.dart
+++ b/pkg/analysis_server/tool/code_completion/metrics_util.dart
@@ -5,6 +5,8 @@
import 'package:analysis_server/src/status/pages.dart';
import 'package:analyzer/src/generated/utilities_general.dart';
+import 'output_utilities.dart';
+
/// https://en.wikipedia.org/wiki/Average#Arithmetic_mean
class ArithmeticMeanComputer {
final String name;
@@ -74,15 +76,19 @@
int getCountOf(String id) => _buckets[id] ?? 0;
void printCounterValues() {
- print('Counts for \'$name\' (total = $_totalCount):');
if (_totalCount > 0) {
+ var table = [
+ ['', 'count', 'percent']
+ ];
var entries = _buckets.entries.toList();
entries.sort((first, second) => second.value - first.value);
for (var entry in entries) {
var id = entry.key;
var count = entry.value;
- print('[$id] $count (${printPercentage(count / _totalCount, 2)})');
+ table.add(
+ [id, count.toString(), printPercentage(count / _totalCount, 2)]);
}
+ printTable(table);
} else {
print('<no counts>');
}
diff --git a/pkg/analysis_server/tool/code_completion/output_utilities.dart b/pkg/analysis_server/tool/code_completion/output_utilities.dart
index 71660df..8f7e096 100644
--- a/pkg/analysis_server/tool/code_completion/output_utilities.dart
+++ b/pkg/analysis_server/tool/code_completion/output_utilities.dart
@@ -4,16 +4,30 @@
import 'dart:math' as math;
+/// Print the given [table].
+void printTable(List<List<String>> table) {
+ var buffer = StringBuffer();
+ buffer.writeTable(table);
+ print(buffer.toString());
+}
+
+/// Return an interable that will produce all of the integer values between
+/// [first] and [last] inclusive.
+Iterable<int> range(int first, int last) sync* {
+ for (var i = first; i < last; i++) {
+ yield i;
+ }
+}
+
/// Given a [table] represented as a list of rows, right justify all of the
-/// cells in the given [column], except for the first row under the assumption
-/// that the first row contains headers.
-void rightJustifyColumn(int column, List<List<String>> table) {
+/// cells in the given [column].
+void rightJustifyColumn(List<List<String>> table, int column) {
var width = 0;
for (var i = 0; i < table.length; i++) {
var row = table[i];
width = math.max(width, row[column].length);
}
- for (var i = 1; i < table.length; i++) {
+ for (var i = 0; i < table.length; i++) {
var row = table[i];
var cellValue = row[column];
var length = cellValue.length;
@@ -24,6 +38,14 @@
}
}
+/// Given a [table] represented as a list of rows, right justify all of the
+/// cells in the given [columns].
+void rightJustifyColumns(List<List<String>> table, Iterable<int> columns) {
+ for (var column in columns) {
+ rightJustifyColumn(table, column);
+ }
+}
+
extension OutputUtilities on StringSink {
/// Write the given [table].
///
diff --git a/pkg/analysis_server/tool/code_completion/visitors.dart b/pkg/analysis_server/tool/code_completion/visitors.dart
index 0b130f7..db428f9 100644
--- a/pkg/analysis_server/tool/code_completion/visitors.dart
+++ b/pkg/analysis_server/tool/code_completion/visitors.dart
@@ -210,68 +210,78 @@
@override
void visitAsExpression(AsExpression node) {
safelyRecordKeywordCompletion(node.asOperator);
- return super.visitAsExpression(node);
+ super.visitAsExpression(node);
+ }
+
+ @override
+ void visitAssertInitializer(AssertInitializer node) {
+ safelyRecordKeywordCompletion(node.assertKeyword);
+ super.visitAssertInitializer(node);
+ }
+
+ @override
+ void visitAssertStatement(AssertStatement node) {
+ safelyRecordKeywordCompletion(node.assertKeyword);
+ super.visitAssertStatement(node);
}
@override
void visitAwaitExpression(AwaitExpression node) {
safelyRecordKeywordCompletion(node.awaitKeyword);
- return super.visitAwaitExpression(node);
+ super.visitAwaitExpression(node);
}
@override
void visitBlockFunctionBody(BlockFunctionBody node) {
// 'async' | 'async' '*' | 'sync' '*':
safelyRecordKeywordCompletion(node.keyword);
- return super.visitBlockFunctionBody(node);
+ super.visitBlockFunctionBody(node);
}
@override
void visitBooleanLiteral(BooleanLiteral node) {
// 'false' | 'true'
safelyRecordKeywordCompletion(node.literal);
- return super.visitBooleanLiteral(node);
+ super.visitBooleanLiteral(node);
}
@override
void visitBreakStatement(BreakStatement node) {
safelyRecordKeywordCompletion(node.breakKeyword);
- return super.visitBreakStatement(node);
+ super.visitBreakStatement(node);
}
@override
void visitCatchClause(CatchClause node) {
- // Should we 'catch', it won't be suggested when it already exists as a
- // keyword in the file?
safelyRecordKeywordCompletion(node.catchKeyword);
safelyRecordKeywordCompletion(node.onKeyword);
- return super.visitCatchClause(node);
+ super.visitCatchClause(node);
}
@override
void visitClassDeclaration(ClassDeclaration node) {
safelyRecordKeywordCompletion(node.abstractKeyword);
safelyRecordKeywordCompletion(node.classKeyword);
- return super.visitClassDeclaration(node);
+ super.visitClassDeclaration(node);
}
@override
void visitClassTypeAlias(ClassTypeAlias node) {
safelyRecordKeywordCompletion(node.abstractKeyword);
safelyRecordKeywordCompletion(node.typedefKeyword);
- return super.visitClassTypeAlias(node);
+ super.visitClassTypeAlias(node);
}
@override
void visitCompilationUnit(CompilationUnit node) {
_enclosingCompilationUnit = node;
- return super.visitCompilationUnit(node);
+ super.visitCompilationUnit(node);
}
@override
void visitConfiguration(Configuration node) {
safelyRecordKeywordCompletion(node.ifKeyword);
- return super.visitConfiguration(node);
+ super.visitConfiguration(node);
}
@override
@@ -279,70 +289,64 @@
safelyRecordKeywordCompletion(node.externalKeyword);
safelyRecordKeywordCompletion(node.constKeyword);
safelyRecordKeywordCompletion(node.factoryKeyword);
- return super.visitConstructorDeclaration(node);
+ super.visitConstructorDeclaration(node);
}
@override
void visitConstructorFieldInitializer(ConstructorFieldInitializer node) {
safelyRecordKeywordCompletion(node.thisKeyword);
- return super.visitConstructorFieldInitializer(node);
+ super.visitConstructorFieldInitializer(node);
}
@override
void visitContinueStatement(ContinueStatement node) {
safelyRecordKeywordCompletion(node.continueKeyword);
- return super.visitContinueStatement(node);
+ super.visitContinueStatement(node);
}
@override
void visitDeclaredIdentifier(DeclaredIdentifier node) {
// 'final', 'const' or 'var'
safelyRecordKeywordCompletion(node.keyword);
- return super.visitDeclaredIdentifier(node);
+ super.visitDeclaredIdentifier(node);
}
@override
void visitDoStatement(DoStatement node) {
safelyRecordKeywordCompletion(node.doKeyword);
safelyRecordKeywordCompletion(node.whileKeyword);
- return super.visitDoStatement(node);
+ super.visitDoStatement(node);
}
@override
void visitEnumDeclaration(EnumDeclaration node) {
safelyRecordKeywordCompletion(node.enumKeyword);
- return super.visitEnumDeclaration(node);
+ super.visitEnumDeclaration(node);
}
@override
void visitExportDirective(ExportDirective node) {
safelyRecordKeywordCompletion(node.keyword);
- return super.visitExportDirective(node);
+ super.visitExportDirective(node);
}
@override
void visitExpressionFunctionBody(ExpressionFunctionBody node) {
safelyRecordKeywordCompletion(node.keyword);
- return super.visitExpressionFunctionBody(node);
+ super.visitExpressionFunctionBody(node);
}
@override
void visitExtendsClause(ExtendsClause node) {
safelyRecordKeywordCompletion(node.extendsKeyword);
- return super.visitExtendsClause(node);
+ super.visitExtendsClause(node);
}
@override
void visitExtensionDeclaration(ExtensionDeclaration node) {
safelyRecordKeywordCompletion(node.extensionKeyword);
safelyRecordKeywordCompletion(node.onKeyword);
- return super.visitExtensionDeclaration(node);
- }
-
- @override
- void visitExtensionOverride(ExtensionOverride node) {
- node.visitChildren(this);
- return null;
+ super.visitExtensionDeclaration(node);
}
@override
@@ -351,7 +355,7 @@
safelyRecordKeywordCompletion(node.covariantKeyword);
safelyRecordKeywordCompletion(node.externalKeyword);
safelyRecordKeywordCompletion(node.staticKeyword);
- return super.visitFieldDeclaration(node);
+ super.visitFieldDeclaration(node);
}
@override
@@ -359,33 +363,33 @@
// 'final', 'const' or 'var'
safelyRecordKeywordCompletion(node.keyword);
safelyRecordKeywordCompletion(node.thisKeyword);
- return super.visitFieldFormalParameter(node);
+ super.visitFieldFormalParameter(node);
}
@override
void visitForEachPartsWithDeclaration(ForEachPartsWithDeclaration node) {
safelyRecordKeywordCompletion(node.inKeyword);
- return super.visitForEachPartsWithDeclaration(node);
+ super.visitForEachPartsWithDeclaration(node);
}
@override
void visitForEachPartsWithIdentifier(ForEachPartsWithIdentifier node) {
safelyRecordKeywordCompletion(node.inKeyword);
- return super.visitForEachPartsWithIdentifier(node);
+ super.visitForEachPartsWithIdentifier(node);
}
@override
void visitForElement(ForElement node) {
safelyRecordKeywordCompletion(node.awaitKeyword);
safelyRecordKeywordCompletion(node.forKeyword);
- return super.visitForElement(node);
+ super.visitForElement(node);
}
@override
void visitForStatement(ForStatement node) {
safelyRecordKeywordCompletion(node.awaitKeyword);
safelyRecordKeywordCompletion(node.forKeyword);
- return super.visitForStatement(node);
+ super.visitForStatement(node);
}
@override
@@ -393,58 +397,59 @@
safelyRecordKeywordCompletion(node.externalKeyword);
// 'get' or 'set':
safelyRecordKeywordCompletion(node.propertyKeyword);
- return super.visitFunctionDeclaration(node);
+ super.visitFunctionDeclaration(node);
}
@override
void visitFunctionTypeAlias(FunctionTypeAlias node) {
safelyRecordKeywordCompletion(node.typedefKeyword);
- return super.visitFunctionTypeAlias(node);
+ super.visitFunctionTypeAlias(node);
}
@override
void visitGenericFunctionType(GenericFunctionType node) {
safelyRecordKeywordCompletion(node.functionKeyword);
- return super.visitGenericFunctionType(node);
+ super.visitGenericFunctionType(node);
}
@override
void visitGenericTypeAlias(GenericTypeAlias node) {
safelyRecordKeywordCompletion(node.typedefKeyword);
- return super.visitGenericTypeAlias(node);
+ super.visitGenericTypeAlias(node);
}
@override
void visitHideCombinator(HideCombinator node) {
safelyRecordKeywordCompletion(node.keyword);
- return super.visitHideCombinator(node);
+ super.visitHideCombinator(node);
}
@override
void visitIfElement(IfElement node) {
safelyRecordKeywordCompletion(node.ifKeyword);
safelyRecordKeywordCompletion(node.elseKeyword);
- return super.visitIfElement(node);
+ super.visitIfElement(node);
}
@override
void visitIfStatement(IfStatement node) {
safelyRecordKeywordCompletion(node.ifKeyword);
safelyRecordKeywordCompletion(node.elseKeyword);
- return super.visitIfStatement(node);
+ super.visitIfStatement(node);
}
@override
void visitImplementsClause(ImplementsClause node) {
safelyRecordKeywordCompletion(node.implementsKeyword);
- return super.visitImplementsClause(node);
+ super.visitImplementsClause(node);
}
@override
void visitImportDirective(ImportDirective node) {
safelyRecordKeywordCompletion(node.keyword);
safelyRecordKeywordCompletion(node.asKeyword);
- return super.visitImportDirective(node);
+ safelyRecordKeywordCompletion(node.deferredKeyword);
+ super.visitImportDirective(node);
}
@override
@@ -452,29 +457,28 @@
// Here we explicitly do not record 'new' as we don't suggest it in the
// completion service.
// https://dart-review.googlesource.com/c/sdk/+/131020
- var keywordStr = node.keyword?.lexeme;
- if (Keyword.CONST.lexeme == keywordStr) {
+ if (node.keyword?.type == Keyword.CONST) {
safelyRecordKeywordCompletion(node.keyword);
}
- return super.visitInstanceCreationExpression(node);
+ super.visitInstanceCreationExpression(node);
}
@override
void visitIsExpression(IsExpression node) {
safelyRecordKeywordCompletion(node.isOperator);
- return super.visitIsExpression(node);
+ super.visitIsExpression(node);
}
@override
void visitLibraryDirective(LibraryDirective node) {
safelyRecordKeywordCompletion(node.libraryKeyword);
- return super.visitLibraryDirective(node);
+ super.visitLibraryDirective(node);
}
@override
void visitListLiteral(ListLiteral node) {
safelyRecordKeywordCompletion(node.constKeyword);
- return super.visitListLiteral(node);
+ super.visitListLiteral(node);
}
@override
@@ -483,57 +487,57 @@
safelyRecordKeywordCompletion(node.modifierKeyword);
safelyRecordKeywordCompletion(node.operatorKeyword);
safelyRecordKeywordCompletion(node.propertyKeyword);
- return super.visitMethodDeclaration(node);
+ super.visitMethodDeclaration(node);
}
@override
void visitMixinDeclaration(MixinDeclaration node) {
safelyRecordKeywordCompletion(node.mixinKeyword);
- return super.visitMixinDeclaration(node);
+ super.visitMixinDeclaration(node);
}
@override
void visitNativeClause(NativeClause node) {
safelyRecordKeywordCompletion(node.nativeKeyword);
- return super.visitNativeClause(node);
+ super.visitNativeClause(node);
}
@override
void visitNativeFunctionBody(NativeFunctionBody node) {
safelyRecordKeywordCompletion(node.nativeKeyword);
- return super.visitNativeFunctionBody(node);
+ super.visitNativeFunctionBody(node);
}
@override
void visitNullLiteral(NullLiteral node) {
safelyRecordKeywordCompletion(node.literal);
- return super.visitNullLiteral(node);
+ super.visitNullLiteral(node);
}
@override
void visitOnClause(OnClause node) {
safelyRecordKeywordCompletion(node.onKeyword);
- return super.visitOnClause(node);
+ super.visitOnClause(node);
}
@override
void visitPartDirective(PartDirective node) {
safelyRecordKeywordCompletion(node.partKeyword);
- return super.visitPartDirective(node);
+ super.visitPartDirective(node);
}
@override
void visitPartOfDirective(PartOfDirective node) {
safelyRecordKeywordCompletion(node.partKeyword);
safelyRecordKeywordCompletion(node.ofKeyword);
- return super.visitPartOfDirective(node);
+ super.visitPartOfDirective(node);
}
@override
void visitRedirectingConstructorInvocation(
RedirectingConstructorInvocation node) {
safelyRecordKeywordCompletion(node.thisKeyword);
- return super.visitRedirectingConstructorInvocation(node);
+ super.visitRedirectingConstructorInvocation(node);
}
@override
@@ -545,19 +549,19 @@
@override
void visitReturnStatement(ReturnStatement node) {
safelyRecordKeywordCompletion(node.returnKeyword);
- return super.visitReturnStatement(node);
+ super.visitReturnStatement(node);
}
@override
void visitSetOrMapLiteral(SetOrMapLiteral node) {
safelyRecordKeywordCompletion(node.constKeyword);
- return super.visitSetOrMapLiteral(node);
+ super.visitSetOrMapLiteral(node);
}
@override
void visitShowCombinator(ShowCombinator node) {
safelyRecordKeywordCompletion(node.keyword);
- return super.visitShowCombinator(node);
+ super.visitShowCombinator(node);
}
@override
@@ -565,7 +569,7 @@
// 'final', 'const' or 'var'
safelyRecordKeywordCompletion(node.keyword);
safelyRecordKeywordCompletion(node.covariantKeyword);
- return super.visitSimpleFormalParameter(node);
+ super.visitSimpleFormalParameter(node);
}
@override
@@ -620,68 +624,68 @@
}
safelyRecordEntity(node, elementKind: elementKind);
}
- return super.visitSimpleIdentifier(node);
+ super.visitSimpleIdentifier(node);
}
@override
void visitSuperConstructorInvocation(SuperConstructorInvocation node) {
safelyRecordKeywordCompletion(node.superKeyword);
- return super.visitSuperConstructorInvocation(node);
+ super.visitSuperConstructorInvocation(node);
}
@override
void visitSuperExpression(SuperExpression node) {
safelyRecordKeywordCompletion(node.superKeyword);
- return super.visitSuperExpression(node);
+ super.visitSuperExpression(node);
}
@override
void visitSwitchCase(SwitchCase node) {
safelyRecordKeywordCompletion(node.keyword);
- return super.visitSwitchCase(node);
+ super.visitSwitchCase(node);
}
@override
void visitSwitchDefault(SwitchDefault node) {
safelyRecordKeywordCompletion(node.keyword);
- return super.visitSwitchDefault(node);
+ super.visitSwitchDefault(node);
}
@override
void visitSwitchStatement(SwitchStatement node) {
safelyRecordKeywordCompletion(node.switchKeyword);
- return super.visitSwitchStatement(node);
+ super.visitSwitchStatement(node);
}
@override
void visitThisExpression(ThisExpression node) {
safelyRecordKeywordCompletion(node.thisKeyword);
- return super.visitThisExpression(node);
+ super.visitThisExpression(node);
}
@override
void visitThrowExpression(ThrowExpression node) {
safelyRecordKeywordCompletion(node.throwKeyword);
- return super.visitThrowExpression(node);
+ super.visitThrowExpression(node);
}
@override
void visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) {
safelyRecordKeywordCompletion(node.externalKeyword);
- return super.visitTopLevelVariableDeclaration(node);
+ super.visitTopLevelVariableDeclaration(node);
}
@override
void visitTryStatement(TryStatement node) {
safelyRecordKeywordCompletion(node.tryKeyword);
safelyRecordKeywordCompletion(node.finallyKeyword);
- return super.visitTryStatement(node);
+ super.visitTryStatement(node);
}
@override
void visitTypeParameter(TypeParameter node) {
safelyRecordKeywordCompletion(node.extendsKeyword);
- return super.visitTypeParameter(node);
+ super.visitTypeParameter(node);
}
@override
@@ -689,25 +693,25 @@
// 'final', 'const' or 'var'
safelyRecordKeywordCompletion(node.keyword);
safelyRecordKeywordCompletion(node.lateKeyword);
- return super.visitVariableDeclarationList(node);
+ super.visitVariableDeclarationList(node);
}
@override
void visitWhileStatement(WhileStatement node) {
safelyRecordKeywordCompletion(node.whileKeyword);
- return super.visitWhileStatement(node);
+ super.visitWhileStatement(node);
}
@override
void visitWithClause(WithClause node) {
safelyRecordKeywordCompletion(node.withKeyword);
- return super.visitWithClause(node);
+ super.visitWithClause(node);
}
@override
void visitYieldStatement(YieldStatement node) {
safelyRecordKeywordCompletion(node.yieldKeyword);
- return super.visitYieldStatement(node);
+ super.visitYieldStatement(node);
}
bool _doIncludeSimpleIdentifier(SimpleIdentifier node) {
diff --git a/pkg/analyzer/lib/src/generated/error_verifier.dart b/pkg/analyzer/lib/src/generated/error_verifier.dart
index b275202..c9a953d 100644
--- a/pkg/analyzer/lib/src/generated/error_verifier.dart
+++ b/pkg/analyzer/lib/src/generated/error_verifier.dart
@@ -114,26 +114,28 @@
DartType get returnType => catchErrorOnErrorReturnType ?? element.returnType;
- static bool _inFactoryConstructor(ExecutableElement element) {
+ static bool _inFactoryConstructor(Element element) {
+ var enclosing = element?.enclosingElement;
+ if (enclosing == null) {
+ return false;
+ }
if (element is ConstructorElement) {
return element.isFactory;
}
- var enclosing = element?.enclosingElement;
- if (enclosing is ExecutableElement) {
- return _inFactoryConstructor(enclosing);
- }
- return false;
+ return _inFactoryConstructor(enclosing);
}
- static bool _inStaticMethod(ExecutableElement element) {
+ static bool _inStaticMethod(Element element) {
var enclosing = element?.enclosingElement;
+ if (enclosing == null) {
+ return false;
+ }
if (enclosing is ClassElement || enclosing is ExtensionElement) {
- return element.isStatic;
+ if (element is ExecutableElement) {
+ return element.isStatic;
+ }
}
- if (enclosing is ExecutableElement) {
- return _inStaticMethod(enclosing);
- }
- return false;
+ return _inStaticMethod(enclosing);
}
}
diff --git a/pkg/analyzer/lib/src/generated/ffi_verifier.dart b/pkg/analyzer/lib/src/generated/ffi_verifier.dart
index 8a32c10..7693984 100644
--- a/pkg/analyzer/lib/src/generated/ffi_verifier.dart
+++ b/pkg/analyzer/lib/src/generated/ffi_verifier.dart
@@ -775,7 +775,7 @@
}
void _validateRefIndexed(IndexExpression node) {
- DartType targetType = node.target?.staticType;
+ DartType targetType = node.realTarget?.staticType;
if (!_isValidFfiNativeType(targetType, false, true)) {
final AstNode errorNode = node;
_errorReporter.reportErrorForNode(
@@ -795,7 +795,7 @@
}
void _validateRefPropertyAccess(PropertyAccess node) {
- DartType targetType = node.target?.staticType;
+ DartType targetType = node.realTarget?.staticType;
if (!_isValidFfiNativeType(targetType, false, true)) {
final AstNode errorNode = node;
_errorReporter.reportErrorForNode(
diff --git a/pkg/analyzer/lib/src/test_utilities/mock_sdk.dart b/pkg/analyzer/lib/src/test_utilities/mock_sdk.dart
index 36be56b..16f0bcd 100644
--- a/pkg/analyzer/lib/src/test_utilities/mock_sdk.dart
+++ b/pkg/analyzer/lib/src/test_utilities/mock_sdk.dart
@@ -661,6 +661,8 @@
}
class Pointer<T extends NativeType> extends NativeType {
+ external factory Pointer.fromAddress(int ptr);
+
static Pointer<NativeFunction<T>> fromFunction<T extends Function>(
@DartRepresentationOf("T") Function f,
[Object exceptionalReturn]) {}
@@ -685,6 +687,12 @@
class DartRepresentationOf {
const DartRepresentationOf(String nativeType);
}
+
+extension StructPointer<T extends Struct> on Pointer<T> {
+ external T get ref;
+
+ external T operator [](int index);
+}
''',
)
]);
diff --git a/pkg/analyzer/test/src/diagnostics/instance_member_access_from_factory_test.dart b/pkg/analyzer/test/src/diagnostics/instance_member_access_from_factory_test.dart
index 8791394..d4de128 100644
--- a/pkg/analyzer/test/src/diagnostics/instance_member_access_from_factory_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/instance_member_access_from_factory_test.dart
@@ -15,66 +15,97 @@
@reflectiveTest
class InstanceMemberAccessFromFactoryTest extends PubPackageResolutionTest {
- test_named() async {
+ test_named_getter() async {
await assertErrorsInCode(r'''
class A {
- m() {}
- A();
+ int get foo => 0;
+
factory A.make() {
- m();
- return new A();
+ foo;
+ throw 0;
}
}
''', [
- error(CompileTimeErrorCode.INSTANCE_MEMBER_ACCESS_FROM_FACTORY, 51, 1),
+ error(CompileTimeErrorCode.INSTANCE_MEMBER_ACCESS_FROM_FACTORY, 56, 3),
]);
}
- test_property() async {
+ test_named_getter_localFunction() async {
await assertErrorsInCode(r'''
class A {
- int m;
- A();
- factory A.make() {
- m;
- return new A();
- }
-}
-''', [
- error(CompileTimeErrorCode.INSTANCE_MEMBER_ACCESS_FROM_FACTORY, 51, 1),
- ]);
- }
+ int get foo => 0;
- test_property_fromClosure() async {
- await assertErrorsInCode(r'''
-class A {
- int m;
- A();
factory A.make() {
void f() {
- m;
+ foo;
}
f();
- return new A();
+ throw 0;
}
}
''', [
- error(CompileTimeErrorCode.INSTANCE_MEMBER_ACCESS_FROM_FACTORY, 68, 1),
+ error(CompileTimeErrorCode.INSTANCE_MEMBER_ACCESS_FROM_FACTORY, 73, 3),
]);
}
- test_unnamed() async {
+ test_named_method() async {
await assertErrorsInCode(r'''
class A {
- m() {}
- A._();
- factory A() {
- m();
- return new A._();
+ void foo() {}
+
+ factory A.make() {
+ foo();
+ throw 0;
}
}
''', [
- error(CompileTimeErrorCode.INSTANCE_MEMBER_ACCESS_FROM_FACTORY, 48, 1),
+ error(CompileTimeErrorCode.INSTANCE_MEMBER_ACCESS_FROM_FACTORY, 52, 3),
+ ]);
+ }
+
+ test_named_method_functionExpression() async {
+ await assertErrorsInCode(r'''
+class A {
+ void foo() {}
+
+ factory A.make() {
+ () => foo();
+ throw 0;
+ }
+}
+''', [
+ error(CompileTimeErrorCode.INSTANCE_MEMBER_ACCESS_FROM_FACTORY, 58, 3),
+ ]);
+ }
+
+ test_named_method_functionExpression_localVariable() async {
+ await assertErrorsInCode(r'''
+class A {
+ void foo() {}
+
+ factory A.make() {
+ // ignore:unused_local_variable
+ var x = () => foo();
+ throw 0;
+ }
+}
+''', [
+ error(CompileTimeErrorCode.INSTANCE_MEMBER_ACCESS_FROM_FACTORY, 102, 3),
+ ]);
+ }
+
+ test_unnamed_method() async {
+ await assertErrorsInCode(r'''
+class A {
+ void foo() {}
+
+ factory A() {
+ foo();
+ throw 0;
+ }
+}
+''', [
+ error(CompileTimeErrorCode.INSTANCE_MEMBER_ACCESS_FROM_FACTORY, 47, 3),
]);
}
}
diff --git a/pkg/analyzer/test/src/diagnostics/instance_member_access_from_static_test.dart b/pkg/analyzer/test/src/diagnostics/instance_member_access_from_static_test.dart
index c1a122b..f56d41a 100644
--- a/pkg/analyzer/test/src/diagnostics/instance_member_access_from_static_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/instance_member_access_from_static_test.dart
@@ -61,6 +61,35 @@
]);
}
+ test_class_thisGetter_fromMethod_functionExpression() async {
+ await assertErrorsInCode(r'''
+class A {
+ int get foo => 0;
+
+ static void bar() {
+ () => foo;
+ }
+}
+''', [
+ error(CompileTimeErrorCode.INSTANCE_MEMBER_ACCESS_FROM_STATIC, 63, 3),
+ ]);
+ }
+
+ test_class_thisGetter_fromMethod_functionExpression_localVariable() async {
+ await assertErrorsInCode(r'''
+class A {
+ int get foo => 0;
+
+ static void bar() {
+ // ignore:unused_local_variable
+ var x = () => foo;
+ }
+}
+''', [
+ error(CompileTimeErrorCode.INSTANCE_MEMBER_ACCESS_FROM_STATIC, 107, 3),
+ ]);
+ }
+
test_class_thisMethod_fromMethod() async {
await assertErrorsInCode(r'''
class A {
diff --git a/pkg/analyzer/test/src/diagnostics/non_constant_type_argument_test.dart b/pkg/analyzer/test/src/diagnostics/non_constant_type_argument_test.dart
index eee33d4..f783c71 100644
--- a/pkg/analyzer/test/src/diagnostics/non_constant_type_argument_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/non_constant_type_argument_test.dart
@@ -9,11 +9,53 @@
main() {
defineReflectiveSuite(() {
+ defineReflectiveTests(NonConstantTypeArgumentNoWarningTest);
+ defineReflectiveTests(NonConstantTypeArgumentNoWarningTest2);
defineReflectiveTests(NonConstantTypeArgumentTest);
+ defineReflectiveTests(NonConstantTypeArgumentWarningTest);
});
}
@reflectiveTest
+class NonConstantTypeArgumentNoWarningTest extends PubPackageResolutionTest {
+ test_asFunction_R() async {
+ await assertNoErrorsInCode(r'''
+import 'dart:ffi';
+
+class MyStruct extends Struct {
+ @Uint8()
+ int myField;
+}
+
+void main(){
+ final pointer = Pointer<MyStruct>.fromAddress(0);
+ pointer.ref.myField = 1;
+}
+''');
+ }
+}
+
+@reflectiveTest
+class NonConstantTypeArgumentNoWarningTest2 extends PubPackageResolutionTest {
+ test_asFunction_R() async {
+ await assertNoErrorsInCode(r'''
+import 'dart:ffi';
+
+class MyStruct extends Struct {
+ @Uint8()
+ int myField;
+}
+
+void main(){
+ final pointer = Pointer<MyStruct>.fromAddress(0)
+ ..ref.myField = 1;
+ print(pointer);
+}
+''');
+ }
+}
+
+@reflectiveTest
class NonConstantTypeArgumentTest extends PubPackageResolutionTest {
test_asFunction_R() async {
await assertErrorsInCode(r'''
@@ -29,3 +71,15 @@
]);
}
}
+
+@reflectiveTest
+class NonConstantTypeArgumentWarningTest extends PubPackageResolutionTest {
+ test_asFunction_R() async {
+ await assertErrorsInCode(r'''
+import 'dart:ffi';
+
+T genericRef<T extends Struct>(Pointer<T> p) =>
+ p.ref;
+''', [error(FfiCode.NON_CONSTANT_TYPE_ARGUMENT_WARNING, 72, 5)]);
+ }
+}
diff --git a/pkg/analyzer/test/src/diagnostics/subtype_of_ffi_class_test.dart b/pkg/analyzer/test/src/diagnostics/subtype_of_ffi_class_test.dart
index 7073a39..85129db 100644
--- a/pkg/analyzer/test/src/diagnostics/subtype_of_ffi_class_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/subtype_of_ffi_class_test.dart
@@ -75,7 +75,9 @@
test_Pointer() async {
await assertErrorsInCode(r'''
import 'dart:ffi';
-class C extends Pointer {}
+class C extends Pointer {
+ external factory C();
+}
''', [
error(FfiCode.SUBTYPE_OF_FFI_CLASS_IN_EXTENDS, 35, 7),
]);
diff --git a/runtime/vm/flags.cc b/runtime/vm/flags.cc
index f3fc0ef..8b5ba1b 100644
--- a/runtime/vm/flags.cc
+++ b/runtime/vm/flags.cc
@@ -478,11 +478,6 @@
// isolate that has not yet initialized the global field.
FLAG_fields_may_be_reset = true;
- // We will start by only allowing compilation to unoptimized code.
- FLAG_optimization_counter_threshold = -1;
- FLAG_background_compilation = false;
- FLAG_force_clone_compiler_objects = true;
-
// To eliminate potential flakiness, we will start by disabling field guards
// and CHA-based compilations.
FLAG_use_field_guards = false;
diff --git a/runtime/vm/runtime_entry.cc b/runtime/vm/runtime_entry.cc
index 5a0c2ea..c888a56 100644
--- a/runtime/vm/runtime_entry.cc
+++ b/runtime/vm/runtime_entry.cc
@@ -1048,17 +1048,26 @@
const Code& target_code = Code::Handle(zone, target_function.EnsureHasCode());
// Before patching verify that we are not repeatedly patching to the same
// target.
- ASSERT(target_code.ptr() !=
- CodePatcher::GetStaticCallTargetAt(caller_frame->pc(), caller_code));
- CodePatcher::PatchStaticCallAt(caller_frame->pc(), caller_code, target_code);
- caller_code.SetStaticCallTargetCodeAt(caller_frame->pc(), target_code);
- if (FLAG_trace_patching) {
- THR_Print("PatchStaticCall: patching caller pc %#" Px
- ""
- " to '%s' new entry point %#" Px " (%s)\n",
- caller_frame->pc(), target_function.ToFullyQualifiedCString(),
- target_code.EntryPoint(),
- target_code.is_optimized() ? "optimized" : "unoptimized");
+ ASSERT(FLAG_enable_isolate_groups ||
+ target_code.ptr() != CodePatcher::GetStaticCallTargetAt(
+ caller_frame->pc(), caller_code));
+ if (target_code.ptr() !=
+ CodePatcher::GetStaticCallTargetAt(caller_frame->pc(), caller_code)) {
+ SafepointOperationScope safepoint(thread);
+ if (target_code.ptr() !=
+ CodePatcher::GetStaticCallTargetAt(caller_frame->pc(), caller_code)) {
+ CodePatcher::PatchStaticCallAt(caller_frame->pc(), caller_code,
+ target_code);
+ caller_code.SetStaticCallTargetCodeAt(caller_frame->pc(), target_code);
+ if (FLAG_trace_patching) {
+ THR_Print("PatchStaticCall: patching caller pc %#" Px
+ ""
+ " to '%s' new entry point %#" Px " (%s)\n",
+ caller_frame->pc(), target_function.ToFullyQualifiedCString(),
+ target_code.EntryPoint(),
+ target_code.is_optimized() ? "optimized" : "unoptimized");
+ }
+ }
}
arguments.SetReturn(target_code);
#else
diff --git a/tests/ffi/structs_test.dart b/tests/ffi/structs_test.dart
index 6bc1ed1..a38c241 100644
--- a/tests/ffi/structs_test.dart
+++ b/tests/ffi/structs_test.dart
@@ -24,6 +24,7 @@
testBareStruct();
testTypeTest();
testUtf8();
+ testDotDotRef();
}
}
@@ -135,3 +136,10 @@
Expect.equals(test, Utf8.fromUtf8(medium));
calloc.free(medium);
}
+
+void testDotDotRef() {
+ final pointer = calloc<Coordinate>()
+ ..ref.x = 1
+ ..ref.y = 1;
+ calloc.free(pointer);
+}
diff --git a/tests/ffi_2/structs_test.dart b/tests/ffi_2/structs_test.dart
index 2759056..57bdbc6 100644
--- a/tests/ffi_2/structs_test.dart
+++ b/tests/ffi_2/structs_test.dart
@@ -24,6 +24,7 @@
testBareStruct();
testTypeTest();
testUtf8();
+ testDotDotRef();
}
}
@@ -135,3 +136,10 @@
Expect.equals(test, Utf8.fromUtf8(medium));
calloc.free(medium);
}
+
+void testDotDotRef() {
+ final pointer = calloc<Coordinate>()
+ ..ref.x = 1
+ ..ref.y = 1;
+ calloc.free(pointer);
+}
diff --git a/tools/VERSION b/tools/VERSION
index b8d079b..c1f184a 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
MAJOR 2
MINOR 12
PATCH 0
-PRERELEASE 264
+PRERELEASE 265
PRERELEASE_PATCH 0
\ No newline at end of file