| 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.refactoring.rename_class_member; | |
| 6 | |
| 7 import 'dart:async'; | |
| 8 | |
| 9 import 'package:analysis_services/correction/change.dart'; | |
| 10 import 'package:analysis_services/correction/status.dart'; | |
| 11 import 'package:analysis_services/refactoring/refactoring.dart'; | |
| 12 import 'package:analysis_services/search/hierarchy.dart'; | |
| 13 import 'package:analysis_services/search/search_engine.dart'; | |
| 14 import 'package:analysis_services/src/correction/util.dart'; | |
| 15 import 'package:analysis_services/src/refactoring/naming_conventions.dart'; | |
| 16 import 'package:analysis_services/src/refactoring/rename.dart'; | |
| 17 import 'package:analyzer/src/generated/element.dart'; | |
| 18 import 'package:analyzer/src/generated/java_core.dart'; | |
| 19 | |
| 20 | |
| 21 /** | |
| 22 * A [Refactoring] for renaming class member [Element]s. | |
| 23 */ | |
| 24 class RenameClassMemberRefactoringImpl extends RenameRefactoringImpl { | |
| 25 _RenameClassMemberValidator _validator; | |
| 26 | |
| 27 RenameClassMemberRefactoringImpl(SearchEngine searchEngine, Element element) | |
| 28 : super(searchEngine, element); | |
| 29 | |
| 30 @override | |
| 31 String get refactoringName { | |
| 32 if (element is TypeParameterElement) { | |
| 33 return "Rename Type Parameter"; | |
| 34 } | |
| 35 if (element is FieldElement) { | |
| 36 return "Rename Field"; | |
| 37 } | |
| 38 return "Rename Method"; | |
| 39 } | |
| 40 | |
| 41 @override | |
| 42 Future<RefactoringStatus> checkFinalConditions() { | |
| 43 _validator = new _RenameClassMemberValidator( | |
| 44 searchEngine, | |
| 45 element, | |
| 46 newName); | |
| 47 return _validator.validate(); | |
| 48 } | |
| 49 | |
| 50 @override | |
| 51 Future<RefactoringStatus> checkInitialConditions() { | |
| 52 RefactoringStatus result = new RefactoringStatus(); | |
| 53 if (element is MethodElement && (element as MethodElement).isOperator) { | |
| 54 result.addFatalError('Cannot rename operator.'); | |
| 55 } | |
| 56 return new Future.value(result); | |
| 57 } | |
| 58 | |
| 59 @override | |
| 60 RefactoringStatus checkNewName() { | |
| 61 RefactoringStatus result = super.checkNewName(); | |
| 62 if (element is FieldElement) { | |
| 63 FieldElement fieldElement = element as FieldElement; | |
| 64 if (fieldElement.isStatic && fieldElement.isConst) { | |
| 65 result.addStatus(validateConstantName(newName)); | |
| 66 } else { | |
| 67 result.addStatus(validateFieldName(newName)); | |
| 68 } | |
| 69 } | |
| 70 if (element is MethodElement) { | |
| 71 result.addStatus(validateMethodName(newName)); | |
| 72 } | |
| 73 return result; | |
| 74 } | |
| 75 | |
| 76 @override | |
| 77 Future<Change> createChange() { | |
| 78 Change change = new Change(refactoringName); | |
| 79 // update declarations | |
| 80 for (Element renameElement in _validator.elements) { | |
| 81 if (renameElement.isSynthetic && renameElement is FieldElement) { | |
| 82 addDeclarationEdit(change, renameElement.getter); | |
| 83 addDeclarationEdit(change, renameElement.setter); | |
| 84 } else { | |
| 85 addDeclarationEdit(change, renameElement); | |
| 86 } | |
| 87 } | |
| 88 // update references | |
| 89 List<SourceReference> references = | |
| 90 getSourceReferences(_validator.references); | |
| 91 for (SourceReference reference in references) { | |
| 92 addReferenceEdit(change, reference); | |
| 93 } | |
| 94 // potential matches | |
| 95 return searchEngine.searchMemberReferences(oldName).then((nameMatches) { | |
| 96 List<SourceReference> nameRefs = getSourceReferences(nameMatches); | |
| 97 for (SourceReference reference in nameRefs) { | |
| 98 // ignore resolved reference, we have already updated it | |
| 99 if (reference.isResolved) { | |
| 100 continue; | |
| 101 } | |
| 102 // check the element being renamed is accessible | |
| 103 { | |
| 104 LibraryElement whereLibrary = reference.element.library; | |
| 105 if (!element.isAccessibleIn(whereLibrary)) { | |
| 106 continue; | |
| 107 } | |
| 108 } | |
| 109 // add edit | |
| 110 Edit edit = createReferenceEdit(reference, newName); | |
| 111 _markEditAsPotential(edit); | |
| 112 change.addEdit(reference.file, edit); | |
| 113 } | |
| 114 }).then((_) => change); | |
| 115 } | |
| 116 | |
| 117 void _markEditAsPotential(Edit edit) { | |
| 118 String id = potentialEditIds.length.toString(); | |
| 119 potentialEditIds.add(id); | |
| 120 edit.id = id; | |
| 121 } | |
| 122 } | |
| 123 | |
| 124 | |
| 125 /** | |
| 126 * Helper to check if renaming of an [Element] to the given name will cause any | |
| 127 * problems. | |
| 128 */ | |
| 129 class _RenameClassMemberValidator { | |
| 130 final SearchEngine searchEngine; | |
| 131 final Element element; | |
| 132 final String oldName; | |
| 133 final String newName; | |
| 134 | |
| 135 Set<Element> elements = new Set(); | |
| 136 List<SearchMatch> references = []; | |
| 137 | |
| 138 _RenameClassMemberValidator(this.searchEngine, Element element, this.newName) | |
| 139 : element = element, | |
| 140 oldName = element.displayName; | |
| 141 | |
| 142 Future<RefactoringStatus> validate() { | |
| 143 RefactoringStatus result = new RefactoringStatus(); | |
| 144 ClassElement elementClass = element.enclosingElement; | |
| 145 // check if there is a member with "newName" in the same ClassElement | |
| 146 for (Element newNameMember in getChildren(elementClass, newName)) { | |
| 147 result.addError( | |
| 148 format( | |
| 149 "Class '{0}' already declares {1} with name '{2}'.", | |
| 150 elementClass.displayName, | |
| 151 getElementKindName(newNameMember), | |
| 152 newName), | |
| 153 new RefactoringStatusContext.forElement(newNameMember)); | |
| 154 } | |
| 155 // do chained computations | |
| 156 Set<ClassElement> superClasses = getSuperClasses(elementClass); | |
| 157 Set<ClassElement> subClasses; | |
| 158 return _prepareReferences().then((_) { | |
| 159 return getSubClasses(searchEngine, elementClass).then((_subs) { | |
| 160 subClasses = _subs; | |
| 161 }); | |
| 162 }).then((_) { | |
| 163 // check shadowing in hierarchy | |
| 164 return searchEngine.searchElementDeclarations(newName).then((decls) { | |
| 165 for (SearchMatch decl in decls) { | |
| 166 Element nameElement = getSyntheticAccessorVariable(decl.element); | |
| 167 Element nameClass = nameElement.enclosingElement; | |
| 168 // renamed Element shadows member of superclass | |
| 169 if (superClasses.contains(nameClass)) { | |
| 170 result.addError( | |
| 171 format( | |
| 172 "Renamed {0} will shadow {1} '{2}'.", | |
| 173 getElementKindName(element), | |
| 174 getElementKindName(nameElement), | |
| 175 getElementQualifiedName(nameElement)), | |
| 176 new RefactoringStatusContext.forElement(nameElement)); | |
| 177 } | |
| 178 // renamed Element is shadowed by member of subclass | |
| 179 if (subClasses.contains(nameClass)) { | |
| 180 result.addError( | |
| 181 format( | |
| 182 "Renamed {0} will be shadowed by {1} '{2}'.", | |
| 183 getElementKindName(element), | |
| 184 getElementKindName(nameElement), | |
| 185 getElementQualifiedName(nameElement)), | |
| 186 new RefactoringStatusContext.forElement(nameElement)); | |
| 187 } | |
| 188 // renamed Element is shadowed by local | |
| 189 if (nameElement is LocalElement) { | |
| 190 LocalElement localElement = nameElement; | |
| 191 ClassElement enclosingClass = | |
| 192 nameElement.getAncestor((element) => element is ClassElement); | |
| 193 if (enclosingClass == elementClass || | |
| 194 subClasses.contains(enclosingClass)) { | |
| 195 for (SearchMatch reference in references) { | |
| 196 if (isReferenceInLocalRange(localElement, reference)) { | |
| 197 result.addError( | |
| 198 format( | |
| 199 "Usage of renamed {0} will be shadowed by {1} '{2}'.", | |
| 200 getElementKindName(element), | |
| 201 getElementKindName(localElement), | |
| 202 localElement.displayName), | |
| 203 new RefactoringStatusContext.forMatch(reference)); | |
| 204 } | |
| 205 } | |
| 206 } | |
| 207 } | |
| 208 } | |
| 209 }); | |
| 210 }).then((_) => result); | |
| 211 } | |
| 212 | |
| 213 /** | |
| 214 * Fills [elements] with [Element]s to rename. | |
| 215 */ | |
| 216 Future _prepareElements() { | |
| 217 if (element is ClassMemberElement) { | |
| 218 return getHierarchyMembers( | |
| 219 searchEngine, | |
| 220 element).then((Set<Element> elements) { | |
| 221 this.elements = elements; | |
| 222 }); | |
| 223 } else { | |
| 224 elements = new Set.from([element]); | |
| 225 return new Future.value(); | |
| 226 } | |
| 227 } | |
| 228 | |
| 229 /** | |
| 230 * Fills [references] with all references to [elements]. | |
| 231 */ | |
| 232 Future _prepareReferences() { | |
| 233 return _prepareElements().then((_) { | |
| 234 return Future.forEach(elements, (Element element) { | |
| 235 return searchEngine.searchReferences(element).then((references) { | |
| 236 this.references.addAll(references); | |
| 237 }); | |
| 238 }); | |
| 239 }); | |
| 240 } | |
| 241 } | |
| OLD | NEW |