blob: a9090d24fb66932af23c865911c8082c846ef11f [file] [log] [blame]
// Copyright (c) 2017, 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:async';
import 'dart:collection';
import 'package:analyzer/dart/analysis/declared_variables.dart';
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/ast/visitor.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/type.dart';
import 'package:analyzer/error/error.dart';
import 'package:analyzer/error/listener.dart';
import 'package:analyzer/src/context/context.dart';
import 'package:analyzer/src/dart/analysis/file_state.dart';
import 'package:analyzer/src/dart/analysis/frontend_resolution.dart';
import 'package:analyzer/src/dart/ast/ast.dart';
import 'package:analyzer/src/dart/ast/utilities.dart';
import 'package:analyzer/src/dart/constant/evaluation.dart';
import 'package:analyzer/src/dart/constant/utilities.dart';
import 'package:analyzer/src/dart/element/element.dart';
import 'package:analyzer/src/dart/element/handle.dart';
import 'package:analyzer/src/dart/element/type.dart';
import 'package:analyzer/src/error/codes.dart';
import 'package:analyzer/src/error/pending_error.dart';
import 'package:analyzer/src/fasta/error_converter.dart';
import 'package:analyzer/src/fasta/resolution_applier.dart';
import 'package:analyzer/src/fasta/resolution_storer.dart' as kernel;
import 'package:analyzer/src/generated/declaration_resolver.dart';
import 'package:analyzer/src/generated/engine.dart';
import 'package:analyzer/src/generated/error_verifier.dart';
import 'package:analyzer/src/generated/resolver.dart';
import 'package:analyzer/src/generated/source.dart';
import 'package:analyzer/src/generated/utilities_dart.dart';
import 'package:analyzer/src/kernel/resynthesize.dart';
import 'package:analyzer/src/lint/linter.dart';
import 'package:analyzer/src/lint/linter_visitor.dart';
import 'package:analyzer/src/services/lint.dart';
import 'package:analyzer/src/task/dart.dart';
import 'package:analyzer/src/task/strong/checker.dart';
import 'package:front_end/src/base/performance_logger.dart';
import 'package:front_end/src/dependency_walker.dart';
import 'package:kernel/kernel.dart' as kernel;
import 'package:kernel/type_algebra.dart' as kernel;
/**
* Analyzer of a single library.
*/
class LibraryAnalyzer {
final PerformanceLog _logger;
final AnalysisOptions _analysisOptions;
final DeclaredVariables _declaredVariables;
final SourceFactory _sourceFactory;
final FileState _library;
final bool _enableKernelDriver;
final bool _useCFE;
final FrontEndCompiler _frontEndCompiler;
final bool Function(Uri) _isLibraryUri;
final AnalysisContextImpl _context;
final ElementResynthesizer _resynthesizer;
final TypeProvider _typeProvider;
LibraryElement _libraryElement;
final Map<FileState, LineInfo> _fileToLineInfo = {};
final Map<FileState, IgnoreInfo> _fileToIgnoreInfo = {};
final Map<FileState, RecordingErrorListener> _errorListeners = {};
final Map<FileState, ErrorReporter> _errorReporters = {};
final List<UsedImportedElements> _usedImportedElementsList = [];
final List<UsedLocalElements> _usedLocalElementsList = [];
final Map<FileState, List<PendingError>> _fileToPendingErrors = {};
final List<ConstantEvaluationTarget> _constants = [];
LibraryAnalyzer(
this._logger,
this._analysisOptions,
this._declaredVariables,
this._sourceFactory,
this._isLibraryUri,
this._context,
this._resynthesizer,
this._library,
{bool enableKernelDriver: false,
bool useCFE: false,
FrontEndCompiler frontEndCompiler})
: _typeProvider = _context.typeProvider,
_enableKernelDriver = enableKernelDriver,
_useCFE = useCFE,
_frontEndCompiler = frontEndCompiler;
/**
* Compute analysis results for all units of the library.
*/
Future<Map<FileState, UnitAnalysisResult>> analyze() async {
// TODO(brianwilkerson) Determine whether this await is necessary.
await null;
return PerformanceStatistics.analysis.makeCurrentWhileAsync(() async {
// TODO(brianwilkerson) Determine whether this await is necessary.
await null;
if (_useCFE) {
return await _analyze2();
} else {
return _analyze();
}
});
}
Map<FileState, UnitAnalysisResult> _analyze() {
Map<FileState, CompilationUnit> units = {};
// Parse all files.
units[_library] = _parse(_library);
for (FileState part in _library.partedFiles) {
units[part] = _parse(part);
}
// Resolve URIs in directives to corresponding sources.
units.forEach((file, unit) {
_resolveUriBasedDirectives(file, unit);
});
try {
_libraryElement = _resynthesizer
.getElement(new ElementLocationImpl.con3([_library.uriStr]));
_resolveDirectives(units);
units.forEach((file, unit) {
_resolveFile(file, unit);
_computePendingMissingRequiredParameters(file, unit);
});
_computeConstants();
PerformanceStatistics.errors.makeCurrentWhile(() {
units.forEach((file, unit) {
_computeVerifyErrors(file, unit);
});
});
if (_analysisOptions.hint) {
PerformanceStatistics.hints.makeCurrentWhile(() {
units.forEach((file, unit) {
{
var visitor = new GatherUsedLocalElementsVisitor(_libraryElement);
unit.accept(visitor);
_usedLocalElementsList.add(visitor.usedElements);
}
{
var visitor =
new GatherUsedImportedElementsVisitor(_libraryElement);
unit.accept(visitor);
_usedImportedElementsList.add(visitor.usedElements);
}
});
units.forEach((file, unit) {
_computeHints(file, unit);
});
});
}
if (_analysisOptions.lint) {
PerformanceStatistics.lints.makeCurrentWhile(() {
units.forEach((file, unit) {
_computeLints(file, unit);
});
});
}
} finally {
_context.dispose();
}
// Return full results.
Map<FileState, UnitAnalysisResult> results = {};
units.forEach((file, unit) {
List<AnalysisError> errors = _getErrorListener(file).errors;
errors = _filterIgnoredErrors(file, errors);
results[file] = new UnitAnalysisResult(file, unit, errors);
});
return results;
}
Future<Map<FileState, UnitAnalysisResult>> _analyze2() async {
// TODO(brianwilkerson) Determine whether this await is necessary.
await null;
return await _logger.runAsync('Analyze', () async {
// TODO(brianwilkerson) Determine whether this await is necessary.
await null;
Map<FileState, CompilationUnit> units = {};
// Parse all files.
_logger.run('Parse units', () {
units[_library] = _parse(_library);
for (FileState part in _library.partedFiles) {
units[part] = _parse(part);
}
});
// Resolve URIs in directives to corresponding sources.
units.forEach((file, unit) {
_resolveUriBasedDirectives(file, unit);
});
try {
_libraryElement = _resynthesizer
.getElement(new ElementLocationImpl.con3([_library.uriStr]));
_resolveDirectives(units);
var libraryResult = await _logger.runAsync('Compile library', () {
return _frontEndCompiler.compile(_library.uri);
});
_logger.run('Apply resolution', () {
units.forEach((file, unit) {
var resolutions = libraryResult.files[file.fileUri].resolutions;
var resolutionProvider = new _ResolutionProvider(resolutions);
_resolveFile2(file, unit, resolutionProvider);
_computePendingMissingRequiredParameters(file, unit);
// Invalid part URIs can result in an element with a null source
if (unit.element.source != null) {
var reporter = new FastaErrorReporter(_getErrorReporter(file));
var fileResult = libraryResult.files[file.fileUri];
fileResult?.errors?.forEach(reporter.reportCompilationMessage);
}
});
});
_computeConstants();
if (_analysisOptions.hint) {
PerformanceStatistics.hints.makeCurrentWhile(() {
units.forEach((file, unit) {
{
var visitor =
new GatherUsedLocalElementsVisitor(_libraryElement);
unit.accept(visitor);
_usedLocalElementsList.add(visitor.usedElements);
}
{
var visitor =
new GatherUsedImportedElementsVisitor(_libraryElement);
unit.accept(visitor);
_usedImportedElementsList.add(visitor.usedElements);
}
});
units.forEach((file, unit) {
_computeHints(file, unit);
});
});
}
if (_analysisOptions.lint) {
PerformanceStatistics.lints.makeCurrentWhile(() {
units.forEach((file, unit) {
_computeLints(file, unit);
});
});
}
} finally {
_context.dispose();
}
// Return full results.
Map<FileState, UnitAnalysisResult> results = {};
units.forEach((file, unit) {
List<AnalysisError> errors = _getErrorListener(file).errors;
errors = _filterIgnoredErrors(file, errors);
results[file] = new UnitAnalysisResult(file, unit, errors);
});
return results;
});
}
/**
* Compute [_constants] in all units.
*/
void _computeConstants() {
ConstantEvaluationEngine evaluationEngine = new ConstantEvaluationEngine(
_typeProvider, _declaredVariables,
typeSystem: _context.typeSystem);
List<_ConstantNode> nodes = [];
Map<ConstantEvaluationTarget, _ConstantNode> nodeMap = {};
for (ConstantEvaluationTarget constant in _constants) {
var node = new _ConstantNode(evaluationEngine, nodeMap, constant);
nodes.add(node);
nodeMap[constant] = node;
}
for (_ConstantNode node in nodes) {
if (!node.isEvaluated) {
new _ConstantWalker(evaluationEngine).walk(node);
}
}
}
void _computeHints(FileState file, CompilationUnit unit) {
if (file.source == null) {
return;
}
AnalysisErrorListener errorListener = _getErrorListener(file);
ErrorReporter errorReporter = _getErrorReporter(file);
//
// Convert the pending errors into actual errors.
//
for (PendingError pendingError in _fileToPendingErrors[file]) {
errorListener.onError(pendingError.toAnalysisError());
}
unit.accept(
new DeadCodeVerifier(errorReporter, typeSystem: _context.typeSystem));
// Dart2js analysis.
if (_analysisOptions.dart2jsHint) {
unit.accept(new Dart2JSVerifier(errorReporter));
}
InheritanceManager inheritanceManager = new InheritanceManager(
_libraryElement,
includeAbstractFromSuperclasses: true);
unit.accept(new BestPracticesVerifier(
errorReporter, _typeProvider, _libraryElement, inheritanceManager,
typeSystem: _context.typeSystem));
unit.accept(new OverrideVerifier(errorReporter, inheritanceManager));
new ToDoFinder(errorReporter).findIn(unit);
// Verify imports.
{
ImportsVerifier verifier = new ImportsVerifier();
verifier.addImports(unit);
_usedImportedElementsList.forEach(verifier.removeUsedElements);
verifier.generateDuplicateImportHints(errorReporter);
verifier.generateDuplicateShownHiddenNameHints(errorReporter);
verifier.generateUnusedImportHints(errorReporter);
verifier.generateUnusedShownNameHints(errorReporter);
}
// Unused local elements.
{
UsedLocalElements usedElements =
new UsedLocalElements.merge(_usedLocalElementsList);
UnusedLocalElementsVerifier visitor =
new UnusedLocalElementsVerifier(errorListener, usedElements);
unit.accept(visitor);
}
}
void _computeLints(FileState file, CompilationUnit unit) {
if (file.source == null) {
return;
}
ErrorReporter errorReporter = _getErrorReporter(file);
var nodeRegistry = new NodeLintRegistry(_analysisOptions.enableTiming);
var visitors = <AstVisitor>[];
for (Linter linter in _analysisOptions.lintRules) {
linter.reporter = errorReporter;
if (linter is NodeLintRule) {
(linter as NodeLintRule).registerNodeProcessors(nodeRegistry);
} else {
AstVisitor visitor = linter.getVisitor();
if (visitor != null) {
if (_analysisOptions.enableTiming) {
var timer = lintRegistry.getTimer(linter);
visitor = new TimedAstVisitor(visitor, timer);
}
visitors.add(visitor);
}
}
}
// Run lints that handle specific node types.
unit.accept(new LinterVisitor(
nodeRegistry, ExceptionHandlingDelegatingAstVisitor.logException));
// Run visitor based lints.
if (visitors.isNotEmpty) {
AstVisitor visitor = new ExceptionHandlingDelegatingAstVisitor(
visitors, ExceptionHandlingDelegatingAstVisitor.logException);
unit.accept(visitor);
}
}
void _computePendingMissingRequiredParameters(
FileState file, CompilationUnit unit) {
// TODO(scheglov) This can be done without "pending" if we resynthesize.
var computer = new RequiredConstantsComputer(file.source);
unit.accept(computer);
_constants.addAll(computer.requiredConstants);
_fileToPendingErrors[file] = computer.pendingErrors;
}
void _computeVerifyErrors(FileState file, CompilationUnit unit) {
if (file.source == null) {
return;
}
RecordingErrorListener errorListener = _getErrorListener(file);
if (_analysisOptions.strongMode) {
AnalysisOptionsImpl options = _analysisOptions as AnalysisOptionsImpl;
CodeChecker checker = new CodeChecker(
_typeProvider,
new StrongTypeSystemImpl(_typeProvider,
implicitCasts: options.implicitCasts,
declarationCasts: options.declarationCasts,
nonnullableTypes: options.nonnullableTypes),
errorListener,
options);
checker.visitCompilationUnit(unit);
}
ErrorReporter errorReporter = _getErrorReporter(file);
//
// Validate the directives.
//
_validateUriBasedDirectives(file, unit);
//
// Use the ConstantVerifier to compute errors.
//
ConstantVerifier constantVerifier = new ConstantVerifier(
errorReporter, _libraryElement, _typeProvider, _declaredVariables);
unit.accept(constantVerifier);
//
// Use the ErrorVerifier to compute errors.
//
ErrorVerifier errorVerifier = new ErrorVerifier(
errorReporter,
_libraryElement,
_typeProvider,
new InheritanceManager(_libraryElement),
_analysisOptions.enableSuperMixins);
unit.accept(errorVerifier);
}
/// Create a new [ResolutionApplier] for the given front-end [resolution].
/// The [context] element is used to associate synthetic elements and access
/// type parameters from the enclosing scopes.
ResolutionApplier _createResolutionApplier(
ElementImpl context, CollectedResolution resolution) {
return new _ResolutionApplierContext(
_resynthesizer, _typeProvider, _libraryElement, resolution, context)
.applier;
}
/**
* Return a subset of the given [errors] that are not marked as ignored in
* the [file].
*/
List<AnalysisError> _filterIgnoredErrors(
FileState file, List<AnalysisError> errors) {
if (errors.isEmpty) {
return errors;
}
IgnoreInfo ignoreInfo = _fileToIgnoreInfo[file];
if (!ignoreInfo.hasIgnores) {
return errors;
}
LineInfo lineInfo = _fileToLineInfo[file];
bool isIgnored(AnalysisError error) {
int errorLine = lineInfo.getLocation(error.offset).lineNumber;
String errorCode = error.errorCode.name.toLowerCase();
return ignoreInfo.ignoredAt(errorCode, errorLine);
}
return errors.where((AnalysisError e) => !isIgnored(e)).toList();
}
RecordingErrorListener _getErrorListener(FileState file) =>
_errorListeners.putIfAbsent(file, () => new RecordingErrorListener());
ErrorReporter _getErrorReporter(FileState file) {
return _errorReporters.putIfAbsent(file, () {
RecordingErrorListener listener = _getErrorListener(file);
return new ErrorReporter(listener, file.source);
});
}
/**
* Return the name of the library that the given part is declared to be a
* part of, or `null` if the part does not contain a part-of directive.
*/
_NameOrSource _getPartLibraryNameOrUri(Source partSource,
CompilationUnit partUnit, List<Directive> directivesToResolve) {
for (Directive directive in partUnit.directives) {
if (directive is PartOfDirective) {
directivesToResolve.add(directive);
LibraryIdentifier libraryName = directive.libraryName;
if (libraryName != null) {
return new _NameOrSource(libraryName.name, null);
}
String uri = directive.uri?.stringValue;
if (uri != null) {
Source librarySource = _sourceFactory.resolveUri(partSource, uri);
if (librarySource != null) {
return new _NameOrSource(null, librarySource);
}
}
}
}
return null;
}
/**
* Return `true` if the given [source] is a library.
*/
bool _isLibrarySource(Source source) {
return _isLibraryUri(source.uri);
}
/**
* Return a new parsed unresolved [CompilationUnit].
*/
CompilationUnit _parse(FileState file) {
AnalysisErrorListener errorListener =
_useCFE ? AnalysisErrorListener.NULL_LISTENER : _getErrorListener(file);
String content = file.content;
CompilationUnit unit = file.parse(errorListener);
LineInfo lineInfo = unit.lineInfo;
_fileToLineInfo[file] = lineInfo;
_fileToIgnoreInfo[file] = IgnoreInfo.calculateIgnores(content, lineInfo);
return unit;
}
void _resolveDirectives(Map<FileState, CompilationUnit> units) {
CompilationUnit definingCompilationUnit = units[_library];
definingCompilationUnit.element = _libraryElement.definingCompilationUnit;
bool matchNodeElement(Directive node, Element element) {
if (_enableKernelDriver) {
return node.keyword.offset == element.nameOffset;
} else {
return node.offset == element.nameOffset;
}
}
ErrorReporter libraryErrorReporter = _getErrorReporter(_library);
LibraryIdentifier libraryNameNode = null;
var seenPartSources = new Set<Source>();
var directivesToResolve = <Directive>[];
int partIndex = 0;
for (Directive directive in definingCompilationUnit.directives) {
if (directive is LibraryDirective) {
libraryNameNode = directive.name;
directivesToResolve.add(directive);
} else if (directive is ImportDirective) {
for (ImportElement importElement in _libraryElement.imports) {
if (matchNodeElement(directive, importElement)) {
directive.element = importElement;
directive.prefix?.staticElement = importElement.prefix;
Source source = importElement.importedLibrary?.source;
if (source != null && !_isLibrarySource(source)) {
ErrorCode errorCode = importElement.isDeferred
? StaticWarningCode.IMPORT_OF_NON_LIBRARY
: CompileTimeErrorCode.IMPORT_OF_NON_LIBRARY;
libraryErrorReporter.reportErrorForNode(
errorCode, directive.uri, [directive.uri]);
}
}
}
} else if (directive is ExportDirective) {
for (ExportElement exportElement in _libraryElement.exports) {
if (matchNodeElement(directive, exportElement)) {
directive.element = exportElement;
Source source = exportElement.exportedLibrary?.source;
if (source != null && !_isLibrarySource(source)) {
libraryErrorReporter.reportErrorForNode(
CompileTimeErrorCode.EXPORT_OF_NON_LIBRARY,
directive.uri,
[directive.uri]);
}
}
}
} else if (directive is PartDirective) {
StringLiteral partUri = directive.uri;
FileState partFile = _library.partedFiles[partIndex];
CompilationUnit partUnit = units[partFile];
CompilationUnitElement partElement = _libraryElement.parts[partIndex];
partUnit.element = partElement;
directive.element = partElement;
partIndex++;
Source partSource = directive.uriSource;
if (partSource == null) {
continue;
}
//
// Validate that the part source is unique in the library.
//
if (!seenPartSources.add(partSource)) {
libraryErrorReporter.reportErrorForNode(
CompileTimeErrorCode.DUPLICATE_PART, partUri, [partSource.uri]);
}
//
// Validate that the part contains a part-of directive with the same
// name or uri as the library.
//
if (_context.exists(partSource)) {
_NameOrSource nameOrSource = _getPartLibraryNameOrUri(
partSource, partUnit, directivesToResolve);
if (nameOrSource == null) {
libraryErrorReporter.reportErrorForNode(
CompileTimeErrorCode.PART_OF_NON_PART,
partUri,
[partUri.toSource()]);
} else {
String name = nameOrSource.name;
if (name != null) {
if (libraryNameNode == null) {
libraryErrorReporter.reportErrorForNode(
ResolverErrorCode.PART_OF_UNNAMED_LIBRARY, partUri, [name]);
} else if (libraryNameNode.name != name) {
libraryErrorReporter.reportErrorForNode(
StaticWarningCode.PART_OF_DIFFERENT_LIBRARY,
partUri,
[libraryNameNode.name, name]);
}
} else {
Source source = nameOrSource.source;
if (source != _library.source) {
libraryErrorReporter.reportErrorForNode(
StaticWarningCode.PART_OF_DIFFERENT_LIBRARY,
partUri,
[_library.uriStr, source.uri]);
}
}
}
}
}
}
// TODO(brianwilkerson) Report the error
// ResolverErrorCode.MISSING_LIBRARY_DIRECTIVE_WITH_PART
//
// Resolve the relevant directives to the library element.
//
for (Directive directive in directivesToResolve) {
directive.element = _libraryElement;
}
// TODO(scheglov) remove DirectiveResolver class
}
void _resolveFile(FileState file, CompilationUnit unit) {
Source source = file.source;
if (source == null) {
return;
}
RecordingErrorListener errorListener = _getErrorListener(file);
CompilationUnitElement unitElement = unit.element;
// TODO(scheglov) Hack: set types for top-level variables
// Otherwise TypeResolverVisitor will set declared types, and because we
// don't run InferStaticVariableTypeTask, we will stuck with these declared
// types. And we don't need to run this task - resynthesized elements have
// inferred types.
for (var e in unitElement.topLevelVariables) {
if (!e.isSynthetic) {
e.type;
}
}
new DeclarationResolver(enableKernelDriver: _enableKernelDriver)
.resolve(unit, unitElement);
if (_libraryElement.context.analysisOptions.previewDart2) {
unit.accept(new AstRewriteVisitor(_context.typeSystem, _libraryElement,
source, _typeProvider, AnalysisErrorListener.NULL_LISTENER));
}
// TODO(scheglov) remove EnumMemberBuilder class
new TypeParameterBoundsResolver(
_context.typeSystem, _libraryElement, source, errorListener)
.resolveTypeBounds(unit);
unit.accept(new TypeResolverVisitor(
_libraryElement, source, _typeProvider, errorListener));
LibraryScope libraryScope = new LibraryScope(_libraryElement);
unit.accept(new VariableResolverVisitor(
_libraryElement, source, _typeProvider, errorListener,
nameScope: libraryScope));
unit.accept(new PartialResolverVisitor(_libraryElement, source,
_typeProvider, AnalysisErrorListener.NULL_LISTENER));
// Nothing for RESOLVED_UNIT8?
// Nothing for RESOLVED_UNIT9?
// Nothing for RESOLVED_UNIT10?
unit.accept(new ResolverVisitor(
_libraryElement, source, _typeProvider, errorListener));
//
// Find constants to compute.
//
{
ConstantFinder constantFinder = new ConstantFinder();
unit.accept(constantFinder);
_constants.addAll(constantFinder.constantsToCompute);
}
//
// Find constant dependencies to compute.
//
{
var finder = new ConstantExpressionsDependenciesFinder();
unit.accept(finder);
_constants.addAll(finder.dependencies);
}
}
void _resolveFile2(
FileState file, CompilationUnit unit, _ResolutionProvider resolutions) {
CompilationUnitElement unitElement = unit.element;
new DeclarationResolver(enableKernelDriver: true, applyKernelTypes: true)
.resolve(unit, unitElement);
if (_libraryElement.context.analysisOptions.previewDart2) {
unit.accept(new AstRewriteVisitor(_context.typeSystem, _libraryElement,
file.source, _typeProvider, AnalysisErrorListener.NULL_LISTENER));
}
for (var declaration in unit.declarations) {
if (declaration is ClassDeclaration) {
if (declaration.metadata.isNotEmpty) {
var resolution = resolutions.next();
var applier = _createResolutionApplier(null, resolution);
applier.applyToAnnotations(declaration);
applier.checkDone();
}
for (var member in declaration.members) {
if (member is ConstructorDeclaration) {
var context = member.element as ConstructorElementImpl;
ConstructorName redirectName = member.redirectedConstructor;
if (redirectName != null) {
var redirectedConstructor = context.redirectedConstructor;
redirectName.staticElement = redirectedConstructor;
// TODO(scheglov) Support for import prefix?
ResolutionApplier.applyConstructorElement(
_libraryElement,
null,
redirectedConstructor,
redirectedConstructor.returnType,
redirectName);
// TODO(scheglov) Add support for type parameterized redirects.
} else {
var resolution = resolutions.next();
var applier = _createResolutionApplier(context, resolution);
member.initializers.accept(applier);
member.parameters.accept(applier);
member.body.accept(applier);
applier.applyToAnnotations(member);
applier.checkDone();
}
} else if (member is FieldDeclaration) {
List<VariableDeclaration> fields = member.fields.variables;
var context = fields[0].element as ElementImpl;
var resolution = resolutions.next();
var applier = _createResolutionApplier(context, resolution);
for (var field in fields.reversed) {
field.initializer?.accept(applier);
}
applier.applyToAnnotations(member);
applier.checkDone();
} else if (member is MethodDeclaration) {
ExecutableElementImpl context = member.element;
var resolution = resolutions.next();
var applier = _createResolutionApplier(context, resolution);
member.parameters?.accept(applier);
member.body.accept(applier);
applier.applyToAnnotations(member);
applier.checkDone();
} else {
throw new StateError('(${declaration.runtimeType}) $declaration');
}
}
} else if (declaration is ClassTypeAlias) {
// No bodies to resolve.
} else if (declaration is EnumDeclaration) {
// No bodies to resolve.
} else if (declaration is FunctionDeclaration) {
var context = declaration.element as ExecutableElementImpl;
var resolution = resolutions.next();
var applier = _createResolutionApplier(context, resolution);
declaration.functionExpression.parameters?.accept(applier);
declaration.functionExpression.body.accept(applier);
applier.applyToAnnotations(declaration);
applier.checkDone();
} else if (declaration is FunctionTypeAlias) {
// No bodies to resolve.
} else if (declaration is GenericTypeAlias) {
// No bodies to resolve.
} else if (declaration is TopLevelVariableDeclaration) {
List<VariableDeclaration> variables = declaration.variables.variables;
var context = variables[0].element as ElementImpl;
var resolution = resolutions.next();
var applier = _createResolutionApplier(context, resolution);
for (var variable in variables.reversed) {
variable.initializer?.accept(applier);
}
applier.applyToAnnotations(declaration);
applier.checkDone();
} else {
throw new StateError('(${declaration.runtimeType}) $declaration');
}
}
//
// Find constants to compute.
//
{
ConstantFinder constantFinder = new ConstantFinder();
unit.accept(constantFinder);
_constants.addAll(constantFinder.constantsToCompute);
}
//
// Find constant dependencies to compute.
//
{
var finder = new ConstantExpressionsDependenciesFinder();
unit.accept(finder);
_constants.addAll(finder.dependencies);
}
}
/**
* Return the result of resolve the given [uriContent], reporting errors
* against the [uriLiteral].
*/
Source _resolveUri(FileState file, bool isImport, StringLiteral uriLiteral,
String uriContent) {
UriValidationCode code =
UriBasedDirectiveImpl.validateUri(isImport, uriLiteral, uriContent);
if (code == null) {
try {
Uri.parse(uriContent);
} on FormatException {
return null;
}
return _sourceFactory.resolveUri(file.source, uriContent);
} else if (code == UriValidationCode.URI_WITH_DART_EXT_SCHEME) {
return null;
} else if (code == UriValidationCode.URI_WITH_INTERPOLATION) {
_getErrorReporter(file).reportErrorForNode(
CompileTimeErrorCode.URI_WITH_INTERPOLATION, uriLiteral);
return null;
} else if (code == UriValidationCode.INVALID_URI) {
_getErrorReporter(file).reportErrorForNode(
CompileTimeErrorCode.INVALID_URI, uriLiteral, [uriContent]);
return null;
}
return null;
}
void _resolveUriBasedDirectives(FileState file, CompilationUnit unit) {
for (Directive directive in unit.directives) {
if (directive is UriBasedDirective) {
StringLiteral uriLiteral = directive.uri;
String uriContent = uriLiteral.stringValue?.trim();
directive.uriContent = uriContent;
Source defaultSource = _resolveUri(
file, directive is ImportDirective, uriLiteral, uriContent);
directive.uriSource = defaultSource;
}
}
}
/**
* Check the given [directive] to see if the referenced source exists and
* report an error if it does not.
*/
void _validateUriBasedDirective(
FileState file, UriBasedDirectiveImpl directive) {
Source source = directive.uriSource;
if (source != null) {
if (_context.exists(source)) {
return;
}
} else {
// Don't report errors already reported by ParseDartTask.resolveDirective
// TODO(scheglov) we don't use this task here
if (directive.validate() != null) {
return;
}
}
StringLiteral uriLiteral = directive.uri;
CompileTimeErrorCode errorCode = CompileTimeErrorCode.URI_DOES_NOT_EXIST;
if (_isGenerated(source)) {
errorCode = CompileTimeErrorCode.URI_HAS_NOT_BEEN_GENERATED;
}
_getErrorReporter(file)
.reportErrorForNode(errorCode, uriLiteral, [directive.uriContent]);
}
/**
* Check each directive in the given [unit] to see if the referenced source
* exists and report an error if it does not.
*/
void _validateUriBasedDirectives(FileState file, CompilationUnit unit) {
for (Directive directive in unit.directives) {
if (directive is UriBasedDirective) {
_validateUriBasedDirective(file, directive);
}
}
}
/**
* Return `true` if the given [source] refers to a file that is assumed to be
* generated.
*/
static bool _isGenerated(Source source) {
if (source == null) {
return false;
}
// TODO(brianwilkerson) Generalize this mechanism.
const List<String> suffixes = const <String>[
'.g.dart',
'.pb.dart',
'.pbenum.dart',
'.pbserver.dart',
'.pbjson.dart',
'.template.dart'
];
String fullName = source.fullName;
for (String suffix in suffixes) {
if (fullName.endsWith(suffix)) {
return true;
}
}
return false;
}
}
/**
* Analysis result for single file.
*/
class UnitAnalysisResult {
final FileState file;
final CompilationUnit unit;
final List<AnalysisError> errors;
UnitAnalysisResult(this.file, this.unit, this.errors);
}
/**
* [Node] that is used to compute constants in dependency order.
*/
class _ConstantNode extends Node<_ConstantNode> {
final ConstantEvaluationEngine evaluationEngine;
final Map<ConstantEvaluationTarget, _ConstantNode> nodeMap;
final ConstantEvaluationTarget constant;
bool isEvaluated = false;
_ConstantNode(this.evaluationEngine, this.nodeMap, this.constant);
@override
List<_ConstantNode> computeDependencies() {
List<ConstantEvaluationTarget> targets = [];
evaluationEngine.computeDependencies(constant, targets.add);
return targets.map(_getNode).toList();
}
_ConstantNode _getNode(ConstantEvaluationTarget constant) {
return nodeMap.putIfAbsent(
constant, () => new _ConstantNode(evaluationEngine, nodeMap, constant));
}
}
/**
* [DependencyWalker] for computing constants and detecting cycles.
*/
class _ConstantWalker extends DependencyWalker<_ConstantNode> {
final ConstantEvaluationEngine evaluationEngine;
_ConstantWalker(this.evaluationEngine);
@override
void evaluate(_ConstantNode node) {
evaluationEngine.computeConstantValue(node.constant);
node.isEvaluated = true;
}
@override
void evaluateScc(List<_ConstantNode> scc) {
var constantsInCycle = scc.map((node) => node.constant);
for (_ConstantNode node in scc) {
if (node.constant is ConstructorElementImpl) {
(node.constant as ConstructorElementImpl).isCycleFree = false;
}
evaluationEngine.generateCycleError(constantsInCycle, node.constant);
node.isEvaluated = true;
}
}
}
/**
* Either the name or the source associated with a part-of directive.
*/
class _NameOrSource {
final String name;
final Source source;
_NameOrSource(this.name, this.source);
}
/// Concrete implementation of [TypeContext].
class _ResolutionApplierContext implements TypeContext {
final KernelResynthesizer resynthesizer;
final TypeProvider typeProvider;
final LibraryElement libraryElement;
final CollectedResolution resolution;
@override
ClassElement enclosingClassElement;
List<ElementImpl> contextStack = [];
ElementImpl context;
List<Element> declaredElements = [];
Map<kernel.TreeNode, Element> declarationToElement = new HashMap.identity();
Map<FunctionElementImpl, kernel.TreeNode> functionElementToDeclaration =
new HashMap.identity();
Map<ParameterElementImpl, kernel.VariableDeclaration>
parameterElementToDeclaration = new HashMap.identity();
ResolutionApplier applier;
_ResolutionApplierContext(this.resynthesizer, this.typeProvider,
this.libraryElement, this.resolution, this.context) {
for (Element element = context;
element != null;
element = element.enclosingElement) {
if (element is ClassElement) {
enclosingClassElement = element;
break;
}
}
// Convert local declarations into elements.
for (var declaredNode in resolution.kernelDeclarations) {
translateKernelDeclaration(declaredNode);
}
// Convert referenced nodes into elements.
List<Element> referencedElements = [];
for (var referencedNode in resolution.kernelReferences) {
Element element;
if (referencedNode is kernel.VariableDeclaration) {
kernel.TreeNode parent = referencedNode.parent;
if (parent is kernel.Statement) {
element = declarationToElement[referencedNode];
} else {
assert(parent is kernel.FunctionNode || parent is kernel.Catch);
// Might be a parameter of a local function.
element = declarationToElement[referencedNode];
// If no element, then it is a parameter of the context executable.
if (element == null) {
ExecutableElementImpl contextExecutable = context;
for (var parameter in contextExecutable.parameters) {
if (parameter.name == referencedNode.name) {
element = parameter;
break;
}
}
}
}
assert(element != null);
} else if (referencedNode is kernel.NamedNode) {
element = resynthesizer
.getElementFromCanonicalName(referencedNode.canonicalName);
assert(element != null);
} else if (referencedNode is kernel.DynamicType) {
element = DynamicElementImpl.instance;
} else if (referencedNode is kernel.FunctionType) {
element = resynthesizer
.getElementFromCanonicalName(referencedNode.typedef.canonicalName);
assert(element != null);
} else if (referencedNode is kernel.InterfaceType) {
element = resynthesizer.getElementFromCanonicalName(
referencedNode.classNode.canonicalName);
assert(element != null);
} else if (referencedNode is kernel.MemberGetterNode) {
if (referencedNode.member == null) {
element = null;
} else {
var memberElement = resynthesizer
.getElementFromCanonicalName(referencedNode.member.canonicalName);
assert(memberElement != null);
if (memberElement is PropertyInducingElementImpl) {
element = memberElement.getter;
assert(element != null);
} else {
element = memberElement;
}
}
} else if (referencedNode is kernel.MemberSetterNode) {
if (referencedNode.member == null) {
element = null;
} else {
var memberElement = resynthesizer
.getElementFromCanonicalName(referencedNode.member.canonicalName);
assert(memberElement != null);
if (memberElement is PropertyInducingElementImpl) {
element = memberElement.setter;
assert(element != null);
} else {
element = memberElement;
}
}
} else if (referencedNode is kernel.ImportPrefixNode) {
assert(referencedNode.name != null);
for (var import in libraryElement.imports) {
if (import.prefix?.name == referencedNode.name) {
element = import.prefix;
break;
}
}
assert(element != null);
} else if (referencedNode is kernel.NullNode) {
element = null;
} else if (referencedNode == null) {
// This will occur if an identifier could not be resolved, such as a
// reference to a member when the target has type `dynamic`.
element = null;
} else {
throw new UnimplementedError(
'Declaration: (${referencedNode.runtimeType}) $referencedNode');
}
referencedElements.add(element);
}
applier = new ValidatingResolutionApplier(
libraryElement,
this,
declaredElements,
referencedElements,
resolution.kernelTypes,
resolution.declarationOffsets,
resolution.referenceOffsets,
resolution.typeOffsets);
}
@override
DartType get stringType => typeProvider.stringType;
@override
DartType get typeType => typeProvider.typeType;
@override
void encloseVariable(ElementImpl element) {
context.encloseElement(element);
}
@override
void enterLocalFunction(FunctionElementImpl element) {
context.encloseElement(element);
// The function is the new resolution context.
contextStack.add(context);
context = element;
var declaration = functionElementToDeclaration[element];
// Get the declaration kernel type.
kernel.FunctionType kernelType;
if (declaration is kernel.VariableDeclaration) {
kernelType = declaration.type;
} else if (declaration is kernel.FunctionExpression) {
kernelType = declaration.function.functionType;
} else {
throw new StateError('(${declaration.runtimeType}) $declaration');
}
element.returnType = resynthesizer.getType(context, kernelType.returnType);
for (var parameter in element.parameters) {
ParameterElementImpl parameterImpl = parameter;
var kernelParameter = parameterElementToDeclaration[parameter];
parameterImpl.type = resynthesizer.getType(context, kernelParameter.type);
}
element.type = new FunctionTypeImpl(element);
}
@override
void exitLocalFunction(FunctionElementImpl element) {
assert(identical(context, element));
context = contextStack.removeLast();
}
/// Translate the given [declaration].
void translateKernelDeclaration(kernel.TreeNode declaration) {
if (declaration is kernel.VariableDeclaration) {
kernel.TreeNode functionDeclaration = declaration.parent;
if (functionDeclaration is kernel.FunctionDeclaration) {
var element =
new FunctionElementImpl(declaration.name, declaration.fileOffset);
functionElementToDeclaration[element] = declaration;
_addFormalParameters(element, functionDeclaration.function);
declaredElements.add(element);
declarationToElement[declaration] = element;
} else {
// TODO(scheglov) Do we need ConstLocalVariableElementImpl?
var element = new LocalVariableElementImpl(
declaration.name, declaration.fileOffset);
declaredElements.add(element);
declarationToElement[declaration] = element;
}
} else if (declaration is kernel.FunctionExpression) {
var element = new FunctionElementImpl('', declaration.fileOffset);
functionElementToDeclaration[element] = declaration;
_addFormalParameters(element, declaration.function);
declaredElements.add(element);
declarationToElement[declaration] = element;
} else {
throw new UnimplementedError(
'Declaration: (${declaration.runtimeType}) $declaration');
}
}
@override
DartType translateType(kernel.DartType kernelType) {
if (kernelType is kernel.NullType) {
return null;
} else if (kernelType is kernel.IndexAssignNullFunctionType) {
return null;
} else if (kernelType is kernel.TypeArgumentsDartType) {
List<kernel.DartType> kernelTypes = kernelType.types;
var types = new List<DartType>(kernelTypes.length);
for (var i = 0; i < kernelTypes.length; i++) {
types[i] = translateType(kernelTypes[i]);
}
return new TypeArgumentsDartType(types);
} else {
return resynthesizer.getType(context, kernelType);
}
}
/// Add formal parameters defined in the [kernelFunction] to the [element].
void _addFormalParameters(
FunctionElementImpl element, kernel.FunctionNode kernelFunction) {
// Set type parameters.
{
var astParameters = <TypeParameterElement>[];
for (var kernelParameter in kernelFunction.typeParameters) {
var astParameter = new TypeParameterElementImpl(
kernelParameter.name, kernelParameter.fileOffset);
astParameter.type = new TypeParameterTypeImpl(astParameter);
// TODO(scheglov) remember mapping to set bounds later
astParameters.add(astParameter);
}
element.typeParameters = astParameters;
}
// Set formal parameters.
{
var astParameters = <ParameterElement>[];
// Add positional parameters
var kernelPositionalParameters = kernelFunction.positionalParameters;
for (var i = 0; i < kernelPositionalParameters.length; i++) {
var kernelParameter = kernelPositionalParameters[i];
var astParameter = new ParameterElementImpl(
kernelParameter.name, kernelParameter.fileOffset);
astParameter.parameterKind = i < kernelFunction.requiredParameterCount
? ParameterKind.REQUIRED
: ParameterKind.POSITIONAL;
astParameters.add(astParameter);
declarationToElement[kernelParameter] = astParameter;
parameterElementToDeclaration[astParameter] = kernelParameter;
}
// Add named parameters.
for (var kernelParameter in kernelFunction.namedParameters) {
var astParameter = new ParameterElementImpl(
kernelParameter.name, kernelParameter.fileOffset);
astParameter.parameterKind = ParameterKind.NAMED;
astParameters.add(astParameter);
declarationToElement[kernelParameter] = astParameter;
parameterElementToDeclaration[astParameter] = kernelParameter;
}
element.parameters = astParameters;
}
}
}
/// [Iterator] like object that provides [CollectedResolution]s.
class _ResolutionProvider {
final List<CollectedResolution> resolutions;
int index = 0;
_ResolutionProvider(this.resolutions);
CollectedResolution next() => resolutions[index++];
}