blob: d4f04be21739eb759cf0ea9b3c8c0f93b8877663 [file] [log] [blame]
// Copyright (c) 2019, 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:analyzer/src/dart/analysis/experiments.dart';
import 'package:analyzer_plugin/src/utilities/change_builder/dart/syntactic_scope.dart';
import 'package:test/test.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
import '../../../../support/abstract_context.dart';
main() {
defineReflectiveSuite(() {
defineReflectiveTests(NotSyntacticScopeReferencedNamesCollectorTest);
defineReflectiveTests(SyntacticScopeNamesCollectorTest);
});
}
@reflectiveTest
class NotSyntacticScopeReferencedNamesCollectorTest
extends AbstractContextTest {
test_notSyntacticScopeNames() async {
var path = convertPath('/home/test/lib/test.dart');
newFile('/home/test/lib/a.dart', content: r'''
var N1;
''');
newFile(path, content: r'''
import 'package:test/a.dart';
class A {
var N2;
void N3() {}
get N4 => null;
set N5(_) {}
}
var S1;
class B<S2> extends A {
var S3;
void S4() {}
get S5 => null;
set S6(_) {}
void f<S7>(S8) {
var S9;
N1;
N1 = 0;
N2;
N3;
N4;
N5 = 0;
B;
S1;
S1 = 0;
S2;
S3;
S4;
S5;
S6 = 0;
S7;
S8;
S9;
}
}
''');
var resolvedUnit = await session.getResolvedUnit(path);
var collector = NotSyntacticScopeReferencedNamesCollector(
resolvedUnit.libraryElement,
(<String>[]
..addAll(List.generate(20, (i) => 'N$i'))
..addAll(List.generate(20, (i) => 'S$i')))
.toSet(),
);
resolvedUnit.unit.accept(collector);
expect(
collector.importedNames,
containsPair('N1', Uri.parse('package:test/a.dart')),
);
expect(
collector.inheritedNames,
unorderedEquals(['N2', 'N3', 'N4', 'N5']),
);
}
test_referencedNames() async {
var path = convertPath('/home/test/lib/test.dart');
newFile(path, content: r'''
class N1 {}
N2 N3<N4>(N5 N6, N7) {
N7.N8(N9);
}
''');
var resolvedUnit = await session.getResolvedUnit(path);
var collector = NotSyntacticScopeReferencedNamesCollector(
resolvedUnit.libraryElement,
<String>[].toSet(),
);
resolvedUnit.unit.accept(collector);
expect(
collector.referencedNames,
unorderedEquals(['N1', 'N2', 'N3', 'N4', 'N5', 'N6', 'N7', 'N8', 'N9']),
);
}
}
@reflectiveTest
class SyntacticScopeNamesCollectorTest extends AbstractContextTest {
test_Block() {
_assertScopeNames(code: r'''
N1() {
^1
N2 N3, N4;
^2
{
^3
var N5;
^4
}
^5
var N6;
^6
{
^7
var N7;
^8
}
^9
}
''', expected: r'''
1: N3, N4, N6
2: N3, N4, N6
3: N3, N4, N5, N6
4: N3, N4, N5, N6
5: N3, N4, N6
6: N3, N4, N6
7: N3, N4, N6, N7
8: N3, N4, N6, N7
9: N3, N4, N6
''');
}
test_CatchClause() {
_assertScopeNames(code: r'''
N1() {
^1
try {
var N2;
^2
} on N3 catch (N4, N5) {
^3
} catch (N6) {
^4
}
^5
}
''', expected: r'''
1: {}
2: N2
3: N4, N5
4: N6
5: {}
''');
}
test_ClassDeclaration() {
_assertScopeNames(code: r'''
class N1<N2 ^1> extends ^2 N3<N4 ^3> with ^4 N5, N6 implements ^5 N7, N8 {
^6
N9 N10, N11;
N1.N12() {}
N13 N14<N15>() {}
^7
}
class N16<N17> {
^8
}
''', expected: r'''
1: N2
2: N2
3: N2
4: N2
5: N2
6: N2, N10, N11, N14
7: N2, N10, N11, N14
8: N17
''');
}
test_ClassTypeAlias() {
_assertScopeNames(code: r'''
class N1<N2 ^1> = N3<N4 ^2> with N5<N6 ^3> implements N7;
''', expected: r'''
1: N2
2: N2
3: N2
''');
}
test_CollectionForElement_ForEachPartsWithDeclaration() {
_enableExperiments();
_assertScopeNames(code: r'''
N1() {
[
0 ^1,
for (var N2 in N3 ^2) {
^3
},
^4
for (var N4 in N5) {
^5
},
^6
];
^7
}
''', expected: r'''
1: {}
2: {}
3: N2
4: {}
5: N4
6: {}
7: {}
''');
}
test_CollectionForElement_ForPartsWithDeclarations() {
_enableExperiments();
_assertScopeNames(code: r'''
N1() {
[
0 ^1,
for (var N2 = 0 ^2; ^3; ^4) {
^5
},
^6
for (var N3 = 0 ^7; ^8; ^9) {
^10
},
^11
];
^12
}
''', expected: r'''
1: {}
2: N2
3: N2
4: N2
5: N2
6: {}
7: N3
8: N3
9: N3
10: N3
11: {}
12: {}
''');
}
test_ConstructorDeclaration() {
_assertScopeNames(code: r'''
class N1<N2> extends N3 {
N1.N4(N5 ^1) {
^2
}
^3
}
''', expected: r'''
1: N2, N5
2: N2, N5
3: N2
''');
}
test_ForEachStatement_identifier() {
_assertScopeNames(code: r'''
N1() {
^1
for (N2 in N3 ^2) {
^3
}
^4
}
''', expected: r'''
1: {}
2: {}
3: {}
4: {}
''');
}
test_ForEachStatement_iterable() {
_assertScopeNames(code: r'''
N1() {
for (var N2 in (){ var N3; ^1 }()) {
^2
}
^3
}
''', expected: r'''
1: N3
2: N2
3: {}
''');
}
test_ForEachStatement_loopVariable() {
_assertScopeNames(code: r'''
N1() {
^1
for (var N2 in N3 ^2) {
^3
}
^4
}
''', expected: r'''
1: {}
2: {}
3: N2
4: {}
''');
}
test_FormalParameter_functionTyped() {
_assertScopeNames(code: r'''
N1 N2(N3 N4(N5 N6 ^1, N7), N8 ^2) {
^3
}
''', expected: r'''
1: N4, N6, N7, N8
2: N4, N8
3: N4, N8
''');
}
test_ForStatement2_ForEachPartsWithDeclaration() {
_enableExperiments();
_assertScopeNames(code: r'''
N1() {
^1
for (var N2 in N3 ^2) {
^3
}
^4
}
''', expected: r'''
1: {}
2: {}
3: N2
4: {}
''');
}
test_ForStatement2_ForEachPartsWithIdentifier() {
_enableExperiments();
_assertScopeNames(code: r'''
N1() {
^1
for (N2 in N3 ^2) {
^3
}
^4
}
''', expected: r'''
1: {}
2: {}
3: {}
4: {}
''');
}
test_ForStatement2_ForPartsWithDeclarations_condition() {
_enableExperiments();
_assertScopeNames(code: r'''
N1() {
^1
for (N1 N2; (){ var N3; ^2 }(); ^3) {
^4
}
^5
}
''', expected: r'''
1: {}
2: N2, N3
3: N2
4: N2
5: {}
''');
}
test_ForStatement2_ForPartsWithDeclarations_updaters() {
_enableExperiments();
_assertScopeNames(code: r'''
N1() {
^1
for (N1 N2; ^2; (){ var N3; ^3 }()) {
^4
}
^5
}
''', expected: r'''
1: {}
2: N2
3: N2, N3
4: N2
5: {}
''');
}
test_ForStatement2_ForPartsWithDeclarations_variables() {
_enableExperiments();
_assertScopeNames(code: r'''
N1() {
^1
for (N2 N3, N4 ^2; N5 ^3; N6 ^4) {
^5
}
^6
}
''', expected: r'''
1: {}
2: N3, N4
3: N3, N4
4: N3, N4
5: N3, N4
6: {}
''');
}
test_ForStatement_condition() {
_assertScopeNames(code: r'''
N1() {
^1
for (N1 N2; (){ var N3; ^2 }(); ^3) {
^4
}
^5
}
''', expected: r'''
1: {}
2: N2, N3
3: N2
4: N2
5: {}
''');
}
test_ForStatement_updaters() {
_assertScopeNames(code: r'''
N1() {
^1
for (N1 N2; ^2; (){ var N3; ^3 }()) {
^4
}
^5
}
''', expected: r'''
1: {}
2: N2
3: N2, N3
4: N2
5: {}
''');
}
test_ForStatement_variables() {
_assertScopeNames(code: r'''
N1() {
^1
for (N2 N3, N4 ^2; N5 ^3; N6 ^4) {
^5
}
^6
}
''', expected: r'''
1: {}
2: N3, N4
3: N3, N4
4: N3, N4
5: N3, N4
6: {}
''');
}
test_FunctionDeclaration() {
_assertScopeNames(code: r'''
N1 N2<N3 extends N4 ^1>(N5 N6 ^2, [N7 N8 = N9, N10]) {
^3
}
''', expected: r'''
1: N3
2: N3, N6, N8, N10
3: N3, N6, N8, N10
''');
}
test_FunctionTypeAlias() {
_assertScopeNames(code: r'''
typedef N1 N2<N3 ^1>(N3 N4, N5 ^2);
''', expected: r'''
1: N3
2: N3, N4, N5
''');
}
test_GenericFunctionType() {
_assertScopeNames(code: r'''
N1 Function<N2 ^1>(N3, N4 N5 ^2) N6;
''', expected: r'''
1: N2
2: N2, N5
''');
}
test_GenericTypeAlias() {
_assertScopeNames(code: r'''
typedef N1<N2 ^1> = Function<N3 ^2>(N4 N5, N6 ^3);
''', expected: r'''
1: N2
2: N2, N3
3: N2, N3, N5
''');
}
test_MethodDeclaration() {
_assertScopeNames(code: r'''
class N1<N2> {
N3 N4, N5;
^1
N6 ^2 N7<N8 ^3>(N9 N10, N11 ^4) {
^5
}
}
''', expected: r'''
1: N2, N4, N5, N7
2: N2, N4, N5, N7, N8
3: N2, N4, N5, N7, N8
4: N2, N4, N5, N7, N8, N10, N11
5: N2, N4, N5, N7, N8, N10, N11
''');
}
test_MixinDeclaration() {
_assertScopeNames(code: r'''
mixin N1<N2> on N3, N4 ^1 implements N5 ^2 {
^3
N6 N7, N8;
^4
N9(N10 ^5) {
^6
}
^7
}
''', expected: r'''
1: N2
2: N2
3: N2, N7, N8, N9
4: N2, N7, N8, N9
5: N2, N7, N8, N9, N10
6: N2, N7, N8, N9, N10
7: N2, N7, N8, N9
''');
}
void _assertScopeNames({String code, String expected}) {
var matches = RegExp(r'\^\d{1,2}').allMatches(code).toList();
var matchOffsets = <String, int>{};
var delta = 0;
for (var match in matches) {
var newStart = match.start - delta;
var newEnd = match.end - delta;
matchOffsets[code.substring(newStart + 1, newEnd)] = newStart;
delta += match.end - match.start;
code = code.substring(0, newStart) + code.substring(newEnd);
}
var path = convertPath('/home/test/lib/a.dart');
newFile(path, content: code);
var parsedResult = session.getParsedUnit(path);
expect(parsedResult.errors, isEmpty);
var unit = parsedResult.unit;
var buffer = StringBuffer();
for (var offsetName in matchOffsets.keys) {
var offset = matchOffsets[offsetName];
var nameSet = Set<String>();
unit.accept(SyntacticScopeNamesCollector(nameSet, offset));
var nameList = nameSet.toList();
nameList.sort((a, b) {
expect(a.startsWith('N'), isTrue);
expect(b.startsWith('N'), isTrue);
return int.parse(a.substring(1)) - int.parse(b.substring(1));
});
buffer.write('$offsetName: ');
if (nameList.isEmpty) {
buffer.writeln('{}');
} else {
buffer.writeln(nameList.join(', '));
}
}
var actual = buffer.toString();
if (actual != expected) {
print(actual);
}
expect(actual, expected);
}
void _enableExperiments() {
createAnalysisOptionsFile(
experiments: [
EnableString.control_flow_collections,
EnableString.spread_collections,
],
);
}
}