e97f1bdbf0
This should allow doing partial migration, specifically protocol files, which are imported by other libraries, but are a small library cycle that does not import much outside of it. Change-Id: I904c05d6d5b444ee9a9dbd1f7ada12aabdcc5165 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/193583 Reviewed-by: Brian Wilkerson <brianwilkerson@google.com> Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
148 lines
4.2 KiB
Dart
148 lines
4.2 KiB
Dart
// Copyright (c) 2017, 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.
|
|
|
|
// @dart = 2.9
|
|
|
|
import 'package:analysis_server/protocol/protocol_generated.dart';
|
|
import 'package:analyzer/dart/ast/ast.dart';
|
|
import 'package:analyzer/dart/ast/visitor.dart';
|
|
import 'package:analyzer/source/line_info.dart';
|
|
|
|
/// A computer for [CompilationUnit] closing labels.
|
|
class DartUnitClosingLabelsComputer {
|
|
final LineInfo _lineInfo;
|
|
final CompilationUnit _unit;
|
|
final List<ClosingLabel> _closingLabels = [];
|
|
final Set<ClosingLabel> hasNestingSet = {};
|
|
final Set<ClosingLabel> isSingleLineSet = {};
|
|
|
|
DartUnitClosingLabelsComputer(this._lineInfo, this._unit);
|
|
|
|
/// Returns a list of closing labels, not `null`.
|
|
List<ClosingLabel> compute() {
|
|
_unit.accept(_DartUnitClosingLabelsComputerVisitor(this));
|
|
|
|
return _closingLabels.where((ClosingLabel label) {
|
|
// Filter labels that don't have some nesting.
|
|
// Filter labels that start and end on the same line.
|
|
return hasNestingSet.contains(label) && !isSingleLineSet.contains(label);
|
|
}).toList();
|
|
}
|
|
|
|
void setHasNesting(ClosingLabel label) {
|
|
hasNestingSet.add(label);
|
|
}
|
|
|
|
void setSingleLine(ClosingLabel label) {
|
|
isSingleLineSet.add(label);
|
|
}
|
|
}
|
|
|
|
/// An AST visitor for [DartUnitClosingLabelsComputer].
|
|
class _DartUnitClosingLabelsComputerVisitor extends RecursiveAstVisitor<void> {
|
|
final DartUnitClosingLabelsComputer computer;
|
|
|
|
int interpolatedStringsEntered = 0;
|
|
List<ClosingLabel> labelStack = [];
|
|
|
|
_DartUnitClosingLabelsComputerVisitor(this.computer);
|
|
|
|
ClosingLabel get _currentLabel => labelStack.isEmpty ? null : labelStack.last;
|
|
|
|
@override
|
|
void visitInstanceCreationExpression(InstanceCreationExpression node) {
|
|
ClosingLabel label;
|
|
|
|
if (node.argumentList != null) {
|
|
var labelText = node.constructorName.type.name.name;
|
|
if (node.constructorName.name != null) {
|
|
labelText += '.${node.constructorName.name.name}';
|
|
}
|
|
// We override the node used for doing line calculations because otherwise
|
|
// constructors that split over multiple lines (but have parens on same
|
|
// line) would incorrectly get labels, because node.start on an instance
|
|
// creation expression starts at the start of the expression.
|
|
label = _addLabel(node, labelText, checkLinesUsing: node.argumentList);
|
|
}
|
|
|
|
if (label != null) _pushLabel(label);
|
|
|
|
try {
|
|
super.visitInstanceCreationExpression(node);
|
|
} finally {
|
|
if (label != null) _popLabel();
|
|
}
|
|
}
|
|
|
|
@override
|
|
void visitListLiteral(ListLiteral node) {
|
|
var args = node.typeArguments?.arguments;
|
|
var typeName = args != null ? args[0]?.toString() : null;
|
|
|
|
ClosingLabel label;
|
|
|
|
if (typeName != null) {
|
|
label = _addLabel(node, '<$typeName>[]');
|
|
}
|
|
|
|
if (label != null) _pushLabel(label);
|
|
|
|
try {
|
|
super.visitListLiteral(node);
|
|
} finally {
|
|
if (label != null) _popLabel();
|
|
}
|
|
}
|
|
|
|
@override
|
|
void visitStringInterpolation(StringInterpolation node) {
|
|
interpolatedStringsEntered++;
|
|
try {
|
|
super.visitStringInterpolation(node);
|
|
} finally {
|
|
interpolatedStringsEntered--;
|
|
}
|
|
}
|
|
|
|
ClosingLabel _addLabel(AstNode node, String label,
|
|
{AstNode checkLinesUsing}) {
|
|
// Never add labels if we're inside strings.
|
|
if (interpolatedStringsEntered > 0) {
|
|
return null;
|
|
}
|
|
|
|
checkLinesUsing = checkLinesUsing ?? node;
|
|
|
|
final CharacterLocation start =
|
|
computer._lineInfo.getLocation(checkLinesUsing.offset);
|
|
final CharacterLocation end =
|
|
computer._lineInfo.getLocation(checkLinesUsing.end - 1);
|
|
|
|
var closingLabel = ClosingLabel(node.offset, node.length, label);
|
|
|
|
var spannedLines = end.lineNumber - start.lineNumber;
|
|
if (spannedLines < 1) {
|
|
computer.setSingleLine(closingLabel);
|
|
}
|
|
|
|
var parent = _currentLabel;
|
|
if (parent != null) {
|
|
computer.setHasNesting(parent);
|
|
computer.setHasNesting(closingLabel);
|
|
}
|
|
|
|
computer._closingLabels.add(closingLabel);
|
|
|
|
return closingLabel;
|
|
}
|
|
|
|
void _popLabel() {
|
|
labelStack.removeLast();
|
|
}
|
|
|
|
void _pushLabel(ClosingLabel label) {
|
|
labelStack.add(label);
|
|
}
|
|
}
|