blob: 9db2ec6383bc0b4bff3f31f6f84d91273d88d5aa [file] [log] [blame]
// Copyright (c) 2018, 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/dart/analysis/results.dart';
import 'package:analyzer/dart/analysis/session.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_provider.dart';
import 'package:analyzer/error/error.dart';
import 'package:analyzer/source/line_info.dart';
import 'package:analyzer/src/dart/element/type_system.dart';
abstract class AnalysisResultImpl implements AnalysisResult {
@override
final AnalysisSession session;
@override
final String path;
@override
final Uri uri;
AnalysisResultImpl(this.session, this.path, this.uri);
}
class ElementDeclarationResultImpl implements ElementDeclarationResult {
@override
final Element element;
@override
final AstNode node;
@override
final ParsedUnitResult? parsedUnit;
@override
final ResolvedUnitResult? resolvedUnit;
ElementDeclarationResultImpl(
this.element, this.node, this.parsedUnit, this.resolvedUnit);
}
class ErrorsResultImpl extends FileResultImpl implements ErrorsResult {
@override
final List<AnalysisError> errors;
ErrorsResultImpl(AnalysisSession session, String path, Uri uri,
LineInfo lineInfo, bool isPart, this.errors)
: super(session, path, uri, lineInfo, isPart);
}
class FileResultImpl extends AnalysisResultImpl implements FileResult {
@override
final LineInfo lineInfo;
@override
final bool isPart;
FileResultImpl(
AnalysisSession session, String path, Uri uri, this.lineInfo, this.isPart)
: super(session, path, uri);
@override
ResultState get state => ResultState.VALID;
}
class LibraryElementResultImpl implements LibraryElementResult {
@override
final LibraryElement element;
LibraryElementResultImpl(this.element);
}
class ParsedLibraryResultImpl extends AnalysisResultImpl
implements ParsedLibraryResult {
@override
final List<ParsedUnitResult> units;
ParsedLibraryResultImpl(
AnalysisSession session, String path, Uri uri, this.units)
: super(session, path, uri);
@override
ResultState get state {
return ResultState.VALID;
}
@override
ElementDeclarationResult? getElementDeclaration(Element element) {
if (state != ResultState.VALID) {
throw StateError('The result is not valid: $state');
}
if (element is CompilationUnitElement ||
element is LibraryElement ||
element.isSynthetic ||
element.nameOffset == -1) {
return null;
}
var elementPath = element.source!.fullName;
var unitResult = units.firstWhere(
(r) => r.path == elementPath,
orElse: () {
var elementStr = element.getDisplayString(withNullability: true);
throw ArgumentError('Element (${element.runtimeType}) $elementStr is '
'not defined in this library.');
},
);
var locator = _DeclarationByElementLocator(element);
unitResult.unit.accept(locator);
var declaration = locator.result;
if (declaration == null) {
return null;
}
return ElementDeclarationResultImpl(element, declaration, unitResult, null);
}
}
class ParsedUnitResultImpl extends FileResultImpl implements ParsedUnitResult {
@override
final String content;
@override
final CompilationUnit unit;
@override
final List<AnalysisError> errors;
ParsedUnitResultImpl(AnalysisSession session, String path, Uri uri,
this.content, LineInfo lineInfo, bool isPart, this.unit, this.errors)
: super(session, path, uri, lineInfo, isPart);
@override
ResultState get state => ResultState.VALID;
}
class ParseStringResultImpl implements ParseStringResult {
@override
final String content;
@override
final List<AnalysisError> errors;
@override
final CompilationUnit unit;
ParseStringResultImpl(this.content, this.unit, this.errors);
@override
LineInfo get lineInfo => unit.lineInfo!;
}
class ResolvedLibraryResultImpl extends AnalysisResultImpl
implements ResolvedLibraryResult {
@override
final LibraryElement element;
@override
final List<ResolvedUnitResult> units;
ResolvedLibraryResultImpl(
AnalysisSession session, String path, Uri uri, this.element, this.units)
: super(session, path, uri);
@override
ResultState get state {
return ResultState.VALID;
}
@override
TypeProvider get typeProvider => element.typeProvider;
@override
ElementDeclarationResult? getElementDeclaration(Element element) {
if (state != ResultState.VALID) {
throw StateError('The result is not valid: $state');
}
if (element is CompilationUnitElement ||
element is LibraryElement ||
element.isSynthetic ||
element.nameOffset == -1) {
return null;
}
var elementPath = element.source!.fullName;
var unitResult = units.firstWhere(
(r) => r.path == elementPath,
orElse: () {
var elementStr = element.getDisplayString(withNullability: true);
var buffer = StringBuffer();
buffer.write('Element (${element.runtimeType}) $elementStr');
buffer.writeln(' is not defined in this library.');
// TODO(scheglov) https://github.com/dart-lang/sdk/issues/45430
buffer.writeln('elementPath: $elementPath');
buffer.writeln('unitPaths: ${units.map((e) => e.path).toList()}');
throw ArgumentError('$buffer');
},
);
var locator = _DeclarationByElementLocator(element);
unitResult.unit.accept(locator);
var declaration = locator.result;
if (declaration == null) {
return null;
}
return ElementDeclarationResultImpl(element, declaration, null, unitResult);
}
}
class ResolvedUnitResultImpl extends FileResultImpl
implements ResolvedUnitResult {
@override
final bool exists;
@override
final String content;
@override
final CompilationUnit unit;
@override
final List<AnalysisError> errors;
ResolvedUnitResultImpl(
AnalysisSession session,
String path,
Uri uri,
this.exists,
this.content,
LineInfo lineInfo,
bool isPart,
this.unit,
this.errors)
: super(session, path, uri, lineInfo, isPart);
@override
LibraryElement get libraryElement {
return unit.declaredElement!.library;
}
@override
ResultState get state => exists ? ResultState.VALID : ResultState.NOT_A_FILE;
@override
TypeProvider get typeProvider => libraryElement.typeProvider;
@override
TypeSystemImpl get typeSystem => libraryElement.typeSystem as TypeSystemImpl;
}
class UnitElementResultImpl extends AnalysisResultImpl
implements UnitElementResult {
@override
final String signature;
@override
final CompilationUnitElement element;
UnitElementResultImpl(AnalysisSession session, String path, Uri uri,
this.signature, this.element)
: super(session, path, uri);
@override
ResultState get state => ResultState.VALID;
}
class _DeclarationByElementLocator extends GeneralizingAstVisitor<void> {
final Element element;
AstNode? result;
_DeclarationByElementLocator(this.element);
@override
void visitNode(AstNode node) {
if (result != null) return;
if (element is ClassElement) {
if (node is ClassOrMixinDeclaration) {
if (_hasOffset(node.name)) {
result = node;
}
} else if (node is ClassTypeAlias) {
if (_hasOffset(node.name)) {
result = node;
}
} else if (node is EnumDeclaration) {
if (_hasOffset(node.name)) {
result = node;
}
}
} else if (element is ConstructorElement) {
if (node is ConstructorDeclaration) {
if (node.name != null) {
if (_hasOffset(node.name)) {
result = node;
}
} else {
if (_hasOffset(node.returnType)) {
result = node;
}
}
}
} else if (element is ExtensionElement) {
if (node is ExtensionDeclaration) {
if (_hasOffset(node.name)) {
result = node;
}
}
} else if (element is FieldElement) {
if (node is EnumConstantDeclaration) {
if (_hasOffset(node.name)) {
result = node;
}
} else if (node is VariableDeclaration) {
if (_hasOffset(node.name)) {
result = node;
}
}
} else if (element is FunctionElement) {
if (node is FunctionDeclaration && _hasOffset(node.name)) {
result = node;
}
} else if (element is LocalVariableElement) {
if (node is VariableDeclaration && _hasOffset(node.name)) {
result = node;
}
} else if (element is MethodElement) {
if (node is MethodDeclaration && _hasOffset(node.name)) {
result = node;
}
} else if (element is ParameterElement) {
if (node is FormalParameter && _hasOffset(node.identifier)) {
result = node;
}
} else if (element is PropertyAccessorElement) {
if (node is FunctionDeclaration) {
if (_hasOffset(node.name)) {
result = node;
}
} else if (node is MethodDeclaration) {
if (_hasOffset(node.name)) {
result = node;
}
}
} else if (element is TopLevelVariableElement) {
if (node is VariableDeclaration && _hasOffset(node.name)) {
result = node;
}
}
super.visitNode(node);
}
bool _hasOffset(AstNode? node) {
return node?.offset == element.nameOffset;
}
}