Skip to content

Commit 56bde6d

Browse files
committed
Issue 27542. Guard against CompilationUnitElement is null in SearchMatch.element getter.
[email protected] BUG= #27542 Review URL: https://codereview.chromium.org/2409403002 .
1 parent 9b2c5ce commit 56bde6d

File tree

4 files changed

+60
-45
lines changed

4 files changed

+60
-45
lines changed

pkg/analysis_server/lib/src/search/element_references.dart

Lines changed: 21 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ library search.element_references;
66

77
import 'dart:async';
88

9-
import 'package:analysis_server/src/collections.dart';
109
import 'package:analysis_server/src/protocol_server.dart'
1110
show SearchResult, newSearchResult_fromMatch;
1211
import 'package:analysis_server/src/services/search/hierarchy.dart';
@@ -25,40 +24,44 @@ class ElementReferencesComputer {
2524
/**
2625
* Computes [SearchResult]s for [element] references.
2726
*/
28-
Future<List<SearchResult>> compute(Element element, bool withPotential) {
29-
var futureGroup = new _ConcatFutureGroup<SearchResult>();
30-
// find element references
31-
futureGroup.add(_findElementsReferences(element));
32-
// add potential references
27+
Future<List<SearchResult>> compute(
28+
Element element, bool withPotential) async {
29+
List<SearchResult> results = <SearchResult>[];
30+
31+
// Add element references.
32+
results.addAll(await _findElementsReferences(element));
33+
34+
// Add potential references.
3335
if (withPotential && _isMemberElement(element)) {
3436
String name = element.displayName;
35-
var matchesFuture = searchEngine.searchMemberReferences(name);
36-
var resultsFuture = matchesFuture.then((List<SearchMatch> matches) {
37-
return matches.where((match) => !match.isResolved).map(toResult);
38-
});
39-
futureGroup.add(resultsFuture);
37+
List<SearchMatch> matches =
38+
await searchEngine.searchMemberReferences(name);
39+
matches = SearchMatch.withNotNullElement(matches);
40+
results.addAll(matches.where((match) => !match.isResolved).map(toResult));
4041
}
41-
// merge results
42-
return futureGroup.future;
42+
43+
return results;
4344
}
4445

4546
/**
4647
* Returns a [Future] completing with a [List] of references to [element] or
4748
* to the corresponding hierarchy [Element]s.
4849
*/
4950
Future<List<SearchResult>> _findElementsReferences(Element element) async {
51+
List<SearchResult> allResults = <SearchResult>[];
5052
Iterable<Element> refElements = await _getRefElements(element);
51-
var futureGroup = new _ConcatFutureGroup<SearchResult>();
5253
for (Element refElement in refElements) {
5354
// add declaration
5455
if (_isDeclarationInteresting(refElement)) {
5556
SearchResult searchResult = _newDeclarationResult(refElement);
56-
futureGroup.add(searchResult);
57+
allResults.add(searchResult);
5758
}
5859
// do search
59-
futureGroup.add(_findSingleElementReferences(refElement));
60+
List<SearchResult> elementResults =
61+
await _findSingleElementReferences(refElement);
62+
allResults.addAll(elementResults);
6063
}
61-
return futureGroup.future;
64+
return allResults;
6265
}
6366

6467
/**
@@ -67,6 +70,7 @@ class ElementReferencesComputer {
6770
Future<List<SearchResult>> _findSingleElementReferences(
6871
Element element) async {
6972
List<SearchMatch> matches = await searchEngine.searchReferences(element);
73+
matches = SearchMatch.withNotNullElement(matches);
7074
return matches.map(toResult).toList();
7175
}
7276

@@ -129,26 +133,3 @@ class ElementReferencesComputer {
129133
return element.enclosingElement is ClassElement;
130134
}
131135
}
132-
133-
/**
134-
* A collection of [Future]s that concats [List] results of added [Future]s into
135-
* a single [List].
136-
*/
137-
class _ConcatFutureGroup<E> {
138-
final List<Future<List<E>>> _futures = <Future<List<E>>>[];
139-
140-
Future<List<E>> get future {
141-
return Future.wait(_futures).then(concatToList);
142-
}
143-
144-
/**
145-
* Adds a [Future] or an [E] value to results.
146-
*/
147-
void add(value) {
148-
if (value is Future) {
149-
_futures.add(value as Future<List<E>>);
150-
} else {
151-
_futures.add(new Future.value(<E>[value as E]));
152-
}
153-
}
154-
}

pkg/analysis_server/lib/src/search/search_domain.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ class SearchDomainHandler implements protocol.RequestHandler {
9898
// search
9999
List<SearchMatch> matches =
100100
await searchEngine.searchMemberDeclarations(params.name);
101+
matches = SearchMatch.withNotNullElement(matches);
101102
_sendSearchNotification(searchId, true, matches.map(toResult));
102103
}
103104

@@ -112,6 +113,7 @@ class SearchDomainHandler implements protocol.RequestHandler {
112113
// search
113114
List<SearchMatch> matches =
114115
await searchEngine.searchMemberReferences(params.name);
116+
matches = SearchMatch.withNotNullElement(matches);
115117
_sendSearchNotification(searchId, true, matches.map(toResult));
116118
}
117119

@@ -135,6 +137,7 @@ class SearchDomainHandler implements protocol.RequestHandler {
135137
// search
136138
List<SearchMatch> matches =
137139
await searchEngine.searchTopLevelDeclarations(params.pattern);
140+
matches = SearchMatch.withNotNullElement(matches);
138141
_sendSearchNotification(searchId, true, matches.map(toResult));
139142
}
140143

pkg/analysis_server/lib/src/services/search/search_engine.dart

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -155,16 +155,20 @@ class SearchMatch {
155155
this.sourceRange, this.isResolved, this.isQualified);
156156

157157
/**
158-
* Return the [Element] containing the match.
158+
* Return the [Element] containing the match. Can return `null` if the unit
159+
* does not exist, or its element was invalidated, or the element cannot be
160+
* found, etc.
159161
*/
160162
Element get element {
161163
if (_element == null) {
162164
CompilationUnitElement unitElement =
163165
context.getCompilationUnitElement(unitSource, librarySource);
164-
_ContainingElementFinder finder =
165-
new _ContainingElementFinder(sourceRange.offset);
166-
unitElement.accept(finder);
167-
_element = finder.containingElement;
166+
if (unitElement != null) {
167+
_ContainingElementFinder finder =
168+
new _ContainingElementFinder(sourceRange.offset);
169+
unitElement.accept(finder);
170+
_element = finder.containingElement;
171+
}
168172
}
169173
return _element;
170174
}
@@ -237,6 +241,16 @@ class SearchMatch {
237241
buffer.write(")");
238242
return buffer.toString();
239243
}
244+
245+
/**
246+
* Return elements of [matches] which has not-null elements.
247+
*
248+
* When [SearchMatch.element] is not `null` we cache its value, so it cannot
249+
* become `null` later.
250+
*/
251+
static List<SearchMatch> withNotNullElement(List<SearchMatch> matches) {
252+
return matches.where((match) => match.element != null).toList();
253+
}
240254
}
241255

242256
/**

pkg/analysis_server/test/services/search/search_engine_test.dart

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -617,6 +617,23 @@ main(A<int> a) {
617617
await _verifyReferences(method, expected);
618618
}
619619

620+
test_searchReferences_null_noUnitElement() async {
621+
_indexTestUnit('''
622+
class A {
623+
m() {}
624+
}
625+
main(A a) {
626+
a.m();
627+
}
628+
''');
629+
MethodElement method = findElement('m');
630+
List<SearchMatch> matches = await searchEngine.searchReferences(method);
631+
expect(matches, hasLength(1));
632+
// Set the source contents, so the element is invalidated.
633+
context.setContents(testSource, '');
634+
expect(matches.single.element, isNull);
635+
}
636+
620637
test_searchReferences_ParameterElement_ofConstructor() async {
621638
_indexTestUnit('''
622639
class C {

0 commit comments

Comments
 (0)