From 038cd323265758451fda23c9a3923596e6c4b1e1 Mon Sep 17 00:00:00 2001 From: Konstantin Shcheglov Date: Thu, 1 Dec 2016 13:49:00 -0800 Subject: [PATCH] Compute exported top-level declarations. R=brianwilkerson@google.com, paulberry@google.com BUG= Review URL: https://codereview.chromium.org/2542883003 . --- .../lib/src/dart/analysis/file_state.dart | 58 ++++- .../src/dart/analysis/file_state_test.dart | 199 ++++++++++++++++++ 2 files changed, 256 insertions(+), 1 deletion(-) diff --git a/pkg/analyzer/lib/src/dart/analysis/file_state.dart b/pkg/analyzer/lib/src/dart/analysis/file_state.dart index 6592d5d6e1c..ae15012012e 100644 --- a/pkg/analyzer/lib/src/dart/analysis/file_state.dart +++ b/pkg/analyzer/lib/src/dart/analysis/file_state.dart @@ -102,6 +102,7 @@ class FileState { String _transitiveSignature; List _topLevelDeclarations; + List _exportedTopLevelDeclarations; FileState._(this._fsState, this.path, this.uri, this.source); @@ -131,6 +132,55 @@ class FileState { */ List get exportedFiles => _exportedFiles; + /** + * Return [TopLevelDeclaration]s exported from the this library file. + */ + List get exportedTopLevelDeclarations { + if (_exportedTopLevelDeclarations == null) { + _exportedTopLevelDeclarations = []; + + Set seenLibraries = new Set(); + + /** + * Compute [TopLevelDeclaration]s exported from the [library]. + */ + List computeExported(FileState library) { + var declarations = {}; + if (seenLibraries.add(library)) { + // Append the library declarations. + for (TopLevelDeclaration t in library.topLevelDeclarations) { + declarations[t.name] = t; + } + for (FileState part in library.partedFiles) { + for (TopLevelDeclaration t in part.topLevelDeclarations) { + declarations[t.name] = t; + } + } + + // Append the exported declarations. + for (int i = 0; i < library._exportedFiles.length; i++) { + List exported = + computeExported(library._exportedFiles[i]); + for (TopLevelDeclaration t in exported) { + if (!declarations.containsKey(t.name) && + library._exportFilters[i].accepts(t.name)) { + declarations[t.name] = t; + } + } + } + + // We're done with this library. + seenLibraries.remove(library); + } + + return declarations.values.toList(); + } + + _exportedTopLevelDeclarations = computeExported(this); + } + return _exportedTopLevelDeclarations; + } + @override int get hashCode => uri.hashCode; @@ -353,18 +403,24 @@ class FileState { _referencedNames = new Set.from(driverUnlinkedUnit.referencedNames); _unlinked = driverUnlinkedUnit.unit; _lineInfo = new LineInfo(_unlinked.lineStarts); + _topLevelDeclarations = null; + + // Prepare API signature. List newApiSignature = _unlinked.apiSignature; bool apiSignatureChanged = _apiSignature != null && !_equalByteLists(_apiSignature, newApiSignature); _apiSignature = newApiSignature; - // If the API signature changed, flush transitive signatures. + // The API signature changed. + // Flush transitive signatures of affected files. + // Flush exported top-level declarations of all files. if (apiSignatureChanged) { for (FileState file in _fsState._uriToFile.values) { if (file._transitiveFiles != null && file._transitiveFiles.contains(this)) { file._transitiveSignature = null; } + file._exportedTopLevelDeclarations = null; } } diff --git a/pkg/analyzer/test/src/dart/analysis/file_state_test.dart b/pkg/analyzer/test/src/dart/analysis/file_state_test.dart index b4cafb27b28..8a3a8da40a8 100644 --- a/pkg/analyzer/test/src/dart/analysis/file_state_test.dart +++ b/pkg/analyzer/test/src/dart/analysis/file_state_test.dart @@ -58,6 +58,199 @@ class FileSystemStateTest { provider, sourceFactory, analysisOptions, new Uint32List(0), ''); } + test_exportedTopLevelDeclarations_export() { + String a = _p('/aaa/lib/a.dart'); + String b = _p('/aaa/lib/b.dart'); + provider.newFile( + a, + r''' +class A {} +'''); + provider.newFile( + b, + r''' +export 'a.dart'; +class B {} +'''); + FileState file = fileSystemState.getFileForPath(b); + List declarations = file.exportedTopLevelDeclarations; + expect(declarations.map((t) => t.name), unorderedEquals(['A', 'B'])); + } + + test_exportedTopLevelDeclarations_export2_show() { + String a = _p('/aaa/lib/a.dart'); + String b = _p('/aaa/lib/b.dart'); + String c = _p('/aaa/lib/c.dart'); + provider.newFile( + a, + r''' +class A1 {} +class A2 {} +class A3 {} +'''); + provider.newFile( + b, + r''' +export 'a.dart' show A1, A2; +class B1 {} +class B2 {} +'''); + provider.newFile( + c, + r''' +export 'b.dart' show A2, A3, B1; +class C {} +'''); + _assertExportedTopLevelDeclarations(c, ['A2', 'B1', 'C']); + } + + test_exportedTopLevelDeclarations_export_flushOnChange() { + String a = _p('/aaa/lib/a.dart'); + String b = _p('/aaa/lib/b.dart'); + provider.newFile( + a, + r''' +class A {} +'''); + provider.newFile( + b, + r''' +export 'a.dart'; +class B {} +'''); + + // Initial exported declarations. + _assertExportedTopLevelDeclarations(b, ['A', 'B']); + + // Update a.dart, so a.dart and b.dart exported declarations are flushed. + provider.newFile(a, 'class A {} class A2 {}'); + fileSystemState.getFileForPath(a).refresh(); + _assertExportedTopLevelDeclarations(b, ['A', 'A2', 'B']); + } + + test_exportedTopLevelDeclarations_export_hide() { + String a = _p('/aaa/lib/a.dart'); + String b = _p('/aaa/lib/b.dart'); + provider.newFile( + a, + r''' +class A1 {} +class A2 {} +class A3 {} +'''); + provider.newFile( + b, + r''' +export 'a.dart' hide A2; +class B {} +'''); + _assertExportedTopLevelDeclarations(b, ['A1', 'A3', 'B']); + } + + test_exportedTopLevelDeclarations_export_preferLocal() { + String a = _p('/aaa/lib/a.dart'); + String b = _p('/aaa/lib/b.dart'); + provider.newFile( + a, + r''' +class V {} +'''); + provider.newFile( + b, + r''' +export 'a.dart'; +int V; +'''); + FileState file = fileSystemState.getFileForPath(b); + List declarations = file.exportedTopLevelDeclarations; + expect(declarations.map((t) => t.name), unorderedEquals(['V'])); + expect(declarations.single.kind, TopLevelDeclarationKind.variable); + } + + test_exportedTopLevelDeclarations_export_show() { + String a = _p('/aaa/lib/a.dart'); + String b = _p('/aaa/lib/b.dart'); + provider.newFile( + a, + r''' +class A1 {} +class A2 {} +'''); + provider.newFile( + b, + r''' +export 'a.dart' show A2; +class B {} +'''); + _assertExportedTopLevelDeclarations(b, ['A2', 'B']); + } + + test_exportedTopLevelDeclarations_export_show2() { + String a = _p('/aaa/lib/a.dart'); + String b = _p('/aaa/lib/b.dart'); + String c = _p('/aaa/lib/c.dart'); + String d = _p('/aaa/lib/d.dart'); + provider.newFile( + a, + r''' +export 'b.dart' show Foo; +export 'c.dart' show Bar; +'''); + provider.newFile( + b, + r''' +export 'd.dart'; +'''); + provider.newFile( + c, + r''' +export 'd.dart'; +'''); + provider.newFile( + d, + r''' +class Foo {} +class Bar {} +'''); + _assertExportedTopLevelDeclarations(a, ['Foo', 'Bar']); + } + + test_exportedTopLevelDeclarations_import() { + String a = _p('/aaa/lib/a.dart'); + String b = _p('/aaa/lib/b.dart'); + provider.newFile( + a, + r''' +class A {} +'''); + provider.newFile( + b, + r''' +import 'a.dart'; +class B {} +'''); + _assertExportedTopLevelDeclarations(b, ['B']); + } + + test_exportedTopLevelDeclarations_parts() { + String a = _p('/aaa/lib/a.dart'); + String a2 = _p('/aaa/lib/a2.dart'); + provider.newFile( + a, + r''' +library lib; +part 'a2.dart'; +class A1 {} +'''); + provider.newFile( + a2, + r''' +part of lib; +class A2 {} +'''); + _assertExportedTopLevelDeclarations(a, ['A1', 'A2']); + } + test_getFileForPath_doesNotExist() { String path = _p('/aaa/lib/a.dart'); FileState file = fileSystemState.getFileForPath(path); @@ -441,6 +634,12 @@ set _V3(_) {} _assertFilesWithoutTransitiveSignatures([fa, fb, fc]); } + void _assertExportedTopLevelDeclarations(String path, List expected) { + FileState file = fileSystemState.getFileForPath(path); + List declarations = file.exportedTopLevelDeclarations; + expect(declarations.map((t) => t.name), unorderedEquals(expected)); + } + void _assertFilesWithoutTransitiveFiles(List expected) { var actual = fileSystemState.test.filesWithoutTransitiveFiles; expect(actual, unorderedEquals(expected));