[analyzer] Add a valueOrThrow extension similar to our oft-used typeOrThrow

This really helps in complying with strict-casts. YamlNode.value returns
a `dynamic`, which is then often passed somewhere which accepts Object
(and then does some type tests). To avoid the implicit cast from
`dynamic`, we can use `.valueOrThrow` (or basically just
`.value as Object`).

The extension getter improves readability and avoids a lot of
as-expressions inside conditional expressions etc.

Change-Id: I50d2eebd51d6fc3eccf1c930652dd5ce70dd9eb1
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/277582
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Commit-Queue: Samuel Rawlins <srawlins@google.com>
This commit is contained in:
Sam Rawlins 2023-01-03 18:53:39 +00:00 committed by Commit Queue
parent c3a1bfdfa5
commit 105efd85b6
9 changed files with 47 additions and 32 deletions

View file

@ -6,6 +6,7 @@ import 'package:analyzer/error/error.dart';
import 'package:analyzer/src/generated/engine.dart';
import 'package:analyzer/src/generated/utilities_general.dart';
import 'package:analyzer/src/task/options.dart';
import 'package:analyzer/src/util/yaml.dart';
import 'package:yaml/yaml.dart';
/// String identifiers mapped to associated severities.
@ -46,7 +47,7 @@ class ErrorConfig {
if (codes is YamlMap) {
codes.nodes.forEach((k, v) {
if (k is YamlScalar && v is YamlScalar) {
_process(k.value, v.value);
_process(k.value as String?, v.valueOrThrow);
}
});
}

View file

@ -4,6 +4,7 @@
import 'dart:collection';
import 'package:analyzer/src/util/yaml.dart';
import 'package:yaml/yaml.dart';
/// Test if the given [value] is `false` or the string "false"
@ -19,7 +20,7 @@ bool isTrue(Object value) =>
/// value could not be converted.
bool? toBool(Object value) {
if (value is YamlScalar) {
value = value.value;
value = value.valueOrThrow;
}
if (value is bool) {
return value;

View file

@ -66,7 +66,7 @@ class _LintConfig implements LintConfig {
void addAsListOrString(Object? value, List<String> list) {
if (value is List) {
for (var entry in value) {
list.add(entry);
list.add(entry as String);
}
} else if (value is String) {
list.add(value);
@ -74,7 +74,7 @@ class _LintConfig implements LintConfig {
}
bool? asBool(Object scalar) {
Object value = scalar is YamlScalar ? scalar.value : scalar;
var value = scalar is YamlScalar ? scalar.valueOrThrow : scalar;
if (value is bool) {
return value;
}
@ -90,7 +90,7 @@ class _LintConfig implements LintConfig {
}
String? asString(Object scalar) {
Object value = scalar is YamlScalar ? scalar.value : scalar;
var value = scalar is YamlScalar ? scalar.valueOrThrow : scalar;
if (value is String) {
return value;
}
@ -142,7 +142,7 @@ class _LintConfig implements LintConfig {
// style_guide: {unnecessary_getters: false, camel_case_types: true}
if (v is YamlMap) {
v.nodes.forEach((key, value) {
v.nodes.cast<Object, YamlNode>().forEach((key, value) {
//{unnecessary_getters: false}
if (asBool(value) != null) {
var config = _RuleConfig();
@ -153,7 +153,7 @@ class _LintConfig implements LintConfig {
// style_guide: {unnecessary_getters: false, camel_case_types: true}
if (value is YamlMap) {
value.nodes.forEach((rule, args) {
value.nodes.cast<Object, YamlNode>().forEach((rule, args) {
// TODO: verify format
// unnecessary_getters: false
var config = _RuleConfig();

View file

@ -36,8 +36,11 @@ class DependencyValidator extends BasePubspecValidator {
for (var dependency in declaredDevDependencies.entries) {
var packageName = dependency.key as YamlNode;
if (declaredDependencies.containsKey(packageName)) {
reportErrorForNode(reporter, packageName,
PubspecWarningCode.UNNECESSARY_DEV_DEPENDENCY, [packageName.value]);
reportErrorForNode(
reporter,
packageName,
PubspecWarningCode.UNNECESSARY_DEV_DEPENDENCY,
[packageName.valueOrThrow]);
}
_validatePathEntries(reporter, dependency.value, false);
}

View file

@ -7,6 +7,7 @@ import 'package:analyzer/error/listener.dart';
import 'package:analyzer/file_system/file_system.dart';
import 'package:analyzer/src/pubspec/pubspec_validator.dart';
import 'package:analyzer/src/pubspec/pubspec_warning_code.dart';
import 'package:analyzer/src/util/yaml.dart';
import 'package:path/path.dart' as path;
import 'package:yaml/yaml.dart';
@ -23,7 +24,7 @@ class FlutterValidator extends BasePubspecValidator {
String packageRoot = context.dirname(source.fullName);
for (YamlNode entryValue in assetsField.nodes) {
if (entryValue is YamlScalar) {
Object entry = entryValue.value;
var entry = entryValue.valueOrThrow;
if (entry is String) {
if (entry.startsWith('packages/')) {
// TODO(brianwilkerson) Add validation of package references.
@ -36,8 +37,8 @@ class FlutterValidator extends BasePubspecValidator {
ErrorCode errorCode = isDirectoryEntry
? PubspecWarningCode.ASSET_DIRECTORY_DOES_NOT_EXIST
: PubspecWarningCode.ASSET_DOES_NOT_EXIST;
reportErrorForNode(
reporter, entryValue, errorCode, [entryValue.value]);
reportErrorForNode(reporter, entryValue, errorCode,
[entryValue.valueOrThrow]);
}
}
} else {

View file

@ -23,7 +23,7 @@ class ScreenshotsValidator extends BasePubspecValidator {
var path = entryValue.value;
if (path is String && !_fileExistsAtPath(path)) {
reportErrorForNode(reporter, entryValue,
PubspecWarningCode.PATH_DOES_NOT_EXIST, [entryValue.value]);
PubspecWarningCode.PATH_DOES_NOT_EXIST, [entryValue.valueOrThrow]);
}
}
}

View file

@ -311,12 +311,12 @@ class CodeStyleOptionsValidator extends OptionsValidator {
format.span,
[AnalyzerOptions.format]);
} else if (format is YamlScalar) {
var formatValue = toBool(format.value);
var formatValue = toBool(format.valueOrThrow);
if (formatValue == null) {
reporter.reportErrorForSpan(
AnalysisOptionsWarningCode.UNSUPPORTED_VALUE, format.span, [
AnalyzerOptions.format,
format.value,
format.valueOrThrow,
AnalyzerOptions.trueOrFalseProposal
]);
}
@ -414,9 +414,10 @@ class ErrorBuilder {
void reportError(ErrorReporter reporter, String scopeName, YamlNode node) {
if (proposal.isNotEmpty) {
reporter.reportErrorForSpan(
code, node.span, [scopeName, node.value, proposal]);
code, node.span, [scopeName, node.valueOrThrow, proposal]);
} else {
reporter.reportErrorForSpan(code, node.span, [scopeName, node.value]);
reporter
.reportErrorForSpan(code, node.span, [scopeName, node.valueOrThrow]);
}
}
}
@ -519,7 +520,7 @@ class LanguageOptionValidator extends OptionsValidator {
reporter.reportErrorForSpan(
AnalysisOptionsWarningCode.UNSUPPORTED_VALUE,
v.span,
[key!, v.value, AnalyzerOptions.trueOrFalseProposal]);
[key!, v.valueOrThrow, AnalyzerOptions.trueOrFalseProposal]);
}
}
});
@ -572,9 +573,11 @@ class OptionalChecksValueValidator extends OptionsValidator {
value = toLowerCase(v.value);
if (!AnalyzerOptions.trueOrFalse.contains(value)) {
reporter.reportErrorForSpan(
AnalysisOptionsWarningCode.UNSUPPORTED_VALUE,
v.span,
[key!, v.value, AnalyzerOptions.trueOrFalseProposal]);
AnalysisOptionsWarningCode.UNSUPPORTED_VALUE, v.span, [
key!,
v.valueOrThrow,
AnalyzerOptions.trueOrFalseProposal
]);
}
}
}
@ -671,7 +674,7 @@ class StrongModeOptionValueValidator extends OptionsValidator {
reporter.reportErrorForSpan(
AnalysisOptionsWarningCode.UNSUPPORTED_VALUE,
v.span,
[key!, v.value, AnalyzerOptions.trueOrFalseProposal]);
[key!, v.valueOrThrow, AnalyzerOptions.trueOrFalseProposal]);
}
}
}
@ -729,8 +732,8 @@ class TopLevelOptionValidator extends OptionsValidator {
node.nodes.forEach((k, v) {
if (k is YamlScalar) {
if (!supportedOptions.contains(k.value)) {
reporter.reportErrorForSpan(
_warningCode, k.span, [pluginName, k.value, _valueProposal]);
reporter.reportErrorForSpan(_warningCode, k.span,
[pluginName, k.valueOrThrow, _valueProposal]);
}
}
//TODO(pq): consider an error if the node is not a Scalar.
@ -803,7 +806,7 @@ class _OptionsProcessor {
}
}
} else if (names is YamlMap) {
for (var key in names.nodes.keys) {
for (var key in names.nodes.keys.cast<YamlNode?>()) {
var pluginName = _toString(key);
if (pluginName != null) {
pluginNames.add(pluginName);
@ -857,7 +860,7 @@ class _OptionsProcessor {
configs.nodes.forEach((key, value) {
if (key is YamlScalar && value is YamlScalar) {
var feature = key.value?.toString();
_applyLanguageOption(options, feature, value.value);
_applyLanguageOption(options, feature, value.valueOrThrow);
}
});
}
@ -867,7 +870,8 @@ class _OptionsProcessor {
if (config is YamlMap) {
config.nodes.forEach((k, v) {
if (k is YamlScalar && v is YamlScalar) {
_applyOptionalChecksOption(options, k.value?.toString(), v.value);
_applyOptionalChecksOption(
options, k.value?.toString(), v.valueOrThrow);
}
});
}
@ -913,7 +917,7 @@ class _OptionsProcessor {
if (config is YamlMap) {
config.nodes.forEach((k, v) {
if (k is YamlScalar && v is YamlScalar) {
_applyStrongModeOption(options, k.value?.toString(), v.value);
_applyStrongModeOption(options, k.value?.toString(), v.valueOrThrow);
}
});
}

View file

@ -112,7 +112,7 @@ extension YamlMapExtensions on YamlMap {
/// Return the [YamlNode] associated with the given [key], or `null` if there
/// is no matching key.
YamlNode? getKey(String key) {
for (YamlNode k in nodes.keys) {
for (var k in nodes.keys.cast<YamlNode>()) {
if (k is YamlScalar && k.value == key) {
return k;
}
@ -125,7 +125,7 @@ extension YamlMapExtensions on YamlMap {
YamlNode? keyAtValue(YamlNode value) {
for (var entry in nodes.entries) {
if (entry.value == value) {
return entry.key;
return entry.key as YamlNode?;
}
}
return null;
@ -142,3 +142,7 @@ extension YamlMapExtensions on YamlMap {
return null;
}
}
extension YamlNodeExtension on YamlNode {
Object get valueOrThrow => value as Object;
}

View file

@ -100,9 +100,10 @@ main() {
final Merger merger = Merger();
Object merge(Object o1, Object o2) => merger.merge(wrap(o1), wrap(o2)).value;
Object merge(Object o1, Object o2) =>
merger.merge(wrap(o1), wrap(o2)).valueOrThrow;
YamlNode wrap(Object value) {
YamlNode wrap(Object? value) {
if (value is List) {
var wrappedElements = value.map((e) => wrap(e)).toList();
return YamlList.internal(