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

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

Issue 1055183004: Add await/async when a method with 'await' in its body is extracted. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 5 years, 8 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
« no previous file with comments | « no previous file | pkg/analysis_server/test/services/refactoring/extract_method_test.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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.extract_method; 5 library services.src.refactoring.extract_method;
6 6
7 import 'dart:async'; 7 import 'dart:async';
8 8
9 import 'package:analysis_server/src/protocol_server.dart' hide Element; 9 import 'package:analysis_server/src/protocol_server.dart' hide Element;
10 import 'package:analysis_server/src/services/correction/name_suggestion.dart'; 10 import 'package:analysis_server/src/services/correction/name_suggestion.dart';
11 import 'package:analysis_server/src/services/correction/selection_analyzer.dart' ; 11 import 'package:analysis_server/src/services/correction/selection_analyzer.dart' ;
12 import 'package:analysis_server/src/services/correction/source_range.dart'; 12 import 'package:analysis_server/src/services/correction/source_range.dart';
13 import 'package:analysis_server/src/services/correction/statement_analyzer.dart' ; 13 import 'package:analysis_server/src/services/correction/statement_analyzer.dart' ;
14 import 'package:analysis_server/src/services/correction/status.dart'; 14 import 'package:analysis_server/src/services/correction/status.dart';
15 import 'package:analysis_server/src/services/correction/util.dart'; 15 import 'package:analysis_server/src/services/correction/util.dart';
16 import 'package:analysis_server/src/services/refactoring/naming_conventions.dart '; 16 import 'package:analysis_server/src/services/refactoring/naming_conventions.dart ';
17 import 'package:analysis_server/src/services/refactoring/refactoring.dart'; 17 import 'package:analysis_server/src/services/refactoring/refactoring.dart';
18 import 'package:analysis_server/src/services/refactoring/refactoring_internal.da rt'; 18 import 'package:analysis_server/src/services/refactoring/refactoring_internal.da rt';
19 import 'package:analysis_server/src/services/refactoring/rename_class_member.dar t'; 19 import 'package:analysis_server/src/services/refactoring/rename_class_member.dar t';
20 import 'package:analysis_server/src/services/refactoring/rename_unit_member.dart '; 20 import 'package:analysis_server/src/services/refactoring/rename_unit_member.dart ';
21 import 'package:analysis_server/src/services/search/element_visitors.dart'; 21 import 'package:analysis_server/src/services/search/element_visitors.dart';
22 import 'package:analysis_server/src/services/search/search_engine.dart'; 22 import 'package:analysis_server/src/services/search/search_engine.dart';
23 import 'package:analyzer/src/generated/ast.dart'; 23 import 'package:analyzer/src/generated/ast.dart';
24 import 'package:analyzer/src/generated/element.dart'; 24 import 'package:analyzer/src/generated/element.dart';
25 import 'package:analyzer/src/generated/engine.dart';
25 import 'package:analyzer/src/generated/java_core.dart'; 26 import 'package:analyzer/src/generated/java_core.dart';
26 import 'package:analyzer/src/generated/resolver.dart' show ExitDetector; 27 import 'package:analyzer/src/generated/resolver.dart' show ExitDetector;
27 import 'package:analyzer/src/generated/scanner.dart'; 28 import 'package:analyzer/src/generated/scanner.dart';
28 import 'package:analyzer/src/generated/source.dart'; 29 import 'package:analyzer/src/generated/source.dart';
29 30
30 const String _TOKEN_SEPARATOR = '\uFFFF'; 31 const String _TOKEN_SEPARATOR = '\uFFFF';
31 32
32 /** 33 /**
33 * Returns the "normalized" version of the given source, which is reconstructed 34 * Returns the "normalized" version of the given source, which is reconstructed
34 * from tokens, so ignores all the comments and spaces. 35 * from tokens, so ignores all the comments and spaces.
(...skipping 20 matching lines...) Expand all
55 class ExtractMethodRefactoringImpl extends RefactoringImpl 56 class ExtractMethodRefactoringImpl extends RefactoringImpl
56 implements ExtractMethodRefactoring { 57 implements ExtractMethodRefactoring {
57 static const ERROR_EXITS = 58 static const ERROR_EXITS =
58 'Selected statements contain a return statement, but not all possible ' 59 'Selected statements contain a return statement, but not all possible '
59 'execuion flows exit. Semantics may not be preserved.'; 60 'execuion flows exit. Semantics may not be preserved.';
60 61
61 final SearchEngine searchEngine; 62 final SearchEngine searchEngine;
62 final CompilationUnit unit; 63 final CompilationUnit unit;
63 final int selectionOffset; 64 final int selectionOffset;
64 final int selectionLength; 65 final int selectionLength;
66 AnalysisContext context;
65 CompilationUnitElement unitElement; 67 CompilationUnitElement unitElement;
66 LibraryElement libraryElement; 68 LibraryElement libraryElement;
67 SourceRange selectionRange; 69 SourceRange selectionRange;
68 CorrectionUtils utils; 70 CorrectionUtils utils;
69 Set<LibraryElement> librariesToImport = new Set<LibraryElement>(); 71 Set<LibraryElement> librariesToImport = new Set<LibraryElement>();
70 72
71 String returnType; 73 String returnType;
74 String variableType;
72 String name; 75 String name;
73 bool extractAll = true; 76 bool extractAll = true;
74 bool canCreateGetter = false; 77 bool canCreateGetter = false;
75 bool createGetter = false; 78 bool createGetter = false;
76 final List<String> names = <String>[]; 79 final List<String> names = <String>[];
77 final List<int> offsets = <int>[]; 80 final List<int> offsets = <int>[];
78 final List<int> lengths = <int>[]; 81 final List<int> lengths = <int>[];
79 82
80 /** 83 /**
81 * The map of local names to their visibility ranges. 84 * The map of local names to their visibility ranges.
82 */ 85 */
83 Map<String, List<SourceRange>> _localNames = <String, List<SourceRange>>{}; 86 Map<String, List<SourceRange>> _localNames = <String, List<SourceRange>>{};
84 87
85 /** 88 /**
86 * The set of names that are referenced without any qualifier. 89 * The set of names that are referenced without any qualifier.
87 */ 90 */
88 Set<String> _unqualifiedNames = new Set<String>(); 91 Set<String> _unqualifiedNames = new Set<String>();
89 92
90 Set<String> _excludedNames = new Set<String>(); 93 Set<String> _excludedNames = new Set<String>();
91 List<RefactoringMethodParameter> _parameters = <RefactoringMethodParameter>[]; 94 List<RefactoringMethodParameter> _parameters = <RefactoringMethodParameter>[];
92 Map<String, RefactoringMethodParameter> _parametersMap = 95 Map<String, RefactoringMethodParameter> _parametersMap =
93 <String, RefactoringMethodParameter>{}; 96 <String, RefactoringMethodParameter>{};
94 Map<String, List<SourceRange>> _parameterReferencesMap = 97 Map<String, List<SourceRange>> _parameterReferencesMap =
95 <String, List<SourceRange>>{}; 98 <String, List<SourceRange>>{};
99 bool _hasAwait = false;
96 DartType _returnType; 100 DartType _returnType;
97 String _returnVariableName; 101 String _returnVariableName;
98 AstNode _parentMember; 102 AstNode _parentMember;
99 Expression _selectionExpression; 103 Expression _selectionExpression;
100 FunctionExpression _selectionFunctionExpression; 104 FunctionExpression _selectionFunctionExpression;
101 List<Statement> _selectionStatements; 105 List<Statement> _selectionStatements;
102 List<_Occurrence> _occurrences = []; 106 List<_Occurrence> _occurrences = [];
103 bool _staticContext = false; 107 bool _staticContext = false;
104 108
105 ExtractMethodRefactoringImpl(this.searchEngine, this.unit, 109 ExtractMethodRefactoringImpl(this.searchEngine, this.unit,
106 this.selectionOffset, this.selectionLength) { 110 this.selectionOffset, this.selectionLength) {
107 unitElement = unit.element; 111 unitElement = unit.element;
108 libraryElement = unitElement.library; 112 libraryElement = unitElement.library;
113 context = libraryElement.context;
109 selectionRange = new SourceRange(selectionOffset, selectionLength); 114 selectionRange = new SourceRange(selectionOffset, selectionLength);
110 utils = new CorrectionUtils(unit); 115 utils = new CorrectionUtils(unit);
111 } 116 }
112 117
113 @override 118 @override
114 List<RefactoringMethodParameter> get parameters => _parameters; 119 List<RefactoringMethodParameter> get parameters => _parameters;
115 120
116 @override 121 @override
117 void set parameters(List<RefactoringMethodParameter> parameters) { 122 void set parameters(List<RefactoringMethodParameter> parameters) {
118 _parameters = parameters.toList(); 123 _parameters = parameters.toList();
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after
175 @override 180 @override
176 Future<RefactoringStatus> checkInitialConditions() { 181 Future<RefactoringStatus> checkInitialConditions() {
177 RefactoringStatus result = new RefactoringStatus(); 182 RefactoringStatus result = new RefactoringStatus();
178 // selection 183 // selection
179 result.addStatus(_checkSelection()); 184 result.addStatus(_checkSelection());
180 if (result.hasFatalError) { 185 if (result.hasFatalError) {
181 return new Future.value(result); 186 return new Future.value(result);
182 } 187 }
183 // prepare parts 188 // prepare parts
184 result.addStatus(_initializeParameters()); 189 result.addStatus(_initializeParameters());
190 _initializeHasAwait();
185 _initializeReturnType(); 191 _initializeReturnType();
186 // occurrences 192 // occurrences
187 _initializeOccurrences(); 193 _initializeOccurrences();
188 _prepareOffsetsLengths(); 194 _prepareOffsetsLengths();
189 // getter 195 // getter
190 canCreateGetter = _computeCanCreateGetter(); 196 canCreateGetter = _computeCanCreateGetter();
191 createGetter = 197 createGetter =
192 canCreateGetter && _isExpressionForGetter(_selectionExpression); 198 canCreateGetter && _isExpressionForGetter(_selectionExpression);
193 // names 199 // names
194 _prepareExcludedNames(); 200 _prepareExcludedNames();
(...skipping 24 matching lines...) Expand all
219 if (!extractAll && !occurence.isSelection) { 225 if (!extractAll && !occurence.isSelection) {
220 continue; 226 continue;
221 } 227 }
222 // prepare invocation source 228 // prepare invocation source
223 String invocationSource; 229 String invocationSource;
224 if (_selectionFunctionExpression != null) { 230 if (_selectionFunctionExpression != null) {
225 invocationSource = name; 231 invocationSource = name;
226 } else { 232 } else {
227 StringBuffer sb = new StringBuffer(); 233 StringBuffer sb = new StringBuffer();
228 // may be returns value 234 // may be returns value
229 if (_selectionStatements != null && returnType != 'void') { 235 if (_selectionStatements != null && variableType != null) {
230 // single variable assignment / return statement 236 // single variable assignment / return statement
231 if (_returnVariableName != null) { 237 if (_returnVariableName != null) {
232 String occurrenceName = 238 String occurrenceName =
233 occurence._parameterOldToOccurrenceName[_returnVariableName]; 239 occurence._parameterOldToOccurrenceName[_returnVariableName];
234 // may be declare variable 240 // may be declare variable
235 if (!_parametersMap.containsKey(_returnVariableName)) { 241 if (!_parametersMap.containsKey(_returnVariableName)) {
236 if (returnType.isEmpty) { 242 if (variableType.isEmpty) {
237 sb.write('var '); 243 sb.write('var ');
238 } else { 244 } else {
239 sb.write(returnType); 245 sb.write(variableType);
240 sb.write(' '); 246 sb.write(' ');
241 } 247 }
242 } 248 }
243 // assign the return value 249 // assign the return value
244 sb.write(occurrenceName); 250 sb.write(occurrenceName);
245 sb.write(' = '); 251 sb.write(' = ');
246 } else { 252 } else {
247 sb.write('return '); 253 sb.write('return ');
248 } 254 }
249 } 255 }
256 // await
257 if (_hasAwait) {
258 sb.write('await ');
259 }
250 // invocation itself 260 // invocation itself
251 sb.write(name); 261 sb.write(name);
252 if (!createGetter) { 262 if (!createGetter) {
253 sb.write('('); 263 sb.write('(');
254 bool firstParameter = true; 264 bool firstParameter = true;
255 for (RefactoringMethodParameter parameter in _parameters) { 265 for (RefactoringMethodParameter parameter in _parameters) {
256 // may be comma 266 // may be comma
257 if (firstParameter) { 267 if (firstParameter) {
258 firstParameter = false; 268 firstParameter = false;
259 } else { 269 } else {
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after
295 String declarationSource = null; 305 String declarationSource = null;
296 { 306 {
297 String returnExpressionSource = _getMethodBodySource(); 307 String returnExpressionSource = _getMethodBodySource();
298 // closure 308 // closure
299 if (_selectionFunctionExpression != null) { 309 if (_selectionFunctionExpression != null) {
300 declarationSource = '${name}${returnExpressionSource}'; 310 declarationSource = '${name}${returnExpressionSource}';
301 if (_selectionFunctionExpression.body is ExpressionFunctionBody) { 311 if (_selectionFunctionExpression.body is ExpressionFunctionBody) {
302 declarationSource += ';'; 312 declarationSource += ';';
303 } 313 }
304 } 314 }
315 // optional 'async' body modifier
316 String asyncKeyword = _hasAwait ? ' async' : '';
305 // expression 317 // expression
306 if (_selectionExpression != null) { 318 if (_selectionExpression != null) {
307 // add return type 319 // add return type
308 if (returnType.isNotEmpty) { 320 if (returnType.isNotEmpty) {
309 annotations += '$returnType '; 321 annotations += '$returnType ';
310 } 322 }
311 // just return expression 323 // just return expression
312 declarationSource = 324 declarationSource = '$annotations$signature$asyncKeyword => ';
313 '${annotations}${signature} => ${returnExpressionSource};'; 325 declarationSource += '$returnExpressionSource;';
314 } 326 }
315 // statements 327 // statements
316 if (_selectionStatements != null) { 328 if (_selectionStatements != null) {
317 if (returnType.isNotEmpty) { 329 if (returnType.isNotEmpty) {
318 annotations += returnType + ' '; 330 annotations += returnType + ' ';
319 } 331 }
320 declarationSource = '${annotations}${signature} {${eol}'; 332 declarationSource = '$annotations$signature$asyncKeyword {$eol';
321 declarationSource += returnExpressionSource; 333 declarationSource += returnExpressionSource;
322 if (_returnVariableName != null) { 334 if (_returnVariableName != null) {
323 declarationSource += 335 declarationSource +=
324 '${prefix} return ${_returnVariableName};$eol'; 336 '${prefix} return ${_returnVariableName};$eol';
325 } 337 }
326 declarationSource += '${prefix}}'; 338 declarationSource += '${prefix}}';
327 } 339 }
328 } 340 }
329 // insert declaration 341 // insert declaration
330 if (declarationSource != null) { 342 if (declarationSource != null) {
(...skipping 194 matching lines...) Expand 10 before | Expand all | Expand 10 after
525 replaceEdits = replaceEdits.reversed.toList(); 537 replaceEdits = replaceEdits.reversed.toList();
526 String source = SourceEdit.applySequence(originalSource, replaceEdits); 538 String source = SourceEdit.applySequence(originalSource, replaceEdits);
527 pattern.normalizedSource = _getNormalizedSource(source); 539 pattern.normalizedSource = _getNormalizedSource(source);
528 return pattern; 540 return pattern;
529 } 541 }
530 542
531 String _getTypeCode(DartType type) { 543 String _getTypeCode(DartType type) {
532 return utils.getTypeSource(type, librariesToImport); 544 return utils.getTypeSource(type, librariesToImport);
533 } 545 }
534 546
547 void _initializeHasAwait() {
548 _HasAwaitVisitor visitor = new _HasAwaitVisitor();
549 if (_selectionExpression != null) {
550 _selectionExpression.accept(visitor);
551 } else if (_selectionStatements != null) {
552 _selectionStatements.forEach((statement) => statement.accept(visitor));
553 }
554 _hasAwait = visitor.result;
555 }
556
535 /** 557 /**
536 * Fills [_occurrences] field. 558 * Fills [_occurrences] field.
537 */ 559 */
538 void _initializeOccurrences() { 560 void _initializeOccurrences() {
539 _occurrences.clear(); 561 _occurrences.clear();
540 // prepare selection 562 // prepare selection
541 _SourcePattern selectionPattern = _getSourcePattern(selectionRange); 563 _SourcePattern selectionPattern = _getSourcePattern(selectionRange);
542 Map<String, String> patternToSelectionName = 564 Map<String, String> patternToSelectionName =
543 _inverseMap(selectionPattern.originalToPatternNames); 565 _inverseMap(selectionPattern.originalToPatternNames);
544 // prepare an enclosing parent - class or unit 566 // prepare an enclosing parent - class or unit
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after
602 result.addFatalError(format( 624 result.addFatalError(format(
603 'Ambiguous return value: Selected block contains more than one ' 625 'Ambiguous return value: Selected block contains more than one '
604 'assignment to local variables. Affected variables are:\n\n{0}', 626 'assignment to local variables. Affected variables are:\n\n{0}',
605 sb.toString().trim())); 627 sb.toString().trim()));
606 } 628 }
607 // done 629 // done
608 return result; 630 return result;
609 } 631 }
610 632
611 void _initializeReturnType() { 633 void _initializeReturnType() {
634 InterfaceType futureType = context.typeProvider.futureType;
612 if (_selectionFunctionExpression != null) { 635 if (_selectionFunctionExpression != null) {
636 variableType = '';
613 returnType = ''; 637 returnType = '';
614 } else if (_returnType == null) { 638 } else if (_returnType == null) {
615 returnType = 'void'; 639 variableType = null;
640 if (_hasAwait) {
641 returnType = _getTypeCode(futureType);
642 } else {
643 returnType = 'void';
644 }
645 } else if (_returnType.isDynamic) {
646 variableType = '';
647 if (_hasAwait) {
648 returnType = _getTypeCode(futureType);
649 } else {
650 returnType = '';
651 }
616 } else { 652 } else {
617 returnType = _getTypeCode(_returnType); 653 variableType = _getTypeCode(_returnType);
618 } 654 if (_hasAwait) {
619 if (returnType == 'dynamic') { 655 if (_returnType.element != futureType.element) {
620 returnType = ''; 656 returnType = _getTypeCode(futureType.substitute4([_returnType]));
657 }
658 } else {
659 returnType = variableType;
660 }
621 } 661 }
622 } 662 }
623 663
624 /** 664 /**
625 * Checks if the given [VariableElement] is declared in [selectionRange]. 665 * Checks if the given [VariableElement] is declared in [selectionRange].
626 */ 666 */
627 bool _isDeclaredInSelection(VariableElement element) { 667 bool _isDeclaredInSelection(VariableElement element) {
628 return selectionRange.contains(element.nameOffset); 668 return selectionRange.contains(element.nameOffset);
629 } 669 }
630 670
(...skipping 246 matching lines...) Expand 10 before | Expand all | Expand 10 after
877 patternName = '__refVar${pattern.originalToPatternNames.length}'; 917 patternName = '__refVar${pattern.originalToPatternNames.length}';
878 pattern.originalToPatternNames[originalName] = patternName; 918 pattern.originalToPatternNames[originalName] = patternName;
879 } 919 }
880 replaceEdits.add(new SourceEdit(nodeRange.offset - partRange.offset, 920 replaceEdits.add(new SourceEdit(nodeRange.offset - partRange.offset,
881 nodeRange.length, patternName)); 921 nodeRange.length, patternName));
882 } 922 }
883 } 923 }
884 } 924 }
885 } 925 }
886 926
927 class _HasAwaitVisitor extends GeneralizingAstVisitor {
928 bool result = false;
929
930 @override
931 visitAwaitExpression(AwaitExpression node) {
932 result = true;
933 }
934
935 @override
936 visitForEachStatement(ForEachStatement node) {
937 if (node.awaitKeyword != null) {
938 result = true;
939 }
940 super.visitForEachStatement(node);
941 }
942 }
943
887 class _HasReturnStatementVisitor extends RecursiveAstVisitor { 944 class _HasReturnStatementVisitor extends RecursiveAstVisitor {
888 bool hasReturn = false; 945 bool hasReturn = false;
889 946
890 @override 947 @override
891 visitBlockFunctionBody(BlockFunctionBody node) {} 948 visitBlockFunctionBody(BlockFunctionBody node) {}
892 949
893 @override 950 @override
894 visitReturnStatement(ReturnStatement node) { 951 visitReturnStatement(ReturnStatement node) {
895 hasReturn = true; 952 hasReturn = true;
896 } 953 }
(...skipping 247 matching lines...) Expand 10 before | Expand all | Expand 10 after
1144 return false; 1201 return false;
1145 } 1202 }
1146 for (int i = 0; i < parameterTypes.length; i++) { 1203 for (int i = 0; i < parameterTypes.length; i++) {
1147 if (other.parameterTypes[i] != parameterTypes[i]) { 1204 if (other.parameterTypes[i] != parameterTypes[i]) {
1148 return false; 1205 return false;
1149 } 1206 }
1150 } 1207 }
1151 return true; 1208 return true;
1152 } 1209 }
1153 } 1210 }
OLDNEW
« no previous file with comments | « no previous file | pkg/analysis_server/test/services/refactoring/extract_method_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698