| 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_unit_member; | 5 library services.src.refactoring.rename_unit_member; |
| 6 | 6 |
| 7 import 'dart:async'; | 7 import 'dart:async'; |
| 8 | 8 |
| 9 import 'package:analysis_server/src/protocol_server.dart' show | 9 import 'package:analysis_server/src/protocol_server.dart' show |
| 10 newLocation_fromElement, newLocation_fromMatch; | 10 newLocation_fromElement, newLocation_fromMatch; |
| (...skipping 122 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 133 this.elementKind, this.name) | 133 this.elementKind, this.name) |
| 134 : isRename = false; | 134 : isRename = false; |
| 135 | 135 |
| 136 _RenameUnitMemberValidator.forRename(this.searchEngine, this.element, | 136 _RenameUnitMemberValidator.forRename(this.searchEngine, this.element, |
| 137 this.name) | 137 this.name) |
| 138 : isRename = true { | 138 : isRename = true { |
| 139 library = element.getAncestor((e) => e is LibraryElement); | 139 library = element.getAncestor((e) => e is LibraryElement); |
| 140 elementKind = element.kind; | 140 elementKind = element.kind; |
| 141 } | 141 } |
| 142 | 142 |
| 143 Future<RefactoringStatus> validate() { | 143 Future<RefactoringStatus> validate() async { |
| 144 _validateWillConflict(); | 144 _validateWillConflict(); |
| 145 List<Future> futures = <Future>[]; | |
| 146 if (isRename) { | 145 if (isRename) { |
| 147 futures.add(_validateWillBeShadowed()); | 146 await _validateWillBeShadowed(); |
| 148 } | 147 } |
| 149 futures.add(_validateWillShadow()); | 148 await _validateWillShadow(); |
| 150 return Future.wait(futures).then((_) { | 149 return result; |
| 151 return result; | |
| 152 }); | |
| 153 } | 150 } |
| 154 | 151 |
| 155 /** | 152 /** |
| 156 * Returns `true` if [element] is visible at the given [SearchMatch]. | 153 * Returns `true` if [element] is visible at the given [SearchMatch]. |
| 157 */ | 154 */ |
| 158 bool _isVisibleAt(Element element, SearchMatch at) { | 155 bool _isVisibleAt(Element element, SearchMatch at) { |
| 159 LibraryElement atLibrary = at.element.library; | 156 LibraryElement atLibrary = at.element.library; |
| 160 // may be the same library | 157 // may be the same library |
| 161 if (library == atLibrary) { | 158 if (library == atLibrary) { |
| 162 return true; | 159 return true; |
| 163 } | 160 } |
| 164 // check imports | 161 // check imports |
| 165 for (ImportElement importElement in atLibrary.imports) { | 162 for (ImportElement importElement in atLibrary.imports) { |
| 166 // ignore if imported with prefix | 163 // ignore if imported with prefix |
| 167 if (importElement.prefix != null) { | 164 if (importElement.prefix != null) { |
| 168 continue; | 165 continue; |
| 169 } | 166 } |
| 170 // check imported elements | 167 // check imported elements |
| 171 if (getImportNamespace(importElement).containsValue(element)) { | 168 if (getImportNamespace(importElement).containsValue(element)) { |
| 172 return true; | 169 return true; |
| 173 } | 170 } |
| 174 } | 171 } |
| 175 // no, it is not visible | 172 // no, it is not visible |
| 176 return false; | 173 return false; |
| 177 } | 174 } |
| 178 | 175 |
| 179 /** | 176 /** |
| 180 * Validates if any usage of [element] renamed to [name] will be shadowed. | 177 * Validates if any usage of [element] renamed to [name] will be shadowed. |
| 181 */ | 178 */ |
| 182 Future _validateWillBeShadowed() { | 179 Future _validateWillBeShadowed() async { |
| 183 if (!isRename) { | 180 if (!isRename) { |
| 184 return new Future.value(); | 181 return; |
| 185 } | 182 } |
| 186 return searchEngine.searchReferences(element).then((references) { | 183 List<SearchMatch> references = await searchEngine.searchReferences(element); |
| 187 for (SearchMatch reference in references) { | 184 for (SearchMatch reference in references) { |
| 188 Element refElement = reference.element; | 185 Element refElement = reference.element; |
| 189 ClassElement refClass = | 186 ClassElement refClass = refElement.getAncestor((e) => e is ClassElement); |
| 190 refElement.getAncestor((e) => e is ClassElement); | 187 if (refClass != null) { |
| 191 if (refClass != null) { | 188 visitChildren(refClass, (shadow) { |
| 192 visitChildren(refClass, (shadow) { | 189 if (hasDisplayName(shadow, name)) { |
| 193 if (hasDisplayName(shadow, name)) { | 190 String message = format( |
| 194 String message = format( | 191 "Reference to renamed {0} will be shadowed by {1} '{2}'.", |
| 195 "Reference to renamed {0} will be shadowed by {1} '{2}'.", | 192 getElementKindName(element), |
| 196 getElementKindName(element), | 193 getElementKindName(shadow), |
| 197 getElementKindName(shadow), | 194 getElementQualifiedName(shadow)); |
| 198 getElementQualifiedName(shadow)); | 195 result.addError(message, newLocation_fromElement(shadow)); |
| 199 result.addError(message, newLocation_fromElement(shadow)); | 196 } |
| 200 } | 197 }); |
| 201 }); | |
| 202 } | |
| 203 } | 198 } |
| 204 }); | 199 } |
| 205 } | 200 } |
| 206 | 201 |
| 207 /** | 202 /** |
| 208 * Validates if [element] renamed to [name] will conflict with another | 203 * Validates if [element] renamed to [name] will conflict with another |
| 209 * top-level [Element] in the same library. | 204 * top-level [Element] in the same library. |
| 210 */ | 205 */ |
| 211 void _validateWillConflict() { | 206 void _validateWillConflict() { |
| 212 visitLibraryTopLevelElements(library, (element) { | 207 visitLibraryTopLevelElements(library, (element) { |
| 213 if (hasDisplayName(element, name)) { | 208 if (hasDisplayName(element, name)) { |
| 214 String message = format( | 209 String message = format( |
| 215 "Library already declares {0} with name '{1}'.", | 210 "Library already declares {0} with name '{1}'.", |
| 216 getElementKindName(element), | 211 getElementKindName(element), |
| 217 name); | 212 name); |
| 218 result.addError(message, newLocation_fromElement(element)); | 213 result.addError(message, newLocation_fromElement(element)); |
| 219 } | 214 } |
| 220 }); | 215 }); |
| 221 } | 216 } |
| 222 | 217 |
| 223 /** | 218 /** |
| 224 * Validates if renamed [element] will shadow any [Element] named [name]. | 219 * Validates if renamed [element] will shadow any [Element] named [name]. |
| 225 */ | 220 */ |
| 226 Future _validateWillShadow() { | 221 Future _validateWillShadow() async { |
| 227 return searchEngine.searchMemberDeclarations(name).then((declarations) { | 222 List<SearchMatch> declarations = |
| 228 return Future.forEach(declarations, (SearchMatch declaration) { | 223 await searchEngine.searchMemberDeclarations(name); |
| 229 Element member = declaration.element; | 224 for (SearchMatch declaration in declarations) { |
| 230 ClassElement declaringClass = member.enclosingElement; | 225 Element member = declaration.element; |
| 231 return searchEngine.searchReferences(member).then((memberReferences) { | 226 ClassElement declaringClass = member.enclosingElement; |
| 232 for (SearchMatch memberReference in memberReferences) { | 227 List<SearchMatch> memberReferences = |
| 233 Element refElement = memberReference.element; | 228 await searchEngine.searchReferences(member); |
| 234 // cannot be shadowed if qualified | 229 for (SearchMatch memberReference in memberReferences) { |
| 235 if (memberReference.isQualified) { | 230 Element refElement = memberReference.element; |
| 236 continue; | 231 // cannot be shadowed if qualified |
| 237 } | 232 if (memberReference.isQualified) { |
| 238 // cannot be shadowed if declared in the same class as reference | 233 continue; |
| 239 ClassElement refClass = | 234 } |
| 240 refElement.getAncestor((e) => e is ClassElement); | 235 // cannot be shadowed if declared in the same class as reference |
| 241 if (refClass == declaringClass) { | 236 ClassElement refClass = |
| 242 continue; | 237 refElement.getAncestor((e) => e is ClassElement); |
| 243 } | 238 if (refClass == declaringClass) { |
| 244 // ignore if not visible | 239 continue; |
| 245 if (!_isVisibleAt(element, memberReference)) { | 240 } |
| 246 continue; | 241 // ignore if not visible |
| 247 } | 242 if (!_isVisibleAt(element, memberReference)) { |
| 248 // OK, reference will be shadowed be the element being renamed | 243 continue; |
| 249 String message = format( | 244 } |
| 250 isRename ? | 245 // OK, reference will be shadowed be the element being renamed |
| 251 "Renamed {0} will shadow {1} '{2}'." : | 246 String message = format( |
| 252 "Created {0} will shadow {1} '{2}'.", | 247 isRename ? |
| 253 elementKind.displayName, | 248 "Renamed {0} will shadow {1} '{2}'." : |
| 254 getElementKindName(member), | 249 "Created {0} will shadow {1} '{2}'.", |
| 255 getElementQualifiedName(member)); | 250 elementKind.displayName, |
| 256 result.addError(message, newLocation_fromMatch(memberReference)); | 251 getElementKindName(member), |
| 257 } | 252 getElementQualifiedName(member)); |
| 258 }); | 253 result.addError(message, newLocation_fromMatch(memberReference)); |
| 259 }); | 254 } |
| 260 }); | 255 } |
| 261 } | 256 } |
| 262 } | 257 } |
| OLD | NEW |