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) {
final String className = node.namePart.typeName.lexeme;
// Skip private classes
if (className.startsWith('_')) return;
// Skip if abstract - abstract classes need different modifiers
if (node.abstractKeyword != null) return;
// Skip if already has a modifier
if (node.baseKeyword != null ||
node.finalKeyword != null ||
node.interfaceKeyword != null ||
node.sealedKeyword != null ||
node.mixinKeyword != null) {
return;
}
// Heuristic: Classes with only private constructors are good candidates
bool hasPublicConstructor = false;
bool hasAnyConstructor = false;
for (final ClassMember member in node.body.members) {
if (member is ConstructorDeclaration) {
hasAnyConstructor = true;
final Token? nameToken = member.name;
final bool isPrivate =
nameToken != null && nameToken.lexeme.startsWith('_');
if (!isPrivate && member.factoryKeyword == null) {
hasPublicConstructor = true;
}
}
}
// If no constructors defined, there's an implicit public constructor
if (!hasAnyConstructor) {
hasPublicConstructor = true;
}
// Only suggest final for classes with private-only constructors
// or classes that look like utility/service classes
if (!hasPublicConstructor) {
reporter.atToken(node.namePart.typeName, code);
}
});
}