Skip to content

Commit

Permalink
Version 3.7.0-143.0.dev
Browse files Browse the repository at this point in the history
Merge 9ea9386 into dev
  • Loading branch information
Dart CI committed Nov 13, 2024
2 parents 3f05b35 + 9ea9386 commit b1dc749
Show file tree
Hide file tree
Showing 20 changed files with 167 additions and 43 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -434,6 +434,27 @@ abstract interface class TypeAnalyzerOperations<
/// step too.
TypeStructure? matchFutureOrInternal(TypeStructure type);

/// If [type] is a parameter type with empty nullability suffix, returns its
/// bound, whether it is its type parameter bound or its promoted bound.
/// Otherwise, returns null.
///
/// In the example below matching the appearance of `X` in the parameters of
/// `foo` returns `num`, matching the appearance of `Y` in the parameters of
/// the function type `int Function<Y extends Object>(Y)` returns `Object`,
/// and matching the inferred type of the variable `z`, which is `X & int`,
/// `X` promoted to `int`, returns `int`. Matching a non-type parameter type,
/// such as `int`, would return null.
///
/// int foo<X extends num>(int Function<Y extends Object>(Y) f, X x) {
/// if (x is int) {
/// var z = x;
/// return z;
/// } else {
/// return f(x);
/// }
/// }
TypeStructure? matchTypeParameterBoundInternal(TypeStructure type);

/// If [type] is a parameter type that is of a kind used in type inference,
/// returns the corresponding parameter.
///
Expand Down
14 changes: 14 additions & 0 deletions pkg/_fe_analyzer_shared/test/mini_ast.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3297,6 +3297,20 @@ class MiniAstOperations
// TODO(paulberry): Implement greatest closure of types in mini ast.
throw UnimplementedError();
}

@override
Type? matchTypeParameterBoundInternal(Type type) {
if (type
case TypeParameterType(
:var promotion,
:var typeParameter,
nullabilitySuffix: NullabilitySuffix.none
)) {
return promotion ?? typeParameter.bound;
} else {
return null;
}
}
}

/// Representation of an expression or statement in the pseudo-Dart language
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -828,6 +828,33 @@ main() {
check(tcg._constraints).isEmpty();
});
});

group('matchTypeParameterBoundInternal', () {
test('Non-promoted parameter on LHS', () {
var tcg = _TypeConstraintGatherer({'T'});
check(tcg.performSubtypeConstraintGenerationInternal(
TypeParameterType(TypeRegistry.addTypeParameter('X')
..bound = Type('Future<String>')),
Type('Future<T>'),
leftSchema: false,
astNodeForTesting: Node.placeholder()))
.isTrue();
check(tcg._constraints).unorderedEquals(['String <: T']);
});

test('Promoted parameter on LHS', () {
var tcg = _TypeConstraintGatherer({'T'});
check(tcg.performSubtypeConstraintGenerationInternal(
TypeParameterType(
TypeRegistry.addTypeParameter('X')..bound = Type('Object'),
promotion: Type('Future<num>')),
Type('Future<T>'),
leftSchema: false,
astNodeForTesting: Node.placeholder()))
.isTrue();
check(tcg._constraints).unorderedEquals(['num <: T']);
});
});
}

class _TypeConstraintGatherer extends TypeConstraintGenerator<Type,
Expand Down Expand Up @@ -948,6 +975,14 @@ class _TypeConstraintGatherer extends TypeConstraintGenerator<Type,
return true;
}

if (typeAnalyzerOperations.matchTypeParameterBoundInternal(p)
case var bound?) {
if (performSubtypeConstraintGenerationInternal(bound, q,
leftSchema: leftSchema, astNodeForTesting: astNodeForTesting)) {
return true;
}
}

