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) {
// Only check private StatelessWidget classes
if (!node.nameToken.lexeme.startsWith('_')) return;
final ExtendsClause? extendsClause = node.extendsClause;
if (extendsClause == null) return;
final String? superclassName = extendsClause.superclass.element?.name;
if (superclassName != 'StatelessWidget') return;
// Check if it has only the build method (no fields, no other methods)
final List<ClassMember> members = node.bodyMembers.toList();
bool hasFields = false;
bool hasOtherMethods = false;
MethodDeclaration? buildMethod;
for (final ClassMember member in members) {
if (member is FieldDeclaration) {
hasFields = true;
break;
}
if (member is MethodDeclaration) {
if (member.name.lexeme == 'build') {
buildMethod = member;
} else {
// Has other methods beyond build - too complex to be a simple method
hasOtherMethods = true;
}
}
if (member is ConstructorDeclaration) {
// Has a constructor with logic - keep as class
if (member.body is! EmptyFunctionBody) {
hasOtherMethods = true;
}
}
}
// Skip if has fields (needs to be a class for state)
if (hasFields) return;
// Skip if has other methods (too complex)
if (hasOtherMethods) return;
// Skip if no build method found
if (buildMethod == null) return;
// Check build method size
final FunctionBody body = buildMethod.body;
final root = node.root;
if (root is! CompilationUnit) return;
final int startLine = root.lineInfo.getLocation(body.offset).lineNumber;
final int endLine = root.lineInfo.getLocation(body.end).lineNumber;
final int lineCount = endLine - startLine + 1;
if (lineCount <= _maxBuildLines) {
reporter.atToken(node.nameToken, code);
}
});
}