| 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.rename_class_member; | 5 library services.src.refactoring.rename_class_member; |
| 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.dart' hide Element, ElementKind; |
| 10 import 'package:analysis_server/src/services/correction/status.dart'; | 10 import 'package:analysis_server/src/services/correction/status.dart'; |
| 11 import 'package:analysis_server/src/services/correction/util.dart'; |
| 12 import 'package:analysis_server/src/services/refactoring/naming_conventions.dart
'; |
| 11 import 'package:analysis_server/src/services/refactoring/refactoring.dart'; | 13 import 'package:analysis_server/src/services/refactoring/refactoring.dart'; |
| 14 import 'package:analysis_server/src/services/refactoring/rename.dart'; |
| 12 import 'package:analysis_server/src/services/search/hierarchy.dart'; | 15 import 'package:analysis_server/src/services/search/hierarchy.dart'; |
| 13 import 'package:analysis_server/src/services/search/search_engine.dart'; | 16 import 'package:analysis_server/src/services/search/search_engine.dart'; |
| 14 import 'package:analysis_server/src/services/correction/util.dart'; | |
| 15 import 'package:analysis_server/src/services/refactoring/naming_conventions.dart
'; | |
| 16 import 'package:analysis_server/src/services/refactoring/rename.dart'; | |
| 17 import 'package:analyzer/src/generated/element.dart'; | 17 import 'package:analyzer/src/generated/element.dart'; |
| 18 import 'package:analyzer/src/generated/java_core.dart'; | 18 import 'package:analyzer/src/generated/java_core.dart'; |
| 19 | 19 |
| 20 | 20 |
| 21 /** | 21 /** |
| 22 * Checks if creating a method with the given [name] in [classElement] will |
| 23 * cause any conflicts. |
| 24 */ |
| 25 Future<RefactoringStatus> validateCreateMethod(SearchEngine searchEngine, |
| 26 ClassElement classElement, String name) { |
| 27 return new _ClassMemberValidator.forCreate( |
| 28 searchEngine, |
| 29 classElement, |
| 30 name).validate(); |
| 31 } |
| 32 |
| 33 |
| 34 /** |
| 22 * A [Refactoring] for renaming class member [Element]s. | 35 * A [Refactoring] for renaming class member [Element]s. |
| 23 */ | 36 */ |
| 24 class RenameClassMemberRefactoringImpl extends RenameRefactoringImpl { | 37 class RenameClassMemberRefactoringImpl extends RenameRefactoringImpl { |
| 25 _RenameClassMemberValidator _validator; | 38 _ClassMemberValidator _validator; |
| 26 | 39 |
| 27 RenameClassMemberRefactoringImpl(SearchEngine searchEngine, Element element) | 40 RenameClassMemberRefactoringImpl(SearchEngine searchEngine, Element element) |
| 28 : super(searchEngine, element); | 41 : super(searchEngine, element); |
| 29 | 42 |
| 30 @override | 43 @override |
| 31 String get refactoringName { | 44 String get refactoringName { |
| 32 if (element is TypeParameterElement) { | 45 if (element is TypeParameterElement) { |
| 33 return "Rename Type Parameter"; | 46 return "Rename Type Parameter"; |
| 34 } | 47 } |
| 35 if (element is FieldElement) { | 48 if (element is FieldElement) { |
| 36 return "Rename Field"; | 49 return "Rename Field"; |
| 37 } | 50 } |
| 38 return "Rename Method"; | 51 return "Rename Method"; |
| 39 } | 52 } |
| 40 | 53 |
| 41 @override | 54 @override |
| 42 Future<RefactoringStatus> checkFinalConditions() { | 55 Future<RefactoringStatus> checkFinalConditions() { |
| 43 _validator = | 56 _validator = |
| 44 new _RenameClassMemberValidator(searchEngine, element, newName); | 57 new _ClassMemberValidator.forRename(searchEngine, element, newName); |
| 45 return _validator.validate(); | 58 return _validator.validate(); |
| 46 } | 59 } |
| 47 | 60 |
| 48 @override | 61 @override |
| 49 Future<RefactoringStatus> checkInitialConditions() { | 62 Future<RefactoringStatus> checkInitialConditions() { |
| 50 RefactoringStatus result = new RefactoringStatus(); | 63 RefactoringStatus result = new RefactoringStatus(); |
| 51 if (element is MethodElement && (element as MethodElement).isOperator) { | 64 if (element is MethodElement && (element as MethodElement).isOperator) { |
| 52 result.addFatalError('Cannot rename operator.'); | 65 result.addFatalError('Cannot rename operator.'); |
| 53 } | 66 } |
| 54 return new Future.value(result); | 67 return new Future.value(result); |
| (...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 114 | 127 |
| 115 String _newPotentialId() { | 128 String _newPotentialId() { |
| 116 String id = potentialEditIds.length.toString(); | 129 String id = potentialEditIds.length.toString(); |
| 117 potentialEditIds.add(id); | 130 potentialEditIds.add(id); |
| 118 return id; | 131 return id; |
| 119 } | 132 } |
| 120 } | 133 } |
| 121 | 134 |
| 122 | 135 |
| 123 /** | 136 /** |
| 124 * Helper to check if renaming of an [Element] to the given name will cause any | 137 * Helper to check if the created or renamed [Element] will cause any conflicts. |
| 125 * problems. | |
| 126 */ | 138 */ |
| 127 class _RenameClassMemberValidator { | 139 class _ClassMemberValidator { |
| 128 final SearchEngine searchEngine; | 140 final SearchEngine searchEngine; |
| 129 final Element element; | 141 final Element element; |
| 130 final String oldName; | 142 final ClassElement elementClass; |
| 131 final String newName; | 143 final ElementKind elementKind; |
| 144 final String name; |
| 145 final bool isRename; |
| 132 | 146 |
| 133 Set<Element> elements = new Set(); | 147 Set<Element> elements = new Set<Element>(); |
| 134 List<SearchMatch> references = []; | 148 List<SearchMatch> references = <SearchMatch>[]; |
| 135 | 149 |
| 136 _RenameClassMemberValidator(this.searchEngine, Element element, this.newName) | 150 _ClassMemberValidator.forCreate(this.searchEngine, this.elementClass, |
| 137 : element = element, | 151 this.name) |
| 138 oldName = element.displayName; | 152 : isRename = false, |
| 153 element = null, |
| 154 elementKind = ElementKind.METHOD; |
| 155 |
| 156 _ClassMemberValidator.forRename(this.searchEngine, Element element, this.name) |
| 157 : isRename = true, |
| 158 element = element, |
| 159 elementClass = element.enclosingElement, |
| 160 elementKind = element.kind; |
| 139 | 161 |
| 140 Future<RefactoringStatus> validate() { | 162 Future<RefactoringStatus> validate() { |
| 141 RefactoringStatus result = new RefactoringStatus(); | 163 RefactoringStatus result = new RefactoringStatus(); |
| 142 ClassElement elementClass = element.enclosingElement; | |
| 143 // check if there is a member with "newName" in the same ClassElement | 164 // check if there is a member with "newName" in the same ClassElement |
| 144 for (Element newNameMember in getChildren(elementClass, newName)) { | 165 for (Element newNameMember in getChildren(elementClass, name)) { |
| 145 result.addError( | 166 result.addError( |
| 146 format( | 167 format( |
| 147 "Class '{0}' already declares {1} with name '{2}'.", | 168 "Class '{0}' already declares {1} with name '{2}'.", |
| 148 elementClass.displayName, | 169 elementClass.displayName, |
| 149 getElementKindName(newNameMember), | 170 getElementKindName(newNameMember), |
| 150 newName), | 171 name), |
| 151 new Location.fromElement(newNameMember)); | 172 new Location.fromElement(newNameMember)); |
| 152 } | 173 } |
| 153 // do chained computations | 174 // do chained computations |
| 154 Set<ClassElement> superClasses = getSuperClasses(elementClass); | 175 Set<ClassElement> superClasses = getSuperClasses(elementClass); |
| 155 Set<ClassElement> subClasses; | 176 Set<ClassElement> subClasses; |
| 156 return _prepareReferences().then((_) { | 177 return _prepareReferences().then((_) { |
| 157 return getSubClasses(searchEngine, elementClass).then((_subs) { | 178 return getSubClasses(searchEngine, elementClass).then((_subs) { |
| 158 subClasses = _subs; | 179 subClasses = _subs; |
| 159 }); | 180 }); |
| 160 }).then((_) { | 181 }).then((_) { |
| 161 // check shadowing in hierarchy | 182 // check shadowing in hierarchy |
| 162 return searchEngine.searchElementDeclarations(newName).then((decls) { | 183 return searchEngine.searchElementDeclarations(name).then((decls) { |
| 163 for (SearchMatch decl in decls) { | 184 for (SearchMatch decl in decls) { |
| 164 Element nameElement = getSyntheticAccessorVariable(decl.element); | 185 Element nameElement = getSyntheticAccessorVariable(decl.element); |
| 165 Element nameClass = nameElement.enclosingElement; | 186 Element nameClass = nameElement.enclosingElement; |
| 166 // renamed Element shadows member of superclass | 187 // renamed Element shadows member of superclass |
| 167 if (superClasses.contains(nameClass)) { | 188 if (superClasses.contains(nameClass)) { |
| 168 result.addError( | 189 result.addError( |
| 169 format( | 190 format( |
| 170 "Renamed {0} will shadow {1} '{2}'.", | 191 isRename ? |
| 171 getElementKindName(element), | 192 "Renamed {0} will shadow {1} '{2}'." : |
| 193 "Created {0} will shadow {1} '{2}'.", |
| 194 elementKind.displayName, |
| 172 getElementKindName(nameElement), | 195 getElementKindName(nameElement), |
| 173 getElementQualifiedName(nameElement)), | 196 getElementQualifiedName(nameElement)), |
| 174 new Location.fromElement(nameElement)); | 197 new Location.fromElement(nameElement)); |
| 175 } | 198 } |
| 176 // renamed Element is shadowed by member of subclass | 199 // renamed Element is shadowed by member of subclass |
| 177 if (subClasses.contains(nameClass)) { | 200 if (isRename && subClasses.contains(nameClass)) { |
| 178 result.addError( | 201 result.addError( |
| 179 format( | 202 format( |
| 180 "Renamed {0} will be shadowed by {1} '{2}'.", | 203 "Renamed {0} will be shadowed by {1} '{2}'.", |
| 181 getElementKindName(element), | 204 elementKind.displayName, |
| 182 getElementKindName(nameElement), | 205 getElementKindName(nameElement), |
| 183 getElementQualifiedName(nameElement)), | 206 getElementQualifiedName(nameElement)), |
| 184 new Location.fromElement(nameElement)); | 207 new Location.fromElement(nameElement)); |
| 185 } | 208 } |
| 186 // renamed Element is shadowed by local | 209 // renamed Element is shadowed by local |
| 187 if (nameElement is LocalElement) { | 210 if (nameElement is LocalElement) { |
| 188 LocalElement localElement = nameElement; | 211 LocalElement localElement = nameElement; |
| 189 ClassElement enclosingClass = | 212 ClassElement enclosingClass = |
| 190 nameElement.getAncestor((element) => element is ClassElement); | 213 nameElement.getAncestor((element) => element is ClassElement); |
| 191 if (enclosingClass == elementClass || | 214 if (enclosingClass == elementClass || |
| 192 subClasses.contains(enclosingClass)) { | 215 subClasses.contains(enclosingClass)) { |
| 193 for (SearchMatch reference in references) { | 216 for (SearchMatch reference in references) { |
| 194 if (isReferenceInLocalRange(localElement, reference)) { | 217 if (isReferenceInLocalRange(localElement, reference)) { |
| 195 result.addError( | 218 result.addError( |
| 196 format( | 219 format( |
| 197 "Usage of renamed {0} will be shadowed by {1} '{2}'.", | 220 "Usage of renamed {0} will be shadowed by {1} '{2}'.", |
| 198 getElementKindName(element), | 221 elementKind.displayName, |
| 199 getElementKindName(localElement), | 222 getElementKindName(localElement), |
| 200 localElement.displayName), | 223 localElement.displayName), |
| 201 new Location.fromMatch(reference)); | 224 new Location.fromMatch(reference)); |
| 202 } | 225 } |
| 203 } | 226 } |
| 204 } | 227 } |
| 205 } | 228 } |
| 206 } | 229 } |
| 207 }); | 230 }); |
| 208 }).then((_) => result); | 231 }).then((_) => result); |
| (...skipping 12 matching lines...) Expand all Loading... |
| 221 } else { | 244 } else { |
| 222 elements = new Set.from([element]); | 245 elements = new Set.from([element]); |
| 223 return new Future.value(); | 246 return new Future.value(); |
| 224 } | 247 } |
| 225 } | 248 } |
| 226 | 249 |
| 227 /** | 250 /** |
| 228 * Fills [references] with all references to [elements]. | 251 * Fills [references] with all references to [elements]. |
| 229 */ | 252 */ |
| 230 Future _prepareReferences() { | 253 Future _prepareReferences() { |
| 254 if (!isRename) { |
| 255 return new Future.value(); |
| 256 } |
| 231 return _prepareElements().then((_) { | 257 return _prepareElements().then((_) { |
| 232 return Future.forEach(elements, (Element element) { | 258 return Future.forEach(elements, (Element element) { |
| 233 return searchEngine.searchReferences(element).then((references) { | 259 return searchEngine.searchReferences(element).then((references) { |
| 234 this.references.addAll(references); | 260 this.references.addAll(references); |
| 235 }); | 261 }); |
| 236 }); | 262 }); |
| 237 }); | 263 }); |
| 238 } | 264 } |
| 239 } | 265 } |
| OLD | NEW |