Suppose I have the following code:
async Task M1Async()
{
CancellationToken l1;
await M2Async();
}
async Task M2Async(CancellationToken p1 = default) {}
and I have the ILocalSymbol corresponding to l1. I don't want to fire the analyzer and generate the fix await M2Async(l1); because l1 is uninitialized. How do I check whether l1 is initialized at a particular position in the syntax tree, in this case inside the expression M2Async()?
edit: I also want to handle cases when the CancellationToken is a parameter rather than a local that's uninitialized, e.g:
async Task M1Async(out CancellationToken p1)
{
await M2Async(); // Do not want to offer 'await M2Async(p1);' codefix here
}
Hi James! You can use SemanticModel.AnalyzeDataFlow, passing in the await M2Async(); statement. This will return a DataFlowAnalysis object that contains a lot of interesting and useful information. The one that seems the most relevant to your use case is:
c#
/// <summary>
/// The set of local variables which are assigned a value outside a region
/// that may be used inside the region.
/// </summary>
public abstract ImmutableArray<ISymbol> DataFlowsIn { get; }
If DataFlowsIn contains your local, then that means it has has its value definitely assigned, and you can use it at that location.
@CyrusNajmabadi Thanks for your suggestion. If I just pass in the node for M2Async();, then in this scenario:
var l1 = CancellationToken.None;
await M2Async();
l1 is not contained in DataFlowsIn. Do I need to grab the first statement of the method and call the overload of AnalyzeDataFlow that accepts two args to make this work?
Hrmm... paging @gafter . He knows this API the best.
Note: you may have to do the analysis on the updated tree. Look at all the values in the DataFlowAnalysis object before and after your tree. See if you can use those together to figure it out. If not, we can brainstorm more ideas.
@CyrusNajmabadi There is only one tree we're talking about; I'm running this before the code fixer is triggered.
@gafter Would be awesome if you could help me with this.
There is only one tree we're talking about;
There is no spoon.
I'm running this before the code fixer is triggered.
Roslyn loves forking. :) Seriously, it's completely normal for a fix to fork the SyntaxTree/Compilation/SemanticModel and check something about the data in the fork. Heck, we even have an internal extension to validate long sequences of edits to make sure they're ok: https://github.com/dotnet/roslyn/blob/master/src/Workspaces/Core/Portable/Shared/Extensions/SyntaxEditorExtensions.cs#L82
The reference assembly for CancellationToken contains no fields for that value type. Therefore a local of that type is, technically, always definitely assigned (because all zero of its fields are assigned). I know that is a horrible state of affairs, and we'd prefer that the reference assemblies not do such things, but there is nothing the Roslyn APIs can do to help you while you are using broken reference assemblies.
To verify if you are using a broken reference assembly, try compiling this code
c#
class C
{
void M(out System.Threading.CancellationToken t) { }
}
If that compiles without error, your reference assembly is broken.
Most helpful comment
Hi James! You can use SemanticModel.AnalyzeDataFlow, passing in the
await M2Async();statement. This will return a DataFlowAnalysis object that contains a lot of interesting and useful information. The one that seems the most relevant to your use case is:c# /// <summary> /// The set of local variables which are assigned a value outside a region /// that may be used inside the region. /// </summary> public abstract ImmutableArray<ISymbol> DataFlowsIn { get; }If DataFlowsIn contains your local, then that means it has has its value definitely assigned, and you can use it at that location.