bool? result = performSubtypeConstraintGenerationForTypeDeclarationTypes(
p, q,
leftSchema: leftSchema, astNodeForTesting: astNodeForTesting);
Expand Down
8 changes: 8 additions & 0 deletions pkg/analyzer/lib/dart/constant/value.dart
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,14 @@ abstract class DartObject {
/// * the value of the object being represented is `null`.
Map<DartObject?, DartObject?>? toMapValue();

/// If this [DartObject] represents a record, returns the positional and named
/// fields of that record.
///
/// If the object being represented is not a [Record] `null` is returned
/// instead.
({List<DartObject> positional, Map<String, DartObject> named})?
toRecordValue();

/// Return a set corresponding to the value of the object being represented,
/// or `null` if
/// * this object is not of type 'Set', or
Expand Down
3 changes: 3 additions & 0 deletions pkg/analyzer/lib/dart/element/scope.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.

import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/element2.dart';

/// Scopes are used to resolve names to elements.
///
Expand All @@ -21,5 +22,7 @@ abstract class Scope {
/// Clients may not extend, implement or mix-in this class.
abstract class ScopeLookupResult {
Element? get getter;
Element2? get getter2;
Element? get setter;
Element2? get setter2;
}
10 changes: 10 additions & 0 deletions pkg/analyzer/lib/src/dart/constant/value.dart
Original file line number Diff line number Diff line change
Expand Up @@ -924,6 +924,16 @@ class DartObjectImpl implements DartObject, Constant {
return null;
}

@override
({List<DartObject> positional, Map<String, DartObject> named})?
toRecordValue() {
if (state case RecordState(:var positionalFields, :var namedFields)) {
return (positional: positionalFields, named: namedFields);
} else {
return null;
}
}

@override
Set<DartObjectImpl>? toSetValue() {
var state = this.state;
Expand Down
7 changes: 7 additions & 0 deletions pkg/analyzer/lib/src/dart/element/scope.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@
import 'package:_fe_analyzer_shared/src/scanner/string_canonicalizer.dart';
import 'package:analyzer/dart/analysis/features.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/element2.dart';
import 'package:analyzer/dart/element/scope.dart';
import 'package:analyzer/src/dart/element/element.dart';
import 'package:analyzer/src/dart/element/extensions.dart';
import 'package:analyzer/src/summary2/combinator.dart';
import 'package:analyzer/src/utilities/extensions/collection.dart';
import 'package:analyzer/src/utilities/extensions/element.dart';

/// The scope for the initializers in a constructor.
class ConstructorInitializerScope extends EnclosedScope {
Expand Down Expand Up @@ -774,6 +776,11 @@ class ScopeLookupResultImpl implements ScopeLookupResult {
required this.getter,
required this.setter,
});

@override
Element2? get getter2 => getter?.asElement2;
@override
Element2? get setter2 => setter?.asElement2;
}

class TypeParameterScope extends EnclosedScope {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/type.dart';
import 'package:analyzer/src/dart/ast/ast.dart';
import 'package:analyzer/src/dart/element/element.dart';
import 'package:analyzer/src/dart/element/type.dart';
import 'package:analyzer/src/dart/element/type_algebra.dart';
import 'package:analyzer/src/dart/element/type_schema.dart';
import 'package:analyzer/src/dart/element/type_system.dart';
Expand Down Expand Up @@ -326,10 +325,10 @@ class TypeConstraintGatherer extends shared.TypeConstraintGenerator<
// variable `X & B`), the match holds with constraint set `C`:
// If `B` is a subtype match for `Q` with constraint set `C`.
// Note: we have already eliminated the case that `X` is a variable in `L`.
if (P_nullability == NullabilitySuffix.none && P is TypeParameterTypeImpl) {
var B = P.promotedBound ?? P.element.bound;
if (B != null &&
trySubtypeMatch(B, Q, leftSchema, nodeForTesting: nodeForTesting)) {
if (typeAnalyzerOperations.matchTypeParameterBoundInternal(P)
case var bound?) {
if (performSubtypeConstraintGenerationInternal(bound, Q,
leftSchema: leftSchema, astNodeForTesting: nodeForTesting)) {
return true;
}
}
Expand Down
10 changes: 10 additions & 0 deletions pkg/analyzer/lib/src/dart/resolver/flow_analysis_visitor.dart
Original file line number Diff line number Diff line change
Expand Up @@ -736,6 +736,16 @@ class TypeSystemOperations
}
}

@override
DartType? matchTypeParameterBoundInternal(DartType type) {
if (type is TypeParameterTypeImpl &&
type.nullabilitySuffix == NullabilitySuffix.none) {
return type.promotedBound ?? type.element.bound;
} else {
return null;
}
}

@override
SharedTypeView<DartType> normalize(SharedTypeView<DartType> type) {
return SharedTypeView(typeSystem.normalize(type.unwrapTypeView()));
Expand Down
19 changes: 19 additions & 0 deletions pkg/analyzer/test/src/dart/constant/value_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1928,6 +1928,25 @@ class DartObjectImplTest {
_assertTimes(_intValue(null), _intValue(null), _intValue(3));
}

void test_toRecordValue_notARecord() {
expect(
_listValue(_typeProvider.boolType, [_boolValue(true), _boolValue(false)])
.toRecordValue(),
isNull,
);
}

void test_toRecordValue_null() {
expect(_nullValue().toRecordValue(), isNull);
}

void test_toRecordValue_record() {
var constant = _recordValue([_intValue(1)], {'bool': _boolValue(true)});
var (:positional, :named) = constant.toRecordValue()!;
expect(positional, [_intValue(1)]);
expect(named, {'bool': _boolValue(true)});
}

/// Assert that the result of executing [fn] is the [expected] value, or, if
/// [expected] is `null`, that the operation throws an exception .
void _assert(DartObjectImpl? expected, DartObjectImpl? Function() fn) {
Expand Down
16 changes: 5 additions & 11 deletions pkg/front_end/lib/src/type_inference/type_constraint_gatherer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -428,17 +428,11 @@ class TypeConstraintGatherer extends shared.TypeConstraintGenerator<
//
// If B is a subtype match for Q with constraint set C. Note that we have
// already eliminated the case that X is a variable in L.
if (p is TypeParameterType) {
if (_isNullabilityAwareSubtypeMatch(p.bound, q,
constrainSupertype: constrainSupertype,
treeNodeForTesting: treeNodeForTesting)) {
return true;
}
} else if (p is StructuralParameterType) {
// Coverage-ignore-block(suite): Not run.
if (_isNullabilityAwareSubtypeMatch(p.bound, q,
constrainSupertype: constrainSupertype,
treeNodeForTesting: treeNodeForTesting)) {
if (typeAnalyzerOperations.matchTypeParameterBoundInternal(p)
case DartType bound?) {
if (performSubtypeConstraintGenerationInternal(bound, q,
leftSchema: constrainSupertype,
astNodeForTesting: treeNodeForTesting)) {
return true;
}
}
Expand Down
17 changes: 17 additions & 0 deletions pkg/front_end/lib/src/type_inference/type_inference_engine.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1013,6 +1013,23 @@ class OperationsCfe
.functionRawType(Nullability.nonNullable))
.eliminateToLeast(type);
}

@override
DartType? matchTypeParameterBoundInternal(DartType type) {
if (type.nullabilitySuffix != NullabilitySuffix.none) {
return null;
}
if (type is TypeParameterType) {
return type.parameter.bound;
} else if (type is StructuralParameterType) {
// Coverage-ignore-block(suite): Not run.
return type.parameter.bound;
} else if (type is IntersectionType) {
return type.right;
} else {
return null;
}
}
}

/// Type inference results used for testing.
Expand Down
2 changes: 1 addition & 1 deletion pkg/front_end/test/coverage_suite_expected.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1046,7 +1046,7 @@ const Map<String, ({int hitCount, int missCount})> _expect = {
),
// 100.0%.
"package:front_end/src/type_inference/type_inference_engine.dart": (
hitCount: 536,
hitCount: 545,
missCount: 0,
),
// 100.0%.
Expand Down
2 changes: 0 additions & 2 deletions pkg/linter/analyzer_use_new_elements.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
lib/src/ast.dart
lib/src/extensions.dart
lib/src/rules/avoid_renaming_method_parameters.dart
lib/src/rules/avoid_setters_without_getters.dart
lib/src/rules/avoid_void_async.dart
lib/src/rules/deprecated_member_use_from_same_package.dart
Expand All @@ -14,5 +13,4 @@ lib/src/rules/use_build_context_synchronously.dart
lib/src/rules/use_late_for_private_fields_and_variables.dart
lib/src/util/dart_type_utilities.dart
lib/src/util/flutter_utils.dart
lib/src/util/scope.dart
test/rules/use_build_context_synchronously_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ class _Visitor extends SimpleAstVisitor<void> {
var result =
resolveNameInScope(name.lexeme, scope, shouldResolveSetter: false);
if (result.isRequestedName) {
var element = result.element2;
var element = result.element;
return element is ClassElement2 ||
element is ExtensionTypeElement2 ||
element is TypeAliasElement2 ||
Expand Down
2 changes: 1 addition & 1 deletion pkg/linter/lib/src/rules/unnecessary_this.dart
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ class _Visitor extends SimpleAstVisitor<void> {
// The requested element is inherited, or from an extension.
if (result.isNone) return true;

var resultElement = result.element2;
var resultElement = result.element;

// The result has the matching name, might be shadowing.
// Check that the element is the same.
Expand Down
13 changes: 4 additions & 9 deletions pkg/linter/lib/src/test_utilities/test_linter.dart
Original file line number Diff line number Diff line change
Expand Up @@ -44,20 +44,17 @@ class TestLinter implements AnalysisErrorListener {
var errors = <AnalysisErrorInfo>[];
var lintDriver = LintDriver(options, _resourceProvider);
errors.addAll(await lintDriver.analyze(files.where(isDartFile)));
files.where(isPubspecFile).forEach((file) {
var errorsForFile = lintPubspecSource(
for (var file in files.where(isPubspecFile)) {
lintPubspecSource(
contents: file.readAsStringSync(),
sourcePath: _resourceProvider.pathContext.normalize(file.absolute.path),
);
errors.addAll(errorsForFile);
});
}
return errors;
}

@visibleForTesting
Iterable<AnalysisErrorInfo> lintPubspecSource(
{required String contents, String? sourcePath}) {
var results = <AnalysisErrorInfo>[];
void lintPubspecSource({required String contents, String? sourcePath}) {
var sourceUrl = sourcePath == null ? null : path.toUri(sourcePath);
var spec = Pubspec.parse(contents, sourceUrl: sourceUrl);

Expand All @@ -78,8 +75,6 @@ class TestLinter implements AnalysisErrorListener {
}
}
}

return results;
}

@override
Expand Down
Loading

0 comments on commit b1dc749

Please sign in to comment.