[cfe] Handle extensions in Extension?.member check
Closes https://github.com/dart-lang/sdk/issues/49127 Change-Id: I26ce537dbed908789333f4aa6baa6b8487e5e993 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/246447 Commit-Queue: Johnni Winther <johnniwinther@google.com> Reviewed-by: Chloe Stefantsova <cstefantsova@google.com>
This commit is contained in:
committed by
Commit Bot
parent
40ad7aee40
commit
69b9af2230
@@ -3845,6 +3845,29 @@ const MessageCode messageExtensionDeclaresInstanceField = const MessageCode(
|
||||
correctionMessage:
|
||||
r"""Try removing the field declaration or making it a static field""");
|
||||
|
||||
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
|
||||
const Template<Message Function(String name)>
|
||||
templateExtensionInNullAwareReceiver =
|
||||
const Template<Message Function(String name)>(
|
||||
problemMessageTemplate: r"""The extension '#name' cannot be null.""",
|
||||
correctionMessageTemplate: r"""Try replacing '?.' with '.'""",
|
||||
withArguments: _withArgumentsExtensionInNullAwareReceiver);
|
||||
|
||||
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
|
||||
const Code<Message Function(String name)> codeExtensionInNullAwareReceiver =
|
||||
const Code<Message Function(String name)>("ExtensionInNullAwareReceiver",
|
||||
severity: Severity.warning);
|
||||
|
||||
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
|
||||
Message _withArgumentsExtensionInNullAwareReceiver(String name) {
|
||||
if (name.isEmpty) throw 'No name provided';
|
||||
name = demangleMixinApplicationName(name);
|
||||
return new Message(codeExtensionInNullAwareReceiver,
|
||||
problemMessage: """The extension '${name}' cannot be null.""",
|
||||
correctionMessage: """Try replacing '?.' with '.'""",
|
||||
arguments: {'name': name});
|
||||
}
|
||||
|
||||
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
|
||||
const Template<Message Function(String name)>
|
||||
templateExtensionMemberConflictsWithObjectMember =
|
||||
|
||||
@@ -2947,7 +2947,7 @@ class BodyBuilder extends StackListenerImpl
|
||||
assert(declaration.isStatic || declaration.isTopLevel);
|
||||
MemberBuilder memberBuilder = declaration as MemberBuilder;
|
||||
return new StaticAccessGenerator(
|
||||
this, token, name, memberBuilder.member, null);
|
||||
this, token, name, memberBuilder.parent, memberBuilder.member, null);
|
||||
} else if (declaration is PrefixBuilder) {
|
||||
assert(prefix == null);
|
||||
return new PrefixUseGenerator(this, token, declaration);
|
||||
|
||||
@@ -20,6 +20,7 @@ import '../builder/class_builder.dart';
|
||||
import '../builder/declaration_builder.dart';
|
||||
import '../builder/extension_builder.dart';
|
||||
import '../builder/invalid_type_declaration_builder.dart';
|
||||
import '../builder/library_builder.dart';
|
||||
import '../builder/member_builder.dart';
|
||||
import '../builder/named_type_builder.dart';
|
||||
import '../builder/nullability_builder.dart';
|
||||
@@ -1386,12 +1387,18 @@ class StaticAccessGenerator extends Generator {
|
||||
final int? typeOffset;
|
||||
final bool isNullAware;
|
||||
|
||||
/// The builder for the parent of [readTarget] and [writeTarget]. This is
|
||||
/// either the builder for the enclosing library, class, or extension.
|
||||
final Builder? parentBuilder;
|
||||
|
||||
StaticAccessGenerator(ExpressionGeneratorHelper helper, Token token,
|
||||
this.targetName, this.readTarget, this.writeTarget,
|
||||
this.targetName, this.parentBuilder, this.readTarget, this.writeTarget,
|
||||
{this.typeOffset, this.isNullAware: false})
|
||||
// ignore: unnecessary_null_comparison
|
||||
: assert(targetName != null),
|
||||
assert(readTarget != null || writeTarget != null),
|
||||
assert(parentBuilder is DeclarationBuilder ||
|
||||
parentBuilder is LibraryBuilder),
|
||||
super(helper, token);
|
||||
|
||||
factory StaticAccessGenerator.fromBuilder(
|
||||
@@ -1402,19 +1409,44 @@ class StaticAccessGenerator extends Generator {
|
||||
MemberBuilder? setterBuilder,
|
||||
{int? typeOffset,
|
||||
bool isNullAware: false}) {
|
||||
return new StaticAccessGenerator(helper, token, targetName,
|
||||
getterBuilder?.readTarget, setterBuilder?.writeTarget,
|
||||
typeOffset: typeOffset, isNullAware: isNullAware);
|
||||
// If both [getterBuilder] and [setterBuilder] exist, they must both be
|
||||
// either top level (potentially from different libraries) or from the same
|
||||
// class/extension.
|
||||
assert(getterBuilder == null ||
|
||||
setterBuilder == null ||
|
||||
(getterBuilder.parent is LibraryBuilder &&
|
||||
setterBuilder.parent is LibraryBuilder) ||
|
||||
getterBuilder.parent == setterBuilder.parent);
|
||||
return new StaticAccessGenerator(
|
||||
helper,
|
||||
token,
|
||||
targetName,
|
||||
getterBuilder?.parent ?? setterBuilder?.parent,
|
||||
getterBuilder?.readTarget,
|
||||
setterBuilder?.writeTarget,
|
||||
typeOffset: typeOffset,
|
||||
isNullAware: isNullAware);
|
||||
}
|
||||
|
||||
void _reportNonNullableInNullAwareWarningIfNeeded() {
|
||||
if (isNullAware && _helper.libraryBuilder.isNonNullableByDefault) {
|
||||
String className = (readTarget ?? writeTarget)!.enclosingClass!.name;
|
||||
_helper.libraryBuilder.addProblem(
|
||||
templateClassInNullAwareReceiver.withArguments(className),
|
||||
typeOffset ?? fileOffset,
|
||||
typeOffset != null ? className.length : noLength,
|
||||
_helper.uri);
|
||||
DeclarationBuilder declarationBuilder =
|
||||
parentBuilder as DeclarationBuilder;
|
||||
if (declarationBuilder.isExtension) {
|
||||
String extensionName = declarationBuilder.name;
|
||||
_helper.libraryBuilder.addProblem(
|
||||
templateExtensionInNullAwareReceiver.withArguments(extensionName),
|
||||
typeOffset ?? fileOffset,
|
||||
typeOffset != null ? extensionName.length : noLength,
|
||||
_helper.uri);
|
||||
} else {
|
||||
String className = declarationBuilder.name;
|
||||
_helper.libraryBuilder.addProblem(
|
||||
templateClassInNullAwareReceiver.withArguments(className),
|
||||
typeOffset ?? fileOffset,
|
||||
typeOffset != null ? className.length : noLength,
|
||||
_helper.uri);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -314,6 +314,7 @@ ExtendsVoid/example: Fail # Feature not yet enabled by default.
|
||||
ExtensionDeclaresAbstractMember/example: Fail
|
||||
ExtensionDeclaresConstructor/example: Fail
|
||||
ExtensionDeclaresInstanceField/example: Fail
|
||||
ExtensionInNullAwareReceiver/analyzerCode: Fail
|
||||
ExtensionMemberConflictsWithObjectMember/analyzerCode: Fail
|
||||
ExternalConstructorWithBody/part_wrapped_script1: Fail
|
||||
ExternalConstructorWithBody/script1: Fail
|
||||
|
||||
@@ -5223,6 +5223,19 @@ ClassInNullAwareReceiver:
|
||||
C?.field;
|
||||
}
|
||||
|
||||
ExtensionInNullAwareReceiver:
|
||||
problemMessage: "The extension '#name' cannot be null."
|
||||
correctionMessage: "Try replacing '?.' with '.'"
|
||||
severity: WARNING
|
||||
configuration: nnbd-strong
|
||||
script: |
|
||||
extension E on int {
|
||||
static var field;
|
||||
}
|
||||
method() {
|
||||
E?.field;
|
||||
}
|
||||
|
||||
NonNullableNotAssignedError:
|
||||
problemMessage: "Non-nullable variable '#name' must be assigned before it can be used."
|
||||
configuration: nnbd-strong
|
||||
|
||||
@@ -187,7 +187,8 @@ Future<void> main() async {
|
||||
"StaticAccessGenerator(offset: 4, targetName: foo,"
|
||||
" readTarget: $uri::myGetter,"
|
||||
" writeTarget: $uri::mySetter)",
|
||||
new StaticAccessGenerator(helper, token, 'foo', getter, setter));
|
||||
new StaticAccessGenerator(
|
||||
helper, token, 'foo', libraryBuilder, getter, setter));
|
||||
check(
|
||||
"LoadLibraryGenerator(offset: 4,"
|
||||
" builder: Instance of 'LoadLibraryBuilder')",
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
// Copyright (c) 2022, 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.
|
||||
|
||||
extension E on int {
|
||||
static String s = "Lily was here";
|
||||
}
|
||||
|
||||
test() {
|
||||
E?.s;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
extension E on int {
|
||||
static String s = "Lily was here";
|
||||
}
|
||||
|
||||
test() {}
|
||||
@@ -0,0 +1,5 @@
|
||||
extension E on int {
|
||||
static String s = "Lily was here";
|
||||
}
|
||||
|
||||
test() {}
|
||||
@@ -0,0 +1,19 @@
|
||||
library /*isNonNullableByDefault*/;
|
||||
//
|
||||
// Problems in library:
|
||||
//
|
||||
// pkg/front_end/testcases/general/issue49127.dart:10:3: Warning: The extension 'E' cannot be null.
|
||||
// Try replacing '?.' with '.'
|
||||
// E?.s;
|
||||
// ^
|
||||
//
|
||||
import self as self;
|
||||
import "dart:core" as core;
|
||||
|
||||
extension E on core::int {
|
||||
static field s = self::E|s;
|
||||
}
|
||||
static field core::String E|s = "Lily was here";
|
||||
static method test() → dynamic {
|
||||
self::E|s;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
library /*isNonNullableByDefault*/;
|
||||
//
|
||||
// Problems in library:
|
||||
//
|
||||
// pkg/front_end/testcases/general/issue49127.dart:10:3: Warning: The extension 'E' cannot be null.
|
||||
// Try replacing '?.' with '.'
|
||||
// E?.s;
|
||||
// ^
|
||||
//
|
||||
import self as self;
|
||||
import "dart:core" as core;
|
||||
|
||||
extension E on core::int {
|
||||
static field s = self::E|s;
|
||||
}
|
||||
static field core::String E|s = "Lily was here";
|
||||
static method test() → dynamic {
|
||||
self::E|s;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
library /*isNonNullableByDefault*/;
|
||||
import self as self;
|
||||
import "dart:core" as core;
|
||||
|
||||
extension E on core::int {
|
||||
static field s = self::E|s;
|
||||
}
|
||||
static field core::String E|s;
|
||||
static method test() → dynamic
|
||||
;
|
||||
@@ -0,0 +1,19 @@
|
||||
library /*isNonNullableByDefault*/;
|
||||
//
|
||||
// Problems in library:
|
||||
//
|
||||
// pkg/front_end/testcases/general/issue49127.dart:10:3: Warning: The extension 'E' cannot be null.
|
||||
// Try replacing '?.' with '.'
|
||||
// E?.s;
|
||||
// ^
|
||||
//
|
||||
import self as self;
|
||||
import "dart:core" as core;
|
||||
|
||||
extension E on core::int {
|
||||
static field s = self::E|s;
|
||||
}
|
||||
static field core::String E|s = "Lily was here";
|
||||
static method test() → dynamic {
|
||||
self::E|s;
|
||||
}
|
||||
Reference in New Issue
Block a user