analysis_server: Enforce strict-casts

Bug: https://github.com/dart-lang/sdk/issues/41651
Change-Id: I9be21ab7e8f8b61707a75d7d4b5f9a872ad0fb95
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/222220
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Commit-Queue: Samuel Rawlins <srawlins@google.com>
This commit is contained in:
Sam Rawlins 2021-12-07 22:45:59 +00:00 committed by Commit Bot
parent 89f2e89719
commit b06b60702b
23 changed files with 236 additions and 223 deletions

View file

@ -1,10 +1,8 @@
include: package:pedantic/analysis_options.1.9.0.yaml
analyzer:
# This currently finds ~1,200 implicit-casts issues when enabled.
# TODO(srawlins): Switch to strict-casts
# strong-mode:
# implicit-casts: false
language:
strict-casts: true
exclude:
- test/mock_packages/**
errors:

View file

@ -846,7 +846,7 @@ class FlutterOutline implements ToJsonable {
try {
final attributes = obj['attributes'];
if (attributes != null &&
!((attributes is List &&
!((attributes is List<Object?> &&
(attributes.every((item) =>
FlutterOutlineAttribute.canParse(item, reporter)))))) {
reporter.reportError('must be of type List<FlutterOutlineAttribute>');
@ -905,7 +905,7 @@ class FlutterOutline implements ToJsonable {
try {
final children = obj['children'];
if (children != null &&
!((children is List &&
!((children is List<Object?> &&
(children.every(
(item) => FlutterOutline.canParse(item, reporter)))))) {
reporter.reportError('must be of type List<FlutterOutline>');
@ -1168,7 +1168,7 @@ class Outline implements ToJsonable {
try {
final children = obj['children'];
if (children != null &&
!((children is List &&
!((children is List<Object?> &&
(children
.every((item) => Outline.canParse(item, reporter)))))) {
reporter.reportError('must be of type List<Outline>');
@ -1376,7 +1376,7 @@ class PublishClosingLabelsParams implements ToJsonable {
reporter.reportError('must not be null');
return false;
}
if (!((labels is List &&
if (!((labels is List<Object?> &&
(labels.every((item) => ClosingLabel.canParse(item, reporter)))))) {
reporter.reportError('must be of type List<ClosingLabel>');
return false;

File diff suppressed because it is too large Load diff

View file

@ -239,9 +239,9 @@ class ResultMerger {
/// Return the index of the region in the collection of [mergedRegions] that
/// covers exactly the same region as the [newRegion], or `-1` if there is
/// no such region.
int matchingRegion(newRegion) {
int newOffset = newRegion.offset;
int newLength = newRegion.length;
int matchingRegion(NavigationRegion newRegion) {
var newOffset = newRegion.offset;
var newLength = newRegion.length;
for (var i = 0; i < mergedRegions.length; i++) {
var mergedRegion = mergedRegions[i];
if (newOffset == mergedRegion.offset &&

View file

@ -40,19 +40,21 @@ class SdkConfiguration {
}
/// Whether analytics is forced on.
bool? get analyticsForceEnabled => _values['server.analytics.forceEnabled'];
bool? get analyticsForceEnabled =>
_values['server.analytics.forceEnabled'] as bool?;
/// Return an override value for the analysis server's google analytics ID, or
/// `null` if the default value should be used.
String? get analyticsId => _values['server.analytics.id'];
String? get analyticsId => _values['server.analytics.id'] as String?;
/// Whether crash reporting is forced on.
bool? get crashReportingForceEnabled =>
_values['server.crash.reporting.forceEnabled'];
_values['server.crash.reporting.forceEnabled'] as bool?;
/// Return an override value for the analysis server's crash reporting product
/// ID, or `null` if the default value should be used.
String? get crashReportingId => _values['server.crash.reporting.id'];
String? get crashReportingId =>
_values['server.crash.reporting.id'] as String?;
/// Return a string describing the contents of this SDK configuration.
String get displayString {
@ -67,9 +69,10 @@ class SdkConfiguration {
void _readFromFile(File file) {
try {
Map m = jsonDecode(file.readAsStringSync());
for (var key in m.keys) {
_values[key] = m[key];
var content =
jsonDecode(file.readAsStringSync()) as Map<Object?, Object?>;
for (var key in content.keys) {
_values[key as String] = content[key];
}
} catch (_) {
// ignore issues reading the file

View file

@ -59,7 +59,7 @@ class PubApi {
final packageNames = json['packages'];
return packageNames is List
? packageNames.map((name) => PubApiPackage(name)).toList()
? packageNames.map((name) => PubApiPackage(name as String)).toList()
: null;
}
@ -103,7 +103,7 @@ class PubApi {
await httpClient.get(Uri.parse(url), headers: _headers);
if (response.statusCode == 200) {
instrumentationService.logInfo('Pub API request successful for $url');
return jsonDecode(response.body);
return jsonDecode(response.body) as Map<String, Object?>?;
} else if (response.statusCode >= 400 && response.statusCode < 500) {
// Do not retry 4xx responses.
instrumentationService.logError(

View file

@ -136,7 +136,7 @@ class PubCommand {
try {
final results = jsonDecode(stdout);
_instrumentationService.logInfo('pub command completed successfully');
return results;
return results as Map<String, Object?>?;
} catch (e) {
_instrumentationService
.logError('pub command returned invalid JSON: $e.');

View file

@ -15,7 +15,7 @@ String printInteger(int value) => numberFormat.format(value);
String printMilliseconds(num value) => '${numberFormat.format(value)} ms';
String printPercentage(num value, [fractionDigits = 1]) =>
String printPercentage(num value, [int fractionDigits = 1]) =>
'${(value * 100).toStringAsFixed(fractionDigits)}%';
/// An entity that knows how to serve itself over http.

View file

@ -29,8 +29,9 @@ extension YamlNodeExtensions on YamlNode {
var nextEntryOffset = i + 1 < entries.length
? (entries[i + 1].key as YamlNode).span.start.offset
: null;
if ((entry.key as YamlNode).containsOffset(offset)) {
return entry.key;
var key = entry.key as YamlNode;
if (key.containsOffset(offset)) {
return key;
}
var value = entry.value;
if (value.containsOffset(offset) ||

View file

@ -56,7 +56,7 @@ class _PosixProcessProfiler extends ProcessProfiler {
return Future.value(null);
}
return Future.value(_parse(result.stdout));
return Future.value(_parse(result.stdout as String));
});
} catch (e) {
return Future.error(e);

View file

@ -49,7 +49,7 @@ class YamlNodeLocator {
} else if (node is YamlMap) {
var nodeMap = node.nodes;
for (var entry in nodeMap.entries) {
_searchWithin(path, entry.key);
_searchWithin(path, entry.key as YamlNode);
if (path.isNotEmpty) {
path.add(node);
return;

View file

@ -61,7 +61,7 @@ List<String> _listBenchmarks() {
[path.join('benchmark', 'benchmarks.dart'), 'list', '--machine'],
workingDirectory: _serverSourcePath,
);
Map m = json.decode(result.stdout);
List benchmarks = m['benchmarks'];
var output = json.decode(result.stdout as String) as Map<Object?, Object?>;
var benchmarks = (output['benchmarks'] as List).cast<Map<Object?, Object?>>();
return benchmarks.map((b) => b['id']).cast<String>().toList();
}

View file

@ -30,7 +30,7 @@ class ExpectMixin {
} else if (x is _Predicate<Null>) {
// x is a unary predicate, but expects a specific type
// so wrap it.
return predicate((a) => (x as dynamic)(a));
return predicate((a) => (x as dynamic)(a) as bool);
} else {
return equals(x);
}

View file

@ -11,7 +11,7 @@ class Analysis_UpdateContent extends ServerOperation {
final String filePath;
/// The overlay used to update the content.
final dynamic overlay;
final Object overlay;
/// Initialize an operation to send an 'analysis.updateContent' request with
/// the given [filePath] and [overlay] as parameters.

View file

@ -766,7 +766,7 @@ class Server {
return;
}
logger?.log(fromServer, '$trimmedLine');
var message = asMap(json.decoder.convert(trimmedLine));
var message = asMap(json.decoder.convert(trimmedLine) as Object);
if (message.containsKey('id')) {
// The message is a response.
var response = Response.fromJson(message)!;

View file

@ -185,7 +185,7 @@ void main() {
}
/// Returns matcher that can compare double values.
Matcher doubleEquals(expected) => _DoubleEquals(expected);
Matcher doubleEquals(double expected) => _DoubleEquals(expected);
class _DoubleEquals extends Matcher {
final double _value;

View file

@ -334,7 +334,8 @@ void main() {
group('fromJson', () {
test('parses JSON for types with unions (left side)', () {
final input = '{"id":1,"method":"shutdown","jsonrpc":"test"}';
final message = RequestMessage.fromJson(jsonDecode(input));
final message =
RequestMessage.fromJson(jsonDecode(input) as Map<String, Object?>);
expect(message.id, equals(Either2<num, String>.t1(1)));
expect(message.id.valueEquals(1), isTrue);
expect(message.jsonrpc, 'test');
@ -343,7 +344,8 @@ void main() {
test('parses JSON for types with unions (right side)', () {
final input = '{"id":"one","method":"shutdown","jsonrpc":"test"}';
final message = RequestMessage.fromJson(jsonDecode(input));
final message =
RequestMessage.fromJson(jsonDecode(input) as Map<String, Object?>);
expect(message.id, equals(Either2<num, String>.t2('one')));
expect(message.id.valueEquals('one'), isTrue);
expect(message.jsonrpc, 'test');
@ -352,13 +354,15 @@ void main() {
test('parses JSON with nulls for unions that allow null', () {
final input = '{"id":null,"jsonrpc":"test"}';
final message = ResponseMessage.fromJson(jsonDecode(input));
final message =
ResponseMessage.fromJson(jsonDecode(input) as Map<String, Object?>);
expect(message.id, isNull);
});
test('parses JSON with nulls for unions that allow null', () {
final input = '{"method":"test","jsonrpc":"test"}';
final message = NotificationMessage.fromJson(jsonDecode(input));
final message = NotificationMessage.fromJson(
jsonDecode(input) as Map<String, Object?>);
expect(message.params, isNull);
});
@ -370,7 +374,8 @@ void main() {
version: 111, uri: 'file:///foo/bar.dart'),
position: Position(line: 1, character: 1),
).toJson());
final params = TextDocumentPositionParams.fromJson(jsonDecode(input));
final params = TextDocumentPositionParams.fromJson(
jsonDecode(input) as Map<String, Object?>);
expect(params.textDocument,
const TypeMatcher<VersionedTextDocumentIdentifier>());
});
@ -378,7 +383,8 @@ void main() {
test('parses JSON with unknown fields', () {
final input =
'{"id":1,"invalidField":true,"method":"foo","jsonrpc":"test"}';
final message = RequestMessage.fromJson(jsonDecode(input));
final message =
RequestMessage.fromJson(jsonDecode(input) as Map<String, Object?>);
expect(message.id.valueEquals(1), isTrue);
expect(message.method, equals(Method('foo')));
expect(message.params, isNull);
@ -401,7 +407,8 @@ void main() {
workspaceFolders: workspaceFolders,
);
final json = jsonEncode(obj);
final restoredObj = InitializeParams.fromJson(jsonDecode(json));
final restoredObj =
InitializeParams.fromJson(jsonDecode(json) as Map<String, Object?>);
final restoredWorkspaceFolders = restoredObj.workspaceFolders!;
expect(restoredWorkspaceFolders, hasLength(workspaceFolders.length));
@ -420,7 +427,8 @@ void main() {
endCharacter: 4,
kind: FoldingRangeKind.Comment);
final json = jsonEncode(obj);
final restoredObj = FoldingRange.fromJson(jsonDecode(json));
final restoredObj =
FoldingRange.fromJson(jsonDecode(json) as Map<String, Object?>);
expect(restoredObj.startLine, equals(obj.startLine));
expect(restoredObj.startCharacter, equals(obj.startCharacter));
@ -438,7 +446,8 @@ void main() {
'fileB': [TextEdit(range: range, newText: 'text B')]
});
final json = jsonEncode(obj);
final restoredObj = WorkspaceEdit.fromJson(jsonDecode(json));
final restoredObj =
WorkspaceEdit.fromJson(jsonDecode(json) as Map<String, Object?>);
expect(restoredObj.documentChanges, equals(obj.documentChanges));
expect(restoredObj.changes, equals(obj.changes));

View file

@ -18,7 +18,7 @@ Future<void> main(List<String> args) async {
exit(1);
}
final repos = [];
final repos = <String>[];
if (args.length == 1 && !Directory(args[0]).existsSync()) {
final contents = File(args[0]).readAsStringSync();
repos.addAll(LineSplitter().convert(contents));
@ -86,7 +86,7 @@ Future<CloneResult> _clone(String repo) async {
result = await Process.run(
'git', ['clone', '--recurse-submodules', '$repo.git', cloneDir]);
}
return CloneResult(result.exitCode, cloneDir, msg: result.stderr);
return CloneResult(result.exitCode, cloneDir, msg: result.stderr as String);
}
Future<String> _getBody(String url) async => (await _getResponse(url)).body;
@ -105,11 +105,11 @@ Future<void> _runPubGet(FileSystemEntity dir) async {
if (_hasPubspec(dir)) {
final packageFile = path.join(dir.path, _package_config);
if (!File(packageFile).existsSync() || forcePubUpdate) {
print(
'Getting pub dependencies for "${path.relative(dir.path, from: _appDir)}"...');
final relativeDirPath = path.relative(dir.path, from: _appDir);
print('Getting pub dependencies for "$relativeDirPath"...');
final pubRun = await _runPub(dir.path);
if (pubRun.exitCode != 0) {
print('Error: ' + pubRun.stderr);
print('Error: ${pubRun.stderr}');
}
}
}

View file

@ -307,7 +307,7 @@ class InstrumentationLog {
if (entry.isServerStatus) {
var analysisStatus = entry.param('analysis');
if (analysisStatus is Map) {
if (analysisStatus['isAnalyzing']) {
if (analysisStatus['isAnalyzing'] as bool) {
if (analysisStartEntry != null) {
analysisStartEntry.recordProblem(
'Analysis started without being terminated.');
@ -331,7 +331,7 @@ class InstrumentationLog {
}
var pubStatus = entry.param('pub');
if (pubStatus is Map) {
if (pubStatus['isListingPackageDirs']) {
if (pubStatus['isListingPackageDirs'] as bool) {
if (pubStartEntry != null) {
pubStartEntry.recordProblem(
'Pub started without previous being terminated.');
@ -441,9 +441,9 @@ abstract class JsonBasedEntry extends LogEntry {
object.forEach((key, value) {
var newIndent = indent + singleIndent;
buffer.write(newIndent);
_format(buffer, newIndent, key);
_format(buffer, newIndent, key as Object);
buffer.write(' : ');
_format(buffer, newIndent, value);
_format(buffer, newIndent, value as Object);
buffer.write('<br>');
});
buffer.write(indent);
@ -453,7 +453,7 @@ abstract class JsonBasedEntry extends LogEntry {
object.forEach((element) {
var newIndent = indent + singleIndent;
buffer.write(newIndent);
_format(buffer, newIndent, element);
_format(buffer, newIndent, element as Object);
buffer.write('<br>');
});
buffer.write(indent);
@ -578,7 +578,7 @@ abstract class LogEntry {
} else if (entryKind == InstrumentationLogAdapter.TAG_LOG_ENTRY) {
// Fall through
} else if (entryKind == InstrumentationLogAdapter.TAG_NOTIFICATION) {
Map requestData = json.decode(components[2]);
var requestData = json.decode(components[2]) as Map<Object?, Object?>;
return NotificationEntry(index, timeStamp, requestData);
} else if (entryKind == InstrumentationLogAdapter.TAG_PLUGIN_ERROR) {
return PluginErrorEntry(index, timeStamp, entryKind,
@ -588,25 +588,25 @@ abstract class LogEntry {
components.sublist(2, 5), components.sublist(5));
} else if (entryKind ==
InstrumentationLogAdapter.TAG_PLUGIN_NOTIFICATION) {
Map requestData = json.decode(components[2]);
var requestData = json.decode(components[2]) as Map<Object?, Object?>;
return PluginNotificationEntry(
index, timeStamp, requestData, components.sublist(3));
} else if (entryKind == InstrumentationLogAdapter.TAG_PLUGIN_REQUEST) {
Map requestData = json.decode(components[2]);
var requestData = json.decode(components[2]) as Map<Object?, Object?>;
return PluginRequestEntry(
index, timeStamp, requestData, components.sublist(3));
} else if (entryKind == InstrumentationLogAdapter.TAG_PLUGIN_RESPONSE) {
Map responseData = json.decode(components[2]);
var responseData = json.decode(components[2]) as Map<Object?, Object?>;
return PluginResponseEntry(
index, timeStamp, responseData, components.sublist(3));
} else if (entryKind == InstrumentationLogAdapter.TAG_PLUGIN_TIMEOUT) {
return PluginErrorEntry(index, timeStamp, entryKind,
components.sublist(2, 3), components.sublist(3));
} else if (entryKind == InstrumentationLogAdapter.TAG_REQUEST) {
Map requestData = json.decode(components[2]);
var requestData = json.decode(components[2]) as Map<Object?, Object?>;
return RequestEntry(index, timeStamp, requestData);
} else if (entryKind == InstrumentationLogAdapter.TAG_RESPONSE) {
Map responseData = json.decode(components[2]);
var responseData = json.decode(components[2]) as Map<Object?, Object?>;
return ResponseEntry(index, timeStamp, responseData);
} else if (entryKind == InstrumentationLogAdapter.TAG_VERSION) {
// Fall through
@ -675,7 +675,7 @@ class NotificationEntry extends JsonBasedEntry {
: super(index, timeStamp, notificationData);
/// Return the event field of the request.
String get event => data['event'];
String get event => data['event'] as String;
/// Return `true` if this is a server error notification.
bool get isServerError => event == 'server.error';
@ -747,7 +747,7 @@ class PluginNotificationEntry extends JsonBasedPluginEntry {
: super(index, timeStamp, notificationData, pluginData);
/// Return the event field of the notification.
String get event => data['event'];
String get event => data['event'] as String;
@override
String get kind => 'PluginNoti';
@ -773,13 +773,13 @@ class PluginRequestEntry extends JsonBasedPluginEntry {
: super(index, timeStamp, requestData, pluginData);
/// Return the id field of the request.
String get id => data['id'];
String get id => data['id'] as String;
@override
String get kind => 'PluginReq';
/// Return the method field of the request.
String get method => data['method'];
String get method => data['method'] as String;
/// Return the value of the parameter with the given [parameterName], or
/// `null` if there is no such parameter.
@ -802,7 +802,7 @@ class PluginResponseEntry extends JsonBasedPluginEntry {
: super(index, timeStamp, responseData, pluginData);
/// Return the id field of the response.
String get id => data['id'];
String get id => data['id'] as String;
@override
String get kind => 'PluginRes';
@ -827,16 +827,16 @@ class RequestEntry extends JsonBasedEntry {
: super(index, timeStamp, requestData);
/// Return the clientRequestTime field of the request.
int get clientRequestTime => data['clientRequestTime'];
int get clientRequestTime => data['clientRequestTime'] as int;
/// Return the id field of the request.
String get id => data['id'];
String get id => data['id'] as String;
@override
String get kind => 'Req';
/// Return the method field of the request.
String get method => data['method'];
String get method => data['method'] as String;
/// Return the value of the parameter with the given [parameterName], or
/// `null` if there is no such parameter.
@ -858,7 +858,7 @@ class ResponseEntry extends JsonBasedEntry {
: super(index, timeStamp, responseData);
/// Return the id field of the response.
String get id => data['id'];
String get id => data['id'] as String;
@override
String get kind => 'Res';

View file

@ -70,14 +70,14 @@ class Driver {
void start(List<String> args) {
var parser = createParser();
var options = parser.parse(args);
if (options[helpFlag]) {
if (options[helpFlag] as bool) {
printUsage(parser);
return;
}
var port = defaultPortNumber;
try {
port = int.parse(options[portOption]);
port = int.parse(options[portOption] as String);
} catch (exception) {
printUsage(parser, error: 'Invalid port number');
return;

View file

@ -142,7 +142,7 @@ function selectEntryGroup(pageStart) {
if (entry.isServerStatus) {
var analysisStatus = entry.param('analysis');
if (analysisStatus is Map) {
if (analysisStatus['isAnalyzing']) {
if (analysisStatus['isAnalyzing'] as bool) {
description = '$description <span class="gray">(analyzing)</span>';
} else {
var duration = _getDuration(pairedEntry, entry);
@ -152,7 +152,7 @@ function selectEntryGroup(pageStart) {
}
var pubStatus = entry.param('pub');
if (pubStatus is Map) {
if (pubStatus['isListingPackageDirs']) {
if (pubStatus['isListingPackageDirs'] as bool) {
description = '$description <span class="gray">(pub)</span>';
} else {
var duration = _getDuration(pairedEntry, entry);

View file

@ -60,7 +60,7 @@ void recordTypes(List<AstNode> types) {
.forEach((namespace) => _namespaces[namespace.name] = namespace);
}
TypeBase resolveTypeAlias(TypeBase type, {resolveEnumClasses = false}) {
TypeBase resolveTypeAlias(TypeBase type, {bool resolveEnumClasses = false}) {
if (type is Type) {
// The LSP spec contains type aliases for `integer` and `uinteger` that map
// into the `number` type, with comments stating they must be integers. To
@ -380,7 +380,7 @@ void _writeEnumClass(IndentableStringBuffer buffer, Namespace namespace) {
..writeIndentedln('final ${typeOfValues.dartTypeWithTypeArgs} _value;')
..writeln()
..writeIndentedln(
'static bool canParse(Object obj, LspJsonReporter reporter) {')
'static bool canParse(Object? obj, LspJsonReporter reporter) {')
..indent();
if (allowsAnyValue) {
buffer.writeIndentedln('return ');
@ -866,7 +866,7 @@ void _writeTypeCheckCondition(IndentableStringBuffer buffer,
} else if (_isSpecType(type)) {
buffer.write('$dartType.canParse($valueCode, $reporter)');
} else if (type is ArrayType) {
buffer.write('($valueCode is List');
buffer.write('($valueCode is List<Object?>');
if (fullDartType != 'Object?') {
// TODO(dantup): If we're happy to assume we never have two lists in a union
// we could skip this bit.

View file

@ -16,7 +16,8 @@ import 'typescript_parser.dart';
Future<void> main(List<String> arguments) async {
final args = argParser.parse(arguments);
if (args[argHelp]) {
var help = args[argHelp] as bool;
if (help) {
print(argParser.usage);
return;
}
@ -189,15 +190,14 @@ List<AstNode> getCustomClasses() {
Field field(
String name, {
required String type,
array = false,
canBeNull = false,
canBeUndefined = false,
bool array = false,
bool canBeUndefined = false,
}) {
var fieldType =
array ? ArrayType(Type.identifier(type)) : Type.identifier(type);
return Field(
null, Token.identifier(name), fieldType, canBeNull, canBeUndefined);
null, Token.identifier(name), fieldType, false, canBeUndefined);
}
final customTypes = <AstNode>[
@ -302,7 +302,8 @@ List<AstNode> getCustomClasses() {
}
Future<List<AstNode>> getSpecClasses(ArgResults args) async {
if (args[argDownload]) {
var download = args[argDownload] as bool;
if (download) {
await downloadSpec();
}
final spec = await readSpec();