|
// 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);
}
}
}
}
}
}
|