Skip to content

Commit

Permalink
Version 3.5.0-92.0.dev
Browse files Browse the repository at this point in the history
Merge ce99413 into dev
  • Loading branch information
Dart CI committed Apr 23, 2024
2 parents 6a670b6 + ce99413 commit cf91449
Show file tree
Hide file tree
Showing 20 changed files with 440 additions and 65 deletions.
2 changes: 1 addition & 1 deletion DEPS
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ vars = {
"args_rev": "5c83bc9785d6c32ffe6824ba79fadcc51fbcd1c1",
"async_rev": "47968047eb9888f74ca0691640821bd55b47e763",
"bazel_worker_rev": "79d2ad13c83d5e0883136503d86ddf60fe665900",
"benchmark_harness_rev": "aa139fdf3a3b829fa8c10719102c66729495afb6",
"benchmark_harness_rev": "197702c2c73e58eb2d31c1fd83cc5d8096d3eceb",
"boolean_selector_rev": "24635df68661bb44c1c13fb405562421e24298e5",
"browser_launcher_rev": "c4b2c81aa9debcce3651eda1b68a9bc5d5adf400",
"characters_rev": "7633a16a22c626e19ca750223237396315268a06",
Expand Down
256 changes: 256 additions & 0 deletions benchmarks/ObjectHashPerf/dart/ObjectHashPerf.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,256 @@
// Copyright (c) 2024, 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.

// ignore_for_file: hash_and_equals

// Benchmark for `Object.hash` and `Object.hashAll`.

import 'dart:math';

import 'package:benchmark_harness/perf_benchmark_harness.dart';

int get nextHash => Random().nextInt(0x20000000);

// An object with a fast hashCode.
class Leaf {
@override
final int hashCode = nextHash;
}

abstract class Node5 {
final item1 = Leaf();
final item2 = Leaf();
final item3 = Leaf();
final item4 = Random().nextBool();
final item5 = nextHash;
}

class Node5Hash extends Node5 {
// This is the main subject of the benchmark - a typical use of `Object.hash`.
@override
int get hashCode => Object.hash(item1, item2, item3, item4, item5);
}

class Node5Manual extends Node5 {
// This is a similar quality hashCode but with statically resolvable
// `hashCode` calls and a 0 seed (instead of loading a unique random seed from
// global late-final variable).
@override
int get hashCode => _SystemHash.hash5(item1.hashCode, item2.hashCode,
item3.hashCode, item4.hashCode, item5.hashCode, 0);
}

class Node5List extends Node5 {
// This is a pattern that is sometimes used, especially for large numbers of
// items.
@override
int get hashCode => Object.hashAll([item1, item2, item3, item4, item5]);
}

/// Returns a list with most values created by [makeValue], and a few objects of
/// different types so that the `hashCode` calls are polymorphic, like the ones
/// in the hashed collections.
List generateData(Object Function(int) makeValue) {
final List data = List.generate(1000, makeValue);
final exceptions = [
Leaf(),
Node5Hash(),
Node5Manual(),
Node5List(),
'',
true,
false,
123,
Object()
];
data.setRange(1, 1 + exceptions.length, exceptions);
return data;
}

class BenchmarkNode5Hash extends PerfBenchmarkBase {
final List data = generateData((_) => Node5Hash());

BenchmarkNode5Hash() : super('ObjectHashPerf.hash.5');

@override
void run() {
for (final e in data) {
sink = e.hashCode;
}
}
}

class BenchmarkNode5Manual extends PerfBenchmarkBase {
final List data = generateData((_) => Node5Manual());

BenchmarkNode5Manual() : super('ObjectHashPerf.manual.5');

@override
void run() {
for (final e in data) {
sink = e.hashCode;
}
}
}

class BenchmarkNode5List extends PerfBenchmarkBase {
final List data = generateData((_) => Node5List());

BenchmarkNode5List() : super('ObjectHashPerf.list.5');

@override
void run() {
for (final e in data) {
sink = e.hashCode;
}
}
}

class BenchmarkNode5HashHashAll extends PerfBenchmarkBase {
final List data = generateData((_) => Node5Hash());

BenchmarkNode5HashHashAll() : super('ObjectHashPerf.hash.5.hashAll');

@override
void run() {
sink = Object.hashAll(data);
}
}

class BenchmarkNode5ManualHashAll extends PerfBenchmarkBase {
final List data = generateData((_) => Node5Manual());

BenchmarkNode5ManualHashAll() : super('ObjectHashPerf.manual.5.hashAll');

@override
void run() {
sink = Object.hashAll(data);
}
}

Object? sink;

void main() async {
generalUses();

final benchmarks = [
BenchmarkNode5Hash.new,
BenchmarkNode5Manual.new,
BenchmarkNode5List.new,
BenchmarkNode5HashHashAll.new,
BenchmarkNode5ManualHashAll.new,
];

// Warmup all benchmarks so that JIT compilers see full polymorphism before
// measuring.
for (var benchmark in benchmarks) {
benchmark().warmup();
}

if (sink == null) throw StateError('sink unassigned');

generalUses();

for (var benchmark in benchmarks) {
await benchmark().reportPerf();
}
}

/// Does a variety of calls to `Object.hash` to ensure the compiler does not
/// over-specialize the code on a few benchmark inputs.
void generalUses() {
void check(int a, int b) {
if (a != b) throw StateError('inconsistent');
}

// Exercise arity dispatch.
check(Object.hash(1, 2), Object.hash(1, 2));
check(Object.hash(1, 2, 3), Object.hash(1, 2, 3));
check(Object.hash(1, 2, 3, 4), Object.hash(1, 2, 3, 4));
check(Object.hash(1, 2, 3, 4, 5), Object.hash(1, 2, 3, 4, 5));
check(Object.hash(1, 2, 3, 4, 5, 6), Object.hash(1, 2, 3, 4, 5, 6));
check(Object.hash(1, 2, 3, 4, 5, 6, 7), Object.hash(1, 2, 3, 4, 5, 6, 7));

final xs = Iterable.generate(20).toList();
check(Function.apply(Object.hash, xs), Function.apply(Object.hash, xs));

// Exercise internal hashCode dispatch.
final a1 = 123;
final a2 = 'hello';
final a3 = true;
final a4 = Object();
final a5 = StringBuffer();
const a6 = Point<int>(1, 2);
const a7 = Rectangle<int>(100, 200, 1, 1);

check(Object.hash(a1, a2, a3, a4, a5), Object.hash(a1, a2, a3, a4, a5));
check(Object.hash(a2, a3, a4, a5, a6), Object.hash(a2, a3, a4, a5, a6));
check(Object.hash(a3, a4, a5, a6, a7), Object.hash(a3, a4, a5, a6, a7));
check(Object.hash(a4, a5, a6, a7, a1), Object.hash(a4, a5, a6, a7, a1));
check(Object.hash(a5, a6, a7, a1, a2), Object.hash(a5, a6, a7, a1, a2));
check(Object.hash(a6, a7, a1, a2, a3), Object.hash(a6, a7, a1, a2, a3));
check(Object.hash(a7, a1, a2, a3, a4), Object.hash(a7, a1, a2, a3, a4));

check(_SystemHash.hash2(1, 2, 0), _SystemHash.hash2(1, 2, 0));
check(_SystemHash.hash3(1, 2, 3, 0), _SystemHash.hash3(1, 2, 3, 0));
check(_SystemHash.hash4(1, 2, 3, 4, 0), _SystemHash.hash4(1, 2, 3, 4, 0));
check(
_SystemHash.hash5(1, 2, 3, 4, 5, 0), _SystemHash.hash5(1, 2, 3, 4, 5, 0));

// Pollute hashAll argument type.
check(Object.hashAll({}), Object.hashAll([]));
check(Object.hashAll({}.values), Object.hashAll({}.keys));
check(Object.hashAll(''.codeUnits), Object.hashAll(const Iterable.empty()));
check(Object.hashAll(const [0]), Object.hashAll(Iterable.generate(1)));
}

// Partial copy of dart:internal `SystemHash` that is used by `Object.hash` so
// that we can create comparable manual hashCode methods.
class _SystemHash {
static int combine(int hash, int value) {
hash = 0x1fffffff & (hash + value);
hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10));
return hash ^ (hash >> 6);
}

