Version 2.17.2
* Cherry-pick 2b0cd3c892cc414ae2d585de3f373bdcad446523 to stable
* Cherry-pick 85615ce2e8004a59c62ea6f552fa25e66f3c11bb to stable
* Cherry-pick 5a99a2e4db5560009aae49f8113d7c15ef145aa5 to stable
diff --git a/CHANGELOG.md b/CHANGELOG.md
index ce4c26d..880028a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,13 @@
+## 2.17.2 - 2022-06-01
+
+This is a patch release that fixes:
+
+- a Dart VM compiler crash (issue [#100375][]).
+- code completion when writing method overrides (issue [#49086][]).
+
+[#100375]: https://github.com/flutter/flutter/issues/100375
+[#49086]: https://github.com/dart-lang/sdk/issues/49086
+
## 2.17.1 - 2022-05-18
This is a patch release that fixes:
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/fuzzy_filter_sort.dart b/pkg/analysis_server/lib/src/services/completion/dart/fuzzy_filter_sort.dart
index 69e49fe..1fbda62 100644
--- a/pkg/analysis_server/lib/src/services/completion/dart/fuzzy_filter_sort.dart
+++ b/pkg/analysis_server/lib/src/services/completion/dart/fuzzy_filter_sort.dart
@@ -17,7 +17,7 @@
var matcher = FuzzyMatcher(pattern, matchStyle: MatchStyle.SYMBOL);
double score(CompletionSuggestionBuilder suggestion) {
- var textToMatch = suggestion.completion;
+ var textToMatch = suggestion.textToMatch;
if (suggestion.kind == CompletionSuggestionKind.KEYWORD ||
suggestion.kind == CompletionSuggestionKind.NAMED_ARGUMENT) {
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 aeb07b2..a784e8a 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
@@ -41,6 +41,9 @@
/// See [CompletionSuggestion.relevance].
int get relevance;
+ /// Return the text that should be matched against the filter.
+ String get textToMatch;
+
CompletionSuggestion build();
}
@@ -82,6 +85,9 @@
}
@override
+ String get textToMatch => completion;
+
+ @override
CompletionSuggestion build() {
return CompletionSuggestion(
kind,
@@ -967,7 +973,10 @@
displayText: displayText);
suggestion.element = protocol.convertElement(element,
withNullability: _isNonNullableByDefault);
- _addSuggestion(suggestion);
+ _addSuggestion(
+ suggestion,
+ textToMatchOverride: element.displayName,
+ );
}
/// Add a suggestion for a [parameter].
@@ -1182,9 +1191,15 @@
/// Add the given [suggestion] if it isn't shadowed by a previously added
/// suggestion.
- void _addSuggestion(protocol.CompletionSuggestion suggestion) {
+ void _addSuggestion(
+ protocol.CompletionSuggestion suggestion, {
+ String? textToMatchOverride,
+ }) {
_addBuilder(
- ValueCompletionSuggestionBuilder(suggestion),
+ ValueCompletionSuggestionBuilder(
+ suggestion,
+ textToMatchOverride: textToMatchOverride,
+ ),
);
}
@@ -1528,8 +1543,12 @@
/// [CompletionSuggestionBuilder] that is based on a [CompletionSuggestion].
class ValueCompletionSuggestionBuilder implements CompletionSuggestionBuilder {
final CompletionSuggestion _suggestion;
+ final String? _textToMatchOverride;
- ValueCompletionSuggestionBuilder(this._suggestion);
+ ValueCompletionSuggestionBuilder(
+ this._suggestion, {
+ String? textToMatchOverride,
+ }) : _textToMatchOverride = textToMatchOverride;
@override
String get completion => _suggestion.completion;
@@ -1544,6 +1563,9 @@
int get relevance => _suggestion.relevance;
@override
+ String get textToMatch => _textToMatchOverride ?? completion;
+
+ @override
CompletionSuggestion build() {
return _suggestion;
}
diff --git a/pkg/analysis_server/test/services/completion/dart/completion_check.dart b/pkg/analysis_server/test/services/completion/dart/completion_check.dart
index a7094e8..ff98794 100644
--- a/pkg/analysis_server/test/services/completion/dart/completion_check.dart
+++ b/pkg/analysis_server/test/services/completion/dart/completion_check.dart
@@ -154,6 +154,14 @@
}
@useResult
+ CheckTarget<String?> get displayText {
+ return nest(
+ value.suggestion.displayText,
+ (selected) => 'has displayText ${valueStr(selected)}',
+ );
+ }
+
+ @useResult
CheckTarget<String?> get docComplete {
return nest(
value.suggestion.docComplete,
@@ -402,6 +410,18 @@
}
@useResult
+ CheckTarget<Iterable<CompletionSuggestionForTesting>> get overrides {
+ var result = value
+ .where((suggestion) =>
+ suggestion.suggestion.kind == CompletionSuggestionKind.OVERRIDE)
+ .toList();
+ return nest(
+ result,
+ (selected) => 'overrides ${valueStr(selected)}',
+ );
+ }
+
+ @useResult
CheckTarget<Iterable<CompletionSuggestionForTesting>> get withElementClass {
return nest(
value.where((e) {
diff --git a/pkg/analysis_server/test/services/completion/dart/location/class_body_test.dart b/pkg/analysis_server/test/services/completion/dart/location/class_body_test.dart
index d473b9f..ca3535f 100644
--- a/pkg/analysis_server/test/services/completion/dart/location/class_body_test.dart
+++ b/pkg/analysis_server/test/services/completion/dart/location/class_body_test.dart
@@ -18,14 +18,14 @@
@reflectiveTest
class ClassBodyTest1 extends AbstractCompletionDriverTest
- with ClassBodyTestCases {
+ with ClassBodyTestCases, OverrideTestCases {
@override
TestingCompletionProtocol get protocol => TestingCompletionProtocol.version1;
}
@reflectiveTest
class ClassBodyTest2 extends AbstractCompletionDriverTest
- with ClassBodyTestCases {
+ with ClassBodyTestCases, OverrideTestCases {
@override
TestingCompletionProtocol get protocol => TestingCompletionProtocol.version2;
}
@@ -311,6 +311,79 @@
}
}
+mixin OverrideTestCases on AbstractCompletionDriverTest {
+ Future<void> test_class_method_fromExtends() async {
+ final response = await getTestCodeSuggestions('''
+class A {
+ void foo01() {}
+}
+
+class B extends A {
+ foo^
+}
+''');
+
+ check(response).suggestions.overrides.includesAll([
+ (suggestion) => suggestion
+ ..displayText.isEqualTo('foo01() { … }')
+ ..hasSelection(offset: 60, length: 14)
+ ..completion.isEqualTo(r'''
+@override
+ void foo01() {
+ // TODO: implement foo01
+ super.foo01();
+ }'''),
+ ]);
+ }
+
+ Future<void> test_class_method_fromImplements() async {
+ final response = await getTestCodeSuggestions('''
+class A {
+ void foo01() {}
+}
+
+class B implements A {
+ foo^
+}
+''');
+
+ check(response).suggestions.overrides.includesAll([
+ (suggestion) => suggestion
+ ..displayText.isEqualTo('foo01() { … }')
+ ..hasSelection(offset: 55)
+ ..completion.isEqualTo(r'''
+@override
+ void foo01() {
+ // TODO: implement foo01
+ }'''),
+ ]);
+ }
+
+ Future<void> test_class_method_fromWith() async {
+ final response = await getTestCodeSuggestions('''
+mixin M {
+ void foo01() {}
+}
+
+class A with M {
+ foo^
+}
+''');
+
+ check(response).suggestions.overrides.includesAll([
+ (suggestion) => suggestion
+ ..displayText.isEqualTo('foo01() { … }')
+ ..hasSelection(offset: 60, length: 14)
+ ..completion.isEqualTo(r'''
+@override
+ void foo01() {
+ // TODO: implement foo01
+ super.foo01();
+ }'''),
+ ]);
+ }
+}
+
class _Context {
final bool isClass;
final bool isMixin;
diff --git a/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart b/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart
index 2322191..c8069cd 100644
--- a/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart
@@ -995,6 +995,15 @@
// We have a contributor that looks at the type, but it is syntactic.
if (parent is VariableDeclaration && parent.name == node) {
+ final parent2 = parent.parent;
+ final parent3 = parent2?.parent;
+ // `class A { foo^ }` looks like `class A { <noType> foo; }`.
+ if (parent2 is VariableDeclarationList &&
+ parent2.type == null &&
+ parent3 is FieldDeclaration &&
+ parent3.semicolon.isSynthetic) {
+ return false;
+ }
return true;
}
}
diff --git a/pkg/analyzer/test/src/dart/analysis/resolve_for_completion_test.dart b/pkg/analyzer/test/src/dart/analysis/resolve_for_completion_test.dart
index e9b7143..7d55077 100644
--- a/pkg/analyzer/test/src/dart/analysis/resolve_for_completion_test.dart
+++ b/pkg/analyzer/test/src/dart/analysis/resolve_for_completion_test.dart
@@ -130,6 +130,22 @@
result.assertResolvedNodes([]);
}
+ test_classDeclaration_body_identifier() async {
+ var result = await _resolveTestCode(r'''
+class A {}
+
+class B {
+ void foo() {}
+
+ bar^
+}
+''');
+
+ result.assertResolvedNodes([
+ 'bar;',
+ ]);
+ }
+
test_constructorDeclaration_body() async {
var result = await _resolveTestCode(r'''
class A {}
diff --git a/runtime/vm/compiler/backend/il.cc b/runtime/vm/compiler/backend/il.cc
index 30f6bec..829f715 100644
--- a/runtime/vm/compiler/backend/il.cc
+++ b/runtime/vm/compiler/backend/il.cc
@@ -2389,21 +2389,21 @@
return left()->definition();
} else if (rhs == 0) {
return right()->definition();
- } else if (rhs == 2) {
- const int64_t shift_1 = 1;
- ConstantInstr* constant_1 =
- flow_graph->GetConstant(Smi::Handle(Smi::New(shift_1)));
+ } else if ((rhs > 0) && Utils::IsPowerOfTwo(rhs)) {
+ const int64_t shift_amount = Utils::ShiftForPowerOfTwo(rhs);
+ ConstantInstr* constant_shift_amount = flow_graph->GetConstant(
+ Smi::Handle(Smi::New(shift_amount)), representation());
BinaryIntegerOpInstr* shift = BinaryIntegerOpInstr::Make(
representation(), Token::kSHL, left()->CopyWithType(),
- new Value(constant_1), GetDeoptId(), can_overflow(),
+ new Value(constant_shift_amount), GetDeoptId(), can_overflow(),
is_truncating(), range(), SpeculativeModeOfInputs());
if (shift != nullptr) {
// Assign a range to the shift factor, just in case range
// analysis no longer runs after this rewriting.
if (auto shift_with_range = shift->AsShiftIntegerOp()) {
shift_with_range->set_shift_range(
- new Range(RangeBoundary::FromConstant(shift_1),
- RangeBoundary::FromConstant(shift_1)));
+ new Range(RangeBoundary::FromConstant(shift_amount),
+ RangeBoundary::FromConstant(shift_amount)));
}
flow_graph->InsertBefore(this, shift, env(), FlowGraph::kValue);
return shift;
diff --git a/tools/VERSION b/tools/VERSION
index c67ab37..419566c 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -26,6 +26,6 @@
CHANNEL stable
MAJOR 2
MINOR 17
-PATCH 1
+PATCH 2
PRERELEASE 0
PRERELEASE_PATCH 0
\ No newline at end of file