|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Collections.Immutable;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Microsoft.CodeAnalysis.CSharp.Syntax;
namespace Microsoft.CodeAnalysis.CSharp.MakeLocalFunctionStatic
{
internal static class MakeLocalFunctionStaticHelper
{
public static bool IsStaticLocalFunctionSupported(LanguageVersion languageVersion)
=> languageVersion >= LanguageVersion.CSharp8;
private static bool TryGetDataFlowAnalysis(LocalFunctionStatementSyntax localFunction, SemanticModel semanticModel, [NotNullWhen(returnValue: true)] out DataFlowAnalysis? dataFlow)
{
dataFlow = semanticModel.AnalyzeDataFlow(localFunction);
return dataFlow is { Succeeded: true };
}
private static bool CanBeCalledFromStaticContext(LocalFunctionStatementSyntax localFunction, DataFlowAnalysis dataFlow)
{
// If other local functions are called the it can't be made static unless the are static, or the local
// function is recursive, or its calling a child local function
return !dataFlow.UsedLocalFunctions.Any(static (usedLocalFunction, localFunctionStatement) =>
!usedLocalFunction.IsStatic && !IsChildOrSelf(localFunctionStatement, usedLocalFunction), localFunction);
static bool IsChildOrSelf(LocalFunctionStatementSyntax containingLocalFunction, ISymbol calledLocationFunction)
{
var node = calledLocationFunction.DeclaringSyntaxReferences.FirstOrDefault()?.GetSyntax();
// Contains also returns true if node is equal to the containing local function
return containingLocalFunction.Contains(node);
}
}
public static bool CanMakeLocalFunctionStaticBecauseNoCaptures(LocalFunctionStatementSyntax localFunction, SemanticModel semanticModel)
=> TryGetDataFlowAnalysis(localFunction, semanticModel, out var dataFlow)
&& CanBeCalledFromStaticContext(localFunction, dataFlow)
&& dataFlow.CapturedInside.IsEmpty;
public static bool CanMakeLocalFunctionStaticByRefactoringCaptures(LocalFunctionStatementSyntax localFunction, SemanticModel semanticModel, out ImmutableArray<ISymbol> captures)
{
if (TryGetDataFlowAnalysis(localFunction, semanticModel, out var dataFlow) &&
CanBeCalledFromStaticContext(localFunction, dataFlow) &&
!dataFlow.CapturedInside.IsEmpty)
{
captures = dataFlow.CapturedInside;
return true;
}
captures = default;
return false;
}
}
}
|