static int finish(int hash) {
hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3));
hash = hash ^ (hash >> 11);
return 0x1fffffff & (hash + ((0x00003fff & hash) << 15));
}

static int hash2(int v1, int v2, int seed) {
int hash = seed;
hash = combine(hash, v1);
hash = combine(hash, v2);
return finish(hash);
}

static int hash3(int v1, int v2, int v3, int seed) {
int hash = seed;
hash = combine(hash, v1);
hash = combine(hash, v2);
hash = combine(hash, v3);
return finish(hash);
}

static int hash4(int v1, int v2, int v3, int v4, int seed) {
int hash = seed;
hash = combine(hash, v1);
hash = combine(hash, v2);
hash = combine(hash, v3);
hash = combine(hash, v4);
return finish(hash);
}

static int hash5(int v1, int v2, int v3, int v4, int v5, int seed) {
int hash = seed;
hash = combine(hash, v1);
hash = combine(hash, v2);
hash = combine(hash, v3);
hash = combine(hash, v4);
hash = combine(hash, v5);
return finish(hash);
}
}
60 changes: 41 additions & 19 deletions pkg/analyzer/lib/src/error/unused_local_elements_verifier.dart
Original file line number Diff line number Diff line change
Expand Up @@ -723,6 +723,32 @@ class UnusedLocalElementsVerifier extends RecursiveAstVisitor<void> {
super.visitVariableDeclarationStatement(node);
}

/// Returns the parameter element, if any, that corresponds to the given
/// parameter in the overridden element.
ParameterElement? _getCorrespondingParameter(ParameterElement parameter,
ExecutableElement overridden, ExecutableElement enclosingElement) {
ParameterElement? correspondingParameter;
if (parameter.isNamed) {
correspondingParameter = overridden.parameters
.firstWhereOrNull((p) => p.name == parameter.name);
} else {
var parameterIndex = 0;
var parameterCount = enclosingElement.parameters.length;
while (parameterIndex < parameterCount) {
if (enclosingElement.parameters[parameterIndex] == parameter) {
break;
}
parameterIndex++;
}
if (overridden.parameters.length <= parameterIndex) {
// Something is wrong with the overridden element. Ignore it.
return null;
}
correspondingParameter = overridden.parameters[parameterIndex];
}
return correspondingParameter;
}

/// Returns whether the name of [element] consists only of underscore
/// characters.
bool _isNamedUnderscore(LocalVariableElement element) {
Expand Down Expand Up @@ -830,6 +856,19 @@ class UnusedLocalElementsVerifier extends RecursiveAstVisitor<void> {
// "used". See https://github.com/dart-lang/sdk/issues/47839.
return true;
}
if (enclosingElement is ConstructorElement) {
var superConstructor = enclosingElement.superConstructor;
if (superConstructor != null) {
var correspondingParameter = _getCorrespondingParameter(
element, superConstructor, enclosingElement);
if (correspondingParameter != null) {
if (correspondingParameter.isRequiredNamed ||
correspondingParameter.isRequiredPositional) {
return true;
}
}
}
}
if (enclosingElement is ExecutableElement) {
if (enclosingElement.typeParameters.isNotEmpty) {
// There is an issue matching arguments of generic function
Expand Down Expand Up @@ -904,25 +943,8 @@ class UnusedLocalElementsVerifier extends RecursiveAstVisitor<void> {
ParameterElement element, ExecutableElement enclosingElement) {
var overriddenElements = _overriddenElements(enclosingElement);
for (var overridden in overriddenElements) {
ParameterElement? correspondingParameter;
if (element.isNamed) {
correspondingParameter = overridden.parameters
.firstWhereOrNull((p) => p.name == element.name);
} else {
var parameterIndex = 0;
var parameterCount = enclosingElement.parameters.length;
while (parameterIndex < parameterCount) {
if (enclosingElement.parameters[parameterIndex] == element) {
break;
}
parameterIndex++;
}
if (overridden.parameters.length <= parameterIndex) {
// Something is wrong with the overridden element. Ignore it.
continue;
}
correspondingParameter = overridden.parameters[parameterIndex];
}
ParameterElement? correspondingParameter =
_getCorrespondingParameter(element, overridden, enclosingElement);
// The parameter was added in the override.
if (correspondingParameter == null) {
continue;
Expand Down
39 changes: 39 additions & 0 deletions pkg/analyzer/test/src/diagnostics/unused_element_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,21 @@ class A {
''');
}

test_constructor_notUsed_single_inSubclass() async {
await assertErrorsInCode(r'''
class A {
A._constructor();
}
class B extends A {
B() : super._constructor();
B._named() : super._constructor();
}
''', [
error(WarningCode.UNUSED_ELEMENT, 87, 6),
]);
}

test_enum_constructor_parameter_optionalNamed_isUsed() async {
await assertNoErrorsInCode(r'''
enum E {
Expand Down Expand Up @@ -1620,6 +1635,30 @@ f() => A()._m();
]);
}

test_optionalParameter_notUsed_overrideRequired() async {
await assertNoErrorsInCode(r'''
class A {
const A({
required this.a,
required this.b,
});
final String a;
final String b;
}
class _B extends A {
const _B({
required super.a,
super.b = 'b',
});
}
const foo = _B(
a: 'a',
);
''');
}

test_optionalParameter_notUsed_positional() async {
await assertErrorsInCode(r'''
class A {
Expand Down
Loading

0 comments on commit cf91449

Please sign in to comment.