Skip to content

Commit c78d163

Browse files
Fix duplicate InputSizeLimit warnings per file (#27)
* first shot * tidy * always earliest param in file * tidy
1 parent 3040668 commit c78d163

File tree

2 files changed

+42
-14
lines changed

2 files changed

+42
-14
lines changed

src/CustomCode-Analyzer/Analyzer.cs

Lines changed: 37 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -336,11 +336,12 @@ public static class Categories
336336
private static readonly DiagnosticDescriptor InputSizeLimitRule = new(
337337
DiagnosticIds.InputSizeLimit,
338338
title: "Possible input size limit",
339-
messageFormat: "This method accepts binary data. Note that external libraries have a 5.5MB total input size limit. For large files, use a REST API endpoint or file URL instead.",
339+
messageFormat: "One or more methods accept binary data. Note that external libraries have a 5.5MB total input size limit. For large files, use a REST API endpoint or file URL instead.",
340340
category: Categories.Design,
341341
defaultSeverity: DiagnosticSeverity.Warning,
342342
isEnabledByDefault: true,
343343
description: "External libraries have a 5.5MB total input size limit. For large binary files, expose them through a REST API endpoint in your app or provide a URL to download them.",
344+
customTags: WellKnownDiagnosticTags.CompilationEnd,
344345
helpLinkUri: "https://success.outsystems.com/documentation/outsystems_developer_cloud/building_apps/extend_your_apps_with_custom_code/external_libraries_sdk_readme/#use-with-large-binary-files"
345346
);
346347

@@ -441,23 +442,27 @@ CompilationStartAnalysisContext compilationContext
441442
SymbolKind.NamedType
442443
);
443444

445+
// Dictionary tracking the earliest 'byte[]' parameter location per syntax tree
446+
var candidateInputSizeLimitDiagnostics =
447+
new ConcurrentDictionary<SyntaxTree, Location>();
448+
444449
// Register a symbol action for method-level analysis
445450
compilationContext.RegisterSymbolAction(
446451
context =>
447452
{
448453
if (context.Symbol is IMethodSymbol methodSymbol)
449454
{
450-
AnalyzeMethod(context, methodSymbol);
455+
AnalyzeMethod(context, methodSymbol, candidateInputSizeLimitDiagnostics);
451456
}
452457
},
453458
SymbolKind.Method
454459
);
455460

456461
// Register a compilation end action to check for any final conditions
457462
// that can only be verified after all symbols have been processed (for example, # of OSInterfaces).
458-
compilationContext.RegisterCompilationEndAction(ctx =>
463+
compilationContext.RegisterCompilationEndAction(context =>
459464
{
460-
AnalyzeCompilationEnd(ctx, osInterfaces);
465+
AnalyzeCompilationEnd(context, osInterfaces, candidateInputSizeLimitDiagnostics);
461466
});
462467
}
463468

@@ -835,13 +840,15 @@ originalNameArg.Key is not null
835840
/// <summary>
836841
/// Analyzes method declarations.
837842
/// <summary>
838-
private static void AnalyzeMethod(SymbolAnalysisContext context, IMethodSymbol methodSymbol)
843+
private static void AnalyzeMethod(
844+
SymbolAnalysisContext context,
845+
IMethodSymbol methodSymbol,
846+
ConcurrentDictionary<SyntaxTree, Location> candidateInputSizeLimitDiagnostics
847+
)
839848
{
840849
var syntaxRef = methodSymbol.DeclaringSyntaxReferences.FirstOrDefault();
841850
if (syntaxRef is null)
842851
return;
843-
if (syntaxRef.GetSyntax() is not MethodDeclarationSyntax methodSyntax)
844-
return;
845852

846853
var containingType = methodSymbol.ContainingType;
847854

@@ -899,15 +906,27 @@ private static void AnalyzeMethod(SymbolAnalysisContext context, IMethodSymbol m
899906
}
900907
}
901908

902-
// Check for potential input size limit issues
903909
if (
904910
parameter.Type is IArrayTypeSymbol arrayType
905911
&& arrayType.ElementType.SpecialType == SpecialType.System_Byte
906912
)
907913
{
908-
context.ReportDiagnostic(
909-
Diagnostic.Create(InputSizeLimitRule, methodSyntax.GetLocation())
910-
);
914+
var parameterSyntax =
915+
parameter.DeclaringSyntaxReferences.FirstOrDefault()?.GetSyntax()
916+
as ParameterSyntax;
917+
if (parameterSyntax?.Type != null)
918+
{
919+
var location = parameterSyntax.Type.GetLocation();
920+
// Update the candidate location for this file if the current 'byte[]' parameter appears earlier
921+
candidateInputSizeLimitDiagnostics.AddOrUpdate(
922+
parameterSyntax.SyntaxTree,
923+
location,
924+
(tree, existingLocation) =>
925+
location.SourceSpan.Start < existingLocation.SourceSpan.Start
926+
? location
927+
: existingLocation
928+
);
929+
}
911930
break;
912931
}
913932

@@ -1101,7 +1120,8 @@ private static void AnalyzeCompilationEnd(
11011120
ConcurrentDictionary<
11021121
string,
11031122
(InterfaceDeclarationSyntax Syntax, INamedTypeSymbol Symbol)
1104-
> osInterfaces
1123+
> osInterfaces,
1124+
ConcurrentDictionary<SyntaxTree, Location> candidateInputSizeLimitDiagnostics
11051125
)
11061126
{
11071127
if (osInterfaces.Count == 0)
@@ -1214,6 +1234,11 @@ is InterfaceDeclarationSyntax ifDecl
12141234
);
12151235
}
12161236
}
1237+
// Report one InputSizeLimit diagnostic per file at the earliest recorded location
1238+
foreach (var kvp in candidateInputSizeLimitDiagnostics)
1239+
{
1240+
context.ReportDiagnostic(Diagnostic.Create(InputSizeLimitRule, kvp.Value));
1241+
}
12171242
}
12181243

12191244
/// <summary>

tests/CustomCode-Analyzer.Tests/AnalyzerTests.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1908,7 +1908,7 @@ await CSharpAnalyzerVerifier<Analyzer>.VerifyAnalyzerAsync(
19081908

19091909
// --------------------- InputSizeLimitRule --------------------------------
19101910
[TestMethod]
1911-
public async Task InputSizeLimitRule_ShowsInfo()
1911+
public async Task InputSizeLimitRule_ReportsOnlyOneWarningForMultipleMethods()
19121912
{
19131913
var test =
19141914
@"
@@ -1918,18 +1918,21 @@ namespace TestNamespace
19181918
public interface IDocumentProcessor
19191919
{
19201920
void ProcessDocument(byte[] documentData);
1921+
void ProcessAnotherDocument(byte[] otherData);
19211922
void ProcessMetadata(string metadata);
19221923
}
19231924
19241925
public class DocumentProcessor : IDocumentProcessor
19251926
{
19261927
public void ProcessDocument(byte[] documentData) { }
1928+
public void ProcessAnotherDocument(byte[] otherData) { }
19271929
public void ProcessMetadata(string metadata) { }
19281930
}
19291931
}";
1932+
// Expect only one diagnostic (highlighting the "byte[]" in the first method)
19301933
var expected = CSharpAnalyzerVerifier<Analyzer>
19311934
.Diagnostic(DiagnosticIds.InputSizeLimit)
1932-
.WithSpan(7, 9, 7, 51);
1935+
.WithSpan(7, 30, 7, 36);
19331936

19341937
await CSharpAnalyzerVerifier<Analyzer>.VerifyAnalyzerAsync(
19351938
test,

0 commit comments

Comments
 (0)