Skip to content

Commit

Permalink
Report more errors on invalid explicit type instantiation function te…
Browse files Browse the repository at this point in the history
…aroffs.

When an expression which is not a type, not a constructor, and not an
identifier for a function, is torn off with type arguments, report
DISALLOWED_TYPE_INSTANTIATION_EXPRESSION.

When a function expression (without a name) is explicitly torn off with
the wrong number of type arguments, report
WRONG_NUMBER_OF_TYPE_ARGUMENTS_ANONYMOUS_FUNCTION. This separate error
code has text which does not have a slot for a function's name.

Add resolution for implicit receiver super-type instance method tearoff.

Bug: #46233
Change-Id: I3243e1824e191b6b703b1c995af5e1bd8a242915
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/207140
Commit-Queue: Samuel Rawlins <srawlins@google.com>
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
  • Loading branch information
srawlins authored and commit-bot@chromium.org committed Jul 18, 2021
1 parent 63996d0 commit 9ef9fce
Show file tree
Hide file tree
Showing 6 changed files with 346 additions and 62 deletions.
2 changes: 2 additions & 0 deletions pkg/analyzer/lib/error/error.dart
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ const List<ErrorCode> errorCodeValues = [
CompileTimeErrorCode.DEFAULT_VALUE_ON_REQUIRED_PARAMETER,
CompileTimeErrorCode.DEFERRED_IMPORT_OF_EXTENSION,
CompileTimeErrorCode.DEFINITELY_UNASSIGNED_LATE_LOCAL_VARIABLE,
CompileTimeErrorCode.DISALLOWED_TYPE_INSTANTIATION_EXPRESSION,
CompileTimeErrorCode.DUPLICATE_CONSTRUCTOR_DEFAULT,
CompileTimeErrorCode.DUPLICATE_CONSTRUCTOR_NAME,
CompileTimeErrorCode.DUPLICATE_DEFINITION,
Expand Down Expand Up @@ -454,6 +455,7 @@ const List<ErrorCode> errorCodeValues = [
CompileTimeErrorCode.WRONG_NUMBER_OF_PARAMETERS_FOR_OPERATOR_MINUS,
CompileTimeErrorCode.WRONG_NUMBER_OF_PARAMETERS_FOR_SETTER,
CompileTimeErrorCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS,
CompileTimeErrorCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS_ANONYMOUS_FUNCTION,
CompileTimeErrorCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS_CONSTRUCTOR,
CompileTimeErrorCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS_EXTENSION,
CompileTimeErrorCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS_FUNCTION,
Expand Down
211 changes: 159 additions & 52 deletions pkg/analyzer/lib/src/dart/resolver/function_reference_resolver.dart
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,47 @@ class FunctionReferenceResolver {
if (function is SimpleIdentifierImpl) {
var element = _resolver.nameScope.lookup(function.name).getter;

if (element == null) {
DartType receiverType;
var enclosingClass = _resolver.enclosingClass;
if (enclosingClass != null) {
receiverType = enclosingClass.thisType;
} else {
// TODO(srawlins): Check `_resolver.enclosingExtension`.
_errorReporter.reportErrorForNode(
CompileTimeErrorCode.UNDEFINED_IDENTIFIER,
function,
[function.name],
);
node.staticType = DynamicTypeImpl.instance;
return;
}

var result = _resolver.typePropertyResolver.resolve(
receiver: null,
receiverType: receiverType,
name: function.name,
propertyErrorEntity: function,
nameErrorEntity: function,
);

var method = result.getter;
if (method != null) {
function.staticElement = method;
_resolve(node: node, rawType: method.type, name: function.name);
return;
} else {
// TODO(srawlins): Report CompileTimeErrorCode.UNDEFINED_METHOD.
return;
}

// TODO(srawlins): If `target.isStatic`, report an error, something like
// [MethodInvocationResolver._reportInstanceAccessToStaticMember].

// TODO(srawlins): if `(target is PropertyAccessorElement)`, report an
// error.
}

// Classes and type aliases are checked first so as to include a
// PropertyAccess parent check, which does not need to be done for
// functions.
Expand All @@ -66,23 +107,32 @@ class FunctionReferenceResolver {
}
} else if (element is ExecutableElement) {
function.staticElement = element;
_resolve(node: node, name: element.name, rawType: element.type);
function.staticType = element.type;
_resolve(node: node, rawType: element.type, name: element.name);
return;
} else if (element is VariableElement) {
var functionType = element.type;
if (functionType is FunctionType) {
function.accept(_resolver);
_resolve(node: node, name: element.name ?? '', rawType: functionType);
return;
}
function.staticElement = element;
function.staticType = element.type;
_resolveDisallowedExpression(node, element.type);
return;
}
}

// TODO(srawlins): Handle `function` being a [SuperExpression].

if (function is PrefixedIdentifierImpl) {
node.staticType = DynamicTypeImpl.instance;
return;
} else if (function is PrefixedIdentifierImpl) {
var prefixElement =
_resolver.nameScope.lookup(function.prefix.name).getter;

if (prefixElement == null) {
_errorReporter.reportErrorForNode(
CompileTimeErrorCode.UNDEFINED_IDENTIFIER,
function.prefix,
[function.name],
);
node.staticType = DynamicTypeImpl.instance;
return;
}

function.prefix.staticElement = prefixElement;
if (prefixElement is PrefixElement) {
var functionName = function.identifier.name;
Expand All @@ -102,10 +152,21 @@ class FunctionReferenceResolver {
node, prefixElement, function, functionElement);
return;
}
} else if (prefixElement is VariableElement) {
function.prefix.staticType = prefixElement.type;
}

DartType? prefixType;
if (prefixElement is VariableElement) {
prefixType = prefixElement.type;
} else if (prefixElement is PropertyAccessorElement) {
function.prefix.staticType = prefixElement.returnType;
prefixType = prefixElement.returnType;
}

function.prefix.staticType = prefixType;
if (prefixType != null && prefixType.isDynamic) {
// TODO(srawlins): Report error. See spec text: "We do not allow dynamic
// explicit instantiation."
node.staticType = DynamicTypeImpl.instance;
return;
}

var methodElement = _resolveTypeProperty(
Expand All @@ -120,8 +181,8 @@ class FunctionReferenceResolver {
function.staticType = methodElement.type;
_resolve(
node: node,
name: function.identifier.name,
rawType: methodElement.type,
name: function.identifier.name,
);
return;
}
Expand All @@ -133,11 +194,17 @@ class FunctionReferenceResolver {
function.accept(_resolver);
node.staticType = DynamicTypeImpl.instance;
return;
}

if (function is PropertyAccessImpl) {
} else if (function is PropertyAccessImpl) {
function.accept(_resolver);
var target = function.target;
if (target == null) {
// TODO(srawlins): Can we get here? Perhaps as part of a cascade, but
// there is currently a parsing error with cascades.
// https://github.com/dart-lang/sdk/issues/46635
node.staticType = DynamicTypeImpl.instance;
return;
}

DartType targetType;
if (target is SuperExpressionImpl) {
targetType = target.typeOrThrow;
Expand Down Expand Up @@ -182,9 +249,7 @@ class FunctionReferenceResolver {
return;
}
} else {
// TODO(srawlins): Can we get here?
node.staticType = DynamicTypeImpl.instance;
return;
targetType = target.typeOrThrow;
}

var propertyElement = _resolver.typePropertyResolver
Expand All @@ -197,38 +262,51 @@ class FunctionReferenceResolver {
)
.getter;

var functionType = function.typeOrThrow;
if (functionType is FunctionType && propertyElement != null) {
_resolve(
node: node,
name: propertyElement.name,
rawType: functionType,
);
if (propertyElement is TypeParameterElement) {
_resolveDisallowedExpression(node, propertyElement!.type);
return;
}

// TODO(srawlins): Handle type variables bound to function type, like
// `T extends void Function<U>(U)`.
}
_resolve(
node: node,
rawType: function.staticType,
name: propertyElement?.name,
);
return;
} else {
// TODO(srawlins): Handle `function` being a [SuperExpression].

// TODO(srawlins): Enumerate and handle all cases that fall through to
// here; ultimately it should just be a case of "unknown identifier."
function.accept(_resolver);
node.staticType = DynamicTypeImpl.instance;
function.accept(_resolver);
_resolveDisallowedExpression(node, node.function.staticType);
return;
}
}

List<DartType> _checkTypeArguments(
TypeArgumentList typeArgumentList,
String name,
String? name,
List<TypeParameterElement> typeParameters,
CompileTimeErrorCode errorCode,
) {
if (typeArgumentList.arguments.length != typeParameters.length) {
_errorReporter.reportErrorForNode(
errorCode,
typeArgumentList,
[name, typeParameters.length, typeArgumentList.arguments.length],
);
if (name == null &&
errorCode ==
CompileTimeErrorCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS_FUNCTION) {
errorCode = CompileTimeErrorCode
.WRONG_NUMBER_OF_TYPE_ARGUMENTS_ANONYMOUS_FUNCTION;
_errorReporter.reportErrorForNode(
errorCode,
typeArgumentList,
[typeParameters.length, typeArgumentList.arguments.length],
);
} else {
assert(name != null);
_errorReporter.reportErrorForNode(
errorCode,
typeArgumentList,
[name, typeParameters.length, typeArgumentList.arguments.length],
);
}
return List.filled(typeParameters.length, DynamicTypeImpl.instance);
} else {
return typeArgumentList.arguments
Expand All @@ -241,18 +319,33 @@ class FunctionReferenceResolver {
/// argument types, using [rawType] as the uninstantiated function type.
void _resolve({
required FunctionReferenceImpl node,
required String name,
required FunctionType rawType,
required DartType? rawType,
String? name,
}) {
var typeArguments = _checkTypeArguments(
// `node.typeArguments`, coming from the parser, is never null.
node.typeArguments!, name, rawType.typeFormals,
CompileTimeErrorCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS_FUNCTION,
);
if (rawType == null) {
node.staticType = DynamicTypeImpl.instance;
}

var invokeType = rawType.instantiate(typeArguments);
node.typeArgumentTypes = typeArguments;
node.staticType = invokeType;
if (rawType is TypeParameterTypeImpl) {
// If the type of the function is a type parameter, the tearoff is
// disallowed, reported in [_resolveDisallowedExpression]. Use the type
// parameter's bound here in an attempt to assign the intended types.
rawType = rawType.element.bound;
}

if (rawType is FunctionType) {
var typeArguments = _checkTypeArguments(
// `node.typeArguments`, coming from the parser, is never null.
node.typeArguments!, name, rawType.typeFormals,
CompileTimeErrorCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS_FUNCTION,
);

var invokeType = rawType.instantiate(typeArguments);
node.typeArgumentTypes = typeArguments;
node.staticType = invokeType;
} else {
node.staticType = DynamicTypeImpl.instance;
}
}

void _resolveConstructorReference(FunctionReferenceImpl node) {
Expand All @@ -277,6 +370,20 @@ class FunctionReferenceResolver {
_resolveTypeLiteral(node: node, instantiatedType: type, name: name);
}

/// Resolves [node] as a type instantiation on an illegal expression.
///
/// This function attempts to give [node] a static type, to continue working
/// with what the user may be intending.
void _resolveDisallowedExpression(
FunctionReferenceImpl node, DartType? rawType) {
_errorReporter.reportErrorForNode(
CompileTimeErrorCode.DISALLOWED_TYPE_INSTANTIATION_EXPRESSION,
node.function,
[],
);
_resolve(node: node, rawType: rawType);
}

void _resolveReceiverPrefix(
FunctionReferenceImpl node,
PrefixElement prefixElement,
Expand Down Expand Up @@ -315,8 +422,8 @@ class FunctionReferenceResolver {
node.function.accept(_resolver);
_resolve(
node: node,
name: element.name,
rawType: node.function.typeOrThrow as FunctionType,
name: element.name,
);
return;
}
Expand Down
28 changes: 28 additions & 0 deletions pkg/analyzer/lib/src/error/codes.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10502,6 +10502,18 @@ class CompileTimeErrorCode extends AnalyzerErrorCode {
'Type parameter bound types must be instantiated.',
correction: 'Try adding type arguments to the type parameter bound.');

/**
* No parameters.
*/
static const CompileTimeErrorCode DISALLOWED_TYPE_INSTANTIATION_EXPRESSION =
CompileTimeErrorCode(
'DISALLOWED_TYPE_INSTANTIATION_EXPRESSION',
'Only a generic type, generic function, generic instance method, or '
'generic constructor can be type instantiated.',
correction:
'Try instantiating the type(s) of a generic type, generic '
'function, generic instance method, or generic constructor.');

/**
* No parameters.
*/
Expand Down Expand Up @@ -14630,6 +14642,22 @@ class CompileTimeErrorCode extends AnalyzerErrorCode {
"of type parameters.",
hasPublishedDocs: true);

/**
* Parameters:
* 0: the number of type parameters that were declared
* 1: the number of type arguments provided
*/
static const CompileTimeErrorCode
WRONG_NUMBER_OF_TYPE_ARGUMENTS_ANONYMOUS_FUNCTION = CompileTimeErrorCode(
'WRONG_NUMBER_OF_TYPE_ARGUMENTS_FUNCTION',
"This function is declared with {0} type parameters, "
"but {1} type arguments were given.",
correction:
"Try adjusting the number of type arguments to match the number "
"of type parameters.",
hasPublishedDocs: true,
uniqueName: 'WRONG_NUMBER_OF_TYPE_ARGUMENTS_ANONYMOUS_FUNCTION');

/**
* Parameters:
* 0: the name of the class being instantiated
Expand Down
Loading

0 comments on commit 9ef9fce

Please sign in to comment.