diff --git a/.codacy/cli.sh b/.codacy/cli.sh index 7057e3bff..0d04c43a1 100755 --- a/.codacy/cli.sh +++ b/.codacy/cli.sh @@ -37,7 +37,8 @@ version_file="$CODACY_CLI_V2_TMP_FOLDER/version.yaml" get_version_from_yaml() { if [ -f "$version_file" ]; then - local version=$(grep -o 'version: *"[^"]*"' "$version_file" | cut -d'"' -f2) + local version + version=$(grep -o 'version: *"[^"]*"' "$version_file" | cut -d'"' -f2) if [ -n "$version" ]; then echo "$version" return 0 @@ -55,7 +56,8 @@ get_latest_version() { fi handle_rate_limit "$response" - local version=$(echo "$response" | grep -m 1 tag_name | cut -d'"' -f4) + local version + version=$(echo "$response" | grep -m 1 tag_name | cut -d'"' -f4) echo "$version" } diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 0040aeb56..f2c2d82c2 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -3,7 +3,7 @@ ## 🚩 Quick Reference: Critical Rules for AI Agents | Rule | Requirement | -|------|-------------| +| ------ | ------------- | | .NET/C# Target | All C# code must target .NET 9 and C# 13; Analyzers and CodeFix must target .NET Standard 2.0 | | No Trial-and-Error | Never guess or use trial-and-error; STOP if unsure | | Test Coverage | All changes must be covered by tests (including edge/failure paths) | @@ -49,7 +49,6 @@ If any of these are missing, the PR will not be reviewed. ### 1. Pre-Implementation Expertise Validation - --- ## Escalation and Stop Conditions @@ -65,15 +64,18 @@ If you encounter a diagnostic span test failure, or are unsure about any Roslyn ### Symbol-Based Detection (MANDATORY) **Always prefer symbol-based detection over string matching:** + - ✅ Use `ISymbol` and `SemanticModel.GetSymbolInfo()` for type-safe detection - ✅ Register types in `MoqKnownSymbols` using `TypeProvider.GetOrCreateTypeByMetadataName()` - ❌ Avoid string-based method name matching (fragile, not refactoring-safe) **Generic Type Handling:** + - Use backtick notation for generic arity: `"Moq.Language.IRaise\`1"` for `IRaise` - Collect method overloads: `GetMembers("MethodName").OfType().ToImmutableArray()` **Moq Fluent API Chain Pattern:** + - Moq methods return different interfaces at different chain positions - Example: `Setup()` → `ISetup` → `.Raises()` → `IRaise` → `.Returns()` → `IReturns` - **Register ALL interfaces in the chain**, not just endpoint types @@ -81,6 +83,7 @@ If you encounter a diagnostic span test failure, or are unsure about any Roslyn ### Diagnostic Investigation Pattern When tests fail after removing string-based detection: + 1. Create temporary diagnostic test using `SemanticModel.GetSymbolInfo()` 2. Capture actual symbol type at runtime 3. Compare against `MoqKnownSymbols` registry to find missing entries @@ -89,6 +92,7 @@ When tests fail after removing string-based detection: ### Context Preservation Use appropriate analysis contexts to maintain compilation access: + - `SyntaxNodeAnalysisContext` - For syntax tree analysis with semantic model - `SemanticModelAnalysisContext` - For semantic analysis - `SyntaxTreeAnalysisContext` - For whole-file analysis @@ -96,6 +100,7 @@ Use appropriate analysis contexts to maintain compilation access: --- **Implementation:** + - Answer domain-specific technical questions before coding - Provide concrete examples of your understanding - Request expert guidance if uncertain about any concept @@ -111,6 +116,7 @@ Use appropriate analysis contexts to maintain compilation access: - **Stop progression** if any required step is incomplete **Implementation:** + - Read and understand project-specific instructions first - Follow established patterns and conventions - Verify each workflow step was completed successfully @@ -119,12 +125,14 @@ Use appropriate analysis contexts to maintain compilation access: ### 3. Critical Failure Recognition **You MUST establish clear stop conditions:** + - **Immediate halt** for uncertainty or lack of understanding - **Specific criteria** for when to request expert guidance - **No trial-and-error tolerance** - require deliberate, correct understanding - **Clear escalation paths** when encountering complex situations **Implementation:** + - Stop immediately if you cannot explain your approach - Request expert guidance when uncertain about domain concepts - Never attempt to "figure out" solutions through guessing @@ -133,12 +141,14 @@ Use appropriate analysis contexts to maintain compilation access: ### 4. Tool Usage Reliability **You MUST use available tools consistently and reliably:** + - **Consistent, reliable use** of available tools regardless of platform - **Graceful handling** of tool failures and interruptions - **Validation** that tools were used correctly and effectively - **Retry mechanisms** for interrupted operations **Implementation:** + - Use tools systematically and consistently - Handle tool failures gracefully with clear error messages - Validate tool outputs before proceeding @@ -147,12 +157,14 @@ Use appropriate analysis contexts to maintain compilation access: ### 5. Context and State Management **You MUST preserve context and maintain state:** + - **Preserve context** across task interruptions or resumptions - **Maintain state** during complex multi-step operations - **Automatic recovery** of context after interruptions - **Clear state transitions** between different phases of work **Implementation:** + - Maintain clear state throughout complex operations - Recover context automatically after interruptions - Document state transitions clearly @@ -161,12 +173,14 @@ Use appropriate analysis contexts to maintain compilation access: ### 6. Documentation and Configuration Awareness **You MUST check and understand project context:** + - **Check relevant files** before making changes - **Read and understand** project-specific instructions - **Follow established patterns** and conventions - **Respect existing architecture** and design decisions **Implementation:** + - Always read configuration files and documentation first - Understand project structure and conventions - Follow established naming and architectural patterns @@ -175,12 +189,14 @@ Use appropriate analysis contexts to maintain compilation access: ### 7. Validation and Verification **You MUST verify work through appropriate means:** + - **Verify work** through appropriate means (tests, analysis, etc.) - **Confirm changes** meet requirements before considering tasks complete - **Run validation checks** after modifications - **Ensure quality** through systematic verification **Implementation:** + - Run tests and validation checks after changes - Verify that modifications meet stated requirements - Use appropriate verification methods for the domain @@ -189,12 +205,14 @@ Use appropriate analysis contexts to maintain compilation access: ### 8. No Trial-and-Error Tolerance **You MUST require deliberate understanding:** + - **Require deliberate understanding** before implementation - **No guessing** at solutions or approaches - **Clear escalation paths** when uncertain - **Expert guidance triggers** for complex or unclear situations **Implementation:** + - Never implement solutions you don't fully understand - Stop and request clarification when uncertain - Establish clear criteria for when to seek expert guidance @@ -279,7 +297,6 @@ When working with task lists, the AI must: 5. **Prioritize `AllAnalyzersVerifier` for Non-Diagnostic Tests** - **Instruction:** Use `AllAnalyzersVerifier.VerifyAllAnalyzersAsync()` for "no diagnostics" tests. - ### AI Agent Workflow When making changes, follow this workflow: @@ -304,7 +321,6 @@ All formatting, linting, and static analysis feedback from bots must be addresse --- - ### AI Agent Specific Output Checklist - Output only complete, compiling code (classes or methods) with all required `using` directives. @@ -320,7 +336,7 @@ All formatting, linting, and static analysis feedback from bots must be addresse All commits must use the [Conventional Commits](https://www.conventionalcommits.org/) format: -``` +```text [optional scope]: [optional body] @@ -335,6 +351,7 @@ All commits must use the [Conventional Commits](https://www.conventionalcommits. - `docs(readme): update installation instructions` **Bad:** + - `fixed bug on landing page` - `oops` - `I think I fixed it this time?` @@ -356,6 +373,7 @@ All commits must use the [Conventional Commits](https://www.conventionalcommits. Before writing a single line of code, you must internally verify you can make the following declaration. If not, you must halt immediately. > "I declare that I have expert-level, demonstrable expertise in: +> > - Roslyn syntax tree navigation from `SyntaxNode` down to `SyntaxToken` and `SyntaxTrivia`. > - Precise, character-level diagnostic span calculation and verification. > - The distinction and correct application of `IOperation` vs. `ISyntaxNode` analysis. @@ -435,13 +453,13 @@ If you encounter: --- - ## AI Agent Code Review I need your help tracking down and fixing some bugs that have been reported in this codebase. I suspect the bugs are related to: -- Incorrect handling of edge cases + +- Incorrect handling of edge cases - Off-by-one errors in loops or array indexing - Unexpected data types - Uncaught exceptions @@ -449,17 +467,19 @@ I suspect the bugs are related to: - Improper configuration settings To diagnose: -1. Review the code carefully and systematically -2. Trace the relevant code paths + +1. Review the code carefully and systematically +2. Trace the relevant code paths 3. Consider boundary conditions and potential error states 4. Look for antipatterns that tend to cause bugs -5. Run the code mentally with example inputs +5. Run the code mentally with example inputs 6. Think about interactions between components When you find potential bugs, for each one provide: + 1. File path and line number(s) 2. Description of the issue and why it's a bug -3. Example input that would trigger the bug +3. Example input that would trigger the bug 4. Suggestions for how to fix it After analysis, please update the code with your proposed fixes. Try to match the existing code style. Add regression tests if possible, to prevent the bugs from recurring. @@ -527,6 +547,7 @@ The following instruction files provide comprehensive, self-contained guidance f 5. **Test coverage** - Ensure all file types have appropriate instruction coverage **Common areas requiring updates across multiple files:** + - Git commit message guidelines - Pull request requirements - Code quality standards @@ -543,6 +564,7 @@ The following instruction files provide comprehensive, self-contained guidance f ## Reference to General Guidelines For comprehensive contributor guidance including: + - Development workflow requirements - Code quality standards - Testing requirements and patterns diff --git a/.github/dependabot.yml b/.github/dependabot.yml index bfb467025..0e033db2c 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -10,10 +10,10 @@ updates: reviewers: - "rjmurillo" - "mattkotsenas" - # NuGet dependency management is handled by Renovate (see renovate.json). - # Dependabot NuGet PRs were disabled because: - # 1. PR titles use "Bump X from A to B" format, which fails the required - # "Validate PR title" check (conventional commits required). - # 2. Both bots created duplicate PRs for every package update. - # 3. Renovate supports better grouping, automerge rules, and conventional - # commit formatting out of the box. +# NuGet dependency management is handled by Renovate (see renovate.json). +# Dependabot NuGet PRs were disabled because: +# 1. PR titles use "Bump X from A to B" format, which fails the required +# "Validate PR title" check (conventional commits required). +# 2. Both bots created duplicate PRs for every package update. +# 3. Renovate supports better grouping, automerge rules, and conventional +# commit formatting out of the box. diff --git a/.github/instructions/README.md b/.github/instructions/README.md index b6ec763ec..53d85a38c 100644 --- a/.github/instructions/README.md +++ b/.github/instructions/README.md @@ -2,19 +2,19 @@ This table maps file patterns in the repository to their corresponding instruction files. Use this as a quick reference to ensure you are following the correct guidance for each file type. If a file type is not listed, refer to `generic.instructions.md`. -| File Pattern | Instruction File | Description/Notes | -|------------------------|----------------------------------------------------|-----------------------------------------| -| `*.cs` | `csharp.instructions.md` | C# source files | -| `*.csproj`, `*.sln` | `project.instructions.md` | Project/solution files | -| `*.md` | `markdown.instructions.md` | Markdown documentation | -| `*.json` | `json.instructions.md` | JSON config | -| `*.yml`, `*.yaml` | `yaml.instructions.md` | CI/CD workflows | -| `*.sh`, `*.ps1` | `shell.instructions.md` | Shell/PowerShell scripts | -| `*.xml` | `xml.instructions.md` | XML config/docs | -| `*.txt` | `text.instructions.md` | Text files | -| `.editorconfig` | `editorconfig.instructions.md` | EditorConfig rules | -| `.gitignore` | `gitignore.instructions.md` | Git ignore rules | -| `*.props`, `*.targets` | `msbuild.instructions.md` | MSBuild property/target files | -| _Other_ | `generic.instructions.md` | Fallback for unknown file types | +| File Pattern | Instruction File | Description/Notes | +| --- | --- | --- | +| `*.cs` | `csharp.instructions.md` | C# source files | +| `*.csproj`, `*.sln` | `project.instructions.md` | Project/solution files | +| `*.md` | `markdown.instructions.md` | Markdown documentation | +| `*.json` | `json.instructions.md` | JSON config | +| `*.yml`, `*.yaml` | `yaml.instructions.md` | CI/CD workflows | +| `*.sh`, `*.ps1` | `shell.instructions.md` | Shell/PowerShell scripts | +| `*.xml` | `xml.instructions.md` | XML config/docs | +| `*.txt` | `text.instructions.md` | Text files | +| `.editorconfig` | `editorconfig.instructions.md` | EditorConfig rules | +| `.gitignore` | `gitignore.instructions.md` | Git ignore rules | +| `*.props`, `*.targets` | `msbuild.instructions.md` | MSBuild property/target files | +| _Other_ | `generic.instructions.md` | Fallback for unknown file types | -**Note:** If you are editing a file type not listed above, always check for a matching instruction file in this directory or use the generic fallback. When in doubt, escalate by tagging `@repo-maintainers` in your PR. \ No newline at end of file +**Note:** If you are editing a file type not listed above, always check for a matching instruction file in this directory or use the generic fallback. When in doubt, escalate by tagging `@repo-maintainers` in your PR. diff --git a/.github/instructions/csharp.instructions.md b/.github/instructions/csharp.instructions.md index 5230b16f4..719aa8102 100644 --- a/.github/instructions/csharp.instructions.md +++ b/.github/instructions/csharp.instructions.md @@ -8,9 +8,10 @@ applyTo: '**/*.cs' - **For complex changes, see the Decision Trees section below** -**MANDATORY: Only Valid C# Code in All Tests** +## MANDATORY: Only Valid C# Code in All Tests > **You MUST NEVER write or include code in analyzer or code fix tests that produces C# compiler errors.** +> > - All test code must be valid, compilable C#. > - Do not write tests for static, const, readonly, or event members if the code would not compile. > - Do not include code that triggers CSxxxx errors (e.g., invalid member access, missing members, or illegal syntax). @@ -18,6 +19,7 @@ applyTo: '**/*.cs' > - Any test that fails to compile is an immediate failure and must be removed or rewritten. **Rationale:** + - Roslyn analyzers and code fixes only operate on valid, successfully parsed C# code. Compiler errors prevent analyzers from running and invalidate the test scenario. - Writing invalid code in tests causes build failures, test failures, and wastes review/CI resources. - This is a non-negotiable rule. If you are unsure whether a test is valid C#, STOP and request expert guidance. @@ -94,22 +96,23 @@ If you update guidance in copilot-instructions.md that affects C# development, e If tests fail after adding/modifying symbol-based detection: 1. **Create Temporary Diagnostic Test:** -```csharp -[TestMethod] -public async Task DiagnosticSymbolTest() -{ - string code = """ - var mock = new Mock(); - mock.Setup(x => x.Method()).Raises(x => x.Event += null, EventArgs.Empty); - """; - - var (compilation, semanticModel, invocation) = await GetSemanticInfo(code); - var symbolInfo = semanticModel.GetSymbolInfo(invocation); - - // Output actual symbol type to understand what's missing - Console.WriteLine($"Symbol: {symbolInfo.Symbol?.ContainingType}"); -} -``` + + ```csharp + [TestMethod] + public async Task DiagnosticSymbolTest() + { + string code = """ + var mock = new Mock(); + mock.Setup(x => x.Method()).Raises(x => x.Event += null, EventArgs.Empty); + """; + + var (compilation, semanticModel, invocation) = await GetSemanticInfo(code); + var symbolInfo = semanticModel.GetSymbolInfo(invocation); + + // Output actual symbol type to understand what's missing + Console.WriteLine($"Symbol: {symbolInfo.Symbol?.ContainingType}"); + } + ``` 2. **Compare Against Registry:** Check if symbol type exists in `MoqKnownSymbols` 3. **Add Missing Symbol:** Register in `MoqKnownSymbols` with proper generic arity @@ -124,6 +127,7 @@ public async Task DiagnosticSymbolTest() ## Test Data & Sample Inputs/Outputs ### What Constitutes Good Test Data? + - Cover all code paths: positive, negative, and edge cases - Include both minimal and complex/realistic examples - Test for invalid inputs, exceptions, and boundary conditions @@ -131,6 +135,7 @@ public async Task DiagnosticSymbolTest() - For analyzers/code fixes: test all diagnostic locations, fix applications, and no-fix scenarios ### Coverage Strategy + - For every new analyzer/code fix, include: - At least one positive, one negative, and one edge case test - Data-driven tests for all fixable patterns diff --git a/.github/instructions/editorconfig.instructions.md b/.github/instructions/editorconfig.instructions.md index 9968017ba..df161f8f3 100644 --- a/.github/instructions/editorconfig.instructions.md +++ b/.github/instructions/editorconfig.instructions.md @@ -13,6 +13,7 @@ applyTo: '.editorconfig' ## Context Loading for Copilot When working on this file, you MUST: + 1. Read this entire instruction file before making any changes 2. Validate your understanding by checking the "Validation Checklist" section 3. If uncertain about any requirement, stop and request clarification @@ -23,6 +24,7 @@ If you (AI or human) are blocked or cannot proceed, leave a comment in the PR de ## Validation Checklist Before submitting any changes, verify: + - [ ] All formatting and linting rules are correct and documented - [ ] All changes are compatible with project coding standards - [ ] All affected files are re-formatted and linted @@ -30,6 +32,7 @@ Before submitting any changes, verify: - [ ] PR description includes validation evidence and checklist ## Validation Evidence Requirements + - Attach a log section titled `## EditorConfig Validation Log` showing the output of `dotnet format` or equivalent - Include a checklist like: - [x] All files formatted @@ -37,6 +40,7 @@ Before submitting any changes, verify: - Paste CI run link under `## CI Evidence` ## .editorconfig Guidance + - .editorconfig controls code formatting and linting for the repository - Changes may affect build results, code reviews, and CI - Only update rules when necessary and after confirming with maintainers @@ -44,6 +48,7 @@ Before submitting any changes, verify: - Document any rule changes in the PR description ## Related Instruction Files + - [csharp.instructions.md](csharp.instructions.md) - For C# code formatting - [markdown.instructions.md](markdown.instructions.md) - For markdown formatting -- [project.instructions.md](project.instructions.md) - For build configuration \ No newline at end of file +- [project.instructions.md](project.instructions.md) - For build configuration diff --git a/.github/instructions/generic.instructions.md b/.github/instructions/generic.instructions.md index 0b5bf826c..517cd3eb4 100644 --- a/.github/instructions/generic.instructions.md +++ b/.github/instructions/generic.instructions.md @@ -14,6 +14,7 @@ applyTo: '**/*' ## Context Loading for Copilot When working on this file, you MUST: + 1. Read this entire instruction file before making any changes 2. Validate your understanding by checking the "Validation Checklist" section 3. If uncertain about any requirement, stop and request clarification @@ -24,26 +25,30 @@ If you (AI or human) are blocked or cannot proceed, leave a comment in the PR de ## Validation Checklist Before submitting any changes, verify: + - [ ] All changes are compatible with project requirements - [ ] No breaking changes to build, test, or deployment - [ ] Machine-readable evidence is attached (see below) - [ ] PR description includes validation evidence and checklist ## Validation Evidence Requirements + - Attach a log section titled `## Validation Log` showing the output of any relevant validation commands (build, test, lint, etc.) - Include a checklist like: - [x] No errors or warnings - Paste CI run link under `## CI Evidence` ## Guidance + - If you are unsure how to edit this file type, consult maintainers before proceeding - Document any changes in the PR description ## Related Instruction Files + - [project.instructions.md](project.instructions.md) - For project/solution files - [csharp.instructions.md](csharp.instructions.md) - For C# files - [markdown.instructions.md](markdown.instructions.md) - For documentation -- [shell.instructions.md](shell.instructions.md) - For scripts +- [shell.instructions.md](shell.instructions.md) - For scripts ## Decision Trees for Complex Scenarios @@ -80,16 +85,18 @@ Before submitting any changes, verify: 6. **Run all validations (build, test, lint, Codacy, etc.)** 7. **Prepare PR with validation evidence for each file type** 8. **If any diagnostic span or test fails more than once, STOP and escalate** -9. **If uncertain about Roslyn APIs, Moq semantics, or workflow, escalate** +9. **If uncertain about Roslyn APIs, Moq semantics, or workflow, escalate** ## Test Data & Sample Inputs/Outputs ### What Constitutes Good Test Data for Unknown File Types? + - Validate file with any available tool (lint, build, CI, etc.) - Include both valid and invalid examples if possible - Test for missing required content, invalid formats, and edge cases - Document test data rationale in comments or PR description ### Coverage Strategy + - For every change, validate with available tools and test both valid and invalid cases -- Document test data rationale in comments or PR description \ No newline at end of file +- Document test data rationale in comments or PR description diff --git a/.github/instructions/gitignore.instructions.md b/.github/instructions/gitignore.instructions.md index 4503aa137..d948f6727 100644 --- a/.github/instructions/gitignore.instructions.md +++ b/.github/instructions/gitignore.instructions.md @@ -13,6 +13,7 @@ applyTo: '.gitignore' ## Context Loading for Copilot When working on this file, you MUST: + 1. Read this entire instruction file before making any changes 2. Validate your understanding by checking the "Validation Checklist" section 3. If uncertain about any requirement, stop and request clarification @@ -23,6 +24,7 @@ If you (AI or human) are blocked or cannot proceed, leave a comment in the PR de ## Validation Checklist Before submitting any changes, verify: + - [ ] Only files that should not be tracked are ignored - [ ] No source, config, or required files are ignored - [ ] All new build artifacts, logs, and secrets are ignored @@ -30,6 +32,7 @@ Before submitting any changes, verify: - [ ] PR description includes validation evidence and checklist ## Validation Evidence Requirements + - Attach a log section titled `## Gitignore Validation Log` showing the output of `git status` before and after changes - Include a checklist like: - [x] No required files ignored @@ -37,6 +40,7 @@ Before submitting any changes, verify: - Paste CI run link under `## CI Evidence` ## .gitignore Guidance + - Only ignore files that are not needed in the repository (build output, logs, secrets, etc.) - Never ignore source code, configuration, or documentation files - Review changes with maintainers if unsure @@ -44,5 +48,6 @@ Before submitting any changes, verify: - Document any changes in the PR description ## Related Instruction Files + - [project.instructions.md](project.instructions.md) - For build artifacts -- [text.instructions.md](text.instructions.md) - For text file patterns \ No newline at end of file +- [text.instructions.md](text.instructions.md) - For text file patterns diff --git a/.github/instructions/json.instructions.md b/.github/instructions/json.instructions.md index 9231c13e6..d9b29ab93 100644 --- a/.github/instructions/json.instructions.md +++ b/.github/instructions/json.instructions.md @@ -12,12 +12,14 @@ applyTo: '**/*.json' ## Context Loading for Copilot When working on this file type, you MUST: + 1. Read this entire instruction file before making any changes 2. Cross-reference with related instruction files (listed below) 3. Validate your understanding by checking the "Validation Checklist" section 4. If uncertain about any requirement, stop and request clarification **Related Instruction Files:** + - [project.instructions.md](project.instructions.md) - For project and build configuration - [yaml.instructions.md](yaml.instructions.md) - For CI/CD workflows - [text.instructions.md](text.instructions.md) - For plain text configuration @@ -27,12 +29,14 @@ When working on this file type, you MUST: Before submitting any changes, verify: **Configuration:** + - [ ] JSON syntax is valid - [ ] Schema compliance is maintained - [ ] All configuration standards followed - [ ] Security scan completed after dependency changes **Process:** + - [ ] Conventional commit format used - [ ] PR description includes validation evidence - [ ] All checklist items completed @@ -41,11 +45,13 @@ Before submitting any changes, verify: ## Decision Trees ### When to Request Human Review + - Is this a new configuration standard? → Yes → Request expert guidance - Is this a breaking change to configuration? → Yes → Document thoroughly and request review - Are you uncertain about JSON schema or configuration? → Yes → Stop and request guidance ### When to Stop and Ask for Help + - Uncertain about configuration requirements - Major changes to project configuration - Security or legal implications @@ -53,11 +59,13 @@ Before submitting any changes, verify: ## Common Mistakes to Avoid **DO NOT:** + - Skip validation steps - Ignore security scanning after dependency changes - Submit changes without validation evidence **ALWAYS:** + - Read the entire instruction file first - Validate all configuration changes - Include comprehensive documentation updates @@ -66,16 +74,19 @@ Before submitting any changes, verify: ## Context Management **Before Starting:** + - Read the complete instruction file - Understand the current file's purpose and structure - Identify all related files that may need updates **During Editing:** + - Keep track of all changes made - Validate each change against requirements - Maintain consistency with existing patterns **After Completing:** + - Review all changes against the validation checklist - Ensure all requirements are met - Prepare comprehensive PR description with evidence @@ -83,12 +94,14 @@ Before submitting any changes, verify: ## Handling Uncertainty **Stop and Request Help When:** + - You cannot explain your approach clearly - You're making "educated guesses" about configuration - You're uncertain about project configuration - You cannot trace the logic in configuration without narration **Escalation Process:** + 1. Stop all work immediately 2. Document what you were trying to accomplish 3. Explain what specific aspect is unclear @@ -98,6 +111,7 @@ Before submitting any changes, verify: ## Success Criteria Your changes are successful when: + - All configuration validation checks pass - Security scan is clean - PR description is complete and accurate @@ -155,6 +169,7 @@ When updating version numbers: ### Dependency Update Guidelines **For Renovate/Dependabot PRs:** + - Review changelog and release notes - Test locally to ensure compatibility - Check for breaking changes @@ -162,6 +177,7 @@ When updating version numbers: - Include testing evidence in PR description **For Manual Dependency Updates:** + - Follow the same process as automated updates - Document the reason for the update - Include compatibility testing results @@ -222,7 +238,7 @@ Before submitting a PR, ensure your changes pass all quality checks: Follow the [Conventional Commits](https://www.conventionalcommits.org/) specification: -``` +```text [optional scope]: [optional body] @@ -231,6 +247,7 @@ Follow the [Conventional Commits](https://www.conventionalcommits.org/) specific ``` **Types:** + - `feat`: New features - `fix`: Bug fixes - `docs`: Documentation changes @@ -250,6 +267,7 @@ Follow the [Conventional Commits](https://www.conventionalcommits.org/) specific Follow conventional commit format: `type(scope): description` **Description Requirements:** + 1. **Clear summary** of changes 2. **Problem statement** (what issue does this solve?) 3. **Solution description** (how does this solve the problem?) @@ -271,12 +289,14 @@ Before submitting a PR, ensure: ### Validation Evidence Requirements **What Constitutes Validation Evidence:** + - JSON validation output - Configuration testing results - Screenshots of successful CI runs - Manual testing results for configuration changes **Evidence Format:** + - Include validation logs, screenshots, or links to CI runs - Provide clear, readable evidence - Ensure evidence is recent and relevant @@ -310,12 +330,14 @@ Maintainers will review PRs for: ## Test Data & Sample Inputs/Outputs ### What Constitutes Good JSON Test Data? + - Validate against schema (if available) - Include both valid and invalid examples - Test for missing required fields, extra fields, and type mismatches - Check for correct handling of comments (if allowed) ### Example: Valid Config + ```json { "settingA": true, @@ -324,17 +346,19 @@ Maintainers will review PRs for: ``` ### Example: Negative/Edge Case + ```json { - "settingA": "yes", // invalid type - // missing maxItems + "settingA": "yes", + "maxItems": null } ``` ### Coverage Strategy + - For every config change, validate with schema and test both valid and invalid cases - Document test data rationale in comments or PR description ## Code of Conduct -This project adheres to the [Contributor Covenant Code of Conduct](CODE-OF-CONDUCT.md). By participating, you are expected to uphold this code. \ No newline at end of file +This project adheres to the [Contributor Covenant Code of Conduct](CODE-OF-CONDUCT.md). By participating, you are expected to uphold this code. diff --git a/.github/instructions/markdown.instructions.md b/.github/instructions/markdown.instructions.md index ac023a9b0..551010a9b 100644 --- a/.github/instructions/markdown.instructions.md +++ b/.github/instructions/markdown.instructions.md @@ -12,12 +12,14 @@ applyTo: '**/*.md' ## Context Loading for Copilot When working on this file type, you MUST: + 1. Read this entire instruction file before making any changes 2. Cross-reference with related instruction files (listed below) 3. Validate your understanding by checking the "Validation Checklist" section 4. If uncertain about any requirement, stop and request clarification **Related Instruction Files:** + - [csharp.instructions.md](csharp.instructions.md) - For C# code changes - [project.instructions.md](project.instructions.md) - For build configuration changes - [text.instructions.md](text.instructions.md) - For plain text documentation @@ -27,12 +29,14 @@ When working on this file type, you MUST: Before submitting any changes, verify: **Documentation:** + - [ ] All documentation standards followed - [ ] Formatting and linting requirements met - [ ] Table of contents updated if needed - [ ] All links are valid and working **Process:** + - [ ] Conventional commit format used - [ ] PR description includes validation evidence - [ ] All checklist items completed @@ -41,11 +45,13 @@ Before submitting any changes, verify: ## Decision Trees ### When to Request Human Review + - Is this a new documentation standard? → Yes → Request expert guidance - Is this a breaking change to documentation? → Yes → Document thoroughly and request review - Are you uncertain about documentation structure? → Yes → Stop and request guidance ### When to Stop and Ask for Help + - Uncertain about documentation requirements - Major changes to project documentation - Security or legal implications @@ -53,12 +59,14 @@ Before submitting any changes, verify: ## Common Mistakes to Avoid **DO NOT:** + - Skip validation steps - Assume external file references will be loaded - Ignore automated bot feedback - Submit changes without validation evidence **ALWAYS:** + - Read the entire instruction file first - Validate all links and formatting - Include comprehensive documentation updates @@ -67,16 +75,19 @@ Before submitting any changes, verify: ## Context Management **Before Starting:** + - Read the complete instruction file - Understand the current file's purpose and structure - Identify all related files that may need updates **During Editing:** + - Keep track of all changes made - Validate each change against requirements - Maintain consistency with existing patterns **After Completing:** + - Review all changes against the validation checklist - Ensure all requirements are met - Prepare comprehensive PR description with evidence @@ -84,12 +95,14 @@ Before submitting any changes, verify: ## Handling Uncertainty **Stop and Request Help When:** + - You cannot explain your approach clearly - You're making "educated guesses" about documentation standards - You're uncertain about project documentation structure - You cannot trace the logic in documentation without narration **Escalation Process:** + 1. Stop all work immediately 2. Document what you were trying to accomplish 3. Explain what specific aspect is unclear @@ -99,6 +112,7 @@ Before submitting any changes, verify: ## Success Criteria Your changes are successful when: + - All formatting and linting checks pass - All documentation standards are met - All links are valid @@ -111,6 +125,7 @@ Your changes are successful when: ### When Documentation is Required Documentation updates are required for: + - New analyzers or fixers - Changes to existing analyzer behavior - API changes or additions @@ -151,7 +166,7 @@ This project adheres to the [Contributor Covenant Code of Conduct](CODE-OF-CONDU ### Guidelines 1. **Capitalization and Punctuation**: Capitalize the first word and do not end in punctuation. If using Conventional Commits, remember to use all lowercase. -2. **Mood**: Use imperative mood in the subject line. Example – Add fix for dark mode toggle state. Imperative mood gives the tone you are giving an order or request. +2. **Mood**: Use imperative mood in the subject line. Example: Add fix for dark mode toggle state. Imperative mood gives the tone you are giving an order or request. 3. **Type of Commit**: Specify the type of commit. It is recommended and can be even more beneficial to have a consistent set of words to describe your changes. Example: Bugfix, Update, Refactor, Bump, and so on. See the section on Conventional Commits below for additional information. 4. **Length**: The first line should ideally be no longer than 50 characters, and the body should be restricted to 72 characters. 5. **Content**: Be direct, try to eliminate filler words and phrases in these sentences (examples: though, maybe, I think, kind of). Think like a journalist. @@ -160,7 +175,7 @@ This project adheres to the [Contributor Covenant Code of Conduct](CODE-OF-CONDU Conventional Commit is a formatting convention that provides a set of rules to formulate a consistent commit message structure like so: -``` +```text [optional scope]: [optional body] @@ -170,17 +185,17 @@ Conventional Commit is a formatting convention that provides a set of rules to f The commit type can include the following: -- feat – a new feature is introduced with the changes -- fix – a bug fix has occurred -- chore – changes that do not relate to a fix or feature and don't modify src or test files (for example updating dependencies) -- refactor – refactored code that neither fixes a bug nor adds a feature -- docs – updates to documentation such as a the README or other markdown files -- style – changes that do not affect the meaning of the code, likely related to code formatting such as white-space, missing semi-colons, and so on. -- test – including new or correcting previous tests -- perf – performance improvements -- ci – continuous integration related -- build – changes that affect the build system or external dependencies -- revert – reverts a previous commit +- feat: a new feature is introduced with the changes +- fix: a bug fix has occurred +- chore: changes that do not relate to a fix or feature and don't modify src or test files (for example updating dependencies) +- refactor: refactored code that neither fixes a bug nor adds a feature +- docs: updates to documentation such as a the README or other markdown files +- style: changes that do not affect the meaning of the code, likely related to code formatting such as white-space, missing semi-colons, and so on. +- test: including new or correcting previous tests +- perf: performance improvements +- ci: continuous integration related +- build: changes that affect the build system or external dependencies +- revert: reverts a previous commit ## Pull Request Guidelines @@ -190,6 +205,7 @@ The commit type can include the following: Follow conventional commit format: `type(scope): description` **Description Requirements:** + 1. **Clear summary** of changes 2. **Problem statement** (what issue does this solve?) 3. **Solution description** (how does this solve the problem?) @@ -234,4 +250,4 @@ Maintainers will review PRs for: - All formatting and linting issues flagged by bots or CI must be resolved before requesting review or merging. - If you disagree with a bot's suggestion, explain why in the PR description. - If a bot's feedback is not addressed and a human reviewer must repeat the request, the PR will be closed until all automated feedback is resolved. -- All documentation and markdown reports must pass formatting checks. Use a markdown linter if available. \ No newline at end of file +- All documentation and markdown reports must pass formatting checks. Use a markdown linter if available. diff --git a/.github/instructions/msbuild.instructions.md b/.github/instructions/msbuild.instructions.md index cbf0df99c..9f51eb8f8 100644 --- a/.github/instructions/msbuild.instructions.md +++ b/.github/instructions/msbuild.instructions.md @@ -13,6 +13,7 @@ applyTo: '**/*.{props,targets}' ## Context Loading for Copilot When working on this file, you MUST: + 1. Read this entire instruction file before making any changes 2. Validate your understanding by checking the "Validation Checklist" section 3. If uncertain about any requirement, stop and request clarification @@ -23,6 +24,7 @@ If you (AI or human) are blocked or cannot proceed, leave a comment in the PR de ## Validation Checklist Before submitting any changes, verify: + - [ ] All property/target changes are compatible with project build requirements - [ ] No breaking changes to build or packaging - [ ] All affected projects build and test successfully @@ -30,6 +32,7 @@ Before submitting any changes, verify: - [ ] PR description includes validation evidence and checklist ## Validation Evidence Requirements + - Attach a log section titled `## MSBuild Validation Log` showing the output of `dotnet build` and `dotnet test` for all affected projects - Include a checklist like: - [x] All projects build @@ -37,11 +40,13 @@ Before submitting any changes, verify: - Paste CI run link under `## CI Evidence` ## MSBuild Guidance + - .props and .targets files control build, packaging, and deployment - Only update when necessary and after confirming with maintainers - After changes, run `dotnet build` and `dotnet test` for all affected projects - Document any changes in the PR description ## Related Instruction Files + - [project.instructions.md](project.instructions.md) - For project/solution files -- [shell.instructions.md](shell.instructions.md) - For build scripts \ No newline at end of file +- [shell.instructions.md](shell.instructions.md) - For build scripts diff --git a/.github/instructions/project.instructions.md b/.github/instructions/project.instructions.md index 1a4359393..766a5dc0a 100644 --- a/.github/instructions/project.instructions.md +++ b/.github/instructions/project.instructions.md @@ -6,7 +6,7 @@ applyTo: '**/*.{csproj,sln}' > **MANDATORY:** You MUST follow these instructions when editing any project file (.csproj, .sln) in this repository. -# Project File Instructions (Quick Reference) +## Project File Instructions (Quick Reference) - **Read this file before editing any .csproj or .sln file** - **Cross-reference with related instruction files** @@ -16,12 +16,14 @@ applyTo: '**/*.{csproj,sln}' ## Context Loading for Copilot When working on this file type, you MUST: + 1. Read this entire instruction file before making any changes 2. Cross-reference with related instruction files (listed below) 3. Validate your understanding by checking the "Validation Checklist" section 4. If uncertain about any requirement, stop and request clarification **Related Instruction Files:** + - [csharp.instructions.md](csharp.instructions.md) - For C# code changes - [json.instructions.md](json.instructions.md) - For configuration files - [yaml.instructions.md](yaml.instructions.md) - For CI/CD workflows @@ -31,6 +33,7 @@ When working on this file type, you MUST: Before submitting any changes, verify: **Build & Dependency Management:** + - [ ] Target .NET 8, C# 12 by default; Analyzers and Code Fixes target .NET Standard 2.0 - [ ] All build and dependency requirements met - [ ] No warnings or errors in build @@ -38,6 +41,7 @@ Before submitting any changes, verify: - [ ] Security scan completed after dependency changes **Process:** + - [ ] Conventional commit format used - [ ] PR description includes validation evidence - [ ] All checklist items completed @@ -46,11 +50,13 @@ Before submitting any changes, verify: ## Decision Trees ### When to Request Human Review + - Is this a new project or build configuration? → Yes → Request expert guidance - Is this a breaking change to build or dependencies? → Yes → Document thoroughly and request review - Are you uncertain about MSBuild or dependency management? → Yes → Stop and request guidance ### When to Stop and Ask for Help + - Uncertain about build or dependency requirements - Major changes to project structure - Security or legal implications @@ -58,11 +64,13 @@ Before submitting any changes, verify: ## Common Mistakes to Avoid **DO NOT:** + - Skip validation steps - Ignore security scanning after dependency changes - Submit changes without validation evidence **ALWAYS:** + - Read the entire instruction file first - Validate all build and dependency changes - Include comprehensive documentation updates @@ -71,16 +79,19 @@ Before submitting any changes, verify: ## Context Management **Before Starting:** + - Read the complete instruction file - Understand the current file's purpose and structure - Identify all related files that may need updates **During Editing:** + - Keep track of all changes made - Validate each change against requirements - Maintain consistency with existing patterns **After Completing:** + - Review all changes against the validation checklist - Ensure all requirements are met - Prepare comprehensive PR description with evidence @@ -88,12 +99,14 @@ Before submitting any changes, verify: ## Handling Uncertainty **Stop and Request Help When:** + - You cannot explain your approach clearly - You're making "educated guesses" about build or dependency management - You're uncertain about project structure - You cannot trace the logic in configuration without narration **Escalation Process:** + 1. Stop all work immediately 2. Document what you were trying to accomplish 3. Explain what specific aspect is unclear @@ -103,6 +116,7 @@ Before submitting any changes, verify: ## Success Criteria Your changes are successful when: + - All builds pass without warnings - All tests pass - No linting errors @@ -123,6 +137,7 @@ Your changes are successful when: ### Dependency Update Guidelines **For Renovate/Dependabot PRs:** + - Review changelog and release notes - Test locally to ensure compatibility - Check for breaking changes @@ -130,6 +145,7 @@ Your changes are successful when: - Include testing evidence in PR description **For Manual Dependency Updates:** + - Follow the same process as automated updates - Document the reason for the update - Include compatibility testing results @@ -200,7 +216,7 @@ Before submitting a PR, ensure your changes pass all quality checks: Follow the [Conventional Commits](https://www.conventionalcommits.org/) specification: -``` +```text [optional scope]: [optional body] @@ -209,6 +225,7 @@ Follow the [Conventional Commits](https://www.conventionalcommits.org/) specific ``` **Types:** + - `feat`: New features - `fix`: Bug fixes - `docs`: Documentation changes @@ -228,6 +245,7 @@ Follow the [Conventional Commits](https://www.conventionalcommits.org/) specific Follow conventional commit format: `type(scope): description` **Description Requirements:** + 1. **Clear summary** of changes 2. **Problem statement** (what issue does this solve?) 3. **Solution description** (how does this solve the problem?) @@ -250,12 +268,14 @@ Before submitting a PR, ensure: ### Validation Evidence Requirements **What Constitutes Validation Evidence:** + - Test execution logs showing all tests pass - Build output showing successful compilation - Screenshots of successful CI runs - Dependency compatibility test results **Evidence Format:** + - Include logs, screenshots, or links to CI runs - Provide clear, readable evidence - Ensure evidence is recent and relevant @@ -282,4 +302,4 @@ Maintainers will review PRs for: ## Code of Conduct -This project adheres to the [Contributor Covenant Code of Conduct](../../CODE-OF-CONDUCT.md). By participating, you are expected to uphold this code. \ No newline at end of file +This project adheres to the [Contributor Covenant Code of Conduct](../../CODE-OF-CONDUCT.md). By participating, you are expected to uphold this code. diff --git a/.github/instructions/shell.instructions.md b/.github/instructions/shell.instructions.md index 4d2fea8b7..c36e8778b 100644 --- a/.github/instructions/shell.instructions.md +++ b/.github/instructions/shell.instructions.md @@ -12,12 +12,14 @@ applyTo: '**/*.{sh,ps1}' ## Context Loading for Copilot When working on this file type, you MUST: + 1. Read this entire instruction file before making any changes 2. Cross-reference with related instruction files (listed below) 3. Validate your understanding by checking the "Validation Checklist" section 4. If uncertain about any requirement, stop and request clarification **Related Instruction Files:** + - [yaml.instructions.md](yaml.instructions.md) - For CI/CD workflows - [project.instructions.md](project.instructions.md) - For build configuration - [text.instructions.md](text.instructions.md) - For plain text documentation @@ -27,12 +29,14 @@ When working on this file type, you MUST: Before submitting any changes, verify: **Script Quality & Security:** + - [ ] Script executes without errors - [ ] Error handling is comprehensive - [ ] Security measures are implemented - [ ] Performance impact is assessed **Process:** + - [ ] Conventional commit format used - [ ] PR description includes validation evidence - [ ] All checklist items completed @@ -41,11 +45,13 @@ Before submitting any changes, verify: ## Decision Trees ### When to Request Human Review + - Is this a new script or automation? → Yes → Request expert guidance - Is this a breaking change to build or deployment? → Yes → Document thoroughly and request review - Are you uncertain about scripting or security? → Yes → Stop and request guidance ### When to Stop and Ask for Help + - Uncertain about scripting or security requirements - Major changes to build or deployment process - Security or legal implications @@ -53,11 +59,13 @@ Before submitting any changes, verify: ## Common Mistakes to Avoid **DO NOT:** + - Skip validation steps - Ignore security scanning after dependency changes - Submit changes without validation evidence **ALWAYS:** + - Read the entire instruction file first - Validate all script and security changes - Include comprehensive documentation updates @@ -66,16 +74,19 @@ Before submitting any changes, verify: ## Context Management **Before Starting:** + - Read the complete instruction file - Understand the current file's purpose and structure - Identify all related files that may need updates **During Editing:** + - Keep track of all changes made - Validate each change against requirements - Maintain consistency with existing patterns **After Completing:** + - Review all changes against the validation checklist - Ensure all requirements are met - Prepare comprehensive PR description with evidence @@ -83,12 +94,14 @@ Before submitting any changes, verify: ## Handling Uncertainty **Stop and Request Help When:** + - You cannot explain your approach clearly - You're making "educated guesses" about scripting or security - You're uncertain about build or deployment process - You cannot trace the logic in script without narration **Escalation Process:** + 1. Stop all work immediately 2. Document what you were trying to accomplish 3. Explain what specific aspect is unclear @@ -98,6 +111,7 @@ Before submitting any changes, verify: ## Success Criteria Your changes are successful when: + - All script and security validation checks pass - Security scan is clean - PR description is complete and accurate @@ -238,7 +252,7 @@ Before submitting a PR, ensure your changes pass all quality checks: Follow the [Conventional Commits](https://www.conventionalcommits.org/) specification: -``` +```text [optional scope]: [optional body] @@ -247,6 +261,7 @@ Follow the [Conventional Commits](https://www.conventionalcommits.org/) specific ``` **Types:** + - `feat`: New features - `fix`: Bug fixes - `docs`: Documentation changes @@ -266,6 +281,7 @@ Follow the [Conventional Commits](https://www.conventionalcommits.org/) specific Follow conventional commit format: `type(scope): description` **Description Requirements:** + 1. **Clear summary** of changes 2. **Problem statement** (what issue does this solve?) 3. **Solution description** (how does this solve the problem?) @@ -288,6 +304,7 @@ Before submitting a PR, ensure: ### Validation Evidence Requirements **What Constitutes Validation Evidence:** + - Script execution logs showing successful completion - Error handling test results - Performance benchmark results @@ -295,6 +312,7 @@ Before submitting a PR, ensure: - Screenshots of successful CI runs **Evidence Format:** + - Include logs, screenshots, or links to CI runs - Provide clear, readable evidence - Ensure evidence is recent and relevant @@ -322,6 +340,7 @@ Maintainers will review PRs for: ### Common Review Feedback **Frequently Requested Changes:** + - Add missing error handling - Improve security measures - Update documentation for script changes @@ -329,6 +348,7 @@ Maintainers will review PRs for: - Clarify PR description or validation evidence **PRs That May Be Rejected:** + - Missing validation evidence - Incomplete error handling - Security vulnerabilities @@ -362,28 +382,32 @@ Maintainers will review PRs for: ## Code of Conduct -This project adheres to the [Contributor Covenant Code of Conduct](CODE-OF-CONDUCT.md). By participating, you are expected to uphold this code. +This project adheres to the [Contributor Covenant Code of Conduct](CODE-OF-CONDUCT.md). By participating, you are expected to uphold this code. ## Test Data & Sample Inputs/Outputs ### What Constitutes Good Shell Script Test Data? + - Validate script runs without errors - Include both working and intentionally broken examples - Test for missing shebang, syntax errors, and invalid arguments - Check for correct error handling and exit codes ### Example: Valid Script + ```sh #!/bin/bash echo "Hello, world!" ``` ### Example: Negative/Edge Case + ```sh echo "Hello, world!" # missing shebang exit 1 ``` ### Coverage Strategy + - For every script change, test both valid and invalid scenarios -- Document test data rationale in comments or PR description \ No newline at end of file +- Document test data rationale in comments or PR description diff --git a/.github/instructions/text.instructions.md b/.github/instructions/text.instructions.md index 13689b6af..f59e64933 100644 --- a/.github/instructions/text.instructions.md +++ b/.github/instructions/text.instructions.md @@ -12,12 +12,14 @@ applyTo: '**/*.txt' ## Context Loading for Copilot When working on this file type, you MUST: + 1. Read this entire instruction file before making any changes 2. Cross-reference with related instruction files (listed below) 3. Validate your understanding by checking the "Validation Checklist" section 4. If uncertain about any requirement, stop and request clarification **Related Instruction Files:** + - [markdown.instructions.md](markdown.instructions.md) - For documentation files - [project.instructions.md](project.instructions.md) - For build configuration - [json.instructions.md](json.instructions.md) - For configuration files @@ -27,12 +29,14 @@ When working on this file type, you MUST: Before submitting any changes, verify: **Text Quality & Documentation:** + - [ ] Text formatting is consistent - [ ] Content is accurate and complete - [ ] Documentation is updated - [ ] Changes work as expected **Process:** + - [ ] Conventional commit format used - [ ] PR description includes validation evidence - [ ] All checklist items completed @@ -41,11 +45,13 @@ Before submitting any changes, verify: ## Decision Trees ### When to Request Human Review + - Is this a new documentation or configuration standard? → Yes → Request expert guidance - Is this a breaking change to documentation or configuration? → Yes → Document thoroughly and request review - Are you uncertain about text file requirements? → Yes → Stop and request guidance ### When to Stop and Ask for Help + - Uncertain about text file requirements - Major changes to project documentation or configuration - Security or legal implications @@ -53,10 +59,12 @@ Before submitting any changes, verify: ## Common Mistakes to Avoid **DO NOT:** + - Skip validation steps - Submit changes without validation evidence **ALWAYS:** + - Read the entire instruction file first - Validate all text and documentation changes - Include comprehensive documentation updates @@ -65,16 +73,19 @@ Before submitting any changes, verify: ## Context Management **Before Starting:** + - Read the complete instruction file - Understand the current file's purpose and structure - Identify all related files that may need updates **During Editing:** + - Keep track of all changes made - Validate each change against requirements - Maintain consistency with existing patterns **After Completing:** + - Review all changes against the validation checklist - Ensure all requirements are met - Prepare comprehensive PR description with evidence @@ -82,12 +93,14 @@ Before submitting any changes, verify: ## Handling Uncertainty **Stop and Request Help When:** + - You cannot explain your approach clearly - You're making "educated guesses" about documentation or configuration - You're uncertain about project documentation or configuration - You cannot trace the logic in text file without narration **Escalation Process:** + 1. Stop all work immediately 2. Document what you were trying to accomplish 3. Explain what specific aspect is unclear @@ -97,6 +110,7 @@ Before submitting any changes, verify: ## Success Criteria Your changes are successful when: + - All text and documentation validation checks pass - PR description is complete and accurate - All checklist items completed @@ -194,7 +208,7 @@ Before submitting a PR, ensure your changes pass all quality checks: Follow the [Conventional Commits](https://www.conventionalcommits.org/) specification: -``` +```text [optional scope]: [optional body] @@ -203,6 +217,7 @@ Follow the [Conventional Commits](https://www.conventionalcommits.org/) specific ``` **Types:** + - `feat`: New features - `fix`: Bug fixes - `docs`: Documentation changes @@ -222,6 +237,7 @@ Follow the [Conventional Commits](https://www.conventionalcommits.org/) specific Follow conventional commit format: `type(scope): description` **Description Requirements:** + 1. **Clear summary** of changes 2. **Problem statement** (what issue does this solve?) 3. **Solution description** (how does this solve the problem?) @@ -243,6 +259,7 @@ Before submitting a PR, ensure: ### Validation Evidence Requirements **What Constitutes Validation Evidence:** + - Text validation output - Content accuracy verification - Formatting consistency checks @@ -250,6 +267,7 @@ Before submitting a PR, ensure: - Manual verification of text file changes **Evidence Format:** + - Include validation logs, screenshots, or links to CI runs - Provide clear, readable evidence - Ensure evidence is recent and relevant @@ -276,6 +294,7 @@ Maintainers will review PRs for: ### Common Review Feedback **Frequently Requested Changes:** + - Fix formatting inconsistencies - Improve content accuracy - Update documentation quality @@ -283,6 +302,7 @@ Maintainers will review PRs for: - Clarify PR description or validation evidence **PRs That May Be Rejected:** + - Inconsistent formatting - Missing validation evidence - Inaccurate or incomplete content @@ -324,4 +344,4 @@ Maintainers will review PRs for: ## Code of Conduct -This project adheres to the [Contributor Covenant Code of Conduct](CODE-OF-CONDUCT.md). By participating, you are expected to uphold this code. \ No newline at end of file +This project adheres to the [Contributor Covenant Code of Conduct](CODE-OF-CONDUCT.md). By participating, you are expected to uphold this code. diff --git a/.github/instructions/xml.instructions.md b/.github/instructions/xml.instructions.md index fe24a48d0..f6da1349f 100644 --- a/.github/instructions/xml.instructions.md +++ b/.github/instructions/xml.instructions.md @@ -12,12 +12,14 @@ applyTo: '**/*.xml' ## Context Loading for Copilot When working on this file type, you MUST: + 1. Read this entire instruction file before making any changes 2. Cross-reference with related instruction files (listed below) 3. Validate your understanding by checking the "Validation Checklist" section 4. If uncertain about any requirement, stop and request clarification **Related Instruction Files:** + - [project.instructions.md](project.instructions.md) - For project and build configuration - [csharp.instructions.md](csharp.instructions.md) - For C# code changes - [text.instructions.md](text.instructions.md) - For plain text documentation @@ -27,12 +29,14 @@ When working on this file type, you MUST: Before submitting any changes, verify: **XML Quality & Documentation:** + - [ ] XML syntax is valid - [ ] Schema compliance is maintained - [ ] Documentation is updated - [ ] Changes work as expected **Process:** + - [ ] Conventional commit format used - [ ] PR description includes validation evidence - [ ] All checklist items completed @@ -41,11 +45,13 @@ Before submitting any changes, verify: ## Decision Trees ### When to Request Human Review + - Is this a new XML schema or documentation standard? → Yes → Request expert guidance - Is this a breaking change to XML structure or documentation? → Yes → Document thoroughly and request review - Are you uncertain about XML schema or documentation? → Yes → Stop and request guidance ### When to Stop and Ask for Help + - Uncertain about XML requirements - Major changes to project configuration - Security or legal implications @@ -53,10 +59,12 @@ Before submitting any changes, verify: ## Common Mistakes to Avoid **DO NOT:** + - Skip validation steps - Submit changes without validation evidence **ALWAYS:** + - Read the entire instruction file first - Validate all XML and documentation changes - Include comprehensive documentation updates @@ -65,16 +73,19 @@ Before submitting any changes, verify: ## Context Management **Before Starting:** + - Read the complete instruction file - Understand the current file's purpose and structure - Identify all related files that may need updates **During Editing:** + - Keep track of all changes made - Validate each change against requirements - Maintain consistency with existing patterns **After Completing:** + - Review all changes against the validation checklist - Ensure all requirements are met - Prepare comprehensive PR description with evidence @@ -82,12 +93,14 @@ Before submitting any changes, verify: ## Handling Uncertainty **Stop and Request Help When:** + - You cannot explain your approach clearly - You're making "educated guesses" about XML or documentation - You're uncertain about project configuration - You cannot trace the logic in XML without narration **Escalation Process:** + 1. Stop all work immediately 2. Document what you were trying to accomplish 3. Explain what specific aspect is unclear @@ -97,6 +110,7 @@ Before submitting any changes, verify: ## Success Criteria Your changes are successful when: + - All XML and documentation validation checks pass - PR description is complete and accurate - All checklist items completed @@ -128,36 +142,38 @@ This repository contains several types of XML files: **Required for all public APIs:** - **Use `` tags for all type references** instead of plain text - - ✅ Good: `` or `` - - ❌ Bad: `Task` or `MoqKnownSymbols` + - Good: `` or `` + - Bad: `Task` or `MoqKnownSymbols` - **Use `` for C# keywords** - - ✅ Good: `` or `` - - ❌ Bad: `true` or `null` + - Good: `` or `` + - Bad: `true` or `null` - **Use `` for parameter references** - - ✅ Good: `` - - ❌ Bad: `mockedMemberSymbol` + - Good: `` + - Bad: `mockedMemberSymbol` - **Use `..` for inline code snippets** - - ✅ Good: `x => x.Method()` - - ❌ Bad: `x => x.Method()` + - Good: `x => x.Method()` + - Bad: `x => x.Method()` **Examples:** + ```xml /// -/// Determines whether a member symbol is either overridable or represents a +/// Determines whether a member symbol is either overridable or represents a /// / Result property -/// that Moq allows to be setup even if the underlying +/// that Moq allows to be setup even if the underlying /// property is not overridable. /// /// The mocked member symbol. /// A instance for resolving well-known types. /// -/// Returns when the member is overridable or is a -/// / Result property; +/// Returns when the member is overridable or is a +/// / Result property; /// otherwise . /// ``` **Validation:** + - All public APIs must have complete XML documentation - All type references must use `` tags - All C# keywords must use `` tags @@ -215,7 +231,7 @@ Before submitting a PR, ensure your changes pass all quality checks: Follow the [Conventional Commits](https://www.conventionalcommits.org/) specification: -``` +```text [optional scope]: [optional body] @@ -224,6 +240,7 @@ Follow the [Conventional Commits](https://www.conventionalcommits.org/) specific ``` **Types:** + - `feat`: New features - `fix`: Bug fixes - `docs`: Documentation changes @@ -243,6 +260,7 @@ Follow the [Conventional Commits](https://www.conventionalcommits.org/) specific Follow conventional commit format: `type(scope): description` **Description Requirements:** + 1. **Clear summary** of changes 2. **Problem statement** (what issue does this solve?) 3. **Solution description** (how does this solve the problem?) @@ -264,6 +282,7 @@ Before submitting a PR, ensure: ### Validation Evidence Requirements **What Constitutes Validation Evidence:** + - XML validation output - Schema compliance verification - Documentation accuracy checks @@ -271,6 +290,7 @@ Before submitting a PR, ensure: - Manual testing results for XML changes **Evidence Format:** + - Include validation logs, screenshots, or links to CI runs - Provide clear, readable evidence - Ensure evidence is recent and relevant @@ -297,6 +317,7 @@ Maintainers will review PRs for: ### Common Review Feedback **Frequently Requested Changes:** + - Fix XML syntax errors - Improve documentation quality - Update schema compliance @@ -304,6 +325,7 @@ Maintainers will review PRs for: - Clarify PR description or validation evidence **PRs That May Be Rejected:** + - Invalid XML syntax - Missing validation evidence - Schema compliance issues @@ -337,4 +359,4 @@ Maintainers will review PRs for: ## Code of Conduct -This project adheres to the [Contributor Covenant Code of Conduct](CODE-OF-CONDUCT.md). By participating, you are expected to uphold this code. \ No newline at end of file +This project adheres to the [Contributor Covenant Code of Conduct](CODE-OF-CONDUCT.md). By participating, you are expected to uphold this code. diff --git a/.github/instructions/yaml.instructions.md b/.github/instructions/yaml.instructions.md index 632691297..0186c92a4 100644 --- a/.github/instructions/yaml.instructions.md +++ b/.github/instructions/yaml.instructions.md @@ -12,12 +12,14 @@ applyTo: '**/*.{yml,yaml}' ## Context Loading for Copilot When working on this file type, you MUST: + 1. Read this entire instruction file before making any changes 2. Cross-reference with related instruction files (listed below) 3. Validate your understanding by checking the "Validation Checklist" section 4. If uncertain about any requirement, stop and request clarification **Related Instruction Files:** + - [project.instructions.md](project.instructions.md) - For project and build configuration - [json.instructions.md](json.instructions.md) - For configuration files - [shell.instructions.md](shell.instructions.md) - For scripts used in workflows @@ -27,12 +29,14 @@ When working on this file type, you MUST: Before submitting any changes, verify: **Workflow & Security:** + - [ ] YAML syntax is valid - [ ] All workflow requirements met - [ ] Security scan completed after dependency changes - [ ] Performance impact assessed **Process:** + - [ ] Conventional commit format used - [ ] PR description includes validation evidence - [ ] All checklist items completed @@ -41,11 +45,13 @@ Before submitting any changes, verify: ## Decision Trees ### When to Request Human Review + - Is this a new CI/CD workflow? → Yes → Request expert guidance - Is this a breaking change to workflow or security? → Yes → Document thoroughly and request review - Are you uncertain about workflow or security requirements? → Yes → Stop and request guidance ### When to Stop and Ask for Help + - Uncertain about workflow or security requirements - Major changes to CI/CD process - Security or legal implications @@ -53,11 +59,13 @@ Before submitting any changes, verify: ## Common Mistakes to Avoid **DO NOT:** + - Skip validation steps - Ignore security scanning after dependency changes - Submit changes without validation evidence **ALWAYS:** + - Read the entire instruction file first - Validate all workflow and security changes - Include comprehensive documentation updates @@ -66,16 +74,19 @@ Before submitting any changes, verify: ## Context Management **Before Starting:** + - Read the complete instruction file - Understand the current file's purpose and structure - Identify all related files that may need updates **During Editing:** + - Keep track of all changes made - Validate each change against requirements - Maintain consistency with existing patterns **After Completing:** + - Review all changes against the validation checklist - Ensure all requirements are met - Prepare comprehensive PR description with evidence @@ -83,12 +94,14 @@ Before submitting any changes, verify: ## Handling Uncertainty **Stop and Request Help When:** + - You cannot explain your approach clearly - You're making "educated guesses" about workflow or security - You're uncertain about CI/CD process - You cannot trace the logic in workflow without narration **Escalation Process:** + 1. Stop all work immediately 2. Document what you were trying to accomplish 3. Explain what specific aspect is unclear @@ -98,6 +111,7 @@ Before submitting any changes, verify: ## Success Criteria Your changes are successful when: + - All workflow and security validation checks pass - Security scan is clean - PR description is complete and accurate @@ -205,7 +219,7 @@ Before submitting a PR, ensure your changes pass all quality checks: Follow the [Conventional Commits](https://www.conventionalcommits.org/) specification: -``` +```text [optional scope]: [optional body] @@ -214,6 +228,7 @@ Follow the [Conventional Commits](https://www.conventionalcommits.org/) specific ``` **Types:** + - `feat`: New features - `fix`: Bug fixes - `docs`: Documentation changes @@ -233,6 +248,7 @@ Follow the [Conventional Commits](https://www.conventionalcommits.org/) specific Follow conventional commit format: `type(scope): description` **Description Requirements:** + 1. **Clear summary** of changes 2. **Problem statement** (what issue does this solve?) 3. **Solution description** (how does this solve the problem?) @@ -255,6 +271,7 @@ Before submitting a PR, ensure: ### Validation Evidence Requirements **What Constitutes Validation Evidence:** + - YAML validation output - Workflow execution logs - Performance benchmark results @@ -262,6 +279,7 @@ Before submitting a PR, ensure: - Manual testing results for workflow changes **Evidence Format:** + - Include logs, screenshots, or links to CI runs - Provide clear, readable evidence - Ensure evidence is recent and relevant @@ -289,6 +307,7 @@ Maintainers will review PRs for: ### Common Review Feedback **Frequently Requested Changes:** + - Add missing workflow validation - Update documentation for new workflows - Improve error handling and logging @@ -296,6 +315,7 @@ Maintainers will review PRs for: - Clarify PR description or validation evidence **PRs That May Be Rejected:** + - Missing validation evidence - Incomplete workflow testing - Performance regressions without justification @@ -329,4 +349,4 @@ Maintainers will review PRs for: ## Code of Conduct -This project adheres to the [Contributor Covenant Code of Conduct](CODE-OF-CONDUCT.md). By participating, you are expected to uphold this code. \ No newline at end of file +This project adheres to the [Contributor Covenant Code of Conduct](CODE-OF-CONDUCT.md). By participating, you are expected to uphold this code. diff --git a/.github/labeler.yml b/.github/labeler.yml index 90194d51f..6973f4115 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -1,41 +1,41 @@ # Add 'build' label to any change in the 'build' directory build: -- changed-files: - - any-glob-to-any-file: 'build/**/*' + - changed-files: + - any-glob-to-any-file: 'build/**/*' # Add 'dependencies' label to any change in one of the packages files dependencies: -- changed-files: - - any-glob-to-any-file: ['build/**/Packages.props', 'Directory.Packages.props'] + - changed-files: + - any-glob-to-any-file: ['build/**/Packages.props', 'Directory.Packages.props'] # Add 'documentation' label to any change within the 'docs' directory or any '.md' file documentation: -- changed-files: - - any-glob-to-any-file: ['docs/**', '**/*.md'] + - changed-files: + - any-glob-to-any-file: ['docs/**', '**/*.md'] # Add 'feature' label to any PR where the head branch name starts with `feature` or has a `feature` section in the name feature: - - head-branch: ['^feature', 'feature'] + - head-branch: ['^feature', 'feature'] # Add 'bug' label to any PR where the head branch name starts with `bug` or has a `bug` section in the name bug: - - head-branch: ['^bug', 'bug'] + - head-branch: ['^bug', 'bug'] # Add 'releasable' label to any PR that is opened against the `main` branch releasable: - - base-branch: 'main' + - base-branch: 'main' # Add 'github_actions' label to any change to one of the GitHub workflows or configuration files github_actions: -- changed-files: - - any-glob-to-any-file: ['.github/workflows/*.yml', '.github/dependabot.yml', '.github/labeler.yml'] + - changed-files: + - any-glob-to-any-file: ['.github/workflows/*.yml', '.github/dependabot.yml', '.github/labeler.yml'] # Add 'analyzers' label to any change to an analyzer, code fix, or shipping documentation analyzers: -- changed-files: - - any-glob-to-any-file: ['src/Moq.Analyzers/AnalyzerReleases.*.md', 'src/Moq.Analyzers/**/*Analyzer.cs', 'src/Moq.Analyzers/**/*CodeFix.cs', 'src/Analyzers/**/*Analyzer.cs', 'src/CodeFixes/*Fixer.cs', 'src/Analyzers/AnalyzerReleases.*.md'] + - changed-files: + - any-glob-to-any-file: ['src/Moq.Analyzers/AnalyzerReleases.*.md', 'src/Moq.Analyzers/**/*Analyzer.cs', 'src/Moq.Analyzers/**/*CodeFix.cs', 'src/Analyzers/**/*Analyzer.cs', 'src/CodeFixes/*Fixer.cs', 'src/Analyzers/AnalyzerReleases.*.md'] # Add 'powershell' label to any change to a PowerShell file powershell: -- changed-files: - - any-glob-to-any-file: ['*.ps1', '*.ps1x', '*.psm1', '*.psd1', '*.ps1xml', '*.pssc', '*.psrc', '*.cdxml'] + - changed-files: + - any-glob-to-any-file: ['*.ps1', '*.ps1x', '*.psm1', '*.psd1', '*.ps1xml', '*.pssc', '*.psrc', '*.cdxml'] diff --git a/.github/workflows/copilot-setup-steps.yml b/.github/workflows/copilot-setup-steps.yml index 1ec7a0f3b..be046f16d 100644 --- a/.github/workflows/copilot-setup-steps.yml +++ b/.github/workflows/copilot-setup-steps.yml @@ -22,7 +22,7 @@ jobs: # the above doesn't work as expected, so pre-empt a workaround to avoid agent getting stuck and ignoring instructions in copilot-instructions.md - name: NBGV workaround run: git fetch --unshallow - + - name: Setup .NET uses: actions/setup-dotnet@v5 with: diff --git a/.github/workflows/devskim.yml b/.github/workflows/devskim.yml index 2446df634..1a184c5c0 100644 --- a/.github/workflows/devskim.yml +++ b/.github/workflows/devskim.yml @@ -7,9 +7,9 @@ name: DevSkim on: push: - branches: [ "main" ] + branches: ["main"] pull_request: - branches: [ "main" ] + branches: ["main"] schedule: - cron: '32 8 * * 1' diff --git a/.github/workflows/label-issues.yml b/.github/workflows/label-issues.yml index 72b4cafba..1e93e8b8e 100644 --- a/.github/workflows/label-issues.yml +++ b/.github/workflows/label-issues.yml @@ -22,16 +22,16 @@ jobs: // Get the issue body and title const body = context.payload.issue.body let title = context.payload.issue.title - + // Define the labels array let labels = ["triage"] - + // Check if the body or the title contains the word 'PowerShell' (case-insensitive) if ((body != null && body.match(/powershell/i)) || (title != null && title.match(/powershell/i))) { // Add the 'powershell' label to the array labels.push("powershell") } - + // Check if the body or the title contains the words 'dotnet', '.net', 'c#' or 'csharp' (case-insensitive) if ((body != null && body.match(/.net/i)) || (title != null && title.match(/.net/i)) || (body != null && body.match(/dotnet/i)) || (title != null && title.match(/dotnet/i)) || diff --git a/.github/workflows/label-pr.yml b/.github/workflows/label-pr.yml index ccd5ded9f..6bc0e39a2 100644 --- a/.github/workflows/label-pr.yml +++ b/.github/workflows/label-pr.yml @@ -18,4 +18,4 @@ jobs: steps: - uses: actions/labeler@v6 with: - repo-token: "${{ secrets.GH_ACTIONS_PR_WRITE }}" \ No newline at end of file + repo-token: "${{ secrets.GH_ACTIONS_PR_WRITE }}" diff --git a/.github/workflows/linters.yml b/.github/workflows/linters.yml new file mode 100644 index 000000000..4a34e4ec2 --- /dev/null +++ b/.github/workflows/linters.yml @@ -0,0 +1,40 @@ +name: Lint + +on: + push: + pull_request: + +permissions: {} + +jobs: + build: + name: Lint + runs-on: ubuntu-latest + + permissions: + contents: read + # To report GitHub Actions status checks + statuses: write + + steps: + - name: Checkout code + uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 + with: + # super-linter needs the full git history to get the + # list of files that changed across commits + fetch-depth: 0 + + - name: Super-linter + uses: super-linter/super-linter@61abc07d755095a68f4987d1c2c3d1d64408f1f9 # v8.5.0 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + DEFAULT_BRANCH: main + LINTER_RULES_PATH: "." + MARKDOWN_CONFIG_FILE: ".markdownlint.json" + YAML_CONFIG_FILE: ".yamllint.yml" + VALIDATE_GITHUB_ACTIONS: true + VALIDATE_YAML: true + VALIDATE_MARKDOWN: true + VALIDATE_JSON: true + VALIDATE_BASH: true + FILTER_REGEX_EXCLUDE: '(\.verified\.(txt|xml|json)$|AnalyzerReleases\.(Shipped|Unshipped)\.md$|/\.cursor/|/\.github/chatmodes/|/\.windsurf/|/\.agents/)' diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 91d96d4ca..a0477b24c 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -86,83 +86,83 @@ jobs: runs-on: ubuntu-24.04-arm steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - if: needs.check-paths.outputs.code-changed == 'true' - with: - fetch-depth: 0 - - - name: Setup, Restore, and Build Solution - if: needs.check-paths.outputs.code-changed == 'true' - uses: ./.github/actions/setup-restore-build - - - name: Validate analyzer host compatibility - if: needs.check-paths.outputs.code-changed == 'true' - shell: pwsh - run: | - # Verify shipped analyzer DLLs don't reference assembly versions - # exceeding what the minimum supported SDK host (.NET 8) provides. - # See: https://github.com/rjmurillo/moq.analyzers/issues/850 - $maxVersions = @{ - 'System.Collections.Immutable' = [Version]'8.0.0.0' - 'System.Reflection.Metadata' = [Version]'8.0.0.0' - } - $shippedDlls = @( - 'artifacts/bin/Moq.Analyzers/release/Moq.Analyzers.dll', - 'artifacts/bin/Moq.Analyzers/release/Moq.CodeFixes.dll', - 'artifacts/bin/Moq.Analyzers/release/Microsoft.CodeAnalysis.AnalyzerUtilities.dll' - ) - $failed = $false - foreach ($dll in $shippedDlls) { - $name = [System.IO.Path]::GetFileName($dll) - $bytes = [System.IO.File]::ReadAllBytes($dll) - $asm = [System.Reflection.Assembly]::Load($bytes) - $dllFailed = $false - foreach ($ref in $asm.GetReferencedAssemblies()) { - if ($maxVersions.ContainsKey($ref.Name) -and $ref.Version -gt $maxVersions[$ref.Name]) { - Write-Error "$name references $($ref.Name) $($ref.Version), max allowed is $($maxVersions[$ref.Name])" - $failed = $true - $dllFailed = $true + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + if: needs.check-paths.outputs.code-changed == 'true' + with: + fetch-depth: 0 + + - name: Setup, Restore, and Build Solution + if: needs.check-paths.outputs.code-changed == 'true' + uses: ./.github/actions/setup-restore-build + + - name: Validate analyzer host compatibility + if: needs.check-paths.outputs.code-changed == 'true' + shell: pwsh + run: | + # Verify shipped analyzer DLLs don't reference assembly versions + # exceeding what the minimum supported SDK host (.NET 8) provides. + # See: https://github.com/rjmurillo/moq.analyzers/issues/850 + $maxVersions = @{ + 'System.Collections.Immutable' = [Version]'8.0.0.0' + 'System.Reflection.Metadata' = [Version]'8.0.0.0' + } + $shippedDlls = @( + 'artifacts/bin/Moq.Analyzers/release/Moq.Analyzers.dll', + 'artifacts/bin/Moq.Analyzers/release/Moq.CodeFixes.dll', + 'artifacts/bin/Moq.Analyzers/release/Microsoft.CodeAnalysis.AnalyzerUtilities.dll' + ) + $failed = $false + foreach ($dll in $shippedDlls) { + $name = [System.IO.Path]::GetFileName($dll) + $bytes = [System.IO.File]::ReadAllBytes($dll) + $asm = [System.Reflection.Assembly]::Load($bytes) + $dllFailed = $false + foreach ($ref in $asm.GetReferencedAssemblies()) { + if ($maxVersions.ContainsKey($ref.Name) -and $ref.Version -gt $maxVersions[$ref.Name]) { + Write-Error "$name references $($ref.Name) $($ref.Version), max allowed is $($maxVersions[$ref.Name])" + $failed = $true + $dllFailed = $true + } } + if (-not $dllFailed) { Write-Host "$name - OK" } } - if (-not $dllFailed) { Write-Host "$name - OK" } - } - if ($failed) { exit 1 } - - - name: Upload binlogs - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 - if: needs.check-paths.outputs.code-changed == 'true' && (success() || failure()) - with: - name: binlogs - path: ./artifacts/logs - if-no-files-found: error - - - name: Upload SARIF files - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 - if: needs.check-paths.outputs.code-changed == 'true' && (success() || failure()) - with: - name: SARIF files - path: ./artifacts/obj/**/*.sarif - - - name: Upload packages - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 - if: needs.check-paths.outputs.code-changed == 'true' - with: - name: packages - path: | - ./artifacts/package - if-no-files-found: error - - - name: Upload artifacts - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 - if: needs.check-paths.outputs.code-changed == 'true' && (success() || failure()) - with: - name: artifacts - path: ./artifacts - if-no-files-found: error - - - name: Skip notice - if: needs.check-paths.outputs.code-changed != 'true' - run: echo "Skipped -- no code changes detected" + if ($failed) { exit 1 } + + - name: Upload binlogs + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 + if: needs.check-paths.outputs.code-changed == 'true' && (success() || failure()) + with: + name: binlogs + path: ./artifacts/logs + if-no-files-found: error + + - name: Upload SARIF files + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 + if: needs.check-paths.outputs.code-changed == 'true' && (success() || failure()) + with: + name: SARIF files + path: ./artifacts/obj/**/*.sarif + + - name: Upload packages + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 + if: needs.check-paths.outputs.code-changed == 'true' + with: + name: packages + path: | + ./artifacts/package + if-no-files-found: error + + - name: Upload artifacts + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 + if: needs.check-paths.outputs.code-changed == 'true' && (success() || failure()) + with: + name: artifacts + path: ./artifacts + if-no-files-found: error + + - name: Skip notice + if: needs.check-paths.outputs.code-changed != 'true' + run: echo "Skipped -- no code changes detected" # Run unit tests on multiple platforms. # Build happens independently on each runner so tests use platform-native binaries. @@ -181,90 +181,90 @@ jobs: IS_TARGET_MAIN: ${{ github.ref == 'refs/heads/main' }} steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - if: needs.check-paths.outputs.code-changed == 'true' - with: - fetch-depth: 0 - - - name: Setup, Restore, and Build Solution - if: needs.check-paths.outputs.code-changed == 'true' - uses: ./.github/actions/setup-restore-build - - - name: Restore Code Coverage history - if: needs.check-paths.outputs.code-changed == 'true' - uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7 - with: - name: CoverageHistory-${{ matrix.os }} - path: ./artifacts/TestResults/coveragehistory - continue-on-error: true - - - name: Test - if: needs.check-paths.outputs.code-changed == 'true' - run: dotnet test --no-build --configuration Release --settings ./build/targets/tests/test.runsettings - env: - REPORTGENERATOR_LICENSE: ${{ secrets.REPORTGENERATOR_LICENSE }} - - - name: Upload coverage history - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 - if: >- - needs.check-paths.outputs.code-changed == 'true' - && success() - && env.IS_TARGET_MAIN == 'true' - with: - name: CoverageHistory-${{ matrix.os }} - path: ./artifacts/TestResults/coveragehistory - - - name: Upload *.received.* files - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 - if: needs.check-paths.outputs.code-changed == 'true' && failure() - with: - name: verify-test-results-${{ matrix.os }} - path: | - **/*.received.* - - - name: Upload Test Report - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 - if: needs.check-paths.outputs.code-changed == 'true' && (success() || failure()) - with: - name: .NET Test Reports (${{ matrix.os }}) - path: "artifacts/TestResults/**/*.trx" - if-no-files-found: error - - - name: Upload Code Coverage Report - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 - if: needs.check-paths.outputs.code-changed == 'true' && (success() || failure()) - with: - name: .NET Code Coverage Reports (${{ matrix.os }}) - path: "artifacts/TestResults/coverage/**" - - - name: Publish coverage summary to GitHub - if: needs.check-paths.outputs.code-changed == 'true' - run: cat artifacts/TestResults/coverage/SummaryGithub.md >> $GITHUB_STEP_SUMMARY - shell: bash - - - name: Upload coverage data to Codacy - if: >- - needs.check-paths.outputs.code-changed == 'true' - && runner.os == 'Linux' - && env.IS_CODACY_COVERAGE_ALLOWED == 'true' - uses: codacy/codacy-coverage-reporter-action@89d6c85cfafaec52c72b6c5e8b2878d33104c699 # v1.3.0 - with: - project-token: ${{ secrets.CODACY_PROJECT_TOKEN }} - coverage-reports: ${{ github.workspace }}/artifacts/TestResults/coverage/Cobertura.xml - - - name: Upload coverage data to Qlty - if: >- - needs.check-paths.outputs.code-changed == 'true' - && runner.os == 'Linux' - && env.IS_QLTY_COVERAGE_ALLOWED == 'true' - uses: qltysh/qlty-action/coverage@a19242102d17e497f437d7466aa01b528537e899 # v2 - with: - token: ${{ secrets.QLTY_COVERAGE_TOKEN }} - files: ${{ github.workspace }}/artifacts/TestResults/coverage/Cobertura.xml - - - name: Skip notice - if: needs.check-paths.outputs.code-changed != 'true' - run: echo "Skipped -- no code changes detected" + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + if: needs.check-paths.outputs.code-changed == 'true' + with: + fetch-depth: 0 + + - name: Setup, Restore, and Build Solution + if: needs.check-paths.outputs.code-changed == 'true' + uses: ./.github/actions/setup-restore-build + + - name: Restore Code Coverage history + if: needs.check-paths.outputs.code-changed == 'true' + uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7 + with: + name: CoverageHistory-${{ matrix.os }} + path: ./artifacts/TestResults/coveragehistory + continue-on-error: true + + - name: Test + if: needs.check-paths.outputs.code-changed == 'true' + run: dotnet test --no-build --configuration Release --settings ./build/targets/tests/test.runsettings + env: + REPORTGENERATOR_LICENSE: ${{ secrets.REPORTGENERATOR_LICENSE }} + + - name: Upload coverage history + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 + if: >- + needs.check-paths.outputs.code-changed == 'true' + && success() + && env.IS_TARGET_MAIN == 'true' + with: + name: CoverageHistory-${{ matrix.os }} + path: ./artifacts/TestResults/coveragehistory + + - name: Upload *.received.* files + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 + if: needs.check-paths.outputs.code-changed == 'true' && failure() + with: + name: verify-test-results-${{ matrix.os }} + path: | + **/*.received.* + + - name: Upload Test Report + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 + if: needs.check-paths.outputs.code-changed == 'true' && (success() || failure()) + with: + name: .NET Test Reports (${{ matrix.os }}) + path: "artifacts/TestResults/**/*.trx" + if-no-files-found: error + + - name: Upload Code Coverage Report + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 + if: needs.check-paths.outputs.code-changed == 'true' && (success() || failure()) + with: + name: .NET Code Coverage Reports (${{ matrix.os }}) + path: "artifacts/TestResults/coverage/**" + + - name: Publish coverage summary to GitHub + if: needs.check-paths.outputs.code-changed == 'true' + run: cat artifacts/TestResults/coverage/SummaryGithub.md >> "$GITHUB_STEP_SUMMARY" + shell: bash + + - name: Upload coverage data to Codacy + if: >- + needs.check-paths.outputs.code-changed == 'true' + && runner.os == 'Linux' + && env.IS_CODACY_COVERAGE_ALLOWED == 'true' + uses: codacy/codacy-coverage-reporter-action@89d6c85cfafaec52c72b6c5e8b2878d33104c699 # v1.3.0 + with: + project-token: ${{ secrets.CODACY_PROJECT_TOKEN }} + coverage-reports: ${{ github.workspace }}/artifacts/TestResults/coverage/Cobertura.xml + + - name: Upload coverage data to Qlty + if: >- + needs.check-paths.outputs.code-changed == 'true' + && runner.os == 'Linux' + && env.IS_QLTY_COVERAGE_ALLOWED == 'true' + uses: qltysh/qlty-action/coverage@a19242102d17e497f437d7466aa01b528537e899 # v2 + with: + token: ${{ secrets.QLTY_COVERAGE_TOKEN }} + files: ${{ github.workspace }}/artifacts/TestResults/coverage/Cobertura.xml + + - name: Skip notice + if: needs.check-paths.outputs.code-changed != 'true' + run: echo "Skipped -- no code changes detected" # Verify the shipped nupkg loads without CS8032 on every supported host. # This is the end-to-end integration test for issue #850. @@ -320,127 +320,127 @@ jobs: build-engine: msbuild steps: - - name: Setup .NET ${{ matrix.dotnet-version }} - if: needs.check-paths.outputs.code-changed == 'true' - uses: actions/setup-dotnet@baa11fbfe1d6520db94683bd5c7a3818018e4309 # v5 - with: - dotnet-version: ${{ matrix.dotnet-version }} - - - name: Setup MSBuild - if: needs.check-paths.outputs.code-changed == 'true' && matrix.build-engine == 'msbuild' - uses: microsoft/setup-msbuild@6fb02220983dee41ce7ae257b6f4d8f9bf5ed4ce # v2 - - - name: Download nupkg artifact - if: needs.check-paths.outputs.code-changed == 'true' - uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7 - with: - name: packages - path: ./local-feed - - - name: Create test project - if: needs.check-paths.outputs.code-changed == 'true' - shell: pwsh - run: | - $pkg = Get-ChildItem ./local-feed -Recurse -Filter 'Moq.Analyzers.*.nupkg' | - Where-Object { $_.Name -notlike '*.symbols.*' } | - Select-Object -First 1 - if (-not $pkg) { - Write-Error "Could not find Moq.Analyzers.*.nupkg (excluding symbols) in ./local-feed" - exit 1 - } - $version = $pkg.Name -replace '^Moq\.Analyzers\.' -replace '\.nupkg$' - $feedDir = (Resolve-Path $pkg.DirectoryName).Path - Write-Host "Testing Moq.Analyzers $version with ${{ matrix.build-engine }} / ${{ matrix.tfm }}" - echo "ANALYZER_VERSION=$version" >> $env:GITHUB_ENV - echo "FEED_DIR=$feedDir" >> $env:GITHUB_ENV - - New-Item -ItemType Directory -Path test-project | Out-Null - - @" - - - - - - - - - "@ | Set-Content test-project/nuget.config - - @" - - - Library - ${{ matrix.tfm }} - latest - - - - - - - "@ | Set-Content test-project/TestAnalyzerLoad.csproj - - @" - namespace TestAnalyzerLoad; - public class Placeholder { } - "@ | Set-Content test-project/Placeholder.cs - - - name: Build with dotnet CLI - if: needs.check-paths.outputs.code-changed == 'true' && matrix.build-engine == 'dotnet' - shell: pwsh - working-directory: test-project - run: | - $output = dotnet build -v n 2>&1 | Out-String - $buildExitCode = $LASTEXITCODE - Write-Host $output - - if ($buildExitCode -ne 0) { - Write-Host "::error::Build failed with exit code $buildExitCode (dotnet / ${{ matrix.tfm }})" - exit $buildExitCode - } - - if ($output -match 'CS8032') { - Write-Host "::error::CS8032: analyzer failed to load (dotnet / ${{ matrix.tfm }}). See https://github.com/rjmurillo/moq.analyzers/issues/850" - exit 1 - } - if ($output -match '(?i)could not load file or assembly') { - Write-Host "::error::Assembly binding failure (dotnet / ${{ matrix.tfm }}). See https://github.com/rjmurillo/moq.analyzers/issues/850" - exit 1 - } - - Write-Host "" - Write-Host "Analyzer loaded successfully (dotnet / ${{ matrix.tfm }})" - - - name: Build with MSBuild - if: needs.check-paths.outputs.code-changed == 'true' && matrix.build-engine == 'msbuild' - shell: pwsh - working-directory: test-project - run: | - $output = msbuild TestAnalyzerLoad.csproj -restore -p:Configuration=Release -v:n 2>&1 | Out-String - $buildExitCode = $LASTEXITCODE - Write-Host $output - - if ($buildExitCode -ne 0) { - Write-Host "::error::Build failed with exit code $buildExitCode (msbuild / ${{ matrix.tfm }})" - exit $buildExitCode - } - - if ($output -match 'CS8032') { - Write-Host "::error::CS8032: analyzer failed to load (msbuild / ${{ matrix.tfm }}). See https://github.com/rjmurillo/moq.analyzers/issues/850" - exit 1 - } - if ($output -match '(?i)could not load file or assembly') { - Write-Host "::error::Assembly binding failure (msbuild / ${{ matrix.tfm }}). See https://github.com/rjmurillo/moq.analyzers/issues/850" - exit 1 - } - - Write-Host "" - Write-Host "Analyzer loaded successfully (msbuild / ${{ matrix.tfm }})" - - - name: Skip notice - if: needs.check-paths.outputs.code-changed != 'true' - run: echo "Skipped -- no code changes detected" + - name: Setup .NET ${{ matrix.dotnet-version }} + if: needs.check-paths.outputs.code-changed == 'true' + uses: actions/setup-dotnet@baa11fbfe1d6520db94683bd5c7a3818018e4309 # v5 + with: + dotnet-version: ${{ matrix.dotnet-version }} + + - name: Setup MSBuild + if: needs.check-paths.outputs.code-changed == 'true' && matrix.build-engine == 'msbuild' + uses: microsoft/setup-msbuild@6fb02220983dee41ce7ae257b6f4d8f9bf5ed4ce # v2 + + - name: Download nupkg artifact + if: needs.check-paths.outputs.code-changed == 'true' + uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7 + with: + name: packages + path: ./local-feed + + - name: Create test project + if: needs.check-paths.outputs.code-changed == 'true' + shell: pwsh + run: | + $pkg = Get-ChildItem ./local-feed -Recurse -Filter 'Moq.Analyzers.*.nupkg' | + Where-Object { $_.Name -notlike '*.symbols.*' } | + Select-Object -First 1 + if (-not $pkg) { + Write-Error "Could not find Moq.Analyzers.*.nupkg (excluding symbols) in ./local-feed" + exit 1 + } + $version = $pkg.Name -replace '^Moq\.Analyzers\.' -replace '\.nupkg$' + $feedDir = (Resolve-Path $pkg.DirectoryName).Path + Write-Host "Testing Moq.Analyzers $version with ${{ matrix.build-engine }} / ${{ matrix.tfm }}" + echo "ANALYZER_VERSION=$version" >> $env:GITHUB_ENV + echo "FEED_DIR=$feedDir" >> $env:GITHUB_ENV + + New-Item -ItemType Directory -Path test-project | Out-Null + + @" + + + + + + + + + "@ | Set-Content test-project/nuget.config + + @" + + + Library + ${{ matrix.tfm }} + latest + + + + + + + "@ | Set-Content test-project/TestAnalyzerLoad.csproj + + @" + namespace TestAnalyzerLoad; + public class Placeholder { } + "@ | Set-Content test-project/Placeholder.cs + + - name: Build with dotnet CLI + if: needs.check-paths.outputs.code-changed == 'true' && matrix.build-engine == 'dotnet' + shell: pwsh + working-directory: test-project + run: | + $output = dotnet build -v n 2>&1 | Out-String + $buildExitCode = $LASTEXITCODE + Write-Host $output + + if ($buildExitCode -ne 0) { + Write-Host "::error::Build failed with exit code $buildExitCode (dotnet / ${{ matrix.tfm }})" + exit $buildExitCode + } + + if ($output -match 'CS8032') { + Write-Host "::error::CS8032: analyzer failed to load (dotnet / ${{ matrix.tfm }}). See https://github.com/rjmurillo/moq.analyzers/issues/850" + exit 1 + } + if ($output -match '(?i)could not load file or assembly') { + Write-Host "::error::Assembly binding failure (dotnet / ${{ matrix.tfm }}). See https://github.com/rjmurillo/moq.analyzers/issues/850" + exit 1 + } + + Write-Host "" + Write-Host "Analyzer loaded successfully (dotnet / ${{ matrix.tfm }})" + + - name: Build with MSBuild + if: needs.check-paths.outputs.code-changed == 'true' && matrix.build-engine == 'msbuild' + shell: pwsh + working-directory: test-project + run: | + $output = msbuild TestAnalyzerLoad.csproj -restore -p:Configuration=Release -v:n 2>&1 | Out-String + $buildExitCode = $LASTEXITCODE + Write-Host $output + + if ($buildExitCode -ne 0) { + Write-Host "::error::Build failed with exit code $buildExitCode (msbuild / ${{ matrix.tfm }})" + exit $buildExitCode + } + + if ($output -match 'CS8032') { + Write-Host "::error::CS8032: analyzer failed to load (msbuild / ${{ matrix.tfm }}). See https://github.com/rjmurillo/moq.analyzers/issues/850" + exit 1 + } + if ($output -match '(?i)could not load file or assembly') { + Write-Host "::error::Assembly binding failure (msbuild / ${{ matrix.tfm }}). See https://github.com/rjmurillo/moq.analyzers/issues/850" + exit 1 + } + + Write-Host "" + Write-Host "Analyzer loaded successfully (msbuild / ${{ matrix.tfm }})" + + - name: Skip notice + if: needs.check-paths.outputs.code-changed != 'true' + run: echo "Skipped -- no code changes detected" # Performance validation runs last, after build and tests confirm correctness. # Builds from source on Linux ARM to get consistent benchmark results. @@ -453,100 +453,100 @@ jobs: FORCE_PERF_BASELINE: ${{ github.event.inputs.force_baseline }} steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - if: needs.check-paths.outputs.code-changed == 'true' - with: - fetch-depth: 0 - - - name: Setup, Restore, and Build Solution - if: needs.check-paths.outputs.code-changed == 'true' - uses: ./.github/actions/setup-restore-build - - - name: Get baseline SHA - if: needs.check-paths.outputs.code-changed == 'true' - id: get-baseline-sha - run: | - if (-not (Test-Path build/perf/baseline.json)) { - Write-Error "baseline.json not found - aborting performance job." - exit 1 - } - $baseline = Get-Content build/perf/baseline.json | ConvertFrom-Json - echo "sha=$($baseline.sha)" >> $env:GITHUB_OUTPUT - shell: pwsh - - # The baseline SHA may require a different .NET SDK version - - name: Checkout baseline - if: needs.check-paths.outputs.code-changed == 'true' - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - with: - ref: ${{ steps.get-baseline-sha.outputs.sha }} - fetch-depth: 0 - - - name: Setup .NET for performance baseline - if: needs.check-paths.outputs.code-changed == 'true' - uses: actions/setup-dotnet@baa11fbfe1d6520db94683bd5c7a3818018e4309 # v5 - with: - global-json-file: ./global.json - - - name: Restore checkout - if: needs.check-paths.outputs.code-changed == 'true' - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - with: - fetch-depth: 0 - - - name: Get performance test filter - if: needs.check-paths.outputs.code-changed == 'true' - id: get-perf-filter - run: | - if ('${{ env.RUN_FULL_PERF }}' -eq 'true') { - echo "filter='*'" >> $env:GITHUB_OUTPUT - } else { - echo "filter='*(FileCount: 1)'" >> $env:GITHUB_OUTPUT - } - shell: pwsh - - - name: Validate performance - if: needs.check-paths.outputs.code-changed == 'true' - shell: pwsh - run: ${{ github.workspace }}/build/scripts/perf/PerfCore.ps1 -v diag -diff -ci -filter ${{ steps.get-perf-filter.outputs.filter }} - - - name: Publish baseline performance summaries to GitHub - if: needs.check-paths.outputs.code-changed == 'true' && (success() || failure()) - shell: pwsh - run: | - $resultsDir = "artifacts/performance/perfResults/baseline/results" - if (Test-Path $resultsDir) { - Add-Content -Path $env:GITHUB_STEP_SUMMARY -Value "### Baseline Performance Results" - Add-Content -Path $env:GITHUB_STEP_SUMMARY -Value "Baseline SHA: ${{ steps.get-baseline-sha.outputs.sha }}" - $files = Get-ChildItem -Path $resultsDir -Filter "*-report-github.md" | Sort-Object Name - foreach ($file in $files) { - Get-Content $file.FullName | Out-File -Append -FilePath $env:GITHUB_STEP_SUMMARY + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + if: needs.check-paths.outputs.code-changed == 'true' + with: + fetch-depth: 0 + + - name: Setup, Restore, and Build Solution + if: needs.check-paths.outputs.code-changed == 'true' + uses: ./.github/actions/setup-restore-build + + - name: Get baseline SHA + if: needs.check-paths.outputs.code-changed == 'true' + id: get-baseline-sha + run: | + if (-not (Test-Path build/perf/baseline.json)) { + Write-Error "baseline.json not found - aborting performance job." + exit 1 } - Add-Content -Path $env:GITHUB_STEP_SUMMARY -Value "---" - } - - - name: Publish performance summaries to GitHub - if: needs.check-paths.outputs.code-changed == 'true' && (success() || failure()) - shell: pwsh - run: | - $resultsDir = "artifacts/performance/perfResults/perfTest/results" - if (Test-Path $resultsDir) { - Add-Content -Path $env:GITHUB_STEP_SUMMARY -Value "### Current Performance Results" - $files = Get-ChildItem -Path $resultsDir -Filter "*-report-github.md" | Sort-Object Name - foreach ($file in $files) { - Get-Content $file.FullName | Out-File -Append -FilePath $env:GITHUB_STEP_SUMMARY + $baseline = Get-Content build/perf/baseline.json | ConvertFrom-Json + echo "sha=$($baseline.sha)" >> $env:GITHUB_OUTPUT + shell: pwsh + + # The baseline SHA may require a different .NET SDK version + - name: Checkout baseline + if: needs.check-paths.outputs.code-changed == 'true' + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + with: + ref: ${{ steps.get-baseline-sha.outputs.sha }} + fetch-depth: 0 + + - name: Setup .NET for performance baseline + if: needs.check-paths.outputs.code-changed == 'true' + uses: actions/setup-dotnet@baa11fbfe1d6520db94683bd5c7a3818018e4309 # v5 + with: + global-json-file: ./global.json + + - name: Restore checkout + if: needs.check-paths.outputs.code-changed == 'true' + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + with: + fetch-depth: 0 + + - name: Get performance test filter + if: needs.check-paths.outputs.code-changed == 'true' + id: get-perf-filter + run: | + if ('${{ env.RUN_FULL_PERF }}' -eq 'true') { + echo "filter='*'" >> $env:GITHUB_OUTPUT + } else { + echo "filter='*(FileCount: 1)'" >> $env:GITHUB_OUTPUT } - Add-Content -Path $env:GITHUB_STEP_SUMMARY -Value "---" - } - - - name: Upload performance files - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 - if: needs.check-paths.outputs.code-changed == 'true' && (success() || failure()) - with: - name: performance - path: | - ./artifacts/performance/** - - - name: Skip notice - if: needs.check-paths.outputs.code-changed != 'true' - run: echo "Skipped -- no code changes detected" + shell: pwsh + + - name: Validate performance + if: needs.check-paths.outputs.code-changed == 'true' + shell: pwsh + run: ${{ github.workspace }}/build/scripts/perf/PerfCore.ps1 -v diag -diff -ci -filter ${{ steps.get-perf-filter.outputs.filter }} + + - name: Publish baseline performance summaries to GitHub + if: needs.check-paths.outputs.code-changed == 'true' && (success() || failure()) + shell: pwsh + run: | + $resultsDir = "artifacts/performance/perfResults/baseline/results" + if (Test-Path $resultsDir) { + Add-Content -Path $env:GITHUB_STEP_SUMMARY -Value "### Baseline Performance Results" + Add-Content -Path $env:GITHUB_STEP_SUMMARY -Value "Baseline SHA: ${{ steps.get-baseline-sha.outputs.sha }}" + $files = Get-ChildItem -Path $resultsDir -Filter "*-report-github.md" | Sort-Object Name + foreach ($file in $files) { + Get-Content $file.FullName | Out-File -Append -FilePath $env:GITHUB_STEP_SUMMARY + } + Add-Content -Path $env:GITHUB_STEP_SUMMARY -Value "---" + } + + - name: Publish performance summaries to GitHub + if: needs.check-paths.outputs.code-changed == 'true' && (success() || failure()) + shell: pwsh + run: | + $resultsDir = "artifacts/performance/perfResults/perfTest/results" + if (Test-Path $resultsDir) { + Add-Content -Path $env:GITHUB_STEP_SUMMARY -Value "### Current Performance Results" + $files = Get-ChildItem -Path $resultsDir -Filter "*-report-github.md" | Sort-Object Name + foreach ($file in $files) { + Get-Content $file.FullName | Out-File -Append -FilePath $env:GITHUB_STEP_SUMMARY + } + Add-Content -Path $env:GITHUB_STEP_SUMMARY -Value "---" + } + + - name: Upload performance files + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 + if: needs.check-paths.outputs.code-changed == 'true' && (success() || failure()) + with: + name: performance + path: | + ./artifacts/performance/** + + - name: Skip notice + if: needs.check-paths.outputs.code-changed != 'true' + run: echo "Skipped -- no code changes detected" diff --git a/.github/workflows/milestone-tracking.yml b/.github/workflows/milestone-tracking.yml index 183c784f0..cb52aa746 100644 --- a/.github/workflows/milestone-tracking.yml +++ b/.github/workflows/milestone-tracking.yml @@ -10,44 +10,47 @@ jobs: runs-on: ubuntu-24.04-arm if: ${{ github.repository == 'rjmurillo/moq.analyzers' && github.event.pull_request.merged_at != null && github.event.pull_request.milestone == null && github.event.pull_request.base.ref == 'main' }} steps: - - name: Get milestone data - env: - GITHUB_TOKEN: ${{ secrets.GH_ACTIONS_PR_WRITE }} - ORGANIZATION: rjmurillo - REPOSITORY: moq.analyzers - MILESTONE_NAME: vNext - run: | - gh api graphql -f query=' - query($org: String!, $repo: String!, $milestone: String!) { - repository(name: $repo, owner: $org) { - milestones(query: $milestone, first: 2) { - nodes { - id - title + - name: Get milestone data + env: + GITHUB_TOKEN: ${{ secrets.GH_ACTIONS_PR_WRITE }} + ORGANIZATION: rjmurillo + REPOSITORY: moq.analyzers + MILESTONE_NAME: vNext + run: | + # shellcheck disable=SC2016 + gh api graphql -f query=' + query($org: String!, $repo: String!, $milestone: String!) { + repository(name: $repo, owner: $org) { + milestones(query: $milestone, first: 2) { + nodes { + id + title + } } } - } - }' -f org=$ORGANIZATION -f repo=$REPOSITORY -f milestone="$MILESTONE_NAME" > milestone_data.json + }' -f org="$ORGANIZATION" -f repo="$REPOSITORY" -f milestone="$MILESTONE_NAME" > milestone_data.json - echo 'MILESTONE_ID='$(jq -r 'if (((.data.repository.milestones.nodes | length) == 1) and .data.repository.milestones.nodes[0].title == $MILESTONE_NAME) then .data.repository.milestones.nodes[0].id else "" end' --arg MILESTONE_NAME "$MILESTONE_NAME" milestone_data.json) >> $GITHUB_ENV + MILESTONE_ID=$(jq -r 'if (((.data.repository.milestones.nodes | length) == 1) and .data.repository.milestones.nodes[0].title == $MILESTONE_NAME) then .data.repository.milestones.nodes[0].id else "" end' --arg MILESTONE_NAME "$MILESTONE_NAME" milestone_data.json) + echo "MILESTONE_ID=$MILESTONE_ID" >> "$GITHUB_ENV" - - name: Assign milestone - env: - GITHUB_TOKEN: ${{ secrets.GH_ACTIONS_PR_WRITE }} - PULL_REQUEST_ID: ${{ github.event.pull_request.node_id }} - if: ${{ env.MILESTONE_ID != '' }} - run: | - gh api graphql -f query=' - mutation($pull: ID!, $milestone: ID!) { - updatePullRequest(input: {pullRequestId: $pull, milestoneId: $milestone}) { - pullRequest { - id - number - milestone { + - name: Assign milestone + env: + GITHUB_TOKEN: ${{ secrets.GH_ACTIONS_PR_WRITE }} + PULL_REQUEST_ID: ${{ github.event.pull_request.node_id }} + if: ${{ env.MILESTONE_ID != '' }} + run: | + # shellcheck disable=SC2016 + gh api graphql -f query=' + mutation($pull: ID!, $milestone: ID!) { + updatePullRequest(input: {pullRequestId: $pull, milestoneId: $milestone}) { + pullRequest { id number - title + milestone { + id + number + title + } } } - } - }' -f pull=$PULL_REQUEST_ID -f milestone=$MILESTONE_ID + }' -f pull="$PULL_REQUEST_ID" -f milestone="$MILESTONE_ID" diff --git a/.github/workflows/powershell.yml b/.github/workflows/powershell.yml index bbb8cd1c0..cd98a47fc 100644 --- a/.github/workflows/powershell.yml +++ b/.github/workflows/powershell.yml @@ -11,9 +11,9 @@ name: PSScriptAnalyzer on: push: - branches: [ "main" ] + branches: ["main"] pull_request: - branches: [ "main" ] + branches: ["main"] schedule: - cron: '19 22 * * 0' diff --git a/.github/workflows/pr-labeler-current-milestone.yml b/.github/workflows/pr-labeler-current-milestone.yml index 2c3edb6e2..0982b11bb 100644 --- a/.github/workflows/pr-labeler-current-milestone.yml +++ b/.github/workflows/pr-labeler-current-milestone.yml @@ -11,45 +11,45 @@ jobs: pull-requests: write runs-on: ubuntu-24.04-arm steps: - - # Label PRs - - uses: actions/labeler@v6 - with: - pr-number: | - 52 - 57 - 59 - 60 - 61 - 63 - 65 - 66 - 69 - 70 - 71 - 73 - 74 - 76 - 77 - 79 - 80 - 81 - 82 - 83 - 84 - 86 - 87 - 88 - 89 - 91 - 92 - 93 - 94 - 99 - 101 - 102 - 105 - 106 - 108 - 113 - 114 + + # Label PRs + - uses: actions/labeler@v6 + with: + pr-number: | + 52 + 57 + 59 + 60 + 61 + 63 + 65 + 66 + 69 + 70 + 71 + 73 + 74 + 76 + 77 + 79 + 80 + 81 + 82 + 83 + 84 + 86 + 87 + 88 + 89 + 91 + 92 + 93 + 94 + 99 + 101 + 102 + 105 + 106 + 108 + 113 + 114 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7d696286e..09ecc6872 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -32,6 +32,6 @@ jobs: shell: pwsh run: | foreach ($file in (Get-ChildItem ./packages/release -Recurse -Include *.nupkg)) { - echo "NuGet publish for file: '$file'" + echo "NuGet publish for file: '$file'" dotnet nuget push $file --api-key "${{ secrets.NUGET_API_KEY }}" --source https://api.nuget.org/v3/index.json --skip-duplicate } diff --git a/.markdownlint.json b/.markdownlint.json new file mode 100644 index 000000000..484d3db4c --- /dev/null +++ b/.markdownlint.json @@ -0,0 +1,6 @@ +{ + "MD013": false, + "MD024": false, + "MD033": false, + "MD041": false +} diff --git a/.yamllint.yml b/.yamllint.yml new file mode 100644 index 000000000..9eaf5e971 --- /dev/null +++ b/.yamllint.yml @@ -0,0 +1,10 @@ +extends: default +rules: + truthy: + check-keys: false + line-length: + max: 320 + allow-non-breakable-inline-mappings: true + document-start: disable + comments: + min-spaces-from-content: 1 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e3946fb38..0640d1d6f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -45,6 +45,10 @@ This project adheres to the [Contributor Covenant Code of Conduct](CODE-OF-CONDU dotnet test --settings ./build/targets/tests/test.runsettings ``` +5. **Install linting tools** (for local validation): + - [yamllint](https://yamllint.readthedocs.io/) (Python): `pip install yamllint` + - [markdownlint-cli](https://github.com/igorshubovych/markdownlint-cli) (Node.js): `npm install -g markdownlint-cli` + ## Universal Agent Success Principles for Project Maintainers > **IMPORTANT:** These guidelines help project maintainers create environments where AI agents can be more successful, regardless of the specific agent platform or tools being used. @@ -355,6 +359,28 @@ Before submitting a PR, ensure your code passes all quality checks: - If a bot's feedback is not addressed and a human reviewer must repeat the request, the PR will be closed until all automated feedback is resolved. - All documentation and markdown reports must pass formatting checks. Use a markdown linter if available. +### Local Linting + +The repository uses [super-linter](https://github.com/super-linter/super-linter) in CI to validate YAML, Markdown, JSON, shell scripts, and GitHub Actions workflows. You can run the same checks locally: + +**YAML** (uses `.yamllint.yml` config): + +```bash +yamllint -c .yamllint.yml .github/ .codacy/ +``` + +**Markdown** (uses `.markdownlint.json` config): + +```bash +npx markdownlint-cli --config .markdownlint.json "**/*.md" +``` + +**All linters via Docker** (exact CI match): + +```bash +docker run --rm -e RUN_LOCAL=true -v "$(pwd):/tmp/lint" ghcr.io/super-linter/super-linter:v8.5.0 +``` + ## Testing Requirements ### Unit Tests @@ -387,13 +413,14 @@ When testing code fixes that modify a class member (method, property, etc.), you Define a `public static IEnumerable` method to provide test cases. -- **Signature:** `public static IEnumerable YourDataSourceMethod()` -- **Content:** Return a `new object[][] { ... }`. Each inner `object[]` must contain two strings: - 1. The original code snippet that triggers the analyzer (`brokenCode`). - 2. The target code snippet after the code fix is applied (`fixedCode`). -- **Helpers:** You **MUST** chain `.WithNamespaces().WithMoqReferenceAssemblyGroups()` to the collection to automatically generate test variations. +- **Signature:** `public static IEnumerable YourDataSourceMethod()` +- **Content:** Return a `new object[][] { ... }`. Each inner `object[]` must contain two strings: + 1. The original code snippet that triggers the analyzer (`brokenCode`). + 2. The target code snippet after the code fix is applied (`fixedCode`). +- **Helpers:** You **MUST** chain `.WithNamespaces().WithMoqReferenceAssemblyGroups()` to the collection to automatically generate test variations. **Example:** + ```csharp public static IEnumerable MakesNonVirtualMethodVirtualData() { @@ -411,9 +438,10 @@ public static IEnumerable MakesNonVirtualMethodVirtualData() Create an `async Task` method decorated with `[Theory]` and `[MemberData]`. -- **Signature:** The signature **MUST** match the data source output: `async Task YourTestMethod(string referenceAssemblyGroup, string @namespace, string brokenCode, string fixedCode)` +- **Signature:** The signature **MUST** match the data source output: `async Task YourTestMethod(string referenceAssemblyGroup, string @namespace, string brokenCode, string fixedCode)` **Example:** + ```csharp [Theory] [MemberData(nameof(MakesNonVirtualMethodVirtualData))] @@ -427,11 +455,12 @@ public async Task MakesNonVirtualMethodVirtual(string referenceAssemblyGroup, st Inside the test method, define a `static` local function named `Template` that builds the full source code using a raw string literal. -- **Placeholders:** The template **MUST** use `{{ns}}` for the namespace and `{{code}}` for the code snippet. -- **Context:** The template **MUST** include all necessary `using` statements and class structures to create a valid, compilable test case. Note that `tests\Moq.Analyzers.Test\Helpers\Test.cs` inserts global usings common for tests. -- **Diagnostic Marker:** The code that triggers the analyzer **MUST** be wrapped with `{|DIAGNOSTIC_ID: ... |}` (e.g., `{|Moq1210:...|}`). This is non-negotiable for the test verifier to work. +- **Placeholders:** The template **MUST** use `{{ns}}` for the namespace and `{{code}}` for the code snippet. +- **Context:** The template **MUST** include all necessary `using` statements and class structures to create a valid, compilable test case. Note that `tests\Moq.Analyzers.Test\Helpers\Test.cs` inserts global usings common for tests. +- **Diagnostic Marker:** The code that triggers the analyzer **MUST** be wrapped with `{|DIAGNOSTIC_ID: ... |}` (e.g., `{|Moq1210:...|}`). This is non-negotiable for the test verifier to work. **Example:** + ```csharp static string Template(string ns, string code) => $$""" @@ -454,11 +483,12 @@ public class MyTest """; ``` -**4. Verify the Code Fix** +#### 4. Verify the Code Fix Use the `Template` function to generate the "before" and "after" source files and pass them to `Verify.VerifyCodeFixAsync`. **Example:** + ```csharp string originalSource = Template(@namespace, brokenCode); string fixedSource = Template(@namespace, fixedCode); @@ -475,6 +505,7 @@ await AllAnalyzersVerifier.VerifyAllAnalyzersAsync(sourceCode, referenceAssembly ``` **Key Benefits:** + - **Automatic Discovery**: Uses reflection to find all `DiagnosticAnalyzer` types in the `Moq.Analyzers` namespace - **No Manual Maintenance**: New analyzers are automatically included without code changes - **Comprehensive Coverage**: Tests against ALL analyzers simultaneously to ensure no false positives @@ -484,12 +515,14 @@ await AllAnalyzersVerifier.VerifyAllAnalyzersAsync(sourceCode, referenceAssembly ### Moq-Specific Testing Guidelines **Test Data Grouping:** + - When adding or updating test data, group tests by Moq version compatibility: - Place tests for features only available in Moq 4.18.4+ in a "new" group. - Place tests for features available in both 4.8.2 and 4.18.4 in a "both" or "old" group. - Do not include tests for features/APIs that do not exist in the targeted Moq version. **Moq Version Compatibility:** + - **Moq 4.8.2:** - Does _not_ support `SetupAdd`, `SetupRemove`, or `.Protected().Setup`. - Indexer setups are supported only for virtual or interface indexers. @@ -503,6 +536,7 @@ await AllAnalyzersVerifier.VerifyAllAnalyzersAsync(sourceCode, referenceAssembly - When in doubt, consult the official Moq documentation and changelogs for feature support. **Required Moq Testing Patterns:** + - **Overridable Members Only:** Only set up or verify virtual, abstract, or interface members. Do **not** attempt to set up or verify non-virtual, static, or sealed members. - **Events and Indexers:** Use `SetupAdd` and `SetupRemove` **only** for virtual events, and only in Moq 4.18.4+. - **Explicit Interface Implementations:** Setups for explicit interface implementations must use the correct cast syntax (e.g., `((IMyInterface)x).Method()`). @@ -525,6 +559,7 @@ await AllAnalyzersVerifier.VerifyAllAnalyzersAsync(sourceCode, referenceAssembly ### When Documentation is Required Documentation updates are required for: + - New analyzers or fixers - Changes to existing analyzer behavior - API changes or additions @@ -564,6 +599,7 @@ Documentation updates are required for: - ❌ Bad: `x => x.Method()` **Examples:** + ```csharp /// /// Determines whether a member symbol is either overridable or represents a @@ -581,6 +617,7 @@ Documentation updates are required for: ``` **Validation:** + - All public APIs must have complete XML documentation - All type references must use `` tags - All C# keywords must use `` tags @@ -699,18 +736,21 @@ For full documentation, see [nektosact.com](https://nektosact.com/usage/). ### Performance Testing Guidelines **When Performance Testing is Required:** + - New analyzers or fixers - Changes to existing analyzer logic - Dependency updates that might affect performance - CI/CD changes that impact build times **Performance Testing Process:** + 1. Run benchmarks locally using `dotnet run --project tests/Moq.Analyzers.Benchmarks/` 2. Compare results against baseline 3. Document any performance regressions or improvements 4. Include benchmark results in PR description **Performance Validation Evidence:** + - Benchmark output showing no significant regressions - Comparison with previous baseline results - Explanation of any performance changes @@ -720,6 +760,7 @@ For full documentation, see [nektosact.com](https://nektosact.com/usage/). ### Dependency Update Guidelines **For Renovate/Dependabot PRs:** + - Review changelog and release notes - Test locally to ensure compatibility - Check for breaking changes @@ -727,6 +768,7 @@ For full documentation, see [nektosact.com](https://nektosact.com/usage/). - Include testing evidence in PR description **For Manual Dependency Updates:** + - Follow the same process as automated updates - Document the reason for the update - Include compatibility testing results @@ -746,6 +788,7 @@ For full documentation, see [nektosact.com](https://nektosact.com/usage/). Follow conventional commit format: `type(scope): description` **Description Requirements:** + 1. **Clear summary** of changes 2. **Problem statement** (what issue does this solve?) 3. **Solution description** (how does this solve the problem?) @@ -768,6 +811,7 @@ Before submitting a PR, ensure: ### Validation Evidence Requirements **What Constitutes Validation Evidence:** + - Test execution logs showing all tests pass - **Code coverage report summary and/or screenshots showing coverage for changed code** - Performance benchmark results (if applicable) @@ -775,6 +819,7 @@ Before submitting a PR, ensure: - Manual testing results for UI changes **Evidence Format:** + - Include logs, screenshots, or links to CI runs - Provide clear, readable evidence - Ensure evidence is recent and relevant @@ -808,6 +853,7 @@ Maintainers will review PRs for: ### Common Review Feedback **Frequently Requested Changes:** + - Add missing tests for edge cases - Update documentation for new features - Improve error handling and logging @@ -815,6 +861,7 @@ Maintainers will review PRs for: - Clarify PR description or validation evidence **PRs That May Be Rejected:** + - Missing validation evidence - Incomplete test coverage - Performance regressions without justification @@ -828,7 +875,7 @@ This project uses [Nerdbank.GitVersioning](https://github.com/dotnet/Nerdbank.Gi ### How Versioning Works | Branch | `version.json` value | Produced version | -|--------|---------------------|-----------------| +| -------- | --------------------- | ----------------- | | `main` | `0.4.0-alpha` | `0.4.0-alpha.{height}` (prerelease) | | `release/v0.4.0` | `0.4.0` | `0.4.0` (stable) | | `release/v0.4.1` | `0.4.1` | `0.4.1` (stable) | @@ -1022,11 +1069,13 @@ If the answer to **ANY** question is "no" or "unsure," you **MUST STOP** and req This repository uses **symbol-based detection** for all Moq pattern matching: **Symbol Registry Pattern:** + - Central registry: `src/Common/WellKnown/MoqKnownSymbols.cs` - Symbols loaded via `TypeProvider.GetOrCreateTypeByMetadataName()` - Method collections via `GetMembers("MethodName").OfType().ToImmutableArray()` **Example - Adding New Moq Interface:** + ```csharp // In MoqKnownSymbols.cs internal INamedTypeSymbol? IRaise1 => @@ -1038,6 +1087,7 @@ internal ImmutableArray IRaise1Raises => ``` **Detection Helper Pattern:** + ```csharp // In ISymbolExtensions.cs public static bool IsRaiseableMethod(this ISymbol symbol, MoqKnownSymbols knownSymbols) @@ -1052,7 +1102,7 @@ public static bool IsRaiseableMethod(this ISymbol symbol, MoqKnownSymbols knownS Moq uses **method chaining** with different return types at each stage: | Stage | Method | Returns Interface | -|-------|--------|-------------------| +| ------- | -------- | ------------------- | | Setup | `Setup(x => x.Method())` | `ISetup` | | Callback | `.Callback(...)` | `ICallback` | | Event | `.Raises(...)` | `IRaise` (generic) | @@ -1067,6 +1117,7 @@ Moq uses **method chaining** with different return types at each stage: **Root Cause**: Missing symbol registration in `MoqKnownSymbols`. **Solution Process**: + 1. Create temporary test to capture `SemanticModel.GetSymbolInfo()` output 2. Identify the actual symbol type (e.g., `Moq.Language.IRaise`) 3. Add missing symbol to `MoqKnownSymbols` with proper metadata name @@ -1074,6 +1125,7 @@ Moq uses **method chaining** with different return types at each stage: 5. Delete temporary diagnostic test **Example Investigation**: + ```csharp // Temporary diagnostic test var symbolInfo = semanticModel.GetSymbolInfo(invocationExpression); @@ -1147,7 +1199,7 @@ In filmmaking, it is often quoted "show, don't tell" using visuals as the commun In our case, "tell, don't [just] show" – though we have some visuals at our disposal such as the browser, most of the specifics come from reading the physical code. -When writing the commit, imagine how useful this could be in troubleshooting a bug or back-tracing changes made. +When writing the commit, imagine how useful this could be in troubleshooting a bug or back-tracing changes made. Where possible, use **Conventional Commits** @@ -1155,7 +1207,7 @@ Where possible, use **Conventional Commits** Conventional Commit is a formatting convention that provides a set of rules to formulate a consistent commit message structure like so: -``` +```text [optional scope]: [optional body] @@ -1187,10 +1239,10 @@ The footer is also optional. We use the footer to link the GitHub issue that wou Example: -``` +```text fix: fix foo to enable bar -This fixes the broken behavior of the component by doing xyz. +This fixes the broken behavior of the component by doing xyz. BREAKING CHANGE Before this fix foo wasn't enabled at all, behavior changes from to @@ -1200,20 +1252,20 @@ Closes #12345 #### Commit Message Examples -**Good** +#### Good - `feat: improve performance with lazy load implementation for images` - `chore: update npm dependency to latest version` - `Fix bug preventing users from submitting the subscribe form` - `Update incorrect client phone number within footer body per client request` -**Bad** +#### Bad - `fixed bug on landing page` - `Changed style` - `oops` - `I think I fixed it this time?` -- *empty commit messages* +- _empty commit messages_ ## Getting Help @@ -1227,9 +1279,9 @@ If you need help with any aspect of contributing: ## Recognition Contributors will be recognized in: + - Release notes for significant contributions - Project README for major contributors - GitHub contributors list Thank you for contributing to Moq.Analyzers! - diff --git a/Perf.sh b/Perf.sh index 341e57197..f099ab20e 100755 --- a/Perf.sh +++ b/Perf.sh @@ -6,5 +6,5 @@ SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" # Execute the PowerShell script with all passed arguments -pwsh -ExecutionPolicy ByPass -NoProfile -command "& \"$SCRIPT_DIR/build/scripts/perf/PerfCore.ps1\" \"$@\"" +pwsh -ExecutionPolicy ByPass -NoProfile -File "$SCRIPT_DIR/build/scripts/perf/PerfCore.ps1" "$@" exit $? \ No newline at end of file diff --git a/actionlint.yml b/actionlint.yml new file mode 100644 index 000000000..79edf4d4a --- /dev/null +++ b/actionlint.yml @@ -0,0 +1,3 @@ +self-hosted-runner: + labels: + - windows-2025-vs2026 diff --git a/build/scripts/perf/CIPerf.sh b/build/scripts/perf/CIPerf.sh index 5d175cdb9..a00e0d95b 100755 --- a/build/scripts/perf/CIPerf.sh +++ b/build/scripts/perf/CIPerf.sh @@ -6,5 +6,5 @@ SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" # Execute the PowerShell script with CI parameters and all passed arguments -pwsh -ExecutionPolicy ByPass -NoProfile -command "& \"$SCRIPT_DIR/PerfCore.ps1\" -v diag -diff -ci \"$@\"" +pwsh -ExecutionPolicy ByPass -NoProfile -File "$SCRIPT_DIR/PerfCore.ps1" -v diag -diff -ci "$@" exit $? \ No newline at end of file diff --git a/docs/dependency-management.md b/docs/dependency-management.md index de6a4530b..947a96602 100644 --- a/docs/dependency-management.md +++ b/docs/dependency-management.md @@ -10,14 +10,14 @@ Dependencies fall into distinct categories with different upgrade policies. These packages are bundled in the analyzer NuGet package and run inside the **user's** compiler host (Visual Studio, dotnet CLI). Version constraints are critical because users may run older SDKs. -| Package | Central Pin | Constraint | -|---------|------------|------------| -| Microsoft.CodeAnalysis.CSharp | 4.8 | Minimum supported VS/SDK version | -| Microsoft.CodeAnalysis.CSharp.Workspaces | 4.8 | Same as above | -| Microsoft.CodeAnalysis.AnalyzerUtilities | 3.3.4 | Must reference SCI <= 8.0.0.0 | -| System.Collections.Immutable | 8.0.0 | Must not exceed .NET 8 SDK host assembly version | -| System.Formats.Asn1 | 10.0.0 | Transitive pin in shipped section; flagged for host compat review | -| System.Reflection.Metadata | (transitive, no central pin) | Must not exceed .NET 8 SDK host assembly version | +| Package | Central Pin | Constraint | +| ---------------------------------------- | ---------------------------- | ----------------------------------------------------------------- | +| Microsoft.CodeAnalysis.CSharp | 4.8 | Minimum supported VS/SDK version | +| Microsoft.CodeAnalysis.CSharp.Workspaces | 4.8 | Same as above | +| Microsoft.CodeAnalysis.AnalyzerUtilities | 3.3.4 | Must reference SCI <= 8.0.0.0 | +| System.Collections.Immutable | 8.0.0 | Must not exceed .NET 8 SDK host assembly version | +| System.Formats.Asn1 | 10.0.0 | Transitive pin in shipped section; flagged for host compat review | +| System.Reflection.Metadata | (transitive, no central pin) | Must not exceed .NET 8 SDK host assembly version | **Why this matters:** In v0.4.0, a transitive dependency bump pushed SCI to 10.0.0.0, causing CS8032 assembly load failures for every user on .NET 8 SDK. See [issue #850](https://github.com/rjmurillo/moq.analyzers/issues/850). @@ -33,12 +33,12 @@ These run only during builds and are not shipped. Updates do not affect end user Representative packages: -| Package | Location | -|---------|----------| -| Meziantou.Analyzer | build/targets/codeanalysis/Packages.props | -| SonarAnalyzer.CSharp | build/targets/codeanalysis/Packages.props | -| Roslynator.Analyzers | build/targets/codeanalysis/Packages.props | -| StyleCop.Analyzers | build/targets/codeanalysis/Packages.props | +| Package | Location | +| -------------------------------- | ----------------------------------------- | +| Meziantou.Analyzer | build/targets/codeanalysis/Packages.props | +| SonarAnalyzer.CSharp | build/targets/codeanalysis/Packages.props | +| Roslynator.Analyzers | build/targets/codeanalysis/Packages.props | +| StyleCop.Analyzers | build/targets/codeanalysis/Packages.props | | Microsoft.CodeAnalysis.Analyzers | build/targets/codeanalysis/Packages.props | **Upgrade policy:** Automerge minor/patch. Major versions may introduce new warnings that break the build (warnings are errors). Review new rules before merging major bumps. @@ -49,12 +49,12 @@ Test-only dependencies with no shipped impact. See [`build/targets/tests/Package Representative packages: -| Package | Location | -|---------|----------| -| Verify.Xunit | build/targets/tests/Packages.props | -| xunit | build/targets/tests/Packages.props | -| Microsoft.NET.Test.Sdk | build/targets/tests/Packages.props | -| coverlet.msbuild | build/targets/tests/Packages.props | +| Package | Location | +| ---------------------- | ------------------------------------ | +| Verify.Xunit | build/targets/tests/Packages.props | +| xunit | build/targets/tests/Packages.props | +| Microsoft.NET.Test.Sdk | build/targets/tests/Packages.props | +| coverlet.msbuild | build/targets/tests/Packages.props | **Upgrade policy:** Automerge minor/patch. CI validates compatibility. @@ -62,10 +62,10 @@ Representative packages: BenchmarkDotNet and Perfolizer have intertwined version requirements. BenchmarkDotNet declares a minimum Perfolizer version and a minimum Microsoft.CodeAnalysis.CSharp version. -| Package | Central Pin | Notes | -|---------|------------|-------| +| Package | Central Pin | Notes | +| --------------- | ------------------------ | --------------------------------------- | | BenchmarkDotNet | Directory.Packages.props | Transitive dep on Perfolizer and Roslyn | -| Perfolizer | Directory.Packages.props | Used directly by PerfDiff tool | +| Perfolizer | Directory.Packages.props | Used directly by PerfDiff tool | **Upgrade policy:** Grouped in Renovate as `benchmark-tooling` with `automerge: false`. Both packages must be updated together. The benchmark project uses `VersionOverride` for packages whose central pins are constrained by shipped analyzer compatibility (e.g., `System.Collections.Immutable`). @@ -73,32 +73,32 @@ BenchmarkDotNet and Perfolizer have intertwined version requirements. BenchmarkD The PerfDiff tool (`src/tools/PerfDiff/`) uses System.CommandLine, which had breaking API changes between beta and stable releases. The `IConsole` interface was removed in 2.0.3. -| Package | Status | -|---------|--------| -| System.CommandLine | **Disabled** in Renovate until PerfDiff is rewritten | +| Package | Status | +| ---------------------------- | --------------------------------------------------------- | +| System.CommandLine | **Disabled** in Renovate until PerfDiff is rewritten | | System.CommandLine.Rendering | **Disabled** (folded into main package in stable release) | **Why disabled:** The perf CI check builds PerfDiff on-demand. It is excluded from the normal build/test matrix. Updates that break PerfDiff only surface as `perf` check failures, which are a required status check. ### Build Infrastructure - MODERATE CAUTION -| Package | Location | Notes | -|---------|----------|-------| -| Polyfill | build/targets/compiler/Packages.props | Compiler polyfills | -| DotNet.ReproducibleBuilds | build/targets/reproducible/Packages.props | Build reproducibility | -| DotNet.ReproducibleBuilds.Isolated | global.json (msbuild-sdks) | MSBuild SDK isolation | -| Nerdbank.GitVersioning | Directory.Packages.props | Version calculation | +| Package | Location | Notes | +| ---------------------------------- | -------------------------------------------- | ------------------------ | +| Polyfill | build/targets/compiler/Packages.props | Compiler polyfills | +| DotNet.ReproducibleBuilds | build/targets/reproducible/Packages.props | Build reproducibility | +| DotNet.ReproducibleBuilds.Isolated | global.json (msbuild-sdks) | MSBuild SDK isolation | +| Nerdbank.GitVersioning | Directory.Packages.props | Version calculation | **Upgrade policy:** Automerge minor/patch for stable versions. ReproducibleBuilds and Isolated should be updated together (same release cadence). ## Configuration Files -| File | Purpose | -|------|---------| -| `renovate.json` | Renovate bot configuration (primary dependency bot) | -| `.github/dependabot.yml` | Dependabot configuration (GitHub Actions only) | -| `Directory.Packages.props` | Central package version management | -| `build/targets/*/Packages.props` | Category-specific package versions | +| File | Purpose | +| -------------------------------- | --------------------------------------------------- | +| `renovate.json` | Renovate bot configuration (primary dependency bot) | +| `.github/dependabot.yml` | Dependabot configuration (GitHub Actions only) | +| `Directory.Packages.props` | Central package version management | +| `build/targets/*/Packages.props` | Category-specific package versions | ## VersionOverride Pattern diff --git a/docs/rules/Moq1000.md b/docs/rules/Moq1000.md index c239415bb..581ac9a1d 100644 --- a/docs/rules/Moq1000.md +++ b/docs/rules/Moq1000.md @@ -5,6 +5,7 @@ | Enabled | True | | Severity | Warning | | CodeFix | False | + --- Mocking requires generating a subclass of the class to be mocked. Sealed classes cannot be subclassed. To fix: diff --git a/docs/rules/Moq1001.md b/docs/rules/Moq1001.md index da5765fe4..986a7362e 100644 --- a/docs/rules/Moq1001.md +++ b/docs/rules/Moq1001.md @@ -5,6 +5,7 @@ | Enabled | True | | Severity | Warning | | CodeFix | False | + --- Mocking interfaces requires generating a class on-the-fly that implements the interface. That generated class is diff --git a/docs/rules/Moq1002.md b/docs/rules/Moq1002.md index 7937d2886..c0d4af134 100644 --- a/docs/rules/Moq1002.md +++ b/docs/rules/Moq1002.md @@ -5,6 +5,7 @@ | Enabled | True | | Severity | Warning | | CodeFix | False | + --- In order to construct the mocked type, constructor parameters must match a constructor. To fix: diff --git a/docs/rules/Moq1100.md b/docs/rules/Moq1100.md index 6ac9483ea..51b52041e 100644 --- a/docs/rules/Moq1100.md +++ b/docs/rules/Moq1100.md @@ -5,6 +5,7 @@ | Enabled | True | | Severity | Warning | | CodeFix | True | + --- The signature of the `.Callback()` method must match the signature of the `.Setup()` method. To fix: diff --git a/docs/rules/Moq1101.md b/docs/rules/Moq1101.md index 5aec4dc1d..e9bb87d42 100644 --- a/docs/rules/Moq1101.md +++ b/docs/rules/Moq1101.md @@ -5,6 +5,7 @@ | Enabled | True | | Severity | Warning | | CodeFix | False | + --- `.SetupGet()`, `.SetupSet()`, and `.SetupProperty()` are methods for mocking properties, not methods. Use `.Setup()` to mock methods instead. diff --git a/docs/rules/Moq1200.md b/docs/rules/Moq1200.md index a0913f79d..816a482f5 100644 --- a/docs/rules/Moq1200.md +++ b/docs/rules/Moq1200.md @@ -5,6 +5,7 @@ | Enabled | True | | Severity | Error | | CodeFix | False | + --- Mocking requires generating a subclass of the class to be mocked. Methods not marked `virtual` cannot be overridden. diff --git a/docs/rules/Moq1201.md b/docs/rules/Moq1201.md index 22684803c..f9bf6409a 100644 --- a/docs/rules/Moq1201.md +++ b/docs/rules/Moq1201.md @@ -5,6 +5,7 @@ | Enabled | True | | Severity | Error | | CodeFix | False | + --- Moq now supports the `.ReturnsAsync()` method to support mocking async methods. Use it instead of returning `.Result`, diff --git a/docs/rules/Moq1202.md b/docs/rules/Moq1202.md index 12340c484..b2d93b3b9 100644 --- a/docs/rules/Moq1202.md +++ b/docs/rules/Moq1202.md @@ -61,4 +61,4 @@ class Test - `Action` (no parameters) - `Action`, `Action`, etc. (generic Action delegates) - `EventHandler` (expects single argument of type T) -- Custom delegate types (analyzed via their Invoke method signature) \ No newline at end of file +- Custom delegate types (analyzed via their Invoke method signature) diff --git a/docs/rules/Moq1203.md b/docs/rules/Moq1203.md index 41578fc48..62105cd3b 100644 --- a/docs/rules/Moq1203.md +++ b/docs/rules/Moq1203.md @@ -5,6 +5,7 @@ | Enabled | True | | Severity | Warning | | CodeFix | False | + --- Method setups that have a return type should specify what value to return using `Returns()`, `ReturnsAsync()`, `Throws()`, or `ThrowsAsync()`. @@ -111,4 +112,4 @@ dotnet_diagnostic.Moq1203.severity = none ``` For more information, see -[How to suppress code analysis warnings](https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/suppress-warnings). \ No newline at end of file +[How to suppress code analysis warnings](https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/suppress-warnings). diff --git a/docs/rules/Moq1204.md b/docs/rules/Moq1204.md index e3630cf56..bb0fe5655 100644 --- a/docs/rules/Moq1204.md +++ b/docs/rules/Moq1204.md @@ -73,4 +73,4 @@ class Test ## Compatibility -This rule applies to all supported Moq versions as the `Raises` method has been available since early versions. \ No newline at end of file +This rule applies to all supported Moq versions as the `Raises` method has been available since early versions. diff --git a/docs/rules/Moq1206.md b/docs/rules/Moq1206.md index fb8ce99bb..062de30aa 100644 --- a/docs/rules/Moq1206.md +++ b/docs/rules/Moq1206.md @@ -1,10 +1,11 @@ # Moq1206: Async method setups should use ReturnsAsync instead of Returns with async lambda -| Item | Value | -| -------- | ----- | -| Enabled | True | +| Item | Value | +| -------- | ------- | +| Enabled | True | | Severity | Warning | -| CodeFix | False | +| CodeFix | False | + --- When setting up async methods in Moq, you should use `.ReturnsAsync()` instead of `.Returns()` with an async lambda. Using async lambdas in `.Returns()` can lead to compiler warnings and unexpected behavior. @@ -66,4 +67,4 @@ dotnet_diagnostic.Moq1206.severity = none ``` For more information, see -[How to suppress code analysis warnings](https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/suppress-warnings). \ No newline at end of file +[How to suppress code analysis warnings](https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/suppress-warnings). diff --git a/docs/rules/Moq1207.md b/docs/rules/Moq1207.md index 9d34fb253..38348b9af 100644 --- a/docs/rules/Moq1207.md +++ b/docs/rules/Moq1207.md @@ -1,11 +1,11 @@ # Moq1207: SetupSequence should be used only for overridable members -| Property | Value | -|----------------------------|-------------------------------------------------------------------------------------------------------------------------------------| -| **Rule ID** | Moq1207 | -| **Title** | SetupSequence should be used only for overridable members | -| **Category** | Moq | -| **Default severity** | Error | +| Property | Value | +| -------------------- | --------------------------------------------------------- | +| **Rule ID** | Moq1207 | +| **Title** | SetupSequence should be used only for overridable members | +| **Category** | Moq | +| **Default severity** | Error | ## Cause @@ -20,6 +20,7 @@ SetupSequence should only be used for members that can be overridden. This inclu - Members that override virtual or abstract members (and are not sealed) Non-overridable members include: + - Non-virtual methods and properties - Static members - Sealed members @@ -85,4 +86,4 @@ classMock.SetupSequence(x => x.VirtualMethod()); ```csharp [SuppressMessage("Moq", "Moq1207:SetupSequence should be used only for overridable members", Justification = "Justified reason")] -``` \ No newline at end of file +``` diff --git a/docs/rules/Moq1210.md b/docs/rules/Moq1210.md index 01d24c559..014e36a30 100644 --- a/docs/rules/Moq1210.md +++ b/docs/rules/Moq1210.md @@ -10,6 +10,7 @@ codeFix: True Moq works by creating a dynamic proxy class at runtime. This proxy inherits from the class being mocked and overrides its `virtual` or `abstract` members. This allows Moq to intercept calls and implement mocking behavior, including verification. Because of this design, Moq can only intercept calls to members that are overridable: + - Members of an interface (which are implicitly virtual). - `virtual` or `abstract` members of a non-sealed class. @@ -18,6 +19,7 @@ Non-virtual members cannot be overridden in a subclass due to how the .NET CLR w This analyzer helps you avoid this runtime error by identifying `Verify` calls on non-overridable members at compile time. To fix this issue, you must ensure the member you are verifying can be overridden: + - If mocking a class, mark the member as `virtual`. - Prefer mocking an interface instead of a concrete class. @@ -96,4 +98,4 @@ dotnet_diagnostic.Moq1210.severity = none ``` For more information, see -[How to suppress code analysis warnings](https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/suppress-warnings). \ No newline at end of file +[How to suppress code analysis warnings](https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/suppress-warnings). diff --git a/docs/rules/Moq1300.md b/docs/rules/Moq1300.md index 6eb9fa672..d53de992f 100644 --- a/docs/rules/Moq1300.md +++ b/docs/rules/Moq1300.md @@ -6,6 +6,7 @@ | Enabled | True | | Severity | Error | | CodeFix | No | + --- The `.As()` method is used when a mocked object must implement multiple interfaces. It cannot be used with abstract or diff --git a/docs/rules/Moq1301.md b/docs/rules/Moq1301.md index 92f2832ad..bf5192d9c 100644 --- a/docs/rules/Moq1301.md +++ b/docs/rules/Moq1301.md @@ -5,6 +5,7 @@ | Enabled | True | | Severity | Warning | | CodeFix | False | + --- `Mock.Get()` is used to retrieve the Mock instance associated with a mocked object. It should not be called with literal values because literals cannot have mock instances associated with them. @@ -47,4 +48,4 @@ dotnet_diagnostic.Moq1301.severity = none ``` For more information, see -[How to suppress code analysis warnings](https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/suppress-warnings). \ No newline at end of file +[How to suppress code analysis warnings](https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/suppress-warnings). diff --git a/docs/rules/Moq1302.md b/docs/rules/Moq1302.md index 743974d33..876bd9e18 100644 --- a/docs/rules/Moq1302.md +++ b/docs/rules/Moq1302.md @@ -5,11 +5,13 @@ | Enabled | True | | Severity | Warning | | CodeFix | False | + --- LINQ to Mocks (using `Mock.Of()`) allows creating mock objects using lambda expressions that define their behavior. However, these expressions can only reference virtual, abstract, or interface members that can actually be mocked. Attempting to reference non-virtual members will cause runtime issues. Additional patterns flagged by this analyzer include: + - Instance and static fields - Events - Nested or chained member accesses diff --git a/docs/rules/Moq1400.md b/docs/rules/Moq1400.md index af9755702..5423f02d3 100644 --- a/docs/rules/Moq1400.md +++ b/docs/rules/Moq1400.md @@ -5,6 +5,7 @@ | Enabled | True | | Severity | Warning | | CodeFix | False | + --- Mocks use the `MockBehavior.Loose` by default. Some people find this default behavior undesirable, as it can lead to diff --git a/docs/rules/Moq1420.md b/docs/rules/Moq1420.md index daf893e13..6581620ee 100644 --- a/docs/rules/Moq1420.md +++ b/docs/rules/Moq1420.md @@ -79,4 +79,4 @@ dotnet_diagnostic.Moq1420.severity = none ``` For more information, see -[How to suppress code analysis warnings](https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/suppress-warnings). \ No newline at end of file +[How to suppress code analysis warnings](https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/suppress-warnings). diff --git a/docs/rules/Moq1500.md b/docs/rules/Moq1500.md index 66a044ba2..c9ed3e70a 100644 --- a/docs/rules/Moq1500.md +++ b/docs/rules/Moq1500.md @@ -5,6 +5,7 @@ | Enabled | True | | Severity | Warning | | CodeFix | False | + --- When using `MockRepository.Create()` methods to create mocks, you should call `MockRepository.Verify()` to verify all mocks created through the repository. This ensures that all setups configured on the repository's mocks are properly verified. @@ -91,4 +92,4 @@ dotnet_diagnostic.Moq1500.severity = none ``` For more information, see -[How to suppress code analysis warnings](https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/suppress-warnings). \ No newline at end of file +[How to suppress code analysis warnings](https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/suppress-warnings). diff --git a/docs/rules/README.md b/docs/rules/README.md index 374af21e4..4fea2fc44 100644 --- a/docs/rules/README.md +++ b/docs/rules/README.md @@ -1,45 +1,48 @@ # Diagnostics / rules -| ID | Category | Title | Implementation File | -| ----------------------- | ------------- | --------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | -| [Moq1000](./Moq1000.md) | Usage | Sealed classes cannot be mocked | [NoSealedClassMocksAnalyzer.cs](../../src/Analyzers/NoSealedClassMocksAnalyzer.cs) | -| [Moq1001](./Moq1001.md) | Usage | Mocked interfaces cannot have constructor parameters | [ConstructorArgumentsShouldMatchAnalyzer.cs](../../src/Analyzers/ConstructorArgumentsShouldMatchAnalyzer.cs) | -| [Moq1002](./Moq1002.md) | Usage | Parameters provided into mock do not match any existing constructors | [ConstructorArgumentsShouldMatchAnalyzer.cs](../../src/Analyzers/ConstructorArgumentsShouldMatchAnalyzer.cs) | -| [Moq1100](./Moq1100.md) | Correctness | Callback signature must match the signature of the mocked method | [CallbackSignatureShouldMatchMockedMethodAnalyzer.cs](../../src/Analyzers/CallbackSignatureShouldMatchMockedMethodAnalyzer.cs) | -| [Moq1101](./Moq1101.md) | Usage | SetupGet/SetupSet/SetupProperty should be used for properties, not for methods | [NoMethodsInPropertySetupAnalyzer.cs](../../src/Analyzers/NoMethodsInPropertySetupAnalyzer.cs) | -| [Moq1200](./Moq1200.md) | Correctness | Setup should be used only for overridable members | [SetupShouldBeUsedOnlyForOverridableMembersAnalyzer.cs](../../src/Analyzers/SetupShouldBeUsedOnlyForOverridableMembersAnalyzer.cs) | -| [Moq1201](./Moq1201.md) | Correctness | Setup of async methods should use `.ReturnsAsync` instance instead of `.Result` | [SetupShouldNotIncludeAsyncResultAnalyzer.cs](../../src/Analyzers/SetupShouldNotIncludeAsyncResultAnalyzer.cs) | -| [Moq1202](./Moq1202.md) | Correctness | Raise event arguments should match the event delegate signature | [RaiseEventArgumentsShouldMatchEventSignatureAnalyzer.cs](../../src/Analyzers/RaiseEventArgumentsShouldMatchEventSignatureAnalyzer.cs) | -| [Moq1203](./Moq1203.md) | Correctness | Method setup should specify a return value | [MethodSetupShouldSpecifyReturnValueAnalyzer.cs](../../src/Analyzers/MethodSetupShouldSpecifyReturnValueAnalyzer.cs) | -| [Moq1204](./Moq1204.md) | Correctness | Raises event arguments should match event signature | [RaisesEventArgumentsShouldMatchEventSignatureAnalyzer.cs](../../src/Analyzers/RaisesEventArgumentsShouldMatchEventSignatureAnalyzer.cs) | -| [Moq1205](./Moq1205.md) | Correctness | Event setup handler type should match event delegate type | [EventSetupHandlerShouldMatchEventTypeAnalyzer.cs](../../src/Analyzers/EventSetupHandlerShouldMatchEventTypeAnalyzer.cs) | -| [Moq1206](./Moq1206.md) | Correctness | Async method setups should use ReturnsAsync instead of Returns with async lambda | [ReturnsAsyncShouldBeUsedForAsyncMethodsAnalyzer.cs](../../src/Analyzers/ReturnsAsyncShouldBeUsedForAsyncMethodsAnalyzer.cs) | +| ID | Category | Title | Implementation File | +| ----------------------- | ------------- | --------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- | +| [Moq1000](./Moq1000.md) | Usage | Sealed classes cannot be mocked | [NoSealedClassMocksAnalyzer.cs](../../src/Analyzers/NoSealedClassMocksAnalyzer.cs) | +| [Moq1001](./Moq1001.md) | Usage | Mocked interfaces cannot have constructor parameters | [ConstructorArgumentsShouldMatchAnalyzer.cs](../../src/Analyzers/ConstructorArgumentsShouldMatchAnalyzer.cs) | +| [Moq1002](./Moq1002.md) | Usage | Parameters provided into mock do not match any existing constructors | [ConstructorArgumentsShouldMatchAnalyzer.cs](../../src/Analyzers/ConstructorArgumentsShouldMatchAnalyzer.cs) | +| [Moq1100](./Moq1100.md) | Correctness | Callback signature must match the signature of the mocked method | [CallbackSignatureShouldMatchMockedMethodAnalyzer.cs](../../src/Analyzers/CallbackSignatureShouldMatchMockedMethodAnalyzer.cs) | +| [Moq1101](./Moq1101.md) | Usage | SetupGet/SetupSet/SetupProperty should be used for properties, not for methods | [NoMethodsInPropertySetupAnalyzer.cs](../../src/Analyzers/NoMethodsInPropertySetupAnalyzer.cs) | +| [Moq1200](./Moq1200.md) | Correctness | Setup should be used only for overridable members | [SetupShouldBeUsedOnlyForOverridableMembersAnalyzer.cs](../../src/Analyzers/SetupShouldBeUsedOnlyForOverridableMembersAnalyzer.cs) | +| [Moq1201](./Moq1201.md) | Correctness | Setup of async methods should use `.ReturnsAsync` instance instead of `.Result` | [SetupShouldNotIncludeAsyncResultAnalyzer.cs](../../src/Analyzers/SetupShouldNotIncludeAsyncResultAnalyzer.cs) | +| [Moq1202](./Moq1202.md) | Correctness | Raise event arguments should match the event delegate signature | [RaiseEventArgumentsShouldMatchEventSignatureAnalyzer.cs](../../src/Analyzers/RaiseEventArgumentsShouldMatchEventSignatureAnalyzer.cs) | +| [Moq1203](./Moq1203.md) | Correctness | Method setup should specify a return value | [MethodSetupShouldSpecifyReturnValueAnalyzer.cs](../../src/Analyzers/MethodSetupShouldSpecifyReturnValueAnalyzer.cs) | +| [Moq1204](./Moq1204.md) | Correctness | Raises event arguments should match event signature | [RaisesEventArgumentsShouldMatchEventSignatureAnalyzer.cs](../../src/Analyzers/RaisesEventArgumentsShouldMatchEventSignatureAnalyzer.cs) | +| [Moq1205](./Moq1205.md) | Correctness | Event setup handler type should match event delegate type | [EventSetupHandlerShouldMatchEventTypeAnalyzer.cs](../../src/Analyzers/EventSetupHandlerShouldMatchEventTypeAnalyzer.cs) | +| [Moq1206](./Moq1206.md) | Correctness | Async method setups should use ReturnsAsync instead of Returns with async lambda | [ReturnsAsyncShouldBeUsedForAsyncMethodsAnalyzer.cs](../../src/Analyzers/ReturnsAsyncShouldBeUsedForAsyncMethodsAnalyzer.cs) | | [Moq1207](./Moq1207.md) | Correctness | SetupSequence should be used only for overridable members | [SetupSequenceShouldBeUsedOnlyForOverridableMembersAnalyzer.cs](../../src/Analyzers/SetupSequenceShouldBeUsedOnlyForOverridableMembersAnalyzer.cs) | -| [Moq1210](./Moq1210.md) | Correctness | Verify should be used only for overridable members | [VerifyShouldBeUsedOnlyForOverridableMembersAnalyzer.cs](../../src/Analyzers/VerifyShouldBeUsedOnlyForOverridableMembersAnalyzer.cs) | -| [Moq1300](./Moq1300.md) | Usage | `Mock.As()` should take interfaces only | [AsShouldBeUsedOnlyForInterfaceAnalyzer.cs](../../src/Analyzers/AsShouldBeUsedOnlyForInterfaceAnalyzer.cs) | -| [Moq1301](./Moq1301.md) | Usage | Mock.Get() should not take literals | [MockGetShouldNotTakeLiteralsAnalyzer.cs](../../src/Analyzers/MockGetShouldNotTakeLiteralsAnalyzer.cs) | -| [Moq1302](./Moq1302.md) | Usage | LINQ to Mocks expression should be valid | [LinqToMocksExpressionShouldBeValidAnalyzer.cs](../../src/Analyzers/LinqToMocksExpressionShouldBeValidAnalyzer.cs) | -| [Moq1400](./Moq1400.md) | Best Practice | Explicitly choose a mocking behavior instead of relying on the default (Loose) behavior | [SetExplicitMockBehaviorAnalyzer.cs](../../src/Analyzers/SetExplicitMockBehaviorAnalyzer.cs) | -| [Moq1410](./Moq1410.md) | Best Practice | Explicitly set the Strict mocking behavior | [SetStrictMockBehaviorAnalyzer.cs](../../src/Analyzers/SetStrictMockBehaviorAnalyzer.cs) | -| [Moq1420](./Moq1420.md) | Best Practice | Redundant Times.AtLeastOnce() specification can be removed | [RedundantTimesSpecificationAnalyzer.cs](../../src/Analyzers/RedundantTimesSpecificationAnalyzer.cs) | -| [Moq1500](./Moq1500.md) | Best Practice | MockRepository.Verify() should be called | [MockRepositoryVerifyAnalyzer.cs](../../src/Analyzers/MockRepositoryVerifyAnalyzer.cs) | +| [Moq1210](./Moq1210.md) | Correctness | Verify should be used only for overridable members | [VerifyShouldBeUsedOnlyForOverridableMembersAnalyzer.cs](../../src/Analyzers/VerifyShouldBeUsedOnlyForOverridableMembersAnalyzer.cs) | +| [Moq1300](./Moq1300.md) | Usage | `Mock.As()` should take interfaces only | [AsShouldBeUsedOnlyForInterfaceAnalyzer.cs](../../src/Analyzers/AsShouldBeUsedOnlyForInterfaceAnalyzer.cs) | +| [Moq1301](./Moq1301.md) | Usage | Mock.Get() should not take literals | [MockGetShouldNotTakeLiteralsAnalyzer.cs](../../src/Analyzers/MockGetShouldNotTakeLiteralsAnalyzer.cs) | +| [Moq1302](./Moq1302.md) | Usage | LINQ to Mocks expression should be valid | [LinqToMocksExpressionShouldBeValidAnalyzer.cs](../../src/Analyzers/LinqToMocksExpressionShouldBeValidAnalyzer.cs) | +| [Moq1400](./Moq1400.md) | Best Practice | Explicitly choose a mocking behavior instead of relying on the default (Loose) behavior | [SetExplicitMockBehaviorAnalyzer.cs](../../src/Analyzers/SetExplicitMockBehaviorAnalyzer.cs) | +| [Moq1410](./Moq1410.md) | Best Practice | Explicitly set the Strict mocking behavior | [SetStrictMockBehaviorAnalyzer.cs](../../src/Analyzers/SetStrictMockBehaviorAnalyzer.cs) | +| [Moq1420](./Moq1420.md) | Best Practice | Redundant Times.AtLeastOnce() specification can be removed | [RedundantTimesSpecificationAnalyzer.cs](../../src/Analyzers/RedundantTimesSpecificationAnalyzer.cs) | +| [Moq1500](./Moq1500.md) | Best Practice | MockRepository.Verify() should be called | [MockRepositoryVerifyAnalyzer.cs](../../src/Analyzers/MockRepositoryVerifyAnalyzer.cs) | + ## Guidance for Future Rules ### Categories + - **Usage**: Rules that guide correct use of Moq APIs (e.g., not mocking sealed classes, correct use of As, etc.) - **Correctness**: Rules that prevent bugs or incorrect test logic (e.g., callback signatures, constructor arguments). - **Best Practice**: Rules that encourage maintainable, robust, or idiomatic Moq usage (e.g., explicit/strict behavior). ### Diagnostic ID Ranges -| Range | Category | Description / Example Rules | -|---------------|---------------|-------------------------------------------------------------| -| Moq1000-1099 | Usage | Prohibits sealed class mocks, restricts As to interfaces | -| Moq1100-1199 | Correctness | Ensures callback signatures match, setup is valid | -| Moq1200-1299 | Correctness | Prevents async result setups, checks constructor args | -| Moq1300-1399 | Usage | Restricts use of literals, enforces API usage patterns | -| Moq1400-1499 | Best Practice | Encourages explicit/strict mock behavior | -| Moq1500-1599 | Best Practice | Repository and verification patterns | -| Moq1600-1999 | Reserved | Reserved for future rules | + +| Range | Category | Description / Example Rules | +| ------------ | ------------- | ----------------------------------------------------------- | +| Moq1000-1099 | Usage | Prohibits sealed class mocks, restricts As to interfaces | +| Moq1100-1199 | Correctness | Ensures callback signatures match, setup is valid | +| Moq1200-1299 | Correctness | Prevents async result setups, checks constructor args | +| Moq1300-1399 | Usage | Restricts use of literals, enforces API usage patterns | +| Moq1400-1499 | Best Practice | Encourages explicit/strict mock behavior | +| Moq1500-1599 | Best Practice | Repository and verification patterns | +| Moq1600-1999 | Reserved | Reserved for future rules | - When adding new rules, assign the next available ID in the appropriate category range. - Document new rules in this table, including their category, a concise title, and links to both documentation and implementation.