From 9f8aaf65c72a46599434375b00621402de981d3a Mon Sep 17 00:00:00 2001 From: Paul Berry Date: Thu, 9 Jul 2020 23:09:18 +0000 Subject: [PATCH] Issue an error if a prefix in a type name is shadowed by a local variable. See #42620. Change-Id: Ifd18c939082935390df0c2aa0bd724cf6f76c27c Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/153705 Commit-Queue: Paul Berry Reviewed-by: Konstantin Shcheglov --- pkg/analyzer/lib/error/error.dart | 1 + pkg/analyzer/lib/src/error/codes.dart | 18 +++++ .../lib/src/generated/error_verifier.dart | 16 +++++ .../diagnostics/new_with_non_type_test.dart | 13 ++++ ...ix_shadowed_by_local_declaration_test.dart | 69 +++++++++++++++++++ .../test/src/diagnostics/test_all.dart | 3 + .../class/variable_shadow_class_test.dart | 1 - tests/language/prefix/shadow_test.dart | 2 + .../class/variable_shadow_class_test.dart | 1 - tests/language_2/prefix/shadow_test.dart | 2 + 10 files changed, 124 insertions(+), 2 deletions(-) create mode 100644 pkg/analyzer/test/src/diagnostics/prefix_shadowed_by_local_declaration_test.dart diff --git a/pkg/analyzer/lib/error/error.dart b/pkg/analyzer/lib/error/error.dart index fcfc708274d6..5c052f051e77 100644 --- a/pkg/analyzer/lib/error/error.dart +++ b/pkg/analyzer/lib/error/error.dart @@ -286,6 +286,7 @@ const List errorCodeValues = [ CompileTimeErrorCode.PART_OF_UNNAMED_LIBRARY, CompileTimeErrorCode.PREFIX_COLLIDES_WITH_TOP_LEVEL_MEMBER, CompileTimeErrorCode.PREFIX_IDENTIFIER_NOT_FOLLOWED_BY_DOT, + CompileTimeErrorCode.PREFIX_SHADOWED_BY_LOCAL_DECLARATION, CompileTimeErrorCode.PRIVATE_COLLISION_IN_MIXIN_APPLICATION, CompileTimeErrorCode.PRIVATE_OPTIONAL_PARAMETER, CompileTimeErrorCode.PRIVATE_SETTER, diff --git a/pkg/analyzer/lib/src/error/codes.dart b/pkg/analyzer/lib/src/error/codes.dart index 21c9153e93de..a6e9392cbb60 100644 --- a/pkg/analyzer/lib/src/error/codes.dart +++ b/pkg/analyzer/lib/src/error/codes.dart @@ -5589,6 +5589,24 @@ class CompileTimeErrorCode extends AnalyzerErrorCode { "prefix, or renaming the prefix.", hasPublishedDocs: true); + /** + * From the `Static Types` section of the spec: + * + * A type T is malformed if: + * - T has the form id or the form prefix.id, and in the enclosing lexical + * scope, the name id (respectively prefix.id) does not denote a type. + * + * In particular, this means that if an import prefix is shadowed by a local + * declaration, it is an error to try to use it as a prefix for a type name. + */ + static const CompileTimeErrorCode PREFIX_SHADOWED_BY_LOCAL_DECLARATION = + CompileTimeErrorCode( + 'PREFIX_SHADOWED_BY_LOCAL_DECLARATION', + "The prefix '{0}' can't be used here because it is shadowed by a " + "local declaration.", + correction: + "Try renaming either the prefix or the local declaration."); + /** * It is an error for a mixin to add a private name that conflicts with a * private name added by a superclass or another mixin. diff --git a/pkg/analyzer/lib/src/generated/error_verifier.dart b/pkg/analyzer/lib/src/generated/error_verifier.dart index 3d4c413c7add..24b8d2f8e7e6 100644 --- a/pkg/analyzer/lib/src/generated/error_verifier.dart +++ b/pkg/analyzer/lib/src/generated/error_verifier.dart @@ -1155,6 +1155,7 @@ class ErrorVerifier extends RecursiveAstVisitor { @override void visitTypeName(TypeName node) { _typeArgumentsVerifier.checkTypeName(node); + _checkForTypeNamePrefixShadowedByLocal(node); super.visitTypeName(node); } @@ -4088,6 +4089,21 @@ class ErrorVerifier extends RecursiveAstVisitor { } } + void _checkForTypeNamePrefixShadowedByLocal(TypeName node) { + var name = node.name; + if (name is PrefixedIdentifier && + name.staticElement is TypeDefiningElement) { + var prefix = name.prefix; + var prefixElement = prefix.staticElement; + if (prefixElement != null && prefixElement is! PrefixElement) { + _errorReporter.reportErrorForNode( + CompileTimeErrorCode.PREFIX_SHADOWED_BY_LOCAL_DECLARATION, + prefix, + [prefix.name]); + } + } + } + /// Check that none of the type [parameters] references itself in its bound. /// /// See [StaticTypeWarningCode.TYPE_PARAMETER_SUPERTYPE_OF_ITS_BOUND]. diff --git a/pkg/analyzer/test/src/diagnostics/new_with_non_type_test.dart b/pkg/analyzer/test/src/diagnostics/new_with_non_type_test.dart index 758cb1fffc87..04bd897d94c1 100644 --- a/pkg/analyzer/test/src/diagnostics/new_with_non_type_test.dart +++ b/pkg/analyzer/test/src/diagnostics/new_with_non_type_test.dart @@ -51,6 +51,19 @@ void f() { ]); } + test_malformed_constructor_call() async { + await assertErrorsInCode(''' +class C { + C.x(); +} +main() { + new C.x.y(); +} +''', [ + error(StaticWarningCode.NEW_WITH_NON_TYPE, 36, 3), + ]); + } + test_typeParameter() async { await assertErrorsInCode(''' void foo() { diff --git a/pkg/analyzer/test/src/diagnostics/prefix_shadowed_by_local_declaration_test.dart b/pkg/analyzer/test/src/diagnostics/prefix_shadowed_by_local_declaration_test.dart new file mode 100644 index 000000000000..b70ac8535d59 --- /dev/null +++ b/pkg/analyzer/test/src/diagnostics/prefix_shadowed_by_local_declaration_test.dart @@ -0,0 +1,69 @@ +// 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/src/error/codes.dart'; +import 'package:test_reflective_loader/test_reflective_loader.dart'; + +import '../dart/resolution/driver_resolution.dart'; + +main() { + defineReflectiveSuite(() { + defineReflectiveTests(PrefixShadowedByLocalDeclarationTest); + }); +} + +@reflectiveTest +class PrefixShadowedByLocalDeclarationTest extends DriverResolutionTest { + test_function_return_type_not_shadowed_by_parameter() async { + await assertNoErrorsInCode(''' +import 'dart:async' as a; +a.Future f(int a) { + return null; +} +'''); + } + + test_local_variable_type_inside_function_with_shadowing_parameter() async { + await assertErrorsInCode(''' +import 'dart:async' as a; +f(int a) { + a.Future x = null; + return x; +} +''', [ + error(HintCode.UNUSED_IMPORT, 7, 12), + error(CompileTimeErrorCode.PREFIX_SHADOWED_BY_LOCAL_DECLARATION, 39, 1), + ]); + } + + test_local_variable_type_inside_function_with_shadowing_variable_after() async { + await assertErrorsInCode(''' +import 'dart:async' as a; +f() { + a.Future x = null; + int a = 0; + return [x, a]; +} +''', [ + error(HintCode.UNUSED_IMPORT, 7, 12), + error(CompileTimeErrorCode.REFERENCED_BEFORE_DECLARATION, 34, 1, + contextMessages: [message('/test/lib/test.dart', 59, 1)]), + error(CompileTimeErrorCode.PREFIX_SHADOWED_BY_LOCAL_DECLARATION, 34, 1), + ]); + } + + test_local_variable_type_inside_function_with_shadowing_variable_before() async { + await assertErrorsInCode(''' +import 'dart:async' as a; +f() { + int a = 0; + a.Future x = null; + return [x, a]; +} +''', [ + error(HintCode.UNUSED_IMPORT, 7, 12), + error(CompileTimeErrorCode.PREFIX_SHADOWED_BY_LOCAL_DECLARATION, 47, 1), + ]); + } +} diff --git a/pkg/analyzer/test/src/diagnostics/test_all.dart b/pkg/analyzer/test/src/diagnostics/test_all.dart index 2c9a764a2f2c..bd8b9b5c4e08 100644 --- a/pkg/analyzer/test/src/diagnostics/test_all.dart +++ b/pkg/analyzer/test/src/diagnostics/test_all.dart @@ -394,6 +394,8 @@ import 'prefix_collides_with_top_level_member_test.dart' as prefix_collides_with_top_level_member; import 'prefix_identifier_not_followed_by_dot_test.dart' as prefix_identifier_not_followed_by_dot; +import 'prefix_shadowed_by_local_declaration_test.dart' + as prefix_shadowed_by_local_declaration; import 'private_collision_in_mixin_application_test.dart' as private_collision_in_mixin_application; import 'private_optional_parameter_test.dart' as private_optional_parameter; @@ -804,6 +806,7 @@ main() { part_of_non_part.main(); prefix_collides_with_top_level_member.main(); prefix_identifier_not_followed_by_dot.main(); + prefix_shadowed_by_local_declaration.main(); private_collision_in_mixin_application.main(); private_optional_parameter.main(); private_setter.main(); diff --git a/tests/language/class/variable_shadow_class_test.dart b/tests/language/class/variable_shadow_class_test.dart index 5e24390387b0..bf3f89573b41 100644 --- a/tests/language/class/variable_shadow_class_test.dart +++ b/tests/language/class/variable_shadow_class_test.dart @@ -17,7 +17,6 @@ main() { var i = new Test.named(10); // ^^^^^^^^^^ // [analyzer] STATIC_WARNING.CREATION_WITH_NON_TYPE - // ^^^^ // [cfe] Method not found: 'Test.named'. Expect.equals(10, i.field); } diff --git a/tests/language/prefix/shadow_test.dart b/tests/language/prefix/shadow_test.dart index 4a5bdc218c5e..1680b1ec7d69 100644 --- a/tests/language/prefix/shadow_test.dart +++ b/tests/language/prefix/shadow_test.dart @@ -11,6 +11,8 @@ import "../library10.dart" as lib10; class P { test() { new T.Library10(10); + // ^ + // [analyzer] COMPILE_TIME_ERROR.PREFIX_SHADOWED_BY_LOCAL_DECLARATION // ^ // [cfe] Method not found: 'T.Library10'. } diff --git a/tests/language_2/class/variable_shadow_class_test.dart b/tests/language_2/class/variable_shadow_class_test.dart index 5e24390387b0..bf3f89573b41 100644 --- a/tests/language_2/class/variable_shadow_class_test.dart +++ b/tests/language_2/class/variable_shadow_class_test.dart @@ -17,7 +17,6 @@ main() { var i = new Test.named(10); // ^^^^^^^^^^ // [analyzer] STATIC_WARNING.CREATION_WITH_NON_TYPE - // ^^^^ // [cfe] Method not found: 'Test.named'. Expect.equals(10, i.field); } diff --git a/tests/language_2/prefix/shadow_test.dart b/tests/language_2/prefix/shadow_test.dart index 4a5bdc218c5e..1680b1ec7d69 100644 --- a/tests/language_2/prefix/shadow_test.dart +++ b/tests/language_2/prefix/shadow_test.dart @@ -11,6 +11,8 @@ import "../library10.dart" as lib10; class P { test() { new T.Library10(10); + // ^ + // [analyzer] COMPILE_TIME_ERROR.PREFIX_SHADOWED_BY_LOCAL_DECLARATION // ^ // [cfe] Method not found: 'T.Library10'. }