Version 2.15.0-297.0.dev
Merge commit 'c224cc2e0d4cd8fc536c21ee963a0254d18a27bb' into 'dev'
diff --git a/DEPS b/DEPS
index df4cc35..8c0e608 100644
--- a/DEPS
+++ b/DEPS
@@ -140,7 +140,7 @@
"pool_rev": "7abe634002a1ba8a0928eded086062f1307ccfae",
"process_rev": "56ece43b53b64c63ae51ec184b76bd5360c28d0b",
"protobuf_rev": "c1eb6cb51af39ccbaa1a8e19349546586a5c8e31",
- "pub_rev": "0035a40f25d027130c0314571da53ffafc6d973b",
+ "pub_rev": "96404e0749864c9fbf8b12e1d424e8078809e00a",
"pub_semver_rev": "a43ad72fb6b7869607581b5fedcb186d1e74276a",
"root_certificates_rev": "692f6d6488af68e0121317a9c2c9eb393eb0ee50",
"rust_revision": "b7856f695d65a8ebc846754f97d15814bcb1c244",
diff --git a/docs/process/breaking-changes.md b/docs/process/breaking-changes.md
index 328c4c6..4ea8bda 100644
--- a/docs/process/breaking-changes.md
+++ b/docs/process/breaking-changes.md
@@ -65,8 +65,6 @@
* Clear steps for mitigating the change.
-[TODO: Link to an issue template for this]
-
* Email Dart Announce (`announce@dartlang.org`):
* Subject: 'Breaking change [bug ID]: [short summary]'
@@ -78,7 +76,7 @@
* A request that developers may leave comments in the linked issue, if this
breaking change poses a severe problem.
-Once you have sent the announce email, please let @devoncarew know in order
+Once you have sent the announce email, please let devoncarew@ know in order
to start the review and approval process.
### Step 2: Approval
@@ -120,7 +118,7 @@
* The impact of the change was significantly larger than described in the
breaking change announcement
-, then they may file a 'request for roll-back' using the following steps:
+Then they may file a 'request for roll-back' using the following steps:
* Create an issue in the Dart SDK issue tracker labelled
`roll-back-request` containing the following:
@@ -133,8 +131,6 @@
* A link to the program that was affected, or another program that illustrated
the same effect.
-[TODO: Link to an issue template for this]
-
Upon receiving such an issue the Dart SDK team will either:
* Roll-back the change, or
diff --git a/pkg/analysis_server/lib/src/cider/completion.dart b/pkg/analysis_server/lib/src/cider/completion.dart
index 17f00da..4f6aaaa3 100644
--- a/pkg/analysis_server/lib/src/cider/completion.dart
+++ b/pkg/analysis_server/lib/src/cider/completion.dart
@@ -70,7 +70,7 @@
var lineInfo = resolvedUnit.lineInfo;
var offset = lineInfo.getOffsetOfLine(line) + column;
- _dartCompletionRequest = DartCompletionRequest(
+ _dartCompletionRequest = DartCompletionRequest.forResolvedUnit(
resolvedUnit: resolvedUnit,
offset: offset,
);
diff --git a/pkg/analysis_server/lib/src/domain_completion.dart b/pkg/analysis_server/lib/src/domain_completion.dart
index 76a3f3b..6bf064f 100644
--- a/pkg/analysis_server/lib/src/domain_completion.dart
+++ b/pkg/analysis_server/lib/src/domain_completion.dart
@@ -329,7 +329,7 @@
);
performanceList.add(completionPerformance);
- var completionRequest = DartCompletionRequest(
+ var completionRequest = DartCompletionRequest.forResolvedUnit(
resolvedUnit: resolvedUnit,
offset: offset,
dartdocDirectiveInfo: server.getDartdocDirectiveInfoFor(
@@ -527,7 +527,7 @@
return;
}
- var completionRequest = DartCompletionRequest(
+ var completionRequest = DartCompletionRequest.forResolvedUnit(
resolvedUnit: resolvedUnit,
offset: offset,
dartdocDirectiveInfo: server.getDartdocDirectiveInfoFor(
diff --git a/pkg/analysis_server/lib/src/domains/execution/completion.dart b/pkg/analysis_server/lib/src/domains/execution/completion.dart
index a2f6ea6..e0eedc4 100644
--- a/pkg/analysis_server/lib/src/domains/execution/completion.dart
+++ b/pkg/analysis_server/lib/src/domains/execution/completion.dart
@@ -69,7 +69,7 @@
return RuntimeCompletionResult([], []);
}
- var dartRequest = DartCompletionRequest(
+ var dartRequest = DartCompletionRequest.forResolvedUnit(
resolvedUnit: targetResult,
offset: targetOffset,
);
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handler_completion.dart b/pkg/analysis_server/lib/src/lsp/handlers/handler_completion.dart
index a4eb456..629101e 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/handler_completion.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_completion.dart
@@ -215,7 +215,7 @@
);
server.performanceStats.completion.add(completionPerformance);
- final completionRequest = DartCompletionRequest(
+ final completionRequest = DartCompletionRequest.forResolvedUnit(
resolvedUnit: unit,
offset: offset,
dartdocDirectiveInfo: server.getDartdocDirectiveInfoFor(unit),
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/completion_manager.dart b/pkg/analysis_server/lib/src/services/completion/dart/completion_manager.dart
index 0789ea2..90da0ef 100644
--- a/pkg/analysis_server/lib/src/services/completion/dart/completion_manager.dart
+++ b/pkg/analysis_server/lib/src/services/completion/dart/completion_manager.dart
@@ -328,17 +328,22 @@
bool _aborted = false;
factory DartCompletionRequest({
- required ResolvedUnitResult resolvedUnit,
+ required AnalysisSession analysisSession,
+ required String filePath,
+ required String fileContent,
+ required CompilationUnitElement unitElement,
+ required AstNode enclosingNode,
required int offset,
DartdocDirectiveInfo? dartdocDirectiveInfo,
CompletionPreference completionPreference = CompletionPreference.insert,
DocumentationCache? documentationCache,
}) {
- var target = CompletionTarget.forOffset(resolvedUnit.unit, offset);
+ var target = CompletionTarget.forOffset(enclosingNode, offset);
+ var libraryElement = unitElement.library;
var featureComputer = FeatureComputer(
- resolvedUnit.typeSystem,
- resolvedUnit.typeProvider,
+ libraryElement.typeSystem,
+ libraryElement.typeProvider,
);
var contextType = featureComputer.computeContextType(
@@ -352,23 +357,43 @@
}
return DartCompletionRequest._(
- analysisSession: resolvedUnit.session as AnalysisSessionImpl,
+ analysisSession: analysisSession as AnalysisSessionImpl,
completionPreference: completionPreference,
- content: resolvedUnit.content,
+ content: fileContent,
contextType: contextType,
dartdocDirectiveInfo: dartdocDirectiveInfo ?? DartdocDirectiveInfo(),
documentationCache: documentationCache,
featureComputer: featureComputer,
- libraryElement: resolvedUnit.libraryElement,
+ libraryElement: libraryElement,
offset: offset,
opType: opType,
- path: resolvedUnit.path,
+ path: filePath,
replacementRange: target.computeReplacementRange(offset),
- source: resolvedUnit.unit.declaredElement!.source,
+ source: unitElement.source,
target: target,
);
}
+ factory DartCompletionRequest.forResolvedUnit({
+ required ResolvedUnitResult resolvedUnit,
+ required int offset,
+ DartdocDirectiveInfo? dartdocDirectiveInfo,
+ CompletionPreference completionPreference = CompletionPreference.insert,
+ DocumentationCache? documentationCache,
+ }) {
+ return DartCompletionRequest(
+ analysisSession: resolvedUnit.session,
+ filePath: resolvedUnit.path,
+ fileContent: resolvedUnit.content,
+ unitElement: resolvedUnit.unit.declaredElement!,
+ enclosingNode: resolvedUnit.unit,
+ offset: offset,
+ dartdocDirectiveInfo: dartdocDirectiveInfo,
+ completionPreference: completionPreference,
+ documentationCache: documentationCache,
+ );
+ }
+
DartCompletionRequest._({
required this.analysisSession,
required this.completionPreference,
diff --git a/pkg/analysis_server/lib/src/services/correction/organize_imports.dart b/pkg/analysis_server/lib/src/services/correction/organize_imports.dart
index 3708b48..e4438f2 100644
--- a/pkg/analysis_server/lib/src/services/correction/organize_imports.dart
+++ b/pkg/analysis_server/lib/src/services/correction/organize_imports.dart
@@ -2,15 +2,20 @@
// 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:math' as math;
+
import 'package:analysis_server/src/utilities/strings.dart';
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/ast/token.dart';
import 'package:analyzer/error/error.dart';
import 'package:analyzer/source/line_info.dart';
+import 'package:analyzer/src/dart/element/extensions.dart';
import 'package:analyzer/src/error/codes.dart';
import 'package:analyzer/src/ignore_comments/ignore_info.dart';
import 'package:analyzer_plugin/protocol/protocol_common.dart'
hide AnalysisError, Element;
+import 'package:collection/collection.dart';
+import 'package:meta/meta_meta.dart';
/// Organizer of imports (and other directives) in the [unit].
class ImportOrganizer {
@@ -67,6 +72,13 @@
var lineInfo = unit.lineInfo ?? LineInfo.fromContent(code);
var hasLibraryDirective = false;
var directives = <_DirectiveInfo>[];
+ // Track the end offset of any library-level comment/annotations that should
+ // remain at the top of the file regardless of whether it was attached to a
+ // directive that's moved/removed.
+ // Code up to this offset will be excluded from the comment/docs/annotation
+ // text for the computed DirectiveInfo and also its range for replacement
+ // in the document.
+ int? libraryDocsAndAnnotationsEndOffset;
for (var directive in unit.directives) {
if (directive is LibraryDirective) {
hasLibraryDirective = true;
@@ -77,15 +89,60 @@
var offset = directive.offset;
var end = directive.end;
- final leadingComment = getLeadingComment(unit, directive, lineInfo);
- final trailingComment =
- getTrailingComment(unit, directive, lineInfo, end);
+ final isPsuedoLibraryDirective = directive == unit.directives.first;
+ Annotation? lastLibraryAnnotation;
+ if (isPsuedoLibraryDirective) {
+ // Find the last library-level annotation that does not come
+ // after any non-library annotation. If there are already
+ // non-library annotations before library annotations, we will not
+ // try to correct those.
+ lastLibraryAnnotation = directive.metadata
+ .takeWhile(_isLibraryTargetAnnotation)
+ .lastOrNull;
+ if (lastLibraryAnnotation != null) {
+ libraryDocsAndAnnotationsEndOffset =
+ lineInfo.getOffsetOfLineAfter(lastLibraryAnnotation.end);
+ // In the case of a blank line after the last library annotation
+ // we should include that in the library part. Otherwise it will
+ // be included in the top of the following directive and may
+ // result in an extra blank line in the annotation block if it
+ // is moved.
+ final nextLineOffset = lineInfo
+ .getOffsetOfLineAfter(libraryDocsAndAnnotationsEndOffset);
+ if (code
+ .substring(libraryDocsAndAnnotationsEndOffset, nextLineOffset)
+ .trim()
+ .isEmpty) {
+ libraryDocsAndAnnotationsEndOffset = nextLineOffset;
+ }
+ }
+ }
+
+ // Usually we look for leading comments on the directive. However if
+ // some library annotations were trimmed off, those comments are part
+ // of that and should not also be included here.
+ final leadingToken =
+ lastLibraryAnnotation == null ? directive.beginToken : null;
+ final leadingComment = leadingToken != null
+ ? getLeadingComment(unit, leadingToken, lineInfo)
+ : null;
+ final trailingComment = getTrailingComment(unit, directive, lineInfo);
+
+ /// Computes the offset to use for the start of directive-specific
+ /// code below taking into account code already included by
+ /// [libraryDocsAndAnnotationsEndOffset].
+ final clampedOffset = libraryDocsAndAnnotationsEndOffset == null
+ ? (int offset) => offset
+ : (int offset) =>
+ math.max(libraryDocsAndAnnotationsEndOffset!, offset);
String? leadingCommentText;
- if (leadingComment != null) {
- leadingCommentText =
- code.substring(leadingComment.offset, directive.offset);
- offset = leadingComment.offset;
+ if (leadingComment != null && leadingToken != null) {
+ offset = clampedOffset(leadingComment.offset);
+ leadingCommentText = code.substring(
+ offset,
+ clampedOffset(leadingToken.offset),
+ );
}
String? trailingCommentText;
if (trailingComment != null) {
@@ -97,13 +154,33 @@
var documentationComment = directive.documentationComment;
if (documentationComment != null) {
documentationText = code.substring(
- documentationComment.offset, documentationComment.end);
+ clampedOffset(documentationComment.offset),
+ clampedOffset(documentationComment.end),
+ );
}
String? annotationText;
+ String? postAnnotationCommentText;
var beginToken = directive.metadata.beginToken;
var endToken = directive.metadata.endToken;
if (beginToken != null && endToken != null) {
- annotationText = code.substring(beginToken.offset, endToken.end);
+ var annotationOffset = clampedOffset(beginToken.offset);
+ var annotationEnd = clampedOffset(endToken.end);
+ if (annotationOffset != annotationEnd) {
+ annotationText = code.substring(
+ annotationOffset,
+ annotationEnd,
+ );
+ }
+ // Capture text between the end of the annotation and the directive
+ // text as there may be end-of line or line comments between.
+ // If not, this will capture the newline between the two, as it
+ // cannot be assumed there is a newline after annotationText because
+ // of the possibility of comments.
+ if (annotationEnd <
+ directive.firstTokenAfterCommentAndMetadata.offset) {
+ postAnnotationCommentText = code.substring(annotationEnd,
+ directive.firstTokenAfterCommentAndMetadata.offset);
+ }
}
var text = code.substring(
directive.firstTokenAfterCommentAndMetadata.offset,
@@ -116,9 +193,12 @@
leadingCommentText,
documentationText,
annotationText,
+ postAnnotationCommentText,
uriContent,
trailingCommentText,
- offset,
+ isPsuedoLibraryDirective
+ ? (libraryDocsAndAnnotationsEndOffset ?? offset)
+ : offset,
end,
text,
),
@@ -151,7 +231,7 @@
sb.write(libraryDocumentationDirective.documentationText);
sb.write(endOfLine);
}
- var currentPriority = directives.first.priority;
+ _DirectivePriority? currentPriority;
for (var directiveInfo in directives) {
if (!hasUnresolvedIdentifierError) {
var directive = directiveInfo.directive;
@@ -160,7 +240,9 @@
}
}
if (currentPriority != directiveInfo.priority) {
- sb.write(endOfLine);
+ if (currentPriority != null) {
+ sb.write(endOfLine);
+ }
currentPriority = directiveInfo.priority;
}
if (directiveInfo.leadingCommentText != null) {
@@ -173,7 +255,9 @@
}
if (directiveInfo.annotationText != null) {
sb.write(directiveInfo.annotationText);
- sb.write(endOfLine);
+ }
+ if (directiveInfo.postAnnotationCommentText != null) {
+ sb.write(directiveInfo.postAnnotationCommentText);
}
sb.write(directiveInfo.text);
if (directiveInfo.trailingCommentText != null) {
@@ -230,7 +314,7 @@
}
/// Gets the first comment token considered to be the leading comment for this
- /// directive.
+ /// token.
///
/// Leading comments for the first directive in a file are considered library
/// comments and not returned unless they contain blank lines, in which case
@@ -239,12 +323,12 @@
/// '// ignore:' comment which should always be treated as attached to the
/// import.
static Token? getLeadingComment(
- CompilationUnit unit, UriBasedDirective directive, LineInfo lineInfo) {
- if (directive.beginToken.precedingComments == null) {
+ CompilationUnit unit, Token beginToken, LineInfo lineInfo) {
+ if (beginToken.precedingComments == null) {
return null;
}
- Token? firstComment = directive.beginToken.precedingComments;
+ Token? firstComment = beginToken.precedingComments;
var comment = firstComment;
var nextComment = comment?.next;
// Don't connect comments that have a blank line between them
@@ -274,7 +358,7 @@
// Skip over any comments on the same line as the previous directive
// as they will be attached to the end of it.
var previousDirectiveLine =
- lineInfo.getLocation(directive.beginToken.previous!.end).lineNumber;
+ lineInfo.getLocation(beginToken.previous!.end).lineNumber;
comment = firstComment;
while (comment != null &&
previousDirectiveLine ==
@@ -289,9 +373,9 @@
///
/// To be considered a trailing comment, the comment must be on the same line
/// as the directive.
- static Token? getTrailingComment(CompilationUnit unit,
- UriBasedDirective directive, LineInfo lineInfo, int end) {
- var line = lineInfo.getLocation(end).lineNumber;
+ static Token? getTrailingComment(
+ CompilationUnit unit, UriBasedDirective directive, LineInfo lineInfo) {
+ var line = lineInfo.getLocation(directive.end).lineNumber;
Token? comment = directive.endToken.next!.precedingComments;
while (comment != null) {
if (lineInfo.getLocation(comment.offset).lineNumber == line) {
@@ -306,6 +390,10 @@
/// '// ignore_for_file:' comment).
static bool _isIgnoreComment(Token token) =>
IgnoreInfo.IGNORE_MATCHER.matchAsPrefix(token.lexeme) != null;
+
+ static bool _isLibraryTargetAnnotation(Annotation annotation) =>
+ annotation.elementAnnotation?.targetKinds.contains(TargetKind.library) ??
+ false;
}
class _DirectiveInfo implements Comparable<_DirectiveInfo> {
@@ -314,6 +402,7 @@
final String? leadingCommentText;
final String? documentationText;
final String? annotationText;
+ final String? postAnnotationCommentText;
final String uri;
final String? trailingCommentText;
@@ -332,6 +421,7 @@
this.leadingCommentText,
this.documentationText,
this.annotationText,
+ this.postAnnotationCommentText,
this.uri,
this.trailingCommentText,
this.offset,
diff --git a/pkg/analysis_server/test/mock_packages/meta/lib/meta_meta.dart b/pkg/analysis_server/test/mock_packages/meta/lib/meta_meta.dart
new file mode 100644
index 0000000..18902be
--- /dev/null
+++ b/pkg/analysis_server/test/mock_packages/meta/lib/meta_meta.dart
@@ -0,0 +1,123 @@
+// Copyright (c) 2020, 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.
+
+/// Annotations that describe the intended use of other annotations.
+library meta_meta;
+
+/// An annotation used on classes that are intended to be used as annotations
+/// to indicate the kinds of declarations and directives for which the
+/// annotation is appropriate.
+///
+/// The kinds are represented by the constants defined in [TargetKind].
+///
+/// Tools, such as the analyzer, can provide feedback if
+///
+/// * the annotation is associated with anything other than a class, where the
+/// class must be usable as an annotation (that is, contain at least one
+/// `const` constructor).
+/// * the annotated annotation is associated with anything other than the kinds
+/// of declarations listed as valid targets.
+@Target({TargetKind.classType})
+class Target {
+ /// The kinds of declarations with which the annotated annotation can be
+ /// associated.
+ final Set<TargetKind> kinds;
+
+ const Target(this.kinds);
+}
+
+/// An enumeration of the kinds of targets to which an annotation can be
+/// applied.
+enum TargetKind {
+ /// Indicates that an annotation is valid on any class declaration.
+ classType,
+
+ /// Indicates that an annotation is valid on any enum declaration.
+ enumType,
+
+ /// Indicates that an annotation is valid on any extension declaration.
+ extension,
+
+ /// Indicates that an annotation is valid on any field declaration, both
+ /// instance and static fields, whether it's in a class, mixin or extension.
+ field,
+
+ /// Indicates that an annotation is valid on any top-level function
+ /// declaration.
+ function,
+
+ /// Indicates that an annotation is valid on the first directive in a library,
+ /// whether that's a `library`, `import`, `export` or `part` directive. This
+ /// doesn't include the `part of` directive in a part file.
+ library,
+
+ /// Indicates that an annotation is valid on any getter declaration, both
+ /// instance or static getters, whether it's in a class, mixin, extension, or
+ /// at the top-level of a library.
+ getter,
+
+ /// Indicates that an annotation is valid on any method declaration, both
+ /// instance and static methods, whether it's in a class, mixin or extension.
+ method,
+
+ /// Indicates that an annotation is valid on any mixin declaration.
+ mixinType,
+
+ /// Indicates that an annotation is valid on any formal parameter declaration,
+ /// whether it's in a function, method, constructor, or closure.
+ parameter,
+
+ /// Indicates that an annotation is valid on any setter declaration, both
+ /// instance or static setters, whether it's in a class, mixin, extension, or
+ /// at the top-level of a library.
+ setter,
+
+ /// Indicates that an annotation is valid on any top-level variable
+ /// declaration.
+ topLevelVariable,
+
+ /// Indicates that an annotation is valid on any declaration that introduces a
+ /// type. This includes classes, enums, mixins and typedefs, but does not
+ /// include extensions because extensions don't introduce a type.
+ type,
+
+ /// Indicates that an annotation is valid on any typedef declaration.
+ typedefType,
+}
+
+extension TargetKindExtension on TargetKind {
+ /// Return a user visible string used to describe this target kind.
+ String get displayString {
+ switch (this) {
+ case TargetKind.classType:
+ return 'classes';
+ case TargetKind.enumType:
+ return 'enums';
+ case TargetKind.extension:
+ return 'extensions';
+ case TargetKind.field:
+ return 'fields';
+ case TargetKind.function:
+ return 'top-level functions';
+ case TargetKind.library:
+ return 'libraries';
+ case TargetKind.getter:
+ return 'getters';
+ case TargetKind.method:
+ return 'methods';
+ case TargetKind.mixinType:
+ return 'mixins';
+ case TargetKind.parameter:
+ return 'parameters';
+ case TargetKind.setter:
+ return 'setters';
+ case TargetKind.topLevelVariable:
+ return 'top-level variables';
+ case TargetKind.type:
+ return 'types (classes, enums, mixins, or typedefs)';
+ case TargetKind.typedefType:
+ return 'typedefs';
+ }
+ }
+}
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 e5d3720..83312b9 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
@@ -531,7 +531,7 @@
result = await session.getResolvedUnit(testFile) as ResolvedUnitResult;
// Build the request
- var request = DartCompletionRequest(
+ var request = DartCompletionRequest.forResolvedUnit(
resolvedUnit: result,
offset: completionOffset,
dartdocDirectiveInfo: dartdocInfo,
diff --git a/pkg/analysis_server/test/services/completion/dart/completion_manager_test.dart b/pkg/analysis_server/test/services/completion/dart/completion_manager_test.dart
index c60efba..be920e7 100644
--- a/pkg/analysis_server/test/services/completion/dart/completion_manager_test.dart
+++ b/pkg/analysis_server/test/services/completion/dart/completion_manager_test.dart
@@ -52,7 +52,7 @@
// Build the request
var resolvedUnit =
await session.getResolvedUnit(testFile) as ResolvedUnitResult;
- request = DartCompletionRequest(
+ request = DartCompletionRequest.forResolvedUnit(
resolvedUnit: resolvedUnit,
offset: completionOffset,
);
diff --git a/pkg/analysis_server/test/services/correction/organize_directives_test.dart b/pkg/analysis_server/test/services/correction/organize_directives_test.dart
index f28613f..160c31d 100644
--- a/pkg/analysis_server/test/services/correction/organize_directives_test.dart
+++ b/pkg/analysis_server/test/services/correction/organize_directives_test.dart
@@ -22,6 +22,12 @@
class OrganizeDirectivesTest extends AbstractSingleUnitTest {
late List<AnalysisError> testErrors;
+ @override
+ void setUp() {
+ super.setUp();
+ writeTestPackageConfig(meta: true);
+ }
+
Future<void> test_docComment_beforeDirective_hasUnresolvedIdentifier() async {
await _computeUnitAndErrors(r'''
/// Library documentation comment A
@@ -682,6 +688,219 @@
import 'package:b/a.dart';''');
}
+ Future<void> test_sort_libraryAnnotation_movedDirective() async {
+ await _addAnnotationsFile();
+ await _computeUnitAndErrors(r'''
+@libraryAnnotation
+@LibraryAnnotation()
+
+// annotations
+import 'annotations.dart';
+
+// io
+import 'dart:io';
+''');
+ // Validate annotation is not moved with import.
+ _assertOrganize(r'''
+@libraryAnnotation
+@LibraryAnnotation()
+
+// io
+import 'dart:io';
+
+// annotations
+import 'annotations.dart';
+''');
+ }
+
+ Future<void> test_sort_libraryAnnotation_removedDirective() async {
+ await _addAnnotationsFile();
+ await _computeUnitAndErrors(r'''
+@libraryAnnotation
+@LibraryAnnotation()
+
+// io
+import 'dart:io'; // unused
+// annotations
+import 'annotations.dart'; // used
+''');
+ // Validate annotation is not removed with import.
+ _assertOrganize(r'''
+@libraryAnnotation
+@LibraryAnnotation()
+
+// annotations
+import 'annotations.dart'; // used
+''', removeUnused: true);
+ }
+
+ Future<void> test_sort_multipleAnnotation_movedDirective() async {
+ await _addAnnotationsFile();
+ await _computeUnitAndErrors(r'''
+@libraryAnnotation
+@LibraryAnnotation()
+
+@nonLibraryAnnotation
+import 'annotations.dart';
+@nonLibraryAnnotation
+import 'dart:io';
+''');
+ // Validate only the non-library annotation is moved with import.
+ _assertOrganize(r'''
+@libraryAnnotation
+@LibraryAnnotation()
+
+@nonLibraryAnnotation
+import 'dart:io';
+
+@nonLibraryAnnotation
+import 'annotations.dart';
+''');
+ }
+
+ Future<void> test_sort_multipleAnnotation_removedDirective() async {
+ await _addAnnotationsFile();
+ await _computeUnitAndErrors(r'''
+@libraryAnnotation
+@LibraryAnnotation()
+
+@nonLibraryAnnotation
+import 'dart:io';
+@nonLibraryAnnotation
+import 'annotations.dart';
+''');
+ // Validate only the non-library annotation is removed with import.
+ _assertOrganize(r'''
+@libraryAnnotation
+@LibraryAnnotation()
+
+@nonLibraryAnnotation
+import 'annotations.dart';
+''', removeUnused: true);
+ }
+
+ Future<void> test_sort_multipleAnnotationWithComments_movedDirective() async {
+ await _addAnnotationsFile();
+ await _computeUnitAndErrors(r'''
+// lib1
+@libraryAnnotation // lib1
+// lib2
+@LibraryAnnotation() // lib2
+
+// nonLib on annotations import
+@nonLibraryAnnotation // nonLib on annotations import
+// annotations import
+import 'annotations.dart'; // annotations import
+// nonLib on io import
+@nonLibraryAnnotation // nonLib on io import
+// io import
+import 'dart:io'; // io import
+''');
+ // Validate only the non-library annotation is moved with import.
+ _assertOrganize(r'''
+// lib1
+@libraryAnnotation // lib1
+// lib2
+@LibraryAnnotation() // lib2
+
+// nonLib on io import
+@nonLibraryAnnotation // nonLib on io import
+// io import
+import 'dart:io'; // io import
+
+// nonLib on annotations import
+@nonLibraryAnnotation // nonLib on annotations import
+// annotations import
+import 'annotations.dart'; // annotations import
+''');
+ }
+
+ Future<void>
+ test_sort_multipleAnnotationWithComments_removedDirective() async {
+ await _addAnnotationsFile();
+ await _computeUnitAndErrors(r'''
+// lib1
+@libraryAnnotation // lib1
+// lib2
+@LibraryAnnotation() // lib2
+
+// nonLib on io import
+@nonLibraryAnnotation // nonLib on io import
+// io import
+import 'dart:io'; // io import
+// nonLib on annotations import
+@nonLibraryAnnotation // nonLib on annotations import
+// annotations import
+import 'annotations.dart'; // annotations import
+''');
+ // Validate only the non-library annotation is removed with import.
+ _assertOrganize(r'''
+// lib1
+@libraryAnnotation // lib1
+// lib2
+@LibraryAnnotation() // lib2
+
+// nonLib on annotations import
+@nonLibraryAnnotation // nonLib on annotations import
+// annotations import
+import 'annotations.dart'; // annotations import
+''', removeUnused: true);
+ }
+
+ Future<void> test_sort_nonLibraryAnnotation_movedDirective() async {
+ await _addAnnotationsFile();
+ await _computeUnitAndErrors(r'''
+@nonLibraryAnnotation
+import 'annotations.dart';
+
+import 'dart:io';
+''');
+ // Validate annotation is moved with import.
+ _assertOrganize(r'''
+import 'dart:io';
+
+@nonLibraryAnnotation
+import 'annotations.dart';
+''');
+ }
+
+ Future<void> test_sort_nonLibraryAnnotation_removedDirective() async {
+ await _addAnnotationsFile();
+ await _computeUnitAndErrors(r'''
+@nonLibraryAnnotation
+// io
+import 'dart:io'; // unused
+// annotations
+import 'annotations.dart'; // used
+''');
+ // Validate annotation is removed with import.
+ _assertOrganize(r'''
+// annotations
+import 'annotations.dart'; // used
+''', removeUnused: true);
+ }
+
+ Future<void> _addAnnotationsFile() async {
+ final annotationsFile = convertPath('/home/test/lib/annotations.dart');
+ const annotationsContent = '''
+import 'package:meta/meta_meta.dart';
+
+const libraryAnnotation = LibraryAnnotation();
+const nonLibraryAnnotation = NonLibraryAnnotation();
+
+@Target({TargetKind.library})
+class LibraryAnnotation {
+ const LibraryAnnotation();
+}
+
+@Target({TargetKind.classType})
+class NonLibraryAnnotation {
+ const NonLibraryAnnotation();
+}
+ ''';
+ addSource(annotationsFile, annotationsContent);
+ }
+
void _assertOrganize(String expectedCode, {bool removeUnused = false}) {
var organizer = ImportOrganizer(testCode, testUnit, testErrors,
removeUnused: removeUnused);
diff --git a/pkg/analysis_server/test/src/services/completion/dart/suggestion_builder_test.dart b/pkg/analysis_server/test/src/services/completion/dart/suggestion_builder_test.dart
index 8c8bc43..9f66816 100644
--- a/pkg/analysis_server/test/src/services/completion/dart/suggestion_builder_test.dart
+++ b/pkg/analysis_server/test/src/services/completion/dart/suggestion_builder_test.dart
@@ -27,7 +27,7 @@
}
Future<CompletionSuggestion> forTopLevelFunction(String functionName) async {
- var request = DartCompletionRequest(
+ var request = DartCompletionRequest.forResolvedUnit(
resolvedUnit: testAnalysisResult,
offset: 0,
);
diff --git a/pkg/analysis_server/test/stress/completion/completion_runner.dart b/pkg/analysis_server/test/stress/completion/completion_runner.dart
index e0754be..2f7a688 100644
--- a/pkg/analysis_server/test/stress/completion/completion_runner.dart
+++ b/pkg/analysis_server/test/stress/completion/completion_runner.dart
@@ -99,7 +99,7 @@
}
timer.start();
- var dartRequest = DartCompletionRequest(
+ var dartRequest = DartCompletionRequest.forResolvedUnit(
resolvedUnit: result,
offset: offset,
);
diff --git a/pkg/analysis_server/tool/code_completion/completion_metrics.dart b/pkg/analysis_server/tool/code_completion/completion_metrics.dart
index b57a1f8..a6a9b85 100644
--- a/pkg/analysis_server/tool/code_completion/completion_metrics.dart
+++ b/pkg/analysis_server/tool/code_completion/completion_metrics.dart
@@ -1368,7 +1368,7 @@
{required MetricsSuggestionListener listener,
required CompletionMetrics metrics}) async {
var stopwatch = Stopwatch()..start();
- var request = DartCompletionRequest(
+ var request = DartCompletionRequest.forResolvedUnit(
resolvedUnit: resolvedUnitResult,
offset: expectedCompletion.offset,
documentationCache: documentationCache,
diff --git a/pkg/analysis_server/tool/lsp_spec/README.md b/pkg/analysis_server/tool/lsp_spec/README.md
index cc61b66..e87c02d 100644
--- a/pkg/analysis_server/tool/lsp_spec/README.md
+++ b/pkg/analysis_server/tool/lsp_spec/README.md
@@ -9,10 +9,10 @@
## Running the Server
-The analysis server snapshot is included in the `bin/snapshots` folder of the Dart SDK. Pass the `--lsp` flag to start the server in LSP mode and the `--client-id` and `--client-version` flags to identify your editor/plugin and version:
+Start the language server using the `dart language-server` command. Pass the `--client-id` and `--client-version` flags to identify your editor/plugin and version:
```
-dart bin/snapshots/analysis_server.dart.snapshot --lsp --client-id my-editor.my-plugin --client-version 1.2
+dart language-server --client-id my-editor.my-plugin --client-version 1.2
```
Note: In LSP the client makes the first request so there is no obvious confirmation that the server is working correctly until the client sends an `initialize` request. Unlike standard JSON RPC, [LSP requires that headers are sent](https://microsoft.github.io/language-server-protocol/specification).
diff --git a/pkg/analyzer/lib/src/dart/element/extensions.dart b/pkg/analyzer/lib/src/dart/element/extensions.dart
index 6eaa94a..7037407 100644
--- a/pkg/analyzer/lib/src/dart/element/extensions.dart
+++ b/pkg/analyzer/lib/src/dart/element/extensions.dart
@@ -6,6 +6,58 @@
import 'package:analyzer/dart/element/type.dart';
import 'package:analyzer/src/dart/element/element.dart';
import 'package:analyzer/src/generated/utilities_dart.dart';
+import 'package:meta/meta_meta.dart';
+
+extension ElementAnnotationExtensions on ElementAnnotation {
+ static final Map<String, TargetKind> _targetKindsByName = {
+ for (final kind in TargetKind.values) kind.toString(): kind,
+ };
+
+ /// Return the target kinds defined for this [ElementAnnotation].
+ Set<TargetKind> get targetKinds {
+ final element = this.element;
+ ClassElement? classElement;
+ if (element is PropertyAccessorElement) {
+ if (element.isGetter) {
+ var type = element.returnType;
+ if (type is InterfaceType) {
+ classElement = type.element;
+ }
+ }
+ } else if (element is ConstructorElement) {
+ classElement = element.enclosingElement;
+ }
+ if (classElement == null) {
+ return const <TargetKind>{};
+ }
+ for (var annotation in classElement.metadata) {
+ if (annotation.isTarget) {
+ var value = annotation.computeConstantValue()!;
+ var kinds = <TargetKind>{};
+
+ for (var kindObject in value.getField('kinds')!.toSetValue()!) {
+ // We can't directly translate the index from the analyzed TargetKind
+ // constant to TargetKinds.values because the analyzer from the SDK
+ // may have been compiled with a different version of pkg:meta.
+ var index = kindObject.getField('index')!.toIntValue()!;
+ var targetKindClass =
+ (kindObject.type as InterfaceType).element as EnumElementImpl;
+ // Instead, map constants to their TargetKind by comparing getter
+ // names.
+ var getter = targetKindClass.constants[index];
+ var name = 'TargetKind.${getter.name}';
+
+ var foundTargetKind = _targetKindsByName[name];
+ if (foundTargetKind != null) {
+ kinds.add(foundTargetKind);
+ }
+ }
+ return kinds;
+ }
+ }
+ return const <TargetKind>{};
+ }
+}
extension ElementExtension on Element {
/// Return `true` if this element is an instance member of a class or mixin.
diff --git a/pkg/analyzer/lib/src/error/best_practices_verifier.dart b/pkg/analyzer/lib/src/error/best_practices_verifier.dart
index 2e4ffc1..5468b6a 100644
--- a/pkg/analyzer/lib/src/error/best_practices_verifier.dart
+++ b/pkg/analyzer/lib/src/error/best_practices_verifier.dart
@@ -44,10 +44,6 @@
class BestPracticesVerifier extends RecursiveAstVisitor<void> {
static const String _TO_INT_METHOD_NAME = "toInt";
- static final Map<String, TargetKind> _targetKindsByName = {
- for (final kind in TargetKind.values) kind.toString(): kind,
- };
-
/// The class containing the AST nodes being visited, or `null` if we are not
/// in the scope of a class.
ClassElementImpl? _enclosingClass;
@@ -305,7 +301,7 @@
}
}
- var kinds = _targetKindsFor(element);
+ var kinds = element.targetKinds;
if (kinds.isNotEmpty) {
if (!_isValidTarget(parent, kinds)) {
var invokedElement = element.element!;
@@ -1717,51 +1713,6 @@
return false;
}
- /// Return the target kinds defined for the given [annotation].
- Set<TargetKind> _targetKindsFor(ElementAnnotation annotation) {
- var element = annotation.element;
- ClassElement? classElement;
- if (element is PropertyAccessorElement) {
- if (element.isGetter) {
- var type = element.returnType;
- if (type is InterfaceType) {
- classElement = type.element;
- }
- }
- } else if (element is ConstructorElement) {
- classElement = element.enclosingElement;
- }
- if (classElement == null) {
- return const <TargetKind>{};
- }
- for (var annotation in classElement.metadata) {
- if (annotation.isTarget) {
- var value = annotation.computeConstantValue()!;
- var kinds = <TargetKind>{};
-
- for (var kindObject in value.getField('kinds')!.toSetValue()!) {
- // We can't directly translate the index from the analyzed TargetKind
- // constant to TargetKinds.values because the analyzer from the SDK
- // may have been compiled with a different version of pkg:meta.
- var index = kindObject.getField('index')!.toIntValue()!;
- var targetKindClass =
- (kindObject.type as InterfaceType).element as EnumElementImpl;
- // Instead, map constants to their TargetKind by comparing getter
- // names.
- var getter = targetKindClass.constants[index];
- var name = 'TargetKind.${getter.name}';
-
- var foundTargetKind = _targetKindsByName[name];
- if (foundTargetKind != null) {
- kinds.add(foundTargetKind);
- }
- }
- return kinds;
- }
- }
- return const <TargetKind>{};
- }
-
/// Checks for the passed as expression for the [HintCode.UNNECESSARY_CAST]
/// hint code.
///
diff --git a/sdk/lib/convert/latin1.dart b/sdk/lib/convert/latin1.dart
index f396682..76b2dbd 100644
--- a/sdk/lib/convert/latin1.dart
+++ b/sdk/lib/convert/latin1.dart
@@ -63,12 +63,48 @@
}
/// This class converts strings of only ISO Latin-1 characters to bytes.
+///
+/// Example:
+/// ```dart
+/// final latin1Encoder = latin1.encoder;
+///
+/// const sample = 'àáâãäå';
+/// final encoded = latin1Encoder.convert(sample);
+/// print(encoded); // [224, 225, 226, 227, 228, 229]
+/// ```
class Latin1Encoder extends _UnicodeSubsetEncoder {
const Latin1Encoder() : super(_latin1Mask);
}
/// This class converts Latin-1 bytes (lists of unsigned 8-bit integers)
/// to a string.
+///
+/// Example:
+/// ```dart
+/// final latin1Decoder = latin1.decoder;
+///
+/// const encodedBytes = [224, 225, 226, 227, 228, 229];
+/// final decoded = latin1Decoder.convert(encodedBytes);
+/// print(decoded); // àáâãäå
+///
+/// // Hexadecimal values as source
+/// const hexBytes = [0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5];
+/// final decodedHexBytes = latin1Decoder.convert(hexBytes);
+/// print(decodedHexBytes); // àáâãäå
+/// ```
+/// Throws a [FormatException] if the encoded input contains values that are
+/// not in the range 0 .. 255 and [allowInvalid] is false ( the default ).
+///
+/// If [allowInvalid] is true, invalid bytes are converted
+/// to Unicode Replacement character U+FFFD (�).
+///
+/// Example with `allowInvalid` set to true:
+/// ```dart
+/// const latin1Decoder = Latin1Decoder(allowInvalid: true);
+/// const encodedBytes = [300];
+/// final decoded = latin1Decoder.convert(encodedBytes);
+/// print(decoded); // �
+/// ```
class Latin1Decoder extends _UnicodeSubsetDecoder {
/// Instantiates a new [Latin1Decoder].
///
diff --git a/tools/VERSION b/tools/VERSION
index 7b162d3..786d33b 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
MAJOR 2
MINOR 15
PATCH 0
-PRERELEASE 296
+PRERELEASE 297
PRERELEASE_PATCH 0
\ No newline at end of file