// 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;
using System.Diagnostics;
using Analyzer.Utilities;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.CopyAnalysis;
using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis;
using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ValueContentAnalysis;
namespace Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ParameterValidationAnalysis
{
using CopyAnalysisResult = DataFlowAnalysisResult<CopyBlockAnalysisResult, CopyAbstractValue>;
using InterproceduralParameterValidationAnalysisData = InterproceduralAnalysisData<DictionaryAnalysisData<AbstractLocation, ParameterValidationAbstractValue>, ParameterValidationAnalysisContext, ParameterValidationAbstractValue>;
using ParameterValidationAnalysisData = DictionaryAnalysisData<AbstractLocation, ParameterValidationAbstractValue>;
using ValueContentAnalysisResult = DataFlowAnalysisResult<ValueContentBlockAnalysisResult, ValueContentAbstractValue>;
/// <summary>
/// Analysis context for execution of <see cref="ParameterValidationAnalysis"/> on a control flow graph.
/// </summary>
internal sealed class ParameterValidationAnalysisContext : AbstractDataFlowAnalysisContext<ParameterValidationAnalysisData, ParameterValidationAnalysisContext, ParameterValidationAnalysisResult, ParameterValidationAbstractValue>
{
private ParameterValidationAnalysisContext(
AbstractValueDomain<ParameterValidationAbstractValue> valueDomain,
WellKnownTypeProvider wellKnownTypeProvider,
ControlFlowGraph controlFlowGraph,
ISymbol owningSymbol,
AnalyzerOptions analyzerOptions,
SymbolNamesWithValueOption<Unit> nullCheckValidationMethods,
InterproceduralAnalysisConfiguration interproceduralAnalysisConfig,
bool pessimisticAnalysis,
PointsToAnalysisResult? pointsToAnalysisResult,
Func<ParameterValidationAnalysisContext, ParameterValidationAnalysisResult?> tryGetOrComputeAnalysisResult,
ControlFlowGraph? parentControlFlowGraph,
InterproceduralParameterValidationAnalysisData? interproceduralAnalysisData,
bool trackHazardousParameterUsages)
: base(valueDomain, wellKnownTypeProvider, controlFlowGraph, owningSymbol, analyzerOptions, interproceduralAnalysisConfig,
pessimisticAnalysis, predicateAnalysis: false, exceptionPathsAnalysis: false,
copyAnalysisResult: null, pointsToAnalysisResult, valueContentAnalysisResult: null,
tryGetOrComputeAnalysisResult, parentControlFlowGraph, interproceduralAnalysisData,
interproceduralAnalysisPredicate: null)
{
TrackHazardousParameterUsages = trackHazardousParameterUsages;
NullCheckValidationMethodNames = nullCheckValidationMethods;
}
public static ParameterValidationAnalysisContext Create(
AbstractValueDomain<ParameterValidationAbstractValue> valueDomain,
WellKnownTypeProvider wellKnownTypeProvider,
ControlFlowGraph controlFlowGraph,
ISymbol owningSymbol,
AnalyzerOptions analyzerOptions,
SymbolNamesWithValueOption<Unit> nullCheckValidationMethods,
InterproceduralAnalysisConfiguration interproceduralAnalysisConfig,
bool pessimisticAnalysis,
PointsToAnalysisResult? pointsToAnalysisResult,
Func<ParameterValidationAnalysisContext, ParameterValidationAnalysisResult?> tryGetOrComputeAnalysisResult)
{
return new ParameterValidationAnalysisContext(
valueDomain, wellKnownTypeProvider, controlFlowGraph, owningSymbol,
analyzerOptions, nullCheckValidationMethods, interproceduralAnalysisConfig,
pessimisticAnalysis, pointsToAnalysisResult, tryGetOrComputeAnalysisResult, parentControlFlowGraph: null,
interproceduralAnalysisData: null, trackHazardousParameterUsages: false);
}
public override ParameterValidationAnalysisContext ForkForInterproceduralAnalysis(
IMethodSymbol invokedMethod,
ControlFlowGraph invokedCfg,
PointsToAnalysisResult? pointsToAnalysisResult,
CopyAnalysisResult? copyAnalysisResult,
ValueContentAnalysisResult? valueContentAnalysisResult,
InterproceduralParameterValidationAnalysisData? interproceduralAnalysisData)
{
Debug.Assert(pointsToAnalysisResult != null);
Debug.Assert(copyAnalysisResult == null);
Debug.Assert(valueContentAnalysisResult == null);
// Do not invoke any interprocedural analysis more than one level down.
// We only care about analyzing validation methods.
return new ParameterValidationAnalysisContext(
ValueDomain, WellKnownTypeProvider, invokedCfg, invokedMethod, AnalyzerOptions,
NullCheckValidationMethodNames, InterproceduralAnalysisConfiguration,
PessimisticAnalysis, pointsToAnalysisResult, TryGetOrComputeAnalysisResult, ControlFlowGraph,
interproceduralAnalysisData, TrackHazardousParameterUsages);
}
public ParameterValidationAnalysisContext WithTrackHazardousParameterUsages()
=> new(
ValueDomain, WellKnownTypeProvider, ControlFlowGraph,
OwningSymbol, AnalyzerOptions, NullCheckValidationMethodNames,
InterproceduralAnalysisConfiguration, PessimisticAnalysis,
PointsToAnalysisResult, TryGetOrComputeAnalysisResult, ParentControlFlowGraph,
InterproceduralAnalysisData, trackHazardousParameterUsages: true);
public bool TrackHazardousParameterUsages { get; }
private SymbolNamesWithValueOption<Unit> NullCheckValidationMethodNames { get; }
public bool IsNullCheckValidationMethod(IMethodSymbol method)
=> NullCheckValidationMethodNames.Contains(method);
protected override void ComputeHashCodePartsSpecific(ref RoslynHashCode hashCode)
{
hashCode.Add(TrackHazardousParameterUsages.GetHashCode());
hashCode.Add(NullCheckValidationMethodNames.GetHashCode());
}
protected override bool ComputeEqualsByHashCodeParts(AbstractDataFlowAnalysisContext<ParameterValidationAnalysisData, ParameterValidationAnalysisContext, ParameterValidationAnalysisResult, ParameterValidationAbstractValue> obj)
{
var other = (ParameterValidationAnalysisContext)obj;
return TrackHazardousParameterUsages.GetHashCode() == other.TrackHazardousParameterUsages.GetHashCode()
&& NullCheckValidationMethodNames.GetHashCode() == other.NullCheckValidationMethodNames.GetHashCode();
}
}
}