| File: DataFlow\FeatureChecksVisitor.cs | Web Access |
| Project: src\runtime\src\tools\illink\src\ILLink.RoslynAnalyzer\ILLink.RoslynAnalyzer.csproj (ILLink.RoslynAnalyzer) |
// Copyright (c) .NET Foundation and contributors. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System; using System.Collections.Immutable; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using ILLink.Shared.DataFlow; using ILLink.Shared.TypeSystemProxy; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.FlowAnalysis; using Microsoft.CodeAnalysis.Operations; using StateValue = ILLink.RoslynAnalyzer.DataFlow.LocalDataFlowState< ILLink.Shared.DataFlow.ValueSet<ILLink.Shared.DataFlow.SingleValue>, ILLink.RoslynAnalyzer.DataFlow.FeatureContext, ILLink.Shared.DataFlow.ValueSetLattice<ILLink.Shared.DataFlow.SingleValue>, ILLink.RoslynAnalyzer.DataFlow.FeatureContextLattice >; namespace ILLink.RoslynAnalyzer.DataFlow { // Visits a conditional expression to optionally produce a 'FeatureChecksValue' // (a set features that are checked to be enabled or disabled). // The visitor takes a LocalDataFlowState as an argument, allowing for checks that // depend on the current dataflow state. internal sealed class FeatureChecksVisitor : OperationVisitor<StateValue, FeatureChecksValue> { private DataFlowAnalyzerContext _dataFlowAnalyzerContext; public FeatureChecksVisitor(DataFlowAnalyzerContext dataFlowAnalyzerContext) { _dataFlowAnalyzerContext = dataFlowAnalyzerContext; } public override FeatureChecksValue DefaultVisit(IOperation operation, StateValue state) { // Visiting a non-understood pattern should return the empty set of features, which will // prevent this check from acting as a guard for any feature. return FeatureChecksValue.None; } public override FeatureChecksValue VisitArgument(IArgumentOperation operation, StateValue state) { return Visit(operation.Value, state); } public override FeatureChecksValue VisitPropertyReference(IPropertyReferenceOperation operation, StateValue state) { // A single property may serve as a feature check for multiple features. FeatureChecksValue featureChecks = FeatureChecksValue.None; foreach (var analyzer in _dataFlowAnalyzerContext.EnabledRequiresAnalyzers) { if (analyzer.IsFeatureGuard(operation.Property, _dataFlowAnalyzerContext.Compilation)) { var featureCheck = new FeatureChecksValue(analyzer.RequiresAttributeFullyQualifiedName); featureChecks = featureChecks.And(featureCheck); } } return featureChecks; } public override FeatureChecksValue VisitUnaryOperator(IUnaryOperation operation, StateValue state) { if (operation.OperatorKind is not UnaryOperatorKind.Not) return FeatureChecksValue.None; FeatureChecksValue context = Visit(operation.Operand, state); return context.Negate(); } public override FeatureChecksValue VisitLiteral(ILiteralOperation operation, StateValue state) { // 'false' can guard any feature if (GetConstantBool(operation.ConstantValue) is false) return FeatureChecksValue.All; return FeatureChecksValue.None; } public static bool? GetLiteralBool(IOperation operation) { if (operation is not ILiteralOperation literal) return null; return GetConstantBool(literal.ConstantValue); } private static bool? GetConstantBool(Optional<object?> constantValue) { if (!constantValue.HasValue || constantValue.Value is not bool value) return null; return value; } public override FeatureChecksValue VisitBinaryOperator(IBinaryOperation operation, StateValue state) { bool expectEqual; switch (operation.OperatorKind) { case BinaryOperatorKind.Equals: expectEqual = true; break; case BinaryOperatorKind.NotEquals: expectEqual = false; break; default: return FeatureChecksValue.None; } if (GetLiteralBool(operation.LeftOperand) is bool leftBool) { FeatureChecksValue rightValue = Visit(operation.RightOperand, state); return leftBool == expectEqual ? rightValue : rightValue.Negate(); } if (GetLiteralBool(operation.RightOperand) is bool rightBool) { FeatureChecksValue leftValue = Visit(operation.LeftOperand, state); return rightBool == expectEqual ? leftValue : leftValue.Negate(); } return FeatureChecksValue.None; } public override FeatureChecksValue VisitIsPattern(IIsPatternOperation operation, StateValue state) { if (GetExpectedValueFromPattern(operation.Pattern) is not bool patternValue) return FeatureChecksValue.None; FeatureChecksValue value = Visit(operation.Value, state); return patternValue ? value : value.Negate(); static bool? GetExpectedValueFromPattern(IPatternOperation pattern) { switch (pattern) { case IConstantPatternOperation constantPattern: return GetConstantBool(constantPattern.Value.ConstantValue); case INegatedPatternOperation negatedPattern: return !GetExpectedValueFromPattern(negatedPattern.Pattern); default: return null; } } } } }