PowerShell / PowerShell/PSScriptAnalyzer

Validate new Rule definition API

オープン
#1,543 コメント 2 件 リアクション 2 件 担当者 0 名 GitHub で見る

まだ誰も着手していません。

API Proposal Consider - 2.0 Issue - Discussion
主要言語
C#
スター
2.2k
フォーク
414
平均マージ
13時間 1分
マージ済み PR(30日)
2

説明

PSSA2 provides a new way to specify rules, which you can see here:

https://github.com/PowerShell/PSScriptAnalyzer/blob/9ee8c0f3110fda305314b050bf0c74766bfcce42/ScriptAnalyzer2/Builtin/Rules/AvoidEmptyCatchBlock.cs#L12-L48

That is based on the following implementation:

https://github.com/PowerShell/PSScriptAnalyzer/blob/9ee8c0f3110fda305314b050bf0c74766bfcce42/ScriptAnalyzer2/Rules/Rule.cs#L12-L91

This is nice because:

  • The rule uses attributes, so we can know about it before instantiating one, and it's also neater and more declarative
  • The abstract class can provide convenience methods that simplify invocation, helpfully transform inputs, and even can do diagnostic suppression before a diagnostic record object is allocated
  • A rule can be generic in its settings object and then allow for those settings to be injected through the constructor based on a deserialised settings object. This adds boilerplate, but allows the object configuration to be totally up to the implementer based on their settings definition (which can be defined next to the rule) and their constructor logic

However, some downsides of the current implementation are:

  • Allowing the same class to implement a rule and, say, a formatter is harder with the abstract class vs an interface (and interfaces should generally be preferred when possible)
  • Using a protected method to emit diagnostics means there's no type check for rules that will never emit a diagnostic
  • We force constructor injection. This is so you can use settings in your rule at the correct time from the code's perspective (e.g. default property values will work, unlike today), but it means more boilerplate for implementers.

I would very much like to keep the feature where we don't allocate an object for suppressed diagnostics, and it has the advantage that there's one fewer classes for an implementer to think about. Some convenience functions can be preserved with extension methods on interfaces, but some cannot.

So an example alternate implementation might look like:

public interface IScriptRule
{
    IEnumerable<Diagnostic> AnalyzeScript(Ast ast, IReadOnlyList<Token> tokens, string scriptFilePath);
}

public static class ScriptRuleExtensions
{
    // Convenience methods
}

But the main issue there is we lose the elegance of (1) making it easier to construct diagnostics, particularly passing in rule metadata automatically, and (2) being able to filter diagnostics before they are created.

So another alternative would be:

public interface IScriptRule
{
    void AnalyzeScript(IDiagnosticEmitter diagnosticEmitter, Ast ast, IReadOnlyList<Token> tokens, string scriptFilePath);
}

// This could be something like an abstract class instead
// But ideally something we could shift the core implementation of if we needed to
public interface IDiagnosticEmitter
{
    // Rule required so that diagnostics know what rule they came from
    // But we trust the caller -- it would be possible to instantiate another rule and pass it in, but this is undesirable
    // Also having to pass `this` is something of an anti-pattern
    void EmitDiagnostic(IScriptRule rule, Ast ast, IReadOnlyList<Token> tokens, string scriptFilePath);
}

public static class DiagnosticEmitterExtensions
{
    // Convenience methods
}

Basically, the abstract class approach makes life better for both the framework and for rule implementers, but has the big downside that multiple inheritance is impossible... OTOH, there are other ways to do something like take a rule and turn it into a formatter, and single-inheritance generally promotes good cohesion...

コントリビューションガイド

コントリビューションガイドを開く

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

調査の方向性

まず、引用されている ScriptAnalyzer2/Builtin/Rules/AvoidEmptyCatchBlock.cs と ScriptAnalyzer2/Rules/Rule.cs の実装を読みます。現在の abstract-class API と、issue で説明されている interface および diagnostic-emitter の代替案を比較します。どの rule-definition design を検証または変更すべきかについて明確な決定に到達し、それを記録できれば完了です。

索引モデルが issue の本文から書いたものです。

評価

技術スタック
csharp
領域
devtools
issue の種類
リファクタリング
難易度
5/5
見積もり時間
1週間以上
活発さ
停滞
明瞭さ
説明が足りない
初心者へのやさしさ
25/100

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。