Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(120)

Unified Diff: pkg/analysis_services/lib/src/correction/fix.dart

Issue 418203002: Implement more fixes. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: pkg/analysis_services/lib/src/correction/fix.dart
diff --git a/pkg/analysis_services/lib/src/correction/fix.dart b/pkg/analysis_services/lib/src/correction/fix.dart
index fe24a149c11e9aefea5e54efb1de33a92b2bc858..0c2fa0a091ef7dc81b77bd49848e170695e39b13 100644
--- a/pkg/analysis_services/lib/src/correction/fix.dart
+++ b/pkg/analysis_services/lib/src/correction/fix.dart
@@ -13,12 +13,14 @@ import 'package:analysis_services/search/search_engine.dart';
import 'package:analysis_services/src/correction/name_suggestion.dart';
import 'package:analysis_services/src/correction/source_buffer.dart';
import 'package:analysis_services/src/correction/source_range.dart' as rf;
+import 'package:analysis_services/src/correction/strings.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/error.dart';
import 'package:analyzer/src/generated/java_core.dart';
import 'package:analyzer/src/generated/parser.dart';
+import 'package:analyzer/src/generated/scanner.dart';
import 'package:analyzer/src/generated/source.dart';
import 'package:analyzer/src/generated/utilities_dart.dart';
@@ -28,13 +30,17 @@ import 'package:analyzer/src/generated/utilities_dart.dart';
*/
class FixProcessor {
final SearchEngine searchEngine;
+ final Source source;
final String file;
final CompilationUnit unit;
final AnalysisError error;
+ CompilationUnitElement unitElement;
+ LibraryElement unitLibraryElement;
final List<Edit> edits = <Edit>[];
final Map<String, LinkedPositionGroup> linkedPositionGroups = <String,
LinkedPositionGroup>{};
+ Position endPosition = null;
final List<Fix> fixes = <Fix>[];
CorrectionUtils utils;
@@ -45,7 +51,13 @@ class FixProcessor {
AstNode coveredNode;
- FixProcessor(this.searchEngine, this.file, this.unit, this.error);
+ FixProcessor(this.searchEngine, this.source, this.file, this.unit, this.error)
+ {
+ unitElement = unit.element;
+ unitLibraryElement = unitElement.library;
+ }
+
+ DartType get coreTypeBool => _getCoreType("bool");
List<Fix> compute() {
utils = new CorrectionUtils(unit);
@@ -69,11 +81,10 @@ class FixProcessor {
CompileTimeErrorCode.NO_DEFAULT_SUPER_CONSTRUCTOR_EXPLICIT) {
_addFix_createConstructorSuperExplicit();
}
-// if (identical(
-// errorCode,
-// CompileTimeErrorCode.NO_DEFAULT_SUPER_CONSTRUCTOR_IMPLICIT)) {
-// _addFix_createConstructorSuperImplicit();
-// }
+ if (errorCode ==
+ CompileTimeErrorCode.NO_DEFAULT_SUPER_CONSTRUCTOR_IMPLICIT) {
+ _addFix_createConstructorSuperImplicit();
+ }
if (errorCode ==
CompileTimeErrorCode.UNDEFINED_CONSTRUCTOR_IN_INITIALIZER_DEFAULT) {
_addFix_createConstructorSuperExplicit();
@@ -139,11 +150,11 @@ class FixProcessor {
_addFix_createClass();
_addFix_undefinedClass_useSimilar();
}
-// if (identical(errorCode, StaticWarningCode.UNDEFINED_IDENTIFIER)) {
-// _addFix_createFunction_forFunctionType();
-// _addFix_importLibrary_withType();
-// _addFix_importLibrary_withTopLevelVariable();
-// }
+ if (errorCode == StaticWarningCode.UNDEFINED_IDENTIFIER) {
+ _addFix_createFunction_forFunctionType();
+ _addFix_importLibrary_withType();
+ _addFix_importLibrary_withTopLevelVariable();
+ }
if (errorCode == StaticTypeWarningCode.INSTANCE_ACCESS_TO_STATIC_MEMBER) {
_addFix_useStaticAccess_method();
_addFix_useStaticAccess_property();
@@ -156,15 +167,15 @@ class FixProcessor {
// _addFix_undefinedFunction_useSimilar();
// _addFix_undefinedFunction_create();
// }
-// if (identical(errorCode, StaticTypeWarningCode.UNDEFINED_GETTER)) {
-// _addFix_createFunction_forFunctionType();
-// }
-// if (identical(errorCode, HintCode.UNDEFINED_METHOD) ||
-// identical(errorCode, StaticTypeWarningCode.UNDEFINED_METHOD)) {
-// _addFix_undefinedMethod_useSimilar();
-// _addFix_undefinedMethod_create();
-// _addFix_undefinedFunction_create();
-// }
+ if (errorCode == StaticTypeWarningCode.UNDEFINED_GETTER) {
+ _addFix_createFunction_forFunctionType();
+ }
+ if (errorCode == HintCode.UNDEFINED_METHOD ||
+ errorCode == StaticTypeWarningCode.UNDEFINED_METHOD) {
+ _addFix_undefinedMethod_useSimilar();
+ _addFix_undefinedMethod_create();
+ _addFix_undefinedFunction_create();
+ }
// done
return fixes;
}
@@ -181,9 +192,14 @@ class FixProcessor {
change.add(fileEdit);
linkedPositionGroups.values.forEach(
(group) => change.addLinkedPositionGroup(group));
+ change.endPosition = endPosition;
// add Fix
Fix fix = new Fix(kind, change);
fixes.add(fix);
+ // clear
+ edits.clear();
+ linkedPositionGroups.clear();
+ endPosition = null;
}
void _addFix_addPackageDependency() {
@@ -208,6 +224,7 @@ class FixProcessor {
// }
}
+
void _addFix_boolInsteadOfBoolean() {
SourceRange range = rf.rangeError(error);
_addReplaceEdit(range, "bool");
@@ -250,7 +267,6 @@ class FixProcessor {
}
}
-
void _addFix_createConstructorSuperExplicit() {
ConstructorDeclaration targetConstructor =
node.parent as ConstructorDeclaration;
@@ -319,79 +335,76 @@ class FixProcessor {
}
void _addFix_createConstructorSuperImplicit() {
- // TODO(scheglov) implement
-// ClassDeclaration targetClassNode = node.parent as ClassDeclaration;
-// ClassElement targetClassElement = targetClassNode.element;
-// ClassElement superClassElement = targetClassElement.supertype.element;
-// String targetClassName = targetClassElement.name;
-// // add proposals for all super constructors
-// List<ConstructorElement> superConstructors = superClassElement.constructors;
-// for (ConstructorElement superConstructor in superConstructors) {
-// String constructorName = superConstructor.name;
-// // skip private
-// if (Identifier.isPrivateName(constructorName)) {
-// continue;
-// }
-// // prepare parameters and arguments
-// JavaStringBuilder parametersBuffer = new JavaStringBuilder();
-// JavaStringBuilder argumentsBuffer = new JavaStringBuilder();
-// bool firstParameter = true;
-// for (ParameterElement parameter in superConstructor.parameters) {
-// // skip non-required parameters
-// if (parameter.parameterKind != ParameterKind.REQUIRED) {
-// break;
-// }
-// // comma
-// if (firstParameter) {
-// firstParameter = false;
-// } else {
-// parametersBuffer.append(", ");
-// argumentsBuffer.append(", ");
-// }
-// // name
-// String parameterName = parameter.displayName;
-// if (parameterName.length > 1 && parameterName.startsWith("_")) {
-// parameterName = parameterName.substring(1);
-// }
-// // parameter & argument
-// _appendParameterSource(parametersBuffer, parameter.type, parameterName);
-// argumentsBuffer.append(parameterName);
-// }
-// // add proposal
-// String eol = utils.endOfLine;
-// QuickFixProcessorImpl_NewConstructorLocation targetLocation =
-// _prepareNewConstructorLocation(targetClassNode, eol);
-// SourceBuilder sb = new SourceBuilder.con1(targetLocation._offset);
-// {
-// String indent = utils.getIndent(1);
-// sb.append(targetLocation._prefix);
-// sb.append(indent);
-// sb.append(targetClassName);
-// if (!constructorName.isEmpty) {
-// sb.startPosition("NAME");
-// sb.append(".");
-// sb.append(constructorName);
-// sb.endPosition();
-// }
-// sb.append("(");
-// sb.append(parametersBuffer.toString());
-// sb.append(") : super");
-// if (!constructorName.isEmpty) {
-// sb.append(".");
-// sb.append(constructorName);
-// }
-// sb.append("(");
-// sb.append(argumentsBuffer.toString());
-// sb.append(");");
-// sb.append(targetLocation._suffix);
-// }
-// _addInsertEdit3(sb);
-// // add proposal
-// String proposalName = _getConstructorProposalName(superConstructor);
-// _addFix(
-// FixKind.CREATE_CONSTRUCTOR_SUPER,
-// [proposalName]);
-// }
+ ClassDeclaration targetClassNode = node.parent as ClassDeclaration;
+ ClassElement targetClassElement = targetClassNode.element;
+ ClassElement superClassElement = targetClassElement.supertype.element;
+ String targetClassName = targetClassElement.name;
+ // add proposals for all super constructors
+ List<ConstructorElement> superConstructors = superClassElement.constructors;
+ for (ConstructorElement superConstructor in superConstructors) {
+ String constructorName = superConstructor.name;
+ // skip private
+ if (Identifier.isPrivateName(constructorName)) {
+ continue;
+ }
+ // prepare parameters and arguments
+ StringBuffer parametersBuffer = new StringBuffer();
+ StringBuffer argumentsBuffer = new StringBuffer();
+ bool firstParameter = true;
+ for (ParameterElement parameter in superConstructor.parameters) {
+ // skip non-required parameters
+ if (parameter.parameterKind != ParameterKind.REQUIRED) {
+ break;
+ }
+ // comma
+ if (firstParameter) {
+ firstParameter = false;
+ } else {
+ parametersBuffer.write(', ');
+ argumentsBuffer.write(', ');
+ }
+ // name
+ String parameterName = parameter.displayName;
+ if (parameterName.length > 1 && parameterName.startsWith('_')) {
+ parameterName = parameterName.substring(1);
+ }
+ // parameter & argument
+ _appendParameterSource(parametersBuffer, parameter.type, parameterName);
+ argumentsBuffer.write(parameterName);
+ }
+ // add proposal
+ String eol = utils.endOfLine;
Paul Berry 2014/07/25 00:28:46 With my suggestion about eol below, this line coul
scheglov 2014/07/25 03:20:50 Done.
+ QuickFixProcessorImpl_NewConstructorLocation targetLocation =
+ _prepareNewConstructorLocation(targetClassNode, eol);
+ SourceBuilder sb = new SourceBuilder(file, targetLocation._offset);
+ {
+ String indent = utils.getIndent(1);
+ sb.append(targetLocation._prefix);
+ sb.append(indent);
+ sb.append(targetClassName);
+ if (!constructorName.isEmpty) {
+ sb.startPosition('NAME');
Paul Berry 2014/07/25 00:28:45 Are the strings passed to startPosition things tha
scheglov 2014/07/25 03:20:51 These strings are IDs of linked position groups. T
+ sb.append('.');
+ sb.append(constructorName);
+ sb.endPosition();
+ }
+ sb.append("(");
+ sb.append(parametersBuffer.toString());
+ sb.append(') : super');
+ if (!constructorName.isEmpty) {
+ sb.append('.');
+ sb.append(constructorName);
+ }
+ sb.append('(');
+ sb.append(argumentsBuffer.toString());
+ sb.append(');');
+ sb.append(targetLocation._suffix);
+ }
+ _insertBuilder(sb);
+ // add proposal
+ String proposalName = _getConstructorProposalName(superConstructor);
+ _addFix(FixKind.CREATE_CONSTRUCTOR_SUPER, [proposalName]);
+ }
}
void _addFix_createConstructor_insteadOfSyntheticDefault() {
@@ -527,171 +540,167 @@ class FixProcessor {
}
void _addFix_createFunction_forFunctionType() {
- // TODO(scheglov) implement
-// if (node is SimpleIdentifier) {
-// SimpleIdentifier nameNode = node as SimpleIdentifier;
-// // prepare argument expression (to get parameter)
-// ClassElement targetElement;
-// Expression argument;
-// {
-// Expression target = CorrectionUtils.getQualifiedPropertyTarget(node);
-// if (target != null) {
-// DartType targetType = target.bestType;
-// if (targetType != null && targetType.element is ClassElement) {
-// targetElement = targetType.element as ClassElement;
-// argument = target.parent as Expression;
-// } else {
-// return;
-// }
-// } else {
-// ClassDeclaration enclosingClass =
-// node.getAncestor((node) => node is ClassDeclaration);
-// targetElement = enclosingClass != null ?
-// enclosingClass.element :
-// null;
-// argument = nameNode;
-// }
-// }
-// // should be argument of some invocation
-// ParameterElement parameterElement = argument.bestParameterElement;
-// if (parameterElement == null) {
-// return;
-// }
-// // should be parameter of function type
-// DartType parameterType = parameterElement.type;
-// if (parameterType is! FunctionType) {
-// return;
-// }
-// FunctionType functionType = parameterType as FunctionType;
-// // add proposal
-// if (targetElement != null) {
-// _addProposal_createFunction_method(targetElement, functionType);
-// } else {
-// _addProposal_createFunction_function(functionType);
-// }
-// }
+ if (node is SimpleIdentifier) {
+ SimpleIdentifier nameNode = node as SimpleIdentifier;
+ // prepare argument expression (to get parameter)
+ ClassElement targetElement;
+ Expression argument;
+ {
+ Expression target = getQualifiedPropertyTarget(node);
+ if (target != null) {
+ DartType targetType = target.bestType;
+ if (targetType != null && targetType.element is ClassElement) {
+ targetElement = targetType.element as ClassElement;
+ argument = target.parent as Expression;
+ } else {
+ return;
+ }
+ } else {
+ ClassDeclaration enclosingClass =
+ node.getAncestor((node) => node is ClassDeclaration);
+ targetElement = enclosingClass != null ?
+ enclosingClass.element :
+ null;
+ argument = nameNode;
+ }
+ }
+ // should be argument of some invocation
+ ParameterElement parameterElement = argument.bestParameterElement;
+ if (parameterElement == null) {
+ return;
+ }
+ // should be parameter of function type
+ DartType parameterType = parameterElement.type;
+ if (parameterType is! FunctionType) {
+ return;
+ }
+ FunctionType functionType = parameterType as FunctionType;
+ // add proposal
+ if (targetElement != null) {
+ _addProposal_createFunction_method(targetElement, functionType);
+ } else {
+ _addProposal_createFunction_function(functionType);
+ }
+ }
}
void
_addFix_createMissingOverrides(List<ExecutableElement> missingOverrides) {
- // TODO(scheglov) implement
-// // sort by name
-// missingOverrides.sort(
-// (Element firstElement, Element secondElement) =>
-// ObjectUtils.compare(firstElement.displayName, secondElement.displayName));
-// // add elements
-// ClassDeclaration targetClass = node.parent as ClassDeclaration;
-// bool isFirst = true;
-// for (ExecutableElement missingOverride in missingOverrides) {
-// _addFix_createMissingOverrides_single(
-// targetClass,
-// missingOverride,
-// isFirst);
-// isFirst = false;
-// }
-// // add proposal
-// _addFix(
-// FixKind.CREATE_MISSING_OVERRIDES,
-// [missingOverrides.length]);
+ // sort by name
+ missingOverrides.sort((Element firstElement, Element secondElement) {
+ return compareStrings(
+ firstElement.displayName,
+ secondElement.displayName);
+ });
+ // add elements
+ ClassDeclaration targetClass = node.parent as ClassDeclaration;
+ bool isFirst = true;
+ for (ExecutableElement missingOverride in missingOverrides) {
+ _addFix_createMissingOverrides_single(
+ targetClass,
+ missingOverride,
+ isFirst);
+ isFirst = false;
+ }
+ // add proposal
+ _addFix(FixKind.CREATE_MISSING_OVERRIDES, [missingOverrides.length]);
}
void _addFix_createMissingOverrides_single(ClassDeclaration targetClass,
ExecutableElement missingOverride, bool isFirst) {
- // TODO(scheglov) implement
-// // prepare environment
-// String eol = utils.endOfLine;
-// String prefix = utils.getIndent(1);
-// String prefix2 = utils.getIndent(2);
-// int insertOffset = targetClass.end - 1;
-// // prepare source
-// JavaStringBuilder sb = new JavaStringBuilder();
-// // may be empty line
-// if (!isFirst || !targetClass.members.isEmpty) {
-// sb.append(eol);
-// }
-// // may be property
-// ElementKind elementKind = missingOverride.kind;
-// bool isGetter = elementKind == ElementKind.GETTER;
-// bool isSetter = elementKind == ElementKind.SETTER;
-// bool isMethod = elementKind == ElementKind.METHOD;
-// bool isOperator = isMethod && (missingOverride as MethodElement).isOperator;
-// sb.append(prefix);
-// if (isGetter) {
-// sb.append("// TODO: implement ${missingOverride.displayName}");
-// sb.append(eol);
-// sb.append(prefix);
-// }
-// // @override
-// {
-// sb.append("@override");
-// sb.append(eol);
-// sb.append(prefix);
-// }
-// // return type
-// _appendType(sb, missingOverride.type.returnType);
-// if (isGetter) {
-// sb.append("get ");
-// } else if (isSetter) {
-// sb.append("set ");
-// } else if (isOperator) {
-// sb.append("operator ");
-// }
-// // name
-// sb.append(missingOverride.displayName);
-// // parameters + body
-// if (isGetter) {
-// sb.append(" => null;");
-// } else if (isMethod || isSetter) {
-// List<ParameterElement> parameters = missingOverride.parameters;
-// _appendParameters(sb, parameters);
-// sb.append(" {");
-// // TO-DO
-// sb.append(eol);
-// sb.append(prefix2);
-// if (isMethod) {
-// sb.append("// TODO: implement ${missingOverride.displayName}");
-// } else {
-// sb.append("// TODO: implement ${missingOverride.displayName}");
-// }
-// sb.append(eol);
-// // close method
-// sb.append(prefix);
-// sb.append("}");
-// }
-// sb.append(eol);
-// // done
-// _addInsertEdit(insertOffset, sb.toString());
-// // maybe set end range
-// if (_endRange == null) {
-// _endRange = SourceRangeFactory.rangeStartLength(insertOffset, 0);
-// }
+ // prepare environment
+ String eol = utils.endOfLine;
+ String prefix = utils.getIndent(1);
+ String prefix2 = utils.getIndent(2);
+ int insertOffset = targetClass.end - 1;
+ // prepare source
+ StringBuffer sb = new StringBuffer();
+ // may be empty line
+ if (!isFirst || !targetClass.members.isEmpty) {
+ sb.write(eol);
+ }
+ // may be property
+ ElementKind elementKind = missingOverride.kind;
+ bool isGetter = elementKind == ElementKind.GETTER;
+ bool isSetter = elementKind == ElementKind.SETTER;
+ bool isMethod = elementKind == ElementKind.METHOD;
+ bool isOperator = isMethod && (missingOverride as MethodElement).isOperator;
+ sb.write(prefix);
+ if (isGetter) {
+ sb.write('// TODO: implement ${missingOverride.displayName}');
+ sb.write(eol);
+ sb.write(prefix);
+ }
+ // @override
+ {
+ sb.write('@override');
+ sb.write(eol);
+ sb.write(prefix);
+ }
+ // return type
+ _appendType(sb, missingOverride.type.returnType);
+ if (isGetter) {
+ sb.write('get ');
+ } else if (isSetter) {
+ sb.write('set ');
+ } else if (isOperator) {
+ sb.write('operator ');
+ }
+ // name
+ sb.write(missingOverride.displayName);
+ // parameters + body
+ if (isGetter) {
+ sb.write(' => null;');
+ } else if (isMethod || isSetter) {
Paul Berry 2014/07/25 00:28:45 Any reason this couldn't just be "} else {"?
scheglov 2014/07/25 03:20:51 Done.
+ List<ParameterElement> parameters = missingOverride.parameters;
+ _appendParameters(sb, parameters, _getDefaultValueMap(parameters));
+ sb.write(' {');
+ // TO-DO
+ sb.write(eol);
+ sb.write(prefix2);
+ if (isMethod) {
Paul Berry 2014/07/25 00:28:45 This if test is unnecessary, since we do the same
scheglov 2014/07/25 03:20:51 Done.
+ sb.write('// TODO: implement ${missingOverride.displayName}');
+ } else {
+ sb.write('// TODO: implement ${missingOverride.displayName}');
+ }
+ sb.write(eol);
+ // close method
+ sb.write(prefix);
+ sb.write('}');
+ }
+ sb.write(eol);
+ // done
+ _addInsertEdit(insertOffset, sb.toString());
+ // maybe set end range
+ if (endPosition == null) {
+ endPosition = new Position(file, insertOffset, 0);
+ }
}
void _addFix_createNoSuchMethod() {
- // TODO(scheglov) implement
-// ClassDeclaration targetClass = node.parent as ClassDeclaration;
-// // prepare environment
-// String eol = utils.endOfLine;
-// String prefix = utils.getIndent(1);
-// int insertOffset = targetClass.end - 1;
-// // prepare source
-// SourceBuilder sb = new SourceBuilder.con1(insertOffset);
-// {
-// // insert empty line before existing member
-// if (!targetClass.members.isEmpty) {
-// sb.append(eol);
-// }
-// // append method
-// sb.append(prefix);
-// sb.append(
-// "noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);");
-// sb.append(eol);
-// }
-// // done
-// _addInsertEdit3(sb);
-// _endRange = SourceRangeFactory.rangeStartLength(insertOffset, 0);
-// // add proposal
-// _addFix(FixKind.CREATE_NO_SUCH_METHOD, []);
+ ClassDeclaration targetClass = node.parent as ClassDeclaration;
+ // prepare environment
+ String eol = utils.endOfLine;
+ String prefix = utils.getIndent(1);
+ int insertOffset = targetClass.end - 1;
+ // prepare source
+ SourceBuilder sb = new SourceBuilder(file, insertOffset);
+ {
+ // insert empty line before existing member
+ if (!targetClass.members.isEmpty) {
+ sb.append(eol);
+ }
+ // append method
+ sb.append(prefix);
+ sb.append(
+ "noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);");
+ sb.append(eol);
+ }
+ // done
+ _insertBuilder(sb);
+ endPosition = new Position(file, insertOffset, 0);
+ // add proposal
+ _addFix(FixKind.CREATE_NO_SUCH_METHOD, []);
}
void _addFix_createPart() {
@@ -727,6 +736,7 @@ class FixProcessor {
// }
}
+
void _addFix_importLibrary(FixKind kind, String importPath) {
// TODO(scheglov) implement
// CompilationUnitElement libraryUnitElement =
@@ -1151,97 +1161,93 @@ class FixProcessor {
}
void _addFix_undefinedMethod_create() {
- // TODO(scheglov) implement
-// if (node is SimpleIdentifier && node.parent is MethodInvocation) {
-// String name = (node as SimpleIdentifier).name;
-// MethodInvocation invocation = node.parent as MethodInvocation;
-// // prepare environment
-// String eol = utils.endOfLine;
-// Source targetSource;
-// String prefix;
-// int insertOffset;
-// String sourcePrefix;
-// String sourceSuffix;
-// bool staticModifier = false;
-// Expression target = invocation.realTarget;
-// if (target == null) {
-// targetSource = _source;
-// ClassMember enclosingMember =
-// node.getAncestor((node) => node is ClassMember);
-// staticModifier = _inStaticMemberContext2(enclosingMember);
-// prefix = utils.getNodePrefix(enclosingMember);
-// insertOffset = enclosingMember.end;
-// sourcePrefix = "${eol}${prefix}${eol}";
-// sourceSuffix = "";
-// } else {
-// // prepare target interface type
-// DartType targetType = target.bestType;
-// if (targetType is! InterfaceType) {
-// return;
-// }
-// ClassElement targetElement = targetType.element as ClassElement;
-// targetSource = targetElement.source;
-// // may be static
-// if (target is Identifier) {
-// staticModifier = target.bestElement.kind == ElementKind.CLASS;
-// }
-// // prepare insert offset
-// ClassDeclaration targetClass = targetElement.node;
-// prefix = " ";
-// insertOffset = targetClass.end - 1;
-// if (targetClass.members.isEmpty) {
-// sourcePrefix = "";
-// } else {
-// sourcePrefix = "${prefix}${eol}";
-// }
-// sourceSuffix = eol;
-// }
-// // build method source
-// SourceBuilder sb = new SourceBuilder.con1(insertOffset);
-// {
-// sb.append(sourcePrefix);
-// sb.append(prefix);
-// // may be "static"
-// if (staticModifier) {
-// sb.append("static ");
-// }
-// // may be return type
-// {
-// DartType type =
-// _addFix_undefinedMethod_create_getReturnType(invocation);
-// if (type != null) {
-// String typeSource = utils.getTypeSource2(type);
-// if (typeSource != "dynamic") {
-// sb.startPosition("RETURN_TYPE");
-// sb.append(typeSource);
-// sb.endPosition();
-// sb.append(" ");
-// }
-// }
-// }
-// // append name
-// {
-// sb.startPosition("NAME");
-// sb.append(name);
-// sb.endPosition();
-// }
-// _addFix_undefinedMethod_create_parameters(sb, invocation.argumentList);
-// sb.append(") {${eol}${prefix}}");
-// sb.append(sourceSuffix);
-// }
-// // insert source
-// _addInsertEdit(insertOffset, sb.toString());
-// // add linked positions
-// if (targetSource == _source) {
-// _addLinkedPosition("NAME", sb, SourceRangeFactory.rangeNode(node));
-// }
-// _addLinkedPositions(sb);
-// // add proposal
-// _addUnitCorrectionProposal2(
-// targetSource,
-// FixKind.CREATE_METHOD,
-// [name]);
-// }
+ if (node is SimpleIdentifier && node.parent is MethodInvocation) {
+ String name = (node as SimpleIdentifier).name;
+ MethodInvocation invocation = node.parent as MethodInvocation;
+ // prepare environment
+ String eol = utils.endOfLine;
+ Source targetSource;
+ String prefix;
+ int insertOffset;
+ String sourcePrefix;
+ String sourceSuffix;
+ bool staticModifier = false;
+ Expression target = invocation.realTarget;
+ if (target == null) {
+ targetSource = source;
+ ClassMember enclosingMember =
+ node.getAncestor((node) => node is ClassMember);
+ staticModifier = _inStaticMemberContext2(enclosingMember);
+ prefix = utils.getNodePrefix(enclosingMember);
+ insertOffset = enclosingMember.end;
+ sourcePrefix = "${eol}${prefix}${eol}";
+ sourceSuffix = "";
+ } else {
+ // prepare target interface type
+ DartType targetType = target.bestType;
+ if (targetType is! InterfaceType) {
+ return;
+ }
+ ClassElement targetElement = targetType.element as ClassElement;
+ targetSource = targetElement.source;
+ // may be static
+ if (target is Identifier) {
+ staticModifier = target.bestElement.kind == ElementKind.CLASS;
+ }
+ // prepare insert offset
+ ClassDeclaration targetClass = targetElement.node;
+ prefix = " ";
+ insertOffset = targetClass.end - 1;
+ if (targetClass.members.isEmpty) {
+ sourcePrefix = "";
+ } else {
+ sourcePrefix = "${prefix}${eol}";
+ }
+ sourceSuffix = eol;
+ }
+ String targetFile = targetSource.fullName;
+ // build method source
+ SourceBuilder sb = new SourceBuilder(targetFile, insertOffset);
+ {
+ sb.append(sourcePrefix);
+ sb.append(prefix);
+ // may be "static"
+ if (staticModifier) {
+ sb.append("static ");
+ }
+ // may be return type
+ {
+ DartType type =
+ _addFix_undefinedMethod_create_getReturnType(invocation);
+ if (type != null) {
+ String typeSource = utils.getTypeSource(type);
+ if (typeSource != "dynamic") {
Paul Berry 2014/07/25 00:28:46 It seems weird that we use a string comparison her
scheglov 2014/07/25 03:20:50 Done.
+ sb.startPosition("RETURN_TYPE");
+ sb.append(typeSource);
+ sb.endPosition();
+ sb.append(" ");
+ }
+ }
+ }
+ // append name
+ {
+ sb.startPosition("NAME");
+ sb.append(name);
+ sb.endPosition();
+ }
+ _addFix_undefinedMethod_create_parameters(sb, invocation.argumentList);
+ sb.append(") {${eol}${prefix}}");
+ sb.append(sourceSuffix);
+ }
+ // insert source
+ _insertBuilder(sb);
+ // add linked positions
+ if (targetSource == source) {
+ _addLinkedPosition3('NAME', sb, rf.rangeNode(node));
+ }
+ // add proposal
+ _addFix(FixKind.CREATE_METHOD, [name], fixFile: targetFile);
+ }
}
/**
@@ -1249,113 +1255,111 @@ class FixProcessor {
*/
DartType
_addFix_undefinedMethod_create_getReturnType(MethodInvocation invocation) {
- // TODO(scheglov) implement
-// AstNode parent = invocation.parent;
-// // myFunction();
-// if (parent is ExpressionStatement) {
-// return VoidTypeImpl.instance;
-// }
-// // return myFunction();
-// if (parent is ReturnStatement) {
-// ExecutableElement executable =
-// CorrectionUtils.getEnclosingExecutableElement(invocation);
-// return executable != null ? executable.returnType : null;
-// }
-// // int v = myFunction();
-// if (parent is VariableDeclaration) {
-// VariableDeclaration variableDeclaration = parent;
-// if (identical(variableDeclaration.initializer, invocation)) {
-// VariableElement variableElement = variableDeclaration.element;
-// if (variableElement != null) {
-// return variableElement.type;
-// }
-// }
-// }
-// // v = myFunction();
-// if (parent is AssignmentExpression) {
-// AssignmentExpression assignment = parent;
-// if (identical(assignment.rightHandSide, invocation)) {
-// if (assignment.operator.type == TokenType.EQ) {
-// // v = myFunction();
-// Expression lhs = assignment.leftHandSide;
-// if (lhs != null) {
-// return lhs.bestType;
-// }
-// } else {
-// // v += myFunction();
-// MethodElement method = assignment.bestElement;
-// if (method != null) {
-// List<ParameterElement> parameters = method.parameters;
-// if (parameters.length == 1) {
-// return parameters[0].type;
-// }
-// }
-// }
-// }
-// }
-// // v + myFunction();
-// if (parent is BinaryExpression) {
-// BinaryExpression binary = parent;
-// MethodElement method = binary.bestElement;
-// if (method != null) {
-// if (identical(binary.rightOperand, invocation)) {
-// List<ParameterElement> parameters = method.parameters;
-// return parameters.length == 1 ? parameters[0].type : null;
-// }
-// }
-// }
-// // foo( myFunction() );
-// if (parent is ArgumentList) {
-// ParameterElement parameter = invocation.bestParameterElement;
-// return parameter != null ? parameter.type : null;
-// }
-// // bool
-// {
-// // assert( myFunction() );
-// if (parent is AssertStatement) {
-// AssertStatement statement = parent;
-// if (identical(statement.condition, invocation)) {
-// return coreTypeBool;
-// }
-// }
-// // if ( myFunction() ) {}
-// if (parent is IfStatement) {
-// IfStatement statement = parent;
-// if (identical(statement.condition, invocation)) {
-// return coreTypeBool;
-// }
-// }
-// // while ( myFunction() ) {}
-// if (parent is WhileStatement) {
-// WhileStatement statement = parent;
-// if (identical(statement.condition, invocation)) {
-// return coreTypeBool;
-// }
-// }
-// // do {} while ( myFunction() );
-// if (parent is DoStatement) {
-// DoStatement statement = parent;
-// if (identical(statement.condition, invocation)) {
-// return coreTypeBool;
-// }
-// }
-// // !myFunction()
-// if (parent is PrefixExpression) {
-// PrefixExpression prefixExpression = parent;
-// if (prefixExpression.operator.type == TokenType.BANG) {
-// return coreTypeBool;
-// }
-// }
-// // binary expression '&&' or '||'
-// if (parent is BinaryExpression) {
-// BinaryExpression binaryExpression = parent;
-// TokenType operatorType = binaryExpression.operator.type;
-// if (operatorType == TokenType.AMPERSAND_AMPERSAND ||
-// operatorType == TokenType.BAR_BAR) {
-// return coreTypeBool;
-// }
-// }
-// }
+ AstNode parent = invocation.parent;
+ // myFunction();
+ if (parent is ExpressionStatement) {
+ return VoidTypeImpl.instance;
+ }
+ // return myFunction();
+ if (parent is ReturnStatement) {
+ ExecutableElement executable = getEnclosingExecutableElement(invocation);
+ return executable != null ? executable.returnType : null;
+ }
+ // int v = myFunction();
+ if (parent is VariableDeclaration) {
+ VariableDeclaration variableDeclaration = parent;
+ if (identical(variableDeclaration.initializer, invocation)) {
+ VariableElement variableElement = variableDeclaration.element;
+ if (variableElement != null) {
+ return variableElement.type;
+ }
+ }
+ }
+ // v = myFunction();
+ if (parent is AssignmentExpression) {
+ AssignmentExpression assignment = parent;
+ if (identical(assignment.rightHandSide, invocation)) {
+ if (assignment.operator.type == TokenType.EQ) {
+ // v = myFunction();
+ Expression lhs = assignment.leftHandSide;
+ if (lhs != null) {
+ return lhs.bestType;
+ }
+ } else {
+ // v += myFunction();
+ MethodElement method = assignment.bestElement;
+ if (method != null) {
+ List<ParameterElement> parameters = method.parameters;
+ if (parameters.length == 1) {
+ return parameters[0].type;
+ }
+ }
+ }
+ }
+ }
+ // v + myFunction();
+ if (parent is BinaryExpression) {
+ BinaryExpression binary = parent;
+ MethodElement method = binary.bestElement;
+ if (method != null) {
+ if (identical(binary.rightOperand, invocation)) {
+ List<ParameterElement> parameters = method.parameters;
+ return parameters.length == 1 ? parameters[0].type : null;
+ }
+ }
+ }
+ // foo( myFunction() );
+ if (parent is ArgumentList) {
+ ParameterElement parameter = invocation.bestParameterElement;
+ return parameter != null ? parameter.type : null;
+ }
+ // bool
+ {
+ // assert( myFunction() );
+ if (parent is AssertStatement) {
+ AssertStatement statement = parent;
+ if (identical(statement.condition, invocation)) {
+ return coreTypeBool;
+ }
+ }
+ // if ( myFunction() ) {}
+ if (parent is IfStatement) {
+ IfStatement statement = parent;
+ if (identical(statement.condition, invocation)) {
+ return coreTypeBool;
+ }
+ }
+ // while ( myFunction() ) {}
+ if (parent is WhileStatement) {
+ WhileStatement statement = parent;
+ if (identical(statement.condition, invocation)) {
+ return coreTypeBool;
+ }
+ }
+ // do {} while ( myFunction() );
+ if (parent is DoStatement) {
+ DoStatement statement = parent;
+ if (identical(statement.condition, invocation)) {
+ return coreTypeBool;
+ }
+ }
+ // !myFunction()
+ if (parent is PrefixExpression) {
+ PrefixExpression prefixExpression = parent;
+ if (prefixExpression.operator.type == TokenType.BANG) {
+ return coreTypeBool;
+ }
+ }
+ // binary expression '&&' or '||'
+ if (parent is BinaryExpression) {
+ BinaryExpression binaryExpression = parent;
+ TokenType operatorType = binaryExpression.operator.type;
+ if (operatorType == TokenType.AMPERSAND_AMPERSAND ||
+ operatorType == TokenType.BAR_BAR) {
+ return coreTypeBool;
+ }
+ }
+ }
// we don't know
return null;
}
@@ -1517,6 +1521,156 @@ class FixProcessor {
}
/**
+ * Adds a single linked position to [groupId].
+ */
+ void _addLinkedPosition3(String groupId, SourceBuilder sb,
+ SourceRange range) {
+ if (sb.offset < range.offset) {
+ int delta = sb.length;
+ range = range.getTranslated(delta);
+ }
+ _addLinkedPosition(groupId, range);
+ }
+
+ /**
+ * Prepares proposal for creating function corresponding to the given [FunctionType].
+ */
+ void _addProposal_createFunction(FunctionType functionType, String name,
Paul Berry 2014/07/25 00:28:46 It seems like there's a lot in common between this
+ Source targetSource, int insertOffset, bool isStatic, String eol, String prefix,
+ String sourcePrefix, String sourceSuffix) {
+ // build method source
+ String targetFile = targetSource.fullName;
+ SourceBuilder sb = new SourceBuilder(targetFile, insertOffset);
+ {
+ sb.append(sourcePrefix);
+ sb.append(prefix);
+ // may be static
+ if (isStatic) {
+ sb.append("static ");
+ }
+ // may be return type
+ {
+ DartType returnType = functionType.returnType;
+ if (returnType != null) {
+ String typeSource = utils.getTypeSource(returnType);
+ if (typeSource != "dynamic") {
Paul Berry 2014/07/25 00:28:46 Use returnType.isDynamic here too.
scheglov 2014/07/25 03:20:50 Done.
+ sb.startPosition("RETURN_TYPE");
+ sb.append(typeSource);
+ sb.endPosition();
+ sb.append(" ");
+ }
+ }
+ }
+ // append name
+ {
+ sb.startPosition("NAME");
+ sb.append(name);
+ sb.endPosition();
+ }
+ // append parameters
+ sb.append("(");
+ List<ParameterElement> parameters = functionType.parameters;
+ for (int i = 0; i < parameters.length; i++) {
+ ParameterElement parameter = parameters[i];
+ // append separator
+ if (i != 0) {
+ sb.append(", ");
+ }
+ // append type name
+ DartType type = parameter.type;
+ String typeSource = utils.getTypeSource(type);
+ {
Paul Berry 2014/07/25 00:28:45 Should we guard this with "if (!type.isDynamic)",
scheglov 2014/07/25 03:20:50 Done. I've also added a test.
+ sb.startPosition("TYPE${i}");
+ sb.append(typeSource);
+ _addSuperTypeProposals(sb, new Set(), type);
+ sb.endPosition();
+ }
+ sb.append(" ");
+ // append parameter name
+ {
+ sb.startPosition("ARG${i}");
+ sb.append(parameter.displayName);
+ sb.endPosition();
+ }
+ }
+ sb.append(")");
+ // close method
+ sb.append(" {${eol}${prefix}}");
Paul Berry 2014/07/25 00:28:46 Space before $eol seems unnecessary.
scheglov 2014/07/25 03:20:50 The space is before "{", which opens the body bloc
+ sb.append(sourceSuffix);
+ }
+ // insert source
+ _insertBuilder(sb);
+ // add linked positions
+ if (targetSource == source) {
+ _addLinkedPosition3("NAME", sb, rf.rangeNode(node));
+ }
+ }
+
+ /**
+ * Adds proposal for creating method corresponding to the given [FunctionType] in the given
+ * [ClassElement].
+ */
+ void _addProposal_createFunction_function(FunctionType functionType) {
+ String name = (node as SimpleIdentifier).name;
+ // prepare environment
+ String eol = utils.endOfLine;
+ int insertOffset = unit.end;
+ // prepare prefix
+ String prefix = "";
+ String sourcePrefix = "${eol}";
+ String sourceSuffix = eol;
+ _addProposal_createFunction(
+ functionType,
+ name,
+ source,
+ insertOffset,
+ false,
+ eol,
+ prefix,
+ sourcePrefix,
+ sourceSuffix);
+ // add proposal
+ _addFix(FixKind.CREATE_FUNCTION, [name], fixFile: file);
+ }
+
+ /**
+ * Adds proposal for creating method corresponding to the given [FunctionType] in the given
+ * [ClassElement].
+ */
+ void _addProposal_createFunction_method(ClassElement targetClassElement,
+ FunctionType functionType) {
+ String name = (node as SimpleIdentifier).name;
+ // prepare environment
+ String eol = utils.endOfLine;
Paul Berry 2014/07/25 00:28:45 Suggestion: rather than force the caller to look u
scheglov 2014/07/25 03:20:50 Done.
+ Source targetSource = targetClassElement.source;
+ String targetFile = targetSource.fullName;
+ // prepare insert offset
+ ClassDeclaration targetClassNode = targetClassElement.node;
+ int insertOffset = targetClassNode.end - 1;
+ // prepare prefix
+ String prefix = " ";
+ String sourcePrefix;
+ if (targetClassNode.members.isEmpty) {
+ sourcePrefix = "";
+ } else {
+ sourcePrefix = "${prefix}${eol}";
Paul Berry 2014/07/25 00:28:45 This will just insert a blank line containing whit
scheglov 2014/07/25 03:20:51 Done.
+ }
+ String sourceSuffix = eol;
+ _addProposal_createFunction(
+ functionType,
+ name,
+ targetSource,
+ insertOffset,
+ _inStaticMemberContext(),
+ eol,
+ prefix,
+ sourcePrefix,
+ sourceSuffix);
+ // add proposal
+ _addFix(FixKind.CREATE_METHOD, [name], fixFile: targetFile);
+ }
+
+ /**
* Adds a new [Edit] to [edits].
*/
void _addRemoveEdit(SourceRange range) {
@@ -1587,15 +1741,13 @@ class FixProcessor {
sb.write(")");
}
-// void _addLinkedPositionProposal(String group,
-// LinkedPositionProposal proposal) {
-// List<LinkedPositionProposal> nodeProposals = linkedPositionProposals[group];
-// if (nodeProposals == null) {
-// nodeProposals = <LinkedPositionProposal>[];
-// linkedPositionProposals[group] = nodeProposals;
-// }
-// nodeProposals.add(proposal);
-// }
+ void _appendType(StringBuffer sb, DartType type) {
+ if (type != null && !type.isDynamic) {
+ String typeSource = utils.getTypeSource(type);
+ sb.write(typeSource);
+ sb.write(' ');
+ }
+ }
/**
* @return the string to display as the name of the given constructor in a proposal name.
@@ -1616,6 +1768,78 @@ class FixProcessor {
}
/**
+ * Returns the [Type] with given name from the `dart:core` library.
+ */
+ DartType _getCoreType(String name) {
+ List<LibraryElement> libraries = unitLibraryElement.importedLibraries;
+ for (LibraryElement library in libraries) {
+ if (library.isDartCore) {
+ ClassElement classElement = library.getType(name);
+ if (classElement != null) {
+ return classElement.type;
+ }
+ return null;
+ }
+ }
+ return null;
+ }
+
+ Map<ParameterElement, String>
+ _getDefaultValueMap(List<ParameterElement> parameters) {
+ Map<ParameterElement, String> defaultSourceMap = {};
+ Map<Source, String> sourceContentMap = {};
+ for (ParameterElement parameter in parameters) {
+ SourceRange valueRange = parameter.defaultValueRange;
+ if (valueRange != null) {
+ Source source = parameter.source;
+ String sourceContent = sourceContentMap[source];
+ if (sourceContent == null) {
+ sourceContent = getSourceContent(parameter.context, source);
+ sourceContentMap[source] = sourceContent;
+ }
+ String valueSource =
+ sourceContent.substring(valueRange.offset, valueRange.end);
+ defaultSourceMap[parameter] = valueSource;
+ }
+ }
+ return defaultSourceMap;
+ }
+
+ /**
+ * Returns `true` if [node] if part of a static method or of a field
+ * initializer.
+ */
+ bool _inStaticMemberContext() {
Paul Berry 2014/07/25 00:28:45 This won't do the right thing if node is in a cons
scheglov 2014/07/25 03:20:50 Added the test and fixed.
+ ClassMember member = node.getAncestor((node) => node is ClassMember);
+ return _inStaticMemberContext2(member);
Paul Berry 2014/07/25 00:28:45 Rather than having two functions and forcing the c
scheglov 2014/07/25 03:20:51 Done.
+ }
+
+// void _addLinkedPositionProposal(String group,
+// LinkedPositionProposal proposal) {
+// List<LinkedPositionProposal> nodeProposals = linkedPositionProposals[group];
+// if (nodeProposals == null) {
+// nodeProposals = <LinkedPositionProposal>[];
+// linkedPositionProposals[group] = nodeProposals;
+// }
+// nodeProposals.add(proposal);
+// }
+
+ /**
+ * Returns `true` if the given [ClassMember] is a part of a static method or
+ * a field initializer.
+ */
+ bool _inStaticMemberContext2(ClassMember member) {
+ if (member is MethodDeclaration) {
+ return member.isStatic;
+ }
+ // field initializer cannot reference "this"
+ if (member is FieldDeclaration) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
* Inserts the given [SourceBuilder] at its offset.
*/
void _insertBuilder(SourceBuilder builder) {

Powered by Google App Engine
This is Rietveld 408576698