| OLD | NEW |
| 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | 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 | 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. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 library services.src.refactoring.inline_method; | 5 library services.src.refactoring.inline_method; |
| 6 | 6 |
| 7 import 'dart:async'; | 7 import 'dart:async'; |
| 8 | 8 |
| 9 import 'package:analysis_server/src/protocol.dart' hide Element; | 9 import 'package:analysis_server/src/protocol_server.dart' hide Element; |
| 10 import 'package:analysis_server/src/services/correction/source_range.dart'; | 10 import 'package:analysis_server/src/services/correction/source_range.dart'; |
| 11 import 'package:analysis_server/src/services/correction/status.dart'; | 11 import 'package:analysis_server/src/services/correction/status.dart'; |
| 12 import 'package:analysis_server/src/services/correction/strings.dart'; | 12 import 'package:analysis_server/src/services/correction/strings.dart'; |
| 13 import 'package:analysis_server/src/services/correction/util.dart'; | 13 import 'package:analysis_server/src/services/correction/util.dart'; |
| 14 import 'package:analysis_server/src/services/refactoring/refactoring.dart'; | 14 import 'package:analysis_server/src/services/refactoring/refactoring.dart'; |
| 15 import 'package:analysis_server/src/services/refactoring/refactoring_internal.da
rt'; | 15 import 'package:analysis_server/src/services/refactoring/refactoring_internal.da
rt'; |
| 16 import 'package:analysis_server/src/services/search/element_visitors.dart'; | 16 import 'package:analysis_server/src/services/search/element_visitors.dart'; |
| 17 import 'package:analysis_server/src/services/search/hierarchy.dart'; | 17 import 'package:analysis_server/src/services/search/hierarchy.dart'; |
| 18 import 'package:analysis_server/src/services/search/search_engine.dart'; | 18 import 'package:analysis_server/src/services/search/search_engine.dart'; |
| 19 import 'package:analyzer/src/generated/ast.dart'; | 19 import 'package:analyzer/src/generated/ast.dart'; |
| (...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 69 for (_ParameterOccurrence occurrence in occurrences) { | 69 for (_ParameterOccurrence occurrence in occurrences) { |
| 70 SourceRange range = occurrence.range; | 70 SourceRange range = occurrence.range; |
| 71 // prepare argument source to apply at this occurrence | 71 // prepare argument source to apply at this occurrence |
| 72 String occurrenceArgumentSource; | 72 String occurrenceArgumentSource; |
| 73 if (argumentPrecedence < occurrence.parentPrecedence) { | 73 if (argumentPrecedence < occurrence.parentPrecedence) { |
| 74 occurrenceArgumentSource = "(${argumentSource})"; | 74 occurrenceArgumentSource = "(${argumentSource})"; |
| 75 } else { | 75 } else { |
| 76 occurrenceArgumentSource = argumentSource; | 76 occurrenceArgumentSource = argumentSource; |
| 77 } | 77 } |
| 78 // do replace | 78 // do replace |
| 79 edits.add(new SourceEdit.range(range, occurrenceArgumentSource)); | 79 edits.add(newSourceEdit_range(range, occurrenceArgumentSource)); |
| 80 } | 80 } |
| 81 }); | 81 }); |
| 82 // replace static field "qualifier" with invocation target | 82 // replace static field "qualifier" with invocation target |
| 83 part._staticFieldQualifiers.forEach( | 83 part._staticFieldQualifiers.forEach( |
| 84 (String className, List<SourceRange> ranges) { | 84 (String className, List<SourceRange> ranges) { |
| 85 for (SourceRange range in ranges) { | 85 for (SourceRange range in ranges) { |
| 86 edits.add(new SourceEdit.range(range, className + '.')); | 86 edits.add(newSourceEdit_range(range, className + '.')); |
| 87 } | 87 } |
| 88 }); | 88 }); |
| 89 // replace instance field "qualifier" with invocation target | 89 // replace instance field "qualifier" with invocation target |
| 90 if (targetExpression != null) { | 90 if (targetExpression != null) { |
| 91 String targetSource = utils.getNodeText(targetExpression) + '.'; | 91 String targetSource = utils.getNodeText(targetExpression) + '.'; |
| 92 for (SourceRange qualifierRange in part._instanceFieldQualifiers) { | 92 for (SourceRange qualifierRange in part._instanceFieldQualifiers) { |
| 93 edits.add(new SourceEdit.range(qualifierRange, targetSource)); | 93 edits.add(newSourceEdit_range(qualifierRange, targetSource)); |
| 94 } | 94 } |
| 95 } | 95 } |
| 96 // prepare edits to replace conflicting variables | 96 // prepare edits to replace conflicting variables |
| 97 Set<String> conflictingNames = _getNamesConflictingAt(contextNode); | 97 Set<String> conflictingNames = _getNamesConflictingAt(contextNode); |
| 98 part._variables.forEach((VariableElement variable, List<SourceRange> ranges) { | 98 part._variables.forEach((VariableElement variable, List<SourceRange> ranges) { |
| 99 String originalName = variable.displayName; | 99 String originalName = variable.displayName; |
| 100 // prepare unique name | 100 // prepare unique name |
| 101 String uniqueName; | 101 String uniqueName; |
| 102 { | 102 { |
| 103 uniqueName = originalName; | 103 uniqueName = originalName; |
| 104 int uniqueIndex = 2; | 104 int uniqueIndex = 2; |
| 105 while (conflictingNames.contains(uniqueName)) { | 105 while (conflictingNames.contains(uniqueName)) { |
| 106 uniqueName = originalName + uniqueIndex.toString(); | 106 uniqueName = originalName + uniqueIndex.toString(); |
| 107 uniqueIndex++; | 107 uniqueIndex++; |
| 108 } | 108 } |
| 109 } | 109 } |
| 110 // update references, if name was change | 110 // update references, if name was change |
| 111 if (uniqueName != originalName) { | 111 if (uniqueName != originalName) { |
| 112 for (SourceRange range in ranges) { | 112 for (SourceRange range in ranges) { |
| 113 edits.add(new SourceEdit.range(range, uniqueName)); | 113 edits.add(newSourceEdit_range(range, uniqueName)); |
| 114 } | 114 } |
| 115 } | 115 } |
| 116 }); | 116 }); |
| 117 // prepare source with applied arguments | 117 // prepare source with applied arguments |
| 118 edits.sort((SourceEdit a, SourceEdit b) => b.offset - a.offset); | 118 edits.sort((SourceEdit a, SourceEdit b) => b.offset - a.offset); |
| 119 return SourceEdit.applySequence(part._source, edits); | 119 return SourceEdit.applySequence(part._source, edits); |
| 120 } | 120 } |
| 121 | 121 |
| 122 | 122 |
| 123 /** | 123 /** |
| (...skipping 108 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 232 result.addError('All references must be inlined to remove the source.'); | 232 result.addError('All references must be inlined to remove the source.'); |
| 233 } | 233 } |
| 234 // prepare changes | 234 // prepare changes |
| 235 for (_ReferenceProcessor processor in _referenceProcessors) { | 235 for (_ReferenceProcessor processor in _referenceProcessors) { |
| 236 processor._process(result); | 236 processor._process(result); |
| 237 } | 237 } |
| 238 // delete method | 238 // delete method |
| 239 if (deleteSource && inlineAll) { | 239 if (deleteSource && inlineAll) { |
| 240 SourceRange methodRange = rangeNode(_methodNode); | 240 SourceRange methodRange = rangeNode(_methodNode); |
| 241 SourceRange linesRange = _methodUtils.getLinesRange(methodRange); | 241 SourceRange linesRange = _methodUtils.getLinesRange(methodRange); |
| 242 change.addElementEdit( | 242 doSourceChange_addElementEdit( |
| 243 change, |
| 243 _methodElement, | 244 _methodElement, |
| 244 new SourceEdit.range(linesRange, '')); | 245 newSourceEdit_range(linesRange, '')); |
| 245 } | 246 } |
| 246 // done | 247 // done |
| 247 return new Future.value(result); | 248 return new Future.value(result); |
| 248 } | 249 } |
| 249 | 250 |
| 250 @override | 251 @override |
| 251 Future<RefactoringStatus> checkInitialConditions() { | 252 Future<RefactoringStatus> checkInitialConditions() { |
| 252 RefactoringStatus result = new RefactoringStatus(); | 253 RefactoringStatus result = new RefactoringStatus(); |
| 253 // prepare method information | 254 // prepare method information |
| 254 result.addStatus(_prepareMethod()); | 255 result.addStatus(_prepareMethod()); |
| (...skipping 166 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 421 Statement refStatement = _node.getAncestor((node) => node is Statement); | 422 Statement refStatement = _node.getAncestor((node) => node is Statement); |
| 422 if (refStatement != null) { | 423 if (refStatement != null) { |
| 423 _refLineRange = _refUtils.getLinesRangeStatements([refStatement]); | 424 _refLineRange = _refUtils.getLinesRangeStatements([refStatement]); |
| 424 _refPrefix = _refUtils.getNodePrefix(refStatement); | 425 _refPrefix = _refUtils.getNodePrefix(refStatement); |
| 425 } else { | 426 } else { |
| 426 _refLineRange = null; | 427 _refLineRange = null; |
| 427 _refPrefix = _refUtils.getLinePrefix(_node.offset); | 428 _refPrefix = _refUtils.getLinePrefix(_node.offset); |
| 428 } | 429 } |
| 429 } | 430 } |
| 430 | 431 |
| 432 void _addRefEdit(SourceEdit edit) { |
| 433 doSourceChange_addElementEdit(ref.change, refElement, edit); |
| 434 } |
| 435 |
| 431 bool _canInlineBody(AstNode usage) { | 436 bool _canInlineBody(AstNode usage) { |
| 432 // no statements, usually just expression | 437 // no statements, usually just expression |
| 433 if (ref._methodStatementsPart == null) { | 438 if (ref._methodStatementsPart == null) { |
| 434 // empty method, inline as closure | 439 // empty method, inline as closure |
| 435 if (ref._methodExpressionPart == null) { | 440 if (ref._methodExpressionPart == null) { |
| 436 return false; | 441 return false; |
| 437 } | 442 } |
| 438 // OK, just expression | 443 // OK, just expression |
| 439 return true; | 444 return true; |
| 440 } | 445 } |
| (...skipping 27 matching lines...) Expand all Loading... |
| 468 // not in block, cannot inline body | 473 // not in block, cannot inline body |
| 469 return false; | 474 return false; |
| 470 } | 475 } |
| 471 | 476 |
| 472 void _inlineMethodInvocation(RefactoringStatus status, Expression usage, | 477 void _inlineMethodInvocation(RefactoringStatus status, Expression usage, |
| 473 bool cascaded, Expression target, List<Expression> arguments) { | 478 bool cascaded, Expression target, List<Expression> arguments) { |
| 474 // we don't support cascade | 479 // we don't support cascade |
| 475 if (cascaded) { | 480 if (cascaded) { |
| 476 status.addError( | 481 status.addError( |
| 477 'Cannot inline cascade invocation.', | 482 'Cannot inline cascade invocation.', |
| 478 new Location.fromNode(usage)); | 483 newLocation_fromNode(usage)); |
| 479 } | 484 } |
| 480 // can we inline method body into "methodUsage" block? | 485 // can we inline method body into "methodUsage" block? |
| 481 if (_canInlineBody(usage)) { | 486 if (_canInlineBody(usage)) { |
| 482 // insert non-return statements | 487 // insert non-return statements |
| 483 if (ref._methodStatementsPart != null) { | 488 if (ref._methodStatementsPart != null) { |
| 484 // prepare statements source for invocation | 489 // prepare statements source for invocation |
| 485 String source = _getMethodSourceForInvocation( | 490 String source = _getMethodSourceForInvocation( |
| 486 ref._methodStatementsPart, | 491 ref._methodStatementsPart, |
| 487 _refUtils, | 492 _refUtils, |
| 488 usage, | 493 usage, |
| 489 target, | 494 target, |
| 490 arguments); | 495 arguments); |
| 491 source = _refUtils.replaceSourceIndent( | 496 source = _refUtils.replaceSourceIndent( |
| 492 source, | 497 source, |
| 493 ref._methodStatementsPart._prefix, | 498 ref._methodStatementsPart._prefix, |
| 494 _refPrefix); | 499 _refPrefix); |
| 495 // do insert | 500 // do insert |
| 496 SourceRange range = rangeStartLength(_refLineRange, 0); | 501 SourceRange range = rangeStartLength(_refLineRange, 0); |
| 497 SourceEdit edit = new SourceEdit.range(range, source); | 502 SourceEdit edit = newSourceEdit_range(range, source); |
| 498 _addRefEdit(edit); | 503 _addRefEdit(edit); |
| 499 } | 504 } |
| 500 // replace invocation with return expression | 505 // replace invocation with return expression |
| 501 if (ref._methodExpressionPart != null) { | 506 if (ref._methodExpressionPart != null) { |
| 502 // prepare expression source for invocation | 507 // prepare expression source for invocation |
| 503 String source = _getMethodSourceForInvocation( | 508 String source = _getMethodSourceForInvocation( |
| 504 ref._methodExpressionPart, | 509 ref._methodExpressionPart, |
| 505 _refUtils, | 510 _refUtils, |
| 506 usage, | 511 usage, |
| 507 target, | 512 target, |
| 508 arguments); | 513 arguments); |
| 509 if (getExpressionPrecedence(ref._methodExpression) < | 514 if (getExpressionPrecedence(ref._methodExpression) < |
| 510 getExpressionParentPrecedence(usage)) { | 515 getExpressionParentPrecedence(usage)) { |
| 511 source = "(${source})"; | 516 source = "(${source})"; |
| 512 } | 517 } |
| 513 // do replace | 518 // do replace |
| 514 SourceRange methodUsageRange = rangeNode(usage); | 519 SourceRange methodUsageRange = rangeNode(usage); |
| 515 SourceEdit edit = new SourceEdit.range(methodUsageRange, source); | 520 SourceEdit edit = newSourceEdit_range(methodUsageRange, source); |
| 516 _addRefEdit(edit); | 521 _addRefEdit(edit); |
| 517 } else { | 522 } else { |
| 518 SourceEdit edit = new SourceEdit.range(_refLineRange, ""); | 523 SourceEdit edit = newSourceEdit_range(_refLineRange, ""); |
| 519 _addRefEdit(edit); | 524 _addRefEdit(edit); |
| 520 } | 525 } |
| 521 return; | 526 return; |
| 522 } | 527 } |
| 523 // inline as closure invocation | 528 // inline as closure invocation |
| 524 String source; | 529 String source; |
| 525 { | 530 { |
| 526 source = ref._methodUtils.getRangeText( | 531 source = ref._methodUtils.getRangeText( |
| 527 rangeStartEnd(ref._methodParameters.leftParenthesis, ref._methodNode))
; | 532 rangeStartEnd(ref._methodParameters.leftParenthesis, ref._methodNode))
; |
| 528 String methodPrefix = | 533 String methodPrefix = |
| 529 ref._methodUtils.getLinePrefix(ref._methodNode.offset); | 534 ref._methodUtils.getLinePrefix(ref._methodNode.offset); |
| 530 source = _refUtils.replaceSourceIndent(source, methodPrefix, _refPrefix); | 535 source = _refUtils.replaceSourceIndent(source, methodPrefix, _refPrefix); |
| 531 source = source.trim(); | 536 source = source.trim(); |
| 532 } | 537 } |
| 533 // do insert | 538 // do insert |
| 534 SourceRange range = rangeNode(_node); | 539 SourceRange range = rangeNode(_node); |
| 535 SourceEdit edit = new SourceEdit.range(range, source); | 540 SourceEdit edit = newSourceEdit_range(range, source); |
| 536 _addRefEdit(edit); | 541 _addRefEdit(edit); |
| 537 } | 542 } |
| 538 | 543 |
| 539 void _process(RefactoringStatus status) { | 544 void _process(RefactoringStatus status) { |
| 540 AstNode nodeParent = _node.parent; | 545 AstNode nodeParent = _node.parent; |
| 541 // may be only single place should be inlined | 546 // may be only single place should be inlined |
| 542 if (!_shouldProcess()) { | 547 if (!_shouldProcess()) { |
| 543 return; | 548 return; |
| 544 } | 549 } |
| 545 // may be invocation of inline method | 550 // may be invocation of inline method |
| 546 if (nodeParent is MethodInvocation) { | 551 if (nodeParent is MethodInvocation) { |
| 547 MethodInvocation invocation = nodeParent; | 552 MethodInvocation invocation = nodeParent; |
| 548 Expression target = invocation.target; | 553 Expression target = invocation.target; |
| 549 List<Expression> arguments = invocation.argumentList.arguments; | 554 List<Expression> arguments = invocation.argumentList.arguments; |
| 550 _inlineMethodInvocation( | 555 _inlineMethodInvocation( |
| 551 status, | 556 status, |
| 552 invocation, | 557 invocation, |
| 553 invocation.isCascaded, | 558 invocation.isCascaded, |
| 554 target, | 559 target, |
| 555 arguments); | 560 arguments); |
| 556 } else { | 561 } else { |
| 557 // cannot inline reference to method: var v = new A().method; | 562 // cannot inline reference to method: var v = new A().method; |
| 558 if (ref._methodElement is MethodElement) { | 563 if (ref._methodElement is MethodElement) { |
| 559 status.addFatalError( | 564 status.addFatalError( |
| 560 'Cannot inline class method reference.', | 565 'Cannot inline class method reference.', |
| 561 new Location.fromNode(_node)); | 566 newLocation_fromNode(_node)); |
| 562 return; | 567 return; |
| 563 } | 568 } |
| 564 // PropertyAccessorElement | 569 // PropertyAccessorElement |
| 565 if (ref._methodElement is PropertyAccessorElement) { | 570 if (ref._methodElement is PropertyAccessorElement) { |
| 566 Expression usage = _node; | 571 Expression usage = _node; |
| 567 Expression target = null; | 572 Expression target = null; |
| 568 bool cascade = false; | 573 bool cascade = false; |
| 569 if (nodeParent is PrefixedIdentifier) { | 574 if (nodeParent is PrefixedIdentifier) { |
| 570 PrefixedIdentifier propertyAccess = nodeParent; | 575 PrefixedIdentifier propertyAccess = nodeParent; |
| 571 usage = propertyAccess; | 576 usage = propertyAccess; |
| (...skipping 24 matching lines...) Expand all Loading... |
| 596 rangeStartEnd(ref._methodParameters.leftParenthesis, ref._methodNode
)); | 601 rangeStartEnd(ref._methodParameters.leftParenthesis, ref._methodNode
)); |
| 597 String methodPrefix = | 602 String methodPrefix = |
| 598 ref._methodUtils.getLinePrefix(ref._methodNode.offset); | 603 ref._methodUtils.getLinePrefix(ref._methodNode.offset); |
| 599 source = | 604 source = |
| 600 _refUtils.replaceSourceIndent(source, methodPrefix, _refPrefix); | 605 _refUtils.replaceSourceIndent(source, methodPrefix, _refPrefix); |
| 601 source = source.trim(); | 606 source = source.trim(); |
| 602 source = removeEnd(source, ';'); | 607 source = removeEnd(source, ';'); |
| 603 } | 608 } |
| 604 // do insert | 609 // do insert |
| 605 SourceRange range = rangeNode(_node); | 610 SourceRange range = rangeNode(_node); |
| 606 SourceEdit edit = new SourceEdit.range(range, source); | 611 SourceEdit edit = newSourceEdit_range(range, source); |
| 607 _addRefEdit(edit); | 612 _addRefEdit(edit); |
| 608 } | 613 } |
| 609 } | 614 } |
| 610 | 615 |
| 611 void _addRefEdit(SourceEdit edit) { | |
| 612 ref.change.addElementEdit(refElement, edit); | |
| 613 } | |
| 614 | |
| 615 bool _shouldProcess() { | 616 bool _shouldProcess() { |
| 616 if (!ref.inlineAll) { | 617 if (!ref.inlineAll) { |
| 617 SourceRange parentRange = rangeNode(_node); | 618 SourceRange parentRange = rangeNode(_node); |
| 618 return parentRange.contains(ref.offset); | 619 return parentRange.contains(ref.offset); |
| 619 } | 620 } |
| 620 return true; | 621 return true; |
| 621 } | 622 } |
| 622 } | 623 } |
| 623 | 624 |
| 624 class _ReturnsValidatorVisitor extends RecursiveAstVisitor { | 625 class _ReturnsValidatorVisitor extends RecursiveAstVisitor { |
| 625 final RefactoringStatus result; | 626 final RefactoringStatus result; |
| 626 int _numReturns = 0; | 627 int _numReturns = 0; |
| 627 | 628 |
| 628 _ReturnsValidatorVisitor(this.result); | 629 _ReturnsValidatorVisitor(this.result); |
| 629 | 630 |
| 630 @override | 631 @override |
| 631 visitReturnStatement(ReturnStatement node) { | 632 visitReturnStatement(ReturnStatement node) { |
| 632 _numReturns++; | 633 _numReturns++; |
| 633 if (_numReturns == 2) { | 634 if (_numReturns == 2) { |
| 634 result.addError('Ambiguous return value.', new Location.fromNode(node)); | 635 result.addError('Ambiguous return value.', newLocation_fromNode(node)); |
| 635 } | 636 } |
| 636 } | 637 } |
| 637 } | 638 } |
| 638 | 639 |
| 639 /** | 640 /** |
| 640 * Information about the source of a method being inlined. | 641 * Information about the source of a method being inlined. |
| 641 */ | 642 */ |
| 642 class _SourcePart { | 643 class _SourcePart { |
| 643 /** | 644 /** |
| 644 * The base for all [SourceRange]s. | 645 * The base for all [SourceRange]s. |
| (...skipping 157 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 802 } | 803 } |
| 803 | 804 |
| 804 void _addVariable(SimpleIdentifier node) { | 805 void _addVariable(SimpleIdentifier node) { |
| 805 VariableElement variableElement = getLocalVariableElement(node); | 806 VariableElement variableElement = getLocalVariableElement(node); |
| 806 if (variableElement != null) { | 807 if (variableElement != null) { |
| 807 SourceRange nodeRange = rangeNode(node); | 808 SourceRange nodeRange = rangeNode(node); |
| 808 result.addVariable(variableElement, nodeRange); | 809 result.addVariable(variableElement, nodeRange); |
| 809 } | 810 } |
| 810 } | 811 } |
| 811 } | 812 } |
| OLD | NEW |