Version 2.16.0-11.0.dev
Merge commit 'ecdcde3a838e1c88c2d00df3b8d36a916b628b49' into 'dev'
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handler_document_color.dart b/pkg/analysis_server/lib/src/lsp/handlers/handler_document_color.dart
new file mode 100644
index 0000000..07bf25b
--- /dev/null
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_document_color.dart
@@ -0,0 +1,62 @@
+// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:analysis_server/lsp_protocol/protocol_generated.dart';
+import 'package:analysis_server/lsp_protocol/protocol_special.dart';
+import 'package:analysis_server/src/computer/computer_color.dart'
+ show ColorComputer, ColorReference;
+import 'package:analysis_server/src/lsp/handlers/handlers.dart';
+import 'package:analysis_server/src/lsp/lsp_analysis_server.dart';
+import 'package:analysis_server/src/lsp/mapping.dart';
+import 'package:analyzer/dart/analysis/results.dart';
+
+/// Handles textDocument/documentColor requests.
+///
+/// This request is sent by the client to the server to request the locations
+/// of any colors in the document so that it can render color previews. If the
+/// editor has a color picker, it may also call textDocument/colorPresentation
+/// to obtain the code to insert when a new color is selected (see
+/// [DocumentColorPresentationHandler]).
+class DocumentColorHandler
+ extends MessageHandler<DocumentColorParams, List<ColorInformation>> {
+ DocumentColorHandler(LspAnalysisServer server) : super(server);
+ @override
+ Method get handlesMessage => Method.textDocument_documentColor;
+
+ @override
+ LspJsonHandler<DocumentColorParams> get jsonHandler =>
+ DocumentColorParams.jsonHandler;
+
+ @override
+ Future<ErrorOr<List<ColorInformation>>> handle(
+ DocumentColorParams params, CancellationToken token) async {
+ if (!isDartDocument(params.textDocument)) {
+ return success([]);
+ }
+
+ final path = pathOfDoc(params.textDocument);
+ final unit = await path.mapResult(requireResolvedUnit);
+ return unit.mapResult((unit) => _getColors(unit));
+ }
+
+ ErrorOr<List<ColorInformation>> _getColors(ResolvedUnitResult unit) {
+ ColorInformation _toColorInformation(ColorReference reference) {
+ return ColorInformation(
+ range: toRange(unit.lineInfo, reference.offset, reference.length),
+ color: Color(
+ // LSP colors are decimal in the range 0-1 but our internal references
+ // are 0-255, so divide them.
+ alpha: reference.color.alpha / 255,
+ red: reference.color.red / 255,
+ green: reference.color.green / 255,
+ blue: reference.color.blue / 255,
+ ),
+ );
+ }
+
+ final computer = ColorComputer(unit);
+ final colors = computer.compute();
+ return success(colors.map(_toColorInformation).toList());
+ }
+}
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handler_document_color_presentation.dart b/pkg/analysis_server/lib/src/lsp/handlers/handler_document_color_presentation.dart
new file mode 100644
index 0000000..52f09fb
--- /dev/null
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_document_color_presentation.dart
@@ -0,0 +1,122 @@
+// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:analysis_server/lsp_protocol/protocol_generated.dart';
+import 'package:analysis_server/lsp_protocol/protocol_special.dart';
+import 'package:analysis_server/src/lsp/handlers/handlers.dart';
+import 'package:analysis_server/src/lsp/lsp_analysis_server.dart';
+import 'package:analysis_server/src/lsp/mapping.dart';
+import 'package:analysis_server/src/utilities/flutter.dart';
+import 'package:analyzer/dart/analysis/results.dart';
+import 'package:analyzer/source/source_range.dart';
+import 'package:analyzer/src/dart/analysis/session_helper.dart';
+import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart';
+
+/// Handles textDocument/colorPresentation.
+///
+/// This request is sent by the client if it allowed the user to pick a color
+/// using a color picker (in a location returned by textDocument/documentColor)
+/// and needs a representation of this color, including the edits to insert it
+/// into the source file.
+class DocumentColorPresentationHandler
+ extends MessageHandler<ColorPresentationParams, List<ColorPresentation>> {
+ DocumentColorPresentationHandler(LspAnalysisServer server) : super(server);
+ @override
+ Method get handlesMessage => Method.textDocument_colorPresentation;
+
+ @override
+ LspJsonHandler<ColorPresentationParams> get jsonHandler =>
+ ColorPresentationParams.jsonHandler;
+
+ @override
+ Future<ErrorOr<List<ColorPresentation>>> handle(
+ ColorPresentationParams params,
+ CancellationToken token,
+ ) async {
+ if (!isDartDocument(params.textDocument)) {
+ return success([]);
+ }
+
+ final path = pathOfDoc(params.textDocument);
+ final unit = await path.mapResult(requireResolvedUnit);
+ return unit.mapResult((unit) => _getPresentations(params, unit));
+ }
+
+ /// Converts individual 0-255 ARGB values into a single int value as
+ /// 0xAARRGGBB as used by the dart:ui Color class.
+ int _colorValueForComponents(int alpha, int red, int green, int blue) {
+ return (alpha << 24) | (red << 16) | (green << 8) | (blue << 0);
+ }
+
+ /// Builds a list of valid color presentations for the requested color.
+ ///
+ /// Currently only a single presentation (`Color(0xAARRGGBB)`) is provided.
+ Future<ErrorOr<List<ColorPresentation>>> _getPresentations(
+ ColorPresentationParams params,
+ ResolvedUnitResult unit,
+ ) async {
+ // The values in LSP are decimals 0-1 so should be scaled up to 255 that
+ // we use internally.
+ final colorValue = _colorValueForComponents(
+ (params.color.alpha * 255).toInt(),
+ (params.color.red * 255).toInt(),
+ (params.color.green * 255).toInt(),
+ (params.color.blue * 255).toInt(),
+ );
+
+ final colorValueHex =
+ '0x${colorValue.toRadixString(16).toUpperCase().padLeft(8, '0')}';
+
+ final editStart = toOffset(unit.lineInfo, params.range.start);
+ final editEnd = toOffset(unit.lineInfo, params.range.end);
+
+ if (editStart.isError) return failure(editStart);
+ if (editEnd.isError) return failure(editEnd);
+
+ final editRange =
+ SourceRange(editStart.result, editEnd.result - editStart.result);
+
+ final sessionHelper = AnalysisSessionHelper(unit.session);
+ final flutter = Flutter.instance;
+ final colorType = await sessionHelper.getClass(flutter.widgetsUri, 'Color');
+ if (colorType == null) {
+ // If we can't find the class (perhaps because this isn't a Flutter
+ // project) we will not include any results. In theory the client should
+ // not be calling this request in that case.
+ return success([]);
+ }
+
+ final builder = ChangeBuilder(session: unit.session);
+ await builder.addDartFileEdit(unit.path, (builder) {
+ builder.addReplacement(editRange, (builder) {
+ builder.writeType(colorType.thisType);
+ builder.write('($colorValueHex)');
+ });
+ });
+
+ // We can only apply changes to the same file, so filter any change from the
+ // builder to only include this file, otherwise we may corrupt the users
+ // source (although hopefully we don't produce edits for other files).
+ final editsForThisFile = builder.sourceChange.edits
+ .where((edit) => edit.file == unit.path)
+ .expand((edit) => edit.edits)
+ .toList();
+
+ // LSP requires that we separate the main edit (changing the color code)
+ // from anything else (imports).
+ final mainEdit =
+ editsForThisFile.singleWhere((edit) => edit.offset == editRange.offset);
+ final otherEdits =
+ editsForThisFile.where((edit) => edit.offset != editRange.offset);
+
+ return success([
+ ColorPresentation(
+ label: 'Color($colorValueHex)',
+ textEdit: toTextEdit(unit.lineInfo, mainEdit),
+ additionalTextEdits:
+ otherEdits.map((edit) => toTextEdit(unit.lineInfo, edit)).toList(),
+ ),
+ ]);
+ }
+}
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handler_states.dart b/pkg/analysis_server/lib/src/lsp/handlers/handler_states.dart
index c0e413d..0d9eb19 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/handler_states.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_states.dart
@@ -15,6 +15,8 @@
import 'package:analysis_server/src/lsp/handlers/handler_completion.dart';
import 'package:analysis_server/src/lsp/handlers/handler_completion_resolve.dart';
import 'package:analysis_server/src/lsp/handlers/handler_definition.dart';
+import 'package:analysis_server/src/lsp/handlers/handler_document_color.dart';
+import 'package:analysis_server/src/lsp/handlers/handler_document_color_presentation.dart';
import 'package:analysis_server/src/lsp/handlers/handler_document_highlights.dart';
import 'package:analysis_server/src/lsp/handlers/handler_document_symbols.dart';
import 'package:analysis_server/src/lsp/handlers/handler_execute_command.dart';
@@ -77,6 +79,8 @@
server,
server.initializationOptions.suggestFromUnimportedLibraries,
));
+ registerHandler(DocumentColorHandler(server));
+ registerHandler(DocumentColorPresentationHandler(server));
registerHandler(CompletionResolveHandler(server));
registerHandler(SignatureHelpHandler(server));
registerHandler(DefinitionHandler(server));
diff --git a/pkg/analysis_server/lib/src/lsp/server_capabilities_computer.dart b/pkg/analysis_server/lib/src/lsp/server_capabilities_computer.dart
index 36bff9b..5a009f5 100644
--- a/pkg/analysis_server/lib/src/lsp/server_capabilities_computer.dart
+++ b/pkg/analysis_server/lib/src/lsp/server_capabilities_computer.dart
@@ -30,6 +30,7 @@
Method.textDocument_signatureHelp,
Method.textDocument_references,
Method.textDocument_documentHighlight,
+ Method.textDocument_documentColor,
Method.textDocument_formatting,
Method.textDocument_onTypeFormatting,
Method.textDocument_rangeFormatting,
@@ -52,6 +53,9 @@
bool get codeActions =>
_capabilities.textDocument?.foldingRange?.dynamicRegistration ?? false;
+ bool get colorProvider =>
+ _capabilities.textDocument?.colorProvider?.dynamicRegistration ?? false;
+
bool get completion =>
_capabilities.textDocument?.completion?.dynamicRegistration ?? false;
@@ -130,8 +134,15 @@
Set<Registration> currentRegistrations = {};
var _lastRegistrationId = 0;
- ServerCapabilitiesComputer(this._server);
+ final dartFiles = DocumentFilter(language: 'dart', scheme: 'file');
+ final pubspecFile = DocumentFilter(
+ language: 'yaml', scheme: 'file', pattern: '**/pubspec.yaml');
+ final analysisOptionsFile = DocumentFilter(
+ language: 'yaml', scheme: 'file', pattern: '**/analysis_options.yaml');
+ final fixDataFile = DocumentFilter(
+ language: 'yaml', scheme: 'file', pattern: '**/lib/fix_data.yaml');
+ ServerCapabilitiesComputer(this._server);
ServerCapabilities computeServerCapabilities(
LspClientCapabilities clientCapabilities) {
final codeActionLiteralSupport = clientCapabilities.literalCodeActions;
@@ -210,6 +221,11 @@
codeActionKinds: DartCodeActionKind.serverSupportedKinds,
))
: Either2<bool, CodeActionOptions>.t1(true),
+ colorProvider: dynamicRegistrations.colorProvider
+ ? null
+ : Either3<bool, DocumentColorOptions,
+ DocumentColorRegistrationOptions>.t3(
+ DocumentColorRegistrationOptions(documentSelector: [dartFiles])),
documentFormattingProvider: dynamicRegistrations.formatting
? null
: Either2<bool, DocumentFormattingOptions>.t1(enableFormatter),
@@ -279,14 +295,6 @@
/// support and it will be up to them to decide which file types they will
/// send requests for.
Future<void> performDynamicRegistration() async {
- final dartFiles = DocumentFilter(language: 'dart', scheme: 'file');
- final pubspecFile = DocumentFilter(
- language: 'yaml', scheme: 'file', pattern: '**/pubspec.yaml');
- final analysisOptionsFile = DocumentFilter(
- language: 'yaml', scheme: 'file', pattern: '**/analysis_options.yaml');
- final fixDataFile = DocumentFilter(
- language: 'yaml', scheme: 'file', pattern: '**/lib/fix_data.yaml');
-
final pluginTypes = _server.pluginManager.plugins
.expand((plugin) => plugin.currentSession?.interestingFiles ?? const [])
// All published plugins use something like `*.extension` as
@@ -410,6 +418,12 @@
TextDocumentRegistrationOptions(documentSelector: fullySupportedTypes),
);
register(
+ dynamicRegistrations.colorProvider,
+ // This registration covers both documentColor and colorPresentation.
+ Method.textDocument_documentColor,
+ DocumentColorRegistrationOptions(documentSelector: [dartFiles]),
+ );
+ register(
enableFormatter && dynamicRegistrations.formatting,
Method.textDocument_formatting,
TextDocumentRegistrationOptions(documentSelector: fullySupportedTypes),
diff --git a/pkg/analysis_server/test/lsp/document_color_test.dart b/pkg/analysis_server/test/lsp/document_color_test.dart
new file mode 100644
index 0000000..12dd16d
--- /dev/null
+++ b/pkg/analysis_server/test/lsp/document_color_test.dart
@@ -0,0 +1,145 @@
+// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:analysis_server/lsp_protocol/protocol_generated.dart';
+import 'package:test/test.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import 'server_abstract.dart';
+
+void main() {
+ defineReflectiveSuite(() {
+ defineReflectiveTests(DocumentColorTest);
+ defineReflectiveTests(DocumentColorPresentationTest);
+ });
+}
+
+@reflectiveTest
+class DocumentColorPresentationTest extends AbstractLspAnalysisServerTest {
+ @override
+ void setUp() {
+ super.setUp();
+ writePackageConfig(projectFolderPath, flutter: true);
+ }
+
+ Future<void> test_includesImportEdit() async {
+ // Create a file that doesn't already import the required library.
+ //
+ // We don't need a real color reference in the file right now, as we're
+ // calling colorPresentation directly to get the new code (and not fetching
+ // colors already in the file).
+ const content = '''
+const white = [[]];
+''';
+
+ const expectedMainEdit = '''
+const white = Color(0xFFFFFFFF);
+''';
+
+ const expectedAllEdits = '''
+import 'package:ui/ui.dart';
+
+const white = Color(0xFFFFFFFF);
+''';
+
+ newFile(mainFilePath, content: withoutMarkers(content));
+ await initialize();
+
+ final colorPresentations = await getColorPresentation(
+ mainFileUri.toString(),
+ rangeFromMarkers(content),
+ Color(alpha: 1, red: 1, green: 1, blue: 1),
+ );
+ expect(colorPresentations, hasLength(1));
+
+ final colorPresentation = colorPresentations[0];
+ expect(colorPresentation.label, 'Color(0xFFFFFFFF)');
+
+ // Test the main edit just replaces the color.
+ final contentWithMainEdit = applyTextEdits(
+ withoutMarkers(content),
+ [colorPresentation.textEdit!],
+ );
+ expect(contentWithMainEdit, equals(expectedMainEdit));
+
+ // Test all of the edits also adds the import.
+ final contentWithAllEdits = applyTextEdits(
+ withoutMarkers(content),
+ [colorPresentation.textEdit!, ...colorPresentation.additionalTextEdits!],
+ );
+ expect(contentWithAllEdits, equals(expectedAllEdits));
+ }
+
+ Future<void> test_nonDartFile() async {
+ newFile(pubspecFilePath, content: simplePubspecContent);
+ await initialize();
+
+ final colors = await getColorPresentation(
+ pubspecFileUri.toString(),
+ startOfDocRange,
+ Color(alpha: 1, red: 1, green: 1, blue: 1),
+ );
+ expect(colors, isEmpty);
+ }
+
+ Future<void> test_simpleColor() async {
+ const content = '''
+ import 'package:flutter/material.dart';
+
+ const white = [[Color(0xFFFFFFFF)]];
+ ''';
+ newFile(mainFilePath, content: withoutMarkers(content));
+ await initialize();
+
+ final colorPresentations = await getColorPresentation(
+ mainFileUri.toString(),
+ rangeFromMarkers(content),
+ // Send a different color to what's in the source to simulate the user
+ // having changed in the color picker. This is the one that we should be
+ // creating a presentation for, not the one in the source.
+ Color(alpha: 1, red: 1, green: 0, blue: 0),
+ );
+ expect(colorPresentations, hasLength(1));
+
+ final colorPresentation = colorPresentations[0];
+ expect(colorPresentation.label, 'Color(0xFFFF0000)');
+ }
+}
+
+@reflectiveTest
+class DocumentColorTest extends AbstractLspAnalysisServerTest {
+ @override
+ void setUp() {
+ super.setUp();
+ writePackageConfig(projectFolderPath, flutter: true);
+ }
+
+ Future<void> test_nonDartFile() async {
+ newFile(pubspecFilePath, content: simplePubspecContent);
+ await initialize();
+
+ final colors = await getDocumentColors(pubspecFileUri.toString());
+ expect(colors, isEmpty);
+ }
+
+ Future<void> test_simpleColor() async {
+ const content = '''
+ import 'package:flutter/material.dart';
+
+ const red = [[Colors.red]];
+ ''';
+ newFile(mainFilePath, content: withoutMarkers(content));
+ await initialize();
+
+ final colors = await getDocumentColors(mainFileUri.toString());
+ expect(colors, hasLength(1));
+
+ final color = colors[0];
+ expect(color.range, rangeFromMarkers(content));
+ expect(color.color.alpha, equals(1));
+ expect(color.color.red, equals(1));
+ expect(color.color.green, equals(0));
+ expect(color.color.blue, equals(0));
+ }
+}
diff --git a/pkg/analysis_server/test/lsp/initialization_test.dart b/pkg/analysis_server/test/lsp/initialization_test.dart
index b6bbc58..4d006be 100644
--- a/pkg/analysis_server/test/lsp/initialization_test.dart
+++ b/pkg/analysis_server/test/lsp/initialization_test.dart
@@ -230,6 +230,7 @@
expect(initResult.capabilities.hoverProvider, isNotNull);
expect(initResult.capabilities.signatureHelpProvider, isNotNull);
expect(initResult.capabilities.referencesProvider, isNotNull);
+ expect(initResult.capabilities.colorProvider, isNotNull);
expect(initResult.capabilities.documentHighlightProvider, isNotNull);
expect(initResult.capabilities.documentFormattingProvider, isNotNull);
expect(initResult.capabilities.documentOnTypeFormattingProvider, isNotNull);
@@ -289,6 +290,7 @@
expect(initResult.capabilities.hoverProvider, isNull);
expect(initResult.capabilities.signatureHelpProvider, isNull);
expect(initResult.capabilities.referencesProvider, isNull);
+ expect(initResult.capabilities.colorProvider, isNull);
expect(initResult.capabilities.documentHighlightProvider, isNull);
expect(initResult.capabilities.documentFormattingProvider, isNull);
expect(initResult.capabilities.documentOnTypeFormattingProvider, isNull);
diff --git a/pkg/analysis_server/test/lsp/server_abstract.dart b/pkg/analysis_server/test/lsp/server_abstract.dart
index 6b8ba52..e46aa1b 100644
--- a/pkg/analysis_server/test/lsp/server_abstract.dart
+++ b/pkg/analysis_server/test/lsp/server_abstract.dart
@@ -284,6 +284,7 @@
'references': {'dynamicRegistration': true},
'documentHighlight': {'dynamicRegistration': true},
'documentSymbol': {'dynamicRegistration': true},
+ 'colorProvider': {'dynamicRegistration': true},
'formatting': {'dynamicRegistration': true},
'onTypeFormatting': {'dynamicRegistration': true},
'rangeFormatting': {'dynamicRegistration': true},
@@ -1035,6 +1036,22 @@
);
}
+ Future<List<ColorPresentation>> getColorPresentation(
+ String fileUri, Range range, Color color) {
+ final request = makeRequest(
+ Method.textDocument_colorPresentation,
+ ColorPresentationParams(
+ textDocument: TextDocumentIdentifier(uri: fileUri),
+ range: range,
+ color: color,
+ ),
+ );
+ return expectSuccessfulResponseTo(
+ request,
+ _fromJsonList(ColorPresentation.fromJson),
+ );
+ }
+
Future<List<CompletionItem>> getCompletion(Uri uri, Position pos,
{CompletionContext? context}) {
final request = makeRequest(
@@ -1093,6 +1110,19 @@
return expectSuccessfulResponseTo(request, DartDiagnosticServer.fromJson);
}
+ Future<List<ColorInformation>> getDocumentColors(String fileUri) {
+ final request = makeRequest(
+ Method.textDocument_documentColor,
+ DocumentColorParams(
+ textDocument: TextDocumentIdentifier(uri: fileUri),
+ ),
+ );
+ return expectSuccessfulResponseTo(
+ request,
+ _fromJsonList(ColorInformation.fromJson),
+ );
+ }
+
Future<List<DocumentHighlight>?> getDocumentHighlights(
Uri uri, Position pos) {
final request = makeRequest(
diff --git a/pkg/analysis_server/test/lsp/test_all.dart b/pkg/analysis_server/test/lsp/test_all.dart
index 752d3f1..bd22081 100644
--- a/pkg/analysis_server/test/lsp/test_all.dart
+++ b/pkg/analysis_server/test/lsp/test_all.dart
@@ -20,6 +20,7 @@
import 'definition_test.dart' as definition;
import 'diagnostic_test.dart' as diagnostic;
import 'document_changes_test.dart' as document_changes;
+import 'document_color_test.dart' as document_color;
import 'document_highlights_test.dart' as document_highlights;
import 'document_symbols_test.dart' as document_symbols;
import 'file_modification_test.dart' as file_modification;
@@ -63,6 +64,7 @@
document_changes.main();
document_highlights.main();
document_symbols.main();
+ document_color.main();
file_modification.main();
flutter_outline.main();
folding.main();
diff --git a/pkg/analysis_server/tool/lsp_spec/README.md b/pkg/analysis_server/tool/lsp_spec/README.md
index e87c02d..ccf6e8a 100644
--- a/pkg/analysis_server/tool/lsp_spec/README.md
+++ b/pkg/analysis_server/tool/lsp_spec/README.md
@@ -101,8 +101,8 @@
| codeLens/resolve | | | | | |
| textDocument/documentLink | | | | | |
| documentLink/resolve | | | | | |
-| textDocument/documentColor | | | | | |
-| textDocument/colorPresentation | | | | | |
+| textDocument/documentColor | ✅ | ✅ | | ✅ | ✅ |
+| textDocument/colorPresentation | ✅ | ✅ | | ✅ | ✅ |
| textDocument/formatting | ✅ | ✅ | | ✅ | ✅ |
| textDocument/rangeFormatting | ✅ | ✅ | | ✅ | ✅ |
| textDocument/onTypeFormatting | ✅ | ✅ | | ✅ | ✅ |
diff --git a/pkg/analyzer/lib/src/lint/pub.dart b/pkg/analyzer/lib/src/lint/pub.dart
index d68ecdf..86d7c17 100644
--- a/pkg/analyzer/lib/src/lint/pub.dart
+++ b/pkg/analyzer/lib/src/lint/pub.dart
@@ -52,17 +52,27 @@
PSHost? _processHost(
YamlScalar key, YamlNode v, ResourceProvider? resourceProvider) {
- if (v is! YamlMap) {
- return null;
+ if (v is YamlScalar) {
+ // dependencies:
+ // mypkg:
+ // hosted: https://some-pub-server.com
+ // version: ^1.2.3
+ _PSHost host = _PSHost(isShortForm: true);
+ host.token = _PSNode(key, resourceProvider);
+ host.url = _processScalar(key, v, resourceProvider);
+ return host;
}
- YamlMap hostMap = v;
- // name: transmogrify
- // url: http://your-package-server.com
- _PSHost host = _PSHost();
- host.token = _PSNode(key, resourceProvider);
- host.name = _findEntry(hostMap, 'name', resourceProvider);
- host.url = _findEntry(hostMap, 'url', resourceProvider);
- return host;
+ if (v is YamlMap) {
+ YamlMap hostMap = v;
+ // name: transmogrify
+ // url: http://your-package-server.com
+ _PSHost host = _PSHost(isShortForm: false);
+ host.token = _PSNode(key, resourceProvider);
+ host.name = _findEntry(hostMap, 'name', resourceProvider);
+ host.url = _findEntry(hostMap, 'url', resourceProvider);
+ return host;
+ }
+ return null;
}
PSNodeList? _processList(
@@ -86,6 +96,16 @@
_PSNode(key, resourceProvider), _PSNode(value, resourceProvider));
}
+/// Representation of a key/value pair a map from package name to
+/// _package description_.
+///
+/// **Example** of a path-dependency:
+/// ```yaml
+/// dependencies:
+/// <name>:
+/// version: <version>
+/// path: <path>
+/// ```
abstract class PSDependency {
PSGitRepo? get git;
PSHost? get host;
@@ -94,6 +114,8 @@
PSEntry? get version;
}
+/// Representation of the map from package name to _package description_ used
+/// under `dependencies`, `dev_dependencies` and `dependency_overrides`.
abstract class PSDependencyList with IterableMixin<PSDependency> {}
class PSEntry {
@@ -112,6 +134,33 @@
}
abstract class PSHost {
+ /// True, if _short-form_ for writing hosted-dependencies was used.
+ ///
+ /// **Example** of a hosted-dependency written in short-form:
+ /// ```yaml
+ /// dependencies:
+ /// foo:
+ /// hosted: https://some-pub-server.com
+ /// version: ^1.2.3
+ /// ```
+ ///
+ /// The _long-form_ for writing the dependency given above is:
+ /// ```yaml
+ /// dependencies:
+ /// foo:
+ /// hosted:
+ /// url: https://some-pub-server.com
+ /// name: foo
+ /// version: ^1.2.3
+ /// ```
+ ///
+ /// The short-form was added in Dart 2.15.0 because:
+ /// * The `name` property just specifies the package name, which can be
+ /// inferred from the context. So it is unnecessary to write it.
+ /// * The nested object and `url` key becomes unnecessary when the `name`
+ /// property is removed.
+ bool get isShortForm;
+
PSEntry? get name;
PSNode? get token;
PSEntry? get url;
@@ -276,11 +325,19 @@
class _PSHost implements PSHost {
@override
- PSNode? token;
+ bool isShortForm;
+
@override
PSEntry? name;
+
+ @override
+ PSNode? token;
+
@override
PSEntry? url;
+
+ _PSHost({required this.isShortForm});
+
@override
String toString() => '''
$token:
diff --git a/pkg/analyzer/test/src/lint/pub_test.dart b/pkg/analyzer/test/src/lint/pub_test.dart
index 4e71879..6634c41 100644
--- a/pkg/analyzer/test/src/lint/pub_test.dart
+++ b/pkg/analyzer/test/src/lint/pub_test.dart
@@ -27,6 +27,13 @@
name: transmogrify
url: http://your-package-server.com
version: '>=0.4.0 <1.0.0'
+ transmogrify_optional_name:
+ hosted:
+ url: http://your-package-server.com
+ version: '>=0.4.0 <1.0.0'
+ transmogrify_short_form:
+ hosted: http://your-package-server.com
+ version: '>=0.4.0 <1.0.0'
analyzer: '0.24.0-dev.1'
cli_util: '>=0.0.1 <0.1.0'
semver: '>=0.2.0 <0.3.0'
@@ -108,6 +115,26 @@
testValueSpan('name', host.name, startOffset: 243, endOffset: 255);
});
+ group('hosted (optional name)', () {
+ PSDependency dep =
+ findDependency(ps.dependencies, name: 'transmogrify_optional_name');
+ PSHost host = dep.host!;
+ test('name', () => expect(host.name, isNull));
+ testValue('url', host.url, equals('http://your-package-server.com'));
+ testKeySpan('url', host.url, startOffset: 376, endOffset: 379);
+ testValueSpan('url', host.url, startOffset: 381, endOffset: 411);
+ });
+
+ group('hosted (short-form)', () {
+ PSDependency dep =
+ findDependency(ps.dependencies, name: 'transmogrify_short_form');
+ PSHost host = dep.host!;
+ test('name', () => expect(host.name, isNull));
+ testValue('url', host.url, equals('http://your-package-server.com'));
+ testKeySpan('url', host.url, startOffset: 473, endOffset: 479);
+ testValueSpan('url', host.url, startOffset: 481, endOffset: 511);
+ });
+
group('git', () {
PSDependency dep = findDependency(ps.dependencies, name: 'kittens');
PSGitRepo git = dep.git!;
diff --git a/pkg/dart_internal/pubspec.yaml b/pkg/dart_internal/pubspec.yaml
index d871d5c..9d5dacc 100644
--- a/pkg/dart_internal/pubspec.yaml
+++ b/pkg/dart_internal/pubspec.yaml
@@ -1,5 +1,5 @@
name: dart_internal
-version: 0.2.2
+version: 0.2.3
repository: https://github.com/dart-lang/sdk/tree/master/pkg/dart_internal
description: >-
This package is not intended for wide use. It provides a temporary API to
@@ -9,4 +9,4 @@
environment:
# Restrict the upper bound so that we can remove support for this in a later
# version of the SDK without it being a breaking change.
- sdk: ">=2.12.0 <2.16.0"
+ sdk: ">=2.12.0 <2.17.0"
diff --git a/pkg/dev_compiler/lib/src/kernel/compiler.dart b/pkg/dev_compiler/lib/src/kernel/compiler.dart
index ba81acf..131d308 100644
--- a/pkg/dev_compiler/lib/src/kernel/compiler.dart
+++ b/pkg/dev_compiler/lib/src/kernel/compiler.dart
@@ -1124,6 +1124,7 @@
for (var i = 0; i < mixinApplications.length; i++) {
var m = mixinApplications[i];
var mixinClass = m.isAnonymousMixin ? m.mixedInClass : m;
+ _declareBeforeUse(mixinClass);
var mixinType =
_hierarchy.getClassAsInstanceOf(c, mixinClass).asInterfaceType;
var mixinName =
diff --git a/tests/language/mixin/regress_47645_test.dart b/tests/language/mixin/regress_47645_test.dart
new file mode 100644
index 0000000..4482a73
--- /dev/null
+++ b/tests/language/mixin/regress_47645_test.dart
@@ -0,0 +1,19 @@
+// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import "package:expect/expect.dart";
+
+// Regression test for https://github.com/dart-lang/sdk/issues/47645.
+// To reproduce the issue the class declaration must appear before the mixin
+// declaration.
+class C<T> with M<C<T>> {}
+
+mixin M<T> {
+ bool fn() => true;
+}
+
+void main() {
+ var c = C<int>();
+ Expect.isTrue(c.fn());
+}
diff --git a/tests/language_2/mixin/regress_47645_test.dart b/tests/language_2/mixin/regress_47645_test.dart
new file mode 100644
index 0000000..27885e9
--- /dev/null
+++ b/tests/language_2/mixin/regress_47645_test.dart
@@ -0,0 +1,21 @@
+// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// @dart = 2.9
+
+import "package:expect/expect.dart";
+
+// Regression test for https://github.com/dart-lang/sdk/issues/47645.
+// To reproduce the issue the class declaration must appear before the mixin
+// declaration.
+class C<T> with M<C<T>> {}
+
+mixin M<T> {
+ bool fn() => true;
+}
+
+void main() {
+ var c = C<int>();
+ Expect.isTrue(c.fn());
+}
diff --git a/tools/VERSION b/tools/VERSION
index 45b16ab..6a1784b 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
MAJOR 2
MINOR 16
PATCH 0
-PRERELEASE 10
+PRERELEASE 11
PRERELEASE_PATCH 0
\ No newline at end of file