blob: 08cd46333d3cebdab9b40a3f050a5c3b6171eb38 [file] [log] [blame]
// Copyright (c) 2016, 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.
library analyzer.test.src.summary.resynthesize_ast_test;
import 'dart:async';
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/error/error.dart';
import 'package:analyzer/src/dart/element/element.dart';
import 'package:analyzer/src/generated/engine.dart'
show AnalysisContext, AnalysisOptionsImpl;
import 'package:analyzer/src/generated/sdk.dart';
import 'package:analyzer/src/generated/source.dart';
import 'package:analyzer/src/summary/format.dart';
import 'package:analyzer/src/summary/idl.dart';
import 'package:analyzer/src/summary/link.dart';
import 'package:analyzer/src/summary/prelink.dart';
import 'package:analyzer/src/summary/resynthesize.dart';
import 'package:analyzer/src/summary/summarize_ast.dart';
import 'package:analyzer/src/summary/summarize_elements.dart'
show PackageBundleAssembler;
import 'package:analyzer/task/dart.dart' show PARSED_UNIT;
import 'package:analyzer/task/general.dart';
import 'package:test/test.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
import '../context/abstract_context.dart';
import 'element_text.dart';
import 'resynthesize_common.dart';
import 'summary_common.dart';
main() {
defineReflectiveSuite(() {
defineReflectiveTests(ResynthesizeAstSpecTest);
defineReflectiveTests(ResynthesizeAstStrongTest);
defineReflectiveTests(ApplyCheckElementTextReplacements);
});
}
@reflectiveTest
class ApplyCheckElementTextReplacements {
test_applyReplacements() {
applyCheckElementTextReplacements();
}
}
@reflectiveTest
class ResynthesizeAstSpecTest extends _ResynthesizeAstTest {
@override
bool get isStrongMode => false;
}
@reflectiveTest
class ResynthesizeAstStrongTest extends _ResynthesizeAstTest {
@override
bool get isStrongMode => true;
@override
AnalysisOptionsImpl createOptions() =>
super.createOptions()..strongMode = true;
@failingTest // See dartbug.com/32290
test_const_constructor_inferred_args() =>
test_const_constructor_inferred_args();
@override
@failingTest
test_syntheticFunctionType_genericClosure() async {
await super.test_syntheticFunctionType_genericClosure();
}
@override
@failingTest
test_syntheticFunctionType_inGenericClass() async {
await super.test_syntheticFunctionType_inGenericClass();
}
@override
@failingTest
test_syntheticFunctionType_noArguments() async {
await super.test_syntheticFunctionType_noArguments();
}
@override
@failingTest
test_syntheticFunctionType_withArguments() async {
await super.test_syntheticFunctionType_withArguments();
}
}
/**
* Abstract mixin for serializing ASTs and resynthesizing elements from it.
*/
abstract class _AstResynthesizeTestMixin
implements _AstResynthesizeTestMixinInterface {
final Set<Source> serializedSources = new Set<Source>();
PackageBundleAssembler bundleAssembler = new PackageBundleAssembler();
final Map<String, UnlinkedUnitBuilder> uriToUnit =
<String, UnlinkedUnitBuilder>{};
AnalysisContext get context;
LibraryElementImpl _encodeDecodeLibraryElement(Source source) {
SummaryResynthesizer resynthesizer = _encodeLibrary(source);
return resynthesizer.getLibraryElement(source.uri.toString());
}
TestSummaryResynthesizer _encodeLibrary(Source source) {
_serializeLibrary(source);
PackageBundle bundle =
new PackageBundle.fromBuffer(bundleAssembler.assemble().toBuffer());
Map<String, UnlinkedUnit> unlinkedSummaries = <String, UnlinkedUnit>{};
for (int i = 0; i < bundle.unlinkedUnitUris.length; i++) {
String uri = bundle.unlinkedUnitUris[i];
unlinkedSummaries[uri] = bundle.unlinkedUnits[i];
}
LinkedLibrary getDependency(String absoluteUri) {
Map<String, LinkedLibrary> sdkLibraries =
SerializedMockSdk.instance.uriToLinkedLibrary;
LinkedLibrary linkedLibrary = sdkLibraries[absoluteUri];
if (linkedLibrary == null && !allowMissingFiles) {
fail('Linker unexpectedly requested LinkedLibrary for "$absoluteUri".'
' Libraries available: ${sdkLibraries.keys}');
}
return linkedLibrary;
}
UnlinkedUnit getUnit(String absoluteUri) {
UnlinkedUnit unit = uriToUnit[absoluteUri] ??
SerializedMockSdk.instance.uriToUnlinkedUnit[absoluteUri];
if (unit == null && !allowMissingFiles) {
fail('Linker unexpectedly requested unit for "$absoluteUri".');
}
return unit;
}
Set<String> nonSdkLibraryUris = serializedSources
.where((Source source) =>
!source.isInSystemLibrary &&
context.computeKindOf(source) == SourceKind.LIBRARY)
.map((Source source) => source.uri.toString())
.toSet();
Map<String, LinkedLibrary> linkedSummaries = link(
nonSdkLibraryUris,
getDependency,
getUnit,
context.declaredVariables.get,
context.analysisOptions.strongMode);
return new TestSummaryResynthesizer(
context,
new Map<String, UnlinkedUnit>()
..addAll(SerializedMockSdk.instance.uriToUnlinkedUnit)
..addAll(unlinkedSummaries),
new Map<String, LinkedLibrary>()
..addAll(SerializedMockSdk.instance.uriToLinkedLibrary)
..addAll(linkedSummaries),
allowMissingFiles);
}
UnlinkedUnit _getUnlinkedUnit(Source source) {
if (source == null) {
return new UnlinkedUnitBuilder();
}
String uriStr = source.uri.toString();
{
UnlinkedUnit unlinkedUnitInSdk =
SerializedMockSdk.instance.uriToUnlinkedUnit[uriStr];
if (unlinkedUnitInSdk != null) {
return unlinkedUnitInSdk;
}
}
return uriToUnit.putIfAbsent(uriStr, () {
int modificationTime = context.computeResult(source, MODIFICATION_TIME);
if (modificationTime < 0) {
// Source does not exist.
if (!allowMissingFiles) {
fail('Unexpectedly tried to get unlinked summary for $source');
}
return null;
}
CompilationUnit unit = context.computeResult(source, PARSED_UNIT);
UnlinkedUnitBuilder unlinkedUnit = serializeAstUnlinked(unit);
bundleAssembler.addUnlinkedUnit(source, unlinkedUnit);
return unlinkedUnit;
});
}
void _serializeLibrary(Source librarySource) {
if (librarySource == null || librarySource.isInSystemLibrary) {
return;
}
if (!serializedSources.add(librarySource)) {
return;
}
UnlinkedUnit getPart(String absoluteUri) {
Source source = context.sourceFactory.forUri(absoluteUri);
return _getUnlinkedUnit(source);
}
UnlinkedPublicNamespace getImport(String relativeUri) {
return getPart(relativeUri)?.publicNamespace;
}
UnlinkedUnit definingUnit = _getUnlinkedUnit(librarySource);
if (definingUnit != null) {
LinkedLibraryBuilder linkedLibrary = prelink(librarySource.uri.toString(),
definingUnit, getPart, getImport, context.declaredVariables.get);
linkedLibrary.dependencies.skip(1).forEach((LinkedDependency d) {
Source source = context.sourceFactory.forUri(d.uri);
_serializeLibrary(source);
});
}
}
}
/**
* Interface that [_AstResynthesizeTestMixin] requires of classes it's mixed
* into. We can't place the getter below into [_AstResynthesizeTestMixin]
* directly, because then it would be overriding a field at the site where the
* mixin is instantiated.
*/
abstract class _AstResynthesizeTestMixinInterface {
/**
* A test should return `true` to indicate that a missing file at the time of
* summary resynthesis shouldn't trigger an error.
*/
bool get allowMissingFiles;
}
abstract class _ResynthesizeAstTest extends ResynthesizeTest
with _AstResynthesizeTestMixin {
bool get isStrongMode;
bool get shouldCompareLibraryElements;
@override
Future<LibraryElementImpl> checkLibrary(String text,
{bool allowErrors: false, bool dumpSummaries: false}) async {
Source source = addTestSource(text);
LibraryElementImpl resynthesized = _encodeDecodeLibraryElement(source);
LibraryElementImpl original = context.computeLibraryElement(source);
if (!allowErrors) {
List<AnalysisError> errors = context.computeErrors(source);
if (errors.where((e) => e.message.startsWith('unused')).isNotEmpty) {
fail('Analysis errors: $errors');
}
}
if (shouldCompareLibraryElements) {
checkLibraryElements(original, resynthesized);
}
return resynthesized;
}
@override
DartSdk createDartSdk() => AbstractContextTest.SHARED_MOCK_SDK;
@override
AnalysisOptionsImpl createOptions() =>
super.createOptions()..strongMode = isStrongMode;
test_getElement_constructor_named() async {
String text = 'class C { C.named(); }';
Source source = addLibrarySource('/test.dart', text);
ConstructorElement original = context
.computeLibraryElement(source)
.getType('C')
.getNamedConstructor('named');
expect(original, isNotNull);
ConstructorElement resynthesized = _validateGetElement(text, original);
compareConstructorElements(resynthesized, original, 'C.constructor named');
}
test_getElement_constructor_unnamed() async {
String text = 'class C { C(); }';
Source source = addLibrarySource('/test.dart', text);
ConstructorElement original =
context.computeLibraryElement(source).getType('C').unnamedConstructor;
expect(original, isNotNull);
ConstructorElement resynthesized = _validateGetElement(text, original);
compareConstructorElements(resynthesized, original, 'C.constructor');
}
test_getElement_field() async {
String text = 'class C { var f; }';
Source source = addLibrarySource('/test.dart', text);
FieldElement original =
context.computeLibraryElement(source).getType('C').getField('f');
expect(original, isNotNull);
FieldElement resynthesized = _validateGetElement(text, original);
compareFieldElements(resynthesized, original, 'C.field f');
}
test_getElement_getter() async {
String text = 'class C { get f => null; }';
Source source = addLibrarySource('/test.dart', text);
PropertyAccessorElement original =
context.computeLibraryElement(source).getType('C').getGetter('f');
expect(original, isNotNull);
PropertyAccessorElement resynthesized = _validateGetElement(text, original);
comparePropertyAccessorElements(resynthesized, original, 'C.getter f');
}
test_getElement_method() async {
String text = 'class C { f() {} }';
Source source = addLibrarySource('/test.dart', text);
MethodElement original =
context.computeLibraryElement(source).getType('C').getMethod('f');
expect(original, isNotNull);
MethodElement resynthesized = _validateGetElement(text, original);
compareMethodElements(resynthesized, original, 'C.method f');
}
test_getElement_operator() async {
String text = 'class C { operator+(x) => null; }';
Source source = addLibrarySource('/test.dart', text);
MethodElement original =
context.computeLibraryElement(source).getType('C').getMethod('+');
expect(original, isNotNull);
MethodElement resynthesized = _validateGetElement(text, original);
compareMethodElements(resynthesized, original, 'C.operator+');
}
test_getElement_setter() async {
String text = 'class C { void set f(value) {} }';
Source source = addLibrarySource('/test.dart', text);
PropertyAccessorElement original =
context.computeLibraryElement(source).getType('C').getSetter('f');
expect(original, isNotNull);
PropertyAccessorElement resynthesized = _validateGetElement(text, original);
comparePropertyAccessorElements(resynthesized, original, 'C.setter f');
}
test_getElement_unit() async {
String text = 'class C { f() {} }';
Source source = addLibrarySource('/test.dart', text);
CompilationUnitElement original =
context.computeLibraryElement(source).definingCompilationUnit;
expect(original, isNotNull);
CompilationUnitElement resynthesized = _validateGetElement(text, original);
compareCompilationUnitElements(resynthesized, original);
}
/**
* Return a [SummaryResynthesizer] to resynthesize the library with the
* given [Source].
*/
TestSummaryResynthesizer _encodeDecodeLibrarySource(Source source) {
return _encodeLibrary(source);
}
/**
* Encode the library containing [original] into a summary and then use
* [TestSummaryResynthesizer.getElement] to retrieve just the original
* element from the resynthesized summary.
*/
Element _validateGetElement(String text, Element original) {
SummaryResynthesizer resynthesizer =
_encodeDecodeLibrarySource(original.library.source);
ElementLocationImpl location = original.location;
Element result = resynthesizer.getElement(location);
checkMinimalResynthesisWork(resynthesizer, original.library);
// Check that no other summaries needed to be resynthesized to resynthesize
// the library element.
expect(resynthesizer.resynthesisCount, 3);
expect(result.location, location);
return result;
}
}