blob: 0f778dd27fbeb72bfee7c137362a1597f0569e5b [file] [log] [blame]
// Copyright (c) 2019, 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.
import 'package:analysis_server/src/protocol_server.dart';
import 'package:analyzer/dart/analysis/results.dart';
import 'package:analyzer/src/generated/source.dart';
import 'package:meta/meta.dart';
import 'package:nnbd_migration/instrumentation.dart';
import 'package:nnbd_migration/nnbd_migration.dart';
import 'package:nnbd_migration/src/decorated_class_hierarchy.dart';
import 'package:nnbd_migration/src/edge_builder.dart';
import 'package:nnbd_migration/src/node_builder.dart';
import 'package:nnbd_migration/src/nullability_node.dart';
import 'package:nnbd_migration/src/potential_modification.dart';
import 'package:nnbd_migration/src/variables.dart';
/// Implementation of the [NullabilityMigration] public API.
class NullabilityMigrationImpl implements NullabilityMigration {
final NullabilityMigrationListener listener;
Variables _variables;
final NullabilityGraph _graph;
final bool _permissive;
final NullabilityMigrationInstrumentation _instrumentation;
DecoratedClassHierarchy _decoratedClassHierarchy;
/// Prepares to perform nullability migration.
///
/// If [permissive] is `true`, exception handling logic will try to proceed
/// as far as possible even though the migration algorithm is not yet
/// complete. TODO(paulberry): remove this mode once the migration algorithm
/// is fully implemented.
NullabilityMigrationImpl(NullabilityMigrationListener listener,
{bool permissive: false,
NullabilityMigrationInstrumentation instrumentation})
: this._(listener, NullabilityGraph(instrumentation: instrumentation),
permissive, instrumentation);
NullabilityMigrationImpl._(
this.listener, this._graph, this._permissive, this._instrumentation) {
_instrumentation?.immutableNodes(_graph.never, _graph.always);
}
void finish() {
_graph.propagate();
if (_graph.unsatisfiedSubstitutions.isNotEmpty) {
// TODO(paulberry): for now we just ignore unsatisfied substitutions, to
// work around https://github.com/dart-lang/sdk/issues/38257
// throw new UnimplementedError('Need to report unsatisfied substitutions');
}
// TODO(paulberry): it would be nice to report on unsatisfied edges as well,
// however, since every `!` we add has an unsatisfied edge associated with
// it, we can't report on every unsatisfied edge. We need to figure out a
// way to report unsatisfied edges that isn't too overwhelming.
if (_variables != null) {
broadcast(_variables, listener, _instrumentation);
}
}
void prepareInput(ResolvedUnitResult result) {
if (_variables == null) {
_variables = Variables(_graph, result.typeProvider,
instrumentation: _instrumentation);
_decoratedClassHierarchy = DecoratedClassHierarchy(_variables, _graph);
}
var unit = result.unit;
unit.accept(NodeBuilder(_variables, unit.declaredElement.source,
_permissive ? listener : null, _graph, result.typeProvider,
instrumentation: _instrumentation));
}
void processInput(ResolvedUnitResult result) {
var unit = result.unit;
unit.accept(EdgeBuilder(
result.typeProvider,
result.typeSystem,
_variables,
_graph,
unit.declaredElement.source,
_permissive ? listener : null,
_decoratedClassHierarchy,
instrumentation: _instrumentation));
}
@visibleForTesting
static void broadcast(
Variables variables,
NullabilityMigrationListener listener,
NullabilityMigrationInstrumentation instrumentation) {
for (var entry in variables.getPotentialModifications().entries) {
var source = entry.key;
final lineInfo = LineInfo.fromContent(source.contents.data);
for (var potentialModification in entry.value) {
var modifications = potentialModification.modifications;
if (modifications.isEmpty) {
continue;
}
var fix =
_SingleNullabilityFix(source, potentialModification, lineInfo);
listener.addFix(fix);
instrumentation?.fix(fix, potentialModification.reasons);
for (var edit in modifications) {
listener.addEdit(fix, edit);
}
}
}
}
}
/// Implementation of [SingleNullabilityFix] used internally by
/// [NullabilityMigration].
class _SingleNullabilityFix extends SingleNullabilityFix {
@override
final Source source;
@override
final NullabilityFixDescription description;
Location _location;
factory _SingleNullabilityFix(Source source,
PotentialModification potentialModification, LineInfo lineInfo) {
Location location;
if (potentialModification.modifications.isNotEmpty) {
final locationInfo = lineInfo
.getLocation(potentialModification.modifications.first.offset);
location = new Location(
source.fullName,
potentialModification.modifications.first.offset,
potentialModification.modifications.first.length,
locationInfo.lineNumber,
locationInfo.columnNumber,
);
}
return _SingleNullabilityFix._(source, potentialModification.description,
location: location);
}
_SingleNullabilityFix._(this.source, this.description, {Location location})
: this._location = location;
Location get location => _location;
}