Skip to content

Commit

Permalink
Avoid debug print (#134)
Browse files Browse the repository at this point in the history
* New lint: avoid_using_debug_print

* udate changelog

* fix PR issues

* version up, comment for function reference

* fix PR
  • Loading branch information
Viktor-Mudrytskyi authored Mar 12, 2024
1 parent 54d3a54 commit b6d5d69
Show file tree
Hide file tree
Showing 10 changed files with 249 additions and 1 deletion.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 0.1.5

- Add `avoid_using_debug_print` rule

## 0.1.4

- Removed deprecated lints:
Expand Down
1 change: 1 addition & 0 deletions example/analysis_options.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ custom_lint:
- avoid_unnecessary_setstate
- double_literal_format
- avoid_unnecessary_type_assertions
- avoid_using_debug_print
- avoid_using_api:
severity: info
entries:
Expand Down
1 change: 1 addition & 0 deletions lib/analysis_options.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ custom_lint:
- avoid_unnecessary_type_casts
- avoid_unrelated_type_assertions
- avoid_unused_parameters
- avoid_using_debug_print

- cyclomatic_complexity:
max_complexity: 10
Expand Down
2 changes: 2 additions & 0 deletions lib/solid_lints.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import 'package:solid_lints/src/lints/avoid_unnecessary_type_casts/avoid_unneces
import 'package:solid_lints/src/lints/avoid_unrelated_type_assertions/avoid_unrelated_type_assertions_rule.dart';
import 'package:solid_lints/src/lints/avoid_unused_parameters/avoid_unused_parameters_rule.dart';
import 'package:solid_lints/src/lints/avoid_using_api/avoid_using_api_rule.dart';
import 'package:solid_lints/src/lints/avoid_using_debug_print/avoid_using_debug_print_rule.dart';
import 'package:solid_lints/src/lints/cyclomatic_complexity/cyclomatic_complexity_metric.dart';
import 'package:solid_lints/src/lints/double_literal_format/double_literal_format_rule.dart';
import 'package:solid_lints/src/lints/function_lines_of_code/function_lines_of_code_metric.dart';
Expand Down Expand Up @@ -59,6 +60,7 @@ class _SolidLints extends PluginBase {
PreferLastRule.createRule(configs),
PreferMatchFileNameRule.createRule(configs),
ProperSuperCallsRule.createRule(configs),
AvoidUsingDebugPrint.createRule(configs),
];

// Return only enabled rules
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/ast/syntactic_entity.dart';
import 'package:analyzer/error/listener.dart';
import 'package:custom_lint_builder/custom_lint_builder.dart';
import 'package:solid_lints/src/lints/avoid_using_debug_print/models/avoid_using_debug_print_func_model.dart';
import 'package:solid_lints/src/models/rule_config.dart';
import 'package:solid_lints/src/models/solid_lint_rule.dart';

/// A `avoid_using_debug_print` rule which forbids calling or referencing
/// debugPrint function from flutter/foundation.
///
/// ### Example
///
/// #### BAD:
///
/// ```dart
/// debugPrint(''); // LINT
/// var ref = debugPrint; // LINT
/// var ref2;
/// ref2 = debugPrint; // LINT
/// ```
///
/// #### GOOD:
///
/// ```dart
/// log('');
/// ```
class AvoidUsingDebugPrint extends SolidLintRule {
/// The [LintCode] of this lint rule that represents
/// the error when debugPrint is called
static const lintName = 'avoid_using_debug_print';

AvoidUsingDebugPrint._(super.config);

/// Creates a new instance of [AvoidUsingDebugPrint]
/// based on the lint configuration.
factory AvoidUsingDebugPrint.createRule(CustomLintConfigs configs) {
final rule = RuleConfig(
configs: configs,
name: lintName,
problemMessage: (_) => "Avoid using 'debugPrint'",
);

return AvoidUsingDebugPrint._(rule);
}

@override
void run(
CustomLintResolver resolver,
ErrorReporter reporter,
CustomLintContext context,
) {
context.registry.addFunctionExpressionInvocation(
(node) {
final func = node.function;
if (func is! Identifier) {
return;
}
_checkIdentifier(
identifier: func,
node: node,
reporter: reporter,
);
},
);

// addFunctionReference does not get triggered.
// addVariableDeclaration and addAssignmentExpression
// are used as a workaround for simple cases

context.registry.addVariableDeclaration((node) {
_handleVariableAssignmentDeclaration(
node: node,
reporter: reporter,
);
});

context.registry.addAssignmentExpression((node) {
_handleVariableAssignmentDeclaration(
node: node,
reporter: reporter,
);
});
}

/// Checks whether the function identifier satisfies conditions
void _checkIdentifier({
required Identifier identifier,
required AstNode node,
required ErrorReporter reporter,
}) {
final funcModel = AvoidUsingDebugPrintFuncModel.parseExpression(identifier);

if (funcModel.hasSameName && funcModel.hasTheSameSource) {
reporter.reportErrorForNode(code, node);
}
}

/// Returns null if doesnt have right operand
SyntacticEntity? _getRightOperand(List<SyntacticEntity> entities) {
/// Example var t = 15; 15 is in 3d position
if (entities.length < 3) {
return null;
}
return entities[2];
}

/// Handles variable assignment and declaration
void _handleVariableAssignmentDeclaration({
required AstNode node,
required ErrorReporter reporter,
}) {
final rightOperand = _getRightOperand(node.childEntities.toList());

if (rightOperand == null || rightOperand is! Identifier) {
return;
}

_checkIdentifier(
identifier: rightOperand,
node: node,
reporter: reporter,
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import 'package:analyzer/dart/ast/ast.dart';

/// A class used to parse function expression
class AvoidUsingDebugPrintFuncModel {
/// Function name
final String name;

/// Function's source path
final String sourcePath;

/// A class used to parse function expression
const AvoidUsingDebugPrintFuncModel({
required this.name,
required this.sourcePath,
});

/// A constructor that parses identifier into [name] and [sourcePath]
factory AvoidUsingDebugPrintFuncModel.parseExpression(
Identifier identifier,
) {
switch (identifier) {
case PrefixedIdentifier():
final prefix = identifier.prefix.name;
return AvoidUsingDebugPrintFuncModel(
name: identifier.name.replaceAll('$prefix.', ''),
sourcePath:
identifier.staticElement?.librarySource?.uri.toString() ?? '',
);
case SimpleIdentifier():
return AvoidUsingDebugPrintFuncModel(
name: identifier.name,
sourcePath:
identifier.staticElement?.librarySource?.uri.toString() ?? '',
);
default:
return AvoidUsingDebugPrintFuncModel._empty();
}
}

factory AvoidUsingDebugPrintFuncModel._empty() {
return const AvoidUsingDebugPrintFuncModel(
name: '',
sourcePath: '',
);
}

static const String _printPath = 'package:flutter/src/foundation/print.dart';

static const String _debugPrint = 'debugPrint';

/// Ehether the function has the same source library as debugPrint func
bool get hasTheSameSource => _printPath == sourcePath;

/// Ehether the function has the same name as debugPrint
bool get hasSameName => _debugPrint == name;

@override
String toString() {
return '$name, $sourcePath';
}
}
1 change: 1 addition & 0 deletions lint_test/analysis_options.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ custom_lint:
- newline_before_return
- no_empty_block
- no_equal_then_else
- avoid_using_debug_print
- member_ordering:
alphabetize: true
order:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// ignore_for_file: unused_local_variable

import 'package:flutter/foundation.dart' as f;

/// Test the avoid_using_debug_print
void avoidDebugPrintTest() {
// expect_lint: avoid_using_debug_print
f.debugPrint('');

// expect_lint: avoid_using_debug_print
final test = f.debugPrint;

test('test');

// expect_lint: avoid_using_debug_print
final test2 = f.debugPrint('');

debugPrint();

debugPrint;
}

void debugPrint() {
return;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// ignore_for_file: unused_local_variable

import 'package:flutter/foundation.dart';

/// Test the avoid_using_debug_print
void avoidDebugPrintTest() {
// expect_lint: avoid_using_debug_print
debugPrint('');

// expect_lint: avoid_using_debug_print
final test = debugPrint;

var test2;

// expect_lint: avoid_using_debug_print
test2 = debugPrint;

test.call('test');

// expect_lint: avoid_using_debug_print
final test3 = debugPrint('');

someOtherFunction();
}

void someOtherFunction() {
print('iii');
}
2 changes: 1 addition & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: solid_lints
description:
Lints for Dart and Flutter based on software industry standards and best
practices.
version: 0.1.4
version: 0.1.5
homepage: https://github.com/solid-software/solid_lints/
documentation: https://solid-software.github.io/solid_lints/docs/intro
topics: [lints, linter, lint, analysis, analyzer]
Expand Down

0 comments on commit b6d5d69

Please sign in to comment.