blob: 9da05f959e5040314215b974786b6197caffb790 [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/error/error.dart';
import 'package:analyzer/source/line_info.dart';
import 'package:analyzer/src/generated/resolver.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 ParsedLibraryResultImpl extends AnalysisResultImpl
implements ParsedLibraryResult {
@override
final List<ParsedUnitResult> units;
ParsedLibraryResultImpl(
AnalysisSession session, String path, Uri uri, this.units)
: super(session, path, uri);
ParsedLibraryResultImpl.external(AnalysisSession session, Uri uri)
: this(session, null, uri, null);
@override
ResultState get state {
if (path == null) {
return ResultState.NOT_A_FILE;
}
return ResultState.VALID;
}
@override
ElementDeclarationResult getElementDeclaration(Element element) {
if (state != ResultState.VALID) {
throw StateError('The result is not valid: $state');
}
var elementPath = element.source.fullName;
var unitResult = units.firstWhere(
(r) => r.path == elementPath,
orElse: () {
throw ArgumentError('Element (${element.runtimeType}) $element is not '
'defined in this library.');
},
);
if (element.isSynthetic || element.nameOffset == -1) {
return null;
}
var locator = _DeclarationByElementLocator(element);
unitResult.unit.accept(locator);
var declaration = locator.result;
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 TypeProvider typeProvider;
@override
final List<ResolvedUnitResult> units;
ResolvedLibraryResultImpl(AnalysisSession session, String path, Uri uri,
this.element, this.typeProvider, this.units)
: super(session, path, uri);
ResolvedLibraryResultImpl.external(AnalysisSession session, Uri uri)
: this(session, null, uri, null, null, null);
@override
ResultState get state {
if (path == null) {
return ResultState.NOT_A_FILE;
}
return ResultState.VALID;
}
@override
ElementDeclarationResult getElementDeclaration(Element element) {
if (state != ResultState.VALID) {
throw StateError('The result is not valid: $state');
}
var elementPath = element.source.fullName;
var unitResult = units.firstWhere(
(r) => r.path == elementPath,
orElse: () {
throw ArgumentError('Element (${element.runtimeType}) $element is not '
'defined in this library.');
},
);
if (element.isSynthetic || element.nameOffset == -1) {
return null;
}
var locator = _DeclarationByElementLocator(element);
unitResult.unit.accept(locator);
var declaration = locator.result;
return ElementDeclarationResultImpl(element, declaration, null, unitResult);
}
}
class ResolvedUnitResultImpl extends FileResultImpl
implements ResolvedUnitResult {
/// Return `true` if the file exists.
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 => unit.declaredElement.library;
@override
ResultState get state => exists ? ResultState.VALID : ResultState.NOT_A_FILE;
@override
TypeProvider get typeProvider => unit.declaredElement.context.typeProvider;
@override
TypeSystem get typeSystem => unit.declaredElement.context.typeSystem;
}
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 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;
}
}