| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | |
| 2 // for details. All rights reserved. Use of this source code is governed by a | |
| 3 // BSD-style license that can be found in the LICENSE file. | |
| 4 | |
| 5 library services.src.correction.fix; | |
| 6 | |
| 7 import 'dart:collection'; | |
| 8 | |
| 9 import 'package:analysis_services/correction/change.dart'; | |
| 10 import 'package:analysis_services/correction/fix.dart'; | |
| 11 import 'package:analysis_services/search/hierarchy.dart'; | |
| 12 import 'package:analysis_services/search/search_engine.dart'; | |
| 13 import 'package:analysis_services/src/correction/levenshtein.dart'; | |
| 14 import 'package:analysis_services/src/correction/name_suggestion.dart'; | |
| 15 import 'package:analysis_services/src/correction/source_buffer.dart'; | |
| 16 import 'package:analysis_services/src/correction/source_range.dart' as rf; | |
| 17 import 'package:analysis_services/src/correction/strings.dart'; | |
| 18 import 'package:analysis_services/src/correction/util.dart'; | |
| 19 import 'package:analyzer/src/generated/ast.dart'; | |
| 20 import 'package:analyzer/src/generated/element.dart'; | |
| 21 import 'package:analyzer/src/generated/engine.dart'; | |
| 22 import 'package:analyzer/src/generated/error.dart'; | |
| 23 import 'package:analyzer/src/generated/java_core.dart'; | |
| 24 import 'package:analyzer/src/generated/parser.dart'; | |
| 25 import 'package:analyzer/src/generated/scanner.dart'; | |
| 26 import 'package:analyzer/src/generated/sdk.dart'; | |
| 27 import 'package:analyzer/src/generated/source.dart'; | |
| 28 import 'package:analyzer/src/generated/utilities_dart.dart'; | |
| 29 import 'package:path/path.dart'; | |
| 30 | |
| 31 | |
| 32 /** | |
| 33 * A predicate is a one-argument function that returns a boolean value. | |
| 34 */ | |
| 35 typedef bool Predicate<E>(E argument); | |
| 36 | |
| 37 | |
| 38 /** | |
| 39 * The computer for Dart fixes. | |
| 40 */ | |
| 41 class FixProcessor { | |
| 42 static const int MAX_LEVENSHTEIN_DISTANCE = 3; | |
| 43 | |
| 44 final SearchEngine searchEngine; | |
| 45 final Source source; | |
| 46 final String file; | |
| 47 final CompilationUnit unit; | |
| 48 final AnalysisError error; | |
| 49 CompilationUnitElement unitElement; | |
| 50 Source unitSource; | |
| 51 LibraryElement unitLibraryElement; | |
| 52 String unitLibraryFile; | |
| 53 String unitLibraryFolder; | |
| 54 | |
| 55 final List<Edit> edits = <Edit>[]; | |
| 56 final Map<String, LinkedEditGroup> linkedPositionGroups = <String, | |
| 57 LinkedEditGroup>{}; | |
| 58 Position exitPosition = null; | |
| 59 final List<Fix> fixes = <Fix>[]; | |
| 60 | |
| 61 CorrectionUtils utils; | |
| 62 int errorOffset; | |
| 63 int errorLength; | |
| 64 int errorEnd; | |
| 65 AstNode node; | |
| 66 AstNode coveredNode; | |
| 67 | |
| 68 FixProcessor(this.searchEngine, this.source, this.file, this.unit, this.error) | |
| 69 { | |
| 70 unitElement = unit.element; | |
| 71 unitSource = unitElement.source; | |
| 72 unitLibraryElement = unitElement.library; | |
| 73 unitLibraryFile = unitLibraryElement.source.fullName; | |
| 74 unitLibraryFolder = dirname(unitLibraryFile); | |
| 75 } | |
| 76 | |
| 77 DartType get coreTypeBool => _getCoreType("bool"); | |
| 78 | |
| 79 /** | |
| 80 * Returns the EOL to use for this [CompilationUnit]. | |
| 81 */ | |
| 82 String get eol => utils.endOfLine; | |
| 83 | |
| 84 List<Fix> compute() { | |
| 85 utils = new CorrectionUtils(unit); | |
| 86 errorOffset = error.offset; | |
| 87 errorLength = error.length; | |
| 88 errorEnd = errorOffset + errorLength; | |
| 89 node = new NodeLocator.con1(errorOffset).searchWithin(unit); | |
| 90 coveredNode = new NodeLocator.con2( | |
| 91 errorOffset, | |
| 92 errorOffset + errorLength).searchWithin(unit); | |
| 93 // analyze ErrorCode | |
| 94 ErrorCode errorCode = error.errorCode; | |
| 95 if (errorCode == StaticWarningCode.UNDEFINED_CLASS_BOOLEAN) { | |
| 96 _addFix_boolInsteadOfBoolean(); | |
| 97 } | |
| 98 if (errorCode == | |
| 99 CompileTimeErrorCode.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE) { | |
| 100 _addFix_replaceWithConstInstanceCreation(); | |
| 101 } | |
| 102 if (errorCode == | |
| 103 CompileTimeErrorCode.NO_DEFAULT_SUPER_CONSTRUCTOR_EXPLICIT) { | |
| 104 _addFix_createConstructorSuperExplicit(); | |
| 105 } | |
| 106 if (errorCode == | |
| 107 CompileTimeErrorCode.NO_DEFAULT_SUPER_CONSTRUCTOR_IMPLICIT) { | |
| 108 _addFix_createConstructorSuperImplicit(); | |
| 109 } | |
| 110 if (errorCode == | |
| 111 CompileTimeErrorCode.UNDEFINED_CONSTRUCTOR_IN_INITIALIZER_DEFAULT) { | |
| 112 _addFix_createConstructorSuperExplicit(); | |
| 113 } | |
| 114 if (errorCode == HintCode.DIVISION_OPTIMIZATION) { | |
| 115 _addFix_useEffectiveIntegerDivision(); | |
| 116 } | |
| 117 if (errorCode == HintCode.TYPE_CHECK_IS_NOT_NULL) { | |
| 118 _addFix_isNotNull(); | |
| 119 } | |
| 120 if (errorCode == HintCode.TYPE_CHECK_IS_NULL) { | |
| 121 _addFix_isNull(); | |
| 122 } | |
| 123 if (errorCode == HintCode.UNNECESSARY_CAST) { | |
| 124 _addFix_removeUnnecessaryCast(); | |
| 125 } | |
| 126 if (errorCode == HintCode.UNUSED_IMPORT) { | |
| 127 _addFix_removeUnusedImport(); | |
| 128 } | |
| 129 if (errorCode == ParserErrorCode.EXPECTED_TOKEN) { | |
| 130 _addFix_insertSemicolon(); | |
| 131 } | |
| 132 if (errorCode == ParserErrorCode.GETTER_WITH_PARAMETERS) { | |
| 133 _addFix_removeParameters_inGetterDeclaration(); | |
| 134 } | |
| 135 if (errorCode == StaticWarningCode.CONCRETE_CLASS_WITH_ABSTRACT_MEMBER) { | |
| 136 _addFix_makeEnclosingClassAbstract(); | |
| 137 } | |
| 138 if (errorCode == StaticWarningCode.EXTRA_POSITIONAL_ARGUMENTS) { | |
| 139 _addFix_createConstructor_insteadOfSyntheticDefault(); | |
| 140 } | |
| 141 if (errorCode == StaticWarningCode.NEW_WITH_UNDEFINED_CONSTRUCTOR) { | |
| 142 _addFix_createConstructor_named(); | |
| 143 } | |
| 144 if (errorCode == | |
| 145 StaticWarningCode.NON_ABSTRACT_CLASS_INHERITS_ABSTRACT_MEMBER_ONE || | |
| 146 errorCode == | |
| 147 StaticWarningCode.NON_ABSTRACT_CLASS_INHERITS_ABSTRACT_MEMBER_TWO || | |
| 148 errorCode == | |
| 149 StaticWarningCode.NON_ABSTRACT_CLASS_INHERITS_ABSTRACT_MEMBER_THREE
|| | |
| 150 errorCode == | |
| 151 StaticWarningCode.NON_ABSTRACT_CLASS_INHERITS_ABSTRACT_MEMBER_FOUR |
| | |
| 152 errorCode == | |
| 153 StaticWarningCode.NON_ABSTRACT_CLASS_INHERITS_ABSTRACT_MEMBER_FIVE_P
LUS) { | |
| 154 // make class abstract | |
| 155 _addFix_makeEnclosingClassAbstract(); | |
| 156 // implement methods | |
| 157 AnalysisErrorWithProperties errorWithProperties = | |
| 158 error as AnalysisErrorWithProperties; | |
| 159 Object property = | |
| 160 errorWithProperties.getProperty(ErrorProperty.UNIMPLEMENTED_METHODS); | |
| 161 List<ExecutableElement> missingOverrides = | |
| 162 property as List<ExecutableElement>; | |
| 163 _addFix_createMissingOverrides(missingOverrides); | |
| 164 _addFix_createNoSuchMethod(); | |
| 165 } | |
| 166 if (errorCode == StaticWarningCode.UNDEFINED_CLASS) { | |
| 167 _addFix_importLibrary_withType(); | |
| 168 _addFix_createClass(); | |
| 169 _addFix_undefinedClass_useSimilar(); | |
| 170 } | |
| 171 if (errorCode == StaticWarningCode.UNDEFINED_IDENTIFIER) { | |
| 172 _addFix_createFunction_forFunctionType(); | |
| 173 _addFix_importLibrary_withType(); | |
| 174 _addFix_importLibrary_withTopLevelVariable(); | |
| 175 } | |
| 176 if (errorCode == StaticTypeWarningCode.INSTANCE_ACCESS_TO_STATIC_MEMBER) { | |
| 177 _addFix_useStaticAccess_method(); | |
| 178 _addFix_useStaticAccess_property(); | |
| 179 } | |
| 180 if (errorCode == StaticTypeWarningCode.INVOCATION_OF_NON_FUNCTION) { | |
| 181 _addFix_removeParentheses_inGetterInvocation(); | |
| 182 } | |
| 183 if (errorCode == StaticTypeWarningCode.UNDEFINED_FUNCTION) { | |
| 184 _addFix_importLibrary_withFunction(); | |
| 185 _addFix_undefinedFunction_useSimilar(); | |
| 186 _addFix_undefinedFunction_create(); | |
| 187 } | |
| 188 if (errorCode == StaticTypeWarningCode.UNDEFINED_GETTER) { | |
| 189 _addFix_createFunction_forFunctionType(); | |
| 190 } | |
| 191 if (errorCode == HintCode.UNDEFINED_METHOD || | |
| 192 errorCode == StaticTypeWarningCode.UNDEFINED_METHOD) { | |
| 193 _addFix_undefinedMethod_useSimilar(); | |
| 194 _addFix_undefinedMethod_create(); | |
| 195 _addFix_undefinedFunction_create(); | |
| 196 } | |
| 197 // done | |
| 198 return fixes; | |
| 199 } | |
| 200 | |
| 201 void _addFix(FixKind kind, List args, {String fixFile}) { | |
| 202 if (fixFile == null) { | |
| 203 fixFile = file; | |
| 204 } | |
| 205 FileEdit fileEdit = new FileEdit(file); | |
| 206 fileEdit.addAll(edits); | |
| 207 // prepare Change | |
| 208 String message = formatList(kind.message, args); | |
| 209 Change change = new Change(message); | |
| 210 change.addFileEdit(fileEdit); | |
| 211 linkedPositionGroups.values.forEach( | |
| 212 (group) => change.addLinkedEditGroup(group)); | |
| 213 change.selection = exitPosition; | |
| 214 // add Fix | |
| 215 Fix fix = new Fix(kind, change); | |
| 216 fixes.add(fix); | |
| 217 // clear | |
| 218 edits.clear(); | |
| 219 linkedPositionGroups.clear(); | |
| 220 exitPosition = null; | |
| 221 } | |
| 222 | |
| 223 void _addFix_boolInsteadOfBoolean() { | |
| 224 SourceRange range = rf.rangeError(error); | |
| 225 _addReplaceEdit(range, "bool"); | |
| 226 _addFix(FixKind.REPLACE_BOOLEAN_WITH_BOOL, []); | |
| 227 } | |
| 228 | |
| 229 void _addFix_createClass() { | |
| 230 if (_mayBeTypeIdentifier(node)) { | |
| 231 String name = (node as SimpleIdentifier).name; | |
| 232 // prepare environment | |
| 233 CompilationUnitMember enclosingMember = | |
| 234 node.getAncestor((node) => node is CompilationUnitMember); | |
| 235 int offset = enclosingMember.end; | |
| 236 String prefix = ""; | |
| 237 // prepare source | |
| 238 SourceBuilder sb = new SourceBuilder(file, offset); | |
| 239 { | |
| 240 sb.append("${eol}${eol}"); | |
| 241 sb.append(prefix); | |
| 242 // "class" | |
| 243 sb.append("class "); | |
| 244 // append name | |
| 245 { | |
| 246 sb.startPosition("NAME"); | |
| 247 sb.append(name); | |
| 248 sb.endPosition(); | |
| 249 } | |
| 250 // no members | |
| 251 sb.append(" {"); | |
| 252 sb.append(eol); | |
| 253 sb.append("}"); | |
| 254 } | |
| 255 // insert source | |
| 256 _insertBuilder(sb); | |
| 257 _addLinkedPosition("NAME", rf.rangeNode(node)); | |
| 258 // add proposal | |
| 259 _addFix(FixKind.CREATE_CLASS, [name]); | |
| 260 } | |
| 261 } | |
| 262 | |
| 263 void _addFix_createConstructorSuperExplicit() { | |
| 264 ConstructorDeclaration targetConstructor = | |
| 265 node.parent as ConstructorDeclaration; | |
| 266 ClassDeclaration targetClassNode = | |
| 267 targetConstructor.parent as ClassDeclaration; | |
| 268 ClassElement targetClassElement = targetClassNode.element; | |
| 269 ClassElement superClassElement = targetClassElement.supertype.element; | |
| 270 // add proposals for all super constructors | |
| 271 List<ConstructorElement> superConstructors = superClassElement.constructors; | |
| 272 for (ConstructorElement superConstructor in superConstructors) { | |
| 273 String constructorName = superConstructor.name; | |
| 274 // skip private | |
| 275 if (Identifier.isPrivateName(constructorName)) { | |
| 276 continue; | |
| 277 } | |
| 278 // prepare SourceBuilder | |
| 279 SourceBuilder sb; | |
| 280 { | |
| 281 List<ConstructorInitializer> initializers = | |
| 282 targetConstructor.initializers; | |
| 283 if (initializers.isEmpty) { | |
| 284 int insertOffset = targetConstructor.parameters.end; | |
| 285 sb = new SourceBuilder(file, insertOffset); | |
| 286 sb.append(" : "); | |
| 287 } else { | |
| 288 ConstructorInitializer lastInitializer = | |
| 289 initializers[initializers.length - 1]; | |
| 290 int insertOffset = lastInitializer.end; | |
| 291 sb = new SourceBuilder(file, insertOffset); | |
| 292 sb.append(", "); | |
| 293 } | |
| 294 } | |
| 295 // add super constructor name | |
| 296 sb.append("super"); | |
| 297 if (!StringUtils.isEmpty(constructorName)) { | |
| 298 sb.append("."); | |
| 299 sb.append(constructorName); | |
| 300 } | |
| 301 // add arguments | |
| 302 sb.append("("); | |
| 303 bool firstParameter = true; | |
| 304 for (ParameterElement parameter in superConstructor.parameters) { | |
| 305 // skip non-required parameters | |
| 306 if (parameter.parameterKind != ParameterKind.REQUIRED) { | |
| 307 break; | |
| 308 } | |
| 309 // comma | |
| 310 if (firstParameter) { | |
| 311 firstParameter = false; | |
| 312 } else { | |
| 313 sb.append(", "); | |
| 314 } | |
| 315 // default value | |
| 316 DartType parameterType = parameter.type; | |
| 317 sb.startPosition(parameter.name); | |
| 318 sb.append(getDefaultValueCode(parameterType)); | |
| 319 sb.endPosition(); | |
| 320 } | |
| 321 sb.append(")"); | |
| 322 // insert proposal | |
| 323 _insertBuilder(sb); | |
| 324 // add proposal | |
| 325 String proposalName = _getConstructorProposalName(superConstructor); | |
| 326 _addFix(FixKind.ADD_SUPER_CONSTRUCTOR_INVOCATION, [proposalName]); | |
| 327 } | |
| 328 } | |
| 329 | |
| 330 void _addFix_createConstructorSuperImplicit() { | |
| 331 ClassDeclaration targetClassNode = node.parent as ClassDeclaration; | |
| 332 ClassElement targetClassElement = targetClassNode.element; | |
| 333 ClassElement superClassElement = targetClassElement.supertype.element; | |
| 334 String targetClassName = targetClassElement.name; | |
| 335 // add proposals for all super constructors | |
| 336 List<ConstructorElement> superConstructors = superClassElement.constructors; | |
| 337 for (ConstructorElement superConstructor in superConstructors) { | |
| 338 String constructorName = superConstructor.name; | |
| 339 // skip private | |
| 340 if (Identifier.isPrivateName(constructorName)) { | |
| 341 continue; | |
| 342 } | |
| 343 // prepare parameters and arguments | |
| 344 SourceBuilder parametersBuffer = new SourceBuilder.buffer(); | |
| 345 SourceBuilder argumentsBuffer = new SourceBuilder.buffer(); | |
| 346 bool firstParameter = true; | |
| 347 for (ParameterElement parameter in superConstructor.parameters) { | |
| 348 // skip non-required parameters | |
| 349 if (parameter.parameterKind != ParameterKind.REQUIRED) { | |
| 350 break; | |
| 351 } | |
| 352 // comma | |
| 353 if (firstParameter) { | |
| 354 firstParameter = false; | |
| 355 } else { | |
| 356 parametersBuffer.append(', '); | |
| 357 argumentsBuffer.append(', '); | |
| 358 } | |
| 359 // name | |
| 360 String parameterName = parameter.displayName; | |
| 361 if (parameterName.length > 1 && parameterName.startsWith('_')) { | |
| 362 parameterName = parameterName.substring(1); | |
| 363 } | |
| 364 // parameter & argument | |
| 365 _appendParameterSource(parametersBuffer, parameter.type, parameterName); | |
| 366 argumentsBuffer.append(parameterName); | |
| 367 } | |
| 368 // add proposal | |
| 369 _ConstructorLocation targetLocation = | |
| 370 _prepareNewConstructorLocation(targetClassNode); | |
| 371 SourceBuilder sb = new SourceBuilder(file, targetLocation._offset); | |
| 372 { | |
| 373 String indent = utils.getIndent(1); | |
| 374 sb.append(targetLocation._prefix); | |
| 375 sb.append(indent); | |
| 376 sb.append(targetClassName); | |
| 377 if (!constructorName.isEmpty) { | |
| 378 sb.startPosition('NAME'); | |
| 379 sb.append('.'); | |
| 380 sb.append(constructorName); | |
| 381 sb.endPosition(); | |
| 382 } | |
| 383 sb.append("("); | |
| 384 sb.append(parametersBuffer.toString()); | |
| 385 sb.append(') : super'); | |
| 386 if (!constructorName.isEmpty) { | |
| 387 sb.append('.'); | |
| 388 sb.append(constructorName); | |
| 389 } | |
| 390 sb.append('('); | |
| 391 sb.append(argumentsBuffer.toString()); | |
| 392 sb.append(');'); | |
| 393 sb.append(targetLocation._suffix); | |
| 394 } | |
| 395 _insertBuilder(sb); | |
| 396 // add proposal | |
| 397 String proposalName = _getConstructorProposalName(superConstructor); | |
| 398 _addFix(FixKind.CREATE_CONSTRUCTOR_SUPER, [proposalName]); | |
| 399 } | |
| 400 } | |
| 401 | |
| 402 void _addFix_createConstructor_insteadOfSyntheticDefault() { | |
| 403 TypeName typeName = null; | |
| 404 ConstructorName constructorName = null; | |
| 405 InstanceCreationExpression instanceCreation = null; | |
| 406 if (node is SimpleIdentifier) { | |
| 407 if (node.parent is TypeName) { | |
| 408 typeName = node.parent as TypeName; | |
| 409 if (typeName.name == node && typeName.parent is ConstructorName) { | |
| 410 constructorName = typeName.parent as ConstructorName; | |
| 411 // should be synthetic default constructor | |
| 412 { | |
| 413 ConstructorElement constructorElement = | |
| 414 constructorName.staticElement; | |
| 415 if (constructorElement == null || | |
| 416 !constructorElement.isDefaultConstructor || | |
| 417 !constructorElement.isSynthetic) { | |
| 418 return; | |
| 419 } | |
| 420 } | |
| 421 // prepare InstanceCreationExpression | |
| 422 if (constructorName.parent is InstanceCreationExpression) { | |
| 423 instanceCreation = constructorName.parent as | |
| 424 InstanceCreationExpression; | |
| 425 if (instanceCreation.constructorName != constructorName) { | |
| 426 return; | |
| 427 } | |
| 428 } | |
| 429 } | |
| 430 } | |
| 431 } | |
| 432 // do we have enough information? | |
| 433 if (instanceCreation == null) { | |
| 434 return; | |
| 435 } | |
| 436 // prepare target | |
| 437 DartType targetType = typeName.type; | |
| 438 if (targetType is! InterfaceType) { | |
| 439 return; | |
| 440 } | |
| 441 ClassElement targetElement = targetType.element as ClassElement; | |
| 442 String targetFile = targetElement.source.fullName; | |
| 443 ClassDeclaration targetClass = targetElement.node; | |
| 444 _ConstructorLocation targetLocation = | |
| 445 _prepareNewConstructorLocation(targetClass); | |
| 446 // build method source | |
| 447 SourceBuilder sb = new SourceBuilder(targetFile, targetLocation._offset); | |
| 448 { | |
| 449 String indent = " "; | |
| 450 sb.append(targetLocation._prefix); | |
| 451 sb.append(indent); | |
| 452 sb.append(targetElement.name); | |
| 453 _addFix_undefinedMethod_create_parameters( | |
| 454 sb, | |
| 455 instanceCreation.argumentList); | |
| 456 sb.append(") {${eol}${indent}}"); | |
| 457 sb.append(targetLocation._suffix); | |
| 458 } | |
| 459 // insert source | |
| 460 _insertBuilder(sb); | |
| 461 // add proposal | |
| 462 _addFix(FixKind.CREATE_CONSTRUCTOR, [constructorName], fixFile: targetFile); | |
| 463 } | |
| 464 | |
| 465 void _addFix_createConstructor_named() { | |
| 466 SimpleIdentifier name = null; | |
| 467 ConstructorName constructorName = null; | |
| 468 InstanceCreationExpression instanceCreation = null; | |
| 469 if (node is SimpleIdentifier) { | |
| 470 // name | |
| 471 name = node as SimpleIdentifier; | |
| 472 if (name.parent is ConstructorName) { | |
| 473 constructorName = name.parent as ConstructorName; | |
| 474 if (constructorName.name == name) { | |
| 475 // Type.name | |
| 476 if (constructorName.parent is InstanceCreationExpression) { | |
| 477 instanceCreation = constructorName.parent as | |
| 478 InstanceCreationExpression; | |
| 479 // new Type.name() | |
| 480 if (instanceCreation.constructorName != constructorName) { | |
| 481 return; | |
| 482 } | |
| 483 } | |
| 484 } | |
| 485 } | |
| 486 } | |
| 487 // do we have enough information? | |
| 488 if (instanceCreation == null) { | |
| 489 return; | |
| 490 } | |
| 491 // prepare target interface type | |
| 492 DartType targetType = constructorName.type.type; | |
| 493 if (targetType is! InterfaceType) { | |
| 494 return; | |
| 495 } | |
| 496 ClassElement targetElement = targetType.element as ClassElement; | |
| 497 String targetFile = targetElement.source.fullName; | |
| 498 ClassDeclaration targetClass = targetElement.node; | |
| 499 _ConstructorLocation targetLocation = | |
| 500 _prepareNewConstructorLocation(targetClass); | |
| 501 // build method source | |
| 502 SourceBuilder sb = new SourceBuilder(targetFile, targetLocation._offset); | |
| 503 { | |
| 504 String indent = " "; | |
| 505 sb.append(targetLocation._prefix); | |
| 506 sb.append(indent); | |
| 507 sb.append(targetElement.name); | |
| 508 sb.append("."); | |
| 509 // append name | |
| 510 { | |
| 511 sb.startPosition("NAME"); | |
| 512 sb.append(name.name); | |
| 513 sb.endPosition(); | |
| 514 } | |
| 515 _addFix_undefinedMethod_create_parameters( | |
| 516 sb, | |
| 517 instanceCreation.argumentList); | |
| 518 sb.append(") {${eol}${indent}}"); | |
| 519 sb.append(targetLocation._suffix); | |
| 520 } | |
| 521 // insert source | |
| 522 _insertBuilder(sb); | |
| 523 if (targetFile == file) { | |
| 524 _addLinkedPosition("NAME", rf.rangeNode(name)); | |
| 525 } | |
| 526 // add proposal | |
| 527 _addFix(FixKind.CREATE_CONSTRUCTOR, [constructorName], fixFile: targetFile); | |
| 528 } | |
| 529 | |
| 530 void _addFix_createFunction_forFunctionType() { | |
| 531 if (node is SimpleIdentifier) { | |
| 532 SimpleIdentifier nameNode = node as SimpleIdentifier; | |
| 533 // prepare argument expression (to get parameter) | |
| 534 ClassElement targetElement; | |
| 535 Expression argument; | |
| 536 { | |
| 537 Expression target = getQualifiedPropertyTarget(node); | |
| 538 if (target != null) { | |
| 539 DartType targetType = target.bestType; | |
| 540 if (targetType != null && targetType.element is ClassElement) { | |
| 541 targetElement = targetType.element as ClassElement; | |
| 542 argument = target.parent as Expression; | |
| 543 } else { | |
| 544 return; | |
| 545 } | |
| 546 } else { | |
| 547 ClassDeclaration enclosingClass = | |
| 548 node.getAncestor((node) => node is ClassDeclaration); | |
| 549 targetElement = enclosingClass != null ? | |
| 550 enclosingClass.element : | |
| 551 null; | |
| 552 argument = nameNode; | |
| 553 } | |
| 554 } | |
| 555 // should be argument of some invocation | |
| 556 ParameterElement parameterElement = argument.bestParameterElement; | |
| 557 if (parameterElement == null) { | |
| 558 return; | |
| 559 } | |
| 560 // should be parameter of function type | |
| 561 DartType parameterType = parameterElement.type; | |
| 562 if (parameterType is! FunctionType) { | |
| 563 return; | |
| 564 } | |
| 565 FunctionType functionType = parameterType as FunctionType; | |
| 566 // add proposal | |
| 567 if (targetElement != null) { | |
| 568 _addProposal_createFunction_method(targetElement, functionType); | |
| 569 } else { | |
| 570 _addProposal_createFunction_function(functionType); | |
| 571 } | |
| 572 } | |
| 573 } | |
| 574 | |
| 575 void | |
| 576 _addFix_createMissingOverrides(List<ExecutableElement> missingOverrides) { | |
| 577 // sort by name | |
| 578 missingOverrides.sort((Element firstElement, Element secondElement) { | |
| 579 return compareStrings( | |
| 580 firstElement.displayName, | |
| 581 secondElement.displayName); | |
| 582 }); | |
| 583 ClassDeclaration targetClass = node.parent as ClassDeclaration; | |
| 584 int insertOffset = targetClass.end - 1; | |
| 585 SourceBuilder sb = new SourceBuilder(file, insertOffset); | |
| 586 // add elements | |
| 587 bool isFirst = true; | |
| 588 for (ExecutableElement missingOverride in missingOverrides) { | |
| 589 if (!isFirst || !targetClass.members.isEmpty) { | |
| 590 sb.append(eol); | |
| 591 } | |
| 592 _addFix_createMissingOverrides_single(sb, targetClass, missingOverride); | |
| 593 isFirst = false; | |
| 594 } | |
| 595 // add proposal | |
| 596 exitPosition = new Position(file, insertOffset); | |
| 597 _insertBuilder(sb); | |
| 598 _addFix(FixKind.CREATE_MISSING_OVERRIDES, [missingOverrides.length]); | |
| 599 } | |
| 600 | |
| 601 void _addFix_createMissingOverrides_single(SourceBuilder sb, | |
| 602 ClassDeclaration targetClass, ExecutableElement missingOverride) { | |
| 603 // prepare environment | |
| 604 String prefix = utils.getIndent(1); | |
| 605 String prefix2 = utils.getIndent(2); | |
| 606 // may be property | |
| 607 ElementKind elementKind = missingOverride.kind; | |
| 608 bool isGetter = elementKind == ElementKind.GETTER; | |
| 609 bool isSetter = elementKind == ElementKind.SETTER; | |
| 610 bool isMethod = elementKind == ElementKind.METHOD; | |
| 611 bool isOperator = isMethod && (missingOverride as MethodElement).isOperator; | |
| 612 sb.append(prefix); | |
| 613 if (isGetter) { | |
| 614 sb.append('// TODO: implement ${missingOverride.displayName}'); | |
| 615 sb.append(eol); | |
| 616 sb.append(prefix); | |
| 617 } | |
| 618 // @override | |
| 619 { | |
| 620 sb.append('@override'); | |
| 621 sb.append(eol); | |
| 622 sb.append(prefix); | |
| 623 } | |
| 624 // return type | |
| 625 _appendType(sb, missingOverride.type.returnType); | |
| 626 if (isGetter) { | |
| 627 sb.append('get '); | |
| 628 } else if (isSetter) { | |
| 629 sb.append('set '); | |
| 630 } else if (isOperator) { | |
| 631 sb.append('operator '); | |
| 632 } | |
| 633 // name | |
| 634 sb.append(missingOverride.displayName); | |
| 635 // parameters + body | |
| 636 if (isGetter) { | |
| 637 sb.append(' => null;'); | |
| 638 } else { | |
| 639 List<ParameterElement> parameters = missingOverride.parameters; | |
| 640 _appendParameters(sb, parameters, _getDefaultValueMap(parameters)); | |
| 641 sb.append(' {'); | |
| 642 // TO-DO | |
| 643 sb.append(eol); | |
| 644 sb.append(prefix2); | |
| 645 sb.append('// TODO: implement ${missingOverride.displayName}'); | |
| 646 sb.append(eol); | |
| 647 // close method | |
| 648 sb.append(prefix); | |
| 649 sb.append('}'); | |
| 650 } | |
| 651 sb.append(eol); | |
| 652 } | |
| 653 | |
| 654 void _addFix_createNoSuchMethod() { | |
| 655 ClassDeclaration targetClass = node.parent as ClassDeclaration; | |
| 656 // prepare environment | |
| 657 String prefix = utils.getIndent(1); | |
| 658 int insertOffset = targetClass.end - 1; | |
| 659 // prepare source | |
| 660 SourceBuilder sb = new SourceBuilder(file, insertOffset); | |
| 661 { | |
| 662 // insert empty line before existing member | |
| 663 if (!targetClass.members.isEmpty) { | |
| 664 sb.append(eol); | |
| 665 } | |
| 666 // append method | |
| 667 sb.append(prefix); | |
| 668 sb.append( | |
| 669 "noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation)
;"); | |
| 670 sb.append(eol); | |
| 671 } | |
| 672 // done | |
| 673 _insertBuilder(sb); | |
| 674 exitPosition = new Position(file, insertOffset); | |
| 675 // add proposal | |
| 676 _addFix(FixKind.CREATE_NO_SUCH_METHOD, []); | |
| 677 } | |
| 678 | |
| 679 void _addFix_importLibrary(FixKind kind, String importPath) { | |
| 680 CompilationUnitElement libraryUnitElement = | |
| 681 unitLibraryElement.definingCompilationUnit; | |
| 682 CompilationUnit libraryUnit = libraryUnitElement.node; | |
| 683 // prepare new import location | |
| 684 int offset = 0; | |
| 685 String prefix; | |
| 686 String suffix; | |
| 687 { | |
| 688 // if no directives | |
| 689 prefix = ""; | |
| 690 suffix = eol; | |
| 691 CorrectionUtils libraryUtils = new CorrectionUtils(libraryUnit); | |
| 692 // after last directive in library | |
| 693 for (Directive directive in libraryUnit.directives) { | |
| 694 if (directive is LibraryDirective || directive is ImportDirective) { | |
| 695 offset = directive.end; | |
| 696 prefix = eol; | |
| 697 suffix = ""; | |
| 698 } | |
| 699 } | |
| 700 // if still beginning of file, skip shebang and line comments | |
| 701 if (offset == 0) { | |
| 702 CorrectionUtils_InsertDesc desc = libraryUtils.getInsertDescTop(); | |
| 703 offset = desc.offset; | |
| 704 prefix = desc.prefix; | |
| 705 suffix = "${desc.suffix}${eol}"; | |
| 706 } | |
| 707 } | |
| 708 // insert new import | |
| 709 String importSource = "${prefix}import '${importPath}';${suffix}"; | |
| 710 _addInsertEdit(offset, importSource); | |
| 711 // add proposal | |
| 712 _addFix(kind, [importPath], fixFile: libraryUnitElement.source.fullName); | |
| 713 } | |
| 714 | |
| 715 void _addFix_importLibrary_withElement(String name, ElementKind kind) { | |
| 716 // ignore if private | |
| 717 if (name.startsWith("_")) { | |
| 718 return; | |
| 719 } | |
| 720 | |
| 721 // may be there is an existing import, | |
| 722 // but it is with prefix and we don't use this prefix | |
| 723 for (ImportElement imp in unitLibraryElement.imports) { | |
| 724 // prepare element | |
| 725 LibraryElement libraryElement = imp.importedLibrary; | |
| 726 Element element = getExportedElement(libraryElement, name); | |
| 727 if (element == null) { | |
| 728 continue; | |
| 729 } | |
| 730 if (element is PropertyAccessorElement) { | |
| 731 element = (element as PropertyAccessorElement).variable; | |
| 732 } | |
| 733 if (element.kind != kind) { | |
| 734 continue; | |
| 735 } | |
| 736 // may be apply prefix | |
| 737 PrefixElement prefix = imp.prefix; | |
| 738 if (prefix != null) { | |
| 739 SourceRange range = rf.rangeStartLength(node, 0); | |
| 740 _addReplaceEdit(range, "${prefix.displayName}."); | |
| 741 _addFix( | |
| 742 FixKind.IMPORT_LIBRARY_PREFIX, | |
| 743 [libraryElement.displayName, prefix.displayName]); | |
| 744 continue; | |
| 745 } | |
| 746 // may be update "show" directive | |
| 747 List<NamespaceCombinator> combinators = imp.combinators; | |
| 748 if (combinators.length == 1 && combinators[0] is ShowElementCombinator) { | |
| 749 ShowElementCombinator showCombinator = | |
| 750 combinators[0] as ShowElementCombinator; | |
| 751 // prepare new set of names to show | |
| 752 Set<String> showNames = new SplayTreeSet<String>(); | |
| 753 showNames.addAll(showCombinator.shownNames); | |
| 754 showNames.add(name); | |
| 755 // prepare library name - unit name or 'dart:name' for SDK library | |
| 756 String libraryName = libraryElement.definingCompilationUnit.displayName; | |
| 757 if (libraryElement.isInSdk) { | |
| 758 libraryName = imp.uri; | |
| 759 } | |
| 760 // update library | |
| 761 String newShowCode = "show ${StringUtils.join(showNames, ", ")}"; | |
| 762 _addReplaceEdit(rf.rangeOffsetEnd(showCombinator), newShowCode); | |
| 763 _addFix( | |
| 764 FixKind.IMPORT_LIBRARY_SHOW, | |
| 765 [libraryName], | |
| 766 fixFile: unitLibraryFile); | |
| 767 // we support only one import without prefix | |
| 768 return; | |
| 769 } | |
| 770 } | |
| 771 // check SDK libraries | |
| 772 AnalysisContext context = unitLibraryElement.context; | |
| 773 { | |
| 774 DartSdk sdk = context.sourceFactory.dartSdk; | |
| 775 List<SdkLibrary> sdkLibraries = sdk.sdkLibraries; | |
| 776 for (SdkLibrary sdkLibrary in sdkLibraries) { | |
| 777 SourceFactory sdkSourceFactory = context.sourceFactory; | |
| 778 String libraryUri = 'dart:' + sdkLibrary.shortName; | |
| 779 Source librarySource = | |
| 780 sdkSourceFactory.resolveUri(unitSource, libraryUri); | |
| 781 // prepare LibraryElement | |
| 782 LibraryElement libraryElement = | |
| 783 context.getLibraryElement(librarySource); | |
| 784 if (libraryElement == null) { | |
| 785 continue; | |
| 786 } | |
| 787 // prepare exported Element | |
| 788 Element element = getExportedElement(libraryElement, name); | |
| 789 if (element == null) { | |
| 790 continue; | |
| 791 } | |
| 792 if (element is PropertyAccessorElement) { | |
| 793 element = (element as PropertyAccessorElement).variable; | |
| 794 } | |
| 795 if (element.kind != kind) { | |
| 796 continue; | |
| 797 } | |
| 798 // add import | |
| 799 _addFix_importLibrary(FixKind.IMPORT_LIBRARY_SDK, libraryUri); | |
| 800 } | |
| 801 } | |
| 802 // check project libraries | |
| 803 { | |
| 804 List<Source> librarySources = context.librarySources; | |
| 805 for (Source librarySource in librarySources) { | |
| 806 // we don't need SDK libraries here | |
| 807 if (librarySource.isInSystemLibrary) { | |
| 808 continue; | |
| 809 } | |
| 810 // prepare LibraryElement | |
| 811 LibraryElement libraryElement = | |
| 812 context.getLibraryElement(librarySource); | |
| 813 if (libraryElement == null) { | |
| 814 continue; | |
| 815 } | |
| 816 // prepare exported Element | |
| 817 Element element = getExportedElement(libraryElement, name); | |
| 818 if (element == null) { | |
| 819 continue; | |
| 820 } | |
| 821 if (element is PropertyAccessorElement) { | |
| 822 element = (element as PropertyAccessorElement).variable; | |
| 823 } | |
| 824 if (element.kind != kind) { | |
| 825 continue; | |
| 826 } | |
| 827 // prepare "library" file | |
| 828 String libraryFile = librarySource.fullName; | |
| 829 // may be "package:" URI | |
| 830 { | |
| 831 String libraryPackageUri = _findPackageUri(context, libraryFile); | |
| 832 if (libraryPackageUri != null) { | |
| 833 _addFix_importLibrary( | |
| 834 FixKind.IMPORT_LIBRARY_PROJECT, | |
| 835 libraryPackageUri); | |
| 836 continue; | |
| 837 } | |
| 838 } | |
| 839 // relative URI | |
| 840 String relativeFile = relative(libraryFile, from: unitLibraryFolder); | |
| 841 relativeFile = split(relativeFile).join('/'); | |
| 842 _addFix_importLibrary(FixKind.IMPORT_LIBRARY_PROJECT, relativeFile); | |
| 843 } | |
| 844 } | |
| 845 } | |
| 846 | |
| 847 void _addFix_importLibrary_withFunction() { | |
| 848 if (node is SimpleIdentifier && node.parent is MethodInvocation) { | |
| 849 MethodInvocation invocation = node.parent as MethodInvocation; | |
| 850 if (invocation.realTarget == null && invocation.methodName == node) { | |
| 851 String name = (node as SimpleIdentifier).name; | |
| 852 _addFix_importLibrary_withElement(name, ElementKind.FUNCTION); | |
| 853 } | |
| 854 } | |
| 855 } | |
| 856 | |
| 857 void _addFix_importLibrary_withTopLevelVariable() { | |
| 858 if (node is SimpleIdentifier) { | |
| 859 String name = (node as SimpleIdentifier).name; | |
| 860 _addFix_importLibrary_withElement(name, ElementKind.TOP_LEVEL_VARIABLE); | |
| 861 } | |
| 862 } | |
| 863 | |
| 864 void _addFix_importLibrary_withType() { | |
| 865 if (_mayBeTypeIdentifier(node)) { | |
| 866 String typeName = (node as SimpleIdentifier).name; | |
| 867 _addFix_importLibrary_withElement(typeName, ElementKind.CLASS); | |
| 868 } | |
| 869 } | |
| 870 | |
| 871 void _addFix_insertSemicolon() { | |
| 872 if (error.message.contains("';'")) { | |
| 873 int insertOffset = error.offset + error.length; | |
| 874 _addInsertEdit(insertOffset, ";"); | |
| 875 _addFix(FixKind.INSERT_SEMICOLON, []); | |
| 876 } | |
| 877 } | |
| 878 | |
| 879 void _addFix_isNotNull() { | |
| 880 if (coveredNode is IsExpression) { | |
| 881 IsExpression isExpression = coveredNode as IsExpression; | |
| 882 _addReplaceEdit( | |
| 883 rf.rangeEndEnd(isExpression.expression, isExpression), | |
| 884 " != null"); | |
| 885 _addFix(FixKind.USE_NOT_EQ_NULL, []); | |
| 886 } | |
| 887 } | |
| 888 | |
| 889 void _addFix_isNull() { | |
| 890 if (coveredNode is IsExpression) { | |
| 891 IsExpression isExpression = coveredNode as IsExpression; | |
| 892 _addReplaceEdit( | |
| 893 rf.rangeEndEnd(isExpression.expression, isExpression), | |
| 894 " == null"); | |
| 895 _addFix(FixKind.USE_EQ_EQ_NULL, []); | |
| 896 } | |
| 897 } | |
| 898 | |
| 899 void _addFix_makeEnclosingClassAbstract() { | |
| 900 ClassDeclaration enclosingClass = | |
| 901 node.getAncestor((node) => node is ClassDeclaration); | |
| 902 String className = enclosingClass.name.name; | |
| 903 _addInsertEdit(enclosingClass.classKeyword.offset, "abstract "); | |
| 904 _addFix(FixKind.MAKE_CLASS_ABSTRACT, [className]); | |
| 905 } | |
| 906 | |
| 907 void _addFix_removeParameters_inGetterDeclaration() { | |
| 908 if (node is SimpleIdentifier && node.parent is MethodDeclaration) { | |
| 909 MethodDeclaration method = node.parent as MethodDeclaration; | |
| 910 FunctionBody body = method.body; | |
| 911 if (method.name == node && body != null) { | |
| 912 _addReplaceEdit(rf.rangeEndStart(node, body), " "); | |
| 913 _addFix(FixKind.REMOVE_PARAMETERS_IN_GETTER_DECLARATION, []); | |
| 914 } | |
| 915 } | |
| 916 } | |
| 917 | |
| 918 void _addFix_removeParentheses_inGetterInvocation() { | |
| 919 if (node is SimpleIdentifier && node.parent is MethodInvocation) { | |
| 920 MethodInvocation invocation = node.parent as MethodInvocation; | |
| 921 if (invocation.methodName == node && invocation.target != null) { | |
| 922 _addRemoveEdit(rf.rangeEndEnd(node, invocation)); | |
| 923 _addFix(FixKind.REMOVE_PARENTHESIS_IN_GETTER_INVOCATION, []); | |
| 924 } | |
| 925 } | |
| 926 } | |
| 927 | |
| 928 void _addFix_removeUnnecessaryCast() { | |
| 929 if (coveredNode is! AsExpression) { | |
| 930 return; | |
| 931 } | |
| 932 AsExpression asExpression = coveredNode as AsExpression; | |
| 933 Expression expression = asExpression.expression; | |
| 934 int expressionPrecedence = getExpressionPrecedence(expression); | |
| 935 // remove 'as T' from 'e as T' | |
| 936 _addRemoveEdit(rf.rangeEndEnd(expression, asExpression)); | |
| 937 _removeEnclosingParentheses(asExpression, expressionPrecedence); | |
| 938 // done | |
| 939 _addFix(FixKind.REMOVE_UNNECASSARY_CAST, []); | |
| 940 } | |
| 941 | |
| 942 void _addFix_removeUnusedImport() { | |
| 943 // prepare ImportDirective | |
| 944 ImportDirective importDirective = | |
| 945 node.getAncestor((node) => node is ImportDirective); | |
| 946 if (importDirective == null) { | |
| 947 return; | |
| 948 } | |
| 949 // remove the whole line with import | |
| 950 _addRemoveEdit(utils.getLinesRange(rf.rangeNode(importDirective))); | |
| 951 // done | |
| 952 _addFix(FixKind.REMOVE_UNUSED_IMPORT, []); | |
| 953 } | |
| 954 | |
| 955 void _addFix_replaceWithConstInstanceCreation() { | |
| 956 if (coveredNode is InstanceCreationExpression) { | |
| 957 var instanceCreation = coveredNode as InstanceCreationExpression; | |
| 958 _addReplaceEdit(rf.rangeToken(instanceCreation.keyword), "const"); | |
| 959 _addFix(FixKind.USE_CONST, []); | |
| 960 } | |
| 961 } | |
| 962 | |
| 963 void _addFix_undefinedClass_useSimilar() { | |
| 964 if (_mayBeTypeIdentifier(node)) { | |
| 965 String name = (node as SimpleIdentifier).name; | |
| 966 _ClosestElementFinder finder = | |
| 967 new _ClosestElementFinder( | |
| 968 name, | |
| 969 (Element element) => element is ClassElement, | |
| 970 MAX_LEVENSHTEIN_DISTANCE); | |
| 971 // find closest element | |
| 972 { | |
| 973 // elements of this library | |
| 974 for (CompilationUnitElement unit in unitLibraryElement.units) { | |
| 975 finder._updateList(unit.types); | |
| 976 } | |
| 977 // elements from imports | |
| 978 for (ImportElement importElement in unitLibraryElement.imports) { | |
| 979 if (importElement.prefix == null) { | |
| 980 Map<String, Element> namespace = getImportNamespace(importElement); | |
| 981 finder._updateList(namespace.values); | |
| 982 } | |
| 983 } | |
| 984 } | |
| 985 // if we have close enough element, suggest to use it | |
| 986 if (finder._element != null) { | |
| 987 String closestName = finder._element.name; | |
| 988 _addReplaceEdit(rf.rangeNode(node), closestName); | |
| 989 // add proposal | |
| 990 if (closestName != null) { | |
| 991 _addFix(FixKind.CHANGE_TO, [closestName]); | |
| 992 } | |
| 993 } | |
| 994 } | |
| 995 } | |
| 996 | |
| 997 void _addFix_undefinedFunction_create() { | |
| 998 // should be the name of the invocation | |
| 999 if (node is SimpleIdentifier && node.parent is MethodInvocation) { | |
| 1000 } else { | |
| 1001 return; | |
| 1002 } | |
| 1003 String name = (node as SimpleIdentifier).name; | |
| 1004 MethodInvocation invocation = node.parent as MethodInvocation; | |
| 1005 // function invocation has no target | |
| 1006 Expression target = invocation.realTarget; | |
| 1007 if (target != null) { | |
| 1008 return; | |
| 1009 } | |
| 1010 // prepare environment | |
| 1011 int insertOffset; | |
| 1012 String sourcePrefix; | |
| 1013 AstNode enclosingMember = | |
| 1014 node.getAncestor((node) => node is CompilationUnitMember); | |
| 1015 insertOffset = enclosingMember.end; | |
| 1016 sourcePrefix = "${eol}${eol}"; | |
| 1017 // build method source | |
| 1018 SourceBuilder sb = new SourceBuilder(file, insertOffset); | |
| 1019 { | |
| 1020 sb.append(sourcePrefix); | |
| 1021 // append return type | |
| 1022 { | |
| 1023 DartType type = _inferReturnType(invocation); | |
| 1024 _appendType(sb, type, 'RETURN_TYPE'); | |
| 1025 } | |
| 1026 // append name | |
| 1027 { | |
| 1028 sb.startPosition("NAME"); | |
| 1029 sb.append(name); | |
| 1030 sb.endPosition(); | |
| 1031 } | |
| 1032 _addFix_undefinedMethod_create_parameters(sb, invocation.argumentList); | |
| 1033 sb.append(") {${eol}}"); | |
| 1034 } | |
| 1035 // insert source | |
| 1036 _insertBuilder(sb); | |
| 1037 _addLinkedPosition3('NAME', sb, rf.rangeNode(node)); | |
| 1038 // add proposal | |
| 1039 _addFix(FixKind.CREATE_FUNCTION, [name]); | |
| 1040 } | |
| 1041 | |
| 1042 void _addFix_undefinedFunction_useSimilar() { | |
| 1043 if (node is SimpleIdentifier) { | |
| 1044 String name = (node as SimpleIdentifier).name; | |
| 1045 _ClosestElementFinder finder = | |
| 1046 new _ClosestElementFinder( | |
| 1047 name, | |
| 1048 (Element element) => element is FunctionElement, | |
| 1049 MAX_LEVENSHTEIN_DISTANCE); | |
| 1050 // this library | |
| 1051 for (CompilationUnitElement unit in unitLibraryElement.units) { | |
| 1052 finder._updateList(unit.functions); | |
| 1053 } | |
| 1054 // imports | |
| 1055 for (ImportElement importElement in unitLibraryElement.imports) { | |
| 1056 if (importElement.prefix == null) { | |
| 1057 Map<String, Element> namespace = getImportNamespace(importElement); | |
| 1058 finder._updateList(namespace.values); | |
| 1059 } | |
| 1060 } | |
| 1061 // if we have close enough element, suggest to use it | |
| 1062 if (finder._element != null) { | |
| 1063 String closestName = finder._element.name; | |
| 1064 _addReplaceEdit(rf.rangeNode(node), closestName); | |
| 1065 _addFix(FixKind.CHANGE_TO, [closestName]); | |
| 1066 } | |
| 1067 } | |
| 1068 } | |
| 1069 | |
| 1070 void _addFix_undefinedMethod_create() { | |
| 1071 if (node is SimpleIdentifier && node.parent is MethodInvocation) { | |
| 1072 String name = (node as SimpleIdentifier).name; | |
| 1073 MethodInvocation invocation = node.parent as MethodInvocation; | |
| 1074 // prepare environment | |
| 1075 Source targetSource; | |
| 1076 String prefix; | |
| 1077 int insertOffset; | |
| 1078 String sourcePrefix; | |
| 1079 String sourceSuffix; | |
| 1080 bool staticModifier = false; | |
| 1081 Expression target = invocation.realTarget; | |
| 1082 if (target == null) { | |
| 1083 targetSource = source; | |
| 1084 ClassMember enclosingMember = | |
| 1085 node.getAncestor((node) => node is ClassMember); | |
| 1086 staticModifier = _inStaticContext(); | |
| 1087 prefix = utils.getNodePrefix(enclosingMember); | |
| 1088 insertOffset = enclosingMember.end; | |
| 1089 sourcePrefix = "${eol}${eol}"; | |
| 1090 sourceSuffix = ""; | |
| 1091 } else { | |
| 1092 // prepare target interface type | |
| 1093 DartType targetType = target.bestType; | |
| 1094 if (targetType is! InterfaceType) { | |
| 1095 return; | |
| 1096 } | |
| 1097 ClassElement targetElement = targetType.element as ClassElement; | |
| 1098 targetSource = targetElement.source; | |
| 1099 // may be static | |
| 1100 if (target is Identifier) { | |
| 1101 staticModifier = target.bestElement.kind == ElementKind.CLASS; | |
| 1102 } | |
| 1103 // prepare insert offset | |
| 1104 ClassDeclaration targetClass = targetElement.node; | |
| 1105 prefix = " "; | |
| 1106 insertOffset = targetClass.end - 1; | |
| 1107 if (targetClass.members.isEmpty) { | |
| 1108 sourcePrefix = ""; | |
| 1109 } else { | |
| 1110 sourcePrefix = eol; | |
| 1111 } | |
| 1112 sourceSuffix = eol; | |
| 1113 } | |
| 1114 String targetFile = targetSource.fullName; | |
| 1115 // build method source | |
| 1116 SourceBuilder sb = new SourceBuilder(targetFile, insertOffset); | |
| 1117 { | |
| 1118 sb.append(sourcePrefix); | |
| 1119 sb.append(prefix); | |
| 1120 // maybe "static" | |
| 1121 if (staticModifier) { | |
| 1122 sb.append("static "); | |
| 1123 } | |
| 1124 // append return type | |
| 1125 _appendType(sb, _inferReturnType(invocation), 'RETURN_TYPE'); | |
| 1126 // append name | |
| 1127 { | |
| 1128 sb.startPosition("NAME"); | |
| 1129 sb.append(name); | |
| 1130 sb.endPosition(); | |
| 1131 } | |
| 1132 _addFix_undefinedMethod_create_parameters(sb, invocation.argumentList); | |
| 1133 sb.append(") {${eol}${prefix}}"); | |
| 1134 sb.append(sourceSuffix); | |
| 1135 } | |
| 1136 // insert source | |
| 1137 _insertBuilder(sb); | |
| 1138 // add linked positions | |
| 1139 if (targetSource == source) { | |
| 1140 _addLinkedPosition3('NAME', sb, rf.rangeNode(node)); | |
| 1141 } | |
| 1142 // add proposal | |
| 1143 _addFix(FixKind.CREATE_METHOD, [name], fixFile: targetFile); | |
| 1144 } | |
| 1145 } | |
| 1146 | |
| 1147 void _addFix_undefinedMethod_create_parameters(SourceBuilder sb, | |
| 1148 ArgumentList argumentList) { | |
| 1149 // append parameters | |
| 1150 sb.append("("); | |
| 1151 Set<String> excluded = new Set(); | |
| 1152 List<Expression> arguments = argumentList.arguments; | |
| 1153 for (int i = 0; i < arguments.length; i++) { | |
| 1154 Expression argument = arguments[i]; | |
| 1155 // append separator | |
| 1156 if (i != 0) { | |
| 1157 sb.append(", "); | |
| 1158 } | |
| 1159 // append type name | |
| 1160 DartType type = argument.bestType; | |
| 1161 String typeSource = utils.getTypeSource(type); | |
| 1162 { | |
| 1163 sb.startPosition("TYPE${i}"); | |
| 1164 sb.append(typeSource); | |
| 1165 _addSuperTypeProposals(sb, new Set(), type); | |
| 1166 sb.endPosition(); | |
| 1167 } | |
| 1168 sb.append(" "); | |
| 1169 // append parameter name | |
| 1170 { | |
| 1171 List<String> suggestions = | |
| 1172 _getArgumentNameSuggestions(excluded, type, argument, i); | |
| 1173 String favorite = suggestions[0]; | |
| 1174 excluded.add(favorite); | |
| 1175 sb.startPosition("ARG${i}"); | |
| 1176 sb.append(favorite); | |
| 1177 sb.addSuggestions(LinkedEditSuggestionKind.PARAMETER, suggestions); | |
| 1178 sb.endPosition(); | |
| 1179 } | |
| 1180 } | |
| 1181 } | |
| 1182 | |
| 1183 void _addFix_undefinedMethod_useSimilar() { | |
| 1184 if (node is SimpleIdentifier && node.parent is MethodInvocation) { | |
| 1185 MethodInvocation invocation = node.parent as MethodInvocation; | |
| 1186 String name = (node as SimpleIdentifier).name; | |
| 1187 _ClosestElementFinder finder = | |
| 1188 new _ClosestElementFinder( | |
| 1189 name, | |
| 1190 (Element element) => element is MethodElement && !element.isOperat
or, | |
| 1191 MAX_LEVENSHTEIN_DISTANCE); | |
| 1192 // unqualified invocation | |
| 1193 Expression target = invocation.realTarget; | |
| 1194 if (target == null) { | |
| 1195 ClassDeclaration clazz = | |
| 1196 invocation.getAncestor((node) => node is ClassDeclaration); | |
| 1197 if (clazz != null) { | |
| 1198 ClassElement classElement = clazz.element; | |
| 1199 _updateFinderWithClassMembers(finder, classElement); | |
| 1200 } | |
| 1201 } else { | |
| 1202 DartType type = target.bestType; | |
| 1203 if (type is InterfaceType) { | |
| 1204 ClassElement classElement = type.element; | |
| 1205 _updateFinderWithClassMembers(finder, classElement); | |
| 1206 } | |
| 1207 } | |
| 1208 // if we have close enough element, suggest to use it | |
| 1209 if (finder._element != null) { | |
| 1210 String closestName = finder._element.name; | |
| 1211 _addReplaceEdit(rf.rangeNode(node), closestName); | |
| 1212 _addFix(FixKind.CHANGE_TO, [closestName]); | |
| 1213 } | |
| 1214 } | |
| 1215 } | |
| 1216 | |
| 1217 void _addFix_useEffectiveIntegerDivision() { | |
| 1218 for (AstNode n = node; n != null; n = n.parent) { | |
| 1219 if (n is MethodInvocation && | |
| 1220 n.offset == errorOffset && | |
| 1221 n.length == errorLength) { | |
| 1222 MethodInvocation invocation = n as MethodInvocation; | |
| 1223 Expression target = invocation.target; | |
| 1224 while (target is ParenthesizedExpression) { | |
| 1225 target = (target as ParenthesizedExpression).expression; | |
| 1226 } | |
| 1227 // replace "/" with "~/" | |
| 1228 BinaryExpression binary = target as BinaryExpression; | |
| 1229 _addReplaceEdit(rf.rangeToken(binary.operator), "~/"); | |
| 1230 // remove everything before and after | |
| 1231 _addRemoveEdit(rf.rangeStartStart(invocation, binary.leftOperand)); | |
| 1232 _addRemoveEdit(rf.rangeEndEnd(binary.rightOperand, invocation)); | |
| 1233 // add proposal | |
| 1234 _addFix(FixKind.USE_EFFECTIVE_INTEGER_DIVISION, []); | |
| 1235 // done | |
| 1236 break; | |
| 1237 } | |
| 1238 } | |
| 1239 } | |
| 1240 | |
| 1241 void _addFix_useStaticAccess_method() { | |
| 1242 if (node is SimpleIdentifier && node.parent is MethodInvocation) { | |
| 1243 MethodInvocation invocation = node.parent as MethodInvocation; | |
| 1244 if (invocation.methodName == node) { | |
| 1245 Expression target = invocation.target; | |
| 1246 String targetType = utils.getExpressionTypeSource(target); | |
| 1247 // replace "target" with class name | |
| 1248 SourceRange range = rf.rangeNode(target); | |
| 1249 _addReplaceEdit(range, targetType); | |
| 1250 // add proposal | |
| 1251 _addFix(FixKind.CHANGE_TO_STATIC_ACCESS, [targetType]); | |
| 1252 } | |
| 1253 } | |
| 1254 } | |
| 1255 | |
| 1256 void _addFix_useStaticAccess_property() { | |
| 1257 if (node is SimpleIdentifier) { | |
| 1258 if (node.parent is PrefixedIdentifier) { | |
| 1259 PrefixedIdentifier prefixed = node.parent as PrefixedIdentifier; | |
| 1260 if (prefixed.identifier == node) { | |
| 1261 Expression target = prefixed.prefix; | |
| 1262 String targetType = utils.getExpressionTypeSource(target); | |
| 1263 // replace "target" with class name | |
| 1264 SourceRange range = rf.rangeNode(target); | |
| 1265 _addReplaceEdit(range, targetType); | |
| 1266 // add proposal | |
| 1267 _addFix(FixKind.CHANGE_TO_STATIC_ACCESS, [targetType]); | |
| 1268 } | |
| 1269 } | |
| 1270 } | |
| 1271 } | |
| 1272 | |
| 1273 /** | |
| 1274 * Adds a new [Edit] to [edits]. | |
| 1275 */ | |
| 1276 void _addInsertEdit(int offset, String text) { | |
| 1277 Edit edit = new Edit(offset, 0, text); | |
| 1278 edits.add(edit); | |
| 1279 } | |
| 1280 | |
| 1281 /** | |
| 1282 * Adds a single linked position to [groupId]. | |
| 1283 */ | |
| 1284 void _addLinkedPosition(String groupId, SourceRange range) { | |
| 1285 Position position = new Position(file, range.offset); | |
| 1286 LinkedEditGroup group = _getLinkedPosition(groupId); | |
| 1287 group.addPosition(position, range.length); | |
| 1288 } | |
| 1289 | |
| 1290 /** | |
| 1291 * Adds a single linked position to [groupId]. | |
| 1292 */ | |
| 1293 void _addLinkedPosition3(String groupId, SourceBuilder sb, | |
| 1294 SourceRange range) { | |
| 1295 if (sb.offset < range.offset) { | |
| 1296 int delta = sb.length; | |
| 1297 range = range.getTranslated(delta); | |
| 1298 } | |
| 1299 _addLinkedPosition(groupId, range); | |
| 1300 } | |
| 1301 | |
| 1302 /** | |
| 1303 * Prepares proposal for creating function corresponding to the given [Functio
nType]. | |
| 1304 */ | |
| 1305 void _addProposal_createFunction(FunctionType functionType, String name, | |
| 1306 Source targetSource, int insertOffset, bool isStatic, String prefix, | |
| 1307 String sourcePrefix, String sourceSuffix) { | |
| 1308 // build method source | |
| 1309 String targetFile = targetSource.fullName; | |
| 1310 SourceBuilder sb = new SourceBuilder(targetFile, insertOffset); | |
| 1311 { | |
| 1312 sb.append(sourcePrefix); | |
| 1313 sb.append(prefix); | |
| 1314 // may be static | |
| 1315 if (isStatic) { | |
| 1316 sb.append("static "); | |
| 1317 } | |
| 1318 // append return type | |
| 1319 _appendType(sb, functionType.returnType, 'RETURN_TYPE'); | |
| 1320 // append name | |
| 1321 { | |
| 1322 sb.startPosition("NAME"); | |
| 1323 sb.append(name); | |
| 1324 sb.endPosition(); | |
| 1325 } | |
| 1326 // append parameters | |
| 1327 sb.append("("); | |
| 1328 List<ParameterElement> parameters = functionType.parameters; | |
| 1329 for (int i = 0; i < parameters.length; i++) { | |
| 1330 ParameterElement parameter = parameters[i]; | |
| 1331 // append separator | |
| 1332 if (i != 0) { | |
| 1333 sb.append(", "); | |
| 1334 } | |
| 1335 // append type name | |
| 1336 DartType type = parameter.type; | |
| 1337 if (!type.isDynamic) { | |
| 1338 String typeSource = utils.getTypeSource(type); | |
| 1339 { | |
| 1340 sb.startPosition("TYPE${i}"); | |
| 1341 sb.append(typeSource); | |
| 1342 _addSuperTypeProposals(sb, new Set(), type); | |
| 1343 sb.endPosition(); | |
| 1344 } | |
| 1345 sb.append(" "); | |
| 1346 } | |
| 1347 // append parameter name | |
| 1348 { | |
| 1349 sb.startPosition("ARG${i}"); | |
| 1350 sb.append(parameter.displayName); | |
| 1351 sb.endPosition(); | |
| 1352 } | |
| 1353 } | |
| 1354 sb.append(")"); | |
| 1355 // close method | |
| 1356 sb.append(" {${eol}${prefix}}"); | |
| 1357 sb.append(sourceSuffix); | |
| 1358 } | |
| 1359 // insert source | |
| 1360 _insertBuilder(sb); | |
| 1361 // add linked positions | |
| 1362 if (targetSource == source) { | |
| 1363 _addLinkedPosition3("NAME", sb, rf.rangeNode(node)); | |
| 1364 } | |
| 1365 } | |
| 1366 | |
| 1367 /** | |
| 1368 * Adds proposal for creating method corresponding to the given [FunctionType]
in the given | |
| 1369 * [ClassElement]. | |
| 1370 */ | |
| 1371 void _addProposal_createFunction_function(FunctionType functionType) { | |
| 1372 String name = (node as SimpleIdentifier).name; | |
| 1373 // prepare environment | |
| 1374 int insertOffset = unit.end; | |
| 1375 // prepare prefix | |
| 1376 String prefix = ""; | |
| 1377 String sourcePrefix = "${eol}"; | |
| 1378 String sourceSuffix = eol; | |
| 1379 _addProposal_createFunction( | |
| 1380 functionType, | |
| 1381 name, | |
| 1382 source, | |
| 1383 insertOffset, | |
| 1384 false, | |
| 1385 prefix, | |
| 1386 sourcePrefix, | |
| 1387 sourceSuffix); | |
| 1388 // add proposal | |
| 1389 _addFix(FixKind.CREATE_FUNCTION, [name], fixFile: file); | |
| 1390 } | |
| 1391 | |
| 1392 /** | |
| 1393 * Adds proposal for creating method corresponding to the given [FunctionType]
in the given | |
| 1394 * [ClassElement]. | |
| 1395 */ | |
| 1396 void _addProposal_createFunction_method(ClassElement targetClassElement, | |
| 1397 FunctionType functionType) { | |
| 1398 String name = (node as SimpleIdentifier).name; | |
| 1399 // prepare environment | |
| 1400 Source targetSource = targetClassElement.source; | |
| 1401 String targetFile = targetSource.fullName; | |
| 1402 // prepare insert offset | |
| 1403 ClassDeclaration targetClassNode = targetClassElement.node; | |
| 1404 int insertOffset = targetClassNode.end - 1; | |
| 1405 // prepare prefix | |
| 1406 String prefix = " "; | |
| 1407 String sourcePrefix; | |
| 1408 if (targetClassNode.members.isEmpty) { | |
| 1409 sourcePrefix = ""; | |
| 1410 } else { | |
| 1411 sourcePrefix = eol; | |
| 1412 } | |
| 1413 String sourceSuffix = eol; | |
| 1414 _addProposal_createFunction( | |
| 1415 functionType, | |
| 1416 name, | |
| 1417 targetSource, | |
| 1418 insertOffset, | |
| 1419 _inStaticContext(), | |
| 1420 prefix, | |
| 1421 sourcePrefix, | |
| 1422 sourceSuffix); | |
| 1423 // add proposal | |
| 1424 _addFix(FixKind.CREATE_METHOD, [name], fixFile: targetFile); | |
| 1425 } | |
| 1426 | |
| 1427 /** | |
| 1428 * Adds a new [Edit] to [edits]. | |
| 1429 */ | |
| 1430 void _addRemoveEdit(SourceRange range) { | |
| 1431 _addReplaceEdit(range, ''); | |
| 1432 } | |
| 1433 | |
| 1434 /** | |
| 1435 * Adds a new [Edit] to [edits]. | |
| 1436 */ | |
| 1437 void _addReplaceEdit(SourceRange range, String text) { | |
| 1438 Edit edit = new Edit(range.offset, range.length, text); | |
| 1439 edits.add(edit); | |
| 1440 } | |
| 1441 | |
| 1442 void _appendParameterSource(SourceBuilder sb, DartType type, String name) { | |
| 1443 String parameterSource = utils.getParameterSource(type, name); | |
| 1444 sb.append(parameterSource); | |
| 1445 } | |
| 1446 | |
| 1447 void _appendParameters(SourceBuilder sb, List<ParameterElement> parameters, | |
| 1448 Map<ParameterElement, String> defaultValueMap) { | |
| 1449 sb.append("("); | |
| 1450 bool firstParameter = true; | |
| 1451 bool sawNamed = false; | |
| 1452 bool sawPositional = false; | |
| 1453 for (ParameterElement parameter in parameters) { | |
| 1454 if (!firstParameter) { | |
| 1455 sb.append(", "); | |
| 1456 } else { | |
| 1457 firstParameter = false; | |
| 1458 } | |
| 1459 // may be optional | |
| 1460 ParameterKind parameterKind = parameter.parameterKind; | |
| 1461 if (parameterKind == ParameterKind.NAMED) { | |
| 1462 if (!sawNamed) { | |
| 1463 sb.append("{"); | |
| 1464 sawNamed = true; | |
| 1465 } | |
| 1466 } | |
| 1467 if (parameterKind == ParameterKind.POSITIONAL) { | |
| 1468 if (!sawPositional) { | |
| 1469 sb.append("["); | |
| 1470 sawPositional = true; | |
| 1471 } | |
| 1472 } | |
| 1473 // parameter | |
| 1474 _appendParameterSource(sb, parameter.type, parameter.name); | |
| 1475 // default value | |
| 1476 if (defaultValueMap != null) { | |
| 1477 String defaultSource = defaultValueMap[parameter]; | |
| 1478 if (defaultSource != null) { | |
| 1479 if (sawPositional) { | |
| 1480 sb.append(" = "); | |
| 1481 } else { | |
| 1482 sb.append(": "); | |
| 1483 } | |
| 1484 sb.append(defaultSource); | |
| 1485 } | |
| 1486 } | |
| 1487 } | |
| 1488 // close parameters | |
| 1489 if (sawNamed) { | |
| 1490 sb.append("}"); | |
| 1491 } | |
| 1492 if (sawPositional) { | |
| 1493 sb.append("]"); | |
| 1494 } | |
| 1495 sb.append(")"); | |
| 1496 } | |
| 1497 | |
| 1498 void _appendType(SourceBuilder sb, DartType type, [String groupId]) { | |
| 1499 if (type != null && !type.isDynamic) { | |
| 1500 String typeSource = utils.getTypeSource(type); | |
| 1501 if (groupId != null) { | |
| 1502 sb.startPosition(groupId); | |
| 1503 sb.append(typeSource); | |
| 1504 sb.endPosition(); | |
| 1505 } else { | |
| 1506 sb.append(typeSource); | |
| 1507 } | |
| 1508 sb.append(' '); | |
| 1509 } | |
| 1510 } | |
| 1511 | |
| 1512 /** | |
| 1513 * @return the string to display as the name of the given constructor in a pro
posal name. | |
| 1514 */ | |
| 1515 String _getConstructorProposalName(ConstructorElement constructor) { | |
| 1516 SourceBuilder proposalNameBuffer = new SourceBuilder.buffer(); | |
| 1517 proposalNameBuffer.append("super"); | |
| 1518 // may be named | |
| 1519 String constructorName = constructor.displayName; | |
| 1520 if (!constructorName.isEmpty) { | |
| 1521 proposalNameBuffer.append("."); | |
| 1522 proposalNameBuffer.append(constructorName); | |
| 1523 } | |
| 1524 // parameters | |
| 1525 _appendParameters(proposalNameBuffer, constructor.parameters, null); | |
| 1526 // done | |
| 1527 return proposalNameBuffer.toString(); | |
| 1528 } | |
| 1529 | |
| 1530 /** | |
| 1531 * Returns the [Type] with given name from the `dart:core` library. | |
| 1532 */ | |
| 1533 DartType _getCoreType(String name) { | |
| 1534 List<LibraryElement> libraries = unitLibraryElement.importedLibraries; | |
| 1535 for (LibraryElement library in libraries) { | |
| 1536 if (library.isDartCore) { | |
| 1537 ClassElement classElement = library.getType(name); | |
| 1538 if (classElement != null) { | |
| 1539 return classElement.type; | |
| 1540 } | |
| 1541 return null; | |
| 1542 } | |
| 1543 } | |
| 1544 return null; | |
| 1545 } | |
| 1546 | |
| 1547 Map<ParameterElement, String> | |
| 1548 _getDefaultValueMap(List<ParameterElement> parameters) { | |
| 1549 Map<ParameterElement, String> defaultSourceMap = {}; | |
| 1550 Map<Source, String> sourceContentMap = {}; | |
| 1551 for (ParameterElement parameter in parameters) { | |
| 1552 SourceRange valueRange = parameter.defaultValueRange; | |
| 1553 if (valueRange != null) { | |
| 1554 Source source = parameter.source; | |
| 1555 String sourceContent = sourceContentMap[source]; | |
| 1556 if (sourceContent == null) { | |
| 1557 sourceContent = getSourceContent(parameter.context, source); | |
| 1558 sourceContentMap[source] = sourceContent; | |
| 1559 } | |
| 1560 String valueSource = | |
| 1561 sourceContent.substring(valueRange.offset, valueRange.end); | |
| 1562 defaultSourceMap[parameter] = valueSource; | |
| 1563 } | |
| 1564 } | |
| 1565 return defaultSourceMap; | |
| 1566 } | |
| 1567 | |
| 1568 /** | |
| 1569 * Returns an existing or just added [LinkedEditGroup] with [groupId]. | |
| 1570 */ | |
| 1571 LinkedEditGroup _getLinkedPosition(String groupId) { | |
| 1572 LinkedEditGroup group = linkedPositionGroups[groupId]; | |
| 1573 if (group == null) { | |
| 1574 group = new LinkedEditGroup(groupId); | |
| 1575 linkedPositionGroups[groupId] = group; | |
| 1576 } | |
| 1577 return group; | |
| 1578 } | |
| 1579 | |
| 1580 /** | |
| 1581 * Returns `true` if [node] is in static context. | |
| 1582 */ | |
| 1583 bool _inStaticContext() { | |
| 1584 // constructor initializer cannot reference "this" | |
| 1585 if (node.getAncestor((node) => node is ConstructorInitializer) != null) { | |
| 1586 return true; | |
| 1587 } | |
| 1588 // field initializer cannot reference "this" | |
| 1589 if (node.getAncestor((node) => node is FieldDeclaration) != null) { | |
| 1590 return true; | |
| 1591 } | |
| 1592 // static method | |
| 1593 MethodDeclaration method = node.getAncestor((node) { | |
| 1594 return node is MethodDeclaration; | |
| 1595 }); | |
| 1596 return method != null && method.isStatic; | |
| 1597 } | |
| 1598 | |
| 1599 /** | |
| 1600 * Returns a possible return [Type], may be `null` if cannot be inferred. | |
| 1601 */ | |
| 1602 DartType _inferReturnType(MethodInvocation invocation) { | |
| 1603 AstNode parent = invocation.parent; | |
| 1604 // myFunction(); | |
| 1605 if (parent is ExpressionStatement) { | |
| 1606 return VoidTypeImpl.instance; | |
| 1607 } | |
| 1608 // return myFunction(); | |
| 1609 if (parent is ReturnStatement) { | |
| 1610 ExecutableElement executable = getEnclosingExecutableElement(invocation); | |
| 1611 return executable != null ? executable.returnType : null; | |
| 1612 } | |
| 1613 // int v = myFunction(); | |
| 1614 if (parent is VariableDeclaration) { | |
| 1615 VariableDeclaration variableDeclaration = parent; | |
| 1616 if (variableDeclaration.initializer == invocation) { | |
| 1617 VariableElement variableElement = variableDeclaration.element; | |
| 1618 if (variableElement != null) { | |
| 1619 return variableElement.type; | |
| 1620 } | |
| 1621 } | |
| 1622 } | |
| 1623 // v = myFunction(); | |
| 1624 if (parent is AssignmentExpression) { | |
| 1625 AssignmentExpression assignment = parent; | |
| 1626 if (assignment.rightHandSide == invocation) { | |
| 1627 if (assignment.operator.type == TokenType.EQ) { | |
| 1628 // v = myFunction(); | |
| 1629 Expression lhs = assignment.leftHandSide; | |
| 1630 if (lhs != null) { | |
| 1631 return lhs.bestType; | |
| 1632 } | |
| 1633 } else { | |
| 1634 // v += myFunction(); | |
| 1635 MethodElement method = assignment.bestElement; | |
| 1636 if (method != null) { | |
| 1637 List<ParameterElement> parameters = method.parameters; | |
| 1638 if (parameters.length == 1) { | |
| 1639 return parameters[0].type; | |
| 1640 } | |
| 1641 } | |
| 1642 } | |
| 1643 } | |
| 1644 } | |
| 1645 // v + myFunction(); | |
| 1646 if (parent is BinaryExpression) { | |
| 1647 BinaryExpression binary = parent; | |
| 1648 MethodElement method = binary.bestElement; | |
| 1649 if (method != null) { | |
| 1650 if (binary.rightOperand == invocation) { | |
| 1651 List<ParameterElement> parameters = method.parameters; | |
| 1652 return parameters.length == 1 ? parameters[0].type : null; | |
| 1653 } | |
| 1654 } | |
| 1655 } | |
| 1656 // foo( myFunction() ); | |
| 1657 if (parent is ArgumentList) { | |
| 1658 ParameterElement parameter = invocation.bestParameterElement; | |
| 1659 return parameter != null ? parameter.type : null; | |
| 1660 } | |
| 1661 // bool | |
| 1662 { | |
| 1663 // assert( myFunction() ); | |
| 1664 if (parent is AssertStatement) { | |
| 1665 AssertStatement statement = parent; | |
| 1666 if (statement.condition == invocation) { | |
| 1667 return coreTypeBool; | |
| 1668 } | |
| 1669 } | |
| 1670 // if ( myFunction() ) {} | |
| 1671 if (parent is IfStatement) { | |
| 1672 IfStatement statement = parent; | |
| 1673 if (statement.condition == invocation) { | |
| 1674 return coreTypeBool; | |
| 1675 } | |
| 1676 } | |
| 1677 // while ( myFunction() ) {} | |
| 1678 if (parent is WhileStatement) { | |
| 1679 WhileStatement statement = parent; | |
| 1680 if (statement.condition == invocation) { | |
| 1681 return coreTypeBool; | |
| 1682 } | |
| 1683 } | |
| 1684 // do {} while ( myFunction() ); | |
| 1685 if (parent is DoStatement) { | |
| 1686 DoStatement statement = parent; | |
| 1687 if (statement.condition == invocation) { | |
| 1688 return coreTypeBool; | |
| 1689 } | |
| 1690 } | |
| 1691 // !myFunction() | |
| 1692 if (parent is PrefixExpression) { | |
| 1693 PrefixExpression prefixExpression = parent; | |
| 1694 if (prefixExpression.operator.type == TokenType.BANG) { | |
| 1695 return coreTypeBool; | |
| 1696 } | |
| 1697 } | |
| 1698 // binary expression '&&' or '||' | |
| 1699 if (parent is BinaryExpression) { | |
| 1700 BinaryExpression binaryExpression = parent; | |
| 1701 TokenType operatorType = binaryExpression.operator.type; | |
| 1702 if (operatorType == TokenType.AMPERSAND_AMPERSAND || | |
| 1703 operatorType == TokenType.BAR_BAR) { | |
| 1704 return coreTypeBool; | |
| 1705 } | |
| 1706 } | |
| 1707 } | |
| 1708 // we don't know | |
| 1709 return null; | |
| 1710 } | |
| 1711 | |
| 1712 /** | |
| 1713 * Inserts the given [SourceBuilder] at its offset. | |
| 1714 */ | |
| 1715 void _insertBuilder(SourceBuilder builder) { | |
| 1716 String text = builder.toString(); | |
| 1717 _addInsertEdit(builder.offset, text); | |
| 1718 // add linked positions | |
| 1719 builder.linkedPositionGroups.forEach((LinkedEditGroup group) { | |
| 1720 LinkedEditGroup fixGroup = _getLinkedPosition(group.id); | |
| 1721 group.positions.forEach((Position position) { | |
| 1722 fixGroup.addPosition(position, group.length); | |
| 1723 }); | |
| 1724 group.suggestions.forEach((LinkedEditSuggestion suggestion) { | |
| 1725 fixGroup.addSuggestion(suggestion); | |
| 1726 }); | |
| 1727 }); | |
| 1728 } | |
| 1729 | |
| 1730 // void _addLinkedPositionProposal(String group, | |
| 1731 // LinkedPositionProposal proposal) { | |
| 1732 // List<LinkedPositionProposal> nodeProposals = linkedPositionProposals[group
]; | |
| 1733 // if (nodeProposals == null) { | |
| 1734 // nodeProposals = <LinkedPositionProposal>[]; | |
| 1735 // linkedPositionProposals[group] = nodeProposals; | |
| 1736 // } | |
| 1737 // nodeProposals.add(proposal); | |
| 1738 // } | |
| 1739 | |
| 1740 // /** | |
| 1741 // * Returns `true` if the given [ClassMember] is a part of a static method or | |
| 1742 // * a field initializer. | |
| 1743 // */ | |
| 1744 // bool _inStaticMemberContext2(ClassMember member) { | |
| 1745 // if (member is MethodDeclaration) { | |
| 1746 // return member.isStatic; | |
| 1747 // } | |
| 1748 // // field initializer cannot reference "this" | |
| 1749 // if (member is FieldDeclaration) { | |
| 1750 // return true; | |
| 1751 // } | |
| 1752 // return false; | |
| 1753 // } | |
| 1754 | |
| 1755 _ConstructorLocation | |
| 1756 _prepareNewConstructorLocation(ClassDeclaration classDeclaration) { | |
| 1757 List<ClassMember> members = classDeclaration.members; | |
| 1758 // find the last field/constructor | |
| 1759 ClassMember lastFieldOrConstructor = null; | |
| 1760 for (ClassMember member in members) { | |
| 1761 if (member is FieldDeclaration || member is ConstructorDeclaration) { | |
| 1762 lastFieldOrConstructor = member; | |
| 1763 } else { | |
| 1764 break; | |
| 1765 } | |
| 1766 } | |
| 1767 // after the field/constructor | |
| 1768 if (lastFieldOrConstructor != null) { | |
| 1769 return new _ConstructorLocation( | |
| 1770 "${eol}${eol}", | |
| 1771 lastFieldOrConstructor.end, | |
| 1772 ""); | |
| 1773 } | |
| 1774 // at the beginning of the class | |
| 1775 String suffix = members.isEmpty ? "" : eol; | |
| 1776 return new _ConstructorLocation( | |
| 1777 eol, | |
| 1778 classDeclaration.leftBracket.end, | |
| 1779 suffix); | |
| 1780 } | |
| 1781 | |
| 1782 /** | |
| 1783 * Removes any [ParenthesizedExpression] enclosing [expr]. | |
| 1784 * | |
| 1785 * [exprPrecedence] - the effective precedence of [expr]. | |
| 1786 */ | |
| 1787 void _removeEnclosingParentheses(Expression expr, int exprPrecedence) { | |
| 1788 while (expr.parent is ParenthesizedExpression) { | |
| 1789 ParenthesizedExpression parenthesized = | |
| 1790 expr.parent as ParenthesizedExpression; | |
| 1791 if (getExpressionParentPrecedence(parenthesized) > exprPrecedence) { | |
| 1792 break; | |
| 1793 } | |
| 1794 _addRemoveEdit(rf.rangeToken(parenthesized.leftParenthesis)); | |
| 1795 _addRemoveEdit(rf.rangeToken(parenthesized.rightParenthesis)); | |
| 1796 expr = parenthesized; | |
| 1797 } | |
| 1798 } | |
| 1799 | |
| 1800 void _updateFinderWithClassMembers(_ClosestElementFinder finder, | |
| 1801 ClassElement clazz) { | |
| 1802 if (clazz != null) { | |
| 1803 List<Element> members = getMembers(clazz); | |
| 1804 finder._updateList(members); | |
| 1805 } | |
| 1806 } | |
| 1807 | |
| 1808 static void _addSuperTypeProposals(SourceBuilder sb, | |
| 1809 Set<DartType> alreadyAdded, DartType type) { | |
| 1810 if (type != null && | |
| 1811 !alreadyAdded.contains(type) && | |
| 1812 type.element is ClassElement) { | |
| 1813 alreadyAdded.add(type); | |
| 1814 ClassElement element = type.element as ClassElement; | |
| 1815 sb.addSuggestion(LinkedEditSuggestionKind.TYPE, element.name); | |
| 1816 _addSuperTypeProposals(sb, alreadyAdded, element.supertype); | |
| 1817 for (InterfaceType interfaceType in element.interfaces) { | |
| 1818 _addSuperTypeProposals(sb, alreadyAdded, interfaceType); | |
| 1819 } | |
| 1820 } | |
| 1821 } | |
| 1822 | |
| 1823 /** | |
| 1824 * Attempts to convert the given absolute path into a "package" URI. | |
| 1825 * | |
| 1826 * [context] - the [AnalysisContext] to work in. | |
| 1827 * [path] - the absolute path, not `null`. | |
| 1828 * | |
| 1829 * Returns the "package" URI, may be `null`. | |
| 1830 */ | |
| 1831 static String _findPackageUri(AnalysisContext context, String path) { | |
| 1832 // Source fileSource = new FileBasedSource.con1(path); | |
| 1833 Source fileSource = new NonExistingSource(path, UriKind.FILE_URI); | |
| 1834 Uri uri = context.sourceFactory.restoreUri(fileSource); | |
| 1835 if (uri == null) { | |
| 1836 return null; | |
| 1837 } | |
| 1838 return uri.toString(); | |
| 1839 } | |
| 1840 | |
| 1841 /** | |
| 1842 * @return the suggestions for given [Type] and [DartExpression], not empty. | |
| 1843 */ | |
| 1844 static List<String> _getArgumentNameSuggestions(Set<String> excluded, | |
| 1845 DartType type, Expression expression, int index) { | |
| 1846 List<String> suggestions = | |
| 1847 getVariableNameSuggestionsForExpression(type, expression, excluded); | |
| 1848 if (suggestions.length != 0) { | |
| 1849 return suggestions; | |
| 1850 } | |
| 1851 return <String>["arg${index}"]; | |
| 1852 } | |
| 1853 | |
| 1854 /** | |
| 1855 * Returns `true` if [node] is a type name. | |
| 1856 */ | |
| 1857 static bool _mayBeTypeIdentifier(AstNode node) { | |
| 1858 if (node is SimpleIdentifier) { | |
| 1859 AstNode parent = node.parent; | |
| 1860 if (parent is TypeName) { | |
| 1861 return true; | |
| 1862 } | |
| 1863 if (parent is MethodInvocation) { | |
| 1864 return parent.realTarget == node; | |
| 1865 } | |
| 1866 if (parent is PrefixedIdentifier) { | |
| 1867 return parent.prefix == node; | |
| 1868 } | |
| 1869 } | |
| 1870 return false; | |
| 1871 } | |
| 1872 } | |
| 1873 | |
| 1874 /** | |
| 1875 * Helper for finding [Element] with name closest to the given. | |
| 1876 */ | |
| 1877 class _ClosestElementFinder { | |
| 1878 final String _targetName; | |
| 1879 final Predicate<Element> _predicate; | |
| 1880 | |
| 1881 Element _element = null; | |
| 1882 int _distance; | |
| 1883 | |
| 1884 _ClosestElementFinder(this._targetName, this._predicate, this._distance); | |
| 1885 | |
| 1886 void _update(Element element) { | |
| 1887 if (_predicate(element)) { | |
| 1888 int memberDistance = levenshtein(element.name, _targetName, _distance); | |
| 1889 if (memberDistance < _distance) { | |
| 1890 _element = element; | |
| 1891 _distance = memberDistance; | |
| 1892 } | |
| 1893 } | |
| 1894 } | |
| 1895 | |
| 1896 void _updateList(Iterable<Element> elements) { | |
| 1897 for (Element element in elements) { | |
| 1898 _update(element); | |
| 1899 } | |
| 1900 } | |
| 1901 } | |
| 1902 | |
| 1903 /** | |
| 1904 * Describes the location for a newly created [ConstructorDeclaration]. | |
| 1905 */ | |
| 1906 class _ConstructorLocation { | |
| 1907 final String _prefix; | |
| 1908 final int _offset; | |
| 1909 final String _suffix; | |
| 1910 | |
| 1911 _ConstructorLocation(this._prefix, this._offset, this._suffix); | |
| 1912 } | |
| OLD | NEW |