[pkg/vm_snapshot_analysis] switch to using package:lints
Change-Id: Ia36b1bf1127e0b78e0294422cff78338ef19ec26
TEST=this CL updates static analysis settings and address associated warnings
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/204640
Commit-Queue: Devon Carew <devoncarew@google.com>
Reviewed-by: Tess Strickland <sstrickl@google.com>
Reviewed-by: Slava Egorov <vegorov@google.com>
diff --git a/pkg/vm_snapshot_analysis/analysis_options.yaml b/pkg/vm_snapshot_analysis/analysis_options.yaml
index 8981941..cdcc022 100644
--- a/pkg/vm_snapshot_analysis/analysis_options.yaml
+++ b/pkg/vm_snapshot_analysis/analysis_options.yaml
@@ -1,4 +1,12 @@
-include: package:pedantic/analysis_options.1.8.0.yaml
+include: package:lints/recommended.yaml
+
analyzer:
exclude:
- lib/src/assets/**
+
+linter:
+ rules:
+ # Enable additional rules.
+ depend_on_referenced_packages: true
+ directives_ordering: true
+ sort_pub_dependencies: true
diff --git a/pkg/vm_snapshot_analysis/lib/ascii_table.dart b/pkg/vm_snapshot_analysis/lib/ascii_table.dart
index 3721814..4650cca 100644
--- a/pkg/vm_snapshot_analysis/lib/ascii_table.dart
+++ b/pkg/vm_snapshot_analysis/lib/ascii_table.dart
@@ -21,10 +21,10 @@
enum Separator {
/// Line separator looks like this: `+-------+------+`
- Line,
+ line,
/// Wave separator looks like this: `~~~~~~~~~~~~~~~~`.
- Wave,
+ wave,
}
/// A separator row in the [AsciiTable].
@@ -36,7 +36,7 @@
String render(List<int> widths, List<AlignmentDirection> alignments) {
final sb = StringBuffer();
switch (filler) {
- case Separator.Line:
+ case Separator.line:
sb.write('+');
for (var i = 0; i < widths.length; i++) {
sb.write('-' * (widths[i] + 2));
@@ -44,7 +44,7 @@
}
break;
- case Separator.Wave:
+ case Separator.wave:
sb.write('~' * Row.totalWidth(widths));
break;
}
@@ -56,7 +56,7 @@
class TextSeparatorRow extends Row {
final Text text;
TextSeparatorRow(String text)
- : text = Text(value: text, direction: AlignmentDirection.Center);
+ : text = Text(value: text, direction: AlignmentDirection.center);
@override
String render(List<int> widths, List<AlignmentDirection> alignments) {
@@ -84,7 +84,7 @@
}
}
-enum AlignmentDirection { Left, Right, Center }
+enum AlignmentDirection { left, right, center }
/// A chunk of text aligned in the given direction within a cell.
class Text {
@@ -93,11 +93,11 @@
Text({required this.value, required this.direction});
Text.left(String value)
- : this(value: value, direction: AlignmentDirection.Left);
+ : this(value: value, direction: AlignmentDirection.left);
Text.right(String value)
- : this(value: value, direction: AlignmentDirection.Right);
+ : this(value: value, direction: AlignmentDirection.right);
Text.center(String value)
- : this(value: value, direction: AlignmentDirection.Center);
+ : this(value: value, direction: AlignmentDirection.center);
String render(int width) {
if (value.length > width) {
@@ -105,11 +105,11 @@
return value.substring(0, width - 2) + '..';
}
switch (direction) {
- case AlignmentDirection.Left:
+ case AlignmentDirection.left:
return value.padRight(width);
- case AlignmentDirection.Right:
+ case AlignmentDirection.right:
return value.padLeft(width);
- case AlignmentDirection.Center:
+ case AlignmentDirection.center:
final diff = width - value.length;
return ' ' * (diff ~/ 2) + value + (' ' * (diff - diff ~/ 2));
}
@@ -135,7 +135,7 @@
void addRow(List<dynamic> columns) => rows.add(NormalRow(columns));
- void addSeparator([Separator filler = Separator.Line]) =>
+ void addSeparator([Separator filler = Separator.line]) =>
rows.add(SeparatorRow(filler));
void addTextSeparator(String text) => rows.add(TextSeparatorRow(text));
@@ -147,7 +147,7 @@
.whereType<NormalRow>()
.first
.columns
- .map((v) => v is Text ? v.direction : AlignmentDirection.Left)
+ .map((v) => v is Text ? v.direction : AlignmentDirection.left)
.toList();
List<int> widths =
List<int>.filled(rows.whereType<NormalRow>().first.columns.length, 0);
diff --git a/pkg/vm_snapshot_analysis/lib/precompiler_trace.dart b/pkg/vm_snapshot_analysis/lib/precompiler_trace.dart
index 3c9843c..06e8076 100644
--- a/pkg/vm_snapshot_analysis/lib/precompiler_trace.dart
+++ b/pkg/vm_snapshot_analysis/lib/precompiler_trace.dart
@@ -5,9 +5,9 @@
/// Helpers for working with the output of `--trace-precompiler-to` VM flag.
library vm_snapshot_analysis.precompiler_trace;
-import 'package:vm_snapshot_analysis/src/dominators.dart' as dominators;
import 'package:vm_snapshot_analysis/name.dart';
import 'package:vm_snapshot_analysis/program_info.dart';
+import 'package:vm_snapshot_analysis/src/dominators.dart' as dominators;
/// Build [CallGraph] based on the trace written by `--trace-precompiler-to`
/// flag.
@@ -32,10 +32,9 @@
/// Predecessors of this node.
final List<CallGraphNode> pred = [];
- /// Datum associated with this node: a [ProgramInfoNode] (function),
- /// a [String] (dynamic call selector) or an [int] (dispatch table
- /// selector id).
- final data;
+ /// Datum associated with this node: a [ProgramInfoNode] (function), a
+ /// [String] (dynamic call selector) or an [int] (dispatch table selector id).
+ final dynamic data;
/// Dominator of this node.
///
@@ -201,7 +200,7 @@
final selectorIdMap = <ProgramInfoNode, int>{};
/// Set of functions which can be reached through dynamic dispatch.
- final dynamicFunctions = Set<ProgramInfoNode>();
+ final dynamicFunctions = <ProgramInfoNode>{};
_TraceReader(Map<String, dynamic> data)
: strings = (data['strings'] as List<dynamic>).cast<String>(),
@@ -217,7 +216,7 @@
final nodes = <CallGraphNode>[];
final nodeByEntityId = <CallGraphNode?>[];
final callNodesBySelector = <dynamic, CallGraphNode>{};
- final allocated = Set<ProgramInfoNode>();
+ final allocated = <ProgramInfoNode>{};
T next<T>() => trace[pos++] as T;
@@ -281,7 +280,7 @@
pos--;
return false;
} else {
- throw FormatException('unexpected ref: ${ref}');
+ throw FormatException('unexpected ref: $ref');
}
return true;
}
@@ -305,7 +304,7 @@
readRefs();
break;
default:
- throw FormatException('Unknown event: ${op} at ${pos - 1}');
+ throw FormatException('Unknown event: $op at ${pos - 1}');
}
}
}
@@ -413,7 +412,7 @@
name: fieldName, parent: classNode, type: NodeType.other);
default:
- throw FormatException('unrecognized entity type ${type}');
+ throw FormatException('unrecognized entity type $type');
}
}
diff --git a/pkg/vm_snapshot_analysis/lib/program_info.dart b/pkg/vm_snapshot_analysis/lib/program_info.dart
index cca2bca..bbffd0f 100644
--- a/pkg/vm_snapshot_analysis/lib/program_info.dart
+++ b/pkg/vm_snapshot_analysis/lib/program_info.dart
@@ -63,7 +63,7 @@
void recurse(ProgramInfoNode node) {
final prevContext = context[node._type];
if (prevContext != null && node._type == NodeType.functionNode.index) {
- context[node._type] = '${prevContext}.${node.name}';
+ context[node._type] = '$prevContext.${node.name}';
} else {
context[node._type] = node.name;
}
@@ -187,7 +187,7 @@
@override
String toString() {
- return '${_typeToJson(type)} ${qualifiedName}';
+ return '${_typeToJson(type)} $qualifiedName';
}
/// Returns path to this node such that [ProgramInfo.lookup] would return
@@ -308,7 +308,7 @@
if (filter != null) {
final re = RegExp(filter.replaceAll('*', '.*'), caseSensitive: false);
matchesFilter =
- (lib, cls, fun) => re.hasMatch("${lib}::${cls ?? ''}.${fun ?? ''}");
+ (lib, cls, fun) => re.hasMatch("$lib::${cls ?? ''}.${fun ?? ''}");
} else {
matchesFilter = (_, __, ___) => true;
}
@@ -397,9 +397,9 @@
@override
String bucketFor(String? pkg, String lib, String? cls, String? fun) {
if (fun == null) {
- return '@other${_nameSeparator}';
+ return '@other$_nameSeparator';
}
- return '$lib${_nameSeparator}${cls ?? ''}${cls != '' && cls != null ? '.' : ''}${fun}';
+ return '$lib$_nameSeparator${cls ?? ''}${cls != '' && cls != null ? '.' : ''}$fun';
}
@override
@@ -412,9 +412,9 @@
@override
String bucketFor(String? pkg, String lib, String? cls, String? fun) {
if (cls == null) {
- return '@other${_nameSeparator}';
+ return '@other$_nameSeparator';
}
- return '$lib${_nameSeparator}${cls}';
+ return '$lib$_nameSeparator$cls';
}
@override
@@ -425,7 +425,7 @@
class _BucketByLibrary extends Bucketing {
@override
- String bucketFor(String? pkg, String lib, String? cls, String? fun) => '$lib';
+ String bucketFor(String? pkg, String lib, String? cls, String? fun) => lib;
const _BucketByLibrary() : super(nameComponents: const ['Library']);
}
diff --git a/pkg/vm_snapshot_analysis/lib/src/commands/compare.dart b/pkg/vm_snapshot_analysis/lib/src/commands/compare.dart
index bb2bdaf..d0d9aac 100644
--- a/pkg/vm_snapshot_analysis/lib/src/commands/compare.dart
+++ b/pkg/vm_snapshot_analysis/lib/src/commands/compare.dart
@@ -73,8 +73,7 @@
final columnWidth = args['column-width'];
final maxWidth = int.tryParse(columnWidth);
if (maxWidth == null) {
- usageException(
- 'Specified column width (${columnWidth}) is not an integer');
+ usageException('Specified column width ($columnWidth) is not an integer');
}
final oldJsonPath = _checkExists(args.rest[0]);
@@ -149,9 +148,9 @@
maxWidth: maxWidth);
print('Comparing ${oldJson.path} (old) to ${newJson.path} (new)');
- print('Old : ${totalOld} bytes.');
- print('New : ${totalNew} bytes.');
- print('Change: ${totalDiff > 0 ? '+' : ''}${totalDiff}'
+ print('Old : $totalOld bytes.');
+ print('New : $totalNew bytes.');
+ print('Change: ${totalDiff > 0 ? '+' : ''}$totalDiff'
' (${formatPercent(totalDiff, totalOld, withSign: true)}) bytes.');
if (oldSizes.snapshotInfo != null) {
@@ -163,7 +162,7 @@
computeHistogram(newSizes, HistogramType.byNodeType);
final diffTypeHistogram = Histogram.fromIterable<String>(
- Set<String>()
+ <String>{}
..addAll(oldTypeHistogram.buckets.keys)
..addAll(newTypeHistogram.buckets.keys),
sizeOf: (bucket) =>
diff --git a/pkg/vm_snapshot_analysis/lib/src/commands/explain.dart b/pkg/vm_snapshot_analysis/lib/src/commands/explain.dart
index e81833f..130e34b 100644
--- a/pkg/vm_snapshot_analysis/lib/src/commands/explain.dart
+++ b/pkg/vm_snapshot_analysis/lib/src/commands/explain.dart
@@ -49,7 +49,7 @@
precompiler trace (an output of --trace-precompiler-to flag).
''';
- ExplainCommand() {}
+ ExplainDynamicCallsCommand();
@override
Future<void> run() async {
@@ -115,7 +115,7 @@
final callNodes = callGraph.nodes
.where((n) => n.data == selector || n.data == dynSelector);
- print('\nDynamic call to ${selector}'
+ print('\nDynamic call to $selector'
' (retaining ~${histogram.buckets[selector]} bytes) occurs in:');
for (var node in callNodes) {
for (var pred in node.pred) {
diff --git a/pkg/vm_snapshot_analysis/lib/src/commands/summary.dart b/pkg/vm_snapshot_analysis/lib/src/commands/summary.dart
index 90b87b9..3709c82 100644
--- a/pkg/vm_snapshot_analysis/lib/src/commands/summary.dart
+++ b/pkg/vm_snapshot_analysis/lib/src/commands/summary.dart
@@ -110,7 +110,7 @@
final traceJson = args['precompiler-trace'];
if (traceJson != null) {
if (!File(traceJson).existsSync()) {
- usageException('Trace ${traceJson} does not exist!');
+ usageException('Trace $traceJson does not exist!');
}
if (granularity != HistogramType.byPackage &&
@@ -123,21 +123,20 @@
final columnWidth = args['column-width'];
final maxWidth = int.tryParse(columnWidth);
if (maxWidth == null) {
- usageException(
- 'Specified column width (${columnWidth}) is not an integer');
+ usageException('Specified column width ($columnWidth) is not an integer');
}
final depsStartDepthStr = args['deps-start-depth'];
final depsStartDepth = int.tryParse(depsStartDepthStr);
if (depsStartDepth == null) {
- usageException('Specified depsStartDepth (${depsStartDepthStr})'
+ usageException('Specified depsStartDepth ($depsStartDepthStr)'
' is not an integer');
}
final depsDisplayDepthStr = args['deps-display-depth'];
final depsDisplayDepth = int.tryParse(depsDisplayDepthStr);
if (depsDisplayDepth == null) {
- usageException('Specified depsDisplayDepth (${depsStartDepthStr})'
+ usageException('Specified depsDisplayDepth ($depsStartDepthStr)'
' is not an integer');
}
@@ -329,7 +328,7 @@
final size = sizes[i];
isLastPerLevel.add(isLast);
print(
- '${_treeLines(isLastPerLevel)}${n.data.qualifiedName} (total ${size} bytes)');
+ '${_treeLines(isLastPerLevel)}${n.data.qualifiedName} (total $size bytes)');
_printDominatedNodes(n,
displayDepth: displayDepth,
isLastPerLevel: isLastPerLevel,
diff --git a/pkg/vm_snapshot_analysis/lib/src/commands/treemap.dart b/pkg/vm_snapshot_analysis/lib/src/commands/treemap.dart
index 2de0694..2355a41 100644
--- a/pkg/vm_snapshot_analysis/lib/src/commands/treemap.dart
+++ b/pkg/vm_snapshot_analysis/lib/src/commands/treemap.dart
@@ -15,8 +15,8 @@
import 'dart:io';
import 'dart:isolate';
-import 'package:path/path.dart' as p;
import 'package:args/command_runner.dart';
+import 'package:path/path.dart' as p;
import 'package:vm_snapshot_analysis/treemap.dart';
import 'utils.dart';
diff --git a/pkg/vm_snapshot_analysis/lib/src/dominators.dart b/pkg/vm_snapshot_analysis/lib/src/dominators.dart
index 92504f3..9a0444b 100644
--- a/pkg/vm_snapshot_analysis/lib/src/dominators.dart
+++ b/pkg/vm_snapshot_analysis/lib/src/dominators.dart
@@ -17,7 +17,7 @@
required int root,
required Iterable<int> Function(int) succ,
required Iterable<int> Function(int) predOf,
- required void handleEdge(int from, int to),
+ required void Function(int from, int to) handleEdge,
}) {
// Compute preorder numbering for the graph using DFS.
final parent = List<int>.filled(size, -1);
@@ -82,39 +82,39 @@
// Loop over the blocks in reverse preorder (not including the graph
// entry).
- for (var block_index = size - 1; block_index >= 1; --block_index) {
+ for (var blockIndex = size - 1; blockIndex >= 1; --blockIndex) {
// Loop over the predecessors.
- final block = preorder[block_index];
+ final block = preorder[blockIndex];
// Clear the immediately dominated blocks in case ComputeDominators is
// used to recompute them.
for (final pred in predOf(block)) {
// Look for the semidominator by ascending the semidominator path
// starting from pred.
- final pred_index = preorderNumber[pred];
- var best = pred_index;
- if (pred_index > block_index) {
- compressPath(block_index, pred_index);
- best = label[pred_index];
+ final predIndex = preorderNumber[pred];
+ var best = predIndex;
+ if (predIndex > blockIndex) {
+ compressPath(blockIndex, predIndex);
+ best = label[predIndex];
}
// Update the semidominator if we've found a better one.
- semi[block_index] = math.min(semi[block_index], semi[best]);
+ semi[blockIndex] = math.min(semi[blockIndex], semi[best]);
}
// Now use label for the semidominator.
- label[block_index] = semi[block_index];
+ label[blockIndex] = semi[blockIndex];
}
// 2. Compute the immediate dominators as the nearest common ancestor of
// spanning tree parent and semidominator, for all blocks except the entry.
final result = List<int>.filled(size, -1);
- for (var block_index = 1; block_index < size; ++block_index) {
- var dom_index = idom[block_index];
- while (dom_index > semi[block_index]) {
- dom_index = idom[dom_index];
+ for (var blockIndex = 1; blockIndex < size; ++blockIndex) {
+ var domIndex = idom[blockIndex];
+ while (domIndex > semi[blockIndex]) {
+ domIndex = idom[domIndex];
}
- idom[block_index] = dom_index;
- result[preorder[block_index]] = preorder[dom_index];
+ idom[blockIndex] = domIndex;
+ result[preorder[blockIndex]] = preorder[domIndex];
}
return result;
}
diff --git a/pkg/vm_snapshot_analysis/lib/treemap.dart b/pkg/vm_snapshot_analysis/lib/treemap.dart
index 2d6da72..3c4458a 100644
--- a/pkg/vm_snapshot_analysis/lib/treemap.dart
+++ b/pkg/vm_snapshot_analysis/lib/treemap.dart
@@ -7,9 +7,9 @@
import 'dart:math';
-import 'package:vm_snapshot_analysis/program_info.dart';
import 'package:vm_snapshot_analysis/instruction_sizes.dart'
as instruction_sizes;
+import 'package:vm_snapshot_analysis/program_info.dart';
import 'package:vm_snapshot_analysis/utils.dart';
import 'package:vm_snapshot_analysis/v8_profile.dart' as v8_profile;
diff --git a/pkg/vm_snapshot_analysis/lib/utils.dart b/pkg/vm_snapshot_analysis/lib/utils.dart
index 40ef457..d722bb2 100644
--- a/pkg/vm_snapshot_analysis/lib/utils.dart
+++ b/pkg/vm_snapshot_analysis/lib/utils.dart
@@ -4,9 +4,9 @@
library vm_snapshot_analysis.utils;
import 'package:vm_snapshot_analysis/ascii_table.dart';
-import 'package:vm_snapshot_analysis/program_info.dart';
import 'package:vm_snapshot_analysis/instruction_sizes.dart'
as instruction_sizes;
+import 'package:vm_snapshot_analysis/program_info.dart';
import 'package:vm_snapshot_analysis/treemap.dart';
import 'package:vm_snapshot_analysis/v8_profile.dart' as v8_profile;
@@ -44,7 +44,7 @@
String formatPercent(int value, int total, {bool withSign = false}) {
final p = value / total * 100.0;
final sign = (withSign && value > 0) ? '+' : '';
- return '${sign}${p.toStringAsFixed(2)}%';
+ return '$sign${p.toStringAsFixed(2)}%';
}
void printHistogram(ProgramInfo info, Histogram histogram,
@@ -80,19 +80,19 @@
if (wasFiltered) formatPercent(size, totalSize),
]);
}
- table.addSeparator(interestingHiddenRows ? Separator.Wave : Separator.Line);
+ table.addSeparator(interestingHiddenRows ? Separator.wave : Separator.line);
}
if (interestingHiddenRows) {
final totalRestBytes = histogram.totalSize - visibleSize;
table.addTextSeparator(
- '$numRestRows more rows accounting for ${totalRestBytes}'
+ '$numRestRows more rows accounting for $totalRestBytes'
' (${formatPercent(totalRestBytes, totalSize)} of total) bytes');
final avg = (totalRestBytes / numRestRows).round();
table.addTextSeparator(
- 'on average that is ${avg} (${formatPercent(avg, histogram.totalSize)})'
+ 'on average that is $avg (${formatPercent(avg, histogram.totalSize)})'
' bytes per row');
- table.addSeparator(suffix.isNotEmpty ? Separator.Wave : Separator.Line);
+ table.addSeparator(suffix.isNotEmpty ? Separator.wave : Separator.line);
}
if (suffix.isNotEmpty) {
@@ -103,16 +103,16 @@
formatPercent(histogram.buckets[key]!, histogram.totalSize),
]);
}
- table.addSeparator(Separator.Line);
+ table.addSeparator(Separator.line);
}
table.render();
if (wasFiltered || visibleSize != histogram.totalSize) {
- print('In visible rows: ${visibleSize}'
+ print('In visible rows: $visibleSize'
' (${formatPercent(visibleSize, totalSize)} of total)');
}
- print('Total: ${totalSize} bytes');
+ print('Total: $totalSize bytes');
}
List<String> partsForPath(String path) {
diff --git a/pkg/vm_snapshot_analysis/lib/v8_profile.dart b/pkg/vm_snapshot_analysis/lib/v8_profile.dart
index 7dde867..8fbe488 100644
--- a/pkg/vm_snapshot_analysis/lib/v8_profile.dart
+++ b/pkg/vm_snapshot_analysis/lib/v8_profile.dart
@@ -7,10 +7,9 @@
library vm_snapshot_analysis.v8_profile;
import 'package:collection/collection.dart';
-
-import 'package:vm_snapshot_analysis/src/dominators.dart' as dominators;
import 'package:vm_snapshot_analysis/name.dart';
import 'package:vm_snapshot_analysis/program_info.dart';
+import 'package:vm_snapshot_analysis/src/dominators.dart' as dominators;
/// This class represents snapshot graph.
///
@@ -248,7 +247,7 @@
/// Returns the target of an outgoing edge with the given name (if any).
Node? operator [](String edgeName) =>
- this.edges.firstWhereOrNull((e) => e.name == edgeName)?.target;
+ edges.firstWhereOrNull((e) => e.name == edgeName)?.target;
@override
bool operator ==(Object other) {
@@ -256,7 +255,7 @@
}
@override
- int get hashCode => this.index.hashCode;
+ int get hashCode => index.hashCode;
/// Offset into [Snapshot._nodes] list at which this node begins.
int get _offset => index * snapshot.meta.nodeFieldCount;
diff --git a/pkg/vm_snapshot_analysis/pubspec.yaml b/pkg/vm_snapshot_analysis/pubspec.yaml
index ba6357d..cf8bed0 100644
--- a/pkg/vm_snapshot_analysis/pubspec.yaml
+++ b/pkg/vm_snapshot_analysis/pubspec.yaml
@@ -12,9 +12,9 @@
dependencies:
args: ^2.0.0
- path: ^1.8.0
collection: ^1.15.0
+ path: ^1.8.0
dev_dependencies:
- pedantic: ^1.11.0
+ lints: any
test: ^1.16.8
diff --git a/pkg/vm_snapshot_analysis/test/instruction_sizes_test.dart b/pkg/vm_snapshot_analysis/test/instruction_sizes_test.dart
index d136957..1590ea8 100644
--- a/pkg/vm_snapshot_analysis/test/instruction_sizes_test.dart
+++ b/pkg/vm_snapshot_analysis/test/instruction_sizes_test.dart
@@ -4,9 +4,8 @@
import 'dart:io';
-import 'package:test/test.dart';
import 'package:collection/collection.dart';
-
+import 'package:test/test.dart';
import 'package:vm_snapshot_analysis/instruction_sizes.dart'
as instruction_sizes;
import 'package:vm_snapshot_analysis/program_info.dart';
@@ -217,7 +216,7 @@
extension on Histogram {
String bucketFor(String pkg, String lib, String cls, String fun) =>
- (this.bucketInfo as Bucketing).bucketFor(pkg, lib, cls, fun);
+ (bucketInfo as Bucketing).bucketFor(pkg, lib, cls, fun);
}
void main() async {
@@ -838,8 +837,8 @@
test('treemap', () async {
await withV8Profile(testSource, (profileJson) async {
final infoJson = await loadJson(File(profileJson));
- final info = await loadProgramInfoFromJson(infoJson,
- collapseAnonymousClosures: true);
+ final info =
+ loadProgramInfoFromJson(infoJson, collapseAnonymousClosures: true);
final treemap = treemapFromInfo(info);
List<Map<String, dynamic>> childrenOf(Map<String, dynamic> node) =>