@@ -71,7 +71,7 @@ protected virtual bool IsReadOutside(ISymbol symbol, HashSet<ISymbol> readOutsid
7171 public AnalyzerResult Analyze ( )
7272 {
7373 // do data flow analysis
74- var model = this . SemanticDocument . SemanticModel ;
74+ var model = this . SemanticModel ;
7575 var dataFlowAnalysisData = this . SelectionResult . GetDataFlowAnalysis ( ) ;
7676
7777 // build symbol map for the identifiers used inside of the selection
@@ -127,8 +127,6 @@ public AnalyzerResult Analyze()
127127
128128 var ( variables , returnType , returnsByRef ) = GetSignatureInformation ( variableInfoMap ) ;
129129
130- ( returnType , var awaitTaskReturn ) = AdjustReturnType ( returnType ) ;
131-
132130 // collect method type variable used in selected code
133131 var sortedMap = new SortedDictionary < int , ITypeParameterSymbol > ( ) ;
134132 var typeParametersInConstraintList = GetMethodTypeParametersInConstraintList ( variableInfoMap , symbolMap , sortedMap ) ;
@@ -144,69 +142,12 @@ public AnalyzerResult Analyze()
144142 variables ,
145143 returnType ,
146144 returnsByRef ,
147- awaitTaskReturn ,
148145 instanceMemberIsUsed ,
149146 shouldBeReadOnly ,
150147 endOfSelectionReachable ,
151148 operationStatus ) ;
152149 }
153150
154- private ( ITypeSymbol typeSymbol , bool awaitTaskReturn ) AdjustReturnType ( ITypeSymbol returnType )
155- {
156- // if selection contains await which is not under async lambda or anonymous delegate,
157- // change return type to be wrapped in Task
158- var shouldPutAsyncModifier = SelectionResult . CreateAsyncMethod ( ) ;
159- if ( shouldPutAsyncModifier )
160- return WrapReturnTypeInTask ( returnType ) ;
161-
162- // unwrap task if needed
163- return ( UnwrapTaskIfNeeded ( returnType ) , awaitTaskReturn : false ) ;
164- }
165-
166- private ITypeSymbol UnwrapTaskIfNeeded ( ITypeSymbol returnType )
167- {
168- // nothing to unwrap
169- if ( SelectionResult . ContainingScopeHasAsyncKeyword ( ) &&
170- ContainsReturnStatementInSelectedCode ( ) )
171- {
172- var originalDefinition = returnType . OriginalDefinition ;
173-
174- // see whether it needs to be unwrapped
175- var model = this . SemanticDocument . SemanticModel ;
176- var taskType = model . Compilation . TaskType ( ) ;
177- if ( originalDefinition . Equals ( taskType ) )
178- return model . Compilation . GetSpecialType ( SpecialType . System_Void ) ;
179-
180- var genericTaskType = model . Compilation . TaskOfTType ( ) ;
181- if ( originalDefinition . Equals ( genericTaskType ) )
182- return ( ( INamedTypeSymbol ) returnType ) . TypeArguments [ 0 ] ;
183- }
184-
185- // nothing to unwrap
186- return returnType ;
187- }
188-
189- private ( ITypeSymbol returnType , bool awaitTaskReturn ) WrapReturnTypeInTask ( ITypeSymbol returnType )
190- {
191- var compilation = this . SemanticModel . Compilation ;
192- var taskType = compilation . TaskType ( ) ;
193-
194- // convert void to Task type
195- if ( taskType is object && returnType . Equals ( compilation . GetSpecialType ( SpecialType . System_Void ) ) )
196- return ( taskType , awaitTaskReturn : true ) ;
197-
198- if ( ! SelectionResult . IsExtractMethodOnExpression && ContainsReturnStatementInSelectedCode ( ) )
199- return ( returnType , awaitTaskReturn : false ) ;
200-
201- var genericTaskType = compilation . TaskOfTType ( ) ;
202-
203- // okay, wrap the return type in Task<T>
204- if ( genericTaskType is object )
205- returnType = genericTaskType . Construct ( returnType ) ;
206-
207- return ( returnType , awaitTaskReturn : false ) ;
208- }
209-
210151 private ( ImmutableArray < VariableInfo > finalOrderedVariableInfos , ITypeSymbol returnType , bool returnsByRef )
211152 GetSignatureInformation ( Dictionary < ISymbol , VariableInfo > symbolMap )
212153 {
@@ -217,7 +158,7 @@ private ITypeSymbol UnwrapTaskIfNeeded(ITypeSymbol returnType)
217158 // check whether current selection contains return statement
218159 var ( returnType , returnsByRef ) = SelectionResult . GetReturnTypeInfo ( this . CancellationToken ) ;
219160
220- return ( allVariableInfos , returnType , returnsByRef ) ;
161+ return ( allVariableInfos , UnwrapTaskIfNeeded ( returnType ) , returnsByRef ) ;
221162 }
222163 else
223164 {
@@ -230,9 +171,34 @@ private ITypeSymbol UnwrapTaskIfNeeded(ITypeSymbol returnType)
230171 return ( finalOrderedVariableInfos , returnType , returnsByRef : false ) ;
231172 }
232173
174+ ITypeSymbol UnwrapTaskIfNeeded ( ITypeSymbol returnType )
175+ {
176+ if ( this . SelectionResult . ContainingScopeHasAsyncKeyword ( ) )
177+ {
178+ // We compute the desired return type for the extract method from our own return type. But for
179+ // the purposes of manipulating the return type, we need to get to the underlying type if this
180+ // was wrapped in a Task in an explicitly 'async' method. In other words, if we're in an `async
181+ // Task<int>` method, then we want the extract method to return `int`. Note: we will possibly
182+ // then wrap that as `Task<int>` again if we see that we extracted out any await-expressions.
183+
184+ var compilation = this . SemanticModel . Compilation ;
185+ var knownTaskTypes = new KnownTaskTypes ( compilation ) ;
186+
187+ // Map from `Task/ValueTask` to `void`
188+ if ( returnType . Equals ( knownTaskTypes . TaskType ) || returnType . Equals ( knownTaskTypes . ValueTaskType ) )
189+ return compilation . GetSpecialType ( SpecialType . System_Void ) ;
190+
191+ // Map from `Task<T>/ValueTask<T>` to `T`
192+ if ( returnType . OriginalDefinition . Equals ( knownTaskTypes . TaskOfTType ) || returnType . OriginalDefinition . Equals ( knownTaskTypes . ValueTaskOfTType ) )
193+ return returnType . GetTypeArguments ( ) . Single ( ) ;
194+ }
195+
196+ return returnType ;
197+ }
198+
233199 ITypeSymbol GetReturnType ( ImmutableArray < VariableInfo > variablesToUseAsReturnValue )
234200 {
235- var compilation = this . SemanticDocument . SemanticModel . Compilation ;
201+ var compilation = this . SemanticModel . Compilation ;
236202
237203 if ( variablesToUseAsReturnValue . IsEmpty )
238204 return compilation . GetSpecialType ( SpecialType . System_Void ) ;
@@ -289,19 +255,21 @@ private OperationStatus GetOperationStatus(
289255 ? OperationStatus . LocalFunctionCallWithoutDeclaration
290256 : OperationStatus . SucceededStatus ;
291257
292- return readonlyFieldStatus . With ( anonymousTypeStatus )
293- . With ( unsafeAddressStatus )
294- . With ( asyncRefOutParameterStatus )
295- . With ( variableMapStatus )
296- . With ( localFunctionStatus ) ;
258+ return readonlyFieldStatus
259+ . With ( anonymousTypeStatus )
260+ . With ( unsafeAddressStatus )
261+ . With ( asyncRefOutParameterStatus )
262+ . With ( variableMapStatus )
263+ . With ( localFunctionStatus ) ;
297264 }
298265
299266 private OperationStatus CheckAsyncMethodRefOutParameters ( IList < VariableInfo > parameters )
300267 {
301- if ( SelectionResult . CreateAsyncMethod ( ) )
268+ if ( SelectionResult . ContainsAwaitExpression ( ) )
302269 {
303- var names = parameters . Where ( v => v is { UseAsReturnValue : false , ParameterModifier : ParameterBehavior . Out or ParameterBehavior . Ref } )
304- . Select ( p => p . Name ?? string . Empty ) ;
270+ var names = parameters
271+ . Where ( v => v is { UseAsReturnValue : false , ParameterModifier : ParameterBehavior . Out or ParameterBehavior . Ref } )
272+ . Select ( p => p . Name ?? string . Empty ) ;
305273
306274 if ( names . Any ( ) )
307275 return new OperationStatus ( succeeded : true , string . Format ( FeaturesResources . Asynchronous_method_cannot_have_ref_out_parameters_colon_bracket_0_bracket , string . Join ( ", " , names ) ) ) ;
@@ -345,7 +313,7 @@ private ImmutableArray<VariableInfo> MarkVariableInfosToUseAsReturnValueIfPossib
345313 // return values of the method since we can't actually have out/ref with an async method.
346314 var outRefCount = numberOfOutParameters + numberOfRefParameters ;
347315 if ( outRefCount > 0 &&
348- this . SelectionResult . CreateAsyncMethod ( ) &&
316+ this . SelectionResult . ContainsAwaitExpression ( ) &&
349317 this . SyntaxFacts . SupportsTupleDeconstruction ( this . SemanticDocument . Document . Project . ParseOptions ! ) )
350318 {
351319 var result = new FixedSizeArrayBuilder < VariableInfo > ( variableInfo . Length ) ;
0 commit comments