Contributions can come in many forms: filing defects and feature requests, (failing) unit tests, documentations improvements, new analyzers, expanding existing ones, etc.
Remember that this library is not intended to provide productivity helpers or code styling suggestions. It is about surfacing defects at compile time and preventing issues that would otherwise go unnoticed until it's too late.
If you have a new feature in mind, try to be as precise as possible. Think about edge cases beforehand and mention them in your post. Restrict the scope of your analyzer if needed so that it only covers a well known defined set of use cases. It is often tempting to implement it in a more open-ended way but ultimately we want to provide a helpful signal-to-noise ratio.
For example, initially ElementaryMethodsOfTypeInCollectionNotOverridden
would warn every time such a collection was instantiated with such a type. While technically correct it also caused thousands of warnings on large codebases, the vast majority without any actual issues. I've then rewritten the analyzer so that it only warns when an actual lookup is performed. This reduced the warnings from 2000 to 1 but that one warning was actually valuable.
- Copy the structure of an existing unit test file, adjust the names and start writing test cases
- Create a new
DiagnosticId
- Bump the package version
- Update the Changelog
- Copy an existing analyzer and adjust as necessary
- Optionally: copy an existing code fix and adjust as necessary
- Run
dotnet format
to fix any formatting issues
If at any time you have questions, create an issue on Github to start a discussion. If you're interested in learning more about Roslyn analyzers in general, I recommend reading through this paper I wrote years ago.
CI will run to verify you've added the changelog and bumped the version. Once all tests pass and the code is approved, I'll merge it and a new release will be automatically distributed to all delivery platforms (NuGet, VS marketplace & Github packages).
These come in no particular order of importance.
- Unit tests are all written through sample code which exercises (or doesn't) the analyzers. Try to keep the samples as concise as possible. A lot of the older analyzers are quite verbose at times by specifying
namespace
andclass
, even when it's not necessary (e.g. because top-level statements are available). Don't use these as example, go for brevity where it makes sense. - When deciding on what type of syntax node should trigger your analyzer, approach it in a way that should give the best performance in general. e.g. if you're trying to avoid
throw null;
, don't trigger onSyntaxKind.NullKeyword
but useSyntaxKind.ThrowStatement
instead. The former is likely to be much more common than the latter. - Think about the level you need to work on. Sometimes you need a
SyntaxNode
, sometimes you need anISymbol
. In general, the higher level the better.SyntaxNode
will open you up to dealing withpartial
classes and other implementation details.ISymbol
abstracts all that so you're less likely to miss some of the edge cases. - Expect invalid code. When typing
void Method() { }
, you'll have many individual moments where the code is in an invalid state:void M
,void Method (
, etc. Make sure you include the appropriatenull
checks in your code because they tend to go unnoticed through unit tests as they only include the valid code at the end. Nullable reference types are enabled so that should go a very long way towards preventing these issues. - Make sure your unit tests cover the less common scenarios as well. Some frequently relevant test cases:
partial
classes and methods- top-level statements vs nested in a class
- Fully qualified names vs imported ones (
System.Attribute
vsAttribute
) - Short-hand attribute names vs long ones (
Obsolete
vsObsoleteAttribute
) - A declaration with multiple declarators (
int test, test2 = 5;
) - Methods with a body vs those with an expression bodied member
- Methods? Local functions? Global statements? Lambdas? A statement can be contained within many different contexts, don't assume it's all inside a method
- AST visualizers are your friends. I tend to use LINQPad to render an interactive AST tree but Roslyn Quoter or the built-in Syntax Visualizer are great alternatives as well.
Open your Roslyn hive by running the VSIX. In the Visual Studio environment that opens, go to Tools > Options > Text Editor > C# > Advanced
and uncheck Run code analysis in separate process. Stop the VSIX and try again -- you should now see symbols load and breakpoints will get hit again.