Suppress redundant tearoff errors when constructor tearoffs not enabled

Fixes https://github.com/dart-lang/sdk/issues/47035

Change-Id: I745da785d80744f84e3582727e423e4caed866c0
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/211720
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Samuel Rawlins <srawlins@google.com>
This commit is contained in:
Sam Rawlins 2021-08-30 17:19:07 +00:00 committed by commit-bot@chromium.org
parent b12724b7c6
commit 873ecfd8fd
7 changed files with 76 additions and 10 deletions

View file

@ -39,11 +39,16 @@ class ConstructorReferenceResolver {
);
}
var name = node.constructorName.name;
if (element == null && name != null) {
if (element == null &&
name != null &&
_resolver.isConstructorTearoffsEnabled) {
// The illegal construction, which looks like a type-instantiated
// constructor tearoff, may be an attempt to reference a member on
// [enclosingElement]. Try to provide a helpful error, and fall back to
// "unknown constructor."
//
// Only report errors when the constructor tearoff feature is enabled,
// to avoid reporting redundant errors.
var enclosingElement = node.constructorName.type.name.staticElement;
if (enclosingElement is TypeAliasElement) {
enclosingElement = enclosingElement.aliasedType.element;

View file

@ -186,11 +186,15 @@ class FunctionReferenceResolver {
/// with what the user may be intending.
void _resolveDisallowedExpression(
FunctionReferenceImpl node, DartType? rawType) {
_errorReporter.reportErrorForNode(
CompileTimeErrorCode.DISALLOWED_TYPE_INSTANTIATION_EXPRESSION,
node.function,
[],
);
if (_resolver.isConstructorTearoffsEnabled) {
// Only report constructor tearoff-related errors if the constructor
// tearoff feature is enabled.
_errorReporter.reportErrorForNode(
CompileTimeErrorCode.DISALLOWED_TYPE_INSTANTIATION_EXPRESSION,
node.function,
[],
);
}
_resolve(node: node, rawType: rawType);
}

View file

@ -761,6 +761,20 @@ class ResolutionVisitor extends RecursiveAstVisitor<void> {
void visitInstanceCreationExpression(InstanceCreationExpression node) {
var newNode = _astRewriter.instanceCreationExpression(_nameScope, node);
if (newNode != node) {
if (node.constructorName.type.typeArguments != null &&
newNode is MethodInvocation &&
newNode.target is FunctionReference &&
!_libraryElement.featureSet.isEnabled(Feature.constructor_tearoffs)) {
// A function reference with explicit type arguments (an expression of
// the form `a<...>.m(...)` or `p.a<...>.m(...)` where `a` does not
// refer to a class name, nor a type alias), is illegal without the
// constructor tearoff feature.
//
// This is a case where the parser does not report an error, because the
// parser thinks this could be an InstanceCreationExpression.
_errorReporter.reportErrorForNode(
HintCode.SDK_VERSION_CONSTRUCTOR_TEAROFFS, node, []);
}
return newNode.accept(this);
}

View file

@ -805,6 +805,29 @@ bar() {
@reflectiveTest
class ConstructorReferenceResolutionWithoutConstructorTearoffsTest
extends PubPackageResolutionTest with WithoutConstructorTearoffsMixin {
test_class_generic_nonConstructor() async {
await assertErrorsInCode('''
class A<T> {
static int i = 1;
}
void bar() {
A<int>.i;
}
''', [
error(ParserErrorCode.EXPERIMENT_NOT_ENABLED, 52, 5),
]);
var classElement = findElement.class_('A');
assertConstructorReference(
findNode.constructorReference('A<int>.i;'),
null,
classElement,
'dynamic',
expectedTypeNameType: 'A<int>',
);
}
test_constructorTearoff() async {
await assertErrorsInCode('''
class A {

View file

@ -2,6 +2,7 @@
// 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/src/dart/error/syntactic_errors.dart';
import 'package:analyzer/src/error/codes.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
@ -10,6 +11,8 @@ import 'context_collection_resolution.dart';
main() {
defineReflectiveSuite(() {
defineReflectiveTests(FunctionReferenceResolutionTest);
defineReflectiveTests(
FunctionReferenceResolutionWithoutConstructorTearoffsTest);
});
}
@ -996,3 +999,24 @@ void bar() {
findNode.functionReference('foo<int>;'), null, 'dynamic');
}
}
@reflectiveTest
class FunctionReferenceResolutionWithoutConstructorTearoffsTest
extends PubPackageResolutionTest with WithoutConstructorTearoffsMixin {
test_localVariable() async {
// This code includes a disallowed type instantiation (local variable),
// but in the case that the experiment is not enabled, we suppress the
// associated error.
await assertErrorsInCode('''
void bar(void Function<T>(T a) foo) {
foo<int>;
}
''', [
error(ParserErrorCode.EXPERIMENT_NOT_ENABLED, 43, 5),
]);
var reference = findNode.functionReference('foo<int>;');
assertFunctionReference(
reference, findElement.parameter('foo'), 'void Function(int)');
}
}

View file

@ -100,8 +100,6 @@ main() {
// ^^^^^
// [analyzer] COMPILE_TIME_ERROR.WRONG_NUMBER_OF_TYPE_ARGUMENTS_CONSTRUCTOR
Foo.bar<int>.baz();
//^^^^^^^
// [analyzer] COMPILE_TIME_ERROR.DISALLOWED_TYPE_INSTANTIATION_EXPRESSION
// ^
// [cfe] Couldn't find constructor 'bar'.
// ^^^

View file

@ -102,8 +102,6 @@ main() {
// ^^^^^
// [analyzer] COMPILE_TIME_ERROR.WRONG_NUMBER_OF_TYPE_ARGUMENTS_CONSTRUCTOR
Foo.bar<int>.baz();
//^^^^^^^
// [analyzer] COMPILE_TIME_ERROR.DISALLOWED_TYPE_INSTANTIATION_EXPRESSION
// ^
// [cfe] Couldn't find constructor 'bar'.
// ^^^