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_server.dart' | 9 import 'package:analysis_server/src/protocol_server.dart' |
10 hide Element, ElementKind; | 10 hide Element, ElementKind; |
(...skipping 174 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
185 } else { | 185 } else { |
186 if (elementClass.name == name) { | 186 if (elementClass.name == name) { |
187 result.addError( | 187 result.addError( |
188 format( | 188 format( |
189 "Created {0} has the same name as the declaring class '{1}'.", | 189 "Created {0} has the same name as the declaring class '{1}'.", |
190 elementKind.displayName, | 190 elementKind.displayName, |
191 name), | 191 name), |
192 newLocation_fromElement(elementClass)); | 192 newLocation_fromElement(elementClass)); |
193 } | 193 } |
194 } | 194 } |
195 // check shadowing in hierarchy | 195 // usage of the renamed Element is shadowed by a local element |
| 196 { |
| 197 _MatchShadowedByLocal conflict = _getShadowingLocalElement(); |
| 198 if (conflict != null) { |
| 199 LocalElement localElement = conflict.localElement; |
| 200 result.addError( |
| 201 format( |
| 202 "Usage of renamed {0} will be shadowed by {1} '{2}'.", |
| 203 elementKind.displayName, |
| 204 getElementKindName(localElement), |
| 205 localElement.displayName), |
| 206 newLocation_fromMatch(conflict.match)); |
| 207 } |
| 208 } |
| 209 // check shadowing in the hierarchy |
196 List<SearchMatch> declarations = | 210 List<SearchMatch> declarations = |
197 await searchEngine.searchElementDeclarations(name); | 211 await searchEngine.searchMemberDeclarations(name); |
198 for (SearchMatch declaration in declarations) { | 212 for (SearchMatch declaration in declarations) { |
199 Element nameElement = getSyntheticAccessorVariable(declaration.element); | 213 Element nameElement = getSyntheticAccessorVariable(declaration.element); |
200 Element nameClass = nameElement.enclosingElement; | 214 Element nameClass = nameElement.enclosingElement; |
201 // renamed Element shadows member of superclass | 215 // the renamed Element shadows a member of a superclass |
202 if (superClasses.contains(nameClass)) { | 216 if (superClasses.contains(nameClass)) { |
203 result.addError( | 217 result.addError( |
204 format( | 218 format( |
205 isRename | 219 isRename |
206 ? "Renamed {0} will shadow {1} '{2}'." | 220 ? "Renamed {0} will shadow {1} '{2}'." |
207 : "Created {0} will shadow {1} '{2}'.", | 221 : "Created {0} will shadow {1} '{2}'.", |
208 elementKind.displayName, | 222 elementKind.displayName, |
209 getElementKindName(nameElement), | 223 getElementKindName(nameElement), |
210 getElementQualifiedName(nameElement)), | 224 getElementQualifiedName(nameElement)), |
211 newLocation_fromElement(nameElement)); | 225 newLocation_fromElement(nameElement)); |
212 } | 226 } |
213 // renamed Element is shadowed by member of subclass | 227 // the renamed Element is shadowed by a member of a subclass |
214 if (isRename && subClasses.contains(nameClass)) { | 228 if (isRename && subClasses.contains(nameClass)) { |
215 result.addError( | 229 result.addError( |
216 format( | 230 format( |
217 "Renamed {0} will be shadowed by {1} '{2}'.", | 231 "Renamed {0} will be shadowed by {1} '{2}'.", |
218 elementKind.displayName, | 232 elementKind.displayName, |
219 getElementKindName(nameElement), | 233 getElementKindName(nameElement), |
220 getElementQualifiedName(nameElement)), | 234 getElementQualifiedName(nameElement)), |
221 newLocation_fromElement(nameElement)); | 235 newLocation_fromElement(nameElement)); |
222 } | 236 } |
223 // renamed Element is shadowed by local | |
224 if (nameElement is LocalElement) { | |
225 LocalElement localElement = nameElement; | |
226 ClassElement enclosingClass = | |
227 nameElement.getAncestor((element) => element is ClassElement); | |
228 if (enclosingClass == elementClass || | |
229 subClasses.contains(enclosingClass)) { | |
230 for (SearchMatch reference in references) { | |
231 if (isReferenceInLocalRange(localElement, reference)) { | |
232 result.addError( | |
233 format( | |
234 "Usage of renamed {0} will be shadowed by {1} '{2}'.", | |
235 elementKind.displayName, | |
236 getElementKindName(localElement), | |
237 localElement.displayName), | |
238 newLocation_fromMatch(reference)); | |
239 } | |
240 } | |
241 } | |
242 } | |
243 } | 237 } |
244 // visibility | 238 // visibility |
245 if (isRename) { | 239 if (isRename) { |
246 _validateWillBeInvisible(); | 240 _validateWillBeInvisible(); |
247 } | 241 } |
248 // done | 242 // done |
249 return result; | 243 return result; |
250 } | 244 } |
251 | 245 |
| 246 _MatchShadowedByLocal _getShadowingLocalElement() { |
| 247 for (SearchMatch match in references) { |
| 248 // qualified reference cannot be shadowed by a local element |
| 249 if (match.isQualified) { |
| 250 continue; |
| 251 } |
| 252 // check local elements of the enclosing executable |
| 253 Element containingElement = match.element; |
| 254 if (containingElement is ExecutableElement) { |
| 255 Iterable<LocalElement> localElements = <Iterable<LocalElement>>[ |
| 256 containingElement.functions, |
| 257 containingElement.localVariables, |
| 258 containingElement.parameters |
| 259 ].expand((Iterable<LocalElement> x) => x); |
| 260 for (LocalElement localElement in localElements) { |
| 261 if (localElement.displayName == name && |
| 262 localElement.visibleRange.intersects(match.sourceRange)) { |
| 263 return new _MatchShadowedByLocal(match, localElement); |
| 264 } |
| 265 } |
| 266 } |
| 267 } |
| 268 return null; |
| 269 } |
| 270 |
252 /** | 271 /** |
253 * Fills [elements] with [Element]s to rename. | 272 * Fills [elements] with [Element]s to rename. |
254 */ | 273 */ |
255 Future _prepareElements() async { | 274 Future _prepareElements() async { |
256 if (element is ClassMemberElement) { | 275 if (element is ClassMemberElement) { |
257 elements = await getHierarchyMembers(searchEngine, element); | 276 elements = await getHierarchyMembers(searchEngine, element); |
258 } else { | 277 } else { |
259 elements = new Set.from([element]); | 278 elements = new Set.from([element]); |
260 } | 279 } |
261 } | 280 } |
(...skipping 24 matching lines...) Expand all Loading... |
286 Element refElement = reference.element; | 305 Element refElement = reference.element; |
287 LibraryElement refLibrary = refElement.library; | 306 LibraryElement refLibrary = refElement.library; |
288 if (refLibrary != library) { | 307 if (refLibrary != library) { |
289 String message = format("Renamed {0} will be invisible in '{1}'.", | 308 String message = format("Renamed {0} will be invisible in '{1}'.", |
290 getElementKindName(element), getElementQualifiedName(refLibrary)); | 309 getElementKindName(element), getElementQualifiedName(refLibrary)); |
291 result.addError(message, newLocation_fromMatch(reference)); | 310 result.addError(message, newLocation_fromMatch(reference)); |
292 } | 311 } |
293 } | 312 } |
294 } | 313 } |
295 } | 314 } |
| 315 |
| 316 class _MatchShadowedByLocal { |
| 317 final SearchMatch match; |
| 318 final LocalElement localElement; |
| 319 |
| 320 _MatchShadowedByLocal(this.match, this.localElement); |
| 321 } |
OLD | NEW |