Implement more fixes.
R=paulberry@google.com BUG= Review URL: https://codereview.chromium.org//418203002 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@38571 260f80e4-7a28-3924-810f-c04153c831b5
This commit is contained in:
@@ -95,7 +95,7 @@ class EditDomainHandler implements RequestHandler {
|
||||
engine.AnalysisErrorInfo errorInfo = server.getErrors(file);
|
||||
if (errorInfo != null) {
|
||||
for (engine.AnalysisError error in errorInfo.errors) {
|
||||
List<Fix> fixes = computeFixes(searchEngine, file, unit, error);
|
||||
List<Fix> fixes = computeFixes(searchEngine, unit, error);
|
||||
if (fixes.isNotEmpty) {
|
||||
AnalysisError serverError =
|
||||
new AnalysisError.fromEngine(errorInfo.lineInfo, error);
|
||||
|
||||
@@ -117,7 +117,7 @@ main() {
|
||||
engine.AnalysisErrorInfo errors = context.getErrors(testSource);
|
||||
engine.AnalysisError engineError = errors.errors[0];
|
||||
List<services.Fix> servicesFixes =
|
||||
services.computeFixes(searchEngine, testFile, testUnit, engineError);
|
||||
services.computeFixes(searchEngine, testUnit, engineError);
|
||||
AnalysisError error =
|
||||
new AnalysisError.fromEngine(errors.lineInfo, engineError);
|
||||
ErrorFixes fixes = new ErrorFixes(error);
|
||||
|
||||
@@ -37,6 +37,11 @@ class Change implements HasToJson {
|
||||
final List<LinkedPositionGroup> linkedPositionGroups = <LinkedPositionGroup>[
|
||||
];
|
||||
|
||||
/**
|
||||
* An optional position to move selection to after applying this change.
|
||||
*/
|
||||
Position endPosition;
|
||||
|
||||
Change(this.message);
|
||||
|
||||
/**
|
||||
|
||||
@@ -9,6 +9,7 @@ import 'package:analysis_services/search/search_engine.dart';
|
||||
import 'package:analysis_services/src/correction/fix.dart';
|
||||
import 'package:analyzer/src/generated/ast.dart';
|
||||
import 'package:analyzer/src/generated/error.dart';
|
||||
import 'package:analyzer/src/generated/source.dart';
|
||||
|
||||
|
||||
/**
|
||||
@@ -16,9 +17,11 @@ import 'package:analyzer/src/generated/error.dart';
|
||||
*
|
||||
* Returns the computed [Fix]s, not `null`.
|
||||
*/
|
||||
List<Fix> computeFixes(SearchEngine searchEngine, String file,
|
||||
List<Fix> computeFixes(SearchEngine searchEngine,
|
||||
CompilationUnit unit, AnalysisError error) {
|
||||
var processor = new FixProcessor(searchEngine, file, unit, error);
|
||||
Source source = unit.element.source;
|
||||
String file = source.fullName;
|
||||
var processor = new FixProcessor(searchEngine, source, file, unit, error);
|
||||
return processor.compute();
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -26,6 +26,10 @@ class SourceBuilder {
|
||||
|
||||
SourceBuilder(this.file, this.offset);
|
||||
|
||||
SourceBuilder.buffer() : file = null, offset = 0;
|
||||
|
||||
int get length => _buffer.length;
|
||||
|
||||
void addProposal(String proposal) {
|
||||
// TODO(scheglov) implement
|
||||
// _currentPositionGroup.addProposal();
|
||||
|
||||
@@ -58,6 +58,19 @@ String removeStart(String str, String remove) {
|
||||
return str;
|
||||
}
|
||||
|
||||
int compareStrings(String a, String b) {
|
||||
if (a == b) {
|
||||
return 0;
|
||||
}
|
||||
if (a == null) {
|
||||
return 1;
|
||||
}
|
||||
if (b == null) {
|
||||
return -1;
|
||||
}
|
||||
return a.compareTo(b);
|
||||
}
|
||||
|
||||
String repeat(String s, int n) {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
for (int i = 0; i < n; i++) {
|
||||
|
||||
@@ -11,6 +11,7 @@ import 'package:analysis_services/src/correction/source_range.dart';
|
||||
import 'package:analysis_services/src/correction/strings.dart';
|
||||
import 'package:analyzer/src/generated/ast.dart';
|
||||
import 'package:analyzer/src/generated/element.dart';
|
||||
import 'package:analyzer/src/generated/engine.dart';
|
||||
import 'package:analyzer/src/generated/resolver.dart';
|
||||
import 'package:analyzer/src/generated/source.dart';
|
||||
|
||||
@@ -36,6 +37,26 @@ String getDefaultValueCode(DartType type) {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return the [ExecutableElement] of the enclosing executable [AstNode].
|
||||
*/
|
||||
ExecutableElement getEnclosingExecutableElement(AstNode node) {
|
||||
while (node != null) {
|
||||
if (node is FunctionDeclaration) {
|
||||
return node.element;
|
||||
}
|
||||
if (node is ConstructorDeclaration) {
|
||||
return node.element;
|
||||
}
|
||||
if (node is MethodDeclaration) {
|
||||
return node.element;
|
||||
}
|
||||
node = node.parent;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns [getExpressionPrecedence] for the parent of [node],
|
||||
* or `0` if the parent node is [ParenthesizedExpression].
|
||||
@@ -71,6 +92,34 @@ Map<String, Element> getImportNamespace(ImportElement imp) {
|
||||
return namespace.definedNames;
|
||||
}
|
||||
|
||||
/**
|
||||
* If given [AstNode] is name of qualified property extraction, returns target from which
|
||||
* this property is extracted. Otherwise `null`.
|
||||
*/
|
||||
Expression getQualifiedPropertyTarget(AstNode node) {
|
||||
AstNode parent = node.parent;
|
||||
if (parent is PrefixedIdentifier) {
|
||||
PrefixedIdentifier prefixed = parent;
|
||||
if (identical(prefixed.identifier, node)) {
|
||||
return parent.prefix;
|
||||
}
|
||||
}
|
||||
if (parent is PropertyAccess) {
|
||||
PropertyAccess access = parent;
|
||||
if (identical(access.propertyName, node)) {
|
||||
return access.realTarget;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the [String] content of the given [Source].
|
||||
*/
|
||||
String getSourceContent(AnalysisContext context, Source source) {
|
||||
return context.getContents(source).data;
|
||||
}
|
||||
|
||||
class CorrectionUtils {
|
||||
final CompilationUnit unit;
|
||||
@@ -99,6 +148,26 @@ class CorrectionUtils {
|
||||
return _endOfLine;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the actual type source of the given [Expression], may be `null`
|
||||
* if can not be resolved, should be treated as the `dynamic` type.
|
||||
*/
|
||||
String getExpressionTypeSource(Expression expression) {
|
||||
if (expression == null) {
|
||||
return null;
|
||||
}
|
||||
DartType type = expression.bestType;
|
||||
if (type.isDynamic) {
|
||||
return null;
|
||||
}
|
||||
return getTypeSource(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the indentation with the given level.
|
||||
*/
|
||||
String getIndent(int level) => repeat(' ', level);
|
||||
|
||||
/**
|
||||
* Skips whitespace characters and single EOL on the right from [index].
|
||||
*
|
||||
@@ -144,6 +213,40 @@ class CorrectionUtils {
|
||||
return index;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the whitespace prefix of the line which contains given offset.
|
||||
*/
|
||||
String getLinePrefix(int index) {
|
||||
int lineStart = getLineThis(index);
|
||||
int length = _buffer.length;
|
||||
int lineNonWhitespace = lineStart;
|
||||
while (lineNonWhitespace < length) {
|
||||
int c = _buffer.codeUnitAt(lineNonWhitespace);
|
||||
if (c == 0xD || c == 0xA) {
|
||||
break;
|
||||
}
|
||||
if (!isWhitespace(c)) {
|
||||
break;
|
||||
}
|
||||
lineNonWhitespace++;
|
||||
}
|
||||
return getText2(lineStart, lineNonWhitespace - lineStart);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the start index of the line which contains given index.
|
||||
*/
|
||||
int getLineThis(int index) {
|
||||
while (index > 0) {
|
||||
int c = _buffer.codeUnitAt(index - 1);
|
||||
if (c == 0xD || c == 0xA) {
|
||||
break;
|
||||
}
|
||||
index--;
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a [SourceRange] that covers [range] and extends (if possible) to
|
||||
* cover whole lines.
|
||||
@@ -159,6 +262,20 @@ class CorrectionUtils {
|
||||
return rangeStartEnd(startLineOffset, afterEndLineOffset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the line prefix consisting of spaces and tabs on the left from the given
|
||||
* [AstNode].
|
||||
*/
|
||||
String getNodePrefix(AstNode node) {
|
||||
int offset = node.offset;
|
||||
// function literal is special, it uses offset of enclosing line
|
||||
if (node is FunctionExpression) {
|
||||
return getLinePrefix(offset);
|
||||
}
|
||||
// use just prefix directly before node
|
||||
return getPrefix(offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the source for the parameter with the given type and name.
|
||||
*/
|
||||
@@ -198,21 +315,25 @@ class CorrectionUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the actual type source of the given [Expression], may be `null`
|
||||
* if can not be resolved, should be treated as the `dynamic` type.
|
||||
* Returns the line prefix consisting of spaces and tabs on the left from the
|
||||
* given offset.
|
||||
*/
|
||||
String getExpressionTypeSource(Expression expression) {
|
||||
if (expression == null) {
|
||||
return null;
|
||||
}
|
||||
DartType type = expression.bestType;
|
||||
String typeSource = getTypeSource(type);
|
||||
if ("dynamic" == typeSource) {
|
||||
return null;
|
||||
}
|
||||
return typeSource;
|
||||
String getPrefix(int endIndex) {
|
||||
int startIndex = getLineContentStart(endIndex);
|
||||
return _buffer.substring(startIndex, endIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the text of the given [AstNode] in the unit.
|
||||
*/
|
||||
String getText(AstNode node) => getText2(node.offset, node.length);
|
||||
|
||||
/**
|
||||
* Returns the text of the given range in the unit.
|
||||
*/
|
||||
String getText2(int offset, int length) =>
|
||||
_buffer.substring(offset, offset + length);
|
||||
|
||||
/**
|
||||
* Returns the source to reference [type] in this [CompilationUnit].
|
||||
*/
|
||||
|
||||
@@ -59,7 +59,7 @@ class FixProcessorTest extends AbstractSingleUnitTest {
|
||||
|
||||
void assertNoFix(FixKind kind) {
|
||||
AnalysisError error = _findErrorToFix();
|
||||
List<Fix> fixes = computeFixes(searchEngine, testFile, testUnit, error);
|
||||
List<Fix> fixes = computeFixes(searchEngine, testUnit, error);
|
||||
for (Fix fix in fixes) {
|
||||
if (fix.kind == kind) {
|
||||
throw fail('Unexpected fix $kind in\n${fixes.join('\n')}');
|
||||
@@ -242,6 +242,94 @@ class B extends A {
|
||||
assertNoFix(FixKind.ADD_SUPER_CONSTRUCTOR_INVOCATION);
|
||||
}
|
||||
|
||||
void test_createConstructorSuperImplicit() {
|
||||
_indexTestUnit('''
|
||||
class A {
|
||||
A(p1, int p2, List<String> p3, [int p4]);
|
||||
}
|
||||
class B extends A {
|
||||
int existingField;
|
||||
|
||||
void existingMethod() {}
|
||||
}
|
||||
''');
|
||||
assertHasFix(FixKind.CREATE_CONSTRUCTOR_SUPER, '''
|
||||
class A {
|
||||
A(p1, int p2, List<String> p3, [int p4]);
|
||||
}
|
||||
class B extends A {
|
||||
int existingField;
|
||||
|
||||
B(p1, int p2, List<String> p3) : super(p1, p2, p3);
|
||||
|
||||
void existingMethod() {}
|
||||
}
|
||||
''');
|
||||
}
|
||||
|
||||
void test_createConstructorSuperImplicit_fieldInitializer() {
|
||||
_indexTestUnit('''
|
||||
class A {
|
||||
int _field;
|
||||
A(this._field);
|
||||
}
|
||||
class B extends A {
|
||||
int existingField;
|
||||
|
||||
void existingMethod() {}
|
||||
}
|
||||
''');
|
||||
assertHasFix(FixKind.CREATE_CONSTRUCTOR_SUPER, '''
|
||||
class A {
|
||||
int _field;
|
||||
A(this._field);
|
||||
}
|
||||
class B extends A {
|
||||
int existingField;
|
||||
|
||||
B(int field) : super(field);
|
||||
|
||||
void existingMethod() {}
|
||||
}
|
||||
''');
|
||||
}
|
||||
|
||||
void test_createConstructorSuperImplicit_named() {
|
||||
_indexTestUnit('''
|
||||
class A {
|
||||
A.named(p1, int p2);
|
||||
}
|
||||
class B extends A {
|
||||
int existingField;
|
||||
|
||||
void existingMethod() {}
|
||||
}
|
||||
''');
|
||||
assertHasFix(FixKind.CREATE_CONSTRUCTOR_SUPER, '''
|
||||
class A {
|
||||
A.named(p1, int p2);
|
||||
}
|
||||
class B extends A {
|
||||
int existingField;
|
||||
|
||||
B.named(p1, int p2) : super.named(p1, p2);
|
||||
|
||||
void existingMethod() {}
|
||||
}
|
||||
''');
|
||||
}
|
||||
|
||||
void test_createConstructorSuperImplicit_private() {
|
||||
_indexTestUnit('''
|
||||
class A {
|
||||
A._named(p);
|
||||
}
|
||||
class B extends A {
|
||||
}
|
||||
''');
|
||||
assertNoFix(FixKind.CREATE_CONSTRUCTOR_SUPER);
|
||||
}
|
||||
|
||||
void test_createConstructor_insteadOfSyntheticDefault() {
|
||||
_indexTestUnit('''
|
||||
class A {
|
||||
@@ -290,6 +378,455 @@ main() {
|
||||
''');
|
||||
}
|
||||
|
||||
void test_createMissingOverrides_functionType() {
|
||||
_indexTestUnit('''
|
||||
abstract class A {
|
||||
forEach(int f(double p1, String p2));
|
||||
}
|
||||
|
||||
class B extends A {
|
||||
}
|
||||
''');
|
||||
assertHasFix(FixKind.CREATE_MISSING_OVERRIDES, '''
|
||||
abstract class A {
|
||||
forEach(int f(double p1, String p2));
|
||||
}
|
||||
|
||||
class B extends A {
|
||||
@override
|
||||
forEach(int f(double p1, String p2)) {
|
||||
// TODO: implement forEach
|
||||
}
|
||||
}
|
||||
''');
|
||||
}
|
||||
|
||||
void test_createMissingOverrides_generics() {
|
||||
_indexTestUnit('''
|
||||
class Iterator<T> {
|
||||
}
|
||||
|
||||
abstract class IterableMixin<T> {
|
||||
Iterator<T> get iterator;
|
||||
}
|
||||
|
||||
class Test extends IterableMixin<int> {
|
||||
}
|
||||
''');
|
||||
assertHasFix(FixKind.CREATE_MISSING_OVERRIDES, '''
|
||||
class Iterator<T> {
|
||||
}
|
||||
|
||||
abstract class IterableMixin<T> {
|
||||
Iterator<T> get iterator;
|
||||
}
|
||||
|
||||
class Test extends IterableMixin<int> {
|
||||
// TODO: implement iterator
|
||||
@override
|
||||
Iterator<int> get iterator => null;
|
||||
}
|
||||
''');
|
||||
}
|
||||
|
||||
void test_createMissingOverrides_getter() {
|
||||
_indexTestUnit('''
|
||||
abstract class A {
|
||||
get g1;
|
||||
int get g2;
|
||||
}
|
||||
|
||||
class B extends A {
|
||||
}
|
||||
''');
|
||||
assertHasFix(FixKind.CREATE_MISSING_OVERRIDES, '''
|
||||
abstract class A {
|
||||
get g1;
|
||||
int get g2;
|
||||
}
|
||||
|
||||
class B extends A {
|
||||
// TODO: implement g1
|
||||
@override
|
||||
get g1 => null;
|
||||
|
||||
// TODO: implement g2
|
||||
@override
|
||||
int get g2 => null;
|
||||
}
|
||||
''');
|
||||
}
|
||||
|
||||
void test_createMissingOverrides_importPrefix() {
|
||||
_indexTestUnit('''
|
||||
import 'dart:async' as aaa;
|
||||
abstract class A {
|
||||
Map<aaa.Future, List<aaa.Future>> g(aaa.Future p);
|
||||
}
|
||||
|
||||
class B extends A {
|
||||
}
|
||||
''');
|
||||
assertHasFix(FixKind.CREATE_MISSING_OVERRIDES, '''
|
||||
import 'dart:async' as aaa;
|
||||
abstract class A {
|
||||
Map<aaa.Future, List<aaa.Future>> g(aaa.Future p);
|
||||
}
|
||||
|
||||
class B extends A {
|
||||
@override
|
||||
Map<aaa.Future, List<aaa.Future>> g(aaa.Future p) {
|
||||
// TODO: implement g
|
||||
}
|
||||
}
|
||||
''');
|
||||
}
|
||||
|
||||
void test_createMissingOverrides_method() {
|
||||
_indexTestUnit('''
|
||||
abstract class A {
|
||||
m1();
|
||||
int m2();
|
||||
String m3(int p1, double p2, Map<int, List<String>> p3);
|
||||
String m4(p1, p2);
|
||||
String m5(p1, [int p2 = 2, int p3, p4 = 4]);
|
||||
String m6(p1, {int p2: 2, int p3, p4: 4});
|
||||
}
|
||||
|
||||
class B extends A {
|
||||
}
|
||||
''');
|
||||
String expectedCode = '''
|
||||
abstract class A {
|
||||
m1();
|
||||
int m2();
|
||||
String m3(int p1, double p2, Map<int, List<String>> p3);
|
||||
String m4(p1, p2);
|
||||
String m5(p1, [int p2 = 2, int p3, p4 = 4]);
|
||||
String m6(p1, {int p2: 2, int p3, p4: 4});
|
||||
}
|
||||
|
||||
class B extends A {
|
||||
@override
|
||||
m1() {
|
||||
// TODO: implement m1
|
||||
}
|
||||
|
||||
@override
|
||||
int m2() {
|
||||
// TODO: implement m2
|
||||
}
|
||||
|
||||
@override
|
||||
String m3(int p1, double p2, Map<int, List<String>> p3) {
|
||||
// TODO: implement m3
|
||||
}
|
||||
|
||||
@override
|
||||
String m4(p1, p2) {
|
||||
// TODO: implement m4
|
||||
}
|
||||
|
||||
@override
|
||||
String m5(p1, [int p2 = 2, int p3, p4 = 4]) {
|
||||
// TODO: implement m5
|
||||
}
|
||||
|
||||
@override
|
||||
String m6(p1, {int p2: 2, int p3, p4: 4}) {
|
||||
// TODO: implement m6
|
||||
}
|
||||
}
|
||||
''';
|
||||
assertHasFix(FixKind.CREATE_MISSING_OVERRIDES, expectedCode);
|
||||
// end position should be on "m1", not on "m2", "m3", etc
|
||||
{
|
||||
Position endPosition = change.endPosition;
|
||||
expect(endPosition, isNotNull);
|
||||
expect(endPosition.file, testFile);
|
||||
int endOffset = endPosition.offset;
|
||||
String endString = expectedCode.substring(endOffset, endOffset + 25);
|
||||
expect(endString, contains('m1'));
|
||||
expect(endString, isNot(contains('m2')));
|
||||
expect(endString, isNot(contains('m3')));
|
||||
expect(endString, isNot(contains('m4')));
|
||||
expect(endString, isNot(contains('m5')));
|
||||
expect(endString, isNot(contains('m6')));
|
||||
}
|
||||
}
|
||||
|
||||
void test_createMissingOverrides_operator() {
|
||||
_indexTestUnit('''
|
||||
abstract class A {
|
||||
int operator [](int index);
|
||||
void operator []=(int index, String value);
|
||||
}
|
||||
|
||||
class B extends A {
|
||||
}
|
||||
''');
|
||||
assertHasFix(FixKind.CREATE_MISSING_OVERRIDES, '''
|
||||
abstract class A {
|
||||
int operator [](int index);
|
||||
void operator []=(int index, String value);
|
||||
}
|
||||
|
||||
class B extends A {
|
||||
@override
|
||||
int operator [](int index) {
|
||||
// TODO: implement []
|
||||
}
|
||||
|
||||
@override
|
||||
void operator []=(int index, String value) {
|
||||
// TODO: implement []=
|
||||
}
|
||||
}
|
||||
''');
|
||||
}
|
||||
|
||||
void test_createMissingOverrides_setter() {
|
||||
_indexTestUnit('''
|
||||
abstract class A {
|
||||
set s1(x);
|
||||
set s2(int x);
|
||||
void set s3(String x);
|
||||
}
|
||||
|
||||
class B extends A {
|
||||
}
|
||||
''');
|
||||
assertHasFix(FixKind.CREATE_MISSING_OVERRIDES, '''
|
||||
abstract class A {
|
||||
set s1(x);
|
||||
set s2(int x);
|
||||
void set s3(String x);
|
||||
}
|
||||
|
||||
class B extends A {
|
||||
@override
|
||||
set s1(x) {
|
||||
// TODO: implement s1
|
||||
}
|
||||
|
||||
@override
|
||||
set s2(int x) {
|
||||
// TODO: implement s2
|
||||
}
|
||||
|
||||
@override
|
||||
void set s3(String x) {
|
||||
// TODO: implement s3
|
||||
}
|
||||
}
|
||||
''');
|
||||
}
|
||||
|
||||
void test_createNoSuchMethod() {
|
||||
_indexTestUnit('''
|
||||
abstract class A {
|
||||
m1();
|
||||
int m2();
|
||||
}
|
||||
|
||||
class B extends A {
|
||||
existing() {}
|
||||
}
|
||||
''');
|
||||
assertHasFix(FixKind.CREATE_NO_SUCH_METHOD, '''
|
||||
abstract class A {
|
||||
m1();
|
||||
int m2();
|
||||
}
|
||||
|
||||
class B extends A {
|
||||
existing() {}
|
||||
|
||||
noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
|
||||
}
|
||||
''');
|
||||
}
|
||||
|
||||
void test_creationFunction_forFunctionType_cascadeSecond() {
|
||||
_indexTestUnit('''
|
||||
class A {
|
||||
B ma() => null;
|
||||
}
|
||||
class B {
|
||||
useFunction(int g(double a, String b)) {}
|
||||
}
|
||||
|
||||
main() {
|
||||
A a = new A();
|
||||
a..ma().useFunction(test);
|
||||
}
|
||||
''');
|
||||
assertHasFix(FixKind.CREATE_FUNCTION, '''
|
||||
class A {
|
||||
B ma() => null;
|
||||
}
|
||||
class B {
|
||||
useFunction(int g(double a, String b)) {}
|
||||
}
|
||||
|
||||
main() {
|
||||
A a = new A();
|
||||
a..ma().useFunction(test);
|
||||
}
|
||||
|
||||
int test(double a, String b) {
|
||||
}
|
||||
''');
|
||||
}
|
||||
|
||||
void test_creationFunction_forFunctionType_dynamicArgument() {
|
||||
_indexTestUnit('''
|
||||
main() {
|
||||
useFunction(test);
|
||||
}
|
||||
useFunction(int g(a, b)) {}
|
||||
''');
|
||||
assertHasFix(FixKind.CREATE_FUNCTION, '''
|
||||
main() {
|
||||
useFunction(test);
|
||||
}
|
||||
useFunction(int g(a, b)) {}
|
||||
|
||||
int test(a, b) {
|
||||
}
|
||||
''');
|
||||
}
|
||||
|
||||
void test_creationFunction_forFunctionType_function() {
|
||||
_indexTestUnit('''
|
||||
main() {
|
||||
useFunction(test);
|
||||
}
|
||||
useFunction(int g(double a, String b)) {}
|
||||
''');
|
||||
assertHasFix(FixKind.CREATE_FUNCTION, '''
|
||||
main() {
|
||||
useFunction(test);
|
||||
}
|
||||
useFunction(int g(double a, String b)) {}
|
||||
|
||||
int test(double a, String b) {
|
||||
}
|
||||
''');
|
||||
}
|
||||
|
||||
void test_creationFunction_forFunctionType_method_enclosingClass_static() {
|
||||
_indexTestUnit('''
|
||||
class A {
|
||||
static foo() {
|
||||
useFunction(test);
|
||||
}
|
||||
}
|
||||
useFunction(int g(double a, String b)) {}
|
||||
''');
|
||||
assertHasFix(FixKind.CREATE_METHOD, '''
|
||||
class A {
|
||||
static foo() {
|
||||
useFunction(test);
|
||||
}
|
||||
|
||||
static int test(double a, String b) {
|
||||
}
|
||||
}
|
||||
useFunction(int g(double a, String b)) {}
|
||||
''');
|
||||
}
|
||||
|
||||
void test_creationFunction_forFunctionType_method_enclosingClass_static2() {
|
||||
_indexTestUnit('''
|
||||
class A {
|
||||
var f;
|
||||
A() : f = useFunction(test);
|
||||
}
|
||||
useFunction(int g(double a, String b)) {}
|
||||
''');
|
||||
assertHasFix(FixKind.CREATE_METHOD, '''
|
||||
class A {
|
||||
var f;
|
||||
A() : f = useFunction(test);
|
||||
|
||||
static int test(double a, String b) {
|
||||
}
|
||||
}
|
||||
useFunction(int g(double a, String b)) {}
|
||||
''');
|
||||
}
|
||||
|
||||
void test_creationFunction_forFunctionType_method_targetClass() {
|
||||
_indexTestUnit('''
|
||||
main(A a) {
|
||||
useFunction(a.test);
|
||||
}
|
||||
class A {
|
||||
}
|
||||
useFunction(int g(double a, String b)) {}
|
||||
''');
|
||||
assertHasFix(FixKind.CREATE_METHOD, '''
|
||||
main(A a) {
|
||||
useFunction(a.test);
|
||||
}
|
||||
class A {
|
||||
int test(double a, String b) {
|
||||
}
|
||||
}
|
||||
useFunction(int g(double a, String b)) {}
|
||||
''');
|
||||
}
|
||||
|
||||
void
|
||||
test_creationFunction_forFunctionType_method_targetClass_hasOtherMember() {
|
||||
_indexTestUnit('''
|
||||
main(A a) {
|
||||
useFunction(a.test);
|
||||
}
|
||||
class A {
|
||||
m() {}
|
||||
}
|
||||
useFunction(int g(double a, String b)) {}
|
||||
''');
|
||||
assertHasFix(FixKind.CREATE_METHOD, '''
|
||||
main(A a) {
|
||||
useFunction(a.test);
|
||||
}
|
||||
class A {
|
||||
m() {}
|
||||
|
||||
int test(double a, String b) {
|
||||
}
|
||||
}
|
||||
useFunction(int g(double a, String b)) {}
|
||||
''');
|
||||
}
|
||||
|
||||
void test_creationFunction_forFunctionType_notFunctionType() {
|
||||
_indexTestUnit('''
|
||||
main(A a) {
|
||||
useFunction(a.test);
|
||||
}
|
||||
typedef A();
|
||||
useFunction(g) {}
|
||||
''');
|
||||
assertNoFix(FixKind.CREATE_METHOD);
|
||||
assertNoFix(FixKind.CREATE_FUNCTION);
|
||||
}
|
||||
|
||||
void test_creationFunction_forFunctionType_unknownTarget() {
|
||||
_indexTestUnit('''
|
||||
main(A a) {
|
||||
useFunction(a.test);
|
||||
}
|
||||
class A {
|
||||
}
|
||||
useFunction(g) {}
|
||||
''');
|
||||
assertNoFix(FixKind.CREATE_METHOD);
|
||||
}
|
||||
|
||||
void test_expectedToken_semicolon() {
|
||||
_indexTestUnit('''
|
||||
main() {
|
||||
@@ -465,6 +1002,56 @@ const a = const A();
|
||||
''');
|
||||
}
|
||||
|
||||
void test_undefinedMethod_createQualified_fromClass() {
|
||||
_indexTestUnit('''
|
||||
class A {
|
||||
}
|
||||
main() {
|
||||
A.myUndefinedMethod();
|
||||
}
|
||||
''');
|
||||
assertHasFix(FixKind.CREATE_METHOD, '''
|
||||
class A {
|
||||
static void myUndefinedMethod() {
|
||||
}
|
||||
}
|
||||
main() {
|
||||
A.myUndefinedMethod();
|
||||
}
|
||||
''');
|
||||
}
|
||||
|
||||
void test_undefinedMethod_createQualified_fromClass_hasOtherMember() {
|
||||
_indexTestUnit('''
|
||||
class A {
|
||||
foo() {}
|
||||
}
|
||||
main() {
|
||||
A.myUndefinedMethod();
|
||||
}
|
||||
''');
|
||||
assertHasFix(FixKind.CREATE_METHOD, '''
|
||||
class A {
|
||||
foo() {}
|
||||
|
||||
static void myUndefinedMethod() {
|
||||
}
|
||||
}
|
||||
main() {
|
||||
A.myUndefinedMethod();
|
||||
}
|
||||
''');
|
||||
}
|
||||
|
||||
void test_undefinedMethod_createQualified_fromClass_unresolved() {
|
||||
_indexTestUnit('''
|
||||
main() {
|
||||
NoSuchClass.myUndefinedMethod();
|
||||
}
|
||||
''');
|
||||
assertNoFix(FixKind.CREATE_METHOD);
|
||||
}
|
||||
|
||||
void test_useEffectiveIntegerDivision() {
|
||||
_indexTestUnit('''
|
||||
main() {
|
||||
@@ -496,7 +1083,7 @@ main() {
|
||||
* Computes fixes and verifies that there is a fix of the given kind.
|
||||
*/
|
||||
Fix _assertHasFix(FixKind kind, AnalysisError error) {
|
||||
List<Fix> fixes = computeFixes(searchEngine, testFile, testUnit, error);
|
||||
List<Fix> fixes = computeFixes(searchEngine, testUnit, error);
|
||||
for (Fix fix in fixes) {
|
||||
if (fix.kind == kind) {
|
||||
return fix;
|
||||
|
||||
Reference in New Issue
Block a user