Skip to content

Commit 11bd7a1

Browse files
Jim8yshargonWi1l-B0tajara87
authored
Fix: detect nested unsupported collection types (#1907)
Co-authored-by: Shargon <shargon@gmail.com> Co-authored-by: Will <201105916+Wi1l-B0t@users.noreply.github.com> Co-authored-by: Alvaro <amjarag@gmail.com>
1 parent c99492a commit 11bd7a1

2 files changed

Lines changed: 90 additions & 2 deletions

File tree

src/Neo.SmartContract.Analyzer/CollectionTypesUsageAnalyzer.cs

Lines changed: 54 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
using Microsoft.CodeAnalysis.Diagnostics;
1515
using Microsoft.CodeAnalysis.Operations;
1616
using System.Collections.Immutable;
17+
using System.Collections.Generic;
1718
using System.Linq;
1819
using Microsoft.CodeAnalysis.CSharp;
1920
using Microsoft.CodeAnalysis.CSharp.Syntax;
@@ -132,10 +133,61 @@ private void ReportIfUnsupportedCollectionType(SyntaxNodeAnalysisContext context
132133

133134
private string? GetUnsupportedCollectionType(ITypeSymbol? type)
134135
{
136+
return GetUnsupportedCollectionType(
137+
type,
138+
new HashSet<ITypeSymbol>(SymbolEqualityComparer.Default));
139+
}
140+
141+
private string? GetUnsupportedCollectionType(
142+
ITypeSymbol? type,
143+
HashSet<ITypeSymbol> visitedTypes)
144+
{
145+
if (type is null || !visitedTypes.Add(type)) return null;
146+
147+
if (type is IArrayTypeSymbol arrayType)
148+
return GetUnsupportedCollectionType(arrayType.ElementType, visitedTypes);
149+
150+
if (type is IPointerTypeSymbol pointerType)
151+
return GetUnsupportedCollectionType(pointerType.PointedAtType, visitedTypes);
152+
153+
if (type is IFunctionPointerTypeSymbol functionPointerType)
154+
{
155+
var returnType = GetUnsupportedCollectionType(
156+
functionPointerType.Signature.ReturnType,
157+
visitedTypes);
158+
if (returnType is not null) return returnType;
159+
160+
foreach (var parameter in functionPointerType.Signature.Parameters)
161+
{
162+
var parameterType = GetUnsupportedCollectionType(parameter.Type, visitedTypes);
163+
if (parameterType is not null) return parameterType;
164+
}
165+
166+
return null;
167+
}
168+
135169
if (type is not INamedTypeSymbol namedType) return null;
136170

137-
var originalType = namedType.OriginalDefinition.ToString() ?? throw new ArgumentNullException("originalType is null");
138-
return _unsupportedCollectionTypes.Contains(originalType) ? originalType : null;
171+
var originalType = namedType.OriginalDefinition.ToString() ??
172+
throw new ArgumentNullException(nameof(type));
173+
if (_unsupportedCollectionTypes.Contains(originalType))
174+
return originalType;
175+
176+
if (namedType.ContainingType is not null)
177+
{
178+
var containingType = GetUnsupportedCollectionType(
179+
namedType.ContainingType,
180+
visitedTypes);
181+
if (containingType is not null) return containingType;
182+
}
183+
184+
foreach (var typeArgument in namedType.TypeArguments)
185+
{
186+
var unsupportedType = GetUnsupportedCollectionType(typeArgument, visitedTypes);
187+
if (unsupportedType is not null) return unsupportedType;
188+
}
189+
190+
return null;
139191
}
140192

141193
private static string GetSuggestedType(string originalType)

tests/Neo.SmartContract.Analyzer.UnitTests/CollectionTypesUsageAnalyzerUnitTests.cs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,42 @@ class TestClass
143143
await VerifyCS.VerifyAnalyzerAsync(test, returnDiagnostic, parameterDiagnostic, propertyDiagnostic);
144144
}
145145

146+
[TestMethod]
147+
public async Task NestedUnsupportedCollectionTypes_ShouldReportDiagnostics()
148+
{
149+
var test = TestNamespace + """
150+
151+
class TestClass
152+
{
153+
public {|#0:System.Tuple<int, System.Collections.Generic.List<string>>|} GetItems() => null;
154+
public void SetItems({|#1:System.Collections.Generic.Stack<int>[]|} values) { }
155+
}
156+
""";
157+
158+
var returnDiagnostic = VerifyCS.Diagnostic(CollectionTypesUsageAnalyzer.DiagnosticId)
159+
.WithLocation(0)
160+
.WithArguments("System.Collections.Generic.List<T>", "List<T>");
161+
var parameterDiagnostic = VerifyCS.Diagnostic(CollectionTypesUsageAnalyzer.DiagnosticId)
162+
.WithLocation(1)
163+
.WithArguments("System.Collections.Generic.Stack<T>", "List<T>");
164+
165+
await VerifyCS.VerifyAnalyzerAsync(test, returnDiagnostic, parameterDiagnostic);
166+
}
167+
168+
[TestMethod]
169+
public async Task NestedSupportedCollectionTypes_ShouldNotReportDiagnostics()
170+
{
171+
var test = TestNamespace + """
172+
173+
class TestClass
174+
{
175+
public System.Tuple<int, List<string>> GetItems() => null;
176+
}
177+
""";
178+
179+
await VerifyCS.VerifyAnalyzerAsync(test);
180+
}
181+
146182
[TestMethod]
147183
public void CollectionDiagnostic_ShouldNotOfferAutomaticCodeFixes()
148184
{

0 commit comments

Comments
 (0)