Selectorlyzer.Analyzers
Selectorlyzer is a configurable Roslyn Analyzer that uses CSS selector-like syntax for enforcing project specific conventions.
Install / Use
/learn @rlgnak/Selectorlyzer.AnalyzersREADME
Selectorlyzer Analyzers for the .NET Compiler Platform
Selectorlyzer.Analyzers is a highly customizable Roslyn Analyzer designed to empower developers with the ability to create project-specific analyzers using a CSS selector-like syntax.
<img src="https://github.com/rlgnak/Selectorlyzer.Analyzers/assets/1643317/a56e8fef-1e42-47b4-acbf-7be884f91d6f" width="453" height="250">Getting Started
The preferable way to use the analyzers is to add the NuGet package Selectorlyzer.Analyzers to the project where you want to enforce rules.
A selectorlyzer.json or .selectorlyzer.json file is used to specify rules.
Installation
- Install the NuGet Package
Selectorlyzer.Analyzers.
dotnet add package Selectorlyzer.Analyzers
- Create and configure
selectorlyzer.jsonfile.
{
"rules": [
{
"selector": ":class:has([Name='InvalidClassName'])",
"message": "Classes should not be named 'InvalidClassName'",
"severity": "error"
},
{
"selector": "InvocationExpression[Expression='Console.WriteLine']",
"message": "Do not use Console.WriteLine",
"severity": "error"
},
{
"selector": ":class:implements([Name='BaseRepository'])",
"rule": ":implements([Name='I{Name}'])",
"message": "Classes that implement 'BaseRepository' should implement an interface with the same name.",
"severity": "error"
}
]
}
- Add the following to your
.csprojfiles
<ItemGroup>
<AdditionalFiles Include="selectorlyzer.json" />
</ItemGroup>
Example Rules
Naming Conventions
:class:has([Name='InvalidClassName'])- Classes should not be namedInvalidClassName:class:has([Name^='InvalidPrefix'])- Classes should not start withInvalidPrefix:class:has([Name$='InvalidSuffix'])- Classes should not end withInvalidSuffix:method:has([Name='InvalidMethodName'])- Methods should not be namedInvalidMethodName:method[Modifiers~='async']:not([Name$='Async'])- Async method names should end withAsync:method[Modifiers~='async'][Name$='Async']- Async method names should not end withAsync:property[Type^='bool']:not([Identifier$='Flag'])- Boolean property names should end withFlag
Custom Project Conventions
InvocationExpression[Expression='Console.WriteLine']-Console.WriteLineshould not be used.InvocationExpression[Expression^='Assert.\']- Methods starting withAssert.should not be used.:class:implements([Name$='DataTransferObject']) ConstructorDeclaration- Classes that implement DataTransferObject should not have constructors.:class[Name$='Controller'] :method[Modifiers~='public'][ReturnType='void']- Public methdos within classes with names ending inControllershould not returnvoid.:class[Name$='Controller'] :method[Modifiers~='public'] Attribute[Name^='Http']- Public methods within classes with names ending inControllershould have an attribute that starts withHttp:class:implements([Name='BaseRepository'])with a rule of:implements([Name='I{Name}'])- Classes that implementBaseRepositoryshould implement an interface with the same name.:method Block > * ReturnStatement- Methods should only have on return statment and it should be the last statement in the method.
Selectors
Selectorlyzer uses a query langage for Roslyn Inspired by Qulaly and esquery. These selectors are used to identify speicifc sytax nodes.
Supported Selectors
Selectorlizer supports a subset of CSS selector level 4. The selector engine also supports Selectorlizer-specific extensions to the selector.
- SyntaxNode Type:
MethodDeclaration,ClassDeclaration...- See also SyntaxKind enum
- SyntaxNode Univarsal:
* - SyntaxNode pseudo-classes (for short-hand)
:method:class:interface:lambda:property
- Combinators
- Descendant:
node descendant - Child:
node > child - Next-sibling:
node + next - Subsequent-sibling:
node ~ sibling
- Descendant:
- Pseudo-class
- Negation:
:not(...) - Matches-any:
:is(...) - Relational:
:has(...) :nth-child:first-child:last-child
- Negation:
- Attributes (Properties)
[PropName](existance)[PropName = 'Exact'][PropName ^= 'StartsWith'][PropName $= 'EndsWith'][PropName *= 'Contains'][PropName ~= 'Item'](ex.[Modifiers ~= 'async'])
- Extensions
:implements(...): Combinator for checking if a class or interface implements a matching selector[Name = 'MethodName']: Name special propertyNameis a special property for convenience that can be used inMethodDeclaration,ClassDeclaration... etc
[TypeParameters.Count > 0]: ConditionsParameters.CountTypeParameters.Count
License
MIT License
Selectorlyzer.Analyzers Copyright © 2023-present Richard Graves <rlgnak+selectorlyzer@gmail.com>
