diff --git a/analyzers/its/expected/Ember-MM/S3878-Ember Media Manager.json b/analyzers/its/expected/Ember-MM/S3878-Ember Media Manager.json deleted file mode 100644 index 6d1b789ee57..00000000000 --- a/analyzers/its/expected/Ember-MM/S3878-Ember Media Manager.json +++ /dev/null @@ -1,160 +0,0 @@ -{ - "Issues": [ - { - "Id": "S3878", - "Message": "Remove this array creation and simply pass the elements.", - "Uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/Projects/Ember-MM/Ember%20Media%20Manager/frmMain.vb#L6583", - "Location": "Line 6583 Position 71-102" - }, - { - "Id": "S3878", - "Message": "Remove this array creation and simply pass the elements.", - "Uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/Projects/Ember-MM/Ember%20Media%20Manager/frmMain.vb#L6881", - "Location": "Line 6881 Position 47-94" - }, - { - "Id": "S3878", - "Message": "Remove this array creation and simply pass the elements.", - "Uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/Projects/Ember-MM/Ember%20Media%20Manager/frmMain.vb#L6882", - "Location": "Line 6882 Position 47-130" - }, - { - "Id": "S3878", - "Message": "Remove this array creation and simply pass the elements.", - "Uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/Projects/Ember-MM/Ember%20Media%20Manager/frmMain.vb#L6883", - "Location": "Line 6883 Position 47-130" - }, - { - "Id": "S3878", - "Message": "Remove this array creation and simply pass the elements.", - "Uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/Projects/Ember-MM/Ember%20Media%20Manager/frmMain.vb#L6884", - "Location": "Line 6884 Position 47-132" - }, - { - "Id": "S3878", - "Message": "Remove this array creation and simply pass the elements.", - "Uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/Projects/Ember-MM/Ember%20Media%20Manager/frmMain.vb#L6885", - "Location": "Line 6885 Position 47-79" - }, - { - "Id": "S3878", - "Message": "Remove this array creation and simply pass the elements.", - "Uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/Projects/Ember-MM/Ember%20Media%20Manager/frmMain.vb#L7017", - "Location": "Line 7017 Position 47-93" - }, - { - "Id": "S3878", - "Message": "Remove this array creation and simply pass the elements.", - "Uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/Projects/Ember-MM/Ember%20Media%20Manager/frmMain.vb#L7018", - "Location": "Line 7018 Position 47-94" - }, - { - "Id": "S3878", - "Message": "Remove this array creation and simply pass the elements.", - "Uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/Projects/Ember-MM/Ember%20Media%20Manager/frmMain.vb#L7019", - "Location": "Line 7019 Position 47-83" - }, - { - "Id": "S3878", - "Message": "Remove this array creation and simply pass the elements.", - "Uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/Projects/Ember-MM/Ember%20Media%20Manager/frmMain.vb#L7020", - "Location": "Line 7020 Position 47-83" - }, - { - "Id": "S3878", - "Message": "Remove this array creation and simply pass the elements.", - "Uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/Projects/Ember-MM/Ember%20Media%20Manager/frmMain.vb#L7021", - "Location": "Line 7021 Position 47-80" - }, - { - "Id": "S3878", - "Message": "Remove this array creation and simply pass the elements.", - "Uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/Projects/Ember-MM/Ember%20Media%20Manager/frmMain.vb#L7022", - "Location": "Line 7022 Position 47-84" - }, - { - "Id": "S3878", - "Message": "Remove this array creation and simply pass the elements.", - "Uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/Projects/Ember-MM/Ember%20Media%20Manager/frmMain.vb#L7023", - "Location": "Line 7023 Position 47-80" - }, - { - "Id": "S3878", - "Message": "Remove this array creation and simply pass the elements.", - "Uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/Projects/Ember-MM/Ember%20Media%20Manager/frmMain.vb#L7024", - "Location": "Line 7024 Position 47-82" - }, - { - "Id": "S3878", - "Message": "Remove this array creation and simply pass the elements.", - "Uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/Projects/Ember-MM/Ember%20Media%20Manager/frmMain.vb#L7025", - "Location": "Line 7025 Position 47-80" - }, - { - "Id": "S3878", - "Message": "Remove this array creation and simply pass the elements.", - "Uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/Projects/Ember-MM/Ember%20Media%20Manager/frmMain.vb#L7026", - "Location": "Line 7026 Position 47-97" - }, - { - "Id": "S3878", - "Message": "Remove this array creation and simply pass the elements.", - "Uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/Projects/Ember-MM/Ember%20Media%20Manager/frmMain.vb#L7027", - "Location": "Line 7027 Position 47-101" - }, - { - "Id": "S3878", - "Message": "Remove this array creation and simply pass the elements.", - "Uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/Projects/Ember-MM/Ember%20Media%20Manager/frmMain.vb#L7028", - "Location": "Line 7028 Position 47-97" - }, - { - "Id": "S3878", - "Message": "Remove this array creation and simply pass the elements.", - "Uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/Projects/Ember-MM/Ember%20Media%20Manager/frmMain.vb#L7107", - "Location": "Line 7107 Position 43-137" - }, - { - "Id": "S3878", - "Message": "Remove this array creation and simply pass the elements.", - "Uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/Projects/Ember-MM/Ember%20Media%20Manager/frmMain.vb#L7109", - "Location": "Line 7109 Position 47-141" - }, - { - "Id": "S3878", - "Message": "Remove this array creation and simply pass the elements.", - "Uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/Projects/Ember-MM/Ember%20Media%20Manager/frmMain.vb#L7111", - "Location": "Line 7111 Position 43-75" - }, - { - "Id": "S3878", - "Message": "Remove this array creation and simply pass the elements.", - "Uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/Projects/Ember-MM/Ember%20Media%20Manager/frmMain.vb#L7193", - "Location": "Line 7193 Position 47-96" - }, - { - "Id": "S3878", - "Message": "Remove this array creation and simply pass the elements.", - "Uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/Projects/Ember-MM/Ember%20Media%20Manager/frmMain.vb#L7194", - "Location": "Line 7194 Position 47-130" - }, - { - "Id": "S3878", - "Message": "Remove this array creation and simply pass the elements.", - "Uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/Projects/Ember-MM/Ember%20Media%20Manager/frmMain.vb#L7195", - "Location": "Line 7195 Position 47-130" - }, - { - "Id": "S3878", - "Message": "Remove this array creation and simply pass the elements.", - "Uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/Projects/Ember-MM/Ember%20Media%20Manager/frmMain.vb#L7196", - "Location": "Line 7196 Position 47-134" - }, - { - "Id": "S3878", - "Message": "Remove this array creation and simply pass the elements.", - "Uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/Projects/Ember-MM/Ember%20Media%20Manager/frmMain.vb#L7197", - "Location": "Line 7197 Position 47-79" - } - ] -} \ No newline at end of file diff --git a/analyzers/its/expected/Ember-MM/S3878-generic.EmberCore.NMT.json b/analyzers/its/expected/Ember-MM/S3878-generic.EmberCore.NMT.json deleted file mode 100644 index 4695bde4c7c..00000000000 --- a/analyzers/its/expected/Ember-MM/S3878-generic.EmberCore.NMT.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "Issues": [ - { - "Id": "S3878", - "Message": "Remove this array creation and simply pass the elements.", - "Uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/Projects/Ember-MM/Addons/generic.EmberCore.NMT/dlgNMTMovies.vb#L127", - "Location": "Line 127 Position 56-126" - }, - { - "Id": "S3878", - "Message": "Remove this array creation and simply pass the elements.", - "Uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/Projects/Ember-MM/Addons/generic.EmberCore.NMT/dlgNMTMovies.vb#L133", - "Location": "Line 133 Position 56-129" - }, - { - "Id": "S3878", - "Message": "Remove this array creation and simply pass the elements.", - "Uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/Projects/Ember-MM/Addons/generic.EmberCore.NMT/dlgNMTMovies.vb#L916", - "Location": "Line 916 Position 38-59" - }, - { - "Id": "S3878", - "Message": "Remove this array creation and simply pass the elements.", - "Uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/Projects/Ember-MM/Addons/generic.EmberCore.NMT/dlgNMTMovies.vb#L948", - "Location": "Line 948 Position 40-62" - } - ] -} \ No newline at end of file diff --git a/analyzers/its/expected/Ember-MM/S3878-multi.EmberExtras.json b/analyzers/its/expected/Ember-MM/S3878-multi.EmberExtras.json deleted file mode 100644 index 430cd0ebe95..00000000000 --- a/analyzers/its/expected/Ember-MM/S3878-multi.EmberExtras.json +++ /dev/null @@ -1,82 +0,0 @@ -{ - "Issues": [ - { - "Id": "S3878", - "Message": "Remove this array creation and simply pass the elements.", - "Uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/Projects/Ember-MM/Addons/multi.EmberExtras/frmAVCodecEditor.vb#L12", - "Location": "Line 12 Position 50-100" - }, - { - "Id": "S3878", - "Message": "Remove this array creation and simply pass the elements.", - "Uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/Projects/Ember-MM/Addons/multi.EmberExtras/frmAVCodecEditor.vb#L24", - "Location": "Line 24 Position 50-100" - }, - { - "Id": "S3878", - "Message": "Remove this array creation and simply pass the elements.", - "Uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/Projects/Ember-MM/Addons/multi.EmberExtras/frmAVCodecEditor.vb#L40", - "Location": "Line 40 Position 46-87" - }, - { - "Id": "S3878", - "Message": "Remove this array creation and simply pass the elements.", - "Uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/Projects/Ember-MM/Addons/multi.EmberExtras/frmAVCodecEditor.vb#L47", - "Location": "Line 47 Position 46-87" - }, - { - "Id": "S3878", - "Message": "Remove this array creation and simply pass the elements.", - "Uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/Projects/Ember-MM/Addons/multi.EmberExtras/frmGenresEditor.vb#L160", - "Location": "Line 160 Position 47-74" - }, - { - "Id": "S3878", - "Message": "Remove this array creation and simply pass the elements.", - "Uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/Projects/Ember-MM/Addons/multi.EmberExtras/frmGenresEditor.vb#L173", - "Location": "Line 173 Position 45-68" - }, - { - "Id": "S3878", - "Message": "Remove this array creation and simply pass the elements.", - "Uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/Projects/Ember-MM/Addons/multi.EmberExtras/frmGenresEditor.vb#L34", - "Location": "Line 34 Position 34-57" - }, - { - "Id": "S3878", - "Message": "Remove this array creation and simply pass the elements.", - "Uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/Projects/Ember-MM/Addons/multi.EmberExtras/frmGenresEditor.vb#L48", - "Location": "Line 48 Position 59-91" - }, - { - "Id": "S3878", - "Message": "Remove this array creation and simply pass the elements.", - "Uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/Projects/Ember-MM/Addons/multi.EmberExtras/frmGenresEditor.vb#L57", - "Location": "Line 57 Position 59-91" - }, - { - "Id": "S3878", - "Message": "Remove this array creation and simply pass the elements.", - "Uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/Projects/Ember-MM/Addons/multi.EmberExtras/frmMediaSourcesEditor.vb#L127", - "Location": "Line 127 Position 47-88" - }, - { - "Id": "S3878", - "Message": "Remove this array creation and simply pass the elements.", - "Uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/Projects/Ember-MM/Addons/multi.EmberExtras/frmMediaSourcesEditor.vb#L17", - "Location": "Line 17 Position 55-105" - }, - { - "Id": "S3878", - "Message": "Remove this array creation and simply pass the elements.", - "Uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/Projects/Ember-MM/Addons/multi.EmberExtras/frmMediaSourcesEditor.vb#L30", - "Location": "Line 30 Position 52-109" - }, - { - "Id": "S3878", - "Message": "Remove this array creation and simply pass the elements.", - "Uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/Projects/Ember-MM/Addons/multi.EmberExtras/frmMediaSourcesEditor.vb#L37", - "Location": "Line 37 Position 48-89" - } - ] -} \ No newline at end of file diff --git a/analyzers/its/expected/Ember-MM/S3878-scraper.EmberCore.XML.json b/analyzers/its/expected/Ember-MM/S3878-scraper.EmberCore.XML.json deleted file mode 100644 index 36cda30d1ec..00000000000 --- a/analyzers/its/expected/Ember-MM/S3878-scraper.EmberCore.XML.json +++ /dev/null @@ -1,52 +0,0 @@ -{ - "Issues": [ - { - "Id": "S3878", - "Message": "Remove this array creation and simply pass the elements.", - "Uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/Projects/Ember-MM/Addons/scraper.EmberCore.XML/XMLScraper/MediaTags/AlbumTag.vb#L304", - "Location": "Line 304 Position 97-138" - }, - { - "Id": "S3878", - "Message": "Remove this array creation and simply pass the elements.", - "Uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/Projects/Ember-MM/Addons/scraper.EmberCore.XML/XMLScraper/MediaTags/AlbumTag.vb#L411", - "Location": "Line 411 Position 97-138" - }, - { - "Id": "S3878", - "Message": "Remove this array creation and simply pass the elements.", - "Uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/Projects/Ember-MM/Addons/scraper.EmberCore.XML/XMLScraper/MediaTags/ArtistTag.vb#L291", - "Location": "Line 291 Position 97-138" - }, - { - "Id": "S3878", - "Message": "Remove this array creation and simply pass the elements.", - "Uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/Projects/Ember-MM/Addons/scraper.EmberCore.XML/XMLScraper/MediaTags/MovieTag.vb#L446", - "Location": "Line 446 Position 97-138" - }, - { - "Id": "S3878", - "Message": "Remove this array creation and simply pass the elements.", - "Uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/Projects/Ember-MM/Addons/scraper.EmberCore.XML/XMLScraper/MediaTags/MusicVideoTag.vb#L160", - "Location": "Line 160 Position 97-138" - }, - { - "Id": "S3878", - "Message": "Remove this array creation and simply pass the elements.", - "Uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/Projects/Ember-MM/Addons/scraper.EmberCore.XML/XMLScraper/MediaTags/PersonInfo.vb#L272", - "Location": "Line 272 Position 97-138" - }, - { - "Id": "S3878", - "Message": "Remove this array creation and simply pass the elements.", - "Uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/Projects/Ember-MM/Addons/scraper.EmberCore.XML/XMLScraper/MediaTags/TVEpisodeTag.vb#L224", - "Location": "Line 224 Position 97-138" - }, - { - "Id": "S3878", - "Message": "Remove this array creation and simply pass the elements.", - "Uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/Projects/Ember-MM/Addons/scraper.EmberCore.XML/XMLScraper/MediaTags/TVShow.vb#L261", - "Location": "Line 261 Position 97-138" - } - ] -} \ No newline at end of file diff --git a/analyzers/its/expected/Nancy/S3878-Nancy.ViewEngines.Razor.BuildProviders-net452.json b/analyzers/its/expected/Nancy/S3878-Nancy.ViewEngines.Razor.BuildProviders-net452.json deleted file mode 100644 index bdc4d32fa6c..00000000000 --- a/analyzers/its/expected/Nancy/S3878-Nancy.ViewEngines.Razor.BuildProviders-net452.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "Issues": [ - { - "Id": "S3878", - "Message": "Remove this array creation and simply pass the elements.", - "Uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/Projects/Nancy/src/Nancy.ViewEngines.Razor.BuildProviders/NancyCSharpRazorBuildProvider.cs#L47", - "Location": "Line 47 Position 104-175" - }, - { - "Id": "S3878", - "Message": "Remove this array creation and simply pass the elements.", - "Uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/Projects/Nancy/src/Nancy.ViewEngines.Razor.BuildProviders/NancyCSharpRazorBuildProvider.cs#L57", - "Location": "Line 57 Position 106-177" - } - ] -} \ No newline at end of file diff --git a/analyzers/src/SonarAnalyzer.CSharp/Rules/ArrayPassedAsParams.cs b/analyzers/src/SonarAnalyzer.CSharp/Rules/ArrayPassedAsParams.cs index c37342e0a08..9b6f2d7ad63 100644 --- a/analyzers/src/SonarAnalyzer.CSharp/Rules/ArrayPassedAsParams.cs +++ b/analyzers/src/SonarAnalyzer.CSharp/Rules/ArrayPassedAsParams.cs @@ -26,22 +26,22 @@ public sealed class ArrayPassedAsParams : ArrayPassedAsParamsBase Language => CSharpFacade.Instance; protected override SyntaxKind[] ExpressionKinds { get; } = - { + [ SyntaxKind.ObjectCreationExpression, SyntaxKind.InvocationExpression, SyntaxKindEx.ImplicitObjectCreationExpression - }; + ]; protected override ArgumentSyntax LastArgumentIfArrayCreation(SyntaxNode expression) => - LastArgumentIfArrayCreation(ArgumentList(expression)); - - private static ArgumentSyntax LastArgumentIfArrayCreation(BaseArgumentListSyntax argumentList) => - argumentList is { Arguments: { Count: > 0 } arguments } + ArgumentList(expression) is { Arguments: { Count: > 0 } arguments } && arguments.Last() is var lastArgument && IsArrayCreation(lastArgument.Expression) ? lastArgument : null; + protected override ITypeSymbol ArrayElementType(ArgumentSyntax argument, SemanticModel model) => + model.GetTypeInfo(((ArrayCreationExpressionSyntax)argument.Expression).Type.ElementType).Type; + private static BaseArgumentListSyntax ArgumentList(SyntaxNode expression) => expression switch { diff --git a/analyzers/src/SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs b/analyzers/src/SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs index 1fe3f0f0347..79e397e2f03 100644 --- a/analyzers/src/SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs +++ b/analyzers/src/SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs @@ -25,33 +25,43 @@ public abstract class ArrayPassedAsParamsBase : Sona where TArgumentNode : SyntaxNode { private const string DiagnosticId = "S3878"; - protected override string MessageFormat => "Remove this array creation and simply pass the elements."; - - private readonly DiagnosticDescriptor rule; protected abstract TSyntaxKind[] ExpressionKinds { get; } protected abstract TArgumentNode LastArgumentIfArrayCreation(SyntaxNode expression); + protected abstract ITypeSymbol ArrayElementType(TArgumentNode argument, SemanticModel model); + + protected override string MessageFormat => "Remove this array creation and simply pass the elements."; - protected ArrayPassedAsParamsBase() : base(DiagnosticId) => - rule = Language.CreateDescriptor(DiagnosticId, MessageFormat); + protected ArrayPassedAsParamsBase() : base(DiagnosticId) {} protected sealed override void Initialize(SonarAnalysisContext context) => context.RegisterNodeAction(Language.GeneratedCodeRecognizer, c => { if (LastArgumentIfArrayCreation(c.Node) is { } lastArgument - && ParameterSymbol(c.SemanticModel, c.Node, lastArgument) is { IsParams: true } param + && c.SemanticModel.GetSymbolInfo(c.Node).Symbol is IMethodSymbol methodSymbol + && Language.MethodParameterLookup(c.Node, methodSymbol) is { } parameterLookup + && parameterLookup.TryGetSymbol(lastArgument, out var param) + && param is { IsParams: true } + && !IsArrayOfCandidateTypes(lastArgument, parameterLookup, param, c.SemanticModel) && !IsJaggedArrayParam(param)) { - c.ReportIssue(rule, lastArgument.GetLocation()); + c.ReportIssue(Rule, lastArgument.GetLocation()); } }, ExpressionKinds); - private IParameterSymbol ParameterSymbol(SemanticModel model, SyntaxNode invocation, TArgumentNode argument) => - model.GetSymbolInfo(invocation).Symbol is IMethodSymbol methodSymbol - && Language.MethodParameterLookup(invocation, methodSymbol).TryGetSymbol(argument, out var param) - ? param - : null; - private static bool IsJaggedArrayParam(IParameterSymbol param) => param.Type is IArrayTypeSymbol { ElementType: IArrayTypeSymbol }; + + private bool IsArrayOfCandidateTypes(TArgumentNode lastArgument, IMethodParameterLookup parameterLookup, IParameterSymbol param, SemanticModel model) + { + return param.Type is IArrayTypeSymbol array + && (array.ElementType.Is(KnownType.System_Array) + || (array.ElementType.Is(KnownType.System_Object) && !ParamArgumentsAreReferenceTypeArrays(lastArgument, parameterLookup, model))); + + bool ParamArgumentsAreReferenceTypeArrays(TArgumentNode lastArgument, IMethodParameterLookup parameterLookup, SemanticModel model) => + ArrayElementType(lastArgument, model) is { IsReferenceType: true } elementType + && !elementType.Is(KnownType.System_Object) + && parameterLookup.TryGetSyntax(param, out var arguments) + && arguments.Length is 1; + } } diff --git a/analyzers/src/SonarAnalyzer.VisualBasic/Rules/ArrayPassedAsParams.cs b/analyzers/src/SonarAnalyzer.VisualBasic/Rules/ArrayPassedAsParams.cs index d0d699d4e45..d78b9a41689 100644 --- a/analyzers/src/SonarAnalyzer.VisualBasic/Rules/ArrayPassedAsParams.cs +++ b/analyzers/src/SonarAnalyzer.VisualBasic/Rules/ArrayPassedAsParams.cs @@ -26,15 +26,22 @@ public sealed class ArrayPassedAsParams : ArrayPassedAsParamsBase Language => VisualBasicFacade.Instance; protected override SyntaxKind[] ExpressionKinds { get; } = - { + [ SyntaxKind.ObjectCreationExpression, SyntaxKind.InvocationExpression - }; + ]; protected override ArgumentSyntax LastArgumentIfArrayCreation(SyntaxNode expression) => - GetLastArgumentIfArrayCreation(GetArgumentListFromExpression(expression)); + ArgumentList(expression) is { Arguments: { Count: > 0 } arguments } + && arguments.Last() is var lastArgument + && IsArrayCreation(lastArgument.GetExpression()) + ? lastArgument + : null; - private static ArgumentListSyntax GetArgumentListFromExpression(SyntaxNode expression) => + protected override ITypeSymbol ArrayElementType(ArgumentSyntax argument, SemanticModel model) => + model.GetTypeInfo(((ArrayCreationExpressionSyntax)argument.GetExpression()).Type).Type; + + private static ArgumentListSyntax ArgumentList(SyntaxNode expression) => expression switch { ObjectCreationExpressionSyntax { } creation => creation.ArgumentList, @@ -42,16 +49,12 @@ private static ArgumentListSyntax GetArgumentListFromExpression(SyntaxNode expre _ => null }; - private static ArgumentSyntax GetLastArgumentIfArrayCreation(ArgumentListSyntax argumentList) => - argumentList is { Arguments: { Count: > 0 } arguments } - && arguments.Last() is var lastArgument - && IsArrayCreation(lastArgument.GetExpression()) - ? lastArgument - : null; - private static bool IsArrayCreation(ExpressionSyntax expression) => - expression - is ArrayCreationExpressionSyntax { Initializer.Initializers.Count: > 0 } - or ArrayCreationExpressionSyntax { ArrayBounds: null } - or CollectionInitializerSyntax; + expression switch + { + ArrayCreationExpressionSyntax { Initializer.Initializers.Count: > 0 } => true, + ArrayCreationExpressionSyntax { ArrayBounds: null } => true, + CollectionInitializerSyntax => true, + _ => false + }; } diff --git a/analyzers/tests/SonarAnalyzer.Test/TestCases/ArrayPassedAsParams.cs b/analyzers/tests/SonarAnalyzer.Test/TestCases/ArrayPassedAsParams.cs index 54823b4e337..b718a8fc1a3 100644 --- a/analyzers/tests/SonarAnalyzer.Test/TestCases/ArrayPassedAsParams.cs +++ b/analyzers/tests/SonarAnalyzer.Test/TestCases/ArrayPassedAsParams.cs @@ -1,4 +1,5 @@ using System; +using System.Globalization; public class Program { @@ -77,25 +78,28 @@ public class Repro6894 //Reproducer for https://github.com/SonarSource/sonar-dotnet/issues/6894 public void Method(params object[] args) { } + public void MethodMixed(int i, params object[] args) { } public void MethodArray(params Array[] args) { } public void MethodJaggedArray(params int[][] args) { } - public void CallMethod() + public void CallMethod(dynamic d) { - Method(new String[] { "1", "2" }); // Noncompliant, TP. Elements in args: ["1", "2"] - // The argument given for a parameter array can be a single expression that is implicitly convertible (§10.2) to the parameter array type. - // In this case, the parameter array acts precisely like a value parameter. - // see: https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/classes#14625-parameter-arrays - Method(new object[] { new int[] { 1, 2} }); // Noncompliant. TP Elements in args: [System.Int32[]] - Method(new int[] { 1, 2, 3, }); // Noncompliant, FP. Elements in args: [System.Int32[]] - Method(new String[] { "1", "2" }, new String[] { "1", "2"}); // Noncompliant, FP. Elements in args: [System.String[], System.String[]] - // ^^^^^^^^^^^^^^^^^^^^^^^^ - Method(new String[] { "1", "2"}, new int[] { 1, 2}); // Noncompliant, FP. Elements in args: pSystem.String[], System.Int32[]] - // ^^^^^^^^^^^^^^^^^ - MethodArray(new String[] { "1", "2" }, new String[] { "1", "2" }); // Noncompliant, FP. Elements in args: [System.String[], System.String[]] - MethodArray(new int[] { 1, 2 }, new int[] { 1, 2 }); // Noncompliant, FP. Elements in args: [System.Int32[], System.Int32[]] - - MethodJaggedArray(new int[] { 1, 2 }); // Compliant: jagged array [System.Object[]] + Method(new String[] { "1", "2" }); // Noncompliant, elements in args: ["1", "2"] + // The argument given for a parameter array can be a single expression that is implicitly convertible (§10.2) to the parameter array type. + // In this case, the parameter array acts precisely like a value parameter. + // see: https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/classes#14625-parameter-arrays + Method(new object[] { new int[] { 1, 2 } }); // FN, elements in args: [System.Int32[]] + Method(new int[] { 1, 2, 3, }); // Compliant, Elements in args: [System.Int32[]] + Method(new String[] { "1", "2" }, new String[] { "1", "2" }); // Compliant, elements in args: [System.String[], System.String[]] + Method(new String[] { "1", "2"}, new int[] { 1, 2}); // Compliant, elements in args: [System.String[], System.Int32[]] + MethodMixed(1, new String[] { "1", "2" }); // Noncompliant + MethodArray(new String[] { "1", "2" }, new String[] { "1", "2" }); // Compliant, elements in args: [System.String[], System.String[]] + MethodArray(new int[] { 1, 2 }, new int[] { 1, 2 }); // Compliant, elements in args: [System.Int32[], System.Int32[]] + + MethodJaggedArray(new int[] { 1, 2 }); // Compliant: jagged array [System.Object[]] + Method(d); // Compliant + Method("Hello", 2); // Compliant + string.Format(CultureInfo.InvariantCulture, "{0}.{1}", new object[] { "", new object() }); } } @@ -103,10 +107,10 @@ public void CallMethod() public class Repro6893 { public void Method(int a, params object[] argumentArray) { } - + public void CallMethod() { - Method(a: 1, argumentArray: new int[] { 1, 2 }); // Noncompliant FP + Method(a: 1, argumentArray: new int[] { 1, 2 }); // Compliant } } diff --git a/analyzers/tests/SonarAnalyzer.Test/TestCases/ArrayPassedAsParams.vb b/analyzers/tests/SonarAnalyzer.Test/TestCases/ArrayPassedAsParams.vb index 7156b9a9df3..4208c5ceb14 100644 --- a/analyzers/tests/SonarAnalyzer.Test/TestCases/ArrayPassedAsParams.vb +++ b/analyzers/tests/SonarAnalyzer.Test/TestCases/ArrayPassedAsParams.vb @@ -64,3 +64,31 @@ Public Class MyOtherClass Public Sub New(ByVal a As Integer(), ParamArray args As Integer()) End Sub End Class + +Public Class Repro6894 + 'Reproducer for https://github.com/SonarSource/sonar-dotnet/issues/6894 + + Public Sub Method(ParamArray args As Object()) + End Sub + + Public Sub MethodArray(ParamArray args As Array()) + End Sub + + Public Sub MethodJaggedArray(ParamArray args As Integer()()) + End Sub + + Public Sub CallMethod() + Method(New String() {"1", "2"}) ' Noncompliant, elements in args: ["1", "2"] + ' The argument given for a parameter array can be a single expression that is implicitly convertible (§10.2) to the parameter array type. + ' In this case, the parameter array acts precisely like a value parameter. + ' see: https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/classes#14625-parameter-arrays + Method(New Object() {New Integer() {1, 2}}) ' FN, elements in args: [System.Int32[]] + Method(New Integer() {1, 2, 3}) ' Compliant, Elements in args: [System.Int32[]] + Method(New String() {"1", "2"}, New String() {"1", "2"}) ' Compliant, elements in args: [System.String[], System.String[]] + Method(New String() {"1", "2"}, New Integer() {1, 2}) ' Compliant, elements in args: [System.String[], System.Int32[]] + MethodArray(New String() {"1", "2"}, New String() {"1", "2"}) ' Compliant, elements in args: [System.String[], System.String[]] + MethodArray(New Integer() {1, 2}, New Integer() {1, 2}) ' Compliant, elements in args: [System.Int32[], System.Int32[]] + + MethodJaggedArray(New Integer() {1, 2}) ' Compliant: jagged array [System.Object[]] + End Sub +End Class