blob: 765e12002f0b3a28d2f0b0e49ce085f2e060a706 [file] [log] [blame]
// 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:analysis_server/src/services/correction/fix/data_driven/parameter_reference.dart';
import 'package:analyzer/dart/ast/ast.dart';
/// A class that can be used to access a value from a target.
abstract class Accessor {
/// Return the result of using this accessor to access a value from the
/// [target].
AccessorResult getValue(Object target);
}
/// The result of using an accessor to get a result.
abstract class AccessorResult {
/// Initialize a newly created result.
const AccessorResult();
/// Return `true` if the accessor returned a valid result.
bool get isValid;
/// Return the result of the accessor if it was valid.
///
/// Throws a `StateError` if the accessor did not return a valid result.
Object get result;
}
/// An accessor that returns a specific argument from an argument list.
class ArgumentAccessor extends Accessor {
/// The parameter corresponding to the argument from the original invocation.
final ParameterReference parameter;
/// Initialize a newly created accessor to access the argument that
/// corresponds to the given [parameter].
ArgumentAccessor(this.parameter) : assert(parameter != null);
@override
AccessorResult getValue(Object target) {
if (target is AstNode) {
var argumentList = _getArgumentList(target);
if (argumentList != null) {
var argument = parameter.argumentFrom(argumentList);
if (argument != null) {
return ValidResult(argument);
}
}
}
return const InvalidResult();
}
@override
String toString() => 'arguments[$parameter]';
/// Return the argument list associated with the [node].
ArgumentList _getArgumentList(AstNode node) {
if (node is Annotation) {
return node.arguments;
} else if (node is ExtensionOverride) {
return node.argumentList;
} else if (node is InstanceCreationExpression) {
return node.argumentList;
} else if (node is InvocationExpression) {
return node.argumentList;
} else if (node is RedirectingConstructorInvocation) {
return node.argumentList;
} else if (node is SuperConstructorInvocation) {
return node.argumentList;
}
return null;
}
}
/// A representation of an invalid result.
class InvalidResult implements AccessorResult {
/// Initialize a newly created invalid result.
const InvalidResult();
@override
bool get isValid => false;
@override
Object get result => throw StateError('Cannot access an invalid result');
}
/// An accessor that returns a specific type argument from a type argument list.
class TypeArgumentAccessor extends Accessor {
/// The index of the type argument.
final int index;
/// Initialize a newly created accessor to access the type argument at the
/// given [index].
TypeArgumentAccessor(this.index) : assert(index != null);
@override
AccessorResult getValue(Object target) {
if (target is AstNode) {
var typeArgumentList = _getTypeArgumentList(target);
if (typeArgumentList != null) {
var arguments = typeArgumentList.arguments;
if (arguments.length > index) {
var argument = arguments[index];
if (argument != null) {
return ValidResult(argument);
}
}
}
}
return const InvalidResult();
}
@override
String toString() => 'typeArguments[$index]';
/// Return the type argument list associated with the [node].
TypeArgumentList _getTypeArgumentList(AstNode node) {
if (node is ExtensionOverride) {
return node.typeArguments;
} else if (node is InstanceCreationExpression) {
return node.constructorName.type.typeArguments;
} else if (node is InvocationExpression) {
return node.typeArguments;
} else if (node is NamedType) {
return node.typeArguments;
} else if (node is TypedLiteral) {
return node.typeArguments;
}
return null;
}
}
/// A representation of a valid result.
class ValidResult implements AccessorResult {
@override
final Object result;
/// Initialize a newly created valid result.
ValidResult(this.result);
@override
bool get isValid => true;
}