blob: 76179914b6b8c5c79709029607d125c079902701 [file] [log] [blame]
// Copyright (c) 2014, 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.
// This code was auto-generated, is not intended to be edited, and is subject to
// significant change. Please see the README file for more information.
library services.src.correction.statement_analyzer;
import 'package:analysis_services/src/correction/selection_analyzer.dart';
import 'package:analysis_services/src/correction/source_range.dart';
import 'package:analysis_services/src/correction/util.dart';
import 'package:analyzer/src/generated/ast.dart';
import 'package:analyzer/src/generated/element.dart';
import 'package:analyzer/src/generated/scanner.dart';
import 'package:analyzer/src/generated/source.dart';
/**
* Returns [Token]s of the given Dart source, not `null`, may be empty if no
* tokens or some exception happens.
*/
List<Token> _getTokens(String text) {
try {
List<Token> tokens = <Token>[];
Scanner scanner = new Scanner(null, new CharSequenceReader(text), null);
Token token = scanner.tokenize();
while (token.type != TokenType.EOF) {
tokens.add(token);
token = token.next;
}
return tokens;
} catch (e) {
return new List<Token>(0);
}
}
/**
* TODO(scheglov) port the real class
*/
class RefactoringStatus {
bool get hasFatalError => false;
void addFatalError(String message, RefactoringStatusContext context) {
}
}
/**
* TODO(scheglov) port the real class
*/
class RefactoringStatusContext {
RefactoringStatusContext.forUnit(CompilationUnit unit, SourceRange range);
}
/**
* Analyzer to check if a selection covers a valid set of statements of AST.
*/
class StatementAnalyzer extends SelectionAnalyzer {
final CompilationUnit unit;
RefactoringStatus _status = new RefactoringStatus();
StatementAnalyzer(this.unit, SourceRange selection) : super(selection);
/**
* Returns the [RefactoringStatus] result of selection checking.
*/
RefactoringStatus get status => _status;
/**
* Records fatal error with given message.
*/
void invalidSelection(String message) {
invalidSelection2(message, null);
}
/**
* Records fatal error with given message and [RefactoringStatusContext].
*/
void invalidSelection2(String message, RefactoringStatusContext context) {
_status.addFatalError(message, context);
reset();
}
@override
Object visitCompilationUnit(CompilationUnit node) {
super.visitCompilationUnit(node);
if (!hasSelectedNodes) {
return null;
}
// check that selection does not begin/end in comment
{
int selectionStart = selection.offset;
int selectionEnd = selection.end;
List<SourceRange> commentRanges = getCommentRanges(unit);
for (SourceRange commentRange in commentRanges) {
if (commentRange.contains(selectionStart)) {
invalidSelection("Selection begins inside a comment.");
}
if (commentRange.containsExclusive(selectionEnd)) {
invalidSelection("Selection ends inside a comment.");
}
}
}
// more checks
if (!_status.hasFatalError) {
_checkSelectedNodes(node);
}
return null;
}
@override
Object visitDoStatement(DoStatement node) {
super.visitDoStatement(node);
List<AstNode> selectedNodes = this.selectedNodes;
if (_contains(selectedNodes, node.body)) {
invalidSelection(
"Operation not applicable to a 'do' statement's body and expression.");
}
return null;
}
@override
Object visitForStatement(ForStatement node) {
super.visitForStatement(node);
List<AstNode> selectedNodes = this.selectedNodes;
bool containsInit =
_contains(selectedNodes, node.initialization) ||
_contains(selectedNodes, node.variables);
bool containsCondition = _contains(selectedNodes, node.condition);
bool containsUpdaters = _containsAny(selectedNodes, node.updaters);
bool containsBody = _contains(selectedNodes, node.body);
if (containsInit && containsCondition) {
invalidSelection(
"Operation not applicable to a 'for' statement's initializer and condition.");
} else if (containsCondition && containsUpdaters) {
invalidSelection(
"Operation not applicable to a 'for' statement's condition and updaters.");
} else if (containsUpdaters && containsBody) {
invalidSelection(
"Operation not applicable to a 'for' statement's updaters and body.");
}
return null;
}
@override
Object visitSwitchStatement(SwitchStatement node) {
super.visitSwitchStatement(node);
List<AstNode> selectedNodes = this.selectedNodes;
List<SwitchMember> switchMembers = node.members;
for (AstNode selectedNode in selectedNodes) {
if (switchMembers.contains(selectedNode)) {
invalidSelection(
"Selection must either cover whole switch statement or parts of a single case block.");
break;
}
}
return null;
}
@override
Object visitTryStatement(TryStatement node) {
super.visitTryStatement(node);
AstNode firstSelectedNode = this.firstSelectedNode;
if (firstSelectedNode != null) {
if (identical(firstSelectedNode, node.body) ||
identical(firstSelectedNode, node.finallyBlock)) {
invalidSelection(
"Selection must either cover whole try statement or parts of try, catch, or finally block.");
} else {
List<CatchClause> catchClauses = node.catchClauses;
for (CatchClause catchClause in catchClauses) {
if (identical(firstSelectedNode, catchClause) ||
identical(firstSelectedNode, catchClause.body) ||
identical(firstSelectedNode, catchClause.exceptionParameter)) {
invalidSelection(
"Selection must either cover whole try statement or parts of try, catch, or finally block.");
}
}
}
}
return null;
}
@override
Object visitWhileStatement(WhileStatement node) {
super.visitWhileStatement(node);
List<AstNode> selectedNodes = this.selectedNodes;
if (_contains(selectedNodes, node.condition) &&
_contains(selectedNodes, node.body)) {
invalidSelection(
"Operation not applicable to a while statement's expression and body.");
}
return null;
}
/**
* Checks final selected [AstNode]s after processing [CompilationUnit].
*/
void _checkSelectedNodes(CompilationUnit unit) {
List<AstNode> nodes = selectedNodes;
// some tokens before first selected node
{
AstNode firstNode = nodes[0];
SourceRange rangeBeforeFirstNode = rangeStartStart(selection, firstNode);
if (_hasTokens(rangeBeforeFirstNode)) {
invalidSelection2(
"The beginning of the selection contains characters that do not belong to a statement.",
new RefactoringStatusContext.forUnit(unit, rangeBeforeFirstNode));
}
}
// some tokens after last selected node
{
AstNode lastNode = nodes.last;
SourceRange rangeAfterLastNode = rangeEndEnd(lastNode, selection);
if (_hasTokens(rangeAfterLastNode)) {
invalidSelection2(
"The end of the selection contains characters that do not belong to a statement.",
new RefactoringStatusContext.forUnit(unit, rangeAfterLastNode));
}
}
}
/**
* Returns `true` if there are [Token]s in the given [SourceRange].
*/
bool _hasTokens(SourceRange range) {
CompilationUnitElement unitElement = unit.element;
String fullText = unitElement.context.getContents(unitElement.source).data;
String rangeText = fullText.substring(range.offset, range.end);
return _getTokens(rangeText).isNotEmpty;
}
/**
* Returns `true` if [nodes] contains [node].
*/
static bool _contains(List<AstNode> nodes, AstNode node) =>
nodes.contains(node);
/**
* Returns `true` if [nodes] contains one of the [otherNodes].
*/
static bool _containsAny(List<AstNode> nodes, List<AstNode> otherNodes) {
for (AstNode otherNode in otherNodes) {
if (nodes.contains(otherNode)) {
return true;
}
}
return false;
}
}