add lint rule
This commit is contained in:
@@ -1,5 +1,8 @@
|
|||||||
import 'package:analyzer/error/error.dart' show ErrorSeverity;
|
import 'package:analyzer/dart/ast/ast.dart';
|
||||||
|
import 'package:analyzer/dart/element/element2.dart';
|
||||||
|
import 'package:analyzer/error/error.dart' show ErrorSeverity, AnalysisError;
|
||||||
import 'package:analyzer/error/listener.dart';
|
import 'package:analyzer/error/listener.dart';
|
||||||
|
import 'package:analyzer/source/source_range.dart';
|
||||||
import 'package:custom_lint_builder/custom_lint_builder.dart';
|
import 'package:custom_lint_builder/custom_lint_builder.dart';
|
||||||
// ignore: depend_on_referenced_packages
|
// ignore: depend_on_referenced_packages
|
||||||
import 'package:glob/glob.dart';
|
import 'package:glob/glob.dart';
|
||||||
@@ -20,6 +23,10 @@ class ImmichLinter extends PluginBase {
|
|||||||
buildGlob(forbiddenPaths), restrict));
|
buildGlob(forbiddenPaths), restrict));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (configs.rules[NoDebugPrintRule.name]?.enabled ?? true) {
|
||||||
|
rules.add(NoDebugPrintRule());
|
||||||
|
}
|
||||||
return rules;
|
return rules;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -87,3 +94,150 @@ class ImportRule extends DartLintRule {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class NoDebugPrintRule extends DartLintRule {
|
||||||
|
static const name = 'no_debug_print';
|
||||||
|
static const importPath = 'package:flutter/src/foundation/print.dart';
|
||||||
|
static const _code = LintCode(
|
||||||
|
name: name,
|
||||||
|
problemMessage:
|
||||||
|
'Use dPrint instead of debugPrint for proper tree-shaking in release builds.',
|
||||||
|
correctionMessage: 'Replace debugPrint with dPrint',
|
||||||
|
errorSeverity: ErrorSeverity.WARNING,
|
||||||
|
);
|
||||||
|
|
||||||
|
NoDebugPrintRule() : super(code: _code);
|
||||||
|
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
static bool isDebugPrint(Element2? element) {
|
||||||
|
return element is PropertyAccessorElement2 &&
|
||||||
|
element.variable3?.getter2?.name3 == 'debugPrint' &&
|
||||||
|
element.library2.identifier == importPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
static bool isWrapped(AstNode node) {
|
||||||
|
AstNode? parent = node.parent;
|
||||||
|
while (parent != null) {
|
||||||
|
if (parent case IfStatement(:SimpleIdentifier expression)
|
||||||
|
when expression.name == 'kDebugMode') {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
parent = parent.parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void run(
|
||||||
|
CustomLintResolver resolver,
|
||||||
|
ErrorReporter reporter,
|
||||||
|
CustomLintContext context,
|
||||||
|
) {
|
||||||
|
context.registry.addFunctionExpressionInvocation((node) {
|
||||||
|
final function = node.function;
|
||||||
|
if (function case SimpleIdentifier(:final element)
|
||||||
|
when isDebugPrint(element) && !isWrapped(node)) {
|
||||||
|
reporter.atNode(function, code);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
context.registry.addMethodInvocation((node) {
|
||||||
|
final methodName = node.methodName;
|
||||||
|
if (isDebugPrint(methodName.element) && !isWrapped(node)) {
|
||||||
|
reporter.atNode(methodName, code);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Fix> getFixes() => [ReplaceDebugPrintFix()];
|
||||||
|
}
|
||||||
|
|
||||||
|
class ReplaceDebugPrintFix extends DartFix {
|
||||||
|
static const dPrintImportPath =
|
||||||
|
"import 'package:immich_mobile/utils/debug_print.dart';";
|
||||||
|
|
||||||
|
@override
|
||||||
|
void run(
|
||||||
|
CustomLintResolver resolver,
|
||||||
|
ChangeReporter reporter,
|
||||||
|
CustomLintContext context,
|
||||||
|
AnalysisError error,
|
||||||
|
List<AnalysisError> allErrors,
|
||||||
|
) {
|
||||||
|
context.registry.addFunctionExpressionInvocation((node) {
|
||||||
|
final function = node.function;
|
||||||
|
if (error.sourceRange == function.sourceRange &&
|
||||||
|
function is SimpleIdentifier &&
|
||||||
|
NoDebugPrintRule.isDebugPrint(function.element)) {
|
||||||
|
_createFix(reporter, resolver, function, node);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
context.registry.addMethodInvocation((node) {
|
||||||
|
final methodName = node.methodName;
|
||||||
|
if (error.sourceRange == methodName.sourceRange &&
|
||||||
|
NoDebugPrintRule.isDebugPrint(methodName.element)) {
|
||||||
|
_createFix(reporter, resolver, methodName, node);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void _createFix(
|
||||||
|
ChangeReporter reporter,
|
||||||
|
CustomLintResolver resolver,
|
||||||
|
SimpleIdentifier identifier,
|
||||||
|
AstNode node,
|
||||||
|
) {
|
||||||
|
final arguments = switch (node) {
|
||||||
|
MethodInvocation(:final argumentList) => argumentList,
|
||||||
|
FunctionExpressionInvocation(:final argumentList) => argumentList,
|
||||||
|
_ => null,
|
||||||
|
};
|
||||||
|
if (arguments == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final changeBuilder = reporter.createChangeBuilder(
|
||||||
|
message: 'Replace with dPrint',
|
||||||
|
priority: 1,
|
||||||
|
);
|
||||||
|
|
||||||
|
changeBuilder.addDartFileEdit((builder) {
|
||||||
|
builder.addSimpleReplacement(identifier.sourceRange, 'dPrint');
|
||||||
|
final firstArg = arguments.arguments.firstOrNull;
|
||||||
|
if (firstArg != null) {
|
||||||
|
final argSource = resolver.source.contents.data.substring(
|
||||||
|
firstArg.offset,
|
||||||
|
firstArg.end,
|
||||||
|
);
|
||||||
|
|
||||||
|
builder.addSimpleReplacement(
|
||||||
|
SourceRange(firstArg.offset, firstArg.length),
|
||||||
|
'() => $argSource',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resolver.source.contents.data.contains(dPrintImportPath)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final unit = node.root;
|
||||||
|
if (unit is CompilationUnit) {
|
||||||
|
final lastImport =
|
||||||
|
unit.directives.whereType<ImportDirective>().lastOrNull;
|
||||||
|
|
||||||
|
if (lastImport != null) {
|
||||||
|
builder.addSimpleInsertion(lastImport.end, '\n$dPrintImportPath');
|
||||||
|
} else if (unit.directives.isNotEmpty) {
|
||||||
|
builder.addSimpleInsertion(
|
||||||
|
unit.directives.last.end, '\n\n$dPrintImportPath');
|
||||||
|
} else {
|
||||||
|
builder.addSimpleInsertion(0, '$dPrintImportPath\n\n');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
void dPrint(String Function() message) {
|
||||||
|
if (kDebugMode) {
|
||||||
|
debugPrint(message());
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user