| // 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. |
| |
| import 'dart:async'; |
| import 'dart:collection'; |
| import 'dart:core'; |
| |
| import 'package:analysis_server/plugin/edit/fix/fix_core.dart'; |
| import 'package:analysis_server/plugin/edit/fix/fix_dart.dart'; |
| import 'package:analysis_server/src/services/completion/dart/utilities.dart'; |
| import 'package:analysis_server/src/services/correction/fix.dart'; |
| import 'package:analysis_server/src/services/correction/levenshtein.dart'; |
| import 'package:analysis_server/src/services/correction/namespace.dart'; |
| import 'package:analysis_server/src/services/correction/strings.dart'; |
| import 'package:analysis_server/src/services/correction/util.dart'; |
| import 'package:analysis_server/src/services/search/hierarchy.dart'; |
| import 'package:analysis_server/src/utilities/flutter.dart' as flutter; |
| import 'package:analyzer/dart/analysis/session.dart'; |
| import 'package:analyzer/dart/ast/ast.dart'; |
| import 'package:analyzer/dart/ast/standard_resolution_map.dart'; |
| import 'package:analyzer/dart/ast/token.dart'; |
| import 'package:analyzer/dart/element/element.dart'; |
| import 'package:analyzer/dart/element/type.dart'; |
| import 'package:analyzer/error/error.dart'; |
| import 'package:analyzer/exception/exception.dart'; |
| import 'package:analyzer/file_system/file_system.dart'; |
| import 'package:analyzer/src/context/context_root.dart'; |
| import 'package:analyzer/src/dart/analysis/driver.dart'; |
| import 'package:analyzer/src/dart/analysis/session_helper.dart'; |
| import 'package:analyzer/src/dart/analysis/top_level_declaration.dart'; |
| import 'package:analyzer/src/dart/ast/ast.dart'; |
| import 'package:analyzer/src/dart/ast/token.dart'; |
| import 'package:analyzer/src/dart/ast/utilities.dart'; |
| import 'package:analyzer/src/dart/element/ast_provider.dart'; |
| import 'package:analyzer/src/dart/element/element.dart'; |
| import 'package:analyzer/src/dart/element/member.dart'; |
| import 'package:analyzer/src/dart/element/type.dart'; |
| import 'package:analyzer/src/error/codes.dart'; |
| import 'package:analyzer/src/error/inheritance_override.dart'; |
| import 'package:analyzer/src/generated/engine.dart'; |
| import 'package:analyzer/src/generated/error_verifier.dart'; |
| import 'package:analyzer/src/generated/java_core.dart'; |
| import 'package:analyzer/src/generated/parser.dart'; |
| import 'package:analyzer/src/generated/resolver.dart'; |
| import 'package:analyzer/src/generated/source.dart'; |
| import 'package:analyzer_plugin/protocol/protocol_common.dart' |
| hide AnalysisError, Element, ElementKind; |
| import 'package:analyzer_plugin/src/utilities/string_utilities.dart'; |
| import 'package:analyzer_plugin/utilities/change_builder/change_builder_dart.dart'; |
| import 'package:analyzer_plugin/utilities/fixes/fixes.dart' hide FixContributor; |
| import 'package:analyzer_plugin/utilities/range_factory.dart'; |
| import 'package:path/path.dart'; |
| |
| /** |
| * A predicate is a one-argument function that returns a boolean value. |
| */ |
| typedef bool ElementPredicate(Element argument); |
| |
| /** |
| * The implementation of [DartFixContext]. |
| * |
| * Clients may not extend, implement or mix-in this class. |
| */ |
| class DartFixContextImpl extends FixContextImpl implements DartFixContext { |
| @override |
| final AstProvider astProvider; |
| |
| @override |
| final CompilationUnit unit; |
| |
| DartFixContextImpl(FixContext fixContext, this.astProvider, this.unit) |
| : super.from(fixContext); |
| |
| GetTopLevelDeclarations get getTopLevelDeclarations => |
| analysisDriver.getTopLevelNameDeclarations; |
| } |
| |
| /** |
| * A [FixContributor] that provides the default set of fixes. |
| */ |
| class DefaultFixContributor extends DartFixContributor { |
| @override |
| Future<List<Fix>> internalComputeFixes(DartFixContext context) async { |
| // TODO(brianwilkerson) Determine whether this await is necessary. |
| await null; |
| try { |
| FixProcessor processor = new FixProcessor(context); |
| List<Fix> fixes = await processor.compute(); |
| List<Fix> fixAllFixes = await _computeFixAllFixes(context, fixes); |
| return new List.from(fixes)..addAll(fixAllFixes); |
| } on CancelCorrectionException { |
| return const <Fix>[]; |
| } |
| } |
| |
| Future<List<Fix>> _computeFixAllFixes( |
| DartFixContext context, List<Fix> fixes) async { |
| // TODO(brianwilkerson) Determine whether this await is necessary. |
| await null; |
| final AnalysisError analysisError = context.error; |
| final List<AnalysisError> allAnalysisErrors = context.errors; |
| |
| // Validate inputs: |
| // - return if no fixes |
| // - return if no other analysis errors |
| if (fixes.isEmpty || allAnalysisErrors.length < 2) { |
| return const <Fix>[]; |
| } |
| |
| // Remove any analysis errors that don't have the expected error code name |
| allAnalysisErrors |
| .removeWhere((e) => analysisError.errorCode.name != e.errorCode.name); |
| if (allAnalysisErrors.length < 2) { |
| return const <Fix>[]; |
| } |
| |
| // A map between each FixKind and the List of associated fixes |
| final HashMap<FixKind, List<Fix>> map = new HashMap(); |
| |
| // Populate the HashMap by looping through all AnalysisErrors, creating a |
| // new FixProcessor to compute the other fixes that can be applied with this |
| // one. |
| // For each fix, put the fix into the HashMap. |
| for (int i = 0; i < allAnalysisErrors.length; i++) { |
| final FixContext fixContextI = new FixContextImpl( |
| context.resourceProvider, |
| context.analysisDriver, |
| allAnalysisErrors[i], |
| allAnalysisErrors); |
| final DartFixContextImpl dartFixContextI = new DartFixContextImpl( |
| fixContextI, context.astProvider, context.unit); |
| final FixProcessor processorI = new FixProcessor(dartFixContextI); |
| final List<Fix> fixesListI = await processorI.compute(); |
| for (Fix f in fixesListI) { |
| if (!map.containsKey(f.kind)) { |
| map[f.kind] = new List<Fix>()..add(f); |
| } else { |
| map[f.kind].add(f); |
| } |
| } |
| } |
| |
| // For each FixKind in the HashMap, union each list together, then return |
| // the set of unioned Fixes. |
| final List<Fix> result = new List<Fix>(); |
| map.forEach((FixKind kind, List<Fix> fixesListJ) { |
| if (fixesListJ.first.kind.canBeAppliedTogether()) { |
| Fix unionFix = _unionFixList(fixesListJ); |
| if (unionFix != null) { |
| result.add(unionFix); |
| } |
| } |
| }); |
| return result; |
| } |
| |
| Fix _unionFixList(List<Fix> fixList) { |
| if (fixList == null || fixList.isEmpty) { |
| return null; |
| } else if (fixList.length == 1) { |
| return fixList[0]; |
| } |
| final SourceChange sourceChange = |
| new SourceChange(fixList[0].kind.appliedTogetherMessage); |
| sourceChange.edits = new List.from(fixList[0].change.edits); |
| final List<SourceEdit> edits = new List<SourceEdit>(); |
| edits.addAll(fixList[0].change.edits[0].edits); |
| sourceChange.linkedEditGroups = |
| new List.from(fixList[0].change.linkedEditGroups); |
| for (int i = 1; i < fixList.length; i++) { |
| edits.addAll(fixList[i].change.edits[0].edits); |
| sourceChange.linkedEditGroups..addAll(fixList[i].change.linkedEditGroups); |
| } |
| // Sort the list of SourceEdits so that when the edits are applied, they |
| // are applied from the end of the file to the top of the file. |
| edits.sort((s1, s2) => s2.offset - s1.offset); |
| |
| sourceChange.edits[0].edits = edits; |
| |
| return new Fix(fixList[0].kind, sourceChange); |
| } |
| } |
| |
| /** |
| * The computer for Dart fixes. |
| */ |
| class FixProcessor { |
| static const int MAX_LEVENSHTEIN_DISTANCE = 3; |
| |
| ResourceProvider resourceProvider; |
| AstProvider astProvider; |
| GetTopLevelDeclarations getTopLevelDeclarations; |
| CompilationUnit unit; |
| AnalysisError error; |
| |
| /** |
| * The analysis driver being used to perform analysis. |
| */ |
| AnalysisDriver driver; |
| |
| /** |
| * The analysis session to be used to create the change builder. |
| */ |
| AnalysisSession session; |
| |
| /** |
| * The helper wrapper around the [session]. |
| */ |
| AnalysisSessionHelper sessionHelper; |
| |
| String file; |
| CompilationUnitElement unitElement; |
| Source unitSource; |
| LibraryElement unitLibraryElement; |
| File unitLibraryFile; |
| Folder unitLibraryFolder; |
| |
| final List<Fix> fixes = <Fix>[]; |
| |
| CorrectionUtils utils; |
| int errorOffset; |
| int errorLength; |
| int errorEnd; |
| SourceRange errorRange; |
| AstNode node; |
| AstNode coveredNode; |
| |
| TypeProvider _typeProvider; |
| TypeSystem _typeSystem; |
| |
| FixProcessor(DartFixContext dartContext) { |
| resourceProvider = dartContext.resourceProvider; |
| astProvider = dartContext.astProvider; |
| getTopLevelDeclarations = dartContext.getTopLevelDeclarations; |
| |
| driver = dartContext.analysisDriver; |
| session = driver.currentSession; |
| sessionHelper = new AnalysisSessionHelper(session); |
| |
| // unit |
| unit = dartContext.unit; |
| unitElement = unit.declaredElement; |
| unitSource = unitElement.source; |
| // file |
| file = unitSource.fullName; |
| // library |
| unitLibraryElement = unitElement.library; |
| String unitLibraryPath = unitLibraryElement.source.fullName; |
| unitLibraryFile = resourceProvider.getFile(unitLibraryPath); |
| unitLibraryFolder = unitLibraryFile.parent; |
| // error |
| error = dartContext.error; |
| } |
| |
| DartType get coreTypeBool => _getCoreType('bool'); |
| |
| /** |
| * Returns the EOL to use for this [CompilationUnit]. |
| */ |
| String get eol => utils.endOfLine; |
| |
| TypeProvider get typeProvider { |
| if (_typeProvider == null) { |
| _typeProvider = unitElement.context.typeProvider; |
| } |
| return _typeProvider; |
| } |
| |
| TypeSystem get typeSystem { |
| if (_typeSystem == null) { |
| _typeSystem = new StrongTypeSystemImpl(typeProvider); |
| } |
| return _typeSystem; |
| } |
| |
| Future<List<Fix>> compute() async { |
| // TODO(brianwilkerson) Determine whether this await is necessary. |
| await null; |
| try { |
| utils = new CorrectionUtils(unit); |
| } catch (e) { |
| throw new CancelCorrectionException(exception: e); |
| } |
| |
| errorOffset = error.offset; |
| errorLength = error.length; |
| errorEnd = errorOffset + errorLength; |
| errorRange = new SourceRange(errorOffset, errorLength); |
| node = new NodeLocator2(errorOffset).searchWithin(unit); |
| coveredNode = |
| new NodeLocator2(errorOffset, errorEnd - 1).searchWithin(unit); |
| if (coveredNode == null) { |
| // TODO(brianwilkerson) Figure out why the coveredNode is sometimes null. |
| return fixes; |
| } |
| |
| // analyze ErrorCode |
| ErrorCode errorCode = error.errorCode; |
| if (errorCode == StaticWarningCode.UNDEFINED_CLASS_BOOLEAN) { |
| await _addFix_boolInsteadOfBoolean(); |
| } |
| if (errorCode == |
| CompileTimeErrorCode.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE) { |
| await _addFix_replaceWithConstInstanceCreation(); |
| } |
| if (errorCode == CompileTimeErrorCode.ASYNC_FOR_IN_WRONG_CONTEXT || |
| errorCode == StaticWarningCode.UNDEFINED_IDENTIFIER_AWAIT) { |
| await _addFix_addAsync(); |
| } |
| if ((errorCode == CompileTimeErrorCode.BUILT_IN_IDENTIFIER_AS_TYPE || |
| errorCode == ParserErrorCode.UNEXPECTED_TOKEN) && |
| error.message.indexOf("'await'") >= 0) { |
| await _addFix_addAsync(); |
| } |
| if (errorCode == CompileTimeErrorCode.INTEGER_LITERAL_IMPRECISE_AS_DOUBLE) { |
| await _addFix_changeToNearestPreciseValue(); |
| } |
| |
| if (errorCode == CompileTimeErrorCode.INVALID_ANNOTATION || |
| errorCode == CompileTimeErrorCode.UNDEFINED_ANNOTATION) { |
| if (node is Annotation) { |
| Annotation annotation = node; |
| Identifier name = annotation.name; |
| if (name != null && name.staticElement == null) { |
| node = name; |
| if (annotation.arguments == null) { |
| await _addFix_importLibrary_withTopLevelVariable(); |
| } else { |
| await _addFix_importLibrary_withType(); |
| await _addFix_createClass(); |
| await _addFix_undefinedClass_useSimilar(); |
| } |
| } |
| } |
| } |
| if (errorCode == |
| CompileTimeErrorCode.NO_DEFAULT_SUPER_CONSTRUCTOR_EXPLICIT) { |
| await _addFix_createConstructorSuperExplicit(); |
| } |
| if (errorCode == |
| CompileTimeErrorCode.NO_DEFAULT_SUPER_CONSTRUCTOR_IMPLICIT) { |
| await _addFix_createConstructorSuperImplicit(); |
| // TODO(brianwilkerson) The following was added because fasta produces |
| // NO_DEFAULT_SUPER_CONSTRUCTOR_IMPLICIT in places where analyzer produced |
| // NO_DEFAULT_SUPER_CONSTRUCTOR_EXPLICIT |
| await _addFix_createConstructorSuperExplicit(); |
| } |
| if (errorCode == |
| CompileTimeErrorCode.UNDEFINED_CONSTRUCTOR_IN_INITIALIZER_DEFAULT) { |
| await _addFix_createConstructorSuperExplicit(); |
| } |
| if (errorCode == CompileTimeErrorCode.URI_DOES_NOT_EXIST) { |
| await _addFix_createImportUri(); |
| await _addFix_createPartUri(); |
| } |
| if (errorCode == HintCode.CAN_BE_NULL_AFTER_NULL_AWARE) { |
| await _addFix_canBeNullAfterNullAware(); |
| } |
| if (errorCode == HintCode.DEAD_CODE) { |
| await _addFix_removeDeadCode(); |
| } |
| if (errorCode == HintCode.DIVISION_OPTIMIZATION) { |
| await _addFix_useEffectiveIntegerDivision(); |
| } |
| if (errorCode == HintCode.TYPE_CHECK_IS_NOT_NULL) { |
| await _addFix_isNotNull(); |
| } |
| if (errorCode == HintCode.TYPE_CHECK_IS_NULL) { |
| await _addFix_isNull(); |
| } |
| if (errorCode == HintCode.UNDEFINED_GETTER) { |
| await _addFix_undefinedClassAccessor_useSimilar(); |
| await _addFix_createField(); |
| await _addFix_createGetter(); |
| } |
| if (errorCode == HintCode.UNDEFINED_SETTER) { |
| await _addFix_undefinedClassAccessor_useSimilar(); |
| await _addFix_createField(); |
| } |
| if (errorCode == HintCode.UNNECESSARY_CAST) { |
| await _addFix_removeUnnecessaryCast(); |
| } |
| if (errorCode == HintCode.UNUSED_CATCH_CLAUSE) { |
| await _addFix_removeUnusedCatchClause(); |
| } |
| if (errorCode == HintCode.UNUSED_CATCH_STACK) { |
| await _addFix_removeUnusedCatchStack(); |
| } |
| if (errorCode == HintCode.UNUSED_IMPORT) { |
| await _addFix_removeUnusedImport(); |
| } |
| if (errorCode == ParserErrorCode.EXPECTED_TOKEN) { |
| await _addFix_insertSemicolon(); |
| } |
| if (errorCode == ParserErrorCode.GETTER_WITH_PARAMETERS) { |
| await _addFix_removeParameters_inGetterDeclaration(); |
| } |
| if (errorCode == ParserErrorCode.VAR_AS_TYPE_NAME) { |
| await _addFix_replaceVarWithDynamic(); |
| } |
| if (errorCode == StaticWarningCode.ASSIGNMENT_TO_FINAL) { |
| await _addFix_makeFieldNotFinal(); |
| } |
| if (errorCode == StaticWarningCode.CONCRETE_CLASS_WITH_ABSTRACT_MEMBER) { |
| await _addFix_makeEnclosingClassAbstract(); |
| } |
| if (errorCode == CompileTimeErrorCode.EXTRA_POSITIONAL_ARGUMENTS || |
| errorCode == StaticWarningCode.EXTRA_POSITIONAL_ARGUMENTS || |
| errorCode == |
| CompileTimeErrorCode.EXTRA_POSITIONAL_ARGUMENTS_COULD_BE_NAMED || |
| errorCode == |
| StaticWarningCode.EXTRA_POSITIONAL_ARGUMENTS_COULD_BE_NAMED) { |
| await _addFix_createConstructor_insteadOfSyntheticDefault(); |
| await _addFix_addMissingParameter(); |
| } |
| if (errorCode == HintCode.MISSING_REQUIRED_PARAM || |
| errorCode == HintCode.MISSING_REQUIRED_PARAM_WITH_DETAILS) { |
| await _addFix_addMissingRequiredArgument(); |
| } |
| if (errorCode == StaticWarningCode.NEW_WITH_UNDEFINED_CONSTRUCTOR) { |
| await _addFix_createConstructor_named(); |
| } |
| if (errorCode == StaticWarningCode.CONCRETE_CLASS_WITH_ABSTRACT_MEMBER || |
| 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 |
| await _addFix_makeEnclosingClassAbstract(); |
| await _addFix_createNoSuchMethod(); |
| // implement methods |
| await _addFix_createMissingOverrides(); |
| } |
| if (errorCode == CompileTimeErrorCode.UNDEFINED_CLASS || |
| errorCode == StaticWarningCode.CAST_TO_NON_TYPE || |
| errorCode == StaticWarningCode.NOT_A_TYPE || |
| errorCode == StaticWarningCode.TYPE_TEST_WITH_UNDEFINED_NAME || |
| errorCode == StaticWarningCode.UNDEFINED_CLASS) { |
| await _addFix_importLibrary_withType(); |
| await _addFix_createClass(); |
| await _addFix_createMixin(); |
| await _addFix_undefinedClass_useSimilar(); |
| } |
| if (errorCode == |
| CompileTimeErrorCode.EXTRA_POSITIONAL_ARGUMENTS_COULD_BE_NAMED || |
| errorCode == |
| StaticWarningCode.EXTRA_POSITIONAL_ARGUMENTS_COULD_BE_NAMED) { |
| await _addFix_convertToNamedArgument(); |
| } |
| if (errorCode == StaticWarningCode.FINAL_NOT_INITIALIZED) { |
| await _addFix_createConstructor_forUninitializedFinalFields(); |
| } |
| if (errorCode == StaticWarningCode.FINAL_NOT_INITIALIZED_CONSTRUCTOR_1 || |
| errorCode == StaticWarningCode.FINAL_NOT_INITIALIZED_CONSTRUCTOR_2 || |
| errorCode == |
| StaticWarningCode.FINAL_NOT_INITIALIZED_CONSTRUCTOR_3_PLUS) { |
| await _addFix_updateConstructor_forUninitializedFinalFields(); |
| } |
| if (errorCode == StaticWarningCode.UNDEFINED_IDENTIFIER) { |
| await _addFix_undefinedClassAccessor_useSimilar(); |
| await _addFix_createClass(); |
| await _addFix_createField(); |
| await _addFix_createGetter(); |
| await _addFix_createFunction_forFunctionType(); |
| await _addFix_createMixin(); |
| await _addFix_importLibrary_withType(); |
| await _addFix_importLibrary_withTopLevelVariable(); |
| await _addFix_createLocalVariable(); |
| } |
| if (errorCode == CompileTimeErrorCode.UNDEFINED_NAMED_PARAMETER || |
| errorCode == StaticWarningCode.UNDEFINED_NAMED_PARAMETER) { |
| await _addFix_addMissingNamedArgument(); |
| } |
| if (errorCode == StaticTypeWarningCode.UNDEFINED_METHOD_WITH_CONSTRUCTOR) { |
| await _addFix_undefinedMethodWithContructor(); |
| } |
| if (errorCode == StaticTypeWarningCode.ILLEGAL_ASYNC_RETURN_TYPE) { |
| await _addFix_illegalAsyncReturnType(); |
| } |
| if (errorCode == StaticTypeWarningCode.INSTANCE_ACCESS_TO_STATIC_MEMBER) { |
| await _addFix_useStaticAccess_method(); |
| await _addFix_useStaticAccess_property(); |
| } |
| if (errorCode == StaticTypeWarningCode.INVALID_ASSIGNMENT) { |
| await _addFix_addExplicitCast(); |
| await _addFix_changeTypeAnnotation(); |
| } |
| if (errorCode == StaticTypeWarningCode.INVOCATION_OF_NON_FUNCTION) { |
| await _addFix_removeParentheses_inGetterInvocation(); |
| } |
| if (errorCode == StaticTypeWarningCode.NON_BOOL_CONDITION) { |
| await _addFix_nonBoolCondition_addNotNull(); |
| } |
| if (errorCode == StaticTypeWarningCode.NON_TYPE_AS_TYPE_ARGUMENT) { |
| await _addFix_importLibrary_withType(); |
| await _addFix_createClass(); |
| await _addFix_createMixin(); |
| } |
| if (errorCode == StaticTypeWarningCode.UNDEFINED_FUNCTION) { |
| await _addFix_createClass(); |
| await _addFix_importLibrary_withFunction(); |
| await _addFix_importLibrary_withType(); |
| await _addFix_undefinedFunction_useSimilar(); |
| await _addFix_undefinedFunction_create(); |
| } |
| if (errorCode == StaticTypeWarningCode.UNDEFINED_GETTER) { |
| await _addFix_undefinedClassAccessor_useSimilar(); |
| await _addFix_createField(); |
| await _addFix_createGetter(); |
| await _addFix_createFunction_forFunctionType(); |
| // TODO(brianwilkerson) The following were added because fasta produces |
| // UNDEFINED_GETTER in places where analyzer produced UNDEFINED_IDENTIFIER |
| await _addFix_createClass(); |
| await _addFix_createMixin(); |
| await _addFix_createLocalVariable(); |
| await _addFix_importLibrary_withTopLevelVariable(); |
| await _addFix_importLibrary_withType(); |
| } |
| if (errorCode == HintCode.UNDEFINED_METHOD || |
| errorCode == StaticTypeWarningCode.UNDEFINED_METHOD) { |
| await _addFix_createClass(); |
| await _addFix_importLibrary_withFunction(); |
| await _addFix_importLibrary_withType(); |
| await _addFix_undefinedMethod_useSimilar(); |
| await _addFix_undefinedMethod_create(); |
| await _addFix_undefinedFunction_create(); |
| } |
| if (errorCode == StaticTypeWarningCode.UNDEFINED_SETTER) { |
| await _addFix_undefinedClassAccessor_useSimilar(); |
| await _addFix_createField(); |
| } |
| if (errorCode == CompileTimeErrorCode.UNDEFINED_NAMED_PARAMETER || |
| errorCode == StaticWarningCode.UNDEFINED_NAMED_PARAMETER) { |
| await _addFix_convertFlutterChild(); |
| await _addFix_convertFlutterChildren(); |
| } |
| if (errorCode == |
| CompileTimeErrorCode.INITIALIZING_FORMAL_FOR_NON_EXISTENT_FIELD) { |
| await _addFix_createField_initializingFormal(); |
| } |
| if (errorCode == CompileTimeErrorCode.CONST_INSTANCE_FIELD) { |
| await _addFix_addStatic(); |
| } |
| // lints |
| if (errorCode is LintCode) { |
| String name = errorCode.name; |
| if (name == LintNames.always_require_non_null_named_parameters) { |
| await _addFix_addRequiredAnnotation(); |
| } |
| if (name == LintNames.annotate_overrides) { |
| await _addFix_addOverrideAnnotation(); |
| } |
| if (name == LintNames.avoid_annotating_with_dynamic) { |
| await _addFix_removeTypeAnnotation(); |
| } |
| if (name == LintNames.avoid_empty_else) { |
| await _addFix_removeEmptyElse(); |
| } |
| if (name == LintNames.avoid_init_to_null) { |
| await _addFix_removeInitializer(); |
| } |
| if (name == LintNames.avoid_return_types_on_setters) { |
| await _addFix_removeTypeAnnotation(); |
| } |
| if (name == LintNames.avoid_types_on_closure_parameters) { |
| await _addFix_replaceWithIdentifier(); |
| } |
| if (name == LintNames.await_only_futures) { |
| await _addFix_removeAwait(); |
| } |
| if (name == LintNames.empty_catches) { |
| await _addFix_removeEmptyCatch(); |
| } |
| if (name == LintNames.empty_constructor_bodies) { |
| await _addFix_removeEmptyConstructorBody(); |
| } |
| if (name == LintNames.empty_statements) { |
| await _addFix_removeEmptyStatement(); |
| } |
| if (name == LintNames.non_constant_identifier_names) { |
| await _addFix_renameToCamelCase(); |
| } |
| if (name == LintNames.prefer_collection_literals) { |
| await _addFix_replaceWithLiteral(); |
| } |
| if (name == LintNames.prefer_conditional_assignment) { |
| await _addFix_replaceWithConditionalAssignment(); |
| } |
| if (errorCode.name == LintNames.prefer_const_declarations) { |
| await _addFix_replaceFinalWithConst(); |
| } |
| if (name == LintNames.prefer_final_fields) { |
| await _addFix_makeVariableFinal(); |
| } |
| if (name == LintNames.prefer_final_locals) { |
| await _addFix_makeVariableFinal(); |
| } |
| if (name == LintNames.prefer_is_not_empty) { |
| await _addFix_isNotEmpty(); |
| } |
| if (name == LintNames.type_init_formals) { |
| await _addFix_removeTypeAnnotation(); |
| } |
| if (name == LintNames.unnecessary_brace_in_string_interp) { |
| await _addFix_removeInterpolationBraces(); |
| } |
| if (name == LintNames.unnecessary_lambdas) { |
| await _addFix_replaceWithTearOff(); |
| } |
| if (name == LintNames.unnecessary_override) { |
| await _addFix_removeMethodDeclaration(); |
| } |
| if (name == LintNames.unnecessary_this) { |
| await _addFix_removeThisExpression(); |
| } |
| } |
| // done |
| return fixes; |
| } |
| |
| Future<void> _addFix_addAsync() async { |
| // TODO(brianwilkerson) Determine whether this await is necessary. |
| await null; |
| FunctionBody body = node.getAncestor((n) => n is FunctionBody); |
| if (body != null && body.keyword == null) { |
| TypeProvider typeProvider = await this.typeProvider; |
| DartChangeBuilder changeBuilder = new DartChangeBuilder(session); |
| await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) { |
| builder.convertFunctionFromSyncToAsync(body, typeProvider); |
| }); |
| _addFixFromBuilder(changeBuilder, DartFixKind.ADD_ASYNC); |
| } |
| } |
| |
| Future<void> _addFix_addExplicitCast() async { |
| // TODO(brianwilkerson) Determine whether this await is necessary. |
| await null; |
| if (coveredNode is! Expression) { |
| return; |
| } |
| Expression target = coveredNode; |
| DartType fromType = target.staticType; |
| DartType toType; |
| AstNode parent = target.parent; |
| if (parent is AssignmentExpression && target == parent.rightHandSide) { |
| toType = parent.leftHandSide.staticType; |
| } else if (parent is VariableDeclaration && target == parent.initializer) { |
| toType = parent.name.staticType; |
| } else { |
| // TODO(brianwilkerson) Handle function arguments. |
| return; |
| } |
| // TODO(brianwilkerson) Handle `toSet` in a manner similar to the below. |
| if (_isToListMethodInvocation(target)) { |
| Expression targetTarget = (target as MethodInvocation).target; |
| if (targetTarget != null) { |
| DartType targetTargetType = targetTarget.staticType; |
| if (_isDartCoreIterable(targetTargetType) || |
| _isDartCoreList(targetTargetType) || |
| _isDartCoreMap(targetTargetType) || |
| _isDartCoreSet(targetTargetType)) { |
| target = targetTarget; |
| fromType = targetTargetType; |
| } |
| } |
| } |
| if (target is AsExpression) { |
| // TODO(brianwilkerson) Consider updating the right operand. |
| return; |
| } |
| bool needsParentheses = target.precedence < 15; |
| if (((_isDartCoreIterable(fromType) || _isDartCoreList(fromType)) && |
| _isDartCoreList(toType)) || |
| (_isDartCoreSet(fromType) && _isDartCoreSet(toType))) { |
| if (_isCastMethodInvocation(target)) { |
| // TODO(brianwilkerson) Consider updating the type arguments to the |
| // `cast` invocation. |
| return; |
| } |
| DartChangeBuilder changeBuilder = new DartChangeBuilder(session); |
| await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) { |
| if (needsParentheses) { |
| builder.addSimpleInsertion(target.offset, '('); |
| } |
| builder.addInsertion(target.end, (DartEditBuilder builder) { |
| if (needsParentheses) { |
| builder.write(')'); |
| } |
| builder.write('.cast<'); |
| builder.writeType((toType as InterfaceType).typeArguments[0]); |
| builder.write('>()'); |
| }); |
| }); |
| _addFixFromBuilder(changeBuilder, DartFixKind.ADD_EXPLICIT_CAST); |
| } else if (_isDartCoreMap(fromType) && _isDartCoreMap(toType)) { |
| if (_isCastMethodInvocation(target)) { |
| // TODO(brianwilkerson) Consider updating the type arguments to the |
| // `cast` invocation. |
| return; |
| } |
| DartChangeBuilder changeBuilder = new DartChangeBuilder(session); |
| await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) { |
| if (needsParentheses) { |
| builder.addSimpleInsertion(target.offset, '('); |
| } |
| builder.addInsertion(target.end, (DartEditBuilder builder) { |
| if (needsParentheses) { |
| builder.write(')'); |
| } |
| builder.write('.cast<'); |
| builder.writeType((toType as InterfaceType).typeArguments[0]); |
| builder.write(', '); |
| builder.writeType((toType as InterfaceType).typeArguments[1]); |
| builder.write('>()'); |
| }); |
| }); |
| _addFixFromBuilder(changeBuilder, DartFixKind.ADD_EXPLICIT_CAST); |
| } else { |
| DartChangeBuilder changeBuilder = new DartChangeBuilder(session); |
| await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) { |
| if (needsParentheses) { |
| builder.addSimpleInsertion(target.offset, '('); |
| } |
| builder.addInsertion(target.end, (DartEditBuilder builder) { |
| if (needsParentheses) { |
| builder.write(')'); |
| } |
| builder.write(' as '); |
| builder.writeType(toType); |
| }); |
| }); |
| _addFixFromBuilder(changeBuilder, DartFixKind.ADD_EXPLICIT_CAST); |
| } |
| } |
| |
| Future<void> _addFix_addMissingNamedArgument() async { |
| // TODO(brianwilkerson) Determine whether this await is necessary. |
| await null; |
| // Prepare the name of the missing parameter. |
| if (this.node is! SimpleIdentifier) { |
| return; |
| } |
| SimpleIdentifier node = this.node; |
| String name = node.name; |
| |
| // We expect that the node is part of a NamedExpression. |
| if (node.parent?.parent is! NamedExpression) { |
| return; |
| } |
| NamedExpression namedExpression = node.parent.parent; |
| |
| // We should be in an ArgumentList. |
| if (namedExpression.parent is! ArgumentList) { |
| return; |
| } |
| AstNode argumentList = namedExpression.parent; |
| |
| // Prepare the invoked element. |
| var context = new _ExecutableParameters(astProvider, argumentList.parent); |
| if (context == null) { |
| return; |
| } |
| |
| // We cannot add named parameters when there are positional positional. |
| if (context.optionalPositional.isNotEmpty) { |
| return; |
| } |
| |
| Future<void> addParameter(int offset, String prefix, String suffix) async { |
| // TODO(brianwilkerson) Determine whether this await is necessary. |
| await null; |
| if (offset != null) { |
| var changeBuilder = new DartChangeBuilder(session); |
| await changeBuilder.addFileEdit(context.file, (builder) { |
| builder.addInsertion(offset, (builder) { |
| builder.write(prefix); |
| builder.writeParameterMatchingArgument( |
| namedExpression, 0, new Set<String>()); |
| builder.write(suffix); |
| }); |
| }); |
| _addFixFromBuilder( |
| changeBuilder, DartFixKind.ADD_MISSING_PARAMETER_NAMED, |
| args: [name]); |
| } |
| } |
| |
| if (context.named.isNotEmpty) { |
| var prevNode = await context.getParameterNode(context.named.last); |
| await addParameter(prevNode?.end, ', ', ''); |
| } else if (context.required.isNotEmpty) { |
| var prevNode = await context.getParameterNode(context.required.last); |
| await addParameter(prevNode?.end, ', {', '}'); |
| } else { |
| var parameterList = await context.getParameterList(); |
| await addParameter(parameterList?.leftParenthesis?.end, '{', '}'); |
| } |
| } |
| |
| Future<void> _addFix_addMissingParameter() async { |
| // TODO(brianwilkerson) Determine whether this await is necessary. |
| await null; |
| // The error is reported on ArgumentList. |
| if (node is! ArgumentList) { |
| return; |
| } |
| ArgumentList argumentList = node; |
| List<Expression> arguments = argumentList.arguments; |
| |
| // Prepare the invoked element. |
| var context = new _ExecutableParameters(astProvider, node.parent); |
| if (context == null) { |
| return; |
| } |
| |
| // prepare the argument to add a new parameter for |
| int numRequired = context.required.length; |
| if (numRequired >= arguments.length) { |
| return; |
| } |
| Expression argument = arguments[numRequired]; |
| |
| Future<void> addParameter( |
| FixKind kind, int offset, String prefix, String suffix) async { |
| // TODO(brianwilkerson) Determine whether this await is necessary. |
| await null; |
| if (offset != null) { |
| var changeBuilder = new DartChangeBuilder(session); |
| await changeBuilder.addFileEdit(context.file, (builder) { |
| builder.addInsertion(offset, (builder) { |
| builder.write(prefix); |
| builder.writeParameterMatchingArgument( |
| argument, numRequired, new Set<String>()); |
| builder.write(suffix); |
| }); |
| }); |
| _addFixFromBuilder(changeBuilder, kind); |
| } |
| } |
| |
| // Suggest adding a required parameter. |
| { |
| var kind = DartFixKind.ADD_MISSING_PARAMETER_REQUIRED; |
| if (context.required.isNotEmpty) { |
| var prevNode = await context.getParameterNode(context.required.last); |
| await addParameter(kind, prevNode?.end, ', ', ''); |
| } else { |
| var parameterList = await context.getParameterList(); |
| var offset = parameterList?.leftParenthesis?.end; |
| var suffix = context.executable.parameters.isNotEmpty ? ', ' : ''; |
| await addParameter(kind, offset, '', suffix); |
| } |
| } |
| |
| // Suggest adding the first optional positional parameter. |
| if (context.optionalPositional.isEmpty && context.named.isEmpty) { |
| var kind = DartFixKind.ADD_MISSING_PARAMETER_POSITIONAL; |
| var prefix = context.required.isNotEmpty ? ', [' : '['; |
| if (context.required.isNotEmpty) { |
| var prevNode = await context.getParameterNode(context.required.last); |
| await addParameter(kind, prevNode?.end, prefix, ']'); |
| } else { |
| var parameterList = await context.getParameterList(); |
| var offset = parameterList?.leftParenthesis?.end; |
| await addParameter(kind, offset, prefix, ']'); |
| } |
| } |
| } |
| |
| Future<void> _addFix_addMissingRequiredArgument() async { |
| // TODO(brianwilkerson) Determine whether this await is necessary. |
| await null; |
| InstanceCreationExpression creation; |
| Element targetElement; |
| ArgumentList argumentList; |
| |
| if (node is SimpleIdentifier) { |
| AstNode invocation = node.parent; |
| if (invocation is MethodInvocation) { |
| targetElement = invocation.methodName.staticElement; |
| argumentList = invocation.argumentList; |
| } else { |
| creation = |
| invocation.getAncestor((p) => p is InstanceCreationExpression); |
| if (creation != null) { |
| targetElement = creation.staticElement; |
| argumentList = creation.argumentList; |
| } |
| } |
| } |
| |
| if (targetElement is ExecutableElement) { |
| // Format: "Missing required argument 'foo" |
| List<String> messageParts = error.message.split("'"); |
| if (messageParts.length < 2) { |
| return; |
| } |
| String missingParameterName = messageParts[1]; |
| |
| ParameterElement missingParameter = targetElement.parameters.firstWhere( |
| (p) => p.name == missingParameterName, |
| orElse: () => null); |
| if (missingParameter == null) { |
| return; |
| } |
| |
| int offset; |
| bool hasTrailingComma = false; |
| List<Expression> arguments = argumentList.arguments; |
| if (arguments.isEmpty) { |
| offset = argumentList.leftParenthesis.end; |
| } else { |
| Expression lastArgument = arguments.last; |
| offset = lastArgument.end; |
| hasTrailingComma = lastArgument.endToken.next.type == TokenType.COMMA; |
| } |
| |
| DartChangeBuilder changeBuilder = new DartChangeBuilder(session); |
| await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) { |
| builder.addInsertion(offset, (DartEditBuilder builder) { |
| if (arguments.isNotEmpty) { |
| builder.write(', '); |
| } |
| String defaultValue = |
| getDefaultStringParameterValue(missingParameter); |
| builder.write('$missingParameterName: $defaultValue'); |
| // Insert a trailing comma after Flutter instance creation params. |
| if (!hasTrailingComma && flutter.isWidgetExpression(creation)) { |
| builder.write(','); |
| } |
| }); |
| }); |
| _addFixFromBuilder( |
| changeBuilder, DartFixKind.ADD_MISSING_REQUIRED_ARGUMENT, |
| args: [missingParameterName]); |
| } |
| } |
| |
| Future<void> _addFix_addOverrideAnnotation() async { |
| // TODO(brianwilkerson) Determine whether this await is necessary. |
| await null; |
| ClassMember member = node.getAncestor((n) => n is ClassMember); |
| if (member == null) { |
| return; |
| } |
| |
| //TODO(pq): migrate annotation edit building to change_builder |
| |
| // Handle doc comments. |
| Token token = member.beginToken; |
| if (token is CommentToken) { |
| token = (token as CommentToken).parent; |
| } |
| |
| Position exitPosition = new Position(file, token.offset - 1); |
| String indent = utils.getIndent(1); |
| DartChangeBuilder changeBuilder = new DartChangeBuilder(session); |
| await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) { |
| builder.addSimpleReplacement( |
| range.startLength(token, 0), '@override$eol$indent'); |
| }); |
| changeBuilder.setSelection(exitPosition); |
| _addFixFromBuilder(changeBuilder, DartFixKind.LINT_ADD_OVERRIDE); |
| } |
| |
| Future<void> _addFix_addRequiredAnnotation() async { |
| // TODO(brianwilkerson) Determine whether this await is necessary. |
| await null; |
| DartChangeBuilder changeBuilder = new DartChangeBuilder(session); |
| await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) { |
| builder.addSimpleInsertion(node.parent.offset, '@required '); |
| }); |
| _addFixFromBuilder(changeBuilder, DartFixKind.LINT_ADD_REQUIRED); |
| } |
| |
| Future<void> _addFix_addStatic() async { |
| // TODO(brianwilkerson) Determine whether this await is necessary. |
| await null; |
| FieldDeclaration declaration = |
| node.getAncestor((n) => n is FieldDeclaration); |
| DartChangeBuilder changeBuilder = new DartChangeBuilder(session); |
| await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) { |
| builder.addSimpleInsertion(declaration.offset, 'static '); |
| }); |
| _addFixFromBuilder(changeBuilder, DartFixKind.ADD_STATIC); |
| } |
| |
| Future<void> _addFix_boolInsteadOfBoolean() async { |
| // TODO(brianwilkerson) Determine whether this await is necessary. |
| await null; |
| DartChangeBuilder changeBuilder = new DartChangeBuilder(session); |
| await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) { |
| builder.addSimpleReplacement(range.error(error), 'bool'); |
| }); |
| _addFixFromBuilder(changeBuilder, DartFixKind.REPLACE_BOOLEAN_WITH_BOOL); |
| } |
| |
| Future<void> _addFix_canBeNullAfterNullAware() async { |
| // TODO(brianwilkerson) Determine whether this await is necessary. |
| await null; |
| AstNode node = coveredNode; |
| if (node is Expression) { |
| DartChangeBuilder changeBuilder = new DartChangeBuilder(session); |
| await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) { |
| AstNode parent = node.parent; |
| while (parent != null) { |
| if (parent is MethodInvocation && parent.target == node) { |
| builder.addSimpleReplacement(range.token(parent.operator), '?.'); |
| } else if (parent is PropertyAccess && parent.target == node) { |
| builder.addSimpleReplacement(range.token(parent.operator), '?.'); |
| } else { |
| break; |
| } |
| node = parent; |
| parent = node.parent; |
| } |
| }); |
| _addFixFromBuilder(changeBuilder, DartFixKind.REPLACE_WITH_NULL_AWARE); |
| } |
| } |
| |
| Future<void> _addFix_changeToNearestPreciseValue() async { |
| IntegerLiteral integer = node; |
| String lexeme = integer.literal.lexeme; |
| BigInt precise = BigInt.from(IntegerLiteralImpl.nearestValidDouble(lexeme)); |
| String correction = lexeme.toLowerCase().contains('x') |
| ? '0x${precise.toRadixString(16).toUpperCase()}' |
| : precise.toString(); |
| DartChangeBuilder changeBuilder = new DartChangeBuilder(session); |
| await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) { |
| builder.addSimpleReplacement(range.node(integer), correction); |
| }); |
| _addFixFromBuilder( |
| changeBuilder, DartFixKind.CHANGE_TO_NEAREST_PRECISE_VALUE, |
| args: [correction]); |
| } |
| |
| Future<void> _addFix_changeTypeAnnotation() async { |
| // TODO(brianwilkerson) Determine whether this await is necessary. |
| await null; |
| AstNode declaration = coveredNode.parent; |
| if (declaration is VariableDeclaration && |
| declaration.initializer == coveredNode) { |
| AstNode variableList = declaration.parent; |
| if (variableList is VariableDeclarationList && |
| variableList.variables.length == 1) { |
| TypeAnnotation typeNode = variableList.type; |
| if (typeNode != null) { |
| Expression initializer = coveredNode; |
| DartType newType = initializer.staticType; |
| if (newType is InterfaceType || newType is FunctionType) { |
| DartChangeBuilder changeBuilder = new DartChangeBuilder(session); |
| await changeBuilder.addFileEdit(file, |
| (DartFileEditBuilder builder) { |
| builder.addReplacement(range.node(typeNode), |
| (DartEditBuilder builder) { |
| builder.writeType(newType); |
| }); |
| }); |
| _addFixFromBuilder( |
| changeBuilder, DartFixKind.CHANGE_TYPE_ANNOTATION, args: [ |
| resolutionMap.typeForTypeName(typeNode), |
| newType.displayName |
| ]); |
| } |
| } |
| } |
| } |
| } |
| |
| Future<void> _addFix_convertFlutterChild() async { |
| // TODO(brianwilkerson) Determine whether this await is necessary. |
| await null; |
| NamedExpression named = flutter.findNamedExpression(node, 'child'); |
| if (named == null) { |
| return; |
| } |
| |
| // child: widget |
| Expression expression = named.expression; |
| if (flutter.isWidgetExpression(expression)) { |
| DartChangeBuilder changeBuilder = new DartChangeBuilder(session); |
| await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) { |
| flutter.convertChildToChildren2( |
| builder, |
| expression, |
| named, |
| eol, |
| utils.getNodeText, |
| utils.getLinePrefix, |
| utils.getIndent, |
| utils.getText, |
| range.node); |
| }); |
| _addFixFromBuilder(changeBuilder, DartFixKind.CONVERT_FLUTTER_CHILD); |
| return; |
| } |
| |
| // child: [widget1, widget2] |
| if (expression is ListLiteral && |
| expression.elements.every(flutter.isWidgetExpression)) { |
| DartChangeBuilder changeBuilder = new DartChangeBuilder(session); |
| await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) { |
| builder.addSimpleReplacement(range.node(named.name), 'children:'); |
| if (expression.typeArguments == null) { |
| builder.addSimpleInsertion(expression.offset, '<Widget>'); |
| } |
| }); |
| _addFixFromBuilder(changeBuilder, DartFixKind.CONVERT_FLUTTER_CHILD); |
| } |
| } |
| |
| Future<void> _addFix_convertFlutterChildren() async { |
| // TODO(brianwilkerson) Determine whether this await is necessary. |
| await null; |
| AstNode node = this.node; |
| if (node is SimpleIdentifier && |
| node.name == 'children' && |
| node.parent?.parent is NamedExpression) { |
| NamedExpression named = node.parent?.parent; |
| Expression expression = named.expression; |
| if (expression is ListLiteral && expression.elements.length == 1) { |
| Expression widget = expression.elements[0]; |
| if (flutter.isWidgetExpression(widget)) { |
| String widgetText = utils.getNodeText(widget); |
| String indentOld = utils.getLinePrefix(widget.offset); |
| String indentNew = utils.getLinePrefix(named.offset); |
| widgetText = _replaceSourceIndent(widgetText, indentOld, indentNew); |
| |
| var builder = new DartChangeBuilder(session); |
| await builder.addFileEdit(file, (builder) { |
| builder.addReplacement(range.node(named), (builder) { |
| builder.write('child: '); |
| builder.write(widgetText); |
| }); |
| }); |
| _addFixFromBuilder(builder, DartFixKind.CONVERT_FLUTTER_CHILDREN); |
| } |
| } |
| } |
| } |
| |
| Future<void> _addFix_convertToNamedArgument() async { |
| // TODO(brianwilkerson) Determine whether this await is necessary. |
| await null; |
| var argumentList = this.node; |
| if (argumentList is ArgumentList) { |
| // Prepare ExecutableElement. |
| ExecutableElement executable; |
| var parent = argumentList.parent; |
| if (parent is InstanceCreationExpression) { |
| executable = parent.staticElement; |
| } else if (parent is MethodInvocation) { |
| executable = parent.methodName.staticElement; |
| } |
| if (executable == null) { |
| return; |
| } |
| |
| // Prepare named parameters. |
| int numberOfPositionalParameters = 0; |
| var namedParameters = <ParameterElement>[]; |
| for (var parameter in executable.parameters) { |
| if (parameter.isNamed) { |
| namedParameters.add(parameter); |
| } else { |
| numberOfPositionalParameters++; |
| } |
| } |
| if (argumentList.arguments.length <= numberOfPositionalParameters) { |
| return; |
| } |
| |
| // Find named parameters for extra arguments. |
| var argumentToParameter = <Expression, ParameterElement>{}; |
| Iterable<Expression> extraArguments = |
| argumentList.arguments.skip(numberOfPositionalParameters); |
| for (var argument in extraArguments) { |
| if (argument is! NamedExpression) { |
| ParameterElement uniqueNamedParameter = null; |
| for (var namedParameter in namedParameters) { |
| if (typeSystem.isSubtypeOf( |
| argument.staticType, namedParameter.type)) { |
| if (uniqueNamedParameter == null) { |
| uniqueNamedParameter = namedParameter; |
| } else { |
| uniqueNamedParameter = null; |
| break; |
| } |
| } |
| } |
| if (uniqueNamedParameter != null) { |
| argumentToParameter[argument] = uniqueNamedParameter; |
| namedParameters.remove(uniqueNamedParameter); |
| } |
| } |
| } |
| if (argumentToParameter.isEmpty) { |
| return; |
| } |
| |
| DartChangeBuilder changeBuilder = new DartChangeBuilder(session); |
| await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) { |
| for (var argument in argumentToParameter.keys) { |
| var parameter = argumentToParameter[argument]; |
| builder.addSimpleInsertion(argument.offset, '${parameter.name}: '); |
| } |
| }); |
| _addFixFromBuilder(changeBuilder, DartFixKind.CONVERT_TO_NAMED_ARGUMENTS); |
| } |
| } |
| |
| Future<void> _addFix_createClass() async { |
| // TODO(brianwilkerson) Determine whether this await is necessary. |
| await null; |
| Element prefixElement = null; |
| String name = null; |
| SimpleIdentifier nameNode; |
| if (node is SimpleIdentifier) { |
| AstNode parent = node.parent; |
| if (parent is PrefixedIdentifier) { |
| PrefixedIdentifier prefixedIdentifier = parent; |
| prefixElement = prefixedIdentifier.prefix.staticElement; |
| if (prefixElement == null) { |
| return; |
| } |
| parent = prefixedIdentifier.parent; |
| nameNode = prefixedIdentifier.identifier; |
| name = prefixedIdentifier.identifier.name; |
| } else { |
| nameNode = node; |
| name = nameNode.name; |
| } |
| if (!_mayBeTypeIdentifier(nameNode)) { |
| return; |
| } |
| } else { |
| return; |
| } |
| // prepare environment |
| Element targetUnit; |
| String prefix = ''; |
| String suffix = ''; |
| int offset = -1; |
| String filePath; |
| if (prefixElement == null) { |
| targetUnit = unitElement; |
| CompilationUnitMember enclosingMember = node.getAncestor((node) => |
| node is CompilationUnitMember && node.parent is CompilationUnit); |
| if (enclosingMember == null) { |
| return; |
| } |
| offset = enclosingMember.end; |
| filePath = file; |
| prefix = '$eol$eol'; |
| } else { |
| for (ImportElement import in unitLibraryElement.imports) { |
| if (prefixElement is PrefixElement && import.prefix == prefixElement) { |
| LibraryElement library = import.importedLibrary; |
| if (library != null) { |
| targetUnit = library.definingCompilationUnit; |
| Source targetSource = targetUnit.source; |
| try { |
| offset = targetSource.contents.data.length; |
| filePath = targetSource.fullName; |
| prefix = '$eol'; |
| suffix = '$eol'; |
| } on FileSystemException { |
| // If we can't read the file to get the offset, then we can't |
| // create a fix. |
| } |
| break; |
| } |
| } |
| } |
| } |
| if (offset < 0) { |
| return; |
| } |
| DartChangeBuilder changeBuilder = new DartChangeBuilder(session); |
| await changeBuilder.addFileEdit(filePath, (DartFileEditBuilder builder) { |
| builder.addInsertion(offset, (DartEditBuilder builder) { |
| builder.write(prefix); |
| builder.writeClassDeclaration(name, nameGroupName: 'NAME'); |
| builder.write(suffix); |
| }); |
| if (prefixElement == null) { |
| builder.addLinkedPosition(range.node(node), 'NAME'); |
| } |
| }); |
| _addFixFromBuilder(changeBuilder, DartFixKind.CREATE_CLASS, args: [name]); |
| } |
| |
| /** |
| * Here we handle cases when there are no constructors in a class, and the |
| * class has uninitialized final fields. |
| */ |
| Future<void> _addFix_createConstructor_forUninitializedFinalFields() async { |
| // TODO(brianwilkerson) Determine whether this await is necessary. |
| await null; |
| if (node is! SimpleIdentifier || node.parent is! VariableDeclaration) { |
| return; |
| } |
| |
| ClassDeclaration classDeclaration = |
| node.getAncestor((node) => node is ClassDeclaration); |
| if (classDeclaration == null) { |
| return; |
| } |
| String className = classDeclaration.name.name; |
| InterfaceType superType = classDeclaration.declaredElement.supertype; |
| |
| // prepare names of uninitialized final fields |
| List<String> fieldNames = <String>[]; |
| for (ClassMember member in classDeclaration.members) { |
| if (member is FieldDeclaration) { |
| VariableDeclarationList variableList = member.fields; |
| if (variableList.isFinal) { |
| fieldNames.addAll(variableList.variables |
| .where((v) => v.initializer == null) |
| .map((v) => v.name.name)); |
| } |
| } |
| } |
| // prepare location for a new constructor |
| ClassMemberLocation targetLocation = |
| utils.prepareNewConstructorLocation(classDeclaration); |
| |
| DartChangeBuilder changeBuilder = new DartChangeBuilder(session); |
| if (flutter.isExactlyStatelessWidgetType(superType) || |
| flutter.isExactlyStatefulWidgetType(superType)) { |
| // Specialize for Flutter widgets. |
| ClassElement keyClass = |
| await sessionHelper.getClass(flutter.WIDGETS_LIBRARY_URI, 'Key'); |
| await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) { |
| builder.addInsertion(targetLocation.offset, (DartEditBuilder builder) { |
| builder.write(targetLocation.prefix); |
| builder.write('const '); |
| builder.write(className); |
| builder.write('({'); |
| builder.writeType(keyClass.type); |
| builder.write(' key'); |
| |
| List<String> childrenFields = []; |
| for (String fieldName in fieldNames) { |
| if (fieldName == 'child' || fieldName == 'children') { |
| childrenFields.add(fieldName); |
| continue; |
| } |
| builder.write(', this.'); |
| builder.write(fieldName); |
| } |
| for (String fieldName in childrenFields) { |
| builder.write(', this.'); |
| builder.write(fieldName); |
| } |
| |
| builder.write('}) : super(key: key);'); |
| builder.write(targetLocation.suffix); |
| }); |
| }); |
| } else { |
| await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) { |
| builder.addInsertion(targetLocation.offset, (DartEditBuilder builder) { |
| builder.write(targetLocation.prefix); |
| builder.writeConstructorDeclaration(className, |
| fieldNames: fieldNames); |
| builder.write(targetLocation.suffix); |
| }); |
| }); |
| } |
| _addFixFromBuilder( |
| changeBuilder, DartFixKind.CREATE_CONSTRUCTOR_FOR_FINAL_FIELDS); |
| } |
| |
| Future<void> _addFix_createConstructor_insteadOfSyntheticDefault() async { |
| // TODO(brianwilkerson) Determine whether this await is necessary. |
| await null; |
| if (node is! ArgumentList) { |
| return; |
| } |
| if (node.parent is! InstanceCreationExpression) { |
| return; |
| } |
| InstanceCreationExpression instanceCreation = node.parent; |
| ConstructorName constructorName = instanceCreation.constructorName; |
| // should be synthetic default constructor |
| ConstructorElement constructorElement = constructorName.staticElement; |
| if (constructorElement == null || |
| !constructorElement.isDefaultConstructor || |
| !constructorElement.isSynthetic) { |
| return; |
| } |
| // prepare target |
| if (constructorElement.enclosingElement is! ClassElement) { |
| return; |
| } |
| ClassElement targetElement = constructorElement.enclosingElement; |
| // prepare location for a new constructor |
| AstNode targetTypeNode = getParsedClassElementNode(targetElement); |
| if (targetTypeNode is! ClassDeclaration) { |
| return; |
| } |
| ClassMemberLocation targetLocation = |
| utils.prepareNewConstructorLocation(targetTypeNode); |
| Source targetSource = targetElement.source; |
| String targetFile = targetSource.fullName; |
| DartChangeBuilder changeBuilder = new DartChangeBuilder(session); |
| await changeBuilder.addFileEdit(targetFile, (DartFileEditBuilder builder) { |
| builder.addInsertion(targetLocation.offset, (DartEditBuilder builder) { |
| builder.write(targetLocation.prefix); |
| builder.writeConstructorDeclaration(targetElement.name, |
| argumentList: instanceCreation.argumentList); |
| builder.write(targetLocation.suffix); |
| }); |
| }); |
| _addFixFromBuilder(changeBuilder, DartFixKind.CREATE_CONSTRUCTOR, |
| args: [constructorName]); |
| } |
| |
| Future<void> _addFix_createConstructor_named() async { |
| // TODO(brianwilkerson) Determine whether this await is necessary. |
| await null; |
| 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 target interface type |
| DartType targetType = constructorName.type.type; |
| if (targetType is! InterfaceType) { |
| return; |
| } |
| // prepare location for a new constructor |
| ClassElement targetElement = targetType.element as ClassElement; |
| AstNode targetTypeNode = getParsedClassElementNode(targetElement); |
| if (targetTypeNode is! ClassDeclaration) { |
| return; |
| } |
| ClassMemberLocation targetLocation = |
| utils.prepareNewConstructorLocation(targetTypeNode); |
| String targetFile = targetElement.source.fullName; |
| DartChangeBuilder changeBuilder = new DartChangeBuilder(session); |
| await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) { |
| builder.addInsertion(targetLocation.offset, (DartEditBuilder builder) { |
| builder.write(targetLocation.prefix); |
| builder.writeConstructorDeclaration(targetElement.name, |
| argumentList: instanceCreation.argumentList, |
| constructorName: name, |
| constructorNameGroupName: 'NAME'); |
| builder.write(targetLocation.suffix); |
| }); |
| if (targetFile == file) { |
| builder.addLinkedPosition(range.node(name), 'NAME'); |
| } |
| }); |
| _addFixFromBuilder(changeBuilder, DartFixKind.CREATE_CONSTRUCTOR, |
| args: [constructorName]); |
| } |
| |
| Future<void> _addFix_createConstructorSuperExplicit() async { |
| // TODO(brianwilkerson) Determine whether this await is necessary. |
| await null; |
| if (node.parent is! ConstructorDeclaration || |
| node.parent.parent is! ClassDeclaration) { |
| return; |
| } |
| ConstructorDeclaration targetConstructor = |
| node.parent as ConstructorDeclaration; |
| ClassDeclaration targetClassNode = |
| targetConstructor.parent as ClassDeclaration; |
| ClassElement targetClassElement = targetClassNode.declaredElement; |
| InterfaceType superType = targetClassElement.supertype; |
| // add proposals for all super constructors |
| for (ConstructorElement superConstructor in superType.constructors) { |
| String constructorName = superConstructor.name; |
| // skip private |
| if (Identifier.isPrivateName(constructorName)) { |
| continue; |
| } |
| List<ConstructorInitializer> initializers = |
| targetConstructor.initializers; |
| int insertOffset; |
| String prefix; |
| if (initializers.isEmpty) { |
| insertOffset = targetConstructor.parameters.end; |
| prefix = ' : '; |
| } else { |
| ConstructorInitializer lastInitializer = |
| initializers[initializers.length - 1]; |
| insertOffset = lastInitializer.end; |
| prefix = ', '; |
| } |
| String proposalName = _getConstructorProposalName(superConstructor); |
| DartChangeBuilder changeBuilder = new DartChangeBuilder(session); |
| await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) { |
| builder.addInsertion(insertOffset, (DartEditBuilder builder) { |
| builder.write(prefix); |
| // add super constructor name |
| builder.write('super'); |
| if (!isEmpty(constructorName)) { |
| builder.write('.'); |
| builder.addSimpleLinkedEdit('NAME', constructorName); |
| } |
| // add arguments |
| builder.write('('); |
| bool firstParameter = true; |
| for (ParameterElement parameter in superConstructor.parameters) { |
| // skip non-required parameters |
| if (parameter.isOptional) { |
| break; |
| } |
| // comma |
| if (firstParameter) { |
| firstParameter = false; |
| } else { |
| builder.write(', '); |
| } |
| // default value |
| builder.addSimpleLinkedEdit( |
| parameter.name, getDefaultValueCode(parameter.type)); |
| } |
| builder.write(')'); |
| }); |
| }); |
| _addFixFromBuilder( |
| changeBuilder, DartFixKind.ADD_SUPER_CONSTRUCTOR_INVOCATION, |
| args: [proposalName]); |
| } |
| } |
| |
| Future<void> _addFix_createConstructorSuperImplicit() async { |
| // TODO(brianwilkerson) Determine whether this await is necessary. |
| await null; |
| ClassDeclaration targetClassNode = |
| node.getAncestor((parent) => parent is ClassDeclaration); |
| ClassElement targetClassElement = targetClassNode.declaredElement; |
| InterfaceType superType = targetClassElement.supertype; |
| String targetClassName = targetClassElement.name; |
| // add proposals for all super constructors |
| for (ConstructorElement superConstructor in superType.constructors) { |
| superConstructor = ConstructorMember.from(superConstructor, superType); |
| String constructorName = superConstructor.name; |
| // skip private |
| if (Identifier.isPrivateName(constructorName)) { |
| continue; |
| } |
| // prepare parameters and arguments |
| Iterable<ParameterElement> requiredParameters = superConstructor |
| .parameters |
| .where((parameter) => parameter.isNotOptional); |
| // add proposal |
| ClassMemberLocation targetLocation = |
| utils.prepareNewConstructorLocation(targetClassNode); |
| String proposalName = _getConstructorProposalName(superConstructor); |
| DartChangeBuilder changeBuilder = new DartChangeBuilder(session); |
| await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) { |
| builder.addInsertion(targetLocation.offset, (DartEditBuilder builder) { |
| void writeParameters(bool includeType) { |
| bool firstParameter = true; |
| for (ParameterElement parameter in requiredParameters) { |
| if (firstParameter) { |
| firstParameter = false; |
| } else { |
| builder.write(', '); |
| } |
| String parameterName = parameter.displayName; |
| if (parameterName.length > 1 && parameterName.startsWith('_')) { |
| parameterName = parameterName.substring(1); |
| } |
| if (includeType && builder.writeType(parameter.type)) { |
| builder.write(' '); |
| } |
| builder.write(parameterName); |
| } |
| } |
| |
| builder.write(targetLocation.prefix); |
| builder.write(targetClassName); |
| if (!constructorName.isEmpty) { |
| builder.write('.'); |
| builder.addSimpleLinkedEdit('NAME', constructorName); |
| } |
| builder.write('('); |
| writeParameters(true); |
| builder.write(') : super'); |
| if (!constructorName.isEmpty) { |
| builder.write('.'); |
| builder.addSimpleLinkedEdit('NAME', constructorName); |
| } |
| builder.write('('); |
| writeParameters(false); |
| builder.write(');'); |
| builder.write(targetLocation.suffix); |
| }); |
| }); |
| _addFixFromBuilder(changeBuilder, DartFixKind.CREATE_CONSTRUCTOR_SUPER, |
| args: [proposalName]); |
| } |
| } |
| |
| Future<void> _addFix_createField() async { |
| // TODO(brianwilkerson) Determine whether this await is necessary. |
| await null; |
| if (node is! SimpleIdentifier) { |
| return; |
| } |
| SimpleIdentifier nameNode = node; |
| String name = nameNode.name; |
| // prepare target Expression |
| Expression target; |
| { |
| AstNode nameParent = nameNode.parent; |
| if (nameParent is PrefixedIdentifier) { |
| target = nameParent.prefix; |
| } else if (nameParent is PropertyAccess) { |
| target = nameParent.realTarget; |
| } |
| } |
| // prepare target ClassElement |
| bool staticModifier = false; |
| ClassElement targetClassElement; |
| if (target != null) { |
| // prepare target interface type |
| DartType targetType = target.staticType; |
| if (targetType is! InterfaceType) { |
| return; |
| } |
| targetClassElement = targetType.element; |
| // maybe static |
| if (target is Identifier) { |
| Identifier targetIdentifier = target; |
| Element targetElement = targetIdentifier.staticElement; |
| if (targetElement == null) { |
| return; |
| } |
| staticModifier = targetElement.kind == ElementKind.CLASS; |
| } |
| } else { |
| targetClassElement = getEnclosingClassElement(node); |
| if (targetClassElement == null) { |
| return; |
| } |
| staticModifier = _inStaticContext(); |
| } |
| if (targetClassElement.librarySource.isInSystemLibrary) { |
| return; |
| } |
| utils.targetClassElement = targetClassElement; |
| // prepare target ClassDeclaration |
| AstNode targetTypeNode = getParsedClassElementNode(targetClassElement); |
| if (targetTypeNode is! ClassDeclaration) { |
| return; |
| } |
| ClassDeclaration targetClassNode = targetTypeNode; |
| // prepare location |
| ClassMemberLocation targetLocation = |
| _getUtilsFor(targetClassNode).prepareNewFieldLocation(targetClassNode); |
| // build field source |
| Source targetSource = targetClassElement.source; |
| String targetFile = targetSource.fullName; |
| DartChangeBuilder changeBuilder = new DartChangeBuilder(session); |
| await changeBuilder.addFileEdit(targetFile, (DartFileEditBuilder builder) { |
| Expression fieldTypeNode = climbPropertyAccess(nameNode); |
| DartType fieldType = _inferUndefinedExpressionType(fieldTypeNode); |
| 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]); |
| } |
| |
| Future<void> _addFix_createField_initializingFormal() async { |
| // TODO(brianwilkerson) Determine whether this await is necessary. |
| await null; |
| // |
| // Ensure that we are in an initializing formal parameter. |
| // |
| FieldFormalParameter parameter = |
| node.getAncestor((node) => node is FieldFormalParameter); |
| if (parameter == null) { |
| return; |
| } |
| ClassDeclaration targetClassNode = |
| parameter.getAncestor((node) => node is ClassDeclaration); |
| if (targetClassNode == null) { |
| return; |
| } |
| SimpleIdentifier nameNode = parameter.identifier; |
| String name = nameNode.name; |
| ClassMemberLocation targetLocation = |
| utils.prepareNewFieldLocation(targetClassNode); |
| // |
| // Add proposal. |
| // |
| DartChangeBuilder changeBuilder = new DartChangeBuilder(session); |
| await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) { |
| DartType fieldType = parameter.type?.type; |
| builder.addInsertion(targetLocation.offset, (DartEditBuilder builder) { |
| builder.write(targetLocation.prefix); |
| builder.writeFieldDeclaration(name, |
| nameGroupName: 'NAME', type: fieldType, typeGroupName: 'TYPE'); |
| builder.write(targetLocation.suffix); |
| }); |
| }); |
| _addFixFromBuilder(changeBuilder, DartFixKind.CREATE_FIELD, args: [name]); |
| } |
| |
| Future<void> _addFix_createFunction_forFunctionType() async { |
| // TODO(brianwilkerson) Determine whether this await is necessary. |
| await null; |
| 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.staticType; |
| 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?.declaredElement; |
| argument = nameNode; |
| } |
| } |
| argument = stepUpNamedExpression(argument); |
| // should be argument of some invocation |
| ParameterElement parameterElement = argument.staticParameterElement; |
| if (parameterElement == null) { |
| return; |
| } |
| // should be parameter of function type |
| DartType parameterType = parameterElement.type; |
| if (parameterType is InterfaceType && parameterType.isDartCoreFunction) { |
| ExecutableElement element = new MethodElementImpl('', -1); |
| parameterType = new FunctionTypeImpl(element); |
| } |
| if (parameterType is! FunctionType) { |
| return; |
| } |
| FunctionType functionType = parameterType as FunctionType; |
| // add proposal |
| if (targetElement != null) { |
| await _addProposal_createFunction_method(targetElement, functionType); |
| } else { |
| await _addProposal_createFunction_function(functionType); |
| } |
| } |
| } |
| |
| Future<void> _addFix_createGetter() async { |
| // TODO(brianwilkerson) Determine whether this await is necessary. |
| await null; |
| if (node is! SimpleIdentifier) { |
| return; |
| } |
| SimpleIdentifier nameNode = node; |
| String name = nameNode.name; |
| if (!nameNode.inGetterContext()) { |
| return; |
| } |
| // prepare target Expression |
| Expression target; |
| { |
| AstNode nameParent = nameNode.parent; |
| if (nameParent is PrefixedIdentifier) { |
| target = nameParent.prefix; |
| } else if (nameParent is PropertyAccess) { |
| target = nameParent.realTarget; |
| } |
| } |
| // prepare target ClassElement |
| bool staticModifier = false; |
| ClassElement targetClassElement; |
| if (target != null) { |
| // prepare target interface type |
| DartType targetType = target.staticType; |
| if (targetType is! InterfaceType) { |
| return; |
| } |
| targetClassElement = targetType.element; |
| // maybe static |
| if (target is Identifier) { |
| Identifier targetIdentifier = target; |
| Element targetElement = targetIdentifier.staticElement; |
| staticModifier = targetElement?.kind == ElementKind.CLASS; |
| } |
| } else { |
| targetClassElement = getEnclosingClassElement(node); |
| if (targetClassElement == null) { |
| return; |
| } |
| staticModifier = _inStaticContext(); |
| } |
| if (targetClassElement.librarySource.isInSystemLibrary) { |
| return; |
| } |
| utils.targetClassElement = targetClassElement; |
| // prepare target ClassDeclaration |
| AstNode targetTypeNode = getParsedClassElementNode(targetClassElement); |
| if (targetTypeNode is! ClassDeclaration) { |
| return; |
| } |
| ClassDeclaration targetClassNode = targetTypeNode; |
| // prepare location |
| ClassMemberLocation targetLocation = |
| _getUtilsFor(targetClassNode).prepareNewGetterLocation(targetClassNode); |
| // build method source |
| Source targetSource = targetClassElement.source; |
| String targetFile = targetSource.fullName; |
| DartChangeBuilder changeBuilder = new DartChangeBuilder(session); |
| await changeBuilder.addFileEdit(targetFile, (DartFileEditBuilder builder) { |
| builder.addInsertion(targetLocation.offset, (DartEditBuilder builder) { |
| Expression fieldTypeNode = climbPropertyAccess(nameNode); |
| DartType fieldType = _inferUndefinedExpressionType(fieldTypeNode); |
| builder.write(targetLocation.prefix); |
| builder.writeGetterDeclaration(name, |
| isStatic: staticModifier, |
| nameGroupName: 'NAME', |
| returnType: fieldType, |
| returnTypeGroupName: 'TYPE'); |
| builder.write(targetLocation.suffix); |
| }); |
| }); |
| _addFixFromBuilder(changeBuilder, DartFixKind.CREATE_GETTER, args: [name]); |
| } |
| |
| Future<void> _addFix_createImportUri() async { |
| // TODO(brianwilkerson) Determine whether this await is necessary. |
| await null; |
| // TODO(brianwilkerson) Generalize this to allow other valid string literals. |
| // TODO(brianwilkerson) Support the case where the node's parent is a Configuration. |
| if (node is SimpleStringLiteral && node.parent is ImportDirective) { |
| ImportDirective importDirective = node.parent; |
| Source source = importDirective.uriSource; |
| if (source != null) { |
| String file = source.fullName; |
| if (isAbsolute(file) && AnalysisEngine.isDartFileName(file)) { |
| String libName = _computeLibraryName(file); |
| try { |
| DartChangeBuilder changeBuilder = new DartChangeBuilder(session); |
| await changeBuilder.addFileEdit(source.fullName, |
| (DartFileEditBuilder builder) { |
| builder.addSimpleInsertion(0, 'library $libName;$eol$eol'); |
| }); |
| _addFixFromBuilder(changeBuilder, DartFixKind.CREATE_FILE, |
| args: [source.shortName]); |
| } on AnalysisException { |
| // Ignore the exception and just don't create a fix. |
| } |
| } |
| } |
| } |
| } |
| |
| Future<void> _addFix_createLocalVariable() async { |
| // TODO(brianwilkerson) Determine whether this await is necessary. |
| await null; |
| if (node is! SimpleIdentifier) { |
| return; |
| } |
| SimpleIdentifier nameNode = node; |
| String name = nameNode.name; |
| // if variable is assigned, convert assignment into declaration |
| if (node.parent is AssignmentExpression) { |
| AssignmentExpression assignment = node.parent; |
| if (assignment.leftHandSide == node && |
| assignment.operator.type == TokenType.EQ && |
| assignment.parent is ExpressionStatement) { |
| DartChangeBuilder changeBuilder = new DartChangeBuilder(session); |
| await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) { |
| builder.addSimpleInsertion(node.offset, 'var '); |
| }); |
| _addFixFromBuilder(changeBuilder, DartFixKind.CREATE_LOCAL_VARIABLE, |
| args: [name]); |
| return; |
| } |
| } |
| // prepare target Statement |
| Statement target = node.getAncestor((x) => x is Statement); |
| if (target == null) { |
| return; |
| } |
| String prefix = utils.getNodePrefix(target); |
| // compute type |
| DartType type = _inferUndefinedExpressionType(node); |
| if (!(type == null || |
| type is InterfaceType || |
| type is FunctionType && |
| type.element != null && |
| !type.element.isSynthetic)) { |
| return; |
| } |
| // build variable declaration source |
| DartChangeBuilder changeBuilder = new DartChangeBuilder(session); |
| await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) { |
| builder.addInsertion(target.offset, (DartEditBuilder builder) { |
| builder.writeLocalVariableDeclaration(name, |
| nameGroupName: 'NAME', type: type, typeGroupName: 'TYPE'); |
| builder.write(eol); |
| builder.write(prefix); |
| }); |
| builder.addLinkedPosition(range.node(node), 'NAME'); |
| }); |
| _addFixFromBuilder(changeBuilder, DartFixKind.CREATE_LOCAL_VARIABLE, |
| args: [name]); |
| } |
| |
| Future<void> _addFix_createMissingOverrides() async { |
| // TODO(brianwilkerson) Determine whether this await is necessary. |
| await null; |
| if (node.parent is! ClassDeclaration) { |
| return; |
| } |
| ClassDeclaration targetClass = node.parent as ClassDeclaration; |
| ClassElement targetClassElement = targetClass.declaredElement; |
| utils.targetClassElement = targetClassElement; |
| List<ExecutableElement> elements = |
| InheritanceOverrideVerifier.missingOverrides(targetClass).toList(); |
| // sort by name, getters before setters |
| elements.sort((Element a, Element b) { |
| int names = compareStrings(a.displayName, b.displayName); |
| if (names != 0) { |
| return names; |
| } |
| if (a.kind == ElementKind.GETTER) { |
| return -1; |
| } |
| return 1; |
| }); |
| int numElements = elements.length; |
| |
| ClassMemberLocation location = |
| utils.prepareNewClassMemberLocation(targetClass, (_) => true); |
| |
| String prefix = utils.getIndent(1); |
| DartChangeBuilder changeBuilder = new DartChangeBuilder(session); |
| await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) { |
| builder.addInsertion(location.offset, (DartEditBuilder builder) { |
| // TODO(brianwilkerson) Compare with builder.writeOverrideOfInheritedMember |
| // The builder method doesn't merge getter/setter pairs into fields. |
| |
| // Separator management. |
| int numOfMembersWritten = 0; |
| void addSeparatorBetweenDeclarations() { |
| if (numOfMembersWritten == 0) { |
| builder.write(location.prefix); |
| } else { |
| builder.write(eol); // after the previous member |
| builder.write(eol); // empty line separator |
| builder.write(prefix); |
| } |
| numOfMembersWritten++; |
| } |
| |
| // 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 |
| addSeparatorBetweenDeclarations(); |
| // @override |
| 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(';'); |
| } |
| } |
| } |
| // add elements |
| for (ExecutableElement element in elements) { |
| addSeparatorBetweenDeclarations(); |
| _addFix_createMissingOverridesForBuilder( |
| builder, targetClass, element); |
| } |
| builder.write(location.suffix); |
| }); |
| }); |
| changeBuilder.setSelection(new Position(file, location.offset)); |
| _addFixFromBuilder(changeBuilder, DartFixKind.CREATE_MISSING_OVERRIDES, |
| args: [numElements]); |
| } |
| |
| void _addFix_createMissingOverridesForBuilder(DartEditBuilder builder, |
| ClassDeclaration targetClass, ExecutableElement element) { |
| utils.targetExecutableElement = element; |
| // prepare environment |
| String prefix = utils.getIndent(1); |
| String prefix2 = utils.getIndent(2); |
| // may be property |
| ElementKind elementKind = element.kind; |
| bool isGetter = elementKind == ElementKind.GETTER; |
| bool isSetter = elementKind == ElementKind.SETTER; |
| bool isMethod = elementKind == ElementKind.METHOD; |
| bool isOperator = isMethod && (element as MethodElement).isOperator; |
| if (isGetter) { |
| builder.write('// TODO: implement ${element.displayName}'); |
| builder.write(eol); |
| builder.write(prefix); |
| } |
| // @override |
| builder.write('@override'); |
| builder.write(eol); |
| builder.write(prefix); |
| // return type |
| if (!isSetter) { |
| if (builder.writeType(element.type.returnType, |
| methodBeingCopied: element)) { |
| builder.write(' '); |
| } |
| } |
| // keyword |
| if (isGetter) { |
| builder.write('get '); |
| } else if (isSetter) { |
| builder.write('set '); |
| } else if (isOperator) { |
| builder.write('operator '); |
| } |
| // name |
| builder.write(element.displayName); |
| builder.writeTypeParameters(element.typeParameters, |
| methodBeingCopied: element); |
| // parameters + body |
| if (isGetter) { |
| builder.write(' => null;'); |
| } else { |
| List<ParameterElement> parameters = element.parameters; |
| builder.writeParameters(parameters, methodBeingCopied: element); |
| builder.write(' {'); |
| // TO-DO |
| builder.write(eol); |
| builder.write(prefix2); |
| builder.write('// TODO: implement ${element.displayName}'); |
| builder.write(eol); |
| // close method |
| builder.write(prefix); |
| builder.write('}'); |
| } |
| utils.targetExecutableElement = null; |
| } |
| |
| Future<void> _addFix_createMixin() async { |
| Element prefixElement = null; |
| String name = null; |
| SimpleIdentifier nameNode; |
| if (node is SimpleIdentifier) { |
| AstNode parent = node.parent; |
| if (parent is PrefixedIdentifier) { |
| if (parent.parent is InstanceCreationExpression) { |
| return; |
| } |
| PrefixedIdentifier prefixedIdentifier = parent; |
| prefixElement = prefixedIdentifier.prefix.staticElement; |
| if (prefixElement == null) { |
| return; |
| } |
| parent = prefixedIdentifier.parent; |
| nameNode = prefixedIdentifier.identifier; |
| name = prefixedIdentifier.identifier.name; |
| } else if (parent is TypeName && |
| parent.parent is ConstructorName && |
| parent.parent.parent is InstanceCreationExpression) { |
| return; |
| } else { |
| nameNode = node; |
| name = nameNode.name; |
| } |
| if (!_mayBeTypeIdentifier(nameNode)) { |
| return; |
| } |
| } else { |
| return; |
| } |
| // prepare environment |
| Element targetUnit; |
| String prefix = ''; |
| String suffix = ''; |
| int offset = -1; |
| String filePath; |
| if (prefixElement == null) { |
| targetUnit = unitElement; |
| CompilationUnitMember enclosingMember = node.getAncestor((node) => |
| node is CompilationUnitMember && node.parent is CompilationUnit); |
| if (enclosingMember == null) { |
| return; |
| } |
| offset = enclosingMember.end; |
| filePath = file; |
| prefix = '$eol$eol'; |
| } else { |
| for (ImportElement import in unitLibraryElement.imports) { |
| if (prefixElement is PrefixElement && import.prefix == prefixElement) { |
| LibraryElement library = import.importedLibrary; |
| if (library != null) { |
| targetUnit = library.definingCompilationUnit; |
| Source targetSource = targetUnit.source; |
| try { |
| offset = targetSource.contents.data.length; |
| filePath = targetSource.fullName; |
| prefix = '$eol'; |
| suffix = '$eol'; |
| } on FileSystemException { |
| // If we can't read the file to get the offset, then we can't |
| // create a fix. |
| } |
| break; |
| } |
| } |
| } |
| } |
| if (offset < 0) { |
| return; |
| } |
| DartChangeBuilder changeBuilder = new DartChangeBuilder(session); |
| await changeBuilder.addFileEdit(filePath, (DartFileEditBuilder builder) { |
| builder.addInsertion(offset, (DartEditBuilder builder) { |
| builder.write(prefix); |
| builder.writeMixinDeclaration(name, nameGroupName: 'NAME'); |
| builder.write(suffix); |
| }); |
| if (prefixElement == null) { |
| builder.addLinkedPosition(range.node(node), 'NAME'); |
| } |
| }); |
| _addFixFromBuilder(changeBuilder, DartFixKind.CREATE_MIXIN, args: [name]); |
| } |
| |
| Future<void> _addFix_createNoSuchMethod() async { |
| // TODO(brianwilkerson) Determine whether this await is necessary. |
| await null; |
| if (node.parent is! ClassDeclaration) { |
| return; |
| } |
| ClassDeclaration targetClass = node.parent as ClassDeclaration; |
| // prepare environment |
| String prefix = utils.getIndent(1); |
| int insertOffset = targetClass.end - 1; |
| DartChangeBuilder changeBuilder = new DartChangeBuilder(session); |
| await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) { |
| builder.addInsertion(insertOffset, (DartEditBuilder builder) { |
| builder.selectHere(); |
| // insert empty line before existing member |
| if (!targetClass.members.isEmpty) { |
| builder.write(eol); |
| } |
| // append method |
| builder.write(prefix); |
| builder.write( |
| 'noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);'); |
| builder.write(eol); |
| }); |
| }); |
| _addFixFromBuilder(changeBuilder, DartFixKind.CREATE_NO_SUCH_METHOD); |
| } |
| |
| Future<void> _addFix_createPartUri() async { |
| // TODO(brianwilkerson) Determine whether this await is necessary. |
| await null; |
| // TODO(brianwilkerson) Generalize this to allow other valid string literals. |
| if (node is SimpleStringLiteral && node.parent is PartDirective) { |
| PartDirective partDirective = node.parent; |
| Source source = partDirective.uriSource; |
| if (source != null) { |
| String libName = unitLibraryElement.name; |
| DartChangeBuilder changeBuilder = new DartChangeBuilder(session); |
| await changeBuilder.addFileEdit(source.fullName, |
| (DartFileEditBuilder builder) { |
| // TODO(brianwilkerson) Consider using the URI rather than name |
| builder.addSimpleInsertion(0, 'part of $libName;$eol$eol'); |
| }); |
| _addFixFromBuilder(changeBuilder, DartFixKind.CREATE_FILE, |
| args: [source.shortName]); |
| } |
| } |
| } |
| |
| Future<void> _addFix_illegalAsyncReturnType() async { |
| // TODO(brianwilkerson) Determine whether this await is necessary. |
| await null; |
| // prepare the existing type |
| TypeAnnotation typeName = node.getAncestor((n) => n is TypeAnnotation); |
| TypeProvider typeProvider = this.typeProvider; |
| DartChangeBuilder changeBuilder = new DartChangeBuilder(session); |
| await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) { |
| builder.replaceTypeWithFuture(typeName, typeProvider); |
| }); |
| _addFixFromBuilder(changeBuilder, DartFixKind.REPLACE_RETURN_TYPE_FUTURE); |
| } |
| |
| Future<void> _addFix_importLibrary(FixKind kind, Uri library) async { |
| // TODO(brianwilkerson) Determine whether this await is necessary. |
| await null; |
| String uriText; |
| DartChangeBuilder changeBuilder = new DartChangeBuilder(session); |
| await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) { |
| uriText = builder.importLibrary(library); |
| }); |
| _addFixFromBuilder(changeBuilder, kind, args: [uriText]); |
| } |
| |
| Future<void> _addFix_importLibrary_withElement(String name, |
| List<ElementKind> elementKinds, TopLevelDeclarationKind kind2) async { |
| // TODO(brianwilkerson) Determine whether this await is necessary. |
| await null; |
| // 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 |
| Set<Source> alreadyImportedWithPrefix = new Set<Source>(); |
| for (ImportElement imp in unitLibraryElement.imports) { |
| // prepare element |
| LibraryElement libraryElement = imp.importedLibrary; |
| Element element = getExportedElement(libraryElement, name); |
| if (element == null) { |
| continue; |
| } |
| if (element is PropertyAccessorElement) { |
| element = (element as PropertyAccessorElement).variable; |
| } |
| if (!elementKinds.contains(element.kind)) { |
| continue; |
| } |
| // may be apply prefix |
| PrefixElement prefix = imp.prefix; |
| if (prefix != null) { |
| DartChangeBuilder changeBuilder = new DartChangeBuilder(session); |
| await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) { |
| builder.addSimpleReplacement( |
| range.startLength(node, 0), '${prefix.displayName}.'); |
| }); |
| _addFixFromBuilder(changeBuilder, DartFixKind.IMPORT_LIBRARY_PREFIX, |
| args: [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 SplayTreeSet<String>(); |
| showNames.addAll(showCombinator.shownNames); |
| showNames.add(name); |
| // prepare library name - unit name or 'dart:name' for SDK library |
| String libraryName = |
| libraryElement.definingCompilationUnit.source.uri.toString(); |
| if (libraryElement.isInSdk) { |
| libraryName = libraryElement.source.shortName; |
| } |
| // don't add this library again |
| alreadyImportedWithPrefix.add(libraryElement.source); |
| // update library |
| String newShowCode = 'show ${showNames.join(', ')}'; |
| int offset = showCombinator.offset; |
| int length = showCombinator.end - offset; |
| String libraryFile = unitLibraryElement.source.fullName; |
| DartChangeBuilder changeBuilder = new DartChangeBuilder(session); |
| await changeBuilder.addFileEdit(libraryFile, |
| (DartFileEditBuilder builder) { |
| builder.addSimpleReplacement( |
| new SourceRange(offset, length), newShowCode); |
| }); |
| _addFixFromBuilder(changeBuilder, DartFixKind.IMPORT_LIBRARY_SHOW, |
| args: [libraryName]); |
| } |
| } |
| // Find new top-level declarations. |
| { |
| List<TopLevelDeclarationInSource> declarations = |
| await getTopLevelDeclarations(name); |
| for (TopLevelDeclarationInSource declaration in declarations) { |
| // Check the kind. |
| if (declaration.declaration.kind != kind2) { |
| continue; |
| } |
| // Check the source. |
| Source librarySource = declaration.source; |
| if (alreadyImportedWithPrefix.contains(librarySource)) { |
| continue; |
| } |
| if (!_isSourceVisibleToLibrary(librarySource)) { |
| continue; |
| } |
| // Compute the fix kind. |
| FixKind fixKind; |
| if (librarySource.isInSystemLibrary) { |
| fixKind = DartFixKind.IMPORT_LIBRARY_SDK; |
| } else if (_isLibSrcPath(librarySource.fullName)) { |
| // Bad: non-API. |
| fixKind = DartFixKind.IMPORT_LIBRARY_PROJECT3; |
| } else if (declaration.isExported) { |
| // Ugly: exports. |
| fixKind = DartFixKind.IMPORT_LIBRARY_PROJECT2; |
| } else { |
| // Good: direct declaration. |
| fixKind = DartFixKind.IMPORT_LIBRARY_PROJECT1; |
| } |
| // Add the fix. |
| await _addFix_importLibrary(fixKind, librarySource.uri); |
| } |
| } |
| } |
| |
| Future<void> _addFix_importLibrary_withFunction() async { |
| // TODO(brianwilkerson) Determine whether this await is necessary. |
| await null; |
| if (node is SimpleIdentifier && node.parent is MethodInvocation) { |
| MethodInvocation invocation = node.parent as MethodInvocation; |
| if (invocation.realTarget == null && invocation.methodName == node) { |
| String name = (node as SimpleIdentifier).name; |
| await _addFix_importLibrary_withElement(name, |
| const [ElementKind.FUNCTION], TopLevelDeclarationKind.function); |
| } |
| } |
| } |
| |
| Future<void> _addFix_importLibrary_withTopLevelVariable() async { |
| // TODO(brianwilkerson) Determine whether this await is necessary. |
| await null; |
| if (node is SimpleIdentifier) { |
| String name = (node as SimpleIdentifier).name; |
| await _addFix_importLibrary_withElement( |
| name, |
| const [ElementKind.TOP_LEVEL_VARIABLE], |
| TopLevelDeclarationKind.variable); |
| } |
| } |
| |
| Future<void> _addFix_importLibrary_withType() async { |
| // TODO(brianwilkerson) Determine whether this await is necessary. |
| await null; |
| if (_mayBeTypeIdentifier(node)) { |
| String typeName = (node as SimpleIdentifier).name; |
| await _addFix_importLibrary_withElement( |
| typeName, |
| const [ElementKind.CLASS, ElementKind.FUNCTION_TYPE_ALIAS], |
| TopLevelDeclarationKind.type); |
| } else if (_mayBeImplicitConstructor(node)) { |
| String typeName = (node as SimpleIdentifier).name; |
| await _addFix_importLibrary_withElement( |
| typeName, const [ElementKind.CLASS], TopLevelDeclarationKind.type); |
| } |
| } |
| |
| Future<void> _addFix_insertSemicolon() async { |
| // TODO(brianwilkerson) Determine whether this await is necessary. |
| await null; |
| if (error.message.contains("';'")) { |
| if (_isAwaitNode()) { |
| return; |
| } |
| int insertOffset = error.offset + error.length; |
| DartChangeBuilder changeBuilder = new DartChangeBuilder(session); |
| await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) { |
| builder.addSimpleInsertion(insertOffset, ';'); |
| }); |
| _addFixFromBuilder(changeBuilder, DartFixKind.INSERT_SEMICOLON); |
| } |
| } |
| |
| Future<void> _addFix_isNotEmpty() async { |
| // TODO(brianwilkerson) Determine whether this await is necessary. |
| await null; |
| if (node is! PrefixExpression) { |
| return; |
| } |
| PrefixExpression prefixExpression = node; |
| Token negation = prefixExpression.operator; |
| if (negation.type != TokenType.BANG) { |
| return; |
| } |
| SimpleIdentifier identifier; |
| Expression expression = prefixExpression.operand; |
| if (expression is PrefixedIdentifier) { |
| identifier = expression.identifier; |
| } else if (expression is PropertyAccess) { |
| identifier = expression.propertyName; |
| } else { |
| return; |
| } |
| DartChangeBuilder changeBuilder = new DartChangeBuilder(session); |
| await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) { |
| builder.addDeletion(range.token(negation)); |
| builder.addSimpleReplacement(range.node(identifier), 'isNotEmpty'); |
| }); |
| _addFixFromBuilder(changeBuilder, DartFixKind.USE_IS_NOT_EMPTY); |
| } |
| |
| Future<void> _addFix_isNotNull() async { |
| // TODO(brianwilkerson) Determine whether this await is necessary. |
| await null; |
| if (coveredNode is IsExpression) { |
| IsExpression isExpression = coveredNode as IsExpression; |
| DartChangeBuilder changeBuilder = new DartChangeBuilder(session); |
| await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) { |
| builder |
| .addReplacement(range.endEnd(isExpression.expression, isExpression), |
| (DartEditBuilder builder) { |
| builder.write(' != null'); |
| }); |
| }); |
| _addFixFromBuilder(changeBuilder, DartFixKind.USE_NOT_EQ_NULL); |
| } |
| } |
| |
| Future<void> _addFix_isNull() async { |
| // TODO(brianwilkerson) Determine whether this await is necessary. |
| await null; |
| if (coveredNode is IsExpression) { |
| IsExpression isExpression = coveredNode as IsExpression; |
| DartChangeBuilder changeBuilder = new DartChangeBuilder(session); |
| await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) { |
| builder |
| .addReplacement(range.endEnd(isExpression.expression, isExpression), |
| (DartEditBuilder builder) { |
| builder.write(' == null'); |
| }); |
| }); |
| _addFixFromBuilder(changeBuilder, DartFixKind.USE_EQ_EQ_NULL); |
| } |
| } |
| |
| Future<void> _addFix_makeEnclosingClassAbstract() async { |
| // TODO(brianwilkerson) Determine whether this await is necessary. |
| await null; |
| ClassDeclaration enclosingClass = |
| node.getAncestor((node) => node is ClassDeclaration); |
| if (enclosingClass == null) { |
| return; |
| } |
| String className = enclosingClass.name.name; |
| DartChangeBuilder changeBuilder = new DartChangeBuilder(session); |
| await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) { |
| builder.addSimpleInsertion( |
| enclosingClass.classKeyword.offset, 'abstract '); |
| }); |
| _addFixFromBuilder(changeBuilder, DartFixKind.MAKE_CLASS_ABSTRACT, |
| args: [className]); |
| } |
| |
| Future<void> _addFix_makeFieldNotFinal() async { |
| // TODO(brianwilkerson) Determine whether this await is necessary. |
| await null; |
| AstNode node = this.node; |
| if (node is SimpleIdentifier && |
| node.staticElement is PropertyAccessorElement) { |
| PropertyAccessorElement getter = node.staticElement; |
| if (getter.isGetter && |
| getter.isSynthetic && |
| !getter.variable.isSynthetic && |
| getter.variable.setter == null && |
| getter.enclosingElement is ClassElement) { |
| AstNode name = |
| await astProvider.getParsedNameForElement(getter.variable); |
| AstNode variable = name?.parent; |
| if (variable is VariableDeclaration && |
| variable.parent is VariableDeclarationList && |
| variable.parent.parent is FieldDeclaration) { |
| VariableDeclarationList declarationList = variable.parent; |
| Token keywordToken = declarationList.keyword; |
| if (declarationList.variables.length == 1 && |
| keywordToken.keyword == Keyword.FINAL) { |
| DartChangeBuilder changeBuilder = new DartChangeBuilder(session); |
| await changeBuilder.addFileEdit(file, |
| (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; |
| _addFixFromBuilder(changeBuilder, DartFixKind.MAKE_FIELD_NOT_FINAL, |
| args: [fieldName]); |
| } |
| } |
| } |
| } |
| } |
| |
| Future<void> _addFix_makeVariableFinal() async { |
| // TODO(brianwilkerson) Determine whether this await is necessary. |
| await null; |
| AstNode node = this.node; |
| if (node is SimpleIdentifier && node.parent is VariableDeclaration) { |
| VariableDeclaration declaration = node.parent; |
| VariableDeclarationList list = declaration.parent; |
| if (list.variables.length == 1) { |
| DartChangeBuilder changeBuilder = new DartChangeBuilder(session); |
| await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) { |
| if (list.keyword == null) { |
| builder.addSimpleInsertion(list.offset, 'final '); |
| } else if (list.keyword.keyword == Keyword.VAR) { |
| builder.addSimpleReplacement(range.token(list.keyword), 'final'); |
| } |
| }); |
| _addFixFromBuilder(changeBuilder, DartFixKind.MAKE_FINAL); |
| } |
| } |
| } |
| |
| Future<void> _addFix_nonBoolCondition_addNotNull() async { |
| // TODO(brianwilkerson) Determine whether this await is necessary. |
| await null; |
| DartChangeBuilder changeBuilder = new DartChangeBuilder(session); |
| await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) { |
| builder.addSimpleInsertion(error.offset + error.length, ' != null'); |
| }); |
| _addFixFromBuilder(changeBuilder, DartFixKind.ADD_NE_NULL); |
| } |
| |
| Future<void> _addFix_removeAwait() async { |
| // TODO(brianwilkerson) Determine whether this await is necessary. |
| await null; |
| final awaitExpression = node; |
| if (awaitExpression is AwaitExpression) { |
| final awaitToken = awaitExpression.awaitKeyword; |
| DartChangeBuilder changeBuilder = new DartChangeBuilder(session); |
| await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) { |
| builder.addDeletion(range.startStart(awaitToken, awaitToken.next)); |
| }); |
| _addFixFromBuilder(changeBuilder, DartFixKind.REMOVE_AWAIT); |
| } |
| } |
| |
| Future<void> _addFix_removeDeadCode() async { |
| // TODO(brianwilkerson) Determine whether this await is necessary. |
| await null; |
| AstNode coveringNode = this.coveredNode; |
| if (coveringNode is Expression) { |
| AstNode parent = coveredNode.parent; |
| if (parent is BinaryExpression) { |
| if (parent.rightOperand == coveredNode) { |
| DartChangeBuilder changeBuilder = new DartChangeBuilder(session); |
| await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) { |
| builder.addDeletion(range.endEnd(parent.leftOperand, coveredNode)); |
| }); |
| _addFixFromBuilder(changeBuilder, DartFixKind.REMOVE_DEAD_CODE); |
| } |
| } |
| } else if (coveringNode is Block) { |
| Block block = coveringNode; |
| List<Statement> statementsToRemove = <Statement>[]; |
| for (Statement statement in block.statements) { |
| if (range.node(statement).intersects(errorRange)) { |
| statementsToRemove.add(statement); |
| } |
| } |
| if (statementsToRemove.isNotEmpty) { |
| SourceRange rangeToRemove = |
| utils.getLinesRangeStatements(statementsToRemove); |
| DartChangeBuilder changeBuilder = new DartChangeBuilder(session); |
| await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) { |
| builder.addDeletion(rangeToRemove); |
| }); |
| _addFixFromBuilder(changeBuilder, DartFixKind.REMOVE_DEAD_CODE); |
| } |
| } else if (coveringNode is Statement) { |
| SourceRange rangeToRemove = |
| utils.getLinesRangeStatements(<Statement>[coveringNode]); |
| DartChangeBuilder changeBuilder = new DartChangeBuilder(session); |
| await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) { |
| builder.addDeletion(rangeToRemove); |
| }); |
| _addFixFromBuilder(changeBuilder, DartFixKind.REMOVE_DEAD_CODE); |
| } |
| } |
| |
| Future<void> _addFix_removeEmptyCatch() async { |
| // TODO(brianwilkerson) Determine whether this await is necessary. |
| await null; |
| DartChangeBuilder changeBuilder = new DartChangeBuilder(session); |
| await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) { |
| builder.addDeletion(utils.getLinesRange(range.node(node.parent))); |
| }); |
| _addFixFromBuilder(changeBuilder, DartFixKind.REMOVE_EMPTY_CATCH); |
| } |
| |
| Future<void> _addFix_removeEmptyConstructorBody() async { |
| // TODO(brianwilkerson) Determine whether this await is necessary. |
| await null; |
| DartChangeBuilder changeBuilder = new DartChangeBuilder(session); |
| await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) { |
| builder.addSimpleReplacement( |
| utils.getLinesRange(range.node(node.parent)), ';'); |
| }); |
| _addFixFromBuilder( |
| changeBuilder, DartFixKind.REMOVE_EMPTY_CONSTRUCTOR_BODY); |
| } |
| |
| Future<void> _addFix_removeEmptyElse() async { |
| // TODO(brianwilkerson) Determine whether this await is necessary. |
| await null; |
| IfStatement ifStatement = node.parent; |
| DartChangeBuilder changeBuilder = new DartChangeBuilder(session); |
| await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) { |
| builder.addDeletion(utils.getLinesRange( |
| range.startEnd(ifStatement.elseKeyword, ifStatement.elseStatement))); |
| }); |
| _addFixFromBuilder(changeBuilder, DartFixKind.REMOVE_EMPTY_ELSE); |
| } |
| |
| Future<void> _addFix_removeEmptyStatement() async { |
| // TODO(brianwilkerson) Determine whether this await is necessary. |
| await null; |
| EmptyStatement emptyStatement = node; |
| if (emptyStatement.parent is Block) { |
| DartChangeBuilder changeBuilder = new DartChangeBuilder(session); |
| await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) { |
| builder.addDeletion(utils.getLinesRange(range.node(emptyStatement))); |
| }); |
| _addFixFromBuilder(changeBuilder, DartFixKind.REMOVE_EMPTY_STATEMENT); |
| } else { |
| DartChangeBuilder changeBuilder = new DartChangeBuilder(session); |
| await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) { |
| Token previous = emptyStatement.findPrevious(emptyStatement.beginToken); |
| if (previous != null) { |
| builder.addSimpleReplacement( |
| range.endEnd(previous, emptyStatement), ' {}'); |
| } |
| }); |
| _addFixFromBuilder(changeBuilder, DartFixKind.REPLACE_WITH_BRACKETS); |
| } |
| } |
| |
| Future<void> _addFix_removeInitializer() async { |
| // TODO(brianwilkerson) Determine whether this await is necessary. |
| await null; |
| // Retrieve the linted node. |
| VariableDeclaration ancestor = |
| node.getAncestor((a) => a is VariableDeclaration); |
| if (ancestor == null) { |
| return; |
| } |
| DartChangeBuilder changeBuilder = new DartChangeBuilder(session); |
| await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) { |
| builder.addDeletion(range.endEnd(ancestor.name, ancestor.initializer)); |
| }); |
| _addFixFromBuilder(changeBuilder, DartFixKind.REMOVE_INITIALIZER); |
| } |
| |
| Future<void> _addFix_removeInterpolationBraces() async { |
| // TODO(brianwilkerson) Determine whether this await is necessary. |
| await null; |
| AstNode node = this.node; |
| if (node is InterpolationExpression) { |
| Token right = node.rightBracket; |
| if (node.expression != null && right != null) { |
| DartChangeBuilder changeBuilder = new DartChangeBuilder(session); |
| await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) { |
| builder.addSimpleReplacement( |
| range.startStart(node, node.expression), r'$'); |
| builder.addDeletion(range.token(right)); |
| }); |
| _addFixFromBuilder( |
| changeBuilder, DartFixKind.LINT_REMOVE_INTERPOLATION_BRACES); |
| } else {} |
| } |
| } |
| |
| Future<void> _addFix_removeMethodDeclaration() async { |
| // TODO(brianwilkerson) Determine whether this await is necessary. |
| await null; |
| MethodDeclaration declaration = |
| node.getAncestor((node) => node is MethodDeclaration); |
| if (declaration != null) { |
| DartChangeBuilder changeBuilder = new DartChangeBuilder(session); |
| await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) { |
| builder.addDeletion(utils.getLinesRange(range.node(declaration))); |
| }); |
| _addFixFromBuilder(changeBuilder, DartFixKind.REMOVE_METHOD_DECLARATION); |
| } |
| } |
| |
| Future<void> _addFix_removeParameters_inGetterDeclaration() async { |
| // TODO(brianwilkerson) Determine whether this await is necessary. |
| await null; |
| if (node is MethodDeclaration) { |
| // Support for the analyzer error. |
| MethodDeclaration method = node as MethodDeclaration; |
| SimpleIdentifier name = method.name; |
| FunctionBody body = method.body; |
| if (name != null && body != null) { |
| DartChangeBuilder changeBuilder = new DartChangeBuilder(session); |
| await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) { |
| builder.addSimpleReplacement(range.endStart(name, body), ' '); |
| }); |
| _addFixFromBuilder( |
| changeBuilder, DartFixKind.REMOVE_PARAMETERS_IN_GETTER_DECLARATION); |
| } |
| } else if (node is FormalParameterList) { |
| // Support for the fasta error. |
| DartChangeBuilder changeBuilder = new DartChangeBuilder(session); |
| await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) { |
| builder.addDeletion(range.node(node)); |
| }); |
| _addFixFromBuilder( |
| changeBuilder, DartFixKind.REMOVE_PARAMETERS_IN_GETTER_DECLARATION); |
| } |
| } |
| |
| Future<void> _addFix_removeParentheses_inGetterInvocation() async { |
| // TODO(brianwilkerson) Determine whether this await is necessary. |
| await null; |
| if (node is SimpleIdentifier && node.parent is MethodInvocation) { |
| MethodInvocation invocation = node.parent as MethodInvocation; |
| if (invocation.methodName == node && invocation.target != null) { |
| DartChangeBuilder changeBuilder = new DartChangeBuilder(session); |
| await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) { |
| builder.addDeletion(range.endEnd(node, invocation)); |
| }); |
| _addFixFromBuilder( |
| changeBuilder, DartFixKind.REMOVE_PARENTHESIS_IN_GETTER_INVOCATION); |
| } |
| } |
| } |
| |
| Future<void> _addFix_removeThisExpression() async { |
| // TODO(brianwilkerson) Determine whether this await is necessary. |
| await null; |
| final thisExpression = node is ThisExpression |
| ? node |
| : node.getAncestor((node) => node is ThisExpression); |
| final parent = thisExpression?.parent; |
| if (parent is PropertyAccess) { |
| DartChangeBuilder changeBuilder = new DartChangeBuilder(session); |
| await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) { |
| builder.addDeletion(range.startEnd(parent, parent.operator)); |
| }); |
| _addFixFromBuilder(changeBuilder, DartFixKind.REMOVE_THIS_EXPRESSION); |
| } else if (parent is MethodInvocation) { |
| DartChangeBuilder changeBuilder = new DartChangeBuilder(session); |
| await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) { |
| builder.addDeletion(range.startEnd(parent, parent.operator)); |
| }); |
| _addFixFromBuilder(changeBuilder, DartFixKind.REMOVE_THIS_EXPRESSION); |
| } |
| } |
| |
| Future<void> _addFix_removeTypeAnnotation() async { |
| // TODO(brianwilkerson) Determine whether this await is necessary. |
| await null; |
| final TypeAnnotation type = |
| node.getAncestor((node) => node is TypeAnnotation); |
| if (type != null) { |
| DartChangeBuilder changeBuilder = new DartChangeBuilder(session); |
| await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) { |
| builder.addDeletion(range.startStart(type, type.endToken.next)); |
| }); |
| _addFixFromBuilder(changeBuilder, DartFixKind.REMOVE_TYPE_NAME); |
| } |
| } |
| |
| Future<void> _addFix_removeUnnecessaryCast() async { |
| // TODO(brianwilkerson) Determine whether this await is necessary. |
| await null; |
| 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' |
| DartChangeBuilder changeBuilder = new DartChangeBuilder(session); |
| await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) { |
| builder.addDeletion(range.endEnd(expression, asExpression)); |
| _removeEnclosingParentheses(builder, asExpression, expressionPrecedence); |
| }); |
| _addFixFromBuilder(changeBuilder, DartFixKind.REMOVE_UNNECESSARY_CAST); |
| } |
| |
| Future<void> _addFix_removeUnusedCatchClause() async { |
| // TODO(brianwilkerson) Determine whether this await is necessary. |
| await null; |
| if (node is SimpleIdentifier) { |
| AstNode catchClause = node.parent; |
| if (catchClause is |