Skip to content

Commit

Permalink
Issue an error if a prefix in a type name is shadowed by a local vari…
Browse files Browse the repository at this point in the history
…able.

See #42620.

Change-Id: Ifd18c939082935390df0c2aa0bd724cf6f76c27c
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/153705
Commit-Queue: Paul Berry <[email protected]>
Reviewed-by: Konstantin Shcheglov <[email protected]>
  • Loading branch information
stereotype441 authored and [email protected] committed Jul 9, 2020
1 parent fe9596b commit 9f8aaf6
Show file tree
Hide file tree
Showing 10 changed files with 124 additions and 2 deletions.
1 change: 1 addition & 0 deletions pkg/analyzer/lib/error/error.dart
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,7 @@ const List<ErrorCode> 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,
Expand Down
18 changes: 18 additions & 0 deletions pkg/analyzer/lib/src/error/codes.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
16 changes: 16 additions & 0 deletions pkg/analyzer/lib/src/generated/error_verifier.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1155,6 +1155,7 @@ class ErrorVerifier extends RecursiveAstVisitor<void> {
@override
void visitTypeName(TypeName node) {
_typeArgumentsVerifier.checkTypeName(node);
_checkForTypeNamePrefixShadowedByLocal(node);
super.visitTypeName(node);
}

Expand Down Expand Up @@ -4088,6 +4089,21 @@ class ErrorVerifier extends RecursiveAstVisitor<void> {
}
}

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].
Expand Down
13 changes: 13 additions & 0 deletions pkg/analyzer/test/src/diagnostics/new_with_non_type_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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<T>() {
Expand Down
Original file line number Diff line number Diff line change
@@ -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),
]);
}
}
3 changes: 3 additions & 0 deletions pkg/analyzer/test/src/diagnostics/test_all.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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();
Expand Down
1 change: 0 additions & 1 deletion tests/language/class/variable_shadow_class_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
2 changes: 2 additions & 0 deletions tests/language/prefix/shadow_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import "../library10.dart" as lib10;
class P<T> {
test() {
new T.Library10(10);
// ^
// [analyzer] COMPILE_TIME_ERROR.PREFIX_SHADOWED_BY_LOCAL_DECLARATION
// ^
// [cfe] Method not found: 'T.Library10'.
}
Expand Down
1 change: 0 additions & 1 deletion tests/language_2/class/variable_shadow_class_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
2 changes: 2 additions & 0 deletions tests/language_2/prefix/shadow_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import "../library10.dart" as lib10;
class P<T> {
test() {
new T.Library10(10);
// ^
// [analyzer] COMPILE_TIME_ERROR.PREFIX_SHADOWED_BY_LOCAL_DECLARATION
// ^
// [cfe] Method not found: 'T.Library10'.
}
Expand Down

0 comments on commit 9f8aaf6

Please sign in to comment.