mirror of
https://github.com/dart-lang/sdk
synced 2024-10-14 08:51:21 +00:00
[analyzer] Report invalid use of visibility-restricted elements in object patterns
Bug: https://github.com/dart-lang/sdk/issues/51750 Change-Id: Ifefddb398cc612eb8b5af1b945a2a2641ea960e3 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/290264 Reviewed-by: Konstantin Shcheglov <scheglov@google.com> Commit-Queue: Samuel Rawlins <srawlins@google.com>
This commit is contained in:
parent
f59e7d3aaa
commit
6ebf04576d
|
@ -3,6 +3,7 @@
|
|||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
import 'package:analyzer/dart/ast/ast.dart';
|
||||
import 'package:analyzer/dart/ast/syntactic_entity.dart';
|
||||
import 'package:analyzer/dart/element/element.dart';
|
||||
import 'package:analyzer/dart/element/type.dart';
|
||||
import 'package:analyzer/src/dart/ast/ast.dart';
|
||||
|
@ -200,6 +201,25 @@ extension ListOfFormalParameterExtension on List<FormalParameter> {
|
|||
}
|
||||
}
|
||||
|
||||
extension PatternFieldImplExtension on PatternFieldImpl {
|
||||
/// A [SyntacticEntity] which can be used in error reporting, which is valid
|
||||
/// for both explicit getter names (like `Rect(width: var w, height: var h)`)
|
||||
/// and implicit getter names (like `Rect(:var width, :var height)`).
|
||||
SyntacticEntity get errorEntity {
|
||||
var fieldName = name;
|
||||
if (fieldName == null) {
|
||||
return this;
|
||||
}
|
||||
var fieldNameName = fieldName.name;
|
||||
if (fieldNameName == null) {
|
||||
var variablePattern = pattern.variablePattern;
|
||||
return variablePattern?.name ?? this;
|
||||
} else {
|
||||
return fieldNameName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension RecordTypeAnnotationExtension on RecordTypeAnnotation {
|
||||
List<RecordTypeAnnotationField> get fields {
|
||||
return [
|
||||
|
|
|
@ -390,6 +390,7 @@ class BestPracticesVerifier extends RecursiveAstVisitor<void> {
|
|||
_errorReporter.reportErrorForNode(
|
||||
WarningCode.CAST_FROM_NULL_ALWAYS_FAILS, node);
|
||||
}
|
||||
super.visitCastPattern(node);
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -806,6 +807,12 @@ class BestPracticesVerifier extends RecursiveAstVisitor<void> {
|
|||
super.visitNamedType(node);
|
||||
}
|
||||
|
||||
@override
|
||||
void visitPatternField(PatternField node) {
|
||||
_invalidAccessVerifier.verifyPatternField(node as PatternFieldImpl);
|
||||
super.visitPatternField(node);
|
||||
}
|
||||
|
||||
@override
|
||||
void visitPostfixExpression(PostfixExpression node) {
|
||||
_deprecatedVerifier.postfixExpression(node);
|
||||
|
@ -2077,6 +2084,30 @@ class _InvalidAccessVerifier {
|
|||
}
|
||||
}
|
||||
|
||||
void verifyPatternField(PatternFieldImpl node) {
|
||||
var element = node.element;
|
||||
if (element == null || _inCurrentLibrary(element)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (_hasInternal(element) &&
|
||||
!_isLibraryInWorkspacePackage(element.library)) {
|
||||
var fieldName = node.name;
|
||||
if (fieldName == null) {
|
||||
return;
|
||||
}
|
||||
var errorEntity = node.errorEntity;
|
||||
|
||||
_errorReporter.reportErrorForOffset(
|
||||
WarningCode.INVALID_USE_OF_INTERNAL_MEMBER,
|
||||
errorEntity.offset,
|
||||
errorEntity.length,
|
||||
[element.displayName]);
|
||||
}
|
||||
|
||||
_checkForOtherInvalidAccess(node, element);
|
||||
}
|
||||
|
||||
void verifySuperConstructorInvocation(SuperConstructorInvocation node) {
|
||||
if (node.constructorName != null) {
|
||||
// Named constructor calls are handled by [verify].
|
||||
|
@ -2090,8 +2121,7 @@ class _InvalidAccessVerifier {
|
|||
}
|
||||
}
|
||||
|
||||
void _checkForInvalidInternalAccess(
|
||||
SimpleIdentifier identifier, Element element) {
|
||||
void _checkForInvalidInternalAccess(Identifier identifier, Element element) {
|
||||
if (_hasInternal(element) &&
|
||||
!_isLibraryInWorkspacePackage(element.library)) {
|
||||
String name;
|
||||
|
@ -2112,8 +2142,7 @@ class _InvalidAccessVerifier {
|
|||
}
|
||||
}
|
||||
|
||||
void _checkForOtherInvalidAccess(
|
||||
SimpleIdentifier identifier, Element element) {
|
||||
void _checkForOtherInvalidAccess(AstNode node, Element element) {
|
||||
bool hasProtected = _hasProtected(element);
|
||||
if (hasProtected) {
|
||||
var definingClass = element.enclosingElement as InterfaceElement;
|
||||
|
@ -2124,14 +2153,14 @@ class _InvalidAccessVerifier {
|
|||
|
||||
bool isVisibleForTemplateApplied = _isVisibleForTemplateApplied(element);
|
||||
if (isVisibleForTemplateApplied) {
|
||||
if (_inTemplateSource || _inExportDirective(identifier)) {
|
||||
if (_inTemplateSource || _inExportDirective(node)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bool hasVisibleForTesting = _hasVisibleForTesting(element);
|
||||
if (hasVisibleForTesting) {
|
||||
if (_inTestDirectory || _inExportDirective(identifier)) {
|
||||
if (_inTestDirectory || _inExportDirective(node)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -2143,37 +2172,49 @@ class _InvalidAccessVerifier {
|
|||
// annotation present.
|
||||
|
||||
String name;
|
||||
AstNode node;
|
||||
SyntacticEntity errorEntity = node;
|
||||
|
||||
var grandparent = identifier.parent?.parent;
|
||||
|
||||
if (grandparent is ConstructorName) {
|
||||
name = grandparent.toSource();
|
||||
node = grandparent;
|
||||
var grandparent = node.parent?.parent;
|
||||
if (node is Identifier) {
|
||||
if (grandparent is ConstructorName) {
|
||||
name = grandparent.toSource();
|
||||
errorEntity = grandparent;
|
||||
} else {
|
||||
name = node.name;
|
||||
}
|
||||
} else if (node is PatternFieldImpl) {
|
||||
name = element.displayName;
|
||||
errorEntity = node.errorEntity;
|
||||
} else {
|
||||
name = identifier.name;
|
||||
node = identifier;
|
||||
throw StateError('Can only handle Identifier or PatternField, but got '
|
||||
'${node.runtimeType}');
|
||||
}
|
||||
|
||||
var definingClass = element.enclosingElement;
|
||||
if (definingClass == null) {
|
||||
return;
|
||||
}
|
||||
if (hasProtected) {
|
||||
_errorReporter.reportErrorForNode(
|
||||
_errorReporter.reportErrorForOffset(
|
||||
WarningCode.INVALID_USE_OF_PROTECTED_MEMBER,
|
||||
node,
|
||||
[name, definingClass!.source!.uri]);
|
||||
errorEntity.offset,
|
||||
errorEntity.length,
|
||||
[name, definingClass.source!.uri]);
|
||||
}
|
||||
if (isVisibleForTemplateApplied) {
|
||||
_errorReporter.reportErrorForNode(
|
||||
_errorReporter.reportErrorForOffset(
|
||||
WarningCode.INVALID_USE_OF_VISIBLE_FOR_TEMPLATE_MEMBER,
|
||||
node,
|
||||
[name, definingClass!.source!.uri]);
|
||||
errorEntity.offset,
|
||||
errorEntity.length,
|
||||
[name, definingClass.source!.uri]);
|
||||
}
|
||||
|
||||
if (hasVisibleForTesting) {
|
||||
_errorReporter.reportErrorForNode(
|
||||
_errorReporter.reportErrorForOffset(
|
||||
WarningCode.INVALID_USE_OF_VISIBLE_FOR_TESTING_MEMBER,
|
||||
node,
|
||||
[name, definingClass!.source!.uri]);
|
||||
errorEntity.offset,
|
||||
errorEntity.length,
|
||||
[name, definingClass.source!.uri]);
|
||||
}
|
||||
|
||||
if (hasVisibleForOverriding) {
|
||||
|
@ -2183,14 +2224,15 @@ class _InvalidAccessVerifier {
|
|||
parent is PropertyAccess && parent.target is SuperExpression) {
|
||||
var methodDeclaration =
|
||||
grandparent?.thisOrAncestorOfType<MethodDeclaration>();
|
||||
if (methodDeclaration?.name.lexeme == identifier.name) {
|
||||
if (methodDeclaration?.name.lexeme == name) {
|
||||
validOverride = true;
|
||||
}
|
||||
}
|
||||
if (!validOverride) {
|
||||
_errorReporter.reportErrorForNode(
|
||||
_errorReporter.reportErrorForOffset(
|
||||
WarningCode.INVALID_USE_OF_VISIBLE_FOR_OVERRIDING_MEMBER,
|
||||
node,
|
||||
errorEntity.offset,
|
||||
errorEntity.length,
|
||||
[name]);
|
||||
}
|
||||
}
|
||||
|
@ -2282,9 +2324,8 @@ class _InvalidAccessVerifier {
|
|||
|
||||
bool _inCurrentLibrary(Element element) => element.library == _library;
|
||||
|
||||
bool _inExportDirective(SimpleIdentifier identifier) =>
|
||||
identifier.parent is Combinator &&
|
||||
identifier.parent!.parent is ExportDirective;
|
||||
bool _inExportDirective(AstNode node) =>
|
||||
node.parent is Combinator && node.parent!.parent is ExportDirective;
|
||||
|
||||
bool _isLibraryInWorkspacePackage(LibraryElement? library) {
|
||||
if (_workspacePackage == null || library == null) {
|
||||
|
|
|
@ -29,9 +29,7 @@ class InvalidUseOfInternalMemberTest extends PubPackageResolutionTest {
|
|||
..add(
|
||||
name: 'foo',
|
||||
rootPath: fooPackageRootPath,
|
||||
languageVersion: '2.9',
|
||||
),
|
||||
languageVersion: '2.9',
|
||||
meta: true,
|
||||
);
|
||||
}
|
||||
|
@ -68,6 +66,96 @@ A a = A();
|
|||
]);
|
||||
}
|
||||
|
||||
test_outsidePackage_class_inAsExpression() async {
|
||||
newFile('$fooPackageRootPath/lib/src/a.dart', '''
|
||||
import 'package:meta/meta.dart';
|
||||
@internal
|
||||
class A {}
|
||||
''');
|
||||
|
||||
await assertErrorsInCode('''
|
||||
import 'package:foo/src/a.dart';
|
||||
|
||||
void f(Object o) {
|
||||
o as A;
|
||||
}
|
||||
''', [
|
||||
error(WarningCode.INVALID_USE_OF_INTERNAL_MEMBER, 60, 1),
|
||||
]);
|
||||
}
|
||||
|
||||
test_outsidePackage_class_inCastPattern() async {
|
||||
newFile('$fooPackageRootPath/lib/src/a.dart', '''
|
||||
import 'package:meta/meta.dart';
|
||||
@internal
|
||||
class A {}
|
||||
''');
|
||||
|
||||
await assertErrorsInCode('''
|
||||
import 'package:foo/src/a.dart';
|
||||
|
||||
void f(Object a, Object b) {
|
||||
(b as A, ) = (a, );
|
||||
}
|
||||
''', [
|
||||
error(WarningCode.INVALID_USE_OF_INTERNAL_MEMBER, 71, 1),
|
||||
]);
|
||||
}
|
||||
|
||||
test_outsidePackage_class_inIsExpression() async {
|
||||
newFile('$fooPackageRootPath/lib/src/a.dart', '''
|
||||
import 'package:meta/meta.dart';
|
||||
@internal
|
||||
class A {}
|
||||
''');
|
||||
|
||||
await assertErrorsInCode('''
|
||||
import 'package:foo/src/a.dart';
|
||||
|
||||
void f(Object o) {
|
||||
o is A;
|
||||
}
|
||||
''', [
|
||||
error(WarningCode.INVALID_USE_OF_INTERNAL_MEMBER, 60, 1),
|
||||
]);
|
||||
}
|
||||
|
||||
test_outsidePackage_class_inObjectPattern() async {
|
||||
newFile('$fooPackageRootPath/lib/src/a.dart', '''
|
||||
import 'package:meta/meta.dart';
|
||||
@internal
|
||||
class A {}
|
||||
''');
|
||||
|
||||
await assertErrorsInCode('''
|
||||
import 'package:foo/src/a.dart';
|
||||
|
||||
void f(Object a, Object b) {
|
||||
switch (a) {
|
||||
case A(): print('yes');
|
||||
}
|
||||
}
|
||||
''', [
|
||||
error(WarningCode.INVALID_USE_OF_INTERNAL_MEMBER, 87, 1),
|
||||
]);
|
||||
}
|
||||
|
||||
test_outsidePackage_class_inVariableDeclarationType() async {
|
||||
newFile('$fooPackageRootPath/lib/src/a.dart', '''
|
||||
import 'package:meta/meta.dart';
|
||||
@internal
|
||||
class A {}
|
||||
''');
|
||||
|
||||
await assertErrorsInCode('''
|
||||
import 'package:foo/src/a.dart';
|
||||
|
||||
A? a;
|
||||
''', [
|
||||
error(WarningCode.INVALID_USE_OF_INTERNAL_MEMBER, 34, 1),
|
||||
]);
|
||||
}
|
||||
|
||||
test_outsidePackage_constructor_named() async {
|
||||
newFile('$fooPackageRootPath/lib/src/a.dart', '''
|
||||
import 'package:meta/meta.dart';
|
||||
|
@ -153,6 +241,28 @@ int a = 'hello'.f();
|
|||
]);
|
||||
}
|
||||
|
||||
test_outsidePackage_field_inObjectPattern() async {
|
||||
newFile('$fooPackageRootPath/lib/src/a.dart', '''
|
||||
import 'package:meta/meta.dart';
|
||||
class A {
|
||||
@internal
|
||||
int get a => 42;
|
||||
}
|
||||
''');
|
||||
|
||||
await assertErrorsInCode('''
|
||||
import 'package:foo/src/a.dart';
|
||||
|
||||
void f(Object o) {
|
||||
switch (o) {
|
||||
case A(a: 7): print('yes');
|
||||
}
|
||||
}
|
||||
''', [
|
||||
error(WarningCode.INVALID_USE_OF_INTERNAL_MEMBER, 79, 1),
|
||||
]);
|
||||
}
|
||||
|
||||
test_outsidePackage_function() async {
|
||||
newFile('$fooPackageRootPath/lib/src/a.dart', '''
|
||||
import 'package:meta/meta.dart';
|
||||
|
@ -491,7 +601,7 @@ f() {
|
|||
newFile('$fooPackageRootPath/lib/src/a.dart', '''
|
||||
import 'package:meta/meta.dart';
|
||||
class C {
|
||||
int get s() => 1;
|
||||
int? get s() => 1;
|
||||
|
||||
@internal
|
||||
set s(int value) {}
|
||||
|
|
|
@ -231,6 +231,29 @@ class B {
|
|||
]);
|
||||
}
|
||||
|
||||
test_getter_outsideClassAndLibrary_inObjectPattern() async {
|
||||
newFile('$testPackageLibPath/lib1.dart', r'''
|
||||
import 'package:meta/meta.dart';
|
||||
class A {
|
||||
@protected
|
||||
int get a => 42;
|
||||
}
|
||||
''');
|
||||
newFile('$testPackageLibPath/lib2.dart', r'''
|
||||
import 'lib1.dart';
|
||||
void f(Object o) {
|
||||
switch (o) {
|
||||
case A(a: 7): print('yes');
|
||||
}
|
||||
}
|
||||
''');
|
||||
|
||||
await _resolveFile('$testPackageLibPath/lib1.dart');
|
||||
await _resolveFile('$testPackageLibPath/lib2.dart', [
|
||||
error(WarningCode.INVALID_USE_OF_PROTECTED_MEMBER, 65, 1),
|
||||
]);
|
||||
}
|
||||
|
||||
test_getter_subclass() async {
|
||||
await assertNoErrorsInCode(r'''
|
||||
import 'package:meta/meta.dart';
|
||||
|
|
|
@ -111,6 +111,29 @@ class B {
|
|||
]);
|
||||
}
|
||||
|
||||
test_getter_inObjectPattern() async {
|
||||
newFile('$testPackageLibPath/a.dart', '''
|
||||
import 'package:meta/meta.dart';
|
||||
|
||||
class A {
|
||||
@visibleForOverriding
|
||||
int get g => 0;
|
||||
}
|
||||
''');
|
||||
|
||||
await assertErrorsInCode('''
|
||||
import 'a.dart';
|
||||
|
||||
void f(Object o) {
|
||||
switch (o) {
|
||||
case A(g: 7): print('yes');
|
||||
}
|
||||
}
|
||||
''', [
|
||||
error(WarningCode.INVALID_USE_OF_VISIBLE_FOR_OVERRIDING_MEMBER, 63, 1),
|
||||
]);
|
||||
}
|
||||
|
||||
test_operator() async {
|
||||
newFile('$testPackageLibPath/a.dart', '''
|
||||
import 'package:meta/meta.dart';
|
||||
|
|
|
@ -198,6 +198,29 @@ void main() {
|
|||
]);
|
||||
}
|
||||
|
||||
test_getter_inObjectPattern() async {
|
||||
newFile('$testPackageRootPath/lib1.dart', r'''
|
||||
import 'package:meta/meta.dart';
|
||||
class A {
|
||||
@visibleForTesting
|
||||
int get g => 7;
|
||||
}
|
||||
''');
|
||||
newFile('$testPackageRootPath/lib2.dart', r'''
|
||||
import 'lib1.dart';
|
||||
void f(Object o) {
|
||||
switch (o) {
|
||||
case A(g: 7): print('yes');
|
||||
}
|
||||
}
|
||||
''');
|
||||
|
||||
await _resolveFile('$testPackageRootPath/lib1.dart');
|
||||
await _resolveFile('$testPackageRootPath/lib2.dart', [
|
||||
error(WarningCode.INVALID_USE_OF_VISIBLE_FOR_TESTING_MEMBER, 65, 1),
|
||||
]);
|
||||
}
|
||||
|
||||
test_import_hide() async {
|
||||
newFile('$testPackageLibPath/a.dart', r'''
|
||||
import 'package:meta/meta.dart';
|
||||
|
|
Loading…
Reference in a new issue