|
21 | 21 | using SonarAnalyzer.CFG.Roslyn; |
22 | 22 | using CfgAllPathValidator = SonarAnalyzer.CFG.Roslyn.CfgAllPathValidator; |
23 | 23 |
|
24 | | -namespace SonarAnalyzer.Rules.CSharp |
| 24 | +namespace SonarAnalyzer.Rules.CSharp; |
| 25 | + |
| 26 | +public partial class InfiniteRecursion |
25 | 27 | { |
26 | | - public partial class InfiniteRecursion |
| 28 | + private sealed class RoslynChecker : IChecker |
27 | 29 | { |
28 | | - private sealed class RoslynChecker : IChecker |
| 30 | + public void CheckForNoExitProperty(SonarSyntaxNodeReportingContext c, PropertyDeclarationSyntax property, IPropertySymbol propertySymbol) => |
| 31 | + CheckForNoExit(c, |
| 32 | + propertySymbol, |
| 33 | + property.ExpressionBody, |
| 34 | + property.AccessorList, |
| 35 | + property.Identifier.GetLocation(), |
| 36 | + "property's recursion", |
| 37 | + "property accessor's recursion"); |
| 38 | + |
| 39 | + public void CheckForNoExitIndexer(SonarSyntaxNodeReportingContext c, IndexerDeclarationSyntax indexer, IPropertySymbol propertySymbol) => |
| 40 | + CheckForNoExit(c, |
| 41 | + propertySymbol, |
| 42 | + indexer.ExpressionBody, |
| 43 | + indexer.AccessorList, |
| 44 | + indexer.ThisKeyword.GetLocation(), |
| 45 | + "indexer's recursion", |
| 46 | + "indexer accessor's recursion"); |
| 47 | + |
| 48 | + public void CheckForNoExitEvent(SonarSyntaxNodeReportingContext c, EventDeclarationSyntax eventDeclaration, IEventSymbol eventSymbol) |
29 | 49 | { |
30 | | - public void CheckForNoExitProperty(SonarSyntaxNodeReportingContext c, PropertyDeclarationSyntax property, IPropertySymbol propertySymbol) => |
31 | | - CheckForNoExit(c, |
32 | | - propertySymbol, |
33 | | - property.ExpressionBody, |
34 | | - property.AccessorList, |
35 | | - property.Identifier.GetLocation(), |
36 | | - "property's recursion", |
37 | | - "property accessor's recursion"); |
38 | | - |
39 | | - public void CheckForNoExitIndexer(SonarSyntaxNodeReportingContext c, IndexerDeclarationSyntax indexer, IPropertySymbol propertySymbol) => |
40 | | - CheckForNoExit(c, |
41 | | - propertySymbol, |
42 | | - indexer.ExpressionBody, |
43 | | - indexer.AccessorList, |
44 | | - indexer.ThisKeyword.GetLocation(), |
45 | | - "indexer's recursion", |
46 | | - "indexer accessor's recursion"); |
47 | | - |
48 | | - public void CheckForNoExitEvent(SonarSyntaxNodeReportingContext c, EventDeclarationSyntax eventDeclaration, IEventSymbol eventSymbol) |
| 50 | + if (eventDeclaration.AccessorList is not null) |
49 | 51 | { |
50 | | - if (eventDeclaration.AccessorList != null) |
| 52 | + foreach (var accessor in eventDeclaration.AccessorList.Accessors.Where(x => x.HasBodyOrExpressionBody())) |
51 | 53 | { |
52 | | - foreach (var accessor in eventDeclaration.AccessorList.Accessors.Where(a => a.HasBodyOrExpressionBody())) |
53 | | - { |
54 | | - var cfg = ControlFlowGraph.Create(accessor, c.SemanticModel, c.Cancel); |
55 | | - var context = new RecursionContext<ControlFlowGraph>(c, cfg, eventSymbol, accessor.Keyword.GetLocation(), "event accessor's recursion"); |
56 | | - var walker = new RecursionSearcher(context); |
57 | | - walker.CheckPaths(); |
58 | | - } |
| 54 | + var cfg = ControlFlowGraph.Create(accessor, c.SemanticModel, c.Cancel); |
| 55 | + var context = new RecursionContext<ControlFlowGraph>(c, cfg, eventSymbol, accessor.Keyword.GetLocation(), "event accessor's recursion"); |
| 56 | + var walker = new RecursionSearcher(context); |
| 57 | + walker.CheckPaths(); |
59 | 58 | } |
60 | 59 | } |
| 60 | + } |
61 | 61 |
|
62 | | - public void CheckForNoExitMethod(SonarSyntaxNodeReportingContext c, SyntaxNode body, SyntaxToken identifier, IMethodSymbol symbol) |
| 62 | + public void CheckForNoExitMethod(SonarSyntaxNodeReportingContext c, SyntaxNode body, SyntaxToken identifier, IMethodSymbol symbol) |
| 63 | + { |
| 64 | + if (body.CreateCfg(c.SemanticModel, c.Cancel) is { } cfg) |
63 | 65 | { |
64 | | - if (body.CreateCfg(c.SemanticModel, c.Cancel) is { } cfg) |
65 | | - { |
66 | | - var context = new RecursionContext<ControlFlowGraph>(c, cfg, symbol, identifier.GetLocation(), "method's recursion"); |
67 | | - var walker = new RecursionSearcher(context); |
68 | | - walker.CheckPaths(); |
69 | | - } |
| 66 | + var context = new RecursionContext<ControlFlowGraph>(c, cfg, symbol, identifier.GetLocation(), "method's recursion"); |
| 67 | + var walker = new RecursionSearcher(context); |
| 68 | + walker.CheckPaths(); |
70 | 69 | } |
| 70 | + } |
71 | 71 |
|
72 | | - private static void CheckForNoExit(SonarSyntaxNodeReportingContext c, |
73 | | - IPropertySymbol propertySymbol, |
74 | | - ArrowExpressionClauseSyntax expressionBody, |
75 | | - AccessorListSyntax accessorList, |
76 | | - Location location, |
77 | | - string arrowExpressionMessageArg, |
78 | | - string accessorMessageArg) |
| 72 | + private static void CheckForNoExit(SonarSyntaxNodeReportingContext c, |
| 73 | + IPropertySymbol propertySymbol, |
| 74 | + ArrowExpressionClauseSyntax expressionBody, |
| 75 | + AccessorListSyntax accessorList, |
| 76 | + Location location, |
| 77 | + string arrowExpressionMessageArg, |
| 78 | + string accessorMessageArg) |
| 79 | + { |
| 80 | + if (expressionBody?.Expression is not null) |
| 81 | + { |
| 82 | + var cfg = ControlFlowGraph.Create(expressionBody, c.SemanticModel, c.Cancel); |
| 83 | + var walker = new RecursionSearcher(new RecursionContext<ControlFlowGraph>(c, cfg, propertySymbol, location, arrowExpressionMessageArg)); |
| 84 | + walker.CheckPaths(); |
| 85 | + } |
| 86 | + else if (accessorList is not null) |
79 | 87 | { |
80 | | - if (expressionBody?.Expression != null) |
| 88 | + foreach (var accessor in accessorList.Accessors.Where(a => a.HasBodyOrExpressionBody())) |
81 | 89 | { |
82 | | - var cfg = ControlFlowGraph.Create(expressionBody, c.SemanticModel, c.Cancel); |
83 | | - var walker = new RecursionSearcher(new RecursionContext<ControlFlowGraph>(c, cfg, propertySymbol, location, arrowExpressionMessageArg)); |
| 90 | + var cfg = ControlFlowGraph.Create(accessor, c.SemanticModel, c.Cancel); |
| 91 | + var context = new RecursionContext<ControlFlowGraph>(c, cfg, propertySymbol, accessor.Keyword.GetLocation(), accessorMessageArg); |
| 92 | + var walker = new RecursionSearcher(context, !accessor.Keyword.IsAnyKind(SyntaxKind.SetKeyword, SyntaxKindEx.InitKeyword)); |
84 | 93 | walker.CheckPaths(); |
85 | 94 | } |
86 | | - else if (accessorList != null) |
87 | | - { |
88 | | - foreach (var accessor in accessorList.Accessors.Where(a => a.HasBodyOrExpressionBody())) |
89 | | - { |
90 | | - var cfg = ControlFlowGraph.Create(accessor, c.SemanticModel, c.Cancel); |
91 | | - var context = new RecursionContext<ControlFlowGraph>(c, cfg, propertySymbol, accessor.Keyword.GetLocation(), accessorMessageArg); |
92 | | - var walker = new RecursionSearcher(context, !accessor.Keyword.IsAnyKind(SyntaxKind.SetKeyword, SyntaxKindEx.InitKeyword)); |
93 | | - walker.CheckPaths(); |
94 | | - } |
95 | | - } |
96 | 95 | } |
| 96 | + } |
| 97 | + |
| 98 | + private sealed class RecursionSearcher : CfgAllPathValidator |
| 99 | + { |
| 100 | + private readonly RecursionContext<ControlFlowGraph> context; |
| 101 | + private readonly bool isGetAccesor; |
97 | 102 |
|
98 | | - private sealed class RecursionSearcher : CfgAllPathValidator |
| 103 | + public RecursionSearcher(RecursionContext<ControlFlowGraph> context, bool isGetAccesor = true) |
| 104 | + : base(context.ControlFlowGraph) |
99 | 105 | { |
100 | | - private readonly RecursionContext<ControlFlowGraph> context; |
101 | | - private readonly bool isGetAccesor; |
| 106 | + this.context = context; |
| 107 | + this.isGetAccesor = isGetAccesor; |
| 108 | + } |
102 | 109 |
|
103 | | - public RecursionSearcher(RecursionContext<ControlFlowGraph> context, bool isGetAccesor = true) |
104 | | - : base(context.ControlFlowGraph) |
| 110 | + public void CheckPaths() |
| 111 | + { |
| 112 | + if (!CfgCanExit() || CheckAllPaths()) |
105 | 113 | { |
106 | | - this.context = context; |
107 | | - this.isGetAccesor = isGetAccesor; |
| 114 | + context.ReportIssue(); |
108 | 115 | } |
| 116 | + } |
109 | 117 |
|
110 | | - public void CheckPaths() |
| 118 | + protected override bool IsValid(BasicBlock block) |
| 119 | + { |
| 120 | + if (block.OperationsAndBranchValue.ToReversedExecutionOrder().FirstOrDefault(x => context.AnalyzedSymbol.Equals(MemberSymbol(x.Instance))) is { Instance: { } } operation) |
111 | 121 | { |
112 | | - if (!CfgCanExit() || CheckAllPaths()) |
113 | | - { |
114 | | - context.ReportIssue(); |
115 | | - } |
| 122 | + var isWrite = operation.Parent is { Kind: OperationKindEx.SimpleAssignment } parent && ISimpleAssignmentOperationWrapper.FromOperation(parent).Target == operation.Instance; |
| 123 | + return isGetAccesor ^ isWrite; |
116 | 124 | } |
117 | 125 |
|
118 | | - protected override bool IsValid(BasicBlock block) |
119 | | - { |
120 | | - if (block.OperationsAndBranchValue.ToReversedExecutionOrder().FirstOrDefault(x => context.AnalyzedSymbol.Equals(MemberSymbol(x.Instance))) is { Instance: { } } operation) |
121 | | - { |
122 | | - var isWrite = operation.Parent is { Kind: OperationKindEx.SimpleAssignment } parent |
123 | | - && ISimpleAssignmentOperationWrapper.FromOperation(parent).Target == operation.Instance; |
124 | | - return isGetAccesor ^ isWrite; |
125 | | - } |
126 | | - |
127 | | - return false; |
128 | | - |
129 | | - static ISymbol MemberSymbol(IOperation operation) => |
130 | | - operation.Kind switch |
131 | | - { |
132 | | - OperationKindEx.PropertyReference |
133 | | - when IPropertyReferenceOperationWrapper.FromOperation(operation) is var propertyReference && InstanceReferencesThis(propertyReference.Instance) => |
134 | | - propertyReference.Property, |
135 | | - OperationKindEx.Invocation |
136 | | - when IInvocationOperationWrapper.FromOperation(operation) is var invocation && (!invocation.IsVirtual || InstanceReferencesThis(invocation.Instance)) => |
137 | | - invocation.TargetMethod, |
138 | | - OperationKindEx.Binary => |
139 | | - IBinaryOperationWrapper.FromOperation(operation).OperatorMethod, |
140 | | - OperationKindEx.Decrement => |
141 | | - IIncrementOrDecrementOperationWrapper.FromOperation(operation).OperatorMethod, |
142 | | - OperationKindEx.Increment => |
143 | | - IIncrementOrDecrementOperationWrapper.FromOperation(operation).OperatorMethod, |
144 | | - OperationKindEx.Unary => |
145 | | - IUnaryOperationWrapper.FromOperation(operation).OperatorMethod, |
146 | | - OperationKindEx.Conversion=> |
147 | | - IConversionOperationWrapper.FromOperation(operation).OperatorMethod, |
148 | | - OperationKindEx.EventReference => |
149 | | - IEventReferenceOperationWrapper.FromOperation(operation).Member, |
150 | | - _ => null |
151 | | - }; |
152 | | - |
153 | | - static bool InstanceReferencesThis(IOperation instance) => |
154 | | - instance == null || instance.IsAnyKind(OperationKindEx.FlowCaptureReference, OperationKindEx.InstanceReference); |
155 | | - } |
| 126 | + return false; |
156 | 127 |
|
157 | | - protected override bool IsInvalid(BasicBlock block) => false; |
| 128 | + static ISymbol MemberSymbol(IOperation operation) => |
| 129 | + operation.Kind switch |
| 130 | + { |
| 131 | + OperationKindEx.PropertyReference |
| 132 | + when IPropertyReferenceOperationWrapper.FromOperation(operation) is var propertyReference && InstanceReferencesThis(propertyReference.Instance) => |
| 133 | + propertyReference.Property, |
| 134 | + OperationKindEx.Invocation |
| 135 | + when IInvocationOperationWrapper.FromOperation(operation) is var invocation && (!invocation.IsVirtual || InstanceReferencesThis(invocation.Instance)) => |
| 136 | + invocation.TargetMethod, |
| 137 | + OperationKindEx.Binary => IBinaryOperationWrapper.FromOperation(operation).OperatorMethod, |
| 138 | + OperationKindEx.Decrement => IIncrementOrDecrementOperationWrapper.FromOperation(operation).OperatorMethod, |
| 139 | + OperationKindEx.Increment => IIncrementOrDecrementOperationWrapper.FromOperation(operation).OperatorMethod, |
| 140 | + OperationKindEx.Unary => IUnaryOperationWrapper.FromOperation(operation).OperatorMethod, |
| 141 | + OperationKindEx.Conversion => IConversionOperationWrapper.FromOperation(operation).OperatorMethod, |
| 142 | + OperationKindEx.EventReference => IEventReferenceOperationWrapper.FromOperation(operation).Member, |
| 143 | + _ => null |
| 144 | + }; |
158 | 145 |
|
159 | | - private bool CfgCanExit() => |
160 | | - context.ControlFlowGraph.ExitBlock.IsReachable |
161 | | - || context.ControlFlowGraph.Blocks.Any(x => x.FallThroughSuccessor?.Semantics == ControlFlowBranchSemantics.Throw && x.IsReachable); |
| 146 | + static bool InstanceReferencesThis(IOperation instance) => |
| 147 | + instance is null || instance.IsAnyKind(OperationKindEx.FlowCaptureReference, OperationKindEx.InstanceReference); |
162 | 148 | } |
| 149 | + |
| 150 | + protected override bool IsInvalid(BasicBlock block) => false; |
| 151 | + |
| 152 | + private bool CfgCanExit() => |
| 153 | + context.ControlFlowGraph.ExitBlock.IsReachable |
| 154 | + || context.ControlFlowGraph.Blocks.Any(x => x.FallThroughSuccessor?.Semantics == ControlFlowBranchSemantics.Throw && x.IsReachable); |
163 | 155 | } |
164 | 156 | } |
165 | 157 | } |
0 commit comments