Skip to content

Selectors

How the first part of every rule query determines what is checked.

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