Skip to content

Commit b6d5d69

Browse files
Avoid debug print (#134)
* New lint: avoid_using_debug_print * udate changelog * fix PR issues * version up, comment for function reference * fix PR
1 parent 54d3a54 commit b6d5d69

File tree

10 files changed

+249
-1
lines changed

10 files changed

+249
-1
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## 0.1.5
2+
3+
- Add `avoid_using_debug_print` rule
4+
15
## 0.1.4
26

37
- Removed deprecated lints:

example/analysis_options.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ custom_lint:
1919
- avoid_unnecessary_setstate
2020
- double_literal_format
2121
- avoid_unnecessary_type_assertions
22+
- avoid_using_debug_print
2223
- avoid_using_api:
2324
severity: info
2425
entries:

lib/analysis_options.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ custom_lint:
5050
- avoid_unnecessary_type_casts
5151
- avoid_unrelated_type_assertions
5252
- avoid_unused_parameters
53+
- avoid_using_debug_print
5354

5455
- cyclomatic_complexity:
5556
max_complexity: 10

lib/solid_lints.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import 'package:solid_lints/src/lints/avoid_unnecessary_type_casts/avoid_unneces
1111
import 'package:solid_lints/src/lints/avoid_unrelated_type_assertions/avoid_unrelated_type_assertions_rule.dart';
1212
import 'package:solid_lints/src/lints/avoid_unused_parameters/avoid_unused_parameters_rule.dart';
1313
import 'package:solid_lints/src/lints/avoid_using_api/avoid_using_api_rule.dart';
14+
import 'package:solid_lints/src/lints/avoid_using_debug_print/avoid_using_debug_print_rule.dart';
1415
import 'package:solid_lints/src/lints/cyclomatic_complexity/cyclomatic_complexity_metric.dart';
1516
import 'package:solid_lints/src/lints/double_literal_format/double_literal_format_rule.dart';
1617
import 'package:solid_lints/src/lints/function_lines_of_code/function_lines_of_code_metric.dart';
@@ -59,6 +60,7 @@ class _SolidLints extends PluginBase {
5960
PreferLastRule.createRule(configs),
6061
PreferMatchFileNameRule.createRule(configs),
6162
ProperSuperCallsRule.createRule(configs),
63+
AvoidUsingDebugPrint.createRule(configs),
6264
];
6365

6466
// Return only enabled rules
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
import 'package:analyzer/dart/ast/ast.dart';
2+
import 'package:analyzer/dart/ast/syntactic_entity.dart';
3+
import 'package:analyzer/error/listener.dart';
4+
import 'package:custom_lint_builder/custom_lint_builder.dart';
5+
import 'package:solid_lints/src/lints/avoid_using_debug_print/models/avoid_using_debug_print_func_model.dart';
6+
import 'package:solid_lints/src/models/rule_config.dart';
7+
import 'package:solid_lints/src/models/solid_lint_rule.dart';
8+
9+
/// A `avoid_using_debug_print` rule which forbids calling or referencing
10+
/// debugPrint function from flutter/foundation.
11+
///
12+
/// ### Example
13+
///
14+
/// #### BAD:
15+
///
16+
/// ```dart
17+
/// debugPrint(''); // LINT
18+
/// var ref = debugPrint; // LINT
19+
/// var ref2;
20+
/// ref2 = debugPrint; // LINT
21+
/// ```
22+
///
23+
/// #### GOOD:
24+
///
25+
/// ```dart
26+
/// log('');
27+
/// ```
28+
class AvoidUsingDebugPrint extends SolidLintRule {
29+
/// The [LintCode] of this lint rule that represents
30+
/// the error when debugPrint is called
31+
static const lintName = 'avoid_using_debug_print';
32+
33+
AvoidUsingDebugPrint._(super.config);
34+
35+
/// Creates a new instance of [AvoidUsingDebugPrint]
36+
/// based on the lint configuration.
37+
factory AvoidUsingDebugPrint.createRule(CustomLintConfigs configs) {
38+
final rule = RuleConfig(
39+
configs: configs,
40+
name: lintName,
41+
problemMessage: (_) => "Avoid using 'debugPrint'",
42+
);
43+
44+
return AvoidUsingDebugPrint._(rule);
45+
}
46+
47+
@override
48+
void run(
49+
CustomLintResolver resolver,
50+
ErrorReporter reporter,
51+
CustomLintContext context,
52+
) {
53+
context.registry.addFunctionExpressionInvocation(
54+
(node) {
55+
final func = node.function;
56+
if (func is! Identifier) {
57+
return;
58+
}
59+
_checkIdentifier(
60+
identifier: func,
61+
node: node,
62+
reporter: reporter,
63+
);
64+
},
65+
);
66+
67+
// addFunctionReference does not get triggered.
68+
// addVariableDeclaration and addAssignmentExpression
69+
// are used as a workaround for simple cases
70+
71+
context.registry.addVariableDeclaration((node) {
72+
_handleVariableAssignmentDeclaration(
73+
node: node,
74+
reporter: reporter,
75+
);
76+
});
77+
78+
context.registry.addAssignmentExpression((node) {
79+
_handleVariableAssignmentDeclaration(
80+
node: node,
81+
reporter: reporter,
82+
);
83+
});
84+
}
85+
86+
/// Checks whether the function identifier satisfies conditions
87+
void _checkIdentifier({
88+
required Identifier identifier,
89+
required AstNode node,
90+
required ErrorReporter reporter,
91+
}) {
92+
final funcModel = AvoidUsingDebugPrintFuncModel.parseExpression(identifier);
93+
94+
if (funcModel.hasSameName && funcModel.hasTheSameSource) {
95+
reporter.reportErrorForNode(code, node);
96+
}
97+
}
98+
99+
/// Returns null if doesnt have right operand
100+
SyntacticEntity? _getRightOperand(List<SyntacticEntity> entities) {
101+
/// Example var t = 15; 15 is in 3d position
102+
if (entities.length < 3) {
103+
return null;
104+
}
105+
return entities[2];
106+
}
107+
108+
/// Handles variable assignment and declaration
109+
void _handleVariableAssignmentDeclaration({
110+
required AstNode node,
111+
required ErrorReporter reporter,
112+
}) {
113+
final rightOperand = _getRightOperand(node.childEntities.toList());
114+
115+
if (rightOperand == null || rightOperand is! Identifier) {
116+
return;
117+
}
118+
119+
_checkIdentifier(
120+
identifier: rightOperand,
121+
node: node,
122+
reporter: reporter,
123+
);
124+
}
125+
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import 'package:analyzer/dart/ast/ast.dart';
2+
3+
/// A class used to parse function expression
4+
class AvoidUsingDebugPrintFuncModel {
5+
/// Function name
6+
final String name;
7+
8+
/// Function's source path
9+
final String sourcePath;
10+
11+
/// A class used to parse function expression
12+
const AvoidUsingDebugPrintFuncModel({
13+
required this.name,
14+
required this.sourcePath,
15+
});
16+
17+
/// A constructor that parses identifier into [name] and [sourcePath]
18+
factory AvoidUsingDebugPrintFuncModel.parseExpression(
19+
Identifier identifier,
20+
) {
21+
switch (identifier) {
22+
case PrefixedIdentifier():
23+
final prefix = identifier.prefix.name;
24+
return AvoidUsingDebugPrintFuncModel(
25+
name: identifier.name.replaceAll('$prefix.', ''),
26+
sourcePath:
27+
identifier.staticElement?.librarySource?.uri.toString() ?? '',
28+
);
29+
case SimpleIdentifier():
30+
return AvoidUsingDebugPrintFuncModel(
31+
name: identifier.name,
32+
sourcePath:
33+
identifier.staticElement?.librarySource?.uri.toString() ?? '',
34+
);
35+
default:
36+
return AvoidUsingDebugPrintFuncModel._empty();
37+
}
38+
}
39+
40+
factory AvoidUsingDebugPrintFuncModel._empty() {
41+
return const AvoidUsingDebugPrintFuncModel(
42+
name: '',
43+
sourcePath: '',
44+
);
45+
}
46+
47+
static const String _printPath = 'package:flutter/src/foundation/print.dart';
48+
49+
static const String _debugPrint = 'debugPrint';
50+
51+
/// Ehether the function has the same source library as debugPrint func
52+
bool get hasTheSameSource => _printPath == sourcePath;
53+
54+
/// Ehether the function has the same name as debugPrint
55+
bool get hasSameName => _debugPrint == name;
56+
57+
@override
58+
String toString() {
59+
return '$name, $sourcePath';
60+
}
61+
}

lint_test/analysis_options.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ custom_lint:
2727
- newline_before_return
2828
- no_empty_block
2929
- no_equal_then_else
30+
- avoid_using_debug_print
3031
- member_ordering:
3132
alphabetize: true
3233
order:
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// ignore_for_file: unused_local_variable
2+
3+
import 'package:flutter/foundation.dart' as f;
4+
5+
/// Test the avoid_using_debug_print
6+
void avoidDebugPrintTest() {
7+
// expect_lint: avoid_using_debug_print
8+
f.debugPrint('');
9+
10+
// expect_lint: avoid_using_debug_print
11+
final test = f.debugPrint;
12+
13+
test('test');
14+
15+
// expect_lint: avoid_using_debug_print
16+
final test2 = f.debugPrint('');
17+
18+
debugPrint();
19+
20+
debugPrint;
21+
}
22+
23+
void debugPrint() {
24+
return;
25+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// ignore_for_file: unused_local_variable
2+
3+
import 'package:flutter/foundation.dart';
4+
5+
/// Test the avoid_using_debug_print
6+
void avoidDebugPrintTest() {
7+
// expect_lint: avoid_using_debug_print
8+
debugPrint('');
9+
10+
// expect_lint: avoid_using_debug_print
11+
final test = debugPrint;
12+
13+
var test2;
14+
15+
// expect_lint: avoid_using_debug_print
16+
test2 = debugPrint;
17+
18+
test.call('test');
19+
20+
// expect_lint: avoid_using_debug_print
21+
final test3 = debugPrint('');
22+
23+
someOtherFunction();
24+
}
25+
26+
void someOtherFunction() {
27+
print('iii');
28+
}

pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ name: solid_lints
22
description:
33
Lints for Dart and Flutter based on software industry standards and best
44
practices.
5-
version: 0.1.4
5+
version: 0.1.5
66
homepage: https://github.com/solid-software/solid_lints/
77
documentation: https://solid-software.github.io/solid_lints/docs/intro
88
topics: [lints, linter, lint, analysis, analyzer]

0 commit comments

Comments
 (0)