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.addMethodInvocation((MethodInvocation node) {
final String methodName = node.methodName.name;
if (methodName != 'add' && methodName != 'addError') return;
// Check if target is a StreamController (exact name/endsWith to avoid FP)
final Expression? target = node.target;
if (target == null) return;
final String targetNameLower = extractTargetName(target).toLowerCase();
if (!targetNameLower.endsWith('controller') &&
!targetNameLower.endsWith('stream')) {
return;
}
// Check if this is immediately after controller creation
AstNode? current = node.parent;
FunctionBody? functionBody;
while (current != null) {
if (current is FunctionBody) {
functionBody = current;
break;
}
current = current.parent;
}
if (functionBody == null) return;
final String bodySource = functionBody.toSource();
final int nodeOffset = node.offset;
final int bodyOffset = functionBody.offset;
if (!_streamControllerCreationPatterns.any(
(p) => p.hasMatch(bodySource),
)) {
return;
}
final String beforeAdd = bodySource.substring(0, nodeOffset - bodyOffset);
if (_streamSyncMitigationPatterns.any((p) => p.hasMatch(beforeAdd)) ||
_syncTruePattern.hasMatch(bodySource)) {
return;
}
// Check if StreamController creation is within last 200 chars (rough heuristic)
final int controllerIndex = beforeAdd.lastIndexOf('StreamController');
if (controllerIndex != -1 && (beforeAdd.length - controllerIndex) < 200) {
reporter.atNode(node);
}
});
}