runWithReporter method
Override this method to implement your lint rule.
Use context to register callbacks for AST node types:
context.addMethodInvocation((node) {
if (condition) {
reporter.atNode(node);
}
});
Implementation
@override
void runWithReporter(
SaropaDiagnosticReporter reporter,
SaropaContext context,
) {
context.addClassDeclaration((ClassDeclaration node) {
// Skip abstract classes — abstract final class is the correct pattern
// for non-instantiable namespaces (handled by
// prefer_abstract_final_static_class instead)
if (node.abstractKeyword != null) return;
// Skip if class extends something other than Object
if (node.extendsClause != null) return;
// Skip if class has mixins or implements interfaces
if (node.withClause != null || node.implementsClause != null) return;
// Check if all members are static
bool hasNonStaticMember = false;
bool hasStaticMember = false;
bool hasPrivateConstructor = false;
for (final ClassMember member in node.bodyMembers) {
if (member is ConstructorDeclaration) {
final String? name = member.name?.lexeme;
if (name != null && name.startsWith('_')) {
// Private constructor — prefer_abstract_final_static_class
// handles this case instead.
hasPrivateConstructor = true;
} else if (member.factoryKeyword == null) {
hasNonStaticMember = true;
}
} else if (member is FieldDeclaration) {
if (member.isStatic) {
hasStaticMember = true;
} else {
hasNonStaticMember = true;
}
} else if (member is MethodDeclaration) {
if (member.isStatic) {
hasStaticMember = true;
} else {
hasNonStaticMember = true;
}
}
}
if (hasStaticMember && !hasNonStaticMember && !hasPrivateConstructor) {
reporter.atToken(node.nameToken, code);
}
});
}