Skip to content
This repository was archived by the owner on Jul 16, 2023. It is now read-only.

Commit e209a2d

Browse files
authored
feat: add logger and progress indication (#1014)
* feat: add logger and progress indication * chore: update changelog * chore: restore cli runner api * feat: support verbose logger * test: update tests * test: update tests * feat: add new version check * chore: fix analyzer * fix: add override
1 parent 29e09ca commit e209a2d

31 files changed

+563
-162
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
## Unreleased
44

5+
* feat: add logger and progress indication.
56
* chore: changed min `SDK` version to `2.17.1`.
67

78
## 4.19.1

lib/src/analyzers/lint_analyzer/lint_analyzer.dart

Lines changed: 71 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import 'package:path/path.dart';
66

77
import '../../config_builder/config_builder.dart';
88
import '../../config_builder/models/analysis_options.dart';
9+
import '../../logger/logger.dart';
910
import '../../reporters/models/file_report.dart';
1011
import '../../reporters/models/reporter.dart';
1112
import '../../utils/analyzer_utils.dart';
@@ -31,7 +32,9 @@ import 'utils/report_utils.dart';
3132

3233
/// The analyzer responsible for collecting lint reports.
3334
class LintAnalyzer {
34-
const LintAnalyzer();
35+
final Logger? _logger;
36+
37+
const LintAnalyzer([this._logger]);
3538

3639
/// Returns a reporter for the given [name]. Use the reporter
3740
/// to convert analysis reports to console, JSON or other supported format.
@@ -93,7 +96,17 @@ class LintAnalyzer {
9396

9497
final analyzedFiles =
9598
filePaths.intersection(context.contextRoot.analyzedFiles().toSet());
99+
100+
final contextsLength = collection.contexts.length;
101+
final filesLength = analyzedFiles.length;
102+
final updateMessage = contextsLength == 1
103+
? 'Analyzing $filesLength file(s)'
104+
: 'Analyzing ${collection.contexts.indexOf(context) + 1}/$contextsLength contexts with $filesLength file(s)';
105+
_logger?.progress.update(updateMessage);
106+
96107
for (final filePath in analyzedFiles) {
108+
_logger?.infoVerbose('Analyzing $filePath');
109+
97110
final unit = await context.currentSession.getResolvedUnit(filePath);
98111
if (unit is ResolvedUnitResult) {
99112
final result = _analyzeFile(
@@ -104,6 +117,10 @@ class LintAnalyzer {
104117
);
105118

106119
if (result != null) {
120+
_logger?.infoVerbose(
121+
'Analysis result: found ${result.issues.length} issues',
122+
);
123+
107124
analyzerResult.add(result);
108125
}
109126
}
@@ -176,78 +193,60 @@ class LintAnalyzer {
176193
String rootFolder, {
177194
String? filePath,
178195
}) {
179-
if (filePath != null && _isSupported(result)) {
180-
final ignores = Suppression(result.content, result.lineInfo);
181-
final internalResult = InternalResolvedUnitResult(
182-
filePath,
183-
result.content,
184-
result.unit,
185-
result.lineInfo,
186-
);
187-
final relativePath = relative(filePath, from: rootFolder);
188-
189-
final issues = <Issue>[];
190-
if (!isExcluded(filePath, config.rulesExcludes)) {
191-
issues.addAll(
192-
_checkOnCodeIssues(
193-
ignores,
194-
internalResult,
195-
config,
196-
),
197-
);
198-
}
196+
if (filePath == null || !_isSupported(result)) {
197+
return null;
198+
}
199199

200-
if (!isExcluded(filePath, config.metricsExcludes)) {
201-
final visitor = ScopeVisitor();
202-
internalResult.unit.visitChildren(visitor);
203-
204-
final classMetrics =
205-
_checkClassMetrics(visitor, internalResult, config);
206-
207-
final fileMetrics = _checkFileMetrics(visitor, internalResult, config);
208-
209-
final functionMetrics =
210-
_checkFunctionMetrics(visitor, internalResult, config);
211-
212-
final antiPatterns = _checkOnAntiPatterns(
213-
ignores,
214-
internalResult,
215-
config,
216-
classMetrics,
217-
functionMetrics,
218-
);
219-
220-
return LintFileReport(
221-
path: filePath,
222-
relativePath: relativePath,
223-
file: fileMetrics,
224-
classes: Map.unmodifiable(classMetrics
225-
.map<String, Report>((key, value) => MapEntry(key.name, value))),
226-
functions: Map.unmodifiable(functionMetrics.map<String, Report>(
227-
(key, value) => MapEntry(key.fullName, value),
228-
)),
229-
issues: issues,
230-
antiPatternCases: antiPatterns,
231-
);
232-
}
200+
final ignores = Suppression(result.content, result.lineInfo);
201+
final internalResult = InternalResolvedUnitResult(
202+
filePath,
203+
result.content,
204+
result.unit,
205+
result.lineInfo,
206+
);
207+
final relativePath = relative(filePath, from: rootFolder);
208+
209+
final issues = <Issue>[];
210+
if (!isExcluded(filePath, config.rulesExcludes)) {
211+
issues.addAll(_checkOnCodeIssues(ignores, internalResult, config));
212+
}
213+
214+
if (!isExcluded(filePath, config.metricsExcludes)) {
215+
final visitor = ScopeVisitor();
216+
internalResult.unit.visitChildren(visitor);
217+
218+
final classMetrics = _checkClassMetrics(visitor, internalResult, config);
219+
final fileMetrics = _checkFileMetrics(visitor, internalResult, config);
220+
final functionMetrics =
221+
_checkFunctionMetrics(visitor, internalResult, config);
222+
final antiPatterns = _checkOnAntiPatterns(
223+
ignores,
224+
internalResult,
225+
config,
226+
classMetrics,
227+
functionMetrics,
228+
);
233229

234230
return LintFileReport(
235231
path: filePath,
236232
relativePath: relativePath,
237-
file: Report(
238-
location:
239-
nodeLocation(node: internalResult.unit, source: internalResult),
240-
metrics: const [],
241-
declaration: internalResult.unit,
242-
),
243-
classes: const {},
244-
functions: const {},
233+
file: fileMetrics,
234+
classes: Map.unmodifiable(classMetrics
235+
.map<String, Report>((key, value) => MapEntry(key.name, value))),
236+
functions: Map.unmodifiable(functionMetrics.map<String, Report>(
237+
(key, value) => MapEntry(key.fullName, value),
238+
)),
245239
issues: issues,
246-
antiPatternCases: const [],
240+
antiPatternCases: antiPatterns,
247241
);
248242
}
249243

250-
return null;
244+
return LintFileReport.onlyIssues(
245+
path: filePath,
246+
relativePath: relativePath,
247+
file: _getEmptyFileReport(internalResult),
248+
issues: issues,
249+
);
251250
}
252251

253252
Iterable<Issue> _checkOnCodeIssues(
@@ -368,6 +367,14 @@ class LintAnalyzer {
368367
);
369368
}
370369

370+
Report _getEmptyFileReport(InternalResolvedUnitResult internalResult) =>
371+
Report(
372+
location:
373+
nodeLocation(node: internalResult.unit, source: internalResult),
374+
metrics: const [],
375+
declaration: internalResult.unit,
376+
);
377+
371378
Map<ScopedFunctionDeclaration, Report> _checkFunctionMetrics(
372379
ScopeVisitor visitor,
373380
InternalResolvedUnitResult source,

lib/src/analyzers/lint_analyzer/models/lint_file_report.dart

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,4 +36,13 @@ class LintFileReport implements FileReport {
3636
required this.issues,
3737
required this.antiPatternCases,
3838
});
39+
40+
const LintFileReport.onlyIssues({
41+
required this.path,
42+
required this.relativePath,
43+
required this.file,
44+
required this.issues,
45+
}) : classes = const {},
46+
functions = const {},
47+
antiPatternCases = const {};
3948
}

lib/src/analyzers/unnecessary_nullable_analyzer/unnecessary_nullable_analyzer.dart

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import 'package:source_span/source_span.dart';
1515

1616
import '../../config_builder/config_builder.dart';
1717
import '../../config_builder/models/analysis_options.dart';
18+
import '../../logger/logger.dart';
1819
import '../../reporters/models/reporter.dart';
1920
import '../../utils/analyzer_utils.dart';
2021
import '../../utils/flutter_types_utils.dart';
@@ -33,7 +34,9 @@ import 'unnecessary_nullable_config.dart';
3334
class UnnecessaryNullableAnalyzer {
3435
static const _ignoreName = 'unnecessary-nullable';
3536

36-
const UnnecessaryNullableAnalyzer();
37+
final Logger? _logger;
38+
39+
const UnnecessaryNullableAnalyzer([this._logger]);
3740

3841
/// Returns a reporter for the given [name]. Use the reporter
3942
/// to convert analysis reports to console, JSON or other supported format.
@@ -75,22 +78,42 @@ class UnnecessaryNullableAnalyzer {
7578

7679
final analyzedFiles =
7780
filePaths.intersection(context.contextRoot.analyzedFiles().toSet());
81+
82+
final contextsLength = collection.contexts.length;
83+
final filesLength = analyzedFiles.length;
84+
final updateMessage = contextsLength == 1
85+
? 'Checking unnecessary nullable parameters for $filesLength file(s)'
86+
: 'Checking unnecessary nullable parameters for ${collection.contexts.indexOf(context) + 1}/$contextsLength contexts with $filesLength file(s)';
87+
_logger?.progress.update(updateMessage);
88+
7889
for (final filePath in analyzedFiles) {
90+
_logger?.infoVerbose('Analyzing $filePath');
91+
7992
final unit = await context.currentSession.getResolvedUnit(filePath);
8093

8194
final invocationsUsage = _analyzeInvocationsUsage(unit);
8295
if (invocationsUsage != null) {
96+
_logger?.infoVerbose(
97+
'Found invocations: ${invocationsUsage.elements.length}',
98+
);
99+
83100
invocationsUsages.merge(invocationsUsage);
84101
}
85102

86103
if (!unnecessaryNullableAnalysisConfig.analyzerExcludedPatterns
87104
.any((pattern) => pattern.matches(filePath))) {
105+
_logger
106+
?.infoVerbose('Found declarations: ${declarationsUsages.length}');
107+
88108
declarationsUsages[filePath] = _analyzeDeclarationsUsage(unit);
89109
}
90110
}
91111
}
92112

93113
if (!config.isMonorepo) {
114+
_logger?.infoVerbose(
115+
'Removing globally exported files with declarations from the analysis: ${invocationsUsages.exports.length}',
116+
);
94117
invocationsUsages.exports.forEach(declarationsUsages.remove);
95118
}
96119

lib/src/analyzers/unused_code_analyzer/unused_code_analyzer.dart

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import 'package:source_span/source_span.dart';
1010

1111
import '../../config_builder/config_builder.dart';
1212
import '../../config_builder/models/analysis_options.dart';
13+
import '../../logger/logger.dart';
1314
import '../../reporters/models/reporter.dart';
1415
import '../../utils/analyzer_utils.dart';
1516
import '../../utils/suppression.dart';
@@ -27,7 +28,9 @@ import 'used_code_visitor.dart';
2728
class UnusedCodeAnalyzer {
2829
static const _ignoreName = 'unused-code';
2930

30-
const UnusedCodeAnalyzer();
31+
final Logger? _logger;
32+
33+
const UnusedCodeAnalyzer([this._logger]);
3134

3235
/// Returns a reporter for the given [name]. Use the reporter
3336
/// to convert analysis reports to console, JSON or other supported format.
@@ -68,7 +71,17 @@ class UnusedCodeAnalyzer {
6871

6972
final analyzedFiles =
7073
filePaths.intersection(context.contextRoot.analyzedFiles().toSet());
74+
75+
final contextsLength = collection.contexts.length;
76+
final filesLength = analyzedFiles.length;
77+
final updateMessage = contextsLength == 1
78+
? 'Checking unused code for $filesLength file(s)'
79+
: 'Checking unused code for ${collection.contexts.indexOf(context) + 1}/$contextsLength contexts with $filesLength file(s)';
80+
_logger?.progress.update(updateMessage);
81+
7182
for (final filePath in analyzedFiles) {
83+
_logger?.infoVerbose('Analyzing $filePath');
84+
7285
final unit = await context.currentSession.getResolvedUnit(filePath);
7386

7487
final codeUsage = _analyzeFileCodeUsages(unit);
@@ -84,6 +97,9 @@ class UnusedCodeAnalyzer {
8497
}
8598

8699
if (!config.isMonorepo) {
100+
_logger?.infoVerbose(
101+
'Removing globally exported files with code usages from the analysis: ${codeUsages.exports.length}',
102+
);
87103
codeUsages.exports.forEach(publicCode.remove);
88104
}
89105

lib/src/analyzers/unused_files_analyzer/unused_files_analyzer.dart

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import 'package:path/path.dart';
66

77
import '../../config_builder/config_builder.dart';
88
import '../../config_builder/models/analysis_options.dart';
9+
import '../../logger/logger.dart';
910
import '../../reporters/models/reporter.dart';
1011
import '../../utils/analyzer_utils.dart';
1112
import '../../utils/suppression.dart';
@@ -20,7 +21,9 @@ import 'unused_files_visitor.dart';
2021
class UnusedFilesAnalyzer {
2122
static const _ignoreName = 'unused-files';
2223

23-
const UnusedFilesAnalyzer();
24+
final Logger? _logger;
25+
26+
const UnusedFilesAnalyzer([this._logger]);
2427

2528
/// Returns a reporter for the given [name]. Use the reporter
2629
/// to convert analysis reports to console, JSON or other supported format.
@@ -63,6 +66,8 @@ class UnusedFilesAnalyzer {
6366
final analyzedFiles =
6467
filePaths.intersection(context.contextRoot.analyzedFiles().toSet());
6568
for (final filePath in analyzedFiles) {
69+
_logger?.infoVerbose('Analyzing $filePath');
70+
6671
final unit = await context.currentSession.getResolvedUnit(filePath);
6772
unusedFiles.removeAll(_analyzeFile(filePath, unit, config.isMonorepo));
6873
}

lib/src/analyzers/unused_l10n_analyzer/unused_l10n_analyzer.dart

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import 'package:source_span/source_span.dart';
1313

1414
import '../../config_builder/config_builder.dart';
1515
import '../../config_builder/models/analysis_options.dart';
16+
import '../../logger/logger.dart';
1617
import '../../reporters/models/reporter.dart';
1718
import '../../utils/analyzer_utils.dart';
1819
import 'models/unused_l10n_file_report.dart';
@@ -25,7 +26,9 @@ import 'unused_l10n_visitor.dart';
2526

2627
/// The analyzer responsible for collecting unused localization reports.
2728
class UnusedL10nAnalyzer {
28-
const UnusedL10nAnalyzer();
29+
final Logger? _logger;
30+
31+
const UnusedL10nAnalyzer([this._logger]);
2932

3033
/// Returns a reporter for the given [name]. Use the reporter
3134
/// to convert analysis reports to console, JSON or other supported format.
@@ -67,6 +70,8 @@ class UnusedL10nAnalyzer {
6770
filePaths.intersection(context.contextRoot.analyzedFiles().toSet());
6871

6972
for (final filePath in analyzedFiles) {
73+
_logger?.infoVerbose('Analyzing $filePath');
74+
7075
final unit = await context.currentSession.getResolvedUnit(filePath);
7176

7277
_analyzeFile(unit, unusedLocalizationAnalysisConfig.classPattern)

0 commit comments

Comments
 (0)