blob: 35bc907002859624f0a3cc7ab0d887dc1ec7d325 [file]
// Copyright (c) 2020, 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/ast/syntactic_entity.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/src/dart/ast/ast.dart';
import 'package:analyzer/src/dart/element/type.dart';
import 'package:collection/collection.dart';
// TODO(scheglov): https://github.com/dart-lang/sdk/issues/43608
Element? _readElement(AstNode node) {
var parent = node.parent2;
if (parent is AssignmentExpression && parent.leftHandSide2 == node) {
return parent.readElement;
}
if (parent is PostfixExpression && parent.operand2 == node) {
return parent.readElement;
}
if (parent is PrefixExpression && parent.operand2 == node) {
return parent.readElement;
}
if (parent is PrefixedIdentifier && parent.identifier == node) {
return _readElement(parent);
}
if (parent is PropertyAccess && parent.propertyName == node) {
return _readElement(parent);
}
return null;
}
// TODO(scheglov): https://github.com/dart-lang/sdk/issues/43608
Element? _readElementV1(AstNode node) {
var parent = node.parent;
if (parent is AssignmentExpression && parent.leftHandSide == node) {
return parent.readElement;
}
if (parent is PostfixExpression && parent.operand == node) {
return parent.readElement;
}
if (parent is PrefixExpression && parent.operand == node) {
return parent.readElement;
}
if (parent is PrefixedIdentifier && parent.identifier == node) {
return _readElementV1(parent);
}
if (parent is PropertyAccess && parent.propertyName == node) {
return _readElementV1(parent);
}
return null;
}
// TODO(scheglov): https://github.com/dart-lang/sdk/issues/43608
Element? _writeElement(AstNode node) {
var parent = node.parent2;
if (parent is AssignmentExpression && parent.leftHandSide2 == node) {
return parent.writeElement;
}
if (parent is PostfixExpression && parent.operand2 == node) {
return parent.writeElement;
}
if (parent is PrefixExpression && parent.operand2 == node) {
return parent.writeElement;
}
if (parent is PrefixedIdentifier && parent.identifier == node) {
return _writeElement(parent);
}
if (parent is PropertyAccess && parent.propertyName == node) {
return _writeElement(parent);
}
return null;
}
// TODO(scheglov): https://github.com/dart-lang/sdk/issues/43608
Element? _writeElementV1(AstNode node) {
var parent = node.parent;
if (parent is AssignmentExpression && parent.leftHandSide == node) {
return parent.writeElement;
}
if (parent is PostfixExpression && parent.operand == node) {
return parent.writeElement;
}
if (parent is PrefixExpression && parent.operand == node) {
return parent.writeElement;
}
if (parent is PrefixedIdentifier && parent.identifier == node) {
return _writeElementV1(parent);
}
if (parent is PropertyAccess && parent.propertyName == node) {
return _writeElementV1(parent);
}
return null;
}
// TODO(scheglov): https://github.com/dart-lang/sdk/issues/43608
DartType? _writeType(AstNode node) {
var parent = node.parent2;
if (parent is AssignmentExpression && parent.leftHandSide2 == node) {
return parent.writeType;
}
if (parent is PostfixExpression && parent.operand2 == node) {
return parent.writeType;
}
if (parent is PrefixExpression && parent.operand2 == node) {
return parent.writeType;
}
if (parent is PrefixedIdentifier && parent.identifier == node) {
return _writeType(parent);
}
if (parent is PropertyAccess && parent.propertyName == node) {
return _writeType(parent);
}
return null;
}
// TODO(scheglov): https://github.com/dart-lang/sdk/issues/43608
DartType? _writeTypeV1(AstNode node) {
var parent = node.parent;
if (parent is AssignmentExpression && parent.leftHandSide == node) {
return parent.writeType;
}
if (parent is PostfixExpression && parent.operand == node) {
return parent.writeType;
}
if (parent is PrefixExpression && parent.operand == node) {
return parent.writeType;
}
if (parent is PrefixedIdentifier && parent.identifier == node) {
return _writeTypeV1(parent);
}
if (parent is PropertyAccess && parent.propertyName == node) {
return _writeTypeV1(parent);
}
return null;
}
extension ArgumentListExtension on ArgumentList {
/// Returns the named argument with the given [name], or `null` if none.
NamedArgument? byName(String name) => arguments2
.whereType<NamedArgument>()
.firstWhereOrNull((e) => e.name.lexeme == name);
/// Returns the argument with the given [index], or `null` if none.
Argument? elementAtOrNull(int index) {
if (index < arguments2.length) {
return arguments2[index];
}
return null;
}
}
extension ConstructorDeclarationExtension on ConstructorDeclaration {
bool get isNonRedirectingGenerative {
// Must be generative.
if (externalKeyword != null || factoryKeyword != null) {
return false;
}
// Must be non-redirecting.
for (var initializer in initializers) {
if (initializer is RedirectingConstructorInvocation) {
return false;
}
}
return true;
}
}
extension DartPatternExtension on DartPattern {
/// Return the matched value type of this pattern.
///
/// This accessor should be used on patterns that are expected to
/// be already resolved. Every such pattern must have the type set.
DartType get matchedValueTypeOrThrow {
var type = matchedValueType;
if (type == null) {
throw StateError('No type: $this');
}
return type;
}
DartType? get requiredType {
var self = this;
if (self is DeclaredVariablePattern) {
return self.type?.typeOrThrow;
} else if (self is ListPattern) {
return self.requiredType;
} else if (self is MapPattern) {
return self.requiredType;
} else if (self is WildcardPattern) {
return self.type?.typeOrThrow;
}
return null;
}
}
extension ExpressionExtension on Expression {
/// Return the static type of this expression.
///
/// This accessor should be used on expressions that are expected to
/// be already resolved. Every such expression must have the type set,
/// at least `dynamic`.
TypeImpl get typeOrThrow => (this as ExpressionImpl).typeOrThrow;
}
extension ExpressionImplExtension on ExpressionImpl {
/// Return the static type of this expression.
///
/// This accessor should be used on expressions that are expected to
/// be already resolved. Every such expression must have the type set,
/// at least `dynamic`.
TypeImpl get typeOrThrow {
var type = staticType;
if (type == null) {
throw StateError('No type: $this');
}
return type;
}
}
extension FormalParameterExtension on FormalParameter {
bool get isOfLocalFunction {
return thisOrAncestorOfType2<FunctionBody>() != null;
}
FormalParameterList get parentFormalParameterList {
return switch (parent2) {
FormalParameterList parent => parent,
DelimitedFormalParameters(parent2: FormalParameterList parent) => parent,
_ => throw StateError('Formal parameter has no formal parameter list'),
};
}
AstNode get typeOrSelf {
var type = this.type;
if (type != null) {
return type;
}
return this;
}
}
// TODO(scheglov): https://github.com/dart-lang/sdk/issues/43608
extension IdentifierExtension on Identifier {
Element? get readElement {
return _readElement(this);
}
Element? get readElementV1 {
return _readElementV1(this);
}
SimpleIdentifier get simpleName {
var self = this;
if (self is SimpleIdentifier) {
return self;
} else {
return (self as PrefixedIdentifier).identifier;
}
}
Element? get writeElement {
return _writeElement(this);
}
Element? get writeElementV1 {
return _writeElementV1(this);
}
Element? get writeOrReadElement {
return _writeElement(this) ?? element;
}
Element? get writeOrReadElementV1 {
return _writeElementV1(this) ?? element;
}
DartType? get writeOrReadType {
return _writeType(this) ?? staticType;
}
DartType? get writeOrReadTypeV1 {
return _writeTypeV1(this) ?? staticType;
}
}
extension IdentifierImplExtension on IdentifierImpl {
NamedTypeImpl toNamedType({
required TypeArgumentListImpl? typeArguments,
required Token? question,
}) {
var self = this;
if (self is PrefixedIdentifierImpl) {
return NamedTypeImpl(
importPrefix: ImportPrefixReferenceImpl(
name: self.prefix.token,
period: self.period,
)..element = self.prefix.element,
name: self.identifier.token,
typeArguments: typeArguments,
question: question,
)..element = self.identifier.element;
} else if (self is SimpleIdentifierImpl) {
return NamedTypeImpl(
importPrefix: null,
name: self.token,
typeArguments: typeArguments,
question: question,
)..element = self.element;
} else {
throw UnimplementedError('(${self.runtimeType}) $self');
}
}
}
// TODO(scheglov): https://github.com/dart-lang/sdk/issues/43608
extension IndexExpressionExtension on IndexExpression {
Element? get writeOrReadElement {
return _writeElement(this) ?? element;
}
}
extension ListOfFormalParameterExtension on List<FormalParameter> {
Iterable<FormalParameterImpl> get asImpl {
return cast<FormalParameterImpl>();
}
}
extension NamedTypeExtension on NamedType {
String get qualifiedName {
var importPrefix = this.importPrefix;
if (importPrefix != null) {
return '${importPrefix.name.lexeme}.${name.lexeme}';
} else {
return name.lexeme;
}
}
}
extension NullableStringExtension on String? {
bool get isEmptyOrNull {
var str = this;
if (str == null) return true;
if (str.isEmpty) return true;
return false;
}
}
extension PatternFieldImplExtension on PatternFieldImpl {
/// A [SyntacticEntity] which can be used in error reporting, which is valid
/// for both explicit getter names (like `Rect(width: var w, height: var h)`)
/// and implicit getter names (like `Rect(:var width, :var height)`).
SyntacticEntity get errorEntity {
var fieldName = name;
if (fieldName == null) {
return this;
}
var fieldNameName = fieldName.name;
if (fieldNameName == null) {
var variablePattern = pattern.variablePattern;
return variablePattern?.name ?? this;
} else {
return fieldNameName;
}
}
}
extension RecordTypeAnnotationExtension on RecordTypeAnnotation {
List<RecordTypeAnnotationField> get fields {
return [...positionalFields, ...?namedFields?.fields];
}
}
extension TypeAnnotationExtension on TypeAnnotation {
/// Return the static type of this type annotation.
///
/// This accessor should be used on expressions that are expected to
/// be already resolved. Every such expression must have the type set,
/// at least `dynamic`.
TypeImpl get typeOrThrow => (this as TypeAnnotationImpl).typeOrThrow;
}
extension TypeAnnotationImplExtension on TypeAnnotationImpl {
/// Return the static type of this type annotation.
///
/// This accessor should be used on expressions that are expected to
/// be already resolved. Every such expression must have the type set,
/// at least `dynamic`.
TypeImpl get typeOrThrow {
var type = this.type;
if (type == null) {
throw StateError('No type: $this');
}
return type;
}
}