| 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.extract_method; | 5 library services.src.refactoring.extract_method; |
| 6 | 6 |
| 7 import 'dart:async'; | 7 import 'dart:async'; |
| 8 | 8 |
| 9 import 'package:analysis_server/src/protocol_server.dart' hide Element; | 9 import 'package:analysis_server/src/protocol_server.dart' hide Element; |
| 10 import 'package:analysis_server/src/services/correction/name_suggestion.dart'; | 10 import 'package:analysis_server/src/services/correction/name_suggestion.dart'; |
| 11 import 'package:analysis_server/src/services/correction/selection_analyzer.dart'
; | 11 import 'package:analysis_server/src/services/correction/selection_analyzer.dart'
; |
| 12 import 'package:analysis_server/src/services/correction/source_range.dart'; | 12 import 'package:analysis_server/src/services/correction/source_range.dart'; |
| 13 import 'package:analysis_server/src/services/correction/statement_analyzer.dart'
; | 13 import 'package:analysis_server/src/services/correction/statement_analyzer.dart'
; |
| 14 import 'package:analysis_server/src/services/correction/status.dart'; | 14 import 'package:analysis_server/src/services/correction/status.dart'; |
| 15 import 'package:analysis_server/src/services/correction/util.dart'; | 15 import 'package:analysis_server/src/services/correction/util.dart'; |
| 16 import 'package:analysis_server/src/services/refactoring/naming_conventions.dart
'; | 16 import 'package:analysis_server/src/services/refactoring/naming_conventions.dart
'; |
| 17 import 'package:analysis_server/src/services/refactoring/refactoring.dart'; | 17 import 'package:analysis_server/src/services/refactoring/refactoring.dart'; |
| 18 import 'package:analysis_server/src/services/refactoring/refactoring_internal.da
rt'; | 18 import 'package:analysis_server/src/services/refactoring/refactoring_internal.da
rt'; |
| 19 import 'package:analysis_server/src/services/refactoring/rename_class_member.dar
t'; | 19 import 'package:analysis_server/src/services/refactoring/rename_class_member.dar
t'; |
| 20 import 'package:analysis_server/src/services/refactoring/rename_unit_member.dart
'; | 20 import 'package:analysis_server/src/services/refactoring/rename_unit_member.dart
'; |
| 21 import 'package:analysis_server/src/services/search/element_visitors.dart'; | 21 import 'package:analysis_server/src/services/search/element_visitors.dart'; |
| 22 import 'package:analysis_server/src/services/search/search_engine.dart'; | 22 import 'package:analysis_server/src/services/search/search_engine.dart'; |
| 23 import 'package:analyzer/src/generated/ast.dart'; | 23 import 'package:analyzer/src/generated/ast.dart'; |
| 24 import 'package:analyzer/src/generated/element.dart'; | 24 import 'package:analyzer/src/generated/element.dart'; |
| 25 import 'package:analyzer/src/generated/java_core.dart'; | 25 import 'package:analyzer/src/generated/java_core.dart'; |
| 26 import 'package:analyzer/src/generated/scanner.dart'; | 26 import 'package:analyzer/src/generated/scanner.dart'; |
| 27 import 'package:analyzer/src/generated/source.dart'; | 27 import 'package:analyzer/src/generated/source.dart'; |
| 28 import 'package:analyzer/src/generated/resolver.dart' show ExitDetector; |
| 28 | 29 |
| 29 | 30 |
| 30 const String _TOKEN_SEPARATOR = '\uFFFF'; | 31 const String _TOKEN_SEPARATOR = '\uFFFF'; |
| 31 | 32 |
| 32 | 33 |
| 33 /** | 34 /** |
| 34 * Returns the "normalized" version of the given source, which is reconstructed | 35 * Returns the "normalized" version of the given source, which is reconstructed |
| 35 * from tokens, so ignores all the comments and spaces. | 36 * from tokens, so ignores all the comments and spaces. |
| 36 */ | 37 */ |
| 37 String _getNormalizedSource(String src) { | 38 String _getNormalizedSource(String src) { |
| (...skipping 12 matching lines...) Expand all Loading... |
| 50 }); | 51 }); |
| 51 return result; | 52 return result; |
| 52 } | 53 } |
| 53 | 54 |
| 54 | 55 |
| 55 /** | 56 /** |
| 56 * [ExtractMethodRefactoring] implementation. | 57 * [ExtractMethodRefactoring] implementation. |
| 57 */ | 58 */ |
| 58 class ExtractMethodRefactoringImpl extends RefactoringImpl implements | 59 class ExtractMethodRefactoringImpl extends RefactoringImpl implements |
| 59 ExtractMethodRefactoring { | 60 ExtractMethodRefactoring { |
| 61 static const ERROR_EXITS = |
| 62 'Selected statements contain a return statement, but not all possible ' |
| 63 'execuion flows exit. Semantics may not be preserved.'; |
| 64 |
| 60 final SearchEngine searchEngine; | 65 final SearchEngine searchEngine; |
| 61 final CompilationUnit unit; | 66 final CompilationUnit unit; |
| 62 final int selectionOffset; | 67 final int selectionOffset; |
| 63 final int selectionLength; | 68 final int selectionLength; |
| 64 CompilationUnitElement unitElement; | 69 CompilationUnitElement unitElement; |
| 65 SourceRange selectionRange; | 70 SourceRange selectionRange; |
| 66 CorrectionUtils utils; | 71 CorrectionUtils utils; |
| 67 | 72 |
| 68 String returnType; | 73 String returnType; |
| 69 String name; | 74 String name; |
| (...skipping 495 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 565 _parameters.clear(); | 570 _parameters.clear(); |
| 566 _parametersMap.clear(); | 571 _parametersMap.clear(); |
| 567 _parameterReferencesMap.clear(); | 572 _parameterReferencesMap.clear(); |
| 568 RefactoringStatus result = new RefactoringStatus(); | 573 RefactoringStatus result = new RefactoringStatus(); |
| 569 List<VariableElement> assignedUsedVariables = []; | 574 List<VariableElement> assignedUsedVariables = []; |
| 570 unit.accept(new _InitializeParametersVisitor(this, assignedUsedVariables)); | 575 unit.accept(new _InitializeParametersVisitor(this, assignedUsedVariables)); |
| 571 // single expression | 576 // single expression |
| 572 if (_selectionExpression != null) { | 577 if (_selectionExpression != null) { |
| 573 _returnType = _selectionExpression.bestType; | 578 _returnType = _selectionExpression.bestType; |
| 574 } | 579 } |
| 575 // may be ends with "return" statement | 580 // verify that none or all execution flows end with a "return" |
| 581 if (_selectionStatements != null) { |
| 582 bool hasReturn = _selectionStatements.any(_mayEndWithReturnStatement); |
| 583 if (hasReturn && !ExitDetector.exits(_selectionStatements.last)) { |
| 584 result.addError(ERROR_EXITS); |
| 585 } |
| 586 } |
| 587 // maybe ends with "return" statement |
| 576 if (_selectionStatements != null) { | 588 if (_selectionStatements != null) { |
| 577 _ReturnTypeComputer returnTypeComputer = new _ReturnTypeComputer(); | 589 _ReturnTypeComputer returnTypeComputer = new _ReturnTypeComputer(); |
| 578 _selectionStatements.forEach((statement) { | 590 _selectionStatements.forEach((statement) { |
| 579 statement.accept(returnTypeComputer); | 591 statement.accept(returnTypeComputer); |
| 580 }); | 592 }); |
| 581 _returnType = returnTypeComputer.returnType; | 593 _returnType = returnTypeComputer.returnType; |
| 582 } | 594 } |
| 583 // may be single variable to return | 595 // maybe single variable to return |
| 584 if (assignedUsedVariables.length == 1) { | 596 if (assignedUsedVariables.length == 1) { |
| 585 // we cannot both return variable and have explicit return statement | 597 // we cannot both return variable and have explicit return statement |
| 586 if (_returnType != null) { | 598 if (_returnType != null) { |
| 587 result.addFatalError( | 599 result.addFatalError( |
| 588 'Ambiguous return value: Selected block contains assignment(s) to ' | 600 'Ambiguous return value: Selected block contains assignment(s) to ' |
| 589 'local variables and return statement.'); | 601 'local variables and return statement.'); |
| 590 return result; | 602 return result; |
| 591 } | 603 } |
| 592 // prepare to return an assigned variable | 604 // prepare to return an assigned variable |
| 593 VariableElement returnVariable = assignedUsedVariables[0]; | 605 VariableElement returnVariable = assignedUsedVariables[0]; |
| (...skipping 97 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 691 } | 703 } |
| 692 | 704 |
| 693 /** | 705 /** |
| 694 * Checks if [node] has a [MethodInvocation]. | 706 * Checks if [node] has a [MethodInvocation]. |
| 695 */ | 707 */ |
| 696 static bool _hasMethodInvocation(AstNode node) { | 708 static bool _hasMethodInvocation(AstNode node) { |
| 697 var visitor = new _HasMethodInvocationVisitor(); | 709 var visitor = new _HasMethodInvocationVisitor(); |
| 698 node.accept(visitor); | 710 node.accept(visitor); |
| 699 return visitor.result; | 711 return visitor.result; |
| 700 } | 712 } |
| 713 |
| 714 /** |
| 715 * Returns `true` if the given [statement] may end with a [ReturnStatement]. |
| 716 */ |
| 717 static bool _mayEndWithReturnStatement(Statement statement) { |
| 718 _HasReturnStatementVisitor visitor = new _HasReturnStatementVisitor(); |
| 719 statement.accept(visitor); |
| 720 return visitor.hasReturn; |
| 721 } |
| 701 } | 722 } |
| 702 | 723 |
| 703 | 724 |
| 704 /** | 725 /** |
| 705 * [SelectionAnalyzer] for [ExtractMethodRefactoringImpl]. | 726 * [SelectionAnalyzer] for [ExtractMethodRefactoringImpl]. |
| 706 */ | 727 */ |
| 707 class _ExtractMethodAnalyzer extends StatementAnalyzer { | 728 class _ExtractMethodAnalyzer extends StatementAnalyzer { |
| 708 _ExtractMethodAnalyzer(CompilationUnit unit, SourceRange selection) | 729 _ExtractMethodAnalyzer(CompilationUnit unit, SourceRange selection) |
| 709 : super(unit, selection); | 730 : super(unit, selection); |
| 710 | 731 |
| (...skipping 147 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 858 class _HasMethodInvocationVisitor extends RecursiveAstVisitor { | 879 class _HasMethodInvocationVisitor extends RecursiveAstVisitor { |
| 859 bool result = false; | 880 bool result = false; |
| 860 | 881 |
| 861 @override | 882 @override |
| 862 visitMethodInvocation(MethodInvocation node) { | 883 visitMethodInvocation(MethodInvocation node) { |
| 863 result = true; | 884 result = true; |
| 864 } | 885 } |
| 865 } | 886 } |
| 866 | 887 |
| 867 | 888 |
| 889 class _HasReturnStatementVisitor extends RecursiveAstVisitor { |
| 890 bool hasReturn = false; |
| 891 |
| 892 @override |
| 893 visitBlockFunctionBody(BlockFunctionBody node) { |
| 894 } |
| 895 |
| 896 @override |
| 897 visitReturnStatement(ReturnStatement node) { |
| 898 hasReturn = true; |
| 899 } |
| 900 } |
| 901 |
| 902 |
| 868 class _InitializeOccurrencesVisitor extends GeneralizingAstVisitor<Object> { | 903 class _InitializeOccurrencesVisitor extends GeneralizingAstVisitor<Object> { |
| 869 final ExtractMethodRefactoringImpl ref; | 904 final ExtractMethodRefactoringImpl ref; |
| 870 final _SourcePattern selectionPattern; | 905 final _SourcePattern selectionPattern; |
| 871 final Map<String, String> patternToSelectionName; | 906 final Map<String, String> patternToSelectionName; |
| 872 | 907 |
| 873 bool forceStatic = false; | 908 bool forceStatic = false; |
| 874 | 909 |
| 875 _InitializeOccurrencesVisitor(this.ref, this.selectionPattern, | 910 _InitializeOccurrencesVisitor(this.ref, this.selectionPattern, |
| 876 this.patternToSelectionName); | 911 this.patternToSelectionName); |
| 877 | 912 |
| (...skipping 87 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 965 // next statement | 1000 // next statement |
| 966 if (found) { | 1001 if (found) { |
| 967 beginStatementIndex += selectionCount; | 1002 beginStatementIndex += selectionCount; |
| 968 } else { | 1003 } else { |
| 969 beginStatementIndex++; | 1004 beginStatementIndex++; |
| 970 } | 1005 } |
| 971 } | 1006 } |
| 972 } | 1007 } |
| 973 } | 1008 } |
| 974 | 1009 |
| 975 | |
| 976 class _InitializeParametersVisitor extends GeneralizingAstVisitor<Object> { | 1010 class _InitializeParametersVisitor extends GeneralizingAstVisitor<Object> { |
| 977 final ExtractMethodRefactoringImpl ref; | 1011 final ExtractMethodRefactoringImpl ref; |
| 978 final List<VariableElement> assignedUsedVariables; | 1012 final List<VariableElement> assignedUsedVariables; |
| 979 | 1013 |
| 980 _InitializeParametersVisitor(this.ref, this.assignedUsedVariables); | 1014 _InitializeParametersVisitor(this.ref, this.assignedUsedVariables); |
| 981 | 1015 |
| 982 @override | 1016 @override |
| 983 Object visitSimpleIdentifier(SimpleIdentifier node) { | 1017 Object visitSimpleIdentifier(SimpleIdentifier node) { |
| 984 SourceRange nodeRange = rangeNode(node); | 1018 SourceRange nodeRange = rangeNode(node); |
| 985 if (ref.selectionRange.covers(nodeRange)) { | 1019 if (ref.selectionRange.covers(nodeRange)) { |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1024 } | 1058 } |
| 1025 // remember declaration names | 1059 // remember declaration names |
| 1026 if (node.inDeclarationContext()) { | 1060 if (node.inDeclarationContext()) { |
| 1027 ref._usedNames.add(node.name); | 1061 ref._usedNames.add(node.name); |
| 1028 } | 1062 } |
| 1029 } | 1063 } |
| 1030 return null; | 1064 return null; |
| 1031 } | 1065 } |
| 1032 } | 1066 } |
| 1033 | 1067 |
| 1068 |
| 1034 class _IsUsedAfterSelectionVisitor extends GeneralizingAstVisitor { | 1069 class _IsUsedAfterSelectionVisitor extends GeneralizingAstVisitor { |
| 1035 final ExtractMethodRefactoringImpl ref; | 1070 final ExtractMethodRefactoringImpl ref; |
| 1036 final VariableElement element; | 1071 final VariableElement element; |
| 1037 bool result = false; | 1072 bool result = false; |
| 1038 | 1073 |
| 1039 _IsUsedAfterSelectionVisitor(this.ref, this.element); | 1074 _IsUsedAfterSelectionVisitor(this.ref, this.element); |
| 1040 | 1075 |
| 1041 @override | 1076 @override |
| 1042 visitSimpleIdentifier(SimpleIdentifier node) { | 1077 visitSimpleIdentifier(SimpleIdentifier node) { |
| 1043 VariableElement nodeElement = getLocalVariableElement(node); | 1078 VariableElement nodeElement = getLocalVariableElement(node); |
| (...skipping 103 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1147 return false; | 1182 return false; |
| 1148 } | 1183 } |
| 1149 for (int i = 0; i < parameterTypes.length; i++) { | 1184 for (int i = 0; i < parameterTypes.length; i++) { |
| 1150 if (other.parameterTypes[i] != parameterTypes[i]) { | 1185 if (other.parameterTypes[i] != parameterTypes[i]) { |
| 1151 return false; | 1186 return false; |
| 1152 } | 1187 } |
| 1153 } | 1188 } |
| 1154 return true; | 1189 return true; |
| 1155 } | 1190 } |
| 1156 } | 1191 } |
| OLD | NEW |