PSScriptAnalyzer icon indicating copy to clipboard operation
PSScriptAnalyzer copied to clipboard

Added PSUseFullyQualifiedCmdletNames rule with fix capabilities

Open genXdev opened this issue 5 months ago • 7 comments

PR Summary

Add new diagnostic rule PSUseFullyQualifiedCmdletNames to replace aliases and unqualified cmdlet names with fully qualified versions (e.g., ModuleName\CmdletName).

This rule addresses a common pain point in PowerShell scripting where cmdlet names without module prefixes can lead to ambiguity, especially in environments with multiple modules exporting similarly named cmdlets. By enforcing fully qualified names, the rule provides the following benefits:

  • Automatic module loading: PowerShell will auto-load the specified module if it's not already loaded when the qualified cmdlet is invoked.
  • Enhanced safety: Prevents accidental execution of the wrong cmdlet due to name conflicts across modules, reducing runtime errors.
  • Improved readability and maintainability: Explicit module references make the code's intent clearer, aiding in code reviews and long-term upkeep.
  • Better portability: Scripts become more reliable across different PowerShell environments or versions where module availability might vary.
  • Reduced ambiguity: Helps avoid issues with alias resolution or unqualified names that might resolve differently based on session state.
  • Facilitates advanced editor features: Supports better IntelliSense, auto-completion, and static analysis in tools like VS Code.

This change directly relates to PowerShell/PSScriptAnalyzer#2123, where the PowerShell extension for VS Code has been reported to unexpectedly remove module prefixes (e.g., converting MicrosoftTeams\Get-CsLisCivicAddress to Get-CsLisCivicAddress) during code formatting, potentially introducing ambiguities and runtime issues in scripts. This new rule enables users to automatically add or restore fully qualified cmdlet names during analysis or formatting, helping to repair the damage caused by such removals and promoting safer, more explicit scripting practices.

Implemented in Rules/UseFullyQualifiedCmdletNames.cs, with accompanying tests in the test suite to verify replacement logic for aliases (e.g., lsMicrosoft.PowerShell.Management\Get-ChildItem) and unqualified cmdlets.

PR Checklist

genXdev avatar Aug 19 '25 04:08 genXdev

@microsoft-github-policy-service agree

I have sole ownership of intellectual property rights to my Submissions and I am not making Submissions in the course of work for my employer.

@microsoft-github-policy-service agree company="Microsoft"

genXdev avatar Aug 19 '25 07:08 genXdev

@genXdev please read the following Contributor License Agreement(CLA). If you agree with the CLA, please reply with the following information.

@microsoft-github-policy-service agree [company="{your company}"]

Options:

  • (default - no company specified) I have sole ownership of intellectual property rights to my Submissions and I am not making Submissions in the course of work for my employer.
@microsoft-github-policy-service agree
  • (when company given) I am making Submissions in the course of work for my employer (or my employer has intellectual property rights in my Submissions by contract or applicable law). I have permission from my employer to make Submissions and enter into this Agreement on behalf of my employer. By signing below, the defined term “You” includes me and my employer.
@microsoft-github-policy-service agree company="Microsoft"

Contributor License Agreement

@microsoft-github-policy-service agree

I have sole ownership of intellectual property rights to my Submissions and I am not making Submissions in the course of work for my employer.

@microsoft-github-policy-service agree company="Microsoft"

genXdev avatar Aug 19 '25 07:08 genXdev

👋 Hey @genXdev, could you please add your tests and docs to the PR for your new rule? If you're still working on it, please title the PR as WIP and mark as draft.

My initial thoughts on this:

  • This shouldn't be a default rule. I would expect most people would find this behaviour undesirable - it makes even simple scripts incredibly verbose. My suggestion would be to make it a configurable rule, disabled by default. This allows those that want this behaviour to opt into it and still benefit from your hard work. See something like UseSingularNouns as an example for making a rule configurable and offering settings.

  • If you made the rule configurable, you could add a setting for something like IgnoredModules \ IgnoredNamespaces. For commands in those modules (i.e. Microsoft.PowerShell.Management and Microsoft.PowerShell.Utility) the rule would not expand them into their fully qualified names. This could help with some of the verbosity while still retaining the value you're after.

  • Looking through the context of the issue raised in the VSCode-PowerShell project, I'm not sure that this rule resolves that original issue (not that that's your stated goal). I think that, depending on order of rule execution/formatting, if your rule runs first to expand the command names to their fully qualified names - then the faulty rule runs to "correct" them - your hard work is undone. If it runs afterward it's simply masking that erroneous behaviour which will still be happening.

  • The rule name is inconsistent in the PR title (...FullQualified... vs ...FullyQualified...).

liamjpeters avatar Aug 19 '25 09:08 liamjpeters

👋 Hey @genXdev, could you please add your tests and docs to the PR for your new rule? If you're still working on it, please title the PR as WIP and mark as draft.

I have committed them; https://github.com/PowerShell/PSScriptAnalyzer/commit/971f02bb1ee6546eedce761570e75630619e5263

My initial thoughts on this:

  • This shouldn't be a default rule. I would expect most people would find this behaviour undesirable - it makes even simple scripts incredibly verbose. My suggestion would be to make it a configurable rule, disabled by default. This allows those that want this behaviour to opt into it and still benefit from your hard work. See something like UseSingularNouns as an example for making a rule configurable and offering settings.

Whatever you think is best.

  • If you made the rule configurable, you could add a setting for something like IgnoredModules \ IgnoredNamespaces. For commands in those modules (i.e. Microsoft.PowerShell.Management and Microsoft.PowerShell.Utility) the rule would not expand them into their fully qualified names. This could help with some of the verbosity while still retaining the value you're after.

If this still desirable, I'll add them, let me know.

  • Looking through the context of the issue raised in the VSCode-PowerShell project, I'm not sure that this rule resolves that original issue (not that that's your stated goal). I think that, depending on order of rule execution/formatting, if your rule runs first to expand the command names to their fully qualified names - then the faulty rule runs to "correct" them - your hard work is undone. If it runs afterward it's simply masking that erroneous behaviour which will still be happening.

Haven't tested that, for fixing damage, I only enabled my new rule with -Fix

Maybe not the place to mention it, but analyzing 100+ script files at once, I sometimes see 'Collection modified' concurrency exceptions. I think it is because of SSADictionary, VariablesDictionary, VariableAnalysisDetails in .\Engine\VariableAnalysisBase.cs

genXdev avatar Aug 19 '25 22:08 genXdev

Also added 'IgnoredModules' parameter and updated tests and docs accordingly

genXdev avatar Aug 21 '25 18:08 genXdev

Sorry it's taken so long. Agree to not enable it by default but otherwise happy to have it. I know some module owners do this for performance reasons and replace commands with the full version as part of their build process. @genXdev can you resolve the merge conflict please, I will then greenlight the running of the CI test suite

bergmeister avatar Dec 02 '25 22:12 bergmeister

Sorry it's taken so long. Agree to not enable it by default but otherwise happy to have it. I know some module owners do this for performance reasons and replace commands with the full version as part of their build process. @genXdev can you resolve the merge conflict please, I will then greenlight the running of the CI test suite

Hi @bergmeister,

I've resolved the merge conflict. The conflict was in Strings.resx where my UseFullyQualifiedCmdletNames resource entries overlapped with the new AvoidReservedWordsAsFunctionNames entries from main. I kept both sets of changes.

Merge commit: f6d123d

The branch should now be ready for CI tests to run.

genXdev avatar Dec 03 '25 09:12 genXdev