Chromium Code Reviews| Index: pkg/analysis_server/lib/src/services/correction/fix_internal.dart |
| diff --git a/pkg/analysis_server/lib/src/services/correction/fix_internal.dart b/pkg/analysis_server/lib/src/services/correction/fix_internal.dart |
| index 77d91014e401b00f7c50605de72d9d8974b6cd91..4f4a0506dbf37cac3b44a2bdbf08e69afbb0d358 100644 |
| --- a/pkg/analysis_server/lib/src/services/correction/fix_internal.dart |
| +++ b/pkg/analysis_server/lib/src/services/correction/fix_internal.dart |
| @@ -210,7 +210,8 @@ class FixProcessor { |
| CompileTimeErrorCode.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE) { |
| _addFix_replaceWithConstInstanceCreation(); |
| } |
| - if (errorCode == CompileTimeErrorCode.ASYNC_FOR_IN_WRONG_CONTEXT) { |
| + if (errorCode == CompileTimeErrorCode.ASYNC_FOR_IN_WRONG_CONTEXT || |
| + errorCode == StaticWarningCode.UNDEFINED_IDENTIFIER_AWAIT) { |
| await _addFix_addAsync(); |
| } |
| if (errorCode == CompileTimeErrorCode.INVALID_ANNOTATION) { |
| @@ -255,10 +256,10 @@ class FixProcessor { |
| _addFix_useEffectiveIntegerDivision(); |
| } |
| if (errorCode == HintCode.TYPE_CHECK_IS_NOT_NULL) { |
| - _addFix_isNotNull(); |
| + await _addFix_isNotNull(); |
| } |
| if (errorCode == HintCode.TYPE_CHECK_IS_NULL) { |
| - _addFix_isNull(); |
| + await _addFix_isNull(); |
| } |
| if (errorCode == HintCode.UNDEFINED_GETTER) { |
| _addFix_undefinedClassAccessor_useSimilar(); |
| @@ -282,7 +283,7 @@ class FixProcessor { |
| _addFix_removeUnusedImport(); |
| } |
| if (errorCode == ParserErrorCode.EXPECTED_TOKEN) { |
| - _addFix_insertSemicolon(); |
| + await _addFix_insertSemicolon(); |
| } |
| if (errorCode == ParserErrorCode.GETTER_WITH_PARAMETERS) { |
| _addFix_removeParameters_inGetterDeclaration(); |
| @@ -294,7 +295,7 @@ class FixProcessor { |
| await _addFix_makeFieldNotFinal(); |
| } |
| if (errorCode == StaticWarningCode.CONCRETE_CLASS_WITH_ABSTRACT_MEMBER) { |
| - _addFix_makeEnclosingClassAbstract(); |
| + await _addFix_makeEnclosingClassAbstract(); |
| } |
| if (errorCode == StaticWarningCode.EXTRA_POSITIONAL_ARGUMENTS || |
| errorCode == |
| @@ -326,10 +327,10 @@ class FixProcessor { |
| StaticWarningCode |
| .NON_ABSTRACT_CLASS_INHERITS_ABSTRACT_MEMBER_FIVE_PLUS) { |
| // make class abstract |
| - _addFix_makeEnclosingClassAbstract(); |
| + await _addFix_makeEnclosingClassAbstract(); |
| await _addFix_createNoSuchMethod(); |
| // implement methods |
| - _addFix_createMissingOverrides(); |
| + await _addFix_createMissingOverrides(); |
| } |
| if (errorCode == CompileTimeErrorCode.UNDEFINED_CLASS || |
| errorCode == StaticWarningCode.CAST_TO_NON_TYPE || |
| @@ -358,11 +359,8 @@ class FixProcessor { |
| await _addFix_importLibrary_withTopLevelVariable(); |
| await _addFix_createLocalVariable(); |
| } |
| - if (errorCode == StaticWarningCode.UNDEFINED_IDENTIFIER_AWAIT) { |
| - await _addFix_addAsync(); |
| - } |
| if (errorCode == StaticTypeWarningCode.ILLEGAL_ASYNC_RETURN_TYPE) { |
| - _addFix_illegalAsyncReturnType(); |
| + await _addFix_illegalAsyncReturnType(); |
| } |
| if (errorCode == StaticTypeWarningCode.INSTANCE_ACCESS_TO_STATIC_MEMBER) { |
| _addFix_useStaticAccess_method(); |
| @@ -375,7 +373,7 @@ class FixProcessor { |
| _addFix_removeParentheses_inGetterInvocation(); |
| } |
| if (errorCode == StaticTypeWarningCode.NON_BOOL_CONDITION) { |
| - _addFix_nonBoolCondition_addNotNull(); |
| + await _addFix_nonBoolCondition_addNotNull(); |
| } |
| if (errorCode == StaticTypeWarningCode.NON_TYPE_AS_TYPE_ARGUMENT) { |
| await _addFix_importLibrary_withType(); |
| @@ -1202,35 +1200,24 @@ class FixProcessor { |
| ClassMemberLocation targetLocation = |
| utils.prepareNewFieldLocation(targetClassNode); |
| // build method source |
| - String targetFile = targetClassElement.source.fullName; |
| - SourceBuilder sb = new SourceBuilder(targetFile, targetLocation.offset); |
| - { |
| - sb.append(targetLocation.prefix); |
| - // maybe "static" |
| - if (staticModifier) { |
| - sb.append('static '); |
| - } |
| - // append type |
| + Source targetSource = targetClassElement.source; |
| + String targetFile = targetSource.fullName; |
| + DartChangeBuilder changeBuilder = new DartChangeBuilder(driver); |
| + await changeBuilder.addFileEdit(targetFile, targetSource.modificationStamp, |
| + (DartFileEditBuilder builder) { |
| Expression fieldTypeNode = climbPropertyAccess(nameNode); |
| DartType fieldType = _inferUndefinedExpressionType(fieldTypeNode); |
| - _appendType(sb, fieldType, groupId: 'TYPE', orVar: true); |
| - // append name |
| - { |
| - sb.startPosition('NAME'); |
| - sb.append(name); |
| - sb.endPosition(); |
| - } |
| - sb.append(';'); |
| - sb.append(targetLocation.suffix); |
| - } |
| - // insert source |
| - _insertBuilder(sb, targetClassElement); |
| - // add linked positions |
| - if (targetFile == file) { |
| - _addLinkedPosition('NAME', sb, range.node(node)); |
| - } |
| - // add proposal |
| - _addFix(DartFixKind.CREATE_FIELD, [name]); |
| + builder.addInsertion(targetLocation.offset, (DartEditBuilder builder) { |
| + builder.write(targetLocation.prefix); |
| + builder.writeFieldDeclaration(name, |
| + isStatic: staticModifier, |
| + nameGroupName: 'NAME', |
| + type: fieldType, |
| + typeGroupName: 'TYPE'); |
| + builder.write(targetLocation.suffix); |
| + }); |
| + }); |
| + _addFixFromBuilder(changeBuilder, DartFixKind.CREATE_FIELD, args: [name]); |
| } |
| void _addFix_createFunction_forFunctionType() { |
| @@ -1428,7 +1415,7 @@ class FixProcessor { |
| args: [name]); |
| } |
| - void _addFix_createMissingOverrides() { |
| + Future<Null> _addFix_createMissingOverrides() async { |
| // prepare target |
| ClassDeclaration targetClass = node.parent as ClassDeclaration; |
| ClassElement targetClassElement = targetClass.element; |
| @@ -1452,60 +1439,65 @@ class FixProcessor { |
| } |
| return 1; |
| }); |
| - // prepare SourceBuilder |
| + int numElements = elements.length; |
| int insertOffset = targetClass.end - 1; |
| - SourceBuilder sb = new SourceBuilder(file, insertOffset); |
| - // EOL management |
| - bool isFirst = true; |
| - void addEolIfNotFirst() { |
| - if (!isFirst || utils.isClassWithEmptyBody(targetClass)) { |
| - sb.append(eol); |
| - } |
| - isFirst = false; |
| - } |
| - |
| - // merge getter/setter pairs into fields |
| String prefix = utils.getIndent(1); |
| - int numElements = elements.length; |
| - for (int i = 0; i < elements.length; i++) { |
| - ExecutableElement element = elements[i]; |
| - if (element.kind == ElementKind.GETTER && i + 1 < elements.length) { |
| - ExecutableElement nextElement = elements[i + 1]; |
| - if (nextElement.kind == ElementKind.SETTER) { |
| - // remove this and the next elements, adjust iterator |
| - elements.removeAt(i + 1); |
| - elements.removeAt(i); |
| - i--; |
| - numElements--; |
| - // separator |
| - addEolIfNotFirst(); |
| - // @override |
| - { |
| - sb.append(prefix); |
| - sb.append('@override'); |
| - sb.append(eol); |
| + DartChangeBuilder changeBuilder = new DartChangeBuilder(driver); |
| + await changeBuilder.addFileEdit(file, fileStamp, |
| + (DartFileEditBuilder builder) { |
| + builder.addInsertion(insertOffset, (DartEditBuilder builder) { |
| + // TODO(brianwilkerson) Compare with builder.writeOverrideOfInheritedMember |
| + // The builder method doesn't merge getter/setter pairs into fields. |
| + // EOL management |
| + bool isFirst = true; |
| + void addEolIfNotFirst() { |
| + if (!isFirst || utils.isClassWithEmptyBody(targetClass)) { |
| + builder.write(eol); |
| } |
| - // add field |
| - sb.append(prefix); |
| - _appendType(sb, element.type.returnType, orVar: true); |
| - sb.append(element.name); |
| - sb.append(';'); |
| - sb.append(eol); |
| + isFirst = false; |
| } |
| - } |
| - } |
| - // add elements |
| - for (ExecutableElement element in elements) { |
| - addEolIfNotFirst(); |
| - _addFix_createMissingOverrides_single(sb, targetClass, element); |
| - } |
| - // add proposal |
| - exitPosition = new Position(file, insertOffset); |
| - _insertBuilder(sb, unitElement); |
| - _addFix(DartFixKind.CREATE_MISSING_OVERRIDES, [numElements]); |
| + |
| + // merge getter/setter pairs into fields |
| + for (int i = 0; i < elements.length; i++) { |
| + ExecutableElement element = elements[i]; |
| + if (element.kind == ElementKind.GETTER && i + 1 < elements.length) { |
| + ExecutableElement nextElement = elements[i + 1]; |
| + if (nextElement.kind == ElementKind.SETTER) { |
| + // remove this and the next elements, adjust iterator |
| + elements.removeAt(i + 1); |
| + elements.removeAt(i); |
| + i--; |
| + numElements--; |
| + // separator |
| + addEolIfNotFirst(); |
| + // @override |
| + builder.write(prefix); |
| + builder.write('@override'); |
| + builder.write(eol); |
| + // add field |
| + builder.write(prefix); |
| + builder.writeType(element.type.returnType, required: true); |
| + builder.write(' '); |
| + builder.write(element.name); |
| + builder.write(';'); |
| + builder.write(eol); |
| + } |
| + } |
| + } |
| + // add elements |
| + for (ExecutableElement element in elements) { |
| + addEolIfNotFirst(); |
| + _addFix_createMissingOverridesForBuilder( |
| + builder, targetClass, element); |
| + } |
| + }); |
| + }); |
| + changeBuilder.setSelection(new Position(file, insertOffset)); |
| + _addFixFromBuilder(changeBuilder, DartFixKind.CREATE_MISSING_OVERRIDES, |
| + args: [numElements]); |
| } |
| - void _addFix_createMissingOverrides_single(SourceBuilder sb, |
| + void _addFix_createMissingOverridesForBuilder(DartEditBuilder builder, |
| ClassDeclaration targetClass, ExecutableElement element) { |
| utils.targetExecutableElement = element; |
| // prepare environment |
| @@ -1517,50 +1509,51 @@ class FixProcessor { |
| bool isSetter = elementKind == ElementKind.SETTER; |
| bool isMethod = elementKind == ElementKind.METHOD; |
| bool isOperator = isMethod && (element as MethodElement).isOperator; |
| - sb.append(prefix); |
| + builder.write(prefix); |
| if (isGetter) { |
| - sb.append('// TODO: implement ${element.displayName}'); |
| - sb.append(eol); |
| - sb.append(prefix); |
| + builder.write('// TODO: implement ${element.displayName}'); |
| + builder.write(eol); |
| + builder.write(prefix); |
| } |
| // @override |
| - { |
| - sb.append('@override'); |
| - sb.append(eol); |
| - sb.append(prefix); |
| - } |
| + builder.write('@override'); |
| + builder.write(eol); |
| + builder.write(prefix); |
| // return type |
| if (!isSetter) { |
| - _appendType(sb, element.type.returnType); |
| + if (builder.writeType(element.type.returnType, |
| + methodBeingCopied: element)) { |
| + builder.write(' '); |
| + } |
| } |
| // keyword |
| if (isGetter) { |
| - sb.append('get '); |
| + builder.write('get '); |
| } else if (isSetter) { |
| - sb.append('set '); |
| + builder.write('set '); |
| } else if (isOperator) { |
| - sb.append('operator '); |
| + builder.write('operator '); |
| } |
| // name |
| - sb.append(element.displayName); |
| - _appendTypeParameters(sb, element.typeParameters); |
| + builder.write(element.displayName); |
| + builder.writeTypeParameters(element.typeParameters); |
| // parameters + body |
| if (isGetter) { |
| - sb.append(' => null;'); |
| + builder.write(' => null;'); |
| } else { |
| List<ParameterElement> parameters = element.parameters; |
| - _appendParameters(sb, parameters); |
| - sb.append(' {'); |
| + builder.writeParameters(parameters, methodBeingCopied: element); |
| + builder.write(' {'); |
| // TO-DO |
| - sb.append(eol); |
| - sb.append(prefix2); |
| - sb.append('// TODO: implement ${element.displayName}'); |
| - sb.append(eol); |
| + builder.write(eol); |
| + builder.write(prefix2); |
| + builder.write('// TODO: implement ${element.displayName}'); |
| + builder.write(eol); |
| // close method |
| - sb.append(prefix); |
| - sb.append('}'); |
| + builder.write(prefix); |
| + builder.write('}'); |
| } |
| - sb.append(eol); |
| + builder.write(eol); |
| utils.targetExecutableElement = null; |
| } |
| @@ -1602,12 +1595,16 @@ class FixProcessor { |
| } |
| } |
| - void _addFix_illegalAsyncReturnType() { |
| + Future<Null> _addFix_illegalAsyncReturnType() async { |
| // prepare the existing type |
| TypeAnnotation typeName = node.getAncestor((n) => n is TypeAnnotation); |
| - _replaceTypeWithFuture(typeName, typeProvider); |
| - // add proposal |
| - _addFix(DartFixKind.REPLACE_RETURN_TYPE_FUTURE, []); |
| + TypeProvider typeProvider = this.typeProvider; |
| + DartChangeBuilder changeBuilder = new DartChangeBuilder(driver); |
| + await changeBuilder.addFileEdit(file, fileStamp, |
| + (DartFileEditBuilder builder) { |
| + builder.replaceTypeWithFuture(typeName, typeProvider); |
| + }); |
| + _addFixFromBuilder(changeBuilder, DartFixKind.REPLACE_RETURN_TYPE_FUTURE); |
| } |
| void _addFix_importLibrary(FixKind kind, Source library) { |
| @@ -1739,44 +1736,72 @@ class FixProcessor { |
| } |
| } |
| - void _addFix_insertSemicolon() { |
| + Future<Null> _addFix_insertSemicolon() async { |
| if (error.message.contains("';'")) { |
| if (_isAwaitNode()) { |
| return; |
| } |
| int insertOffset = error.offset + error.length; |
| - _addInsertEdit(insertOffset, ';'); |
| - _addFix(DartFixKind.INSERT_SEMICOLON, []); |
| + DartChangeBuilder changeBuilder = new DartChangeBuilder(driver); |
| + await changeBuilder.addFileEdit(file, fileStamp, |
| + (DartFileEditBuilder builder) { |
| + builder.addInsertion(insertOffset, (DartEditBuilder builder) { |
|
scheglov
2017/06/01 16:57:51
addSimpleInsertion() here and in several methods b
Brian Wilkerson
2017/06/01 22:30:29
Done
|
| + builder.write(';'); |
| + }); |
| + }); |
| + _addFixFromBuilder(changeBuilder, DartFixKind.INSERT_SEMICOLON); |
| } |
| } |
| - void _addFix_isNotNull() { |
| + Future<Null> _addFix_isNotNull() async { |
| if (coveredNode is IsExpression) { |
| IsExpression isExpression = coveredNode as IsExpression; |
| - _addReplaceEdit( |
| - range.endEnd(isExpression.expression, isExpression), ' != null'); |
| - _addFix(DartFixKind.USE_NOT_EQ_NULL, []); |
| + DartChangeBuilder changeBuilder = new DartChangeBuilder(driver); |
| + await changeBuilder.addFileEdit(file, fileStamp, |
| + (DartFileEditBuilder builder) { |
| + builder |
| + .addReplacement(range.endEnd(isExpression.expression, isExpression), |
| + (DartEditBuilder builder) { |
| + builder.write(' != null'); |
| + }); |
| + }); |
| + _addFixFromBuilder(changeBuilder, DartFixKind.USE_NOT_EQ_NULL); |
| } |
| } |
| - void _addFix_isNull() { |
| + Future<Null> _addFix_isNull() async { |
| if (coveredNode is IsExpression) { |
| IsExpression isExpression = coveredNode as IsExpression; |
| - _addReplaceEdit( |
| - range.endEnd(isExpression.expression, isExpression), ' == null'); |
| - _addFix(DartFixKind.USE_EQ_EQ_NULL, []); |
| + DartChangeBuilder changeBuilder = new DartChangeBuilder(driver); |
| + await changeBuilder.addFileEdit(file, fileStamp, |
| + (DartFileEditBuilder builder) { |
| + builder |
| + .addReplacement(range.endEnd(isExpression.expression, isExpression), |
| + (DartEditBuilder builder) { |
| + builder.write(' == null'); |
| + }); |
| + }); |
| + _addFixFromBuilder(changeBuilder, DartFixKind.USE_EQ_EQ_NULL); |
| } |
| } |
| - void _addFix_makeEnclosingClassAbstract() { |
| + Future<Null> _addFix_makeEnclosingClassAbstract() async { |
| ClassDeclaration enclosingClass = |
| node.getAncestor((node) => node is ClassDeclaration); |
| if (enclosingClass == null) { |
| return; |
| } |
| String className = enclosingClass.name.name; |
| - _addInsertEdit(enclosingClass.classKeyword.offset, 'abstract '); |
| - _addFix(DartFixKind.MAKE_CLASS_ABSTRACT, [className]); |
| + DartChangeBuilder changeBuilder = new DartChangeBuilder(driver); |
| + await changeBuilder.addFileEdit(file, fileStamp, |
| + (DartFileEditBuilder builder) { |
| + builder.addInsertion(enclosingClass.classKeyword.offset, |
| + (DartEditBuilder builder) { |
| + builder.write('abstract '); |
| + }); |
| + }); |
| + _addFixFromBuilder(changeBuilder, DartFixKind.MAKE_CLASS_ABSTRACT, |
| + args: [className]); |
| } |
| Future<Null> _addFix_makeFieldNotFinal() async { |
| @@ -1799,23 +1824,39 @@ class FixProcessor { |
| Token keywordToken = declarationList.keyword; |
| if (declarationList.variables.length == 1 && |
| keywordToken.keyword == Keyword.FINAL) { |
| - if (declarationList.type != null) { |
| - _addRemoveEdit( |
| - range.startStart(keywordToken, declarationList.type)); |
| - } else { |
| - _addReplaceEdit(range.startStart(keywordToken, variable), 'var '); |
| - } |
| + DartChangeBuilder changeBuilder = new DartChangeBuilder(driver); |
| + await changeBuilder.addFileEdit(file, fileStamp, |
| + (DartFileEditBuilder builder) { |
| + if (declarationList.type != null) { |
| + builder.addReplacement( |
| + range.startStart(keywordToken, declarationList.type), |
| + (DartEditBuilder builder) {}); |
| + } else { |
| + builder.addReplacement(range.startStart(keywordToken, variable), |
| + (DartEditBuilder builder) { |
| + builder.write('var '); |
| + }); |
| + } |
| + }); |
| String fieldName = getter.variable.displayName; |
| - _addFix(DartFixKind.MAKE_FIELD_NOT_FINAL, [fieldName]); |
| + _addFixFromBuilder(changeBuilder, DartFixKind.MAKE_FIELD_NOT_FINAL, |
| + args: [fieldName]); |
| } |
| } |
| } |
| } |
| } |
| - void _addFix_nonBoolCondition_addNotNull() { |
| - _addInsertEdit(error.offset + error.length, ' != null'); |
| - _addFix(DartFixKind.ADD_NE_NULL, []); |
| + Future<Null> _addFix_nonBoolCondition_addNotNull() async { |
| + DartChangeBuilder changeBuilder = new DartChangeBuilder(driver); |
| + await changeBuilder.addFileEdit(file, fileStamp, |
| + (DartFileEditBuilder builder) { |
| + builder.addInsertion(error.offset + error.length, |
| + (DartEditBuilder builder) { |
| + builder.write(' != null'); |
| + }); |
| + }); |
| + _addFixFromBuilder(changeBuilder, DartFixKind.ADD_NE_NULL); |
| } |
| void _addFix_removeAwait() { |
| @@ -2804,31 +2845,6 @@ class FixProcessor { |
| } |
| } |
| - void _appendTypeParameter( |
| - SourceBuilder sb, TypeParameterElement typeParameter) { |
| - sb.append(typeParameter.name); |
| - if (typeParameter.bound != null) { |
| - sb.append(' extends '); |
| - _appendType(sb, typeParameter.bound, trailingSpace: false); |
| - } |
| - } |
| - |
| - void _appendTypeParameters( |
| - SourceBuilder sb, List<TypeParameterElement> typeParameters) { |
| - if (typeParameters.isNotEmpty) { |
| - sb.append('<'); |
| - bool isFirst = true; |
| - for (TypeParameterElement typeParameter in typeParameters) { |
| - if (!isFirst) { |
| - sb.append(', '); |
| - } |
| - isFirst = false; |
| - _appendTypeParameter(sb, typeParameter); |
| - } |
| - sb.append('>'); |
| - } |
| - } |
| - |
| /** |
| * Computes the name of the library at the given [path]. |
| * See https://www.dartlang.org/articles/style-guide/#names for conventions. |
| @@ -3153,29 +3169,6 @@ class FixProcessor { |
| } |
| } |
| - void _replaceTypeWithFuture( |
| - TypeAnnotation typeName, TypeProvider typeProvider) { |
| - InterfaceType futureType = typeProvider.futureType; |
| - // validate the type |
| - DartType type = typeName?.type; |
| - if (type == null || |
| - type.isDynamic || |
| - type is InterfaceType && type.element == futureType.element) { |
| - return; |
| - } |
| - // prepare code for the types |
| - String futureTypeCode = utils.getTypeSource(futureType, librariesToImport); |
| - String nodeCode = utils.getNodeText(typeName); |
| - // wrap the existing type with Future |
| - String returnTypeCode; |
| - if (nodeCode == 'void') { |
| - returnTypeCode = futureTypeCode; |
| - } else { |
| - returnTypeCode = '$futureTypeCode<$nodeCode>'; |
| - } |
| - _addReplaceEdit(range.node(typeName), returnTypeCode); |
| - } |
| - |
| void _updateFinderWithClassMembers( |
| _ClosestElementFinder finder, ClassElement clazz) { |
| if (clazz != null) { |