blob: 3a1d0fa7ffd5847f14c5fcd7dbea10b4cd22b60b [file] [log] [blame]
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/type.dart';
import 'package:analyzer/error/listener.dart';
import 'package:analyzer/src/dart/error/hint_codes.g.dart';
import 'package:analyzer/src/error/codes.g.dart';
import 'package:analyzer/src/task/inference_error.dart';
/// A listener which can receive various inference errors.
///
/// This is separate from [ErrorReporter] for the case of discovering inference
/// errors during AST resolution, which happens far before type analysis and
/// most static error reporting.
abstract class InferenceErrorListener {
final bool _isNonNullableByDefault;
final bool _isGenericMetadataEnabled;
InferenceErrorListener({
required bool isNonNullableByDefault,
required bool isGenericMetadataEnabled,
}) : _isNonNullableByDefault = isNonNullableByDefault,
_isGenericMetadataEnabled = isGenericMetadataEnabled;
void addCouldNotInferError(AstNode node, List<String> arguments);
void addInferenceFailureOnFunctionInvocationError(
AstNode node, List<String> arguments);
void addInferenceFailureOnGenericInvocationError(
AstNode node, List<String> arguments);
void addInferenceFailureOnInstanceCreationError(
AstNode node, List<String> arguments);
/// Reports an inference failure on [errorNode] according to its type.
void reportInferenceFailure(AstNode errorNode) {
if (errorNode.parent is InvocationExpression &&
errorNode.parent?.parent is AsExpression) {
// Casts via `as` do not play a part in downward inference. We allow an
// exception when inference has "failed" but the return value is
// immediately cast with `as`.
return;
}
if (errorNode is ConstructorName &&
!(errorNode.type.type as InterfaceType).element.hasOptionalTypeArgs) {
String constructorName = errorNode.name == null
? errorNode.type.name.name
: '${errorNode.type}.${errorNode.name}';
addInferenceFailureOnInstanceCreationError(errorNode, [constructorName]);
} else if (errorNode is Annotation) {
if (_isGenericMetadataEnabled) {
// Only report an error if generic metadata is valid syntax.
var element = errorNode.name.staticElement;
if (element != null && !element.hasOptionalTypeArgs) {
String constructorName = errorNode.constructorName == null
? errorNode.name.name
: '${errorNode.name.name}.${errorNode.constructorName}';
addInferenceFailureOnInstanceCreationError(
errorNode, [constructorName]);
}
}
} else if (errorNode is SimpleIdentifier) {
var element = errorNode.staticElement;
if (element == null) {
return;
}
if (element is VariableElement) {
// For variable elements, we check their type and possible alias type.
var type = element.type;
final typeElement = type is InterfaceType ? type.element : null;
if (typeElement != null && typeElement.hasOptionalTypeArgs) {
return;
}
var typeAliasElement = type.alias?.element;
if (typeAliasElement != null && typeAliasElement.hasOptionalTypeArgs) {
return;
}
}
if (!element.hasOptionalTypeArgs) {
addInferenceFailureOnFunctionInvocationError(
errorNode, [errorNode.name]);
}
} else if (errorNode is Expression) {
var type = errorNode.staticType;
if (type != null) {
var typeDisplayString =
type.getDisplayString(withNullability: _isNonNullableByDefault);
addInferenceFailureOnGenericInvocationError(
errorNode, [typeDisplayString]);
}
}
}
}
class InferenceErrorRecorder extends InferenceErrorListener {
final Set<TopLevelInferenceError> errors = {};
InferenceErrorRecorder({
required super.isNonNullableByDefault,
required super.isGenericMetadataEnabled,
});
@override
void addCouldNotInferError(AstNode node, List<String> arguments) {
errors.add(TopLevelInferenceError(
kind: TopLevelInferenceErrorKind.couldNotInfer,
arguments: arguments,
));
}
@override
void addInferenceFailureOnFunctionInvocationError(
AstNode node, List<String> arguments) {
// This error is re-discovered and reported during type analysis.
}
@override
void addInferenceFailureOnGenericInvocationError(
AstNode node, List<String> arguments) {
// This error is re-discovered and reported during type analysis.
}
@override
void addInferenceFailureOnInstanceCreationError(
AstNode node, List<String> arguments) {
errors.add(TopLevelInferenceError(
kind: TopLevelInferenceErrorKind.inferenceFailureOnInstanceCreation,
arguments: arguments,
));
}
}
class InferenceErrorReporter extends InferenceErrorListener {
final ErrorReporter _errorReporter;
InferenceErrorReporter(
this._errorReporter, {
required super.isNonNullableByDefault,
required super.isGenericMetadataEnabled,
});
@override
void addCouldNotInferError(AstNode node, List<String> arguments) =>
_errorReporter.reportErrorForNode(
CompileTimeErrorCode.COULD_NOT_INFER, node, arguments);
@override
void addInferenceFailureOnFunctionInvocationError(
AstNode node, List<String> arguments) =>
_errorReporter.reportErrorForNode(
HintCode.INFERENCE_FAILURE_ON_FUNCTION_INVOCATION, node, arguments);
@override
void addInferenceFailureOnGenericInvocationError(
AstNode node, List<String> arguments) =>
_errorReporter.reportErrorForNode(
HintCode.INFERENCE_FAILURE_ON_GENERIC_INVOCATION, node, arguments);
@override
void addInferenceFailureOnInstanceCreationError(
AstNode node, List<String> arguments) =>
_errorReporter.reportErrorForNode(
HintCode.INFERENCE_FAILURE_ON_INSTANCE_CREATION, node, arguments);
}