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

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

Powered by Google App Engine
This is Rietveld 408576698