Skip to content

Add new AvoidUsingArrayList rule#2174

Open
iRon7 wants to merge 19 commits intoPowerShell:mainfrom
iRon7:#2147AvoidArrayList
Open

Add new AvoidUsingArrayList rule#2174
iRon7 wants to merge 19 commits intoPowerShell:mainfrom
iRon7:#2147AvoidArrayList

Conversation

@iRon7
Copy link
Copy Markdown

@iRon7 iRon7 commented Apr 15, 2026

to warn when the ArrayList class is used in PowerShell scripts (#2147).
Add tests for both violations and non-violations of this rule.
Update documentation to include the new rule and its guidelines.
(For this, I followed Liam Peters' blog post, also note that this is my first formal C# contribution to any GitHub project)

PR Summary

PR Checklist

…lass is used in PowerShell scripts. Added tests for both violations and non-violations of this rule. Updated documentation to include the new rule and its guidelines.
Copy link
Copy Markdown
Contributor

@liamjpeters liamjpeters left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👋 @iRon7 - great that you've taken the plunge, and thanks for being one of 3 people to read my blog post (myself included) 😊.

I hope you don't mind, but I took a look over the PR and left some suggestions. I'm happy to discuss or elaborate on anything.

Thanks,

Comment thread .vscode/settings.json Outdated
Comment thread docs/Rules/AvoidUsingArrayList.md Outdated
Comment thread docs/Rules/AvoidUsingArrayList.md Outdated
Comment thread docs/Rules/AvoidUsingArrayList.md Outdated
Comment thread docs/Rules/README.md Outdated
Comment thread docs/Rules/AvoidUsingArrayList.md Outdated
Comment thread Rules/AvoidUsingArrayList.cs Outdated
Comment thread Tests/Rules/AvoidUsingArrayList.tests.ps1 Outdated
Comment thread Tests/Rules/AvoidUsingArrayList.tests.ps1 Outdated
Comment thread Rules/AvoidUsingArrayList.cs Outdated
@iRon7
Copy link
Copy Markdown
Author

iRon7 commented Apr 16, 2026

Liam,
Thanks you for your help and feedback, I learned a lot!

One general thought that came up:
Instead of testing all things afterwards (and still making a lot of copy-pasta misstakes, as I did), a small cmdlet like:

New-CSRule -Name <name> -Description <Description> -Message <Message> -Configurable ...

That prepares things as a stripped .cs rule and .ps1 test (without logic) along with the Strings.resx and the readme updates etc. could in my opinion be helpful and prevent a lot of issues at forehand.

@liamjpeters
Copy link
Copy Markdown
Contributor

Liam, Thanks you for your help and feedback, I learned a lot!

One general thought that came up: Instead of testing all things afterwards (and still making a lot of copy-pasta misstakes, as I did), a small cmdlet like:

New-CSRule -Name <name> -Description <Description> -Message <Message> -Configurable ...

That prepares things as a stripped .cs rule and .ps1 test (without logic) along with the Strings.resx and the readme updates etc. could in my opinion be helpful and prevent a lot of issues at forehand.

There's the RuleMaker module. I've tried it before with mixed results. It needs a little TLC (copyright is outdated, doesn't append to string resources super well etc).

@iRon7
Copy link
Copy Markdown
Author

iRon7 commented Apr 18, 2026

After several additional commits based on the valuable feedback from Liam, I consider this PR as final now.
Meaning that I am not planned to make addition changes (unless new issues come up).
Please review/approve this PR.

Copilot AI review requested due to automatic review settings May 5, 2026 17:52
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

This PR introduces a new PSScriptAnalyzer rule to warn on usage of System.Collections.ArrayList in PowerShell scripts, along with accompanying tests and documentation updates.

Changes:

  • Adds the AvoidUsingArrayList rule implementation and localized strings.
  • Adds Pester tests covering violations, non-violations, and enable/disable behavior.
  • Updates rules documentation to list and describe the new rule.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
docs/Rules/README.md Adds the new rule to the rules index table.
docs/Rules/AvoidUsingArrayList.md Introduces end-user documentation and examples for the new rule.
Tests/Rules/AvoidUsingArrayList.tests.ps1 Adds Pester coverage for rule violations and configuration behavior.
Rules/Strings.resx Adds localized name/description/message resources for the new rule.
Rules/AvoidUsingArrayList.cs Implements the new analyzer rule logic.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

/// <returns>A an enumerable type containing the violations</returns>
public override IEnumerable<DiagnosticRecord> AnalyzeScript(Ast ast, string fileName)
{
if (ast == null) { throw new ArgumentNullException(Strings.NullAstErrorMessage); }
Comment on lines +42 to +60
// If there is an using statement for the Collections namespace, check for the full typename.
// Otherwise also check for the bare ArrayList name.
Regex arrayListName = null;
var sbAst = ast as ScriptBlockAst;
foreach (UsingStatementAst usingAst in sbAst.UsingStatements)
{
if (
usingAst.UsingStatementKind == UsingStatementKind.Namespace &&
(
usingAst.Name.Value.Equals("Collections", StringComparison.OrdinalIgnoreCase) ||
usingAst.Name.Value.Equals("System.Collections", StringComparison.OrdinalIgnoreCase)
)
)
{
arrayListName = new Regex(@"^((System\.)?Collections\.)?ArrayList$", RegexOptions.IgnoreCase);
break;
}
}
if (arrayListName == null) { arrayListName = new Regex(@"^(System\.)?Collections\.ArrayList$", RegexOptions.IgnoreCase); }
Comment on lines +45 to +57
var sbAst = ast as ScriptBlockAst;
foreach (UsingStatementAst usingAst in sbAst.UsingStatements)
{
if (
usingAst.UsingStatementKind == UsingStatementKind.Namespace &&
(
usingAst.Name.Value.Equals("Collections", StringComparison.OrdinalIgnoreCase) ||
usingAst.Name.Value.Equals("System.Collections", StringComparison.OrdinalIgnoreCase)
)
)
{
arrayListName = new Regex(@"^((System\.)?Collections\.)?ArrayList$", RegexOptions.IgnoreCase);
break;
)
)
{
arrayListName = new Regex(@"^((System\.)?Collections\.)?ArrayList$", RegexOptions.IgnoreCase);
break;
}
}
if (arrayListName == null) { arrayListName = new Regex(@"^(System\.)?Collections\.ArrayList$", RegexOptions.IgnoreCase); }
Comment on lines +1 to +6
---
description: Avoid using ArrayList
ms.date: 04/16/2025
ms.topic: reference
title: AvoidUsingArrayList
---
Comment on lines +192 to +201

It "Out of the namespace scope" {
$scriptDefinition = $usingGeneric + {
$List = New-Object ArrayList
$List = [ArrayList](1,2,3)
$List = [ArrayList]@(1,2,3)
$List = [ArrayList]::new()
}.ToString()
$violations = Invoke-ScriptAnalyzer -ScriptDefinition $scriptDefinition -IncludeRule @($ruleName)
$violations | Should -BeNullOrEmpty
Copy link
Copy Markdown
Member

@andyleejordan andyleejordan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for sticking with this one, @iRon7, and thanks @liamjpeters for the thorough review — I read through everything you both worked through.

Most of the review threads have been addressed, which is great. There is one point I want to push back on before merging, plus one small follow-up.

Opt-in by default. Liam asked for Enable = false and you went with Enable = true to head off the pipeline-pollution gotcha. That gotcha is real and your reasoning is fair, but I think Liam's ask is the right call here, for the same reasons he laid out: existing scripts that intentionally use [ArrayList] (Add-Type wrappers, certain interop scenarios, lots of legacy code in the wild) would suddenly emit warnings on upgrade, and PSSA is heavily wired into CI/CD where new diagnostics turn into build failures. Our convention for new rules has been opt-in for that reason — see AvoidExclaimOperator, AvoidUsingDoubleQuotesForConstantString, AvoidSemicolonsAsLineTerminators, UseConstrainedLanguageMode, UseConsistentParametersKind. The pipeline-pollution discoverability problem is better addressed by the rule existing and being toggleable than by enabling it for everyone. Could you flip Enable = true to Enable = false in the constructor and update docs/Rules/README.md so the "Enabled by Default" column reads "No"? Thanks!

One small follow-up:

  • Tests/Rules/AvoidUsingArrayList.tests.ps1 has solid coverage for the match cases. Would you mind adding one negative case for [ArrayList]::new() without a using namespace System.Collections (so the regex's not-prefixed branch is exercised against a non-matching input)? Just to keep that branch covered.

Once those are settled, this is good to go from my side.

Drafted by Copilot (Claude Opus 4.7)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants