| 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 /** | 5 /** |
| 6 * Code for classifying the semantics of identifiers appearing in a Dart file. | 6 * Code for classifying the semantics of identifiers appearing in a Dart file. |
| 7 */ | 7 */ |
| 8 library analyzer2dart.identifierSemantics; | 8 library analyzer2dart.identifierSemantics; |
| 9 | 9 |
| 10 import 'package:analyzer/analyzer.dart'; | 10 import 'package:analyzer/analyzer.dart'; |
| (...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 62 | 62 |
| 63 String toString() => name; | 63 String toString() => name; |
| 64 | 64 |
| 65 const AccessKind._(this.name); | 65 const AccessKind._(this.name); |
| 66 } | 66 } |
| 67 | 67 |
| 68 /** | 68 /** |
| 69 * Data structure used to classify the semantics of a property access or method | 69 * Data structure used to classify the semantics of a property access or method |
| 70 * or function invocation. | 70 * or function invocation. |
| 71 */ | 71 */ |
| 72 // TODO(paulberry,johnniwinther): Support index operations in AccessSemantics. |
| 72 class AccessSemantics { | 73 class AccessSemantics { |
| 73 /** | 74 /** |
| 74 * The kind of access. | 75 * The kind of access. |
| 75 */ | 76 */ |
| 76 final AccessKind kind; | 77 final AccessKind kind; |
| 77 | 78 |
| 78 /** | 79 /** |
| 79 * The identifier being used to access the property, method, or function. | 80 * The identifier being used to access the property, method, or function. |
| 80 */ | 81 */ |
| 81 final SimpleIdentifier identifier; | 82 final SimpleIdentifier identifier; |
| (...skipping 110 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 192 sb.write('target=this.$identifier'); | 193 sb.write('target=this.$identifier'); |
| 193 } else { | 194 } else { |
| 194 sb.write('target=$target.$identifier'); | 195 sb.write('target=$target.$identifier'); |
| 195 } | 196 } |
| 196 } | 197 } |
| 197 sb.write(']'); | 198 sb.write(']'); |
| 198 return sb.toString(); | 199 return sb.toString(); |
| 199 } | 200 } |
| 200 } | 201 } |
| 201 | 202 |
| 202 /** | 203 // TODO(johnniwinther,paulberry): This should be a constant. |
| 203 * Return the semantics for [node]. | 204 final AccessSemanticsVisitor ACCESS_SEMANTICS_VISITOR = |
| 204 */ | 205 new AccessSemanticsVisitor(); |
| 205 AccessSemantics classifyMethodInvocation(MethodInvocation node) { | 206 |
| 206 Expression target = node.realTarget; | 207 // TODO(johnniwinther,paulberry): This should extend a non-recursive visitor. |
| 207 Element staticElement = node.methodName.staticElement; | 208 class AccessSemanticsVisitor extends RecursiveAstVisitor<AccessSemantics> { |
| 208 if (target == null) { | 209 /** |
| 209 if (staticElement is FunctionElement) { | 210 * Return the semantics for [node]. |
| 210 if (staticElement.enclosingElement is CompilationUnitElement) { | 211 */ |
| 212 @override |
| 213 AccessSemantics visitMethodInvocation(MethodInvocation node) { |
| 214 Expression target = node.realTarget; |
| 215 Element staticElement = node.methodName.staticElement; |
| 216 if (target == null) { |
| 217 if (staticElement is FunctionElement) { |
| 218 if (staticElement.enclosingElement is CompilationUnitElement) { |
| 219 return new AccessSemantics.staticMethod( |
| 220 node.methodName, |
| 221 staticElement, |
| 222 null, |
| 223 isInvoke: true); |
| 224 } else { |
| 225 return new AccessSemantics.localFunction( |
| 226 node.methodName, |
| 227 staticElement, |
| 228 isInvoke: true); |
| 229 } |
| 230 } else if (staticElement is MethodElement && staticElement.isStatic) { |
| 211 return new AccessSemantics.staticMethod( | 231 return new AccessSemantics.staticMethod( |
| 212 node.methodName, | 232 node.methodName, |
| 213 staticElement, | 233 staticElement, |
| 214 null, | 234 staticElement.enclosingElement, |
| 215 isInvoke: true); | 235 isInvoke: true); |
| 216 } else { | 236 } else if (staticElement is PropertyAccessorElement) { |
| 217 return new AccessSemantics.localFunction( | 237 if (staticElement.isSynthetic) { |
| 238 if (staticElement.enclosingElement is CompilationUnitElement) { |
| 239 return new AccessSemantics.staticField( |
| 240 node.methodName, |
| 241 staticElement.variable, |
| 242 null, |
| 243 isInvoke: true); |
| 244 } else if (staticElement.isStatic) { |
| 245 return new AccessSemantics.staticField( |
| 246 node.methodName, |
| 247 staticElement.variable, |
| 248 staticElement.enclosingElement, |
| 249 isInvoke: true); |
| 250 } |
| 251 } else { |
| 252 if (staticElement.enclosingElement is CompilationUnitElement) { |
| 253 return new AccessSemantics.staticProperty( |
| 254 node.methodName, |
| 255 staticElement, |
| 256 null, |
| 257 isInvoke: true); |
| 258 } else if (staticElement.isStatic) { |
| 259 return new AccessSemantics.staticProperty( |
| 260 node.methodName, |
| 261 staticElement, |
| 262 staticElement.enclosingElement, |
| 263 isInvoke: true); |
| 264 } |
| 265 } |
| 266 } else if (staticElement is LocalVariableElement) { |
| 267 return new AccessSemantics.localVariable( |
| 218 node.methodName, | 268 node.methodName, |
| 219 staticElement, | 269 staticElement, |
| 220 isInvoke: true); | 270 isInvoke: true); |
| 221 } | 271 } else if (staticElement is ParameterElement) { |
| 222 } else if (staticElement is MethodElement && staticElement.isStatic) { | 272 return new AccessSemantics.parameter( |
| 223 return new AccessSemantics.staticMethod( | 273 node.methodName, |
| 224 node.methodName, | 274 staticElement, |
| 225 staticElement, | 275 isInvoke: true); |
| 226 staticElement.enclosingElement, | 276 } |
| 227 isInvoke: true); | 277 } else if (target is Identifier) { |
| 228 } else if (staticElement is PropertyAccessorElement) { | 278 Element targetStaticElement = target.staticElement; |
| 279 if (targetStaticElement is PrefixElement) { |
| 280 if (staticElement == null) { |
| 281 return new AccessSemantics.dynamic( |
| 282 node.methodName, |
| 283 null, |
| 284 isInvoke: true); |
| 285 } else if (staticElement is PropertyAccessorElement) { |
| 286 if (staticElement.isSynthetic) { |
| 287 return new AccessSemantics.staticField( |
| 288 node.methodName, |
| 289 staticElement.variable, |
| 290 null, |
| 291 isInvoke: true); |
| 292 } else { |
| 293 return new AccessSemantics.staticProperty( |
| 294 node.methodName, |
| 295 staticElement, |
| 296 null, |
| 297 isInvoke: true); |
| 298 } |
| 299 } else { |
| 300 return new AccessSemantics.staticMethod( |
| 301 node.methodName, |
| 302 staticElement, |
| 303 null, |
| 304 isInvoke: true); |
| 305 } |
| 306 } else if (targetStaticElement is ClassElement) { |
| 307 if (staticElement is PropertyAccessorElement) { |
| 308 if (staticElement.isSynthetic) { |
| 309 return new AccessSemantics.staticField( |
| 310 node.methodName, |
| 311 staticElement.variable, |
| 312 targetStaticElement, |
| 313 isInvoke: true); |
| 314 } else { |
| 315 return new AccessSemantics.staticProperty( |
| 316 node.methodName, |
| 317 staticElement, |
| 318 targetStaticElement, |
| 319 isInvoke: true); |
| 320 } |
| 321 } else { |
| 322 return new AccessSemantics.staticMethod( |
| 323 node.methodName, |
| 324 staticElement, |
| 325 targetStaticElement, |
| 326 isInvoke: true); |
| 327 } |
| 328 } |
| 329 } |
| 330 return new AccessSemantics.dynamic(node.methodName, target, isInvoke: true); |
| 331 } |
| 332 |
| 333 /** |
| 334 * Return the access semantics for [node]. |
| 335 */ |
| 336 @override |
| 337 AccessSemantics visitPrefixedIdentifier(PrefixedIdentifier node) { |
| 338 return _classifyPrefixed(node.prefix, node.identifier); |
| 339 } |
| 340 |
| 341 /** |
| 342 * Helper function for classifying an expression of type |
| 343 * Identifier.SimpleIdentifier. |
| 344 */ |
| 345 AccessSemantics _classifyPrefixed(Identifier lhs, SimpleIdentifier rhs) { |
| 346 Element lhsElement = lhs.staticElement; |
| 347 Element rhsElement = rhs.staticElement; |
| 348 if (lhsElement is PrefixElement) { |
| 349 if (rhsElement is PropertyAccessorElement) { |
| 350 if (rhsElement.isSynthetic) { |
| 351 return new AccessSemantics.staticField(rhs, rhsElement.variable, null)
; |
| 352 } else { |
| 353 return new AccessSemantics.staticProperty(rhs, rhsElement, null); |
| 354 } |
| 355 } else if (rhsElement is FunctionElement) { |
| 356 return new AccessSemantics.staticMethod(rhs, rhsElement, null); |
| 357 } else { |
| 358 return new AccessSemantics.dynamic(rhs, null); |
| 359 } |
| 360 } else if (lhsElement is ClassElement) { |
| 361 if (rhsElement is PropertyAccessorElement && rhsElement.isSynthetic) { |
| 362 return new AccessSemantics.staticField( |
| 363 rhs, |
| 364 rhsElement.variable, |
| 365 lhsElement); |
| 366 } else if (rhsElement is MethodElement) { |
| 367 return new AccessSemantics.staticMethod(rhs, rhsElement, lhsElement); |
| 368 } else { |
| 369 return new AccessSemantics.staticProperty(rhs, rhsElement, lhsElement); |
| 370 } |
| 371 } else { |
| 372 return new AccessSemantics.dynamic(rhs, lhs); |
| 373 } |
| 374 } |
| 375 |
| 376 /** |
| 377 * Return the access semantics for [node]. |
| 378 */ |
| 379 @override |
| 380 AccessSemantics visitPropertyAccess(PropertyAccess node) { |
| 381 if (node.target is Identifier) { |
| 382 return _classifyPrefixed(node.target, node.propertyName); |
| 383 } else { |
| 384 return new AccessSemantics.dynamic(node.propertyName, node.realTarget); |
| 385 } |
| 386 } |
| 387 |
| 388 /** |
| 389 * Return the access semantics for [node]. |
| 390 * |
| 391 * Note: if [node] is the right hand side of a [PropertyAccess] or |
| 392 * [PrefixedIdentifier], or the method name of a [MethodInvocation], the retur
n |
| 393 * value is null, since the semantics are determined by the parent. In |
| 394 * practice these cases should never arise because the parent will visit the |
| 395 * parent node before visiting this one. |
| 396 */ |
| 397 @override |
| 398 AccessSemantics visitSimpleIdentifier(SimpleIdentifier node) { |
| 399 AstNode parent = node.parent; |
| 400 if (node.inDeclarationContext()) { |
| 401 // This identifier is a declaration, not a use. |
| 402 return null; |
| 403 } |
| 404 if (parent is TypeName) { |
| 405 // TODO(paulberry): handle this case. Or, perhaps it would be better to |
| 406 // require clients not to visit the children of a TypeName when visiting |
| 407 // the AST structure. |
| 408 // |
| 409 // TODO(paulberry): be sure to consider type literals, e.g.: |
| 410 // class A {} |
| 411 // var a = A; |
| 412 return null; |
| 413 } |
| 414 if ((parent is PropertyAccess && parent.propertyName == node) || |
| 415 (parent is PrefixedIdentifier && parent.identifier == node) || |
| 416 (parent is MethodInvocation && parent.methodName == node)) { |
| 417 // The access semantics are determined by the parent. |
| 418 return null; |
| 419 } |
| 420 // TODO(paulberry): handle PrefixElement. |
| 421 Element staticElement = node.staticElement; |
| 422 if (staticElement is PropertyAccessorElement) { |
| 229 if (staticElement.isSynthetic) { | 423 if (staticElement.isSynthetic) { |
| 230 if (staticElement.enclosingElement is CompilationUnitElement) { | 424 if (staticElement.enclosingElement is CompilationUnitElement) { |
| 231 return new AccessSemantics.staticField( | 425 return new AccessSemantics.staticField( |
| 232 node.methodName, | 426 node, |
| 233 staticElement.variable, | 427 staticElement.variable, |
| 234 null, | 428 null); |
| 235 isInvoke: true); | |
| 236 } else if (staticElement.isStatic) { | 429 } else if (staticElement.isStatic) { |
| 237 return new AccessSemantics.staticField( | 430 return new AccessSemantics.staticField( |
| 238 node.methodName, | 431 node, |
| 239 staticElement.variable, | 432 staticElement.variable, |
| 240 staticElement.enclosingElement, | 433 staticElement.enclosingElement); |
| 241 isInvoke: true); | |
| 242 } | 434 } |
| 243 } else { | 435 } else { |
| 244 if (staticElement.enclosingElement is CompilationUnitElement) { | 436 if (staticElement.enclosingElement is CompilationUnitElement) { |
| 245 return new AccessSemantics.staticProperty( | 437 return new AccessSemantics.staticProperty(node, staticElement, null); |
| 246 node.methodName, | |
| 247 staticElement, | |
| 248 null, | |
| 249 isInvoke: true); | |
| 250 } else if (staticElement.isStatic) { | 438 } else if (staticElement.isStatic) { |
| 251 return new AccessSemantics.staticProperty( | 439 return new AccessSemantics.staticProperty( |
| 252 node.methodName, | 440 node, |
| 253 staticElement, | 441 staticElement, |
| 254 staticElement.enclosingElement, | 442 staticElement.enclosingElement); |
| 255 isInvoke: true); | |
| 256 } | 443 } |
| 257 } | 444 } |
| 258 } else if (staticElement is LocalVariableElement) { | 445 } else if (staticElement is LocalVariableElement) { |
| 259 return new AccessSemantics.localVariable( | 446 return new AccessSemantics.localVariable(node, staticElement); |
| 260 node.methodName, | 447 } else if (staticElement is ParameterElement) { |
| 448 return new AccessSemantics.parameter(node, staticElement); |
| 449 } else if (staticElement is FunctionElement) { |
| 450 if (staticElement.enclosingElement is CompilationUnitElement) { |
| 451 return new AccessSemantics.staticMethod(node, staticElement, null); |
| 452 } else { |
| 453 return new AccessSemantics.localFunction(node, staticElement); |
| 454 } |
| 455 } else if (staticElement is MethodElement && staticElement.isStatic) { |
| 456 return new AccessSemantics.staticMethod( |
| 457 node, |
| 261 staticElement, | 458 staticElement, |
| 262 isInvoke: true); | 459 staticElement.enclosingElement); |
| 263 } else if (staticElement is ParameterElement) { | 460 } |
| 264 return new AccessSemantics.parameter( | 461 return new AccessSemantics.dynamic(node, null); |
| 265 node.methodName, | 462 } |
| 266 staticElement, | 463 } |
| 267 isInvoke: true); | |
| 268 } | |
| 269 } else if (target is Identifier) { | |
| 270 Element targetStaticElement = target.staticElement; | |
| 271 if (targetStaticElement is PrefixElement) { | |
| 272 if (staticElement == null) { | |
| 273 return new AccessSemantics.dynamic( | |
| 274 node.methodName, | |
| 275 null, | |
| 276 isInvoke: true); | |
| 277 } else if (staticElement is PropertyAccessorElement) { | |
| 278 if (staticElement.isSynthetic) { | |
| 279 return new AccessSemantics.staticField( | |
| 280 node.methodName, | |
| 281 staticElement.variable, | |
| 282 null, | |
| 283 isInvoke: true); | |
| 284 } else { | |
| 285 return new AccessSemantics.staticProperty( | |
| 286 node.methodName, | |
| 287 staticElement, | |
| 288 null, | |
| 289 isInvoke: true); | |
| 290 } | |
| 291 } else { | |
| 292 return new AccessSemantics.staticMethod( | |
| 293 node.methodName, | |
| 294 staticElement, | |
| 295 null, | |
| 296 isInvoke: true); | |
| 297 } | |
| 298 } else if (targetStaticElement is ClassElement) { | |
| 299 if (staticElement is PropertyAccessorElement) { | |
| 300 if (staticElement.isSynthetic) { | |
| 301 return new AccessSemantics.staticField( | |
| 302 node.methodName, | |
| 303 staticElement.variable, | |
| 304 targetStaticElement, | |
| 305 isInvoke: true); | |
| 306 } else { | |
| 307 return new AccessSemantics.staticProperty( | |
| 308 node.methodName, | |
| 309 staticElement, | |
| 310 targetStaticElement, | |
| 311 isInvoke: true); | |
| 312 } | |
| 313 } else { | |
| 314 return new AccessSemantics.staticMethod( | |
| 315 node.methodName, | |
| 316 staticElement, | |
| 317 targetStaticElement, | |
| 318 isInvoke: true); | |
| 319 } | |
| 320 } | |
| 321 } | |
| 322 return new AccessSemantics.dynamic(node.methodName, target, isInvoke: true); | |
| 323 } | |
| 324 | |
| 325 /** | |
| 326 * Return the access semantics for [node]. | |
| 327 */ | |
| 328 AccessSemantics classifyPrefixedIdentifier(PrefixedIdentifier node) { | |
| 329 return _classifyPrefixed(node.prefix, node.identifier); | |
| 330 } | |
| 331 | |
| 332 /** | |
| 333 * Helper function for classifying an expression of type | |
| 334 * Identifier.SimpleIdentifier. | |
| 335 */ | |
| 336 AccessSemantics _classifyPrefixed(Identifier lhs, SimpleIdentifier rhs) { | |
| 337 Element lhsElement = lhs.staticElement; | |
| 338 Element rhsElement = rhs.staticElement; | |
| 339 if (lhsElement is PrefixElement) { | |
| 340 if (rhsElement is PropertyAccessorElement) { | |
| 341 if (rhsElement.isSynthetic) { | |
| 342 return new AccessSemantics.staticField(rhs, rhsElement.variable, null); | |
| 343 } else { | |
| 344 return new AccessSemantics.staticProperty(rhs, rhsElement, null); | |
| 345 } | |
| 346 } else if (rhsElement is FunctionElement) { | |
| 347 return new AccessSemantics.staticMethod(rhs, rhsElement, null); | |
| 348 } else { | |
| 349 return new AccessSemantics.dynamic(rhs, null); | |
| 350 } | |
| 351 } else if (lhsElement is ClassElement) { | |
| 352 if (rhsElement is PropertyAccessorElement && rhsElement.isSynthetic) { | |
| 353 return new AccessSemantics.staticField( | |
| 354 rhs, | |
| 355 rhsElement.variable, | |
| 356 lhsElement); | |
| 357 } else if (rhsElement is MethodElement) { | |
| 358 return new AccessSemantics.staticMethod(rhs, rhsElement, lhsElement); | |
| 359 } else { | |
| 360 return new AccessSemantics.staticProperty(rhs, rhsElement, lhsElement); | |
| 361 } | |
| 362 } else { | |
| 363 return new AccessSemantics.dynamic(rhs, lhs); | |
| 364 } | |
| 365 } | |
| 366 | |
| 367 /** | |
| 368 * Return the access semantics for [node]. | |
| 369 */ | |
| 370 AccessSemantics classifyPropertyAccess(PropertyAccess node) { | |
| 371 if (node.target is Identifier) { | |
| 372 return _classifyPrefixed(node.target, node.propertyName); | |
| 373 } else { | |
| 374 return new AccessSemantics.dynamic(node.propertyName, node.realTarget); | |
| 375 } | |
| 376 } | |
| 377 | |
| 378 /** | |
| 379 * Return the access semantics for [node]. | |
| 380 * | |
| 381 * Note: if [node] is the right hand side of a [PropertyAccess] or | |
| 382 * [PrefixedIdentifier], or the method name of a [MethodInvocation], the return | |
| 383 * value is null, since the semantics are determined by the parent. In | |
| 384 * practice these cases should never arise because the parent will visit the | |
| 385 * parent node before visiting this one. | |
| 386 */ | |
| 387 AccessSemantics classifySimpleIdentifier(SimpleIdentifier node) { | |
| 388 AstNode parent = node.parent; | |
| 389 if (node.inDeclarationContext()) { | |
| 390 // This identifier is a declaration, not a use. | |
| 391 return null; | |
| 392 } | |
| 393 if (parent is TypeName) { | |
| 394 // TODO(paulberry): handle this case. Or, perhaps it would be better to | |
| 395 // require clients not to visit the children of a TypeName when visiting | |
| 396 // the AST structure. | |
| 397 // | |
| 398 // TODO(paulberry): be sure to consider type literals, e.g.: | |
| 399 // class A {} | |
| 400 // var a = A; | |
| 401 return null; | |
| 402 } | |
| 403 if ((parent is PropertyAccess && parent.propertyName == node) || | |
| 404 (parent is PrefixedIdentifier && parent.identifier == node) || | |
| 405 (parent is MethodInvocation && parent.methodName == node)) { | |
| 406 // The access semantics are determined by the parent. | |
| 407 return null; | |
| 408 } | |
| 409 // TODO(paulberry): handle PrefixElement. | |
| 410 Element staticElement = node.staticElement; | |
| 411 if (staticElement is PropertyAccessorElement) { | |
| 412 if (staticElement.isSynthetic) { | |
| 413 if (staticElement.enclosingElement is CompilationUnitElement) { | |
| 414 return new AccessSemantics.staticField( | |
| 415 node, | |
| 416 staticElement.variable, | |
| 417 null); | |
| 418 } else if (staticElement.isStatic) { | |
| 419 return new AccessSemantics.staticField( | |
| 420 node, | |
| 421 staticElement.variable, | |
| 422 staticElement.enclosingElement); | |
| 423 } | |
| 424 } else { | |
| 425 if (staticElement.enclosingElement is CompilationUnitElement) { | |
| 426 return new AccessSemantics.staticProperty(node, staticElement, null); | |
| 427 } else if (staticElement.isStatic) { | |
| 428 return new AccessSemantics.staticProperty( | |
| 429 node, | |
| 430 staticElement, | |
| 431 staticElement.enclosingElement); | |
| 432 } | |
| 433 } | |
| 434 } else if (staticElement is LocalVariableElement) { | |
| 435 return new AccessSemantics.localVariable(node, staticElement); | |
| 436 } else if (staticElement is ParameterElement) { | |
| 437 return new AccessSemantics.parameter(node, staticElement); | |
| 438 } else if (staticElement is FunctionElement) { | |
| 439 if (staticElement.enclosingElement is CompilationUnitElement) { | |
| 440 return new AccessSemantics.staticMethod(node, staticElement, null); | |
| 441 } else { | |
| 442 return new AccessSemantics.localFunction(node, staticElement); | |
| 443 } | |
| 444 } else if (staticElement is MethodElement && staticElement.isStatic) { | |
| 445 return new AccessSemantics.staticMethod( | |
| 446 node, | |
| 447 staticElement, | |
| 448 staticElement.enclosingElement); | |
| 449 } | |
| 450 return new AccessSemantics.dynamic(node, null); | |
| 451 } | |
| OLD | NEW |