Thank you for your interest in contributing to CancelCop! We welcome contributions from the community.
- Fork the repository
- Clone your fork locally
- Create a new branch for your feature or bugfix
- Make your changes following our guidelines
- Ensure all tests pass
- Submit a pull request
- .NET 10.0 SDK (or later)
- A C# IDE (Visual Studio, JetBrains Rider, or VS Code with C# extension), or a language-server-capable editor (Neovim, Helix, Emacs, Zed, ...)
- Git
If you use an editor that drives LSP servers directly (Neovim built-in LSP, Helix, Zed, Emacs lsp-mode/eglot, VS Code extensions), this project works with:
| Language | Server | Install |
|---|---|---|
| C# | csharp-ls |
dotnet tool restore (pinned in .config/dotnet-tools.json) |
| YAML (GitHub Actions) | yaml-language-server |
npm install -g yaml-language-server |
| Markdown | marksman |
brew install marksman |
Start the C# server against the solution:
dotnet csharp-ls --solution CancelCop.sln(Editors typically launch it for you; the command above is what your LSP client should run.)
Verify all three respond to an LSP initialize handshake:
python3 scripts/lsp-verify.py dotnet csharp-ls --solution CancelCop.sln
python3 scripts/lsp-verify.py yaml-language-server --stdio
python3 scripts/lsp-verify.py marksman server# Clone your fork
git clone https://github.com/YOUR_USERNAME/CancelCop.Analyzer.git
cd CancelCop.Analyzer
# Restore dependencies
dotnet restore
# Build solution
dotnet build
# Run tests
dotnet test
# Run tests with verbose output
dotnet test --logger "console;verbosity=detailed"CancelCop.Analyzer/
├── src/
│ ├── CancelCop.Analyzer/ # Diagnostic analyzers
│ │ ├── *Analyzer.cs # Diagnostic analyzers (CC001, CC002, etc.)
│ │ └── CancellationTokenHelpers.cs # Shared helper methods
│ ├── CancelCop.Analyzer.CodeFixes/ # Code fix providers and shared fix helpers
│ └── CancelCop.Analyzer.Package/ # NuGet packaging project
├── tests/
│ └── CancelCop.Analyzer.Tests/ # XUnit tests
│ ├── *AnalyzerTests.cs # Tests for analyzers
│ └── *CodeFixTests.cs # Tests for code fixes
├── samples/
│ └── CancelCop.Sample/ # Example project demonstrating rules
└── docs/ # Additional documentation
This project strictly follows TDD principles. For any new feature or bug fix:
- Write tests first: Define the expected behavior through tests
- Run tests: Verify that the new tests fail (red)
- Implement: Write the minimum code to make tests pass (green)
- Refactor: Clean up the code while keeping tests green
- Repeat: Continue until the feature is complete
Follow this checklist when adding a new rule:
- Define the rule ID (e.g., CC010)
- Write a clear description of what it detects
- Document why this matters (the problem it solves)
- Choose appropriate severity (Warning, Info, Error)
- Determine if a code fix is feasible
Create test file: tests/CancelCop.Analyzer.Tests/[RuleName]AnalyzerTests.cs
public class MyNewAnalyzerTests
{
[Fact]
public async Task ViolatingCode_ShouldReportDiagnostic()
{
var test = @"
// Code that violates the rule
";
var expected = VerifyCS.Diagnostic("CC0XX")
.WithLocation(0)
.WithArguments("expected", "arguments");
await VerifyCS.VerifyAnalyzerAsync(test, expected);
}
[Fact]
public async Task CorrectCode_ShouldNotReportDiagnostic()
{
var test = @"
// Code that follows the rule
";
await VerifyCS.VerifyAnalyzerAsync(test);
}
}Create analyzer file: src/CancelCop.Analyzer/[RuleName]Analyzer.cs
- Add comprehensive XML documentation
- Use
CancellationTokenHelpersfor common operations - Register appropriate syntax node actions
- Include the rule ID in a public constant
Create code fix file: src/CancelCop.Analyzer/[RuleName]CodeFixProvider.cs
- Create corresponding test file:
*CodeFixTests.cs - Ensure the fix preserves code formatting
- Handle edge cases gracefully
- Add rule to
AnalyzerReleases.Unshipped.md - Update
NEXT_STEPS.mdwith completion status - Add sample to
samples/CancelCop.Sample/ - Update
README.mdwith new rule
# Run all tests
dotnet test
# Build in Release mode
dotnet build -c Release
# Verify sample project shows expected warnings
dotnet build samples/CancelCop.Sample- Follow the existing code style in the project
- Use meaningful names for variables, methods, and classes
- Keep methods focused and small (single responsibility)
- Prefer early returns over deep nesting
All public APIs must have XML documentation:
/// <summary>
/// Brief description of the class/method.
/// </summary>
/// <remarks>
/// <para><b>Rule ID:</b> CC0XX</para>
/// <para><b>Why this matters:</b> Explanation...</para>
/// </remarks>
/// <example>
/// <code>
/// // Example usage
/// </code>
/// </example>- Use
CancellationTokenHelpersfor common CancellationToken operations - Register for specific syntax nodes, not entire compilation
- Enable concurrent execution:
context.EnableConcurrentExecution() - Exclude generated code:
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None) - Store diagnostic properties for code fix providers
- All tests must pass before submitting a PR
- Add tests for both positive (violations) and negative (no violations) cases
- Test edge cases and boundary conditions
- Use the Roslyn testing framework patterns
[MethodUnderTest]_[Scenario]_[ExpectedBehavior]
Examples:
ForEachLoop_WithoutCancellationCheck_ShouldReportDiagnosticForLoop_WithThrowIfCancellationRequested_ShouldNotReportDiagnostic
Use conventional commit format:
type(scope): brief description
[optional body]
[optional footer]
Types:
feat: New featurefix: Bug fixtest: Adding/updating testsdocs: Documentation changesrefactor: Code changes that don't fix bugs or add featureschore: Maintenance tasks
Examples:
feat(CC009): add loop cancellation analyzerfix(CC002): handle local functions correctlytest(CC001): add ValueTask return type testsdocs: update README with CC009 examples
- All tests pass locally
- Code follows project style guidelines
- XML documentation added for public APIs
-
AnalyzerReleases.Unshipped.mdupdated (if adding/changing rules) - README updated (if adding new features)
- No merge conflicts with the
mainbranch
- One feature per PR: Keep pull requests focused
- Clear title: Use conventional commit format
- Description: Explain what changes you made and why
- Reference issues: Use
Fixes #123orCloses #123 - Screenshots: Include if there are visual changes
- Create PR against the
mainbranch - Wait for automated CI checks to pass
- Address reviewer feedback
- Once approved, maintainer will merge
When reporting bugs, please include:
- Clear description of the issue
- Steps to reproduce the problem
- Expected behavior vs actual behavior
- Code sample that demonstrates the issue
- Environment: OS, .NET version, IDE
Use this template:
## Bug Description
[Clear description of the issue]
## Steps to Reproduce
1. [First step]
2. [Second step]
3. [...]
## Expected Behavior
[What should happen]
## Actual Behavior
[What actually happens]
## Code Sample
```csharp
// Code that demonstrates the issue- OS: [e.g., macOS 14.0, Windows 11]
- .NET SDK: [e.g., 10.0.101]
- IDE: [e.g., Rider 2024.3, VS 2022]
## Suggesting Features
Feature suggestions are welcome! Please:
1. Check existing issues to avoid duplicates
2. Provide a clear use case
3. Explain how it aligns with CancelCop's goals
4. Consider providing a PR if you can implement it
## Questions?
- Open an issue for discussion
- Check existing issues and pull requests
- Review the README.md and documentation
## License
By contributing to CancelCop, you agree that your contributions will be licensed under the MIT License.