File: TrimAnalysis\FeatureCheckReturnValuePattern.cs
Web Access
Project: src\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.Generic;
using ILLink.Shared;
using ILLink.Shared.DataFlow;
using ILLink.Shared.TrimAnalysis;
using ILLink.RoslynAnalyzer.DataFlow;
using Microsoft.CodeAnalysis;
 
namespace ILLink.RoslynAnalyzer.TrimAnalysis
{
	internal readonly record struct FeatureCheckReturnValuePattern
	{
		public FeatureChecksValue ReturnValue { get; init; }
		public ValueSet<string> FeatureCheckAnnotations { get; init; }
		public IOperation Operation { get; init; }
		public IPropertySymbol OwningSymbol { get; init; }
 
		public FeatureCheckReturnValuePattern (
			FeatureChecksValue returnValue,
			ValueSet<string> featureCheckAnnotations,
			IOperation operation,
			IPropertySymbol owningSymbol)
		{
			ReturnValue = returnValue.DeepCopy ();
			FeatureCheckAnnotations = featureCheckAnnotations.DeepCopy ();
			Operation = operation;
			OwningSymbol = owningSymbol;
		}
 
		public void ReportDiagnostics (DataFlowAnalyzerContext context, Action<Diagnostic> reportDiagnostic)
		{
			var diagnosticContext = new DiagnosticContext (Operation.Syntax.GetLocation (), reportDiagnostic);
			// For now, feature check validation is enabled only when trim analysis is enabled.
			if (!context.EnableTrimAnalyzer)
				return;
 
			if (!OwningSymbol.IsStatic || OwningSymbol.Type.SpecialType != SpecialType.System_Boolean || OwningSymbol.SetMethod != null) {
				// Warn about invalid feature checks (non-static or non-bool properties or properties with setter)
				diagnosticContext.AddDiagnostic (
					DiagnosticId.InvalidFeatureGuard);
				return;
			}
 
			if (ReturnValue == FeatureChecksValue.All)
				return;
 
			ValueSet<string> returnValueFeatures = ReturnValue.EnabledFeatures;
			// For any analyzer-supported feature that this property is declared to guard,
			// the abstract return value must include that feature
			// (indicating it is known to be enabled when the return value is true).
			foreach (string feature in FeatureCheckAnnotations.GetKnownValues ()) {
				foreach (var analyzer in context.EnabledRequiresAnalyzers) {
					if (feature != analyzer.RequiresAttributeFullyQualifiedName)
						continue;
 
					if (!returnValueFeatures.Contains (feature)) {
						diagnosticContext.AddDiagnostic (
							DiagnosticId.ReturnValueDoesNotMatchFeatureGuards,
							OwningSymbol.GetDisplayName (),
							feature);
					}
				}
			}
		}
	}
}