The Selector is the first part of a rule — the from clause or the initial
collection access. It determines which kind of code element the rule
operates on.
Top-Level Collections
You can use any of these directly as a starting point:
| Collection | Element type | Description |
|---|---|---|
Types |
TypeModel |
All classes, structs, interfaces, enums |
Methods |
MethodModel |
All methods including constructor variants |
Properties |
PropertyModel |
All properties |
Fields |
FieldModel |
All fields |
Events |
EventModel |
All events |
Namespaces |
NamespaceModel |
All namespaces |
Assemblies |
AssemblyModel |
All analyzed assemblies |
TypeDependencies |
TypeDependency |
Coupling relationships between types |
SyntaxIssues |
SyntaxIssue |
Pre-analyzed pattern findings |
Two Syntax Styles
The DSL supports both from x in Y (query syntax) and Y.Where(x => ...)
(method syntax). Both produce the same result.
Query syntax:
from t in Types
where t.Kind == "Class"
where t.IsAbstract
select t
Method syntax:
Types.Where(t => t.Kind == "Class" && t.IsAbstract)
When to use which? With multiple where clauses, query syntax is more
readable. With a single condition or an aggregation (.Count > 10), method
syntax is shorter.
Per-Element Properties
Each element type has properties you can use in where clauses and
sub-queries.
TypeModel (excerpt)
t.Name // simple name
t.FullName // with namespace
t.Namespace // namespace only
t.Kind // "Class", "Interface", "Struct", "Enum"
t.IsAbstract // bool
t.IsSealed // bool
t.IsStatic // bool
t.LinesOfCode // int
t.NumberOfMethods // int
t.Methods // collection of MethodModel
t.Constructors // collection of MethodModel
t.NestedTypes // collection of TypeModel
t.DerivedTypes // collection of TypeModel
t.UsedTypes // which types does this type reference
t.UsedByTypes // which types reference this one
t.BaseType.FullName // parent class
MethodModel (excerpt)
m.Name // method name
m.AccessModifier // "Public", "Private", "Protected", "Internal"
m.IsAsync // bool
m.IsStatic // bool
m.IsOverride // bool
m.IsInterfaceImplementation // bool
m.Parameters // collection of ParameterModel
m.CalledMethods // which methods does this one call
m.CalledByMethods // which methods call this one
m.LinesOfCode // int
m.DeclaringType // TypeModel
The complete schema list is available in the CLI via codecharter list-rules --schema
(planned, coming in the next minor release).
Composition with Any / All / Count
Sub-queries on collection properties:
# Classes with at least one async method without CancellationToken
from t in Types
where t.Methods.Any(m =>
m.IsAsync &&
!m.Parameters.Any(p => p.TypeShortName == "CancellationToken"))
select t
# Classes with more than 10 public methods
from t in Types
where t.Methods.Where(m => m.AccessModifier == "Public").Count > 10
select t
SyntaxIssues as a Selector
Some patterns are detected syntactically by the analyzer (not semantically via
the type model) and exposed as SyntaxIssue objects. A rule can filter on a
specific category:
@name "Direct DateTime usage"
@severity warn
@category "Testability"
SyntaxIssues.Where(s => s.Kind == "DateTimeDirectUsage")
The Kind values are fixed. See the schema reference
(see Syntax Overview).
What's Next
- Suppressions — when a location should be deliberately excluded
- Best Practices — good vs. bad Selector strategies