Files
sdk/pkg/analyzer/test/generated/analysis_context_factory.dart
T
Konstantin Shcheglov c9d1c4a3b7 Suite of source code based resolution tests.
As opposite to constructing AST nodes and calling partial resolvers.

Also the set of tests that we created for CFE integration is too large,
so I started splitting it into pieces and cleaning up to use findNode,
fincElement, and assertElement/Type().

R=brianwilkerson@google.com, paulberry@google.com

Change-Id: Ide4f0bee9e89eb2daf2317d1d92303a992847f1b
Reviewed-on: https://dart-review.googlesource.com/72240
Reviewed-by: Paul Berry <paulberry@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
2018-08-30 21:49:44 +00:00

585 lines
24 KiB
Dart

// 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.
import 'dart:collection';
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/ast/token.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/type.dart';
import 'package:analyzer/file_system/file_system.dart';
import 'package:analyzer/file_system/memory_file_system.dart';
import 'package:analyzer/file_system/physical_file_system.dart';
import 'package:analyzer/src/context/context.dart';
import 'package:analyzer/src/dart/element/element.dart';
import 'package:analyzer/src/dart/element/type.dart';
import 'package:analyzer/src/dart/sdk/sdk.dart';
import 'package:analyzer/src/file_system/file_system.dart';
import 'package:analyzer/src/generated/engine.dart';
import 'package:analyzer/src/generated/resolver.dart';
import 'package:analyzer/src/generated/sdk.dart';
import 'package:analyzer/src/generated/source_io.dart';
import 'package:analyzer/src/generated/testing/ast_test_factory.dart';
import 'package:analyzer/src/generated/testing/element_factory.dart';
import 'package:analyzer/src/generated/testing/test_type_provider.dart';
import 'package:analyzer/src/generated/utilities_dart.dart';
import 'package:analyzer/src/string_source.dart';
import 'package:test/test.dart';
/**
* The class `AnalysisContextFactory` defines utility methods used to create analysis contexts
* for testing purposes.
*/
class AnalysisContextFactory {
static String _DART_MATH = "dart:math";
static String _DART_INTERCEPTORS = "dart:_interceptors";
static String _DART_JS_HELPER = "dart:_js_helper";
/**
* Create and return an analysis context that has a fake core library already
* resolved. The given [resourceProvider] will be used when accessing the file
* system.
*/
static InternalAnalysisContext contextWithCore(
{UriResolver contributedResolver, ResourceProvider resourceProvider}) {
AnalysisContextForTests context = new AnalysisContextForTests();
return initContextWithCore(context, contributedResolver, resourceProvider);
}
/**
* Create and return an analysis context that uses the given [options] and has
* a fake core library already resolved. The given [resourceProvider] will be
* used when accessing the file system.
*/
static InternalAnalysisContext contextWithCoreAndOptions(
AnalysisOptions options,
{ResourceProvider resourceProvider}) {
AnalysisContextForTests context = new AnalysisContextForTests();
context._internalSetAnalysisOptions(options);
return initContextWithCore(context, null, resourceProvider);
}
/**
* Create and return an analysis context that has a fake core library already
* resolved. If not `null`, the given [packages] map will be used to create a
* package URI resolver. The given [resourceProvider] will be used when
* accessing the file system.
*/
static InternalAnalysisContext contextWithCoreAndPackages(
Map<String, String> packages,
{ResourceProvider resourceProvider}) {
AnalysisContextForTests context = new AnalysisContextForTests();
return initContextWithCore(
context, new TestPackageUriResolver(packages), resourceProvider);
}
/**
* Initialize the given analysis [context] with a fake core library that has
* already been resolved. If not `null`, the given [contributedResolver] will
* be added to the context's source factory. The given [resourceProvider] will
* be used when accessing the file system.
*/
static InternalAnalysisContext initContextWithCore(
InternalAnalysisContext context,
[UriResolver contributedResolver,
ResourceProvider resourceProvider]) {
resourceProvider ??= PhysicalResourceProvider.INSTANCE;
DartSdk sdk = new _AnalysisContextFactory_initContextWithCore(
resourceProvider, '/fake/sdk');
List<UriResolver> resolvers = <UriResolver>[
new DartUriResolver(sdk),
new ResourceUriResolver(resourceProvider)
];
if (contributedResolver != null) {
resolvers.add(contributedResolver);
}
SourceFactory sourceFactory = new SourceFactory(resolvers);
context.sourceFactory = sourceFactory;
AnalysisContext coreContext = sdk.context;
//
// dart:core
//
TestTypeProvider provider = new TestTypeProvider();
CompilationUnitElementImpl coreUnit =
new CompilationUnitElementImpl("core.dart");
Source coreSource = sourceFactory.forUri(DartSdk.DART_CORE);
coreContext.setContents(coreSource, "");
coreUnit.librarySource = coreUnit.source = coreSource;
ClassElementImpl overrideClassElement =
ElementFactory.classElement2("_Override");
ClassElementImpl proxyClassElement = ElementFactory.classElement2("_Proxy");
proxyClassElement.constructors = <ConstructorElement>[
ElementFactory.constructorElement(proxyClassElement, '', true)
..isCycleFree = true
..constantInitializers = <ConstructorInitializer>[]
];
ClassElement objectClassElement = provider.objectType.element;
coreUnit.types = <ClassElement>[
provider.boolType.element,
provider.deprecatedType.element,
provider.doubleType.element,
provider.functionType.element,
provider.intType.element,
provider.iterableType.element,
provider.iteratorType.element,
provider.listType.element,
provider.mapType.element,
provider.nullType.element,
provider.numType.element,
objectClassElement,
overrideClassElement,
proxyClassElement,
provider.stackTraceType.element,
provider.stringType.element,
provider.symbolType.element,
provider.typeType.element
];
coreUnit.functions = <FunctionElement>[
ElementFactory.functionElement3("identical", provider.boolType,
<ClassElement>[objectClassElement, objectClassElement], null),
ElementFactory.functionElement3("print", VoidTypeImpl.instance,
<ClassElement>[objectClassElement], null)
];
TopLevelVariableElement proxyTopLevelVariableElt =
ElementFactory.topLevelVariableElement3(
"proxy", true, false, proxyClassElement.type);
ConstTopLevelVariableElementImpl deprecatedTopLevelVariableElt =
ElementFactory.topLevelVariableElement3(
"deprecated", true, false, provider.deprecatedType);
TopLevelVariableElement overrideTopLevelVariableElt =
ElementFactory.topLevelVariableElement3(
"override", true, false, overrideClassElement.type);
{
ClassElement deprecatedElement = provider.deprecatedType.element;
InstanceCreationExpression initializer =
AstTestFactory.instanceCreationExpression2(
Keyword.CONST,
AstTestFactory.typeName(deprecatedElement),
[AstTestFactory.string2('next release')]);
ConstructorElement constructor = deprecatedElement.constructors.single;
initializer.staticElement = constructor;
initializer.constructorName.staticElement = constructor;
deprecatedTopLevelVariableElt.constantInitializer = initializer;
}
coreUnit.accessors = <PropertyAccessorElement>[
deprecatedTopLevelVariableElt.getter,
overrideTopLevelVariableElt.getter,
proxyTopLevelVariableElt.getter
];
coreUnit.topLevelVariables = <TopLevelVariableElement>[
deprecatedTopLevelVariableElt,
overrideTopLevelVariableElt,
proxyTopLevelVariableElt
];
LibraryElementImpl coreLibrary = new LibraryElementImpl.forNode(
coreContext, AstTestFactory.libraryIdentifier2(["dart", "core"]));
coreLibrary.definingCompilationUnit = coreUnit;
//
// dart:async
//
LibraryElementImpl asyncLibrary = new LibraryElementImpl.forNode(
coreContext, AstTestFactory.libraryIdentifier2(["dart", "async"]));
CompilationUnitElementImpl asyncUnit =
new CompilationUnitElementImpl("async.dart");
Source asyncSource = sourceFactory.forUri(DartSdk.DART_ASYNC);
coreContext.setContents(asyncSource, "");
asyncUnit.librarySource = asyncUnit.source = asyncSource;
asyncLibrary.definingCompilationUnit = asyncUnit;
// Future<T>
ClassElementImpl futureElement =
ElementFactory.classElement2("Future", ["T"]);
// FutureOr<T>
ClassElementImpl futureOrElement =
ElementFactory.classElement2("FutureOr", ["T"]);
futureElement.enclosingElement = asyncUnit;
// factory Future.value([value])
ConstructorElementImpl futureConstructor =
ElementFactory.constructorElement2(futureElement, "value");
futureConstructor.parameters = <ParameterElement>[
ElementFactory.positionalParameter2("value", provider.dynamicType)
];
futureConstructor.factory = true;
futureElement.constructors = <ConstructorElement>[futureConstructor];
// Future<R> then<R>(FutureOr<R> onValue(T value), { Function onError });
TypeDefiningElement futureThenR = DynamicElementImpl.instance;
DartType onValueReturnType = DynamicTypeImpl.instance;
futureThenR = ElementFactory.typeParameterWithType('R');
onValueReturnType = futureOrElement.type.instantiate([futureThenR.type]);
FunctionElementImpl thenOnValue = ElementFactory.functionElement3(
'onValue', onValueReturnType, [futureElement.typeParameters[0]], null);
thenOnValue.isSynthetic = true;
DartType futureRType = futureElement.type.instantiate([futureThenR.type]);
MethodElementImpl thenMethod = ElementFactory.methodElementWithParameters(
futureElement, "then", futureRType, [
ElementFactory.requiredParameter2("onValue", thenOnValue.type),
ElementFactory.namedParameter2("onError", provider.functionType)
]);
if (!futureThenR.type.isDynamic) {
thenMethod.typeParameters = <TypeParameterElement>[futureThenR];
}
thenOnValue.enclosingElement = thenMethod;
thenOnValue.type = new FunctionTypeImpl(thenOnValue);
(thenMethod.parameters[0] as ParameterElementImpl).type = thenOnValue.type;
thenMethod.type = new FunctionTypeImpl(thenMethod);
futureElement.methods = <MethodElement>[thenMethod];
// Completer
ClassElementImpl completerElement =
ElementFactory.classElement2("Completer", ["T"]);
ConstructorElementImpl completerConstructor =
ElementFactory.constructorElement2(completerElement, null);
completerElement.constructors = <ConstructorElement>[completerConstructor];
// StreamSubscription
ClassElementImpl streamSubscriptionElement =
ElementFactory.classElement2("StreamSubscription", ["T"]);
// Stream
ClassElementImpl streamElement =
ElementFactory.classElement2("Stream", ["T"]);
streamElement.abstract = true;
streamElement.constructors = <ConstructorElement>[
ElementFactory.constructorElement2(streamElement, null)
];
DartType returnType = streamSubscriptionElement.type
.instantiate(streamElement.type.typeArguments);
FunctionElementImpl listenOnData = ElementFactory.functionElement3(
'onData',
VoidTypeImpl.instance,
<TypeDefiningElement>[streamElement.typeParameters[0]],
null);
listenOnData.isSynthetic = true;
List<DartType> parameterTypes = <DartType>[
listenOnData.type,
];
// TODO(brianwilkerson) This is missing the optional parameters.
MethodElementImpl listenMethod =
ElementFactory.methodElement('listen', returnType, parameterTypes);
streamElement.methods = <MethodElement>[listenMethod];
listenMethod.type = new FunctionTypeImpl(listenMethod);
FunctionElementImpl listenParamFunction = parameterTypes[0].element;
listenParamFunction.enclosingElement = listenMethod;
listenParamFunction.type = new FunctionTypeImpl(listenParamFunction);
ParameterElementImpl listenParam = listenMethod.parameters[0];
listenParam.type = listenParamFunction.type;
asyncUnit.types = <ClassElement>[
completerElement,
futureElement,
futureOrElement,
streamElement,
streamSubscriptionElement
];
//
// dart:html
//
CompilationUnitElementImpl htmlUnit =
new CompilationUnitElementImpl("html_dartium.dart");
Source htmlSource = sourceFactory.forUri(DartSdk.DART_HTML);
coreContext.setContents(htmlSource, "");
htmlUnit.librarySource = htmlUnit.source = htmlSource;
ClassElementImpl elementElement = ElementFactory.classElement2("Element");
InterfaceType elementType = elementElement.type;
ClassElementImpl canvasElement =
ElementFactory.classElement("CanvasElement", elementType);
ClassElementImpl contextElement =
ElementFactory.classElement2("CanvasRenderingContext");
InterfaceType contextElementType = contextElement.type;
ClassElementImpl context2dElement = ElementFactory.classElement(
"CanvasRenderingContext2D", contextElementType);
canvasElement.methods = <MethodElement>[
ElementFactory.methodElement(
"getContext", contextElementType, [provider.stringType])
];
canvasElement.accessors = <PropertyAccessorElement>[
ElementFactory.getterElement("context2D", false, context2dElement.type)
];
canvasElement.fields = canvasElement.accessors
.map((PropertyAccessorElement accessor) => accessor.variable)
.cast<FieldElement>()
.toList();
ClassElementImpl documentElement =
ElementFactory.classElement("Document", elementType);
ClassElementImpl htmlDocumentElement =
ElementFactory.classElement("HtmlDocument", documentElement.type);
htmlDocumentElement.methods = <MethodElement>[
ElementFactory.methodElement(
"query", elementType, <DartType>[provider.stringType])
];
htmlUnit.types = <ClassElement>[
ElementFactory.classElement("AnchorElement", elementType),
ElementFactory.classElement("BodyElement", elementType),
ElementFactory.classElement("ButtonElement", elementType),
canvasElement,
contextElement,
context2dElement,
ElementFactory.classElement("DivElement", elementType),
documentElement,
elementElement,
htmlDocumentElement,
ElementFactory.classElement("InputElement", elementType),
ElementFactory.classElement("SelectElement", elementType)
];
htmlUnit.functions = <FunctionElement>[
ElementFactory.functionElement3("query", elementElement.type,
<ClassElement>[provider.stringType.element], const <ClassElement>[])
];
TopLevelVariableElementImpl document =
ElementFactory.topLevelVariableElement3(
"document", false, true, htmlDocumentElement.type);
htmlUnit.topLevelVariables = <TopLevelVariableElement>[document];
htmlUnit.accessors = <PropertyAccessorElement>[document.getter];
LibraryElementImpl htmlLibrary = new LibraryElementImpl.forNode(coreContext,
AstTestFactory.libraryIdentifier2(["dart", "dom", "html"]));
htmlLibrary.definingCompilationUnit = htmlUnit;
//
// dart:math
//
CompilationUnitElementImpl mathUnit =
new CompilationUnitElementImpl("math.dart");
Source mathSource = sourceFactory.forUri(_DART_MATH);
coreContext.setContents(mathSource, "");
mathUnit.librarySource = mathUnit.source = mathSource;
FunctionElement cosElement = ElementFactory.functionElement3(
"cos",
provider.doubleType,
<ClassElement>[provider.numType.element],
const <ClassElement>[]);
TopLevelVariableElement ln10Element =
ElementFactory.topLevelVariableElement3(
"LN10", true, false, provider.doubleType);
TypeParameterElement maxT =
ElementFactory.typeParameterWithType('T', provider.numType);
FunctionElementImpl maxElement = ElementFactory.functionElement3(
"max", maxT.type, [maxT, maxT], const <ClassElement>[]);
maxElement.typeParameters = [maxT];
maxElement.type = new FunctionTypeImpl(maxElement);
TopLevelVariableElement piElement = ElementFactory.topLevelVariableElement3(
"PI", true, false, provider.doubleType);
ClassElementImpl randomElement = ElementFactory.classElement2("Random");
randomElement.abstract = true;
ConstructorElementImpl randomConstructor =
ElementFactory.constructorElement2(randomElement, null);
randomConstructor.factory = true;
ParameterElementImpl seedParam = new ParameterElementImpl("seed", 0);
seedParam.parameterKind = ParameterKind.POSITIONAL;
seedParam.type = provider.intType;
randomConstructor.parameters = <ParameterElement>[seedParam];
randomElement.constructors = <ConstructorElement>[randomConstructor];
FunctionElement sinElement = ElementFactory.functionElement3(
"sin",
provider.doubleType,
<ClassElement>[provider.numType.element],
const <ClassElement>[]);
FunctionElement sqrtElement = ElementFactory.functionElement3(
"sqrt",
provider.doubleType,
<ClassElement>[provider.numType.element],
const <ClassElement>[]);
mathUnit.accessors = <PropertyAccessorElement>[
ln10Element.getter,
piElement.getter
];
mathUnit.functions = <FunctionElement>[
cosElement,
maxElement,
sinElement,
sqrtElement
];
mathUnit.topLevelVariables = <TopLevelVariableElement>[
ln10Element,
piElement
];
mathUnit.types = <ClassElement>[randomElement];
LibraryElementImpl mathLibrary = new LibraryElementImpl.forNode(
coreContext, AstTestFactory.libraryIdentifier2(["dart", "math"]));
mathLibrary.definingCompilationUnit = mathUnit;
//
// Set empty sources for the rest of the libraries.
//
Source source = sourceFactory.forUri(_DART_INTERCEPTORS);
coreContext.setContents(source, "");
source = sourceFactory.forUri(_DART_JS_HELPER);
coreContext.setContents(source, "");
//
// Record the elements.
//
Map<Source, LibraryElement> elementMap =
new HashMap<Source, LibraryElement>();
elementMap[coreSource] = coreLibrary;
if (asyncSource != null) {
elementMap[asyncSource] = asyncLibrary;
}
elementMap[htmlSource] = htmlLibrary;
elementMap[mathSource] = mathLibrary;
//
// Set the public and export namespaces. We don't use exports in the fake
// core library so public and export namespaces are the same.
//
for (LibraryElementImpl library in elementMap.values) {
Namespace namespace =
new NamespaceBuilder().createPublicNamespaceForLibrary(library);
library.exportNamespace = namespace;
library.publicNamespace = namespace;
}
context.recordLibraryElements(elementMap);
// Create the synthetic element for `loadLibrary`.
for (LibraryElementImpl library in elementMap.values) {
library.createLoadLibraryFunction(context.typeProvider);
}
return context;
}
}
/**
* An analysis context that has a fake SDK that is much smaller and faster for
* testing purposes.
*/
class AnalysisContextForTests extends AnalysisContextImpl {
@override
void set analysisOptions(AnalysisOptions options) {
AnalysisOptions currentOptions = analysisOptions;
bool needsRecompute = currentOptions.analyzeFunctionBodiesPredicate !=
options.analyzeFunctionBodiesPredicate ||
currentOptions.generateImplicitErrors !=
options.generateImplicitErrors ||
currentOptions.generateSdkErrors != options.generateSdkErrors ||
currentOptions.dart2jsHint != options.dart2jsHint ||
(currentOptions.hint && !options.hint) ||
currentOptions.preserveComments != options.preserveComments;
if (needsRecompute) {
fail(
"Cannot set options that cause the sources to be reanalyzed in a test context");
}
super.analysisOptions = options;
}
@override
bool exists(Source source) =>
super.exists(source) || sourceFactory.dartSdk.context.exists(source);
@override
TimestampedData<String> getContents(Source source) {
if (source.isInSystemLibrary) {
return sourceFactory.dartSdk.context.getContents(source);
}
return super.getContents(source);
}
@override
int getModificationStamp(Source source) {
if (source.isInSystemLibrary) {
return sourceFactory.dartSdk.context.getModificationStamp(source);
}
return super.getModificationStamp(source);
}
/**
* Set the analysis options, even if they would force re-analysis. This method should only be
* invoked before the fake SDK is initialized.
*
* @param options the analysis options to be set
*/
void _internalSetAnalysisOptions(AnalysisOptions options) {
super.analysisOptions = options;
}
}
/**
* Helper for creating and managing single [AnalysisContext].
*/
class AnalysisContextHelper {
MemoryResourceProvider resourceProvider;
AnalysisContext context;
/**
* Creates new [AnalysisContext] using [AnalysisContextFactory].
*/
AnalysisContextHelper(
[AnalysisOptionsImpl options, MemoryResourceProvider provider]) {
resourceProvider = provider ?? new MemoryResourceProvider();
context = AnalysisContextFactory.contextWithCoreAndOptions(
options ?? new AnalysisOptionsImpl(),
resourceProvider: resourceProvider);
}
Source addSource(String path, String code) {
Source source = resourceProvider
.getFile(resourceProvider.convertPath(path))
.createSource();
if (path.endsWith(".dart") || path.endsWith(".html")) {
ChangeSet changeSet = new ChangeSet();
changeSet.addedSource(source);
context.applyChanges(changeSet);
}
context.setContents(source, code);
return source;
}
CompilationUnitElement getDefiningUnitElement(Source source) =>
context.getCompilationUnitElement(source, source);
CompilationUnit resolveDefiningUnit(Source source) {
LibraryElement libraryElement = context.computeLibraryElement(source);
return context.resolveCompilationUnit(source, libraryElement);
}
void runTasks() {
AnalysisResult result = context.performAnalysisTask();
while (result.changeNotices != null) {
result = context.performAnalysisTask();
}
}
}
class TestPackageUriResolver extends UriResolver {
Map<String, Source> sourceMap = new HashMap<String, Source>();
TestPackageUriResolver(Map<String, String> map) {
map.forEach((String uri, String contents) {
sourceMap[uri] = new StringSource(contents, '/test_pkg_source.dart');
});
}
@override
Source resolveAbsolute(Uri uri, [Uri actualUri]) {
String uriString = uri.toString();
return sourceMap[uriString];
}
@override
Uri restoreAbsolute(Source source) => throw new UnimplementedError();
}
class _AnalysisContextFactory_initContextWithCore extends FolderBasedDartSdk {
_AnalysisContextFactory_initContextWithCore(
ResourceProvider resourceProvider, String sdkPath)
: super(resourceProvider, resourceProvider.getFolder(sdkPath));
@override
LibraryMap initialLibraryMap(bool useDart2jsPaths) {
LibraryMap map = new LibraryMap();
_addLibrary(map, DartSdk.DART_ASYNC, false, "async.dart");
_addLibrary(map, DartSdk.DART_CORE, false, "core.dart");
_addLibrary(map, DartSdk.DART_HTML, false, "html_dartium.dart");
_addLibrary(map, AnalysisContextFactory._DART_MATH, false, "math.dart");
_addLibrary(map, AnalysisContextFactory._DART_INTERCEPTORS, true,
"_interceptors.dart");
_addLibrary(
map, AnalysisContextFactory._DART_JS_HELPER, true, "_js_helper.dart");
return map;
}
void _addLibrary(LibraryMap map, String uri, bool isInternal, String path) {
SdkLibraryImpl library = new SdkLibraryImpl(uri);
if (isInternal) {
library.category = "Internal";
}
library.path = path;
map.setLibrary(uri, library);
}
}