| 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
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..faf4f097ee4c8d4d2de8c078f9702f26a42768ca
|
| --- /dev/null
|
| +++ b/pkg/analysis_services/lib/src/correction/fix.dart
|
| @@ -0,0 +1,1736 @@
|
| +// 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.fix;
|
| +
|
| +import 'package:analysis_services/correction/change.dart';
|
| +import 'package:analysis_services/correction/fix.dart';
|
| +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/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/source.dart';
|
| +import 'package:analyzer/src/generated/utilities_dart.dart';
|
| +
|
| +
|
| +/**
|
| + * The computer for Dart fixes.
|
| + */
|
| +class FixProcessor {
|
| + final SearchEngine searchEngine;
|
| + final String file;
|
| + final CompilationUnit unit;
|
| + final AnalysisError error;
|
| +
|
| + final List<Edit> edits = <Edit>[];
|
| + final Map<String, PositionGroup> positionGroups = <String, PositionGroup>{};
|
| + final List<Fix> fixes = <Fix>[];
|
| +
|
| + CorrectionUtils utils;
|
| + int errorOffset;
|
| + int errorLength;
|
| + int errorEnd;
|
| + AstNode node;
|
| + AstNode coveredNode;
|
| +
|
| +
|
| + FixProcessor(this.searchEngine, this.file, this.unit, this.error);
|
| +
|
| + List<Fix> compute() {
|
| + utils = new CorrectionUtils(unit);
|
| + errorOffset = error.offset;
|
| + errorLength = error.length;
|
| + errorEnd = errorOffset + errorLength;
|
| + node = new NodeLocator.con1(errorOffset).searchWithin(unit);
|
| + coveredNode = new NodeLocator.con2(
|
| + errorOffset,
|
| + errorOffset + errorLength).searchWithin(unit);
|
| + // analyze ErrorCode
|
| + ErrorCode errorCode = error.errorCode;
|
| + if (errorCode == StaticWarningCode.UNDEFINED_CLASS_BOOLEAN) {
|
| + _addFix_boolInsteadOfBoolean();
|
| + }
|
| + if (errorCode ==
|
| + CompileTimeErrorCode.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE) {
|
| + _addFix_replaceWithConstInstanceCreation();
|
| + }
|
| + if (errorCode ==
|
| + CompileTimeErrorCode.NO_DEFAULT_SUPER_CONSTRUCTOR_EXPLICIT) {
|
| + _addFix_createConstructorSuperExplicit();
|
| + }
|
| +// if (identical(
|
| +// errorCode,
|
| +// CompileTimeErrorCode.NO_DEFAULT_SUPER_CONSTRUCTOR_IMPLICIT)) {
|
| +// _addFix_createConstructorSuperImplicit();
|
| +// }
|
| + if (errorCode ==
|
| + CompileTimeErrorCode.UNDEFINED_CONSTRUCTOR_IN_INITIALIZER_DEFAULT) {
|
| + _addFix_createConstructorSuperExplicit();
|
| + }
|
| +// if (identical(errorCode, CompileTimeErrorCode.URI_DOES_NOT_EXIST)) {
|
| +// _addFix_createPart();
|
| +// _addFix_addPackageDependency();
|
| +// }
|
| + if (errorCode == HintCode.DIVISION_OPTIMIZATION) {
|
| + _addFix_useEffectiveIntegerDivision();
|
| + }
|
| + if (errorCode == HintCode.TYPE_CHECK_IS_NOT_NULL) {
|
| + _addFix_isNotNull();
|
| + }
|
| + if (errorCode == HintCode.TYPE_CHECK_IS_NULL) {
|
| + _addFix_isNull();
|
| + }
|
| + if (errorCode == HintCode.UNNECESSARY_CAST) {
|
| + _addFix_removeUnnecessaryCast();
|
| + }
|
| + if (errorCode == HintCode.UNUSED_IMPORT) {
|
| + _addFix_removeUnusedImport();
|
| + }
|
| + if (errorCode == ParserErrorCode.EXPECTED_TOKEN) {
|
| + _addFix_insertSemicolon();
|
| + }
|
| + if (errorCode == ParserErrorCode.GETTER_WITH_PARAMETERS) {
|
| + _addFix_removeParameters_inGetterDeclaration();
|
| + }
|
| + if (errorCode == StaticWarningCode.CONCRETE_CLASS_WITH_ABSTRACT_MEMBER) {
|
| + _addFix_makeEnclosingClassAbstract();
|
| + }
|
| + if (errorCode == StaticWarningCode.EXTRA_POSITIONAL_ARGUMENTS) {
|
| + _addFix_createConstructor_insteadOfSyntheticDefault();
|
| + }
|
| + if (errorCode == StaticWarningCode.NEW_WITH_UNDEFINED_CONSTRUCTOR) {
|
| + _addFix_createConstructor_named();
|
| + }
|
| + if (errorCode ==
|
| + StaticWarningCode.NON_ABSTRACT_CLASS_INHERITS_ABSTRACT_MEMBER_ONE ||
|
| + errorCode ==
|
| + StaticWarningCode.NON_ABSTRACT_CLASS_INHERITS_ABSTRACT_MEMBER_TWO ||
|
| + errorCode ==
|
| + StaticWarningCode.NON_ABSTRACT_CLASS_INHERITS_ABSTRACT_MEMBER_THREE ||
|
| + errorCode ==
|
| + StaticWarningCode.NON_ABSTRACT_CLASS_INHERITS_ABSTRACT_MEMBER_FOUR ||
|
| + errorCode ==
|
| + StaticWarningCode.NON_ABSTRACT_CLASS_INHERITS_ABSTRACT_MEMBER_FIVE_PLUS) {
|
| + // make class abstract
|
| + _addFix_makeEnclosingClassAbstract();
|
| + // implement methods
|
| + AnalysisErrorWithProperties errorWithProperties =
|
| + error as AnalysisErrorWithProperties;
|
| + Object property =
|
| + errorWithProperties.getProperty(ErrorProperty.UNIMPLEMENTED_METHODS);
|
| + List<ExecutableElement> missingOverrides =
|
| + property as List<ExecutableElement>;
|
| + _addFix_createMissingOverrides(missingOverrides);
|
| + _addFix_createNoSuchMethod();
|
| + }
|
| + if (errorCode == StaticWarningCode.UNDEFINED_CLASS) {
|
| + _addFix_importLibrary_withType();
|
| + _addFix_createClass();
|
| + _addFix_undefinedClass_useSimilar();
|
| + }
|
| +// if (identical(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();
|
| + }
|
| + if (errorCode == StaticTypeWarningCode.INVOCATION_OF_NON_FUNCTION) {
|
| + _addFix_removeParentheses_inGetterInvocation();
|
| + }
|
| +// if (identical(errorCode, StaticTypeWarningCode.UNDEFINED_FUNCTION)) {
|
| +// _addFix_importLibrary_withFunction();
|
| +// _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();
|
| +// }
|
| + // done
|
| + return fixes;
|
| + }
|
| +
|
| + void _addFix(FixKind kind, List args, {String fixFile}) {
|
| + if (fixFile == null) {
|
| + fixFile = file;
|
| + }
|
| + FileEdit fileEdit = new FileEdit(file);
|
| + edits.forEach((edit) => fileEdit.add(edit));
|
| + // prepare Change
|
| + String message = JavaString.format(kind.message, args);
|
| + Change change = new Change(message);
|
| + change.add(fileEdit);
|
| + positionGroups.values.forEach((group) => change.addPositionGroup(group));
|
| + // add Fix
|
| + Fix fix = new Fix(kind, change);
|
| + fixes.add(fix);
|
| + }
|
| +
|
| + void _addFix_addPackageDependency() {
|
| + // TODO(scheglov) implement
|
| +// if (node is SimpleStringLiteral && node.parent is NamespaceDirective) {
|
| +// SimpleStringLiteral uriLiteral = node as SimpleStringLiteral;
|
| +// String uriString = uriLiteral.value;
|
| +// // we need package: import
|
| +// if (!uriString.startsWith("package:")) {
|
| +// return;
|
| +// }
|
| +// // prepare package name
|
| +// String packageName = StringUtils.removeStart(uriString, "package:");
|
| +// packageName = StringUtils.substringBefore(packageName, "/");
|
| +// // add proposal
|
| +// _proposals.add(
|
| +// new AddDependencyCorrectionProposal(
|
| +// _unitFile,
|
| +// packageName,
|
| +// FixKind.ADD_PACKAGE_DEPENDENCY,
|
| +// [packageName]));
|
| +// }
|
| + }
|
| +
|
| + void _addFix_boolInsteadOfBoolean() {
|
| + SourceRange range = rf.rangeError(error);
|
| + _addReplaceEdit(range, "bool");
|
| + _addFix(FixKind.REPLACE_BOOLEAN_WITH_BOOL, []);
|
| + }
|
| +
|
| +
|
| + void _addFix_createClass() {
|
| + if (_mayBeTypeIdentifier(node)) {
|
| + String name = (node as SimpleIdentifier).name;
|
| + // prepare environment
|
| + String eol = utils.endOfLine;
|
| + CompilationUnitMember enclosingMember =
|
| + node.getAncestor((node) => node is CompilationUnitMember);
|
| + int offset = enclosingMember.end;
|
| + String prefix = "";
|
| + // prepare source
|
| + SourceBuilder sb = new SourceBuilder(file, offset);
|
| + {
|
| + sb.append("${eol}${eol}");
|
| + sb.append(prefix);
|
| + // "class"
|
| + sb.append("class ");
|
| + // append name
|
| + {
|
| + sb.startPosition("NAME");
|
| + sb.append(name);
|
| + sb.endPosition();
|
| + }
|
| + // no members
|
| + sb.append(" {");
|
| + sb.append(eol);
|
| + sb.append("}");
|
| + }
|
| + // insert source
|
| + _insertBuilder(sb);
|
| + _addLinkedPosition("NAME", rf.rangeNode(node));
|
| + // add proposal
|
| + _addFix(FixKind.CREATE_CLASS, [name]);
|
| + }
|
| + }
|
| +
|
| +
|
| + void _addFix_createConstructorSuperExplicit() {
|
| + ConstructorDeclaration targetConstructor =
|
| + node.parent as ConstructorDeclaration;
|
| + ClassDeclaration targetClassNode =
|
| + targetConstructor.parent as ClassDeclaration;
|
| + ClassElement targetClassElement = targetClassNode.element;
|
| + ClassElement superClassElement = targetClassElement.supertype.element;
|
| + // 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 SourceBuilder
|
| + SourceBuilder sb;
|
| + {
|
| + List<ConstructorInitializer> initializers =
|
| + targetConstructor.initializers;
|
| + if (initializers.isEmpty) {
|
| + int insertOffset = targetConstructor.parameters.end;
|
| + sb = new SourceBuilder(file, insertOffset);
|
| + sb.append(" : ");
|
| + } else {
|
| + ConstructorInitializer lastInitializer =
|
| + initializers[initializers.length - 1];
|
| + int insertOffset = lastInitializer.end;
|
| + sb = new SourceBuilder(file, insertOffset);
|
| + sb.append(", ");
|
| + }
|
| + }
|
| + // add super constructor name
|
| + sb.append("super");
|
| + if (!StringUtils.isEmpty(constructorName)) {
|
| + sb.append(".");
|
| + sb.append(constructorName);
|
| + }
|
| + // add arguments
|
| + sb.append("(");
|
| + 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 {
|
| + sb.append(", ");
|
| + }
|
| + // default value
|
| + DartType parameterType = parameter.type;
|
| + sb.startPosition(parameter.name);
|
| + sb.append(getDefaultValueCode(parameterType));
|
| + sb.endPosition();
|
| + }
|
| + sb.append(")");
|
| + // insert proposal
|
| + _insertBuilder(sb);
|
| + // add proposal
|
| + String proposalName = _getConstructorProposalName(superConstructor);
|
| + _addFix(FixKind.ADD_SUPER_CONSTRUCTOR_INVOCATION, [proposalName]);
|
| + }
|
| + }
|
| +
|
| + 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]);
|
| +// }
|
| + }
|
| +
|
| + void _addFix_createConstructor_insteadOfSyntheticDefault() {
|
| + TypeName typeName = null;
|
| + ConstructorName constructorName = null;
|
| + InstanceCreationExpression instanceCreation = null;
|
| + if (node is SimpleIdentifier) {
|
| + if (node.parent is TypeName) {
|
| + typeName = node.parent as TypeName;
|
| + if (typeName.name == node && typeName.parent is ConstructorName) {
|
| + constructorName = typeName.parent as ConstructorName;
|
| + // should be synthetic default constructor
|
| + {
|
| + ConstructorElement constructorElement =
|
| + constructorName.staticElement;
|
| + if (constructorElement == null ||
|
| + !constructorElement.isDefaultConstructor ||
|
| + !constructorElement.isSynthetic) {
|
| + return;
|
| + }
|
| + }
|
| + // prepare InstanceCreationExpression
|
| + if (constructorName.parent is InstanceCreationExpression) {
|
| + instanceCreation = constructorName.parent as
|
| + InstanceCreationExpression;
|
| + if (instanceCreation.constructorName != constructorName) {
|
| + return;
|
| + }
|
| + }
|
| + }
|
| + }
|
| + }
|
| + // do we have enough information?
|
| + if (instanceCreation == null) {
|
| + return;
|
| + }
|
| + // prepare environment
|
| + String eol = utils.endOfLine;
|
| + // prepare target
|
| + DartType targetType = typeName.type;
|
| + if (targetType is! InterfaceType) {
|
| + return;
|
| + }
|
| + ClassElement targetElement = targetType.element as ClassElement;
|
| + String targetFile = targetElement.source.fullName;
|
| + ClassDeclaration targetClass = targetElement.node;
|
| + QuickFixProcessorImpl_NewConstructorLocation targetLocation =
|
| + _prepareNewConstructorLocation(targetClass, eol);
|
| + // build method source
|
| + SourceBuilder sb = new SourceBuilder(targetFile, targetLocation._offset);
|
| + {
|
| + String indent = " ";
|
| + sb.append(targetLocation._prefix);
|
| + sb.append(indent);
|
| + sb.append(targetElement.name);
|
| + _addFix_undefinedMethod_create_parameters(
|
| + sb,
|
| + instanceCreation.argumentList);
|
| + sb.append(") {${eol}${indent}}");
|
| + sb.append(targetLocation._suffix);
|
| + }
|
| + // insert source
|
| + _insertBuilder(sb);
|
| + // add proposal
|
| + _addFix(FixKind.CREATE_CONSTRUCTOR, [constructorName], fixFile: targetFile);
|
| + }
|
| +
|
| + void _addFix_createConstructor_named() {
|
| + SimpleIdentifier name = null;
|
| + ConstructorName constructorName = null;
|
| + InstanceCreationExpression instanceCreation = null;
|
| + if (node is SimpleIdentifier) {
|
| + // name
|
| + name = node as SimpleIdentifier;
|
| + if (name.parent is ConstructorName) {
|
| + constructorName = name.parent as ConstructorName;
|
| + if (constructorName.name == name) {
|
| + // Type.name
|
| + if (constructorName.parent is InstanceCreationExpression) {
|
| + instanceCreation = constructorName.parent as
|
| + InstanceCreationExpression;
|
| + // new Type.name()
|
| + if (instanceCreation.constructorName != constructorName) {
|
| + return;
|
| + }
|
| + }
|
| + }
|
| + }
|
| + }
|
| + // do we have enough information?
|
| + if (instanceCreation == null) {
|
| + return;
|
| + }
|
| + // prepare environment
|
| + String eol = utils.endOfLine;
|
| + // prepare target interface type
|
| + DartType targetType = constructorName.type.type;
|
| + if (targetType is! InterfaceType) {
|
| + return;
|
| + }
|
| + ClassElement targetElement = targetType.element as ClassElement;
|
| + String targetFile = targetElement.source.fullName;
|
| + ClassDeclaration targetClass = targetElement.node;
|
| + QuickFixProcessorImpl_NewConstructorLocation targetLocation =
|
| + _prepareNewConstructorLocation(targetClass, eol);
|
| + // build method source
|
| + SourceBuilder sb = new SourceBuilder(targetFile, targetLocation._offset);
|
| + {
|
| + String indent = " ";
|
| + sb.append(targetLocation._prefix);
|
| + sb.append(indent);
|
| + sb.append(targetElement.name);
|
| + sb.append(".");
|
| + // append name
|
| + {
|
| + sb.startPosition("NAME");
|
| + sb.append(name.name);
|
| + sb.endPosition();
|
| + }
|
| + _addFix_undefinedMethod_create_parameters(
|
| + sb,
|
| + instanceCreation.argumentList);
|
| + sb.append(") {${eol}${indent}}");
|
| + sb.append(targetLocation._suffix);
|
| + }
|
| + // insert source
|
| + _insertBuilder(sb);
|
| + if (targetFile == file) {
|
| + _addLinkedPosition("NAME", rf.rangeNode(name));
|
| + }
|
| + // add proposal
|
| + _addFix(FixKind.CREATE_CONSTRUCTOR, [constructorName], fixFile: targetFile);
|
| + }
|
| +
|
| + 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);
|
| +// }
|
| +// }
|
| + }
|
| +
|
| + 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]);
|
| + }
|
| +
|
| + 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);
|
| +// }
|
| + }
|
| +
|
| + 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, []);
|
| + }
|
| +
|
| + void _addFix_createPart() {
|
| + // TODO(scheglov) implement
|
| +// if (node is SimpleStringLiteral && node.parent is PartDirective) {
|
| +// SimpleStringLiteral uriLiteral = node as SimpleStringLiteral;
|
| +// String uriString = uriLiteral.value;
|
| +// // prepare referenced File
|
| +// JavaFile newFile;
|
| +// {
|
| +// Uri uri = parseUriWithException(uriString);
|
| +// if (uri.isAbsolute) {
|
| +// return;
|
| +// }
|
| +// newFile = new JavaFile.relative(_unitLibraryFolder, uriString);
|
| +// }
|
| +// if (!newFile.exists()) {
|
| +// // prepare new source
|
| +// String source;
|
| +// {
|
| +// String eol = utils.endOfLine;
|
| +// String libraryName = _unitLibraryElement.displayName;
|
| +// source = "part of ${libraryName};${eol}${eol}";
|
| +// }
|
| +// // add proposal
|
| +// _proposals.add(
|
| +// new CreateFileCorrectionProposal(
|
| +// newFile,
|
| +// source,
|
| +// FixKind.CREATE_PART,
|
| +// [uriString]));
|
| +// }
|
| +// }
|
| + }
|
| +
|
| + void _addFix_importLibrary(FixKind kind, String importPath) {
|
| + // TODO(scheglov) implement
|
| +// CompilationUnitElement libraryUnitElement =
|
| +// _unitLibraryElement.definingCompilationUnit;
|
| +// CompilationUnit libraryUnit = libraryUnitElement.node;
|
| +// // prepare new import location
|
| +// int offset = 0;
|
| +// String prefix;
|
| +// String suffix;
|
| +// {
|
| +// String eol = utils.endOfLine;
|
| +// // if no directives
|
| +// prefix = "";
|
| +// suffix = eol;
|
| +// CorrectionUtils libraryUtils = new CorrectionUtils(libraryUnit);
|
| +// // after last directive in library
|
| +// for (Directive directive in libraryUnit.directives) {
|
| +// if (directive is LibraryDirective || directive is ImportDirective) {
|
| +// offset = directive.end;
|
| +// prefix = eol;
|
| +// suffix = "";
|
| +// }
|
| +// }
|
| +// // if still beginning of file, skip shebang and line comments
|
| +// if (offset == 0) {
|
| +// CorrectionUtils_InsertDesc desc = libraryUtils.insertDescTop;
|
| +// offset = desc.offset;
|
| +// prefix = desc.prefix;
|
| +// suffix = "${desc.suffix}${eol}";
|
| +// }
|
| +// }
|
| +// // insert new import
|
| +// String importSource = "${prefix}import '${importPath}';${suffix}";
|
| +// _addInsertEdit(offset, importSource);
|
| +// // add proposal
|
| +// _addUnitCorrectionProposal2(libraryUnitElement.source, kind, [importPath]);
|
| + }
|
| +
|
| + void _addFix_importLibrary_withElement(String name, ElementKind kind) {
|
| + // TODO(scheglov) implement
|
| +// // ignore if private
|
| +// if (name.startsWith("_")) {
|
| +// return;
|
| +// }
|
| +// // may be there is an existing import, but it is with prefix and we don't use this prefix
|
| +// for (ImportElement imp in _unitLibraryElement.imports) {
|
| +// // prepare element
|
| +// LibraryElement libraryElement = imp.importedLibrary;
|
| +// Element element =
|
| +// CorrectionUtils.getExportedElement(libraryElement, name);
|
| +// if (element == null) {
|
| +// continue;
|
| +// }
|
| +// if (element is PropertyAccessorElement) {
|
| +// element = (element as PropertyAccessorElement).variable;
|
| +// }
|
| +// if (element.kind != kind) {
|
| +// continue;
|
| +// }
|
| +// // may be apply prefix
|
| +// PrefixElement prefix = imp.prefix;
|
| +// if (prefix != null) {
|
| +// SourceRange range = SourceRangeFactory.rangeStartLength(node, 0);
|
| +// _addReplaceEdit(range, "${prefix.displayName}.");
|
| +// _addFix(
|
| +// FixKind.IMPORT_LIBRARY_PREFIX,
|
| +// [libraryElement.displayName, prefix.displayName]);
|
| +// continue;
|
| +// }
|
| +// // may be update "show" directive
|
| +// List<NamespaceCombinator> combinators = imp.combinators;
|
| +// if (combinators.length == 1 && combinators[0] is ShowElementCombinator) {
|
| +// ShowElementCombinator showCombinator =
|
| +// combinators[0] as ShowElementCombinator;
|
| +// // prepare new set of names to show
|
| +// Set<String> showNames = new Set<String>();
|
| +// showNames.addAll(showCombinator.shownNames);
|
| +// showNames.add(name);
|
| +// // prepare library name - unit name or 'dart:name' for SDK library
|
| +// String libraryName = libraryElement.definingCompilationUnit.displayName;
|
| +// if (libraryElement.isInSdk) {
|
| +// libraryName = imp.uri;
|
| +// }
|
| +// // update library
|
| +// String newShowCode = "show ${StringUtils.join(showNames, ", ")}";
|
| +// // TODO(scheglov)
|
| +// _addReplaceEdit(
|
| +// SourceRangeFactory.rangeShowCombinator(showCombinator),
|
| +// newShowCode);
|
| +// _addUnitCorrectionProposal2(
|
| +// _unitLibraryElement.source,
|
| +// FixKind.IMPORT_LIBRARY_SHOW,
|
| +// [libraryName]);
|
| +// // we support only one import without prefix
|
| +// return;
|
| +// }
|
| +// }
|
| +// // check SDK libraries
|
| +// AnalysisContext context = _unitLibraryElement.context;
|
| +// {
|
| +// DartSdk sdk = context.sourceFactory.dartSdk;
|
| +// List<SdkLibrary> sdkLibraries = sdk.sdkLibraries;
|
| +// for (SdkLibrary sdkLibrary in sdkLibraries) {
|
| +// SourceFactory sdkSourceFactory = context.sourceFactory;
|
| +// String libraryUri = sdkLibrary.shortName;
|
| +// Source librarySource = sdkSourceFactory.resolveUri(null, libraryUri);
|
| +// // prepare LibraryElement
|
| +// LibraryElement libraryElement =
|
| +// context.getLibraryElement(librarySource);
|
| +// if (libraryElement == null) {
|
| +// continue;
|
| +// }
|
| +// // prepare exported Element
|
| +// Element element =
|
| +// CorrectionUtils.getExportedElement(libraryElement, name);
|
| +// if (element == null) {
|
| +// continue;
|
| +// }
|
| +// if (element is PropertyAccessorElement) {
|
| +// element = (element as PropertyAccessorElement).variable;
|
| +// }
|
| +// if (element.kind != kind) {
|
| +// continue;
|
| +// }
|
| +// // add import
|
| +// _addFix_importLibrary(FixKind.IMPORT_LIBRARY_SDK, libraryUri);
|
| +// }
|
| +// }
|
| +// // check project libraries
|
| +// {
|
| +// List<Source> librarySources = context.librarySources;
|
| +// for (Source librarySource in librarySources) {
|
| +// // we don't need SDK libraries here
|
| +// if (librarySource.isInSystemLibrary) {
|
| +// continue;
|
| +// }
|
| +// // prepare LibraryElement
|
| +// LibraryElement libraryElement =
|
| +// context.getLibraryElement(librarySource);
|
| +// if (libraryElement == null) {
|
| +// continue;
|
| +// }
|
| +// // prepare exported Element
|
| +// Element element =
|
| +// CorrectionUtils.getExportedElement(libraryElement, name);
|
| +// if (element == null) {
|
| +// continue;
|
| +// }
|
| +// if (element.kind != kind) {
|
| +// continue;
|
| +// }
|
| +// // prepare "library" file
|
| +// JavaFile libraryFile = getSourceFile(librarySource);
|
| +// if (libraryFile == null) {
|
| +// continue;
|
| +// }
|
| +// // may be "package:" URI
|
| +// {
|
| +// Uri libraryPackageUri = _findPackageUri(context, libraryFile);
|
| +// if (libraryPackageUri != null) {
|
| +// _addFix_importLibrary(
|
| +// FixKind.IMPORT_LIBRARY_PROJECT,
|
| +// libraryPackageUri.toString());
|
| +// continue;
|
| +// }
|
| +// }
|
| +// // relative URI
|
| +// String relative =
|
| +// URIUtils.computeRelativePath(
|
| +// _unitLibraryFolder.getAbsolutePath(),
|
| +// libraryFile.getAbsolutePath());
|
| +// _addFix_importLibrary(
|
| +// FixKind.IMPORT_LIBRARY_PROJECT,
|
| +// relative);
|
| +// }
|
| +// }
|
| + }
|
| +
|
| + void _addFix_importLibrary_withFunction() {
|
| + // TODO(scheglov) implement
|
| +// if (node is SimpleIdentifier && node.parent is MethodInvocation) {
|
| +// MethodInvocation invocation = node.parent as MethodInvocation;
|
| +// if (invocation.realTarget == null &&
|
| +// identical(invocation.methodName, node)) {
|
| +// String name = (node as SimpleIdentifier).name;
|
| +// _addFix_importLibrary_withElement(name, ElementKind.FUNCTION);
|
| +// }
|
| +// }
|
| + }
|
| +
|
| + void _addFix_importLibrary_withTopLevelVariable() {
|
| + // TODO(scheglov) implement
|
| +// if (node is SimpleIdentifier) {
|
| +// String name = (node as SimpleIdentifier).name;
|
| +// _addFix_importLibrary_withElement(name, ElementKind.TOP_LEVEL_VARIABLE);
|
| +// }
|
| + }
|
| +
|
| + void _addFix_importLibrary_withType() {
|
| + // TODO(scheglov) implement
|
| +// if (_mayBeTypeIdentifier(node)) {
|
| +// String typeName = (node as SimpleIdentifier).name;
|
| +// _addFix_importLibrary_withElement(typeName, ElementKind.CLASS);
|
| +// }
|
| + }
|
| +
|
| + void _addFix_insertSemicolon() {
|
| + if (error.message.contains("';'")) {
|
| + int insertOffset = error.offset + error.length;
|
| + _addInsertEdit(insertOffset, ";");
|
| + _addFix(FixKind.INSERT_SEMICOLON, []);
|
| + }
|
| + }
|
| +
|
| + void _addFix_isNotNull() {
|
| + if (coveredNode is IsExpression) {
|
| + IsExpression isExpression = coveredNode as IsExpression;
|
| + _addReplaceEdit(
|
| + rf.rangeEndEnd(isExpression.expression, isExpression),
|
| + " != null");
|
| + _addFix(FixKind.USE_NOT_EQ_NULL, []);
|
| + }
|
| + }
|
| +
|
| + void _addFix_isNull() {
|
| + if (coveredNode is IsExpression) {
|
| + IsExpression isExpression = coveredNode as IsExpression;
|
| + _addReplaceEdit(
|
| + rf.rangeEndEnd(isExpression.expression, isExpression),
|
| + " == null");
|
| + _addFix(FixKind.USE_EQ_EQ_NULL, []);
|
| + }
|
| + }
|
| +
|
| + void _addFix_makeEnclosingClassAbstract() {
|
| + ClassDeclaration enclosingClass =
|
| + node.getAncestor((node) => node is ClassDeclaration);
|
| + String className = enclosingClass.name.name;
|
| + _addInsertEdit(enclosingClass.classKeyword.offset, "abstract ");
|
| + _addFix(FixKind.MAKE_CLASS_ABSTRACT, [className]);
|
| + }
|
| +
|
| + void _addFix_removeParameters_inGetterDeclaration() {
|
| + if (node is SimpleIdentifier && node.parent is MethodDeclaration) {
|
| + MethodDeclaration method = node.parent as MethodDeclaration;
|
| + FunctionBody body = method.body;
|
| + if (method.name == node && body != null) {
|
| + _addReplaceEdit(rf.rangeEndStart(node, body), " ");
|
| + _addFix(FixKind.REMOVE_PARAMETERS_IN_GETTER_DECLARATION, []);
|
| + }
|
| + }
|
| + }
|
| +
|
| + void _addFix_removeParentheses_inGetterInvocation() {
|
| + if (node is SimpleIdentifier && node.parent is MethodInvocation) {
|
| + MethodInvocation invocation = node.parent as MethodInvocation;
|
| + if (invocation.methodName == node && invocation.target != null) {
|
| + _addRemoveEdit(rf.rangeEndEnd(node, invocation));
|
| + _addFix(FixKind.REMOVE_PARENTHESIS_IN_GETTER_INVOCATION, []);
|
| + }
|
| + }
|
| + }
|
| +
|
| + void _addFix_removeUnnecessaryCast() {
|
| + if (coveredNode is! AsExpression) {
|
| + return;
|
| + }
|
| + AsExpression asExpression = coveredNode as AsExpression;
|
| + Expression expression = asExpression.expression;
|
| + int expressionPrecedence = getExpressionPrecedence(expression);
|
| + // remove 'as T' from 'e as T'
|
| + _addRemoveEdit(rf.rangeEndEnd(expression, asExpression));
|
| + _removeEnclosingParentheses(asExpression, expressionPrecedence);
|
| + // done
|
| + _addFix(FixKind.REMOVE_UNNECASSARY_CAST, []);
|
| + }
|
| +
|
| + void _addFix_removeUnusedImport() {
|
| + // prepare ImportDirective
|
| + ImportDirective importDirective =
|
| + node.getAncestor((node) => node is ImportDirective);
|
| + if (importDirective == null) {
|
| + return;
|
| + }
|
| + // remove the whole line with import
|
| + _addRemoveEdit(utils.getLinesRange(rf.rangeNode(importDirective)));
|
| + // done
|
| + _addFix(FixKind.REMOVE_UNUSED_IMPORT, []);
|
| + }
|
| +
|
| + void _addFix_replaceWithConstInstanceCreation() {
|
| + if (coveredNode is InstanceCreationExpression) {
|
| + var instanceCreation = coveredNode as InstanceCreationExpression;
|
| + _addReplaceEdit(rf.rangeToken(instanceCreation.keyword), "const");
|
| + _addFix(FixKind.USE_CONST, []);
|
| + }
|
| + }
|
| +
|
| + void _addFix_undefinedClass_useSimilar() {
|
| + // TODO(scheglov) implement
|
| +// if (_mayBeTypeIdentifier(node)) {
|
| +// String name = (node as SimpleIdentifier).name;
|
| +// QuickFixProcessorImpl_ClosestElementFinder finder =
|
| +// new QuickFixProcessorImpl_ClosestElementFinder(
|
| +// name,
|
| +// new Predicate_QuickFixProcessorImpl_addFix_undefinedClass_useSimilar());
|
| +// // find closest element
|
| +// {
|
| +// // elements of this library
|
| +// _unitLibraryElement.accept(
|
| +// new RecursiveElementVisitor_QuickFixProcessorImpl_addFix_undefinedClass_useSimilar(
|
| +// finder));
|
| +// // elements from imports
|
| +// for (ImportElement importElement in _unitLibraryElement.imports) {
|
| +// if (importElement.prefix == null) {
|
| +// Map<String, Element> namespace =
|
| +// CorrectionUtils.getImportNamespace(importElement);
|
| +// finder._update2(namespace.values);
|
| +// }
|
| +// }
|
| +// }
|
| +// // if we have close enough element, suggest to use it
|
| +// if (finder != null && finder._distance < 5) {
|
| +// String closestName = finder._element.name;
|
| +// _addReplaceEdit(SourceRangeFactory.rangeNode(node), closestName);
|
| +// // add proposal
|
| +// if (closestName != null) {
|
| +// _addFix(
|
| +// FixKind.CHANGE_TO,
|
| +// [closestName]);
|
| +// }
|
| +// }
|
| +// }
|
| + }
|
| +
|
| + void _addFix_undefinedFunction_create() {
|
| + // TODO(scheglov) implement
|
| +// // should be the name of the invocation
|
| +// if (node is SimpleIdentifier && node.parent is MethodInvocation) {
|
| +// } else {
|
| +// return;
|
| +// }
|
| +// String name = (node as SimpleIdentifier).name;
|
| +// MethodInvocation invocation = node.parent as MethodInvocation;
|
| +// // function invocation has no target
|
| +// Expression target = invocation.realTarget;
|
| +// if (target != null) {
|
| +// return;
|
| +// }
|
| +// // prepare environment
|
| +// String eol = utils.endOfLine;
|
| +// int insertOffset;
|
| +// String sourcePrefix;
|
| +// AstNode enclosingMember =
|
| +// node.getAncestor((node) => node is CompilationUnitMember);
|
| +// insertOffset = enclosingMember.end;
|
| +// sourcePrefix = "${eol}${eol}";
|
| +// // build method source
|
| +// SourceBuilder sb = new SourceBuilder.con1(insertOffset);
|
| +// {
|
| +// sb.append(sourcePrefix);
|
| +// // 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}}");
|
| +// }
|
| +// // insert source
|
| +// _addInsertEdit(insertOffset, sb.toString());
|
| +// // add linked positions
|
| +// _addLinkedPosition("NAME", sb, SourceRangeFactory.rangeNode(node));
|
| +// _addLinkedPositions(sb);
|
| +// // add proposal
|
| +// _addFix(FixKind.CREATE_FUNCTION, [name]);
|
| + }
|
| +
|
| + void _addFix_undefinedFunction_useSimilar() {
|
| + // TODO(scheglov) implement
|
| +// if (node is SimpleIdentifier) {
|
| +// String name = (node as SimpleIdentifier).name;
|
| +// QuickFixProcessorImpl_ClosestElementFinder finder =
|
| +// new QuickFixProcessorImpl_ClosestElementFinder(
|
| +// name,
|
| +// new Predicate_QuickFixProcessorImpl_addFix_undefinedFunction_useSimilar());
|
| +// // this library
|
| +// _unitLibraryElement.accept(
|
| +// new RecursiveElementVisitor_QuickFixProcessorImpl_addFix_undefinedFunction_useSimilar(
|
| +// finder));
|
| +// // imports
|
| +// for (ImportElement importElement in _unitLibraryElement.imports) {
|
| +// if (importElement.prefix == null) {
|
| +// Map<String, Element> namespace =
|
| +// CorrectionUtils.getImportNamespace(importElement);
|
| +// finder._update2(namespace.values);
|
| +// }
|
| +// }
|
| +// // if we have close enough element, suggest to use it
|
| +// String closestName = null;
|
| +// if (finder != null && finder._distance < 5) {
|
| +// closestName = finder._element.name;
|
| +// _addReplaceEdit(SourceRangeFactory.rangeNode(node), closestName);
|
| +// _addFix(FixKind.CHANGE_TO, [closestName]);
|
| +// }
|
| +// }
|
| + }
|
| +
|
| + 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]);
|
| +// }
|
| + }
|
| +
|
| + /**
|
| + * @return the possible return [Type], may be <code>null</code> if can not be identified.
|
| + */
|
| + 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;
|
| +// }
|
| +// }
|
| +// }
|
| + // we don't know
|
| + return null;
|
| + }
|
| +
|
| + void _addFix_undefinedMethod_create_parameters(SourceBuilder sb,
|
| + ArgumentList argumentList) {
|
| + // append parameters
|
| + sb.append("(");
|
| + Set<String> excluded = new Set();
|
| + List<Expression> arguments = argumentList.arguments;
|
| + for (int i = 0; i < arguments.length; i++) {
|
| + Expression argument = arguments[i];
|
| + // append separator
|
| + if (i != 0) {
|
| + sb.append(", ");
|
| + }
|
| + // append type name
|
| + DartType type = argument.bestType;
|
| + String typeSource = utils.getTypeSource2(type);
|
| + {
|
| + sb.startPosition("TYPE${i}");
|
| + sb.append(typeSource);
|
| + _addSuperTypeProposals(sb, new Set(), type);
|
| + sb.endPosition();
|
| + }
|
| + sb.append(" ");
|
| + // append parameter name
|
| + {
|
| + List<String> suggestions =
|
| + _getArgumentNameSuggestions(excluded, type, argument, i);
|
| + String favorite = suggestions[0];
|
| + excluded.add(favorite);
|
| + sb.startPosition("ARG${i}");
|
| + sb.append(favorite);
|
| + sb.addProposals(suggestions);
|
| + sb.endPosition();
|
| + }
|
| + }
|
| + }
|
| +
|
| + void _addFix_undefinedMethod_useSimilar() {
|
| + // TODO(scheglov) implement
|
| +// if (node is SimpleIdentifier && node.parent is MethodInvocation) {
|
| +// MethodInvocation invocation = node.parent as MethodInvocation;
|
| +// String name = (node as SimpleIdentifier).name;
|
| +// QuickFixProcessorImpl_ClosestElementFinder finder =
|
| +// new QuickFixProcessorImpl_ClosestElementFinder(
|
| +// name,
|
| +// new Predicate_QuickFixProcessorImpl_addFix_undefinedMethod_useSimilar());
|
| +// // unqualified invocation
|
| +// Expression target = invocation.realTarget;
|
| +// if (target == null) {
|
| +// ClassDeclaration clazz =
|
| +// invocation.getAncestor((node) => node is ClassDeclaration);
|
| +// if (clazz != null) {
|
| +// ClassElement classElement = clazz.element;
|
| +// _updateFinderWithClassMembers(finder, classElement);
|
| +// }
|
| +// } else {
|
| +// DartType type = target.bestType;
|
| +// if (type is InterfaceType) {
|
| +// ClassElement classElement = type.element;
|
| +// _updateFinderWithClassMembers(finder, classElement);
|
| +// }
|
| +// }
|
| +// // if we have close enough element, suggest to use it
|
| +// String closestName = null;
|
| +// if (finder != null && finder._distance < 5) {
|
| +// closestName = finder._element.name;
|
| +// _addReplaceEdit(SourceRangeFactory.rangeNode(node), closestName);
|
| +// _addFix(FixKind.CHANGE_TO, [closestName]);
|
| +// }
|
| +// }
|
| + }
|
| +
|
| + void _addFix_useEffectiveIntegerDivision() {
|
| + for (AstNode n = node; n != null; n = n.parent) {
|
| + if (n is MethodInvocation &&
|
| + n.offset == errorOffset &&
|
| + n.length == errorLength) {
|
| + MethodInvocation invocation = n as MethodInvocation;
|
| + Expression target = invocation.target;
|
| + while (target is ParenthesizedExpression) {
|
| + target = (target as ParenthesizedExpression).expression;
|
| + }
|
| + // replace "/" with "~/"
|
| + BinaryExpression binary = target as BinaryExpression;
|
| + _addReplaceEdit(rf.rangeToken(binary.operator), "~/");
|
| + // remove everything before and after
|
| + _addRemoveEdit(rf.rangeStartStart(invocation, binary.leftOperand));
|
| + _addRemoveEdit(rf.rangeEndEnd(binary.rightOperand, invocation));
|
| + // add proposal
|
| + _addFix(FixKind.USE_EFFECTIVE_INTEGER_DIVISION, []);
|
| + // done
|
| + break;
|
| + }
|
| + }
|
| + }
|
| +
|
| + void _addFix_useStaticAccess_method() {
|
| + if (node is SimpleIdentifier && node.parent is MethodInvocation) {
|
| + MethodInvocation invocation = node.parent as MethodInvocation;
|
| + if (invocation.methodName == node) {
|
| + Expression target = invocation.target;
|
| + String targetType = utils.getTypeSource(target);
|
| + // replace "target" with class name
|
| + SourceRange range = rf.rangeNode(target);
|
| + _addReplaceEdit(range, targetType);
|
| + // add proposal
|
| + _addFix(FixKind.CHANGE_TO_STATIC_ACCESS, [targetType]);
|
| + }
|
| + }
|
| + }
|
| +
|
| + void _addFix_useStaticAccess_property() {
|
| + if (node is SimpleIdentifier) {
|
| + if (node.parent is PrefixedIdentifier) {
|
| + PrefixedIdentifier prefixed = node.parent as PrefixedIdentifier;
|
| + if (prefixed.identifier == node) {
|
| + Expression target = prefixed.prefix;
|
| + String targetType = utils.getTypeSource(target);
|
| + // replace "target" with class name
|
| + SourceRange range = rf.rangeNode(target);
|
| + _addReplaceEdit(range, targetType);
|
| + // add proposal
|
| + _addFix(FixKind.CHANGE_TO_STATIC_ACCESS, [targetType]);
|
| + }
|
| + }
|
| + }
|
| + }
|
| +
|
| + /**
|
| + * Adds a new [Edit] to [edits].
|
| + */
|
| + void _addInsertEdit(int offset, String text) {
|
| + Edit edit = new Edit(offset, 0, text);
|
| + edits.add(edit);
|
| + }
|
| +
|
| + /**
|
| + * Adds a single linked position to [groupId].
|
| + */
|
| + void _addLinkedPosition(String groupId, SourceRange range) {
|
| + _addLinkedPosition2(
|
| + groupId,
|
| + new Position(file, range.offset, range.length));
|
| + }
|
| +
|
| + /**
|
| + * Adds a single linked position to [groupId].
|
| + */
|
| + void _addLinkedPosition2(String groupId, Position position) {
|
| + PositionGroup group = positionGroups[groupId];
|
| + if (group == null) {
|
| + group = new PositionGroup(groupId);
|
| + positionGroups[groupId] = group;
|
| + }
|
| + group.add(position);
|
| + }
|
| +
|
| + /**
|
| + * Adds a new [Edit] to [edits].
|
| + */
|
| + void _addRemoveEdit(SourceRange range) {
|
| + _addReplaceEdit(range, '');
|
| + }
|
| +
|
| + /**
|
| + * Adds a new [Edit] to [edits].
|
| + */
|
| + void _addReplaceEdit(SourceRange range, String text) {
|
| + Edit edit = new Edit.range(range, text);
|
| + edits.add(edit);
|
| + }
|
| +
|
| + void _appendParameterSource(StringBuffer sb, DartType type, String name) {
|
| + String parameterSource = utils.getParameterSource(type, name);
|
| + sb.write(parameterSource);
|
| + }
|
| +
|
| + void _appendParameters(StringBuffer sb, List<ParameterElement> parameters,
|
| + Map<ParameterElement, String> defaultValueMap) {
|
| + sb.write("(");
|
| + bool firstParameter = true;
|
| + bool sawNamed = false;
|
| + bool sawPositional = false;
|
| + for (ParameterElement parameter in parameters) {
|
| + if (!firstParameter) {
|
| + sb.write(", ");
|
| + } else {
|
| + firstParameter = false;
|
| + }
|
| + // may be optional
|
| + ParameterKind parameterKind = parameter.parameterKind;
|
| + if (parameterKind == ParameterKind.NAMED) {
|
| + if (!sawNamed) {
|
| + sb.write("{");
|
| + sawNamed = true;
|
| + }
|
| + }
|
| + if (parameterKind == ParameterKind.POSITIONAL) {
|
| + if (!sawPositional) {
|
| + sb.write("[");
|
| + sawPositional = true;
|
| + }
|
| + }
|
| + // parameter
|
| + _appendParameterSource(sb, parameter.type, parameter.name);
|
| + // default value
|
| + if (defaultValueMap != null) {
|
| + String defaultSource = defaultValueMap[parameter];
|
| + if (defaultSource != null) {
|
| + if (sawPositional) {
|
| + sb.write(" = ");
|
| + } else {
|
| + sb.write(": ");
|
| + }
|
| + sb.write(defaultSource);
|
| + }
|
| + }
|
| + }
|
| + // close parameters
|
| + if (sawNamed) {
|
| + sb.write("}");
|
| + }
|
| + if (sawPositional) {
|
| + sb.write("]");
|
| + }
|
| + sb.write(")");
|
| + }
|
| +
|
| +// void _addLinkedPositionProposal(String group,
|
| +// LinkedPositionProposal proposal) {
|
| +// List<LinkedPositionProposal> nodeProposals = linkedPositionProposals[group];
|
| +// if (nodeProposals == null) {
|
| +// nodeProposals = <LinkedPositionProposal>[];
|
| +// linkedPositionProposals[group] = nodeProposals;
|
| +// }
|
| +// nodeProposals.add(proposal);
|
| +// }
|
| +
|
| + /**
|
| + * @return the string to display as the name of the given constructor in a proposal name.
|
| + */
|
| + String _getConstructorProposalName(ConstructorElement constructor) {
|
| + StringBuffer proposalNameBuffer = new StringBuffer();
|
| + proposalNameBuffer.write("super");
|
| + // may be named
|
| + String constructorName = constructor.displayName;
|
| + if (!constructorName.isEmpty) {
|
| + proposalNameBuffer.write(".");
|
| + proposalNameBuffer.write(constructorName);
|
| + }
|
| + // parameters
|
| + _appendParameters(proposalNameBuffer, constructor.parameters, null);
|
| + // done
|
| + return proposalNameBuffer.toString();
|
| + }
|
| +
|
| + /**
|
| + * Inserts the given [SourceBuilder] at its offset.
|
| + */
|
| + void _insertBuilder(SourceBuilder builder) {
|
| + String text = builder.toString();
|
| + _addInsertEdit(builder.offset, text);
|
| + // add linked positions
|
| + builder.positionGroups.forEach((PositionGroup group) {
|
| + group.positions.forEach((Position position) {
|
| + _addLinkedPosition2(group.id, position);
|
| + });
|
| + });
|
| + }
|
| +
|
| + QuickFixProcessorImpl_NewConstructorLocation
|
| + _prepareNewConstructorLocation(ClassDeclaration classDeclaration, String eol) {
|
| + List<ClassMember> members = classDeclaration.members;
|
| + // find the last field/constructor
|
| + ClassMember lastFieldOrConstructor = null;
|
| + for (ClassMember member in members) {
|
| + if (member is FieldDeclaration || member is ConstructorDeclaration) {
|
| + lastFieldOrConstructor = member;
|
| + } else {
|
| + break;
|
| + }
|
| + }
|
| + // after the field/constructor
|
| + if (lastFieldOrConstructor != null) {
|
| + return new QuickFixProcessorImpl_NewConstructorLocation(
|
| + "${eol}${eol}",
|
| + lastFieldOrConstructor.end,
|
| + "");
|
| + }
|
| + // at the beginning of the class
|
| + String suffix = members.isEmpty ? "" : eol;
|
| + return new QuickFixProcessorImpl_NewConstructorLocation(
|
| + eol,
|
| + classDeclaration.leftBracket.end,
|
| + suffix);
|
| + }
|
| +
|
| + /**
|
| + * Removes any [ParenthesizedExpression] enclosing [expr].
|
| + *
|
| + * [exprPrecedence] - the effective precedence of [expr].
|
| + */
|
| + void _removeEnclosingParentheses(Expression expr, int exprPrecedence) {
|
| + while (expr.parent is ParenthesizedExpression) {
|
| + ParenthesizedExpression parenthesized =
|
| + expr.parent as ParenthesizedExpression;
|
| + if (getExpressionParentPrecedence(parenthesized) > exprPrecedence) {
|
| + break;
|
| + }
|
| + _addRemoveEdit(rf.rangeToken(parenthesized.leftParenthesis));
|
| + _addRemoveEdit(rf.rangeToken(parenthesized.rightParenthesis));
|
| + expr = parenthesized;
|
| + }
|
| + }
|
| +
|
| + static void _addSuperTypeProposals(SourceBuilder sb,
|
| + Set<DartType> alreadyAdded, DartType type) {
|
| + if (type != null &&
|
| + !alreadyAdded.contains(type) &&
|
| + type.element is ClassElement) {
|
| + alreadyAdded.add(type);
|
| + ClassElement element = type.element as ClassElement;
|
| + sb.addProposal(element.name);
|
| + _addSuperTypeProposals(sb, alreadyAdded, element.supertype);
|
| + for (InterfaceType interfaceType in element.interfaces) {
|
| + _addSuperTypeProposals(sb, alreadyAdded, interfaceType);
|
| + }
|
| + }
|
| + }
|
| +
|
| + /**
|
| + * @return the suggestions for given [Type] and [DartExpression], not empty.
|
| + */
|
| + static List<String> _getArgumentNameSuggestions(Set<String> excluded,
|
| + DartType type, Expression expression, int index) {
|
| + List<String> suggestions =
|
| + getVariableNameSuggestions2(type, expression, excluded);
|
| + if (suggestions.length != 0) {
|
| + return suggestions;
|
| + }
|
| + return <String>["arg${index}"];
|
| + }
|
| +
|
| + /**
|
| + * Returns `true` if [node] is a type name.
|
| + */
|
| + static bool _mayBeTypeIdentifier(AstNode node) {
|
| + if (node is SimpleIdentifier) {
|
| + AstNode parent = node.parent;
|
| + if (parent is TypeName) {
|
| + return true;
|
| + }
|
| + if (parent is MethodInvocation) {
|
| + return parent.realTarget == node;
|
| + }
|
| + if (parent is PrefixedIdentifier) {
|
| + return parent.prefix == node;
|
| + }
|
| + }
|
| + return false;
|
| + }
|
| +}
|
| +
|
| +
|
| +/**
|
| + * Describes the location for a newly created [ConstructorDeclaration].
|
| + *
|
| + * TODO(scheglov) rename
|
| + */
|
| +class QuickFixProcessorImpl_NewConstructorLocation {
|
| + final String _prefix;
|
| + final int _offset;
|
| + final String _suffix;
|
| +
|
| + QuickFixProcessorImpl_NewConstructorLocation(this._prefix, this._offset,
|
| + this._suffix);
|
| +}
|
|
|