Skip to content

Commit 47edf21

Browse files
authored
Merge pull request #6794 from mavasani/CA1508_Enums
Make flow analysis more conservative in presence of entities that can point to multiple different objects
2 parents b3274a6 + 0344c96 commit 47edf21

File tree

7 files changed

+229
-30
lines changed

7 files changed

+229
-30
lines changed

src/Microsoft.CodeAnalysis.AnalyzerUtilities/PublicAPI.Unshipped.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractAnalysisDomain<TAnalysisDat
3838
Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractBlockAnalysisResult
3939
Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractBlockAnalysisResult.AbstractBlockAnalysisResult(Microsoft.CodeAnalysis.FlowAnalysis.BasicBlock! basicBlock) -> void
4040
Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractBlockAnalysisResult.BasicBlock.get -> Microsoft.CodeAnalysis.FlowAnalysis.BasicBlock!
41+
Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity.EntityForInstanceLocation.get -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity?
4142
Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntityFactory.TryGetCopyValueForFlowCapture(Microsoft.CodeAnalysis.FlowAnalysis.CaptureId captureId, out Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.CopyAnalysis.CopyAbstractValue! copyValue) -> bool
4243
Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowAnalysisResult<TBlockAnalysisResult, TAbstractAnalysisValue>.LambdaAndLocalFunctionAnalysisInfo.get -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.LambdaAndLocalFunctionAnalysisInfo!
4344
Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor<TAnalysisData, TAnalysisContext, TAnalysisResult, TAbstractAnalysisValue>.StandaloneLocalFunctionAnalysisResultsMap.get -> System.Collections.Immutable.ImmutableDictionary<Microsoft.CodeAnalysis.IMethodSymbol!, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.IDataFlowAnalysisResult<TAbstractAnalysisValue>!>!
@@ -73,6 +74,7 @@ static Analyzer.Utilities.RoslynHashCode.Combine<T1, T2, T3, T4>(T1 value1, T2 v
7374
static Analyzer.Utilities.RoslynHashCode.Combine<T1, T2, T3>(T1 value1, T2 value2, T3 value3) -> int
7475
static Analyzer.Utilities.RoslynHashCode.Combine<T1, T2>(T1 value1, T2 value2) -> int
7576
static Analyzer.Utilities.RoslynHashCode.Combine<T1>(T1 value1) -> int
77+
static Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity.Create(Microsoft.CodeAnalysis.ISymbol? symbol, System.Collections.Immutable.ImmutableArray<Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractIndex!> indices, Microsoft.CodeAnalysis.ITypeSymbol! type, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAbstractValue! instanceLocation, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity? parent, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity? entityForInstanceLocation) -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity!
7678
static Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DisposeAnalysis.DisposeAnalysis.TryGetOrComputeResult(Microsoft.CodeAnalysis.FlowAnalysis.ControlFlowGraph! cfg, Microsoft.CodeAnalysis.ISymbol! owningSymbol, Analyzer.Utilities.WellKnownTypeProvider! wellKnownTypeProvider, Microsoft.CodeAnalysis.Diagnostics.AnalyzerOptions! analyzerOptions, Microsoft.CodeAnalysis.DiagnosticDescriptor! rule, System.Collections.Immutable.ImmutableHashSet<Microsoft.CodeAnalysis.INamedTypeSymbol!>! disposeOwnershipTransferLikelyTypes, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAnalysisKind defaultPointsToAnalysisKind, bool trackInstanceFields, bool exceptionPathsAnalysis, out Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAnalysisResult? pointsToAnalysisResult, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.InterproceduralAnalysisKind interproceduralAnalysisKind = Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.InterproceduralAnalysisKind.ContextSensitive, bool performCopyAnalysisIfNotUserConfigured = false, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.InterproceduralAnalysisPredicate? interproceduralAnalysisPredicate = null, bool defaultDisposeOwnershipTransferAtConstructor = false, bool defaultDisposeOwnershipTransferAtMethodCall = false) -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DisposeAnalysis.DisposeAnalysisResult?
7779
static Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.InterproceduralAnalysisConfiguration.Create(Microsoft.CodeAnalysis.Diagnostics.AnalyzerOptions! analyzerOptions, Microsoft.CodeAnalysis.DiagnosticDescriptor! rule, Microsoft.CodeAnalysis.FlowAnalysis.ControlFlowGraph! cfg, Microsoft.CodeAnalysis.Compilation! compilation, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.InterproceduralAnalysisKind defaultInterproceduralAnalysisKind, uint defaultMaxInterproceduralMethodCallChain = 3, uint defaultMaxInterproceduralLambdaOrLocalFunctionCallChain = 3) -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.InterproceduralAnalysisConfiguration
7880
static Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.InterproceduralAnalysisConfiguration.Create(Microsoft.CodeAnalysis.Diagnostics.AnalyzerOptions! analyzerOptions, System.Collections.Immutable.ImmutableArray<Microsoft.CodeAnalysis.DiagnosticDescriptor!> rules, Microsoft.CodeAnalysis.FlowAnalysis.ControlFlowGraph! cfg, Microsoft.CodeAnalysis.Compilation! compilation, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.InterproceduralAnalysisKind defaultInterproceduralAnalysisKind, uint defaultMaxInterproceduralMethodCallChain = 3, uint defaultMaxInterproceduralLambdaOrLocalFunctionCallChain = 3) -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.InterproceduralAnalysisConfiguration
@@ -655,7 +657,6 @@ static Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractLocation.CreateThisO
655657
static Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractLocationDataFlowOperationVisitor<TAnalysisData, TAnalysisContext, TAnalysisResult, TAbstractAnalysisValue>.GetClonedAnalysisDataHelper(System.Collections.Generic.IDictionary<Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractLocation!, TAbstractAnalysisValue>! analysisData) -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DictionaryAnalysisData<Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractLocation!, TAbstractAnalysisValue>!
656658
static Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractLocationDataFlowOperationVisitor<TAnalysisData, TAnalysisContext, TAnalysisResult, TAbstractAnalysisValue>.GetEmptyAnalysisDataHelper() -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DictionaryAnalysisData<Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractLocation!, TAbstractAnalysisValue>!
657659
static Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity.Create(Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.InterproceduralCaptureId interproceduralCaptureId, Microsoft.CodeAnalysis.ITypeSymbol! type, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAbstractValue! instanceLocation) -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity!
658-
static Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity.Create(Microsoft.CodeAnalysis.ISymbol? symbol, System.Collections.Immutable.ImmutableArray<Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractIndex!> indices, Microsoft.CodeAnalysis.ITypeSymbol! type, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAbstractValue! instanceLocation, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity? parent) -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity!
659660
static Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity.Create(Microsoft.CodeAnalysis.Operations.IInstanceReferenceOperation! instanceReferenceOperation, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAbstractValue! instanceLocation) -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity!
660661
static Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity.CreateThisOrMeInstance(Microsoft.CodeAnalysis.INamedTypeSymbol! typeSymbol, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAbstractValue! instanceLocation) -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity!
661662
static Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntityDataFlowOperationVisitor<TAnalysisData, TAnalysisContext, TAnalysisResult, TAbstractAnalysisValue>.IsChildAnalysisEntity(Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity! entity, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity! ancestorEntity) -> bool

src/NetAnalyzers/UnitTests/Microsoft.CodeQuality.Analyzers/Maintainability/AvoidDeadConditionalCode_NullAnalysis.cs

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7280,5 +7280,124 @@ private void Method3(IReadOnlyList<int>? items3 = null)
72807280
},
72817281
}.RunAsync();
72827282
}
7283+
7284+
[Trait(Traits.DataflowAnalysis, Traits.Dataflow.PointsToAnalysis)]
7285+
[Trait(Traits.DataflowAnalysis, Traits.Dataflow.NullAnalysis)]
7286+
[Trait(Traits.DataflowAnalysis, Traits.Dataflow.CopyAnalysis)]
7287+
[Fact, WorkItem(6520, "https://github.com/dotnet/roslyn-analyzers/issues/6520")]
7288+
public async Task CompareTwoUnrelatedEnumVariables_NoDiagnosticsAsync()
7289+
{
7290+
await new VerifyCS.Test
7291+
{
7292+
TestCode = @"
7293+
public class Parser
7294+
{
7295+
private static ParsedPredicate ParsePredicate(ParsedPredicate predicate, PredicateOperand identifier, PredicateOperand literal)
7296+
{
7297+
if (predicate.Left.TypePrimitive == PredicateTypePrimitive.F1)
7298+
{
7299+
identifier = predicate.Left;
7300+
literal = predicate.Right;
7301+
}
7302+
else
7303+
{
7304+
identifier = predicate.Right;
7305+
literal = predicate.Left;
7306+
}
7307+
7308+
if (identifier.TypePrimitive != PredicateTypePrimitive.F1)
7309+
{
7310+
return predicate;
7311+
}
7312+
7313+
if (literal.TypePrimitive == PredicateTypePrimitive.F2)
7314+
{
7315+
}
7316+
7317+
return predicate;
7318+
}
7319+
}
7320+
7321+
public class PredicateOperand
7322+
{
7323+
public PredicateTypePrimitive TypePrimitive { get; set; }
7324+
}
7325+
7326+
public enum PredicateTypePrimitive
7327+
{
7328+
F1,
7329+
F2,
7330+
}
7331+
7332+
public class ParsedPredicate
7333+
{
7334+
public PredicateOperand Left { get; }
7335+
public PredicateOperand Right { get; }
7336+
}",
7337+
LanguageVersion = CodeAnalysis.CSharp.LanguageVersion.CSharp8,
7338+
}.RunAsync();
7339+
}
7340+
7341+
[Trait(Traits.DataflowAnalysis, Traits.Dataflow.PointsToAnalysis)]
7342+
[Trait(Traits.DataflowAnalysis, Traits.Dataflow.NullAnalysis)]
7343+
[Trait(Traits.DataflowAnalysis, Traits.Dataflow.CopyAnalysis)]
7344+
[Fact, WorkItem(6520, "https://github.com/dotnet/roslyn-analyzers/issues/6520")]
7345+
public async Task CompareTwoRelatedEnumVariables_DiagnosticsAsync()
7346+
{
7347+
await new VerifyCS.Test
7348+
{
7349+
TestCode = @"
7350+
public class Parser
7351+
{
7352+
private static ParsedPredicate ParsePredicate(ParsedPredicate predicate, PredicateOperand identifier, PredicateOperand literal)
7353+
{
7354+
if (predicate.Left.TypePrimitive == PredicateTypePrimitive.F1)
7355+
{
7356+
identifier = predicate.Left;
7357+
literal = predicate.Right;
7358+
}
7359+
else
7360+
{
7361+
identifier = predicate.Right;
7362+
literal = predicate.Left;
7363+
}
7364+
7365+
if (identifier.TypePrimitive != PredicateTypePrimitive.F1)
7366+
{
7367+
return predicate;
7368+
}
7369+
7370+
if (identifier.TypePrimitive == PredicateTypePrimitive.F2)
7371+
{
7372+
}
7373+
7374+
return predicate;
7375+
}
7376+
}
7377+
7378+
public class PredicateOperand
7379+
{
7380+
public PredicateTypePrimitive TypePrimitive { get; set; }
7381+
}
7382+
7383+
public enum PredicateTypePrimitive
7384+
{
7385+
F1,
7386+
F2,
7387+
}
7388+
7389+
public class ParsedPredicate
7390+
{
7391+
public PredicateOperand Left { get; }
7392+
public PredicateOperand Right { get; }
7393+
}",
7394+
LanguageVersion = CodeAnalysis.CSharp.LanguageVersion.CSharp8,
7395+
ExpectedDiagnostics =
7396+
{
7397+
// /0/Test0.cs(22,13): warning CA1508: 'identifier.TypePrimitive == PredicateTypePrimitive.F2' is always 'false'. Remove or refactor the condition(s) to avoid dead code.
7398+
GetCSharpResultAt(22, 13, "identifier.TypePrimitive == PredicateTypePrimitive.F2", "false")
7399+
}
7400+
}.RunAsync();
7401+
}
72837402
}
72847403
}

src/NetAnalyzers/UnitTests/Microsoft.CodeQuality.Analyzers/Maintainability/AvoidDeadConditionalCode_ValueContentAnalysis.cs

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2733,7 +2733,9 @@ public void Load(Uri productFileUrl, Uri originalLocation = null)
27332733
[InlineData("class", "class")]
27342734
public async Task DataflowAcrossBranchesAsync(string typeTest, string typeA)
27352735
{
2736-
await VerifyCS.VerifyAnalyzerAsync($@"
2736+
var test = new VerifyCS.Test
2737+
{
2738+
TestCode = $@"
27372739
using System;
27382740
27392741
namespace TestNamespace
@@ -2752,27 +2754,46 @@ public void Something(int param)
27522754
Test t = new Test();
27532755
t.A = new A();
27542756
t.A.IntProperty = param;
2757+
A a = new A();
2758+
a.IntProperty = param;
2759+
A a1 = new A();
2760+
a1.IntProperty = param;
2761+
A a2 = new A();
2762+
a2.IntProperty = param;
27552763
if (param >= 0)
27562764
{{
2757-
A a1 = new A();
27582765
a1.IntProperty = 1;
27592766
t.A = a1; // t.A now contains/points to a1
2767+
a = a2;
27602768
}}
27612769
else
27622770
{{
2763-
A a2 = new A();
27642771
a2.IntProperty = 1;
2765-
t.A = a2; // t.A now contains/points to a2
2772+
t.A = a2; // t.A now contains/points to a2
2773+
a = a1;
27662774
}}
27672775
27682776
if (t.A.IntProperty == 1) // t.A now contains/points either a1 or a2, both of which have .IntProperty = """"
2777+
// However, we conservatively don't report it when 'A' is a class
2778+
{{
2779+
}}
2780+
2781+
if (a.IntProperty == 1) // a points to a1 or a2, and a.IntProperty = param for both cases.
27692782
{{
27702783
}}
27712784
}}
27722785
}}
2773-
}}",
2774-
// Test0.cs(33,17): warning CA1508: 't.A.IntProperty == 1' is always 'true'. Remove or refactor the condition(s) to avoid dead code.
2775-
GetCSharpResultAt(33, 17, "t.A.IntProperty == 1", "true"));
2786+
}}"
2787+
};
2788+
2789+
if (typeA != "class")
2790+
{
2791+
test.ExpectedDiagnostics.Add(
2792+
// Test0.cs(33,17): warning CA1508: 't.A.IntProperty == 1' is always 'true'. Remove or refactor the condition(s) to avoid dead code.
2793+
GetCSharpResultAt(39, 17, "t.A.IntProperty == 1", "true"));
2794+
}
2795+
2796+
await test.RunAsync();
27762797
}
27772798

27782799
[Fact, WorkItem(4056, "https://github.com/dotnet/roslyn-analyzers/issues/4056")]

src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Data/ReviewSQLQueriesForSecurityVulnerabilitiesTests_FlowAnalysis.cs

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3349,7 +3349,9 @@ End Sub
33493349
[Fact]
33503350
public async Task FlowAnalysis_PointsTo_ReferenceType_BaseDerived_IfStatement_NoDiagnosticAsync()
33513351
{
3352-
await VerifyCS.VerifyAnalyzerAsync($@"
3352+
await new VerifyCS.Test
3353+
{
3354+
TestCode = $@"
33533355
{SetupCodeCSharp}
33543356
33553357
class Command1 : Command
@@ -3389,12 +3391,22 @@ void M1(string param)
33893391
}}
33903392
33913393
string str = t.B.Field; // t.B now points to either b or d, both of which have .Field = """"
3394+
// However, we are forced to be conservative in our analysis due to
3395+
// potential false positives from multiple variables pointing to the same set, i.e. b or d.
3396+
// See https://github.com/dotnet/roslyn-analyzers/issues/6520 for an example.
33923397
Command c = new Command1(str, str);
33933398
}}
3394-
}}
3395-
");
3399+
}}",
3400+
ExpectedDiagnostics =
3401+
{
3402+
// /0/Test0.cs(126,21): warning CA2100: Review if the query string passed to 'Command1.Command1(string cmd, string parameter2)' in 'M1', accepts any user input
3403+
GetCSharpResultAt(126, 21, "Command1.Command1(string cmd, string parameter2)", "M1"),
3404+
}
3405+
}.RunAsync();
33963406

3397-
await VerifyVB.VerifyAnalyzerAsync($@"
3407+
await new VerifyVB.Test
3408+
{
3409+
TestCode = $@"
33983410
{SetupCodeBasic}
33993411
34003412
Class Command1
@@ -3429,9 +3441,18 @@ Dim b As New Base()
34293441
t.B = b ' t.B now points to b
34303442
End If
34313443
Dim str As String = t.B.Field ' t.B now points to either b or d, both of which have .Field = """"
3444+
' However, we are forced to be conservative in our analysis due to
3445+
' potential false positives from multiple variables pointing to the same set, i.e. b or d.
3446+
' See https://github.com/dotnet/roslyn-analyzers/issues/6520 for an example.
34323447
Dim c As Command = New Command1(str, str)
34333448
End Sub
3434-
End Class");
3449+
End Class",
3450+
ExpectedDiagnostics =
3451+
{
3452+
// /0/Test0.vb(159,28): warning CA2100: Review if the query string passed to 'Sub Command1.New(cmd As String, parameter2 As String)' in 'M1', accepts any user input
3453+
GetBasicResultAt(159, 28, "Sub Command1.New(cmd As String, parameter2 As String)", "M1")
3454+
}
3455+
}.RunAsync();
34353456
}
34363457

34373458
[Trait(Traits.DataflowAnalysis, Traits.Dataflow.PointsToAnalysis)]

src/Utilities/FlowAnalysis/FlowAnalysis/Analysis/GlobalFlowStateAnalysis/GlobalFlowStateDataFlowOperationVisitor.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,8 @@ private static AnalysisEntity GetGlobalEntity(TAnalysisContext analysisContext)
6060
ImmutableArray<AbstractIndex>.Empty,
6161
owningSymbol.GetMemberOrLocalOrParameterType()!,
6262
instanceLocation: PointsToAbstractValue.Unknown,
63-
parent: null);
63+
parent: null,
64+
entityForInstanceLocation: null);
6465
}
6566

6667
public sealed override DictionaryAnalysisData<AnalysisEntity, TAbstractAnalysisValue> Flow(

0 commit comments

Comments
 (0)