Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(464)

Side by Side Diff: pkg/analysis_server/lib/src/services/refactoring/inline_method.dart

Issue 526583002: 'Inline Method' refactoring. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(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.inline_method;
6
7 import 'dart:async';
8
9 import 'package:analysis_server/src/protocol.dart' hide Element;
10 import 'package:analysis_server/src/services/correction/source_range.dart';
11 import 'package:analysis_server/src/services/correction/status.dart';
12 import 'package:analysis_server/src/services/correction/util.dart';
13 import 'package:analysis_server/src/services/refactoring/refactoring.dart';
14 import 'package:analysis_server/src/services/refactoring/refactoring_internal.da rt';
15 import 'package:analysis_server/src/services/search/element_visitors.dart';
16 import 'package:analysis_server/src/services/search/hierarchy.dart';
17 import 'package:analysis_server/src/services/search/search_engine.dart';
18 import 'package:analyzer/src/generated/ast.dart';
19 import 'package:analyzer/src/generated/element.dart';
20 import 'package:analyzer/src/generated/scanner.dart';
21 import 'package:analyzer/src/generated/source.dart';
22
23
24 /**
25 * Resolver sets [ParameterElement] for the most cases, but not for setter invoc ation.
Brian Wilkerson 2014/08/31 15:59:56 First, the resolver never sets the parameter eleme
scheglov 2014/08/31 16:43:23 The underlaying problem is that for "f = 0" the As
26 *
27 * Returns the best available [ParameterElement] for which the given [Expression ] is used.
28 */
29 ParameterElement _getBestParameterElement(Expression expr) {
30 // setter invocation
31 if (expr.parent is AssignmentExpression) {
Brian Wilkerson 2014/08/31 15:59:56 Alternatively, assign "expr.parent" to a local var
32 AssignmentExpression assignment = expr.parent as AssignmentExpression;
33 if (identical(assignment.rightHandSide, expr) &&
34 assignment.operator.type == TokenType.EQ) {
35 Expression lhs = assignment.leftHandSide;
36 Element lhsElement = null;
37 if (lhs is Identifier) {
38 lhsElement = lhs.bestElement;
39 }
40 if (lhs is PropertyAccess) {
41 lhsElement = lhs.propertyName.bestElement;
42 }
43 if (lhsElement is PropertyAccessorElement) {
44 List<ParameterElement> parameters = lhsElement.parameters;
45 if (parameters.length != 0) {
46 return parameters[0];
47 }
48 }
49 }
50 }
51 // use resolver
52 return expr.bestParameterElement;
53 }
54
55
56 /**
57 * Returns the [SourceRange] to find conflicting locals in.
58 */
59 SourceRange _getLocalsConflictingRange(AstNode node) {
60 // maybe Block
61 Block block = node.getAncestor((node) => node is Block);
62 if (block != null) {
63 int offset = node.offset;
64 int endOffset = block.end;
65 return rangeStartEnd(offset, endOffset);
66 }
67 // maybe whole executable
68 AstNode executableNode = getEnclosingExecutableNode(node);
69 if (executableNode != null) {
70 return rangeNode(executableNode);
71 }
72 // not a part of a declaration with locals
73 return SourceRange.EMPTY;
74 }
75
76
77 /**
78 * Returns the source which should replace given invocation with given
79 * arguments.
80 */
81 String _getMethodSourceForInvocation(_SourcePart part, CorrectionUtils utils,
82 AstNode contextNode, Expression targetExpression, List<Expression> arguments ) {
83 // prepare edits to replace parameters with arguments
84 List<SourceEdit> edits = <SourceEdit>[];
85 part._parameters.forEach((ParameterElement parameter, occurrences) {
Brian Wilkerson 2014/08/31 15:59:56 Missing type annotation on "occurrences".
scheglov 2014/08/31 16:43:23 Fixed.
86 // prepare argument
87 Expression argument = null;
88 for (Expression arg in arguments) {
89 if (_getBestParameterElement(arg) == parameter) {
90 argument = arg;
91 break;
92 }
93 }
94 if (argument is NamedExpression) {
95 argument = (argument as NamedExpression).expression;
96 }
97 int argumentPrecedence = getExpressionPrecedence(argument);
98 String argumentSource = utils.getNodeText(argument);
99 // replace all occurrences of this parameter
100 for (_ParameterOccurrence occurrence in occurrences) {
101 SourceRange range = occurrence.range;
102 // prepare argument source to apply at this occurrence
103 String occurrenceArgumentSource;
104 if (argumentPrecedence < occurrence.parentPrecedence) {
105 occurrenceArgumentSource = "(${argumentSource})";
106 } else {
107 occurrenceArgumentSource = argumentSource;
108 }
109 // do replace
110 edits.add(new SourceEdit.range(range, occurrenceArgumentSource));
111 }
112 });
113 // replace static field "qualifier" with invocation target
114 part._staticFieldQualifiers.forEach((className, List<SourceRange> ranges) {
115 for (SourceRange range in ranges) {
116 edits.add(new SourceEdit.range(range, className + '.'));
117 }
118 });
119 // replace instance field "qualifier" with invocation target
120 if (targetExpression != null) {
121 String targetSource = utils.getNodeText(targetExpression) + '.';
122 for (SourceRange qualifierRange in part._instanceFieldQualifiers) {
123 edits.add(new SourceEdit.range(qualifierRange, targetSource));
124 }
125 }
126 // prepare edits to replace conflicting variables
127 Set<String> conflictingNames = _getNamesConflictingAt(contextNode);
128 part._variables.forEach((VariableElement variable, ranges) {
129 String originalName = variable.displayName;
130 // prepare unique name
131 String uniqueName;
132 {
133 uniqueName = originalName;
134 int uniqueIndex = 2;
135 while (conflictingNames.contains(uniqueName)) {
136 uniqueName = originalName + uniqueIndex.toString();
137 uniqueIndex++;
138 }
139 }
140 // update references, if name was change
141 if (uniqueName != originalName) {
142 for (SourceRange range in ranges) {
143 edits.add(new SourceEdit.range(range, uniqueName));
144 }
145 }
146 });
147 // prepare source with applied arguments
148 edits.sort((a, b) => b.offset - a.offset);
149 return SourceEdit.applySequence(part._source, edits);
150 }
151
152
153 /**
154 * Returns the names which will shadow or will be shadowed by any declaration
155 * at [node].
156 */
157 Set<String> _getNamesConflictingAt(AstNode node) {
158 Set<String> result = new Set<String>();
159 // local variables and functions
160 {
161 SourceRange localsRange = _getLocalsConflictingRange(node);
162 ExecutableElement enclosingExecutable = getEnclosingExecutableElement(node);
163 if (enclosingExecutable != null) {
164 visitChildren(enclosingExecutable, (element) {
165 if (element is LocalElement) {
166 SourceRange elementRange = element.visibleRange;
167 if (elementRange != null && elementRange.intersects(localsRange)) {
168 result.add(element.displayName);
169 }
170 }
171 return true;
172 });
173 }
174 }
175 // fields
176 {
177 ClassElement enclosingClassElement = getEnclosingClassElement(node);
178 if (enclosingClassElement != null) {
179 Set<ClassElement> elements = new Set<ClassElement>();
180 elements.add(enclosingClassElement);
181 elements.addAll(getSuperClasses(enclosingClassElement));
182 for (ClassElement classElement in elements) {
183 List<Element> classMembers = getChildren(classElement);
184 for (Element classMemberElement in classMembers) {
185 result.add(classMemberElement.displayName);
186 }
187 }
188 }
189 }
190 // done
191 return result;
192 }
193
194
195 /**
196 * [InlineMethodRefactoring] implementation.
197 */
198 class InlineMethodRefactoringImpl extends RefactoringImpl implements
199 InlineMethodRefactoring {
200 final SearchEngine searchEngine;
201 final CompilationUnit unit;
202 final int offset;
203 String file;
204 CorrectionUtils utils;
205 SourceChange change;
206
207 bool deleteSource = false;
208 bool inlineAll = true;
209
210 ExecutableElement _methodElement;
211 String _methodFile;
212 CompilationUnit _methodUnit;
213 CorrectionUtils _methodUtils;
214 AstNode _methodNode;
215 FormalParameterList _methodParameters;
216 FunctionBody _methodBody;
217 Expression _methodExpression;
218 _SourcePart _methodExpressionPart;
219 _SourcePart _methodStatementsPart;
220 List<_ReferenceProcessor> _referenceProcessors = [];
221
222 InlineMethodRefactoringImpl(this.searchEngine, this.unit, this.offset) {
223 file = unit.element.source.fullName;
224 utils = new CorrectionUtils(unit);
225 }
226
227 @override
228 String get refactoringName {
229 if (_methodElement is MethodElement) {
230 return "Inline Method";
231 } else {
232 return "Inline Function";
233 }
234 }
235
236 @override
237 Future<RefactoringStatus> checkFinalConditions() {
238 change = new SourceChange(refactoringName);
239 RefactoringStatus result = new RefactoringStatus();
240 // check for compatibility of "deleteSource" and "inlineAll"
241 if (deleteSource && !inlineAll) {
242 result.addError('All references must be inlined to remove the source.');
243 }
244 // prepare changes
245 for (_ReferenceProcessor processor in _referenceProcessors) {
246 processor._process(result);
247 }
248 // delete method
249 if (deleteSource && inlineAll) {
250 SourceRange methodRange = rangeNode(_methodNode);
251 SourceRange linesRange = _methodUtils.getLinesRange(methodRange);
252 change.addEdit(_methodFile, new SourceEdit.range(linesRange, ""));
253 }
254 // done
255 return new Future.value(result);
256 }
257
258 @override
259 Future<RefactoringStatus> checkInitialConditions() {
260 RefactoringStatus result = new RefactoringStatus();
261 // prepare method information
262 result.addStatus(_prepareMethod());
263 if (result.hasFatalError) {
264 return new Future.value(result);
265 }
266 // maybe operator
267 if (_methodElement.isOperator) {
268 result = new RefactoringStatus.fatal('Cannot inline operator.');
269 return new Future.value(result);
270 }
271 // analyze method body
272 result.addStatus(_prepareMethodParts());
273 // process references
274 return searchEngine.searchReferences(_methodElement).then((references) {
275 _referenceProcessors.clear();
276 for (SearchMatch reference in references) {
277 _ReferenceProcessor processor =
278 new _ReferenceProcessor(this, reference);
279 _referenceProcessors.add(processor);
280 }
281 }).then((_) {
282 return result;
283 });
284 }
285
286 @override
287 Future<SourceChange> createChange() {
288 return new Future.value(change);
289 }
290
291 @override
292 bool requiresPreview() => false;
293
294 _SourcePart _createSourcePart(SourceRange range) {
295 String source = _methodUtils.getRangeText(range);
296 String prefix = getLinesPrefix(source);
297 _SourcePart result = new _SourcePart(range.offset, source, prefix);
298 // remember parameters and variables occurrences
299 _methodUnit.accept(new _VariablesVisitor(_methodElement, range, result));
300 // done
301 return result;
302 }
303
304 /**
305 * Initializes [_methodElement] and related fields.
306 */
307 RefactoringStatus _prepareMethod() {
308 _methodElement = null;
309 _methodParameters = null;
310 _methodBody = null;
311 deleteSource = false;
312 inlineAll = false;
313 // prepare selected SimpleIdentifier
314 AstNode selectedNode = new NodeLocator.con1(offset).searchWithin(unit);
315 if (selectedNode is! SimpleIdentifier) {
316 return new RefactoringStatus.fatal(
317 'Method declaration or reference must be selected to activate this ref actoring.');
318 }
319 SimpleIdentifier selectedIdentifier = selectedNode as SimpleIdentifier;
320 // prepare selected ExecutableElement
321 Element selectedElement = selectedIdentifier.bestElement;
322 if (selectedElement is! ExecutableElement) {
323 return new RefactoringStatus.fatal(
324 'Method declaration or reference must be selected to activate this ref actoring.');
325 }
326 _methodElement = selectedElement as ExecutableElement;
327 _methodFile = _methodElement.source.fullName;
328 _methodUnit = selectedElement.unit;
329 _methodUtils = new CorrectionUtils(_methodUnit);
330 if (selectedElement is MethodElement ||
331 selectedElement is PropertyAccessorElement) {
332 MethodDeclaration methodDeclaration =
333 _methodElement.node as MethodDeclaration;
334 _methodNode = methodDeclaration;
335 _methodParameters = methodDeclaration.parameters;
336 _methodBody = methodDeclaration.body;
337 // prepare mode
338 deleteSource = selectedNode == methodDeclaration.name;
339 inlineAll = deleteSource;
340 }
341 if (selectedElement is FunctionElement) {
342 FunctionDeclaration functionDeclaration =
343 _methodElement.node as FunctionDeclaration;
344 _methodNode = functionDeclaration;
345 _methodParameters = functionDeclaration.functionExpression.parameters;
346 _methodBody = functionDeclaration.functionExpression.body;
347 // prepare mode
348 deleteSource = selectedNode == functionDeclaration.name;
349 inlineAll = deleteSource;
350 }
351 // OK
352 return new RefactoringStatus();
353 }
354
355 /**
356 * Analyze [_methodBody] to fill [_methodExpressionPart] and
357 * [_methodStatementsPart].
358 */
359 RefactoringStatus _prepareMethodParts() {
360 RefactoringStatus result = new RefactoringStatus();
361 if (_methodBody is ExpressionFunctionBody) {
362 ExpressionFunctionBody body = _methodBody as ExpressionFunctionBody;
363 _methodExpression = body.expression;
364 SourceRange methodExpressionRange = rangeNode(_methodExpression);
365 _methodExpressionPart = _createSourcePart(methodExpressionRange);
366 } else if (_methodBody is BlockFunctionBody) {
367 Block body = (_methodBody as BlockFunctionBody).block;
368 List<Statement> statements = body.statements;
369 if (statements.length >= 1) {
370 Statement lastStatement = statements[statements.length - 1];
371 // "return" statement requires special handling
372 if (lastStatement is ReturnStatement) {
373 _methodExpression = lastStatement.expression;
374 SourceRange methodExpressionRange = rangeNode(_methodExpression);
375 _methodExpressionPart = _createSourcePart(methodExpressionRange);
376 // exclude "return" statement from statements
377 statements = statements.sublist(0, statements.length - 1);
378 }
379 // if there are statements, process them
380 if (!statements.isEmpty) {
381 SourceRange statementsRange =
382 _methodUtils.getLinesRangeStatements(statements);
383 _methodStatementsPart = _createSourcePart(statementsRange);
384 }
385 }
386 // check if more than one return
387 body.accept(new _ReturnsValidatorVisitor(result));
388 } else {
389 return new RefactoringStatus.fatal('Cannot inline method without body.');
390 }
391 return result;
392 }
393 }
394
395
396 class _ParameterOccurrence {
397 final int parentPrecedence;
398 final SourceRange range;
399 _ParameterOccurrence(this.parentPrecedence, this.range);
400 }
401
402
403 /**
404 * Processor for single [SearchMatch] reference to [methodElement].
405 */
406 class _ReferenceProcessor {
407 final InlineMethodRefactoringImpl ref;
408
409 String _refFile;
410 CorrectionUtils _refUtils;
411 AstNode _node;
412 SourceRange _refLineRange;
413 String _refPrefix;
414
415 _ReferenceProcessor(this.ref, SearchMatch reference) {
416 // prepare SourceChange to update
417 Element refElement = reference.element;
418 _refFile = refElement.source.fullName;
419 // prepare CorrectionUtils
420 CompilationUnit refUnit = refElement.unit;
421 _refUtils = new CorrectionUtils(refUnit);
422 // prepare node and environment
423 _node = _refUtils.findNode(reference.sourceRange.offset);
424 Statement refStatement = _node.getAncestor((node) => node is Statement);
425 if (refStatement != null) {
426 _refLineRange = _refUtils.getLinesRangeStatements([refStatement]);
427 _refPrefix = _refUtils.getNodePrefix(refStatement);
428 } else {
429 _refLineRange = null;
430 _refPrefix = _refUtils.getLinePrefix(_node.offset);
431 }
432 }
433
434 bool _canInlineBody(AstNode usage) {
435 // no statements, usually just expression
436 if (ref._methodStatementsPart == null) {
437 // empty method, inline as closure
438 if (ref._methodExpressionPart == null) {
439 return false;
440 }
441 // OK, just expression
442 return true;
443 }
444 // analyze point of invocation
445 AstNode parent = usage.parent;
446 AstNode parent2 = parent.parent;
447 // OK, if statement in block
448 if (parent is Statement) {
449 return parent2 is Block;
450 }
451 // maybe assignment, in block
452 if (parent is AssignmentExpression) {
453 AssignmentExpression assignment = parent;
454 // inlining setter
455 if (identical(assignment.leftHandSide, usage)) {
456 return parent2 is Statement && parent2.parent is Block;
457 }
458 // inlining initializer
459 return ref._methodExpressionPart != null;
460 }
461 // maybe value for variable initializer, in block
462 if (ref._methodExpressionPart != null) {
463 if (parent is VariableDeclaration) {
464 if (parent2 is VariableDeclarationList) {
465 AstNode parent3 = parent2.parent;
466 return parent3 is VariableDeclarationStatement &&
467 parent3.parent is Block;
468 }
469 }
470 }
471 // not in block, cannot inline body
472 return false;
473 }
474
475 void _inlineMethodInvocation(RefactoringStatus status, Expression methodUsage,
476 bool cascaded, Expression target, List<Expression> arguments) {
477 // we don't support cascade
478 if (cascaded) {
479 status.addError(
480 'Cannot inline cascade invocation.',
481 new Location.fromNode(methodUsage));
482 }
483 // can we inline method body into "methodUsage" block?
484 if (_canInlineBody(methodUsage)) {
485 // insert non-return statements
486 if (ref._methodStatementsPart != null) {
487 // prepare statements source for invocation
488 String source = _getMethodSourceForInvocation(
489 ref._methodStatementsPart,
490 _refUtils,
491 methodUsage,
492 target,
493 arguments);
494 source = _refUtils.replaceSourceIndent(
495 source,
496 ref._methodStatementsPart._prefix,
497 _refPrefix);
498 // do insert
499 SourceRange range = rangeStartLength(_refLineRange, 0);
500 SourceEdit edit = new SourceEdit.range(range, source);
501 ref.change.addEdit(_refFile, edit);
502 }
503 // replace invocation with return expression
504 if (ref._methodExpressionPart != null) {
505 // prepare expression source for invocation
506 String source = _getMethodSourceForInvocation(
507 ref._methodExpressionPart,
508 _refUtils,
509 methodUsage,
510 target,
511 arguments);
512 if (getExpressionPrecedence(ref._methodExpression) <
513 getExpressionParentPrecedence(methodUsage)) {
514 source = "(${source})";
515 }
516 // do replace
517 SourceRange methodUsageRange = rangeNode(methodUsage);
518 SourceEdit edit = new SourceEdit.range(methodUsageRange, source);
519 ref.change.addEdit(_refFile, edit);
520 } else {
521 SourceEdit edit = new SourceEdit.range(_refLineRange, "");
522 ref.change.addEdit(_refFile, edit);
523 }
524 return;
525 }
526 // inline as closure invocation
527 String source;
528 {
529 source = ref._methodUtils.getRangeText(
530 rangeStartEnd(ref._methodParameters.leftParenthesis, ref._methodNode)) ;
531 String methodPrefix =
532 ref._methodUtils.getLinePrefix(ref._methodNode.offset);
533 source = _refUtils.replaceSourceIndent(source, methodPrefix, _refPrefix);
534 source = source.trim();
535 }
536 // do insert
537 SourceRange range = rangeNode(_node);
538 SourceEdit edit = new SourceEdit.range(range, source);
539 ref.change.addEdit(_refFile, edit);
540 }
541
542 void _process(RefactoringStatus status) {
543 AstNode nodeParent = _node.parent;
544 // may be only single place should be inlined
545 if (!_shouldProcess()) {
546 return;
547 }
548 // may be invocation of inline method
549 if (nodeParent is MethodInvocation) {
550 MethodInvocation invocation = nodeParent;
551 Expression target = invocation.target;
552 List<Expression> arguments = invocation.argumentList.arguments;
553 _inlineMethodInvocation(
554 status,
555 invocation,
556 invocation.isCascaded,
557 target,
558 arguments);
559 } else {
560 // cannot inline reference to method: var v = new A().method;
561 if (ref._methodElement is MethodElement) {
562 status.addFatalError(
563 'Cannot inline class method reference.',
564 new Location.fromNode(_node));
565 return;
566 }
567 // PropertyAccessorElement
568 if (ref._methodElement is PropertyAccessorElement) {
569 Expression target = null;
570 bool cascade = false;
571 if (nodeParent is PrefixedIdentifier) {
572 PrefixedIdentifier propertyAccess = nodeParent;
573 target = propertyAccess.prefix;
574 cascade = false;
575 }
576 if (nodeParent is PropertyAccess) {
577 PropertyAccess propertyAccess = nodeParent;
578 target = propertyAccess.realTarget;
579 cascade = propertyAccess.isCascaded;
580 }
581 // prepare arguments
582 List<Expression> arguments = [];
583 if ((_node as SimpleIdentifier).inSetterContext()) {
584 arguments.add(
585 (nodeParent.parent as AssignmentExpression).rightHandSide);
586 }
587 // inline body
588 _inlineMethodInvocation(
589 status,
590 nodeParent as Expression,
591 cascade,
592 target,
593 arguments);
594 return;
595 }
596 // not invocation, just reference to function
597 String source;
598 {
599 source = ref._methodUtils.getRangeText(
600 rangeStartEnd(ref._methodParameters.leftParenthesis, ref._methodNode ));
601 String methodPrefix =
602 ref._methodUtils.getLinePrefix(ref._methodNode.offset);
603 source =
604 _refUtils.replaceSourceIndent(source, methodPrefix, _refPrefix);
605 source = source.trim();
606 }
607 // do insert
608 SourceRange range = rangeNode(_node);
609 SourceEdit edit = new SourceEdit.range(range, source);
610 ref.change.addEdit(_refFile, edit);
611 }
612 }
613
614 bool _shouldProcess() {
615 if (!ref.inlineAll) {
616 SourceRange parentRange = rangeNode(_node);
617 return parentRange.contains(ref.offset);
618 }
619 return true;
620 }
621 }
622
623 class _ReturnsValidatorVisitor extends RecursiveAstVisitor {
624 final RefactoringStatus result;
625 int _numReturns = 0;
626
627 _ReturnsValidatorVisitor(this.result);
628
629 @override
630 visitReturnStatement(ReturnStatement node) {
631 _numReturns++;
632 if (_numReturns == 2) {
633 result.addError('Ambiguous return value.', new Location.fromNode(node));
634 }
635 }
636 }
637
638 /**
639 * Information about the source of a method being inlined.
640 */
641 class _SourcePart {
642 /**
643 * The base for all [SourceRange]s.
644 */
645 final int _base;
646
647 /**
648 * The source of the method.
649 */
650 final String _source;
651
652 /**
653 * The original prefix of the method.
654 */
655 final String _prefix;
656
657 /**
658 * The occurrences of the method parameters.
659 */
660 Map<ParameterElement, List<_ParameterOccurrence>> _parameters = {};
661
662 /**
663 * The occurrences of the method local variables.
664 */
665 Map<VariableElement, List<SourceRange>> _variables = {};
666
667 /**
668 * The source ranges of the qualifiers in instance field references.
669 * Some of them have length `0`.
670 */
671 List<SourceRange> _instanceFieldQualifiers = [];
672
673 /**
674 * The source ranges of the qualifiers in instance field references.
675 * Some of them have length `0`.
676 */
677 Map<String, List<SourceRange>> _staticFieldQualifiers = {};
678
679 _SourcePart(this._base, this._source, this._prefix);
680
681 void addInstanceFieldQualifier(SourceRange range) {
682 range = rangeFromBase(range, _base);
683 _instanceFieldQualifiers.add(range);
684 }
685
686 void addParameterOccurrence(ParameterElement parameter, SourceRange range,
687 int precedence) {
688 if (parameter != null) {
689 List<_ParameterOccurrence> occurrences = _parameters[parameter];
690 if (occurrences == null) {
691 occurrences = [];
692 _parameters[parameter] = occurrences;
693 }
694 range = rangeFromBase(range, _base);
695 occurrences.add(new _ParameterOccurrence(precedence, range));
696 }
697 }
698
699 void addStaticFieldQualifier(String className, SourceRange range) {
700 List<SourceRange> ranges = _staticFieldQualifiers[className];
701 if (ranges == null) {
702 ranges = [];
703 _staticFieldQualifiers[className] = ranges;
704 }
705 range = rangeFromBase(range, _base);
706 ranges.add(range);
707 }
708
709 void addVariable(VariableElement element, SourceRange range) {
710 List<SourceRange> ranges = _variables[element];
711 if (ranges == null) {
712 ranges = [];
713 _variables[element] = ranges;
714 }
715 range = rangeFromBase(range, _base);
716 ranges.add(range);
717 }
718 }
719
720 /**
721 * A visitor that fills [_SourcePart] with fields, parameters and variables.
722 */
723 class _VariablesVisitor extends GeneralizingAstVisitor {
724 /**
725 * The [ExecutableElement] being inlined.
726 */
727 final ExecutableElement methodElement;
728
729 /**
730 * The [SourceRange] of the element body.
731 */
732 SourceRange bodyRange;
733
734 /**
735 * The [_SourcePart] to record reference into.
736 */
737 _SourcePart result;
738
739 _VariablesVisitor(this.methodElement, this.bodyRange, this.result);
740
741 @override
742 visitNode(AstNode node) {
743 SourceRange nodeRange = rangeNode(node);
744 if (!bodyRange.intersects(nodeRange)) {
745 return null;
746 }
747 super.visitNode(node);
748 }
749
750 @override
751 visitSimpleIdentifier(SimpleIdentifier node) {
752 SourceRange nodeRange = rangeNode(node);
753 if (bodyRange.covers(nodeRange)) {
754 _addInstanceFieldQualifier(node);
755 _addParameter(node);
756 _addVariable(node);
757 }
758 }
759
760 void _addInstanceFieldQualifier(SimpleIdentifier node) {
761 PropertyAccessorElement accessor = getPropertyAccessorElement(node);
762 if (isFieldAccessorElement(accessor)) {
763 AstNode qualifier = getNodeQualifier(node);
764 if (qualifier == null || qualifier is ThisExpression) {
765 if (accessor.isStatic) {
766 String className = accessor.enclosingElement.displayName;
767 if (qualifier == null) {
768 SourceRange qualifierRange = rangeStartLength(node, 0);
769 result.addStaticFieldQualifier(className, qualifierRange);
770 }
771 } else {
772 SourceRange qualifierRange;
773 if (qualifier != null) {
774 qualifierRange = rangeStartStart(qualifier, node);
775 } else {
776 qualifierRange = rangeStartLength(node, 0);
777 }
778 result.addInstanceFieldQualifier(qualifierRange);
779 }
780 }
781 }
782 }
783
784 void _addParameter(SimpleIdentifier node) {
785 ParameterElement parameterElement = getParameterElement(node);
786 // not a parameter
787 if (parameterElement == null) {
788 return;
789 }
790 // not a parameter of the function being inlined
791 if (!methodElement.parameters.contains(parameterElement)) {
792 return;
793 }
794 // OK, add occurrence
795 SourceRange nodeRange = rangeNode(node);
796 int parentPrecedence = getExpressionParentPrecedence(node);
797 result.addParameterOccurrence(
798 parameterElement,
799 nodeRange,
800 parentPrecedence);
801 }
802
803 void _addVariable(SimpleIdentifier node) {
804 VariableElement variableElement = getLocalVariableElement(node);
805 if (variableElement != null) {
806 SourceRange nodeRange = rangeNode(node);
807 result.addVariable(variableElement, nodeRange);
808 }
809 }
810 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698