Skip to content

Commit

Permalink
Merge pull request #152 from byshy/fix_command
Browse files Browse the repository at this point in the history
Add fix command
  • Loading branch information
santitigaga authored Nov 7, 2024
2 parents 6de4f9d + e21c58a commit 42bbf82
Show file tree
Hide file tree
Showing 8 changed files with 428 additions and 74 deletions.
200 changes: 146 additions & 54 deletions lib/src/analyzers/lint_analyzer/lint_analyzer.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import 'dart:io';

import 'package:analyzer/dart/analysis/analysis_context.dart';
import 'package:analyzer/dart/analysis/analysis_context_collection.dart';
import 'package:analyzer/dart/analysis/results.dart';
import 'package:path/path.dart';

Expand Down Expand Up @@ -77,44 +78,22 @@ class LintAnalyzer {
LintConfig config, {
String? sdkPath,
}) async {
final collection =
createAnalysisContextCollection(folders, rootFolder, sdkPath);

final collection = createAnalysisContextCollection(folders, rootFolder, sdkPath);
final analyzerResult = <LintFileReport>[];

for (final context in collection.contexts) {
final lintAnalysisConfig =
_getAnalysisConfig(context, rootFolder, config);

final report = LintAnalysisOptionsValidator.validateOptions(
lintAnalysisConfig,
final (lintAnalysisConfig, analyzedFiles, report) = prepareLintAnalysis(
context,
folders,
rootFolder,
config,
collection,
);

if (report != null) {
analyzerResult.add(report);
}

if (config.shouldPrintConfig) {
_logger?.printConfig(lintAnalysisConfig.toJson());
}

final filePaths = getFilePaths(
folders,
context,
rootFolder,
lintAnalysisConfig.globalExcludes,
);

final analyzedFiles =
filePaths.intersection(context.contextRoot.analyzedFiles().toSet());

final contextsLength = collection.contexts.length;
final filesLength = analyzedFiles.length;
final updateMessage = contextsLength == 1
? 'Analyzing $filesLength file(s)'
: 'Analyzing ${collection.contexts.indexOf(context) + 1}/$contextsLength contexts with $filesLength file(s)';
_logger?.progress.update(updateMessage);

for (final filePath in analyzedFiles) {
_logger?.infoVerbose('Analyzing $filePath');

Expand All @@ -131,7 +110,6 @@ class LintAnalyzer {
_logger?.infoVerbose(
'Analysis result: found ${result.issues.length} issues',
);

analyzerResult.add(result);
}
}
Expand All @@ -141,6 +119,130 @@ class LintAnalyzer {
return analyzerResult;
}

Future<void> runCliFix(
Iterable<String> folders,
String rootFolder,
LintConfig config, {
String? sdkPath,
}) async {
final collection = createAnalysisContextCollection(folders, rootFolder, sdkPath);

for (final context in collection.contexts) {
final (lintAnalysisConfig, analyzedFiles, _) = prepareLintAnalysis(
context,
folders,
rootFolder,
config,
collection,
);

for (final filePath in analyzedFiles) {
_logger?.infoVerbose('Fixing $filePath\n');

final unit = await context.currentSession.getResolvedUnit(filePath);
if (unit is ResolvedUnitResult) {
final (issuesNo, fixesNo) = _analyzeAndFixFile(
unit,
lintAnalysisConfig,
rootFolder,
filePath: filePath,
);

if (issuesNo != 0) {
_logger?.write(
'\nFix result: fixed $fixesNo out of $issuesNo issues\n',
);
} else {
_logger?.infoVerbose(
'No issues found',
);
}
}
}
}

return;
}

(
LintAnalysisConfig lintAnalysisConfig,
Set<String> analyzedFiles,
LintFileReport? report,
) prepareLintAnalysis(
AnalysisContext context,
Iterable<String> folders,
String rootFolder,
LintConfig config,
AnalysisContextCollection collection,
) {
final lintAnalysisConfig = _getAnalysisConfig(context, rootFolder, config);
final report = LintAnalysisOptionsValidator.validateOptions(
lintAnalysisConfig,
rootFolder,
);

if (config.shouldPrintConfig) {
_logger?.printConfig(lintAnalysisConfig.toJson());
}

final filePaths = getFilePaths(
folders,
context,
rootFolder,
lintAnalysisConfig.globalExcludes,
);
final analyzedFiles = filePaths.intersection(context.contextRoot.analyzedFiles().toSet());

final contextsLength = collection.contexts.length;
final filesLength = analyzedFiles.length;
final updateMessage = contextsLength == 1
? 'Processing $filesLength file(s)'
: 'Processing ${collection.contexts.indexOf(context) + 1}/$contextsLength contexts with $filesLength file(s)';
_logger?.progress.update(updateMessage);

return (lintAnalysisConfig, analyzedFiles, report);
}

(int issuesNo, int fixesNo) _analyzeAndFixFile(
ResolvedUnitResult unit,
LintAnalysisConfig config,
String rootFolder, {
required String filePath,
}) {
final result = _analyzeFile(unit, config, rootFolder, filePath: filePath);

if (result == null || result.issues.isEmpty) {
return (0, 0);
}

final originalContent = StringBuffer(unit.content);
var fixedContent = originalContent.toString();
final fixedIssues = <Issue>[];

for (final issue in result.issues) {
final fix = issue.suggestion;

if (fix != null) {
fixedContent = fixedContent.replaceRange(
issue.location.start.offset,
issue.location.end.offset,
fix.replacement,
);

fixedIssues.add(issue);
}
}

_applyFixesToFile(fixedContent, filePath);

return (result.issues.length, fixedIssues.length);
}

Future<void> _applyFixesToFile(String fixedContent, String filePath) async {
final file = File(filePath);
await file.writeAsString(fixedContent);
}

Iterable<SummaryLintReportRecord<Object>> getSummary(
Iterable<LintFileReport> records,
) =>
Expand All @@ -164,14 +266,12 @@ class LintAnalyzer {
SummaryLintReportRecord<num>(
title: 'Average Cyclomatic Number per line of code',
value: averageCYCLO(records),
violations:
metricViolations(records, CyclomaticComplexityMetric.metricId),
violations: metricViolations(records, CyclomaticComplexityMetric.metricId),
),
SummaryLintReportRecord<int>(
title: 'Average Source Lines of Code per method',
value: averageSLOC(records),
violations:
metricViolations(records, SourceLinesOfCodeMetric.metricId),
violations: metricViolations(records, SourceLinesOfCodeMetric.metricId),
),
SummaryLintReportRecord<String>(
title: 'Total tech debt',
Expand All @@ -184,11 +284,9 @@ class LintAnalyzer {
String rootFolder,
LintConfig config,
) {
final analysisOptions = analysisOptionsFromContext(context) ??
analysisOptionsFromFilePath(rootFolder, context);
final analysisOptions = analysisOptionsFromContext(context) ?? analysisOptionsFromFilePath(rootFolder, context);

final contextConfig =
ConfigBuilder.getLintConfigFromOptions(analysisOptions).merge(config);
final contextConfig = ConfigBuilder.getLintConfigFromOptions(analysisOptions).merge(config);

return ConfigBuilder.getLintAnalysisConfig(
contextConfig,
Expand Down Expand Up @@ -231,8 +329,7 @@ class LintAnalyzer {

final classMetrics = _checkClassMetrics(visitor, internalResult, config);
final fileMetrics = _checkFileMetrics(visitor, internalResult, config);
final functionMetrics =
_checkFunctionMetrics(visitor, internalResult, config);
final functionMetrics = _checkFunctionMetrics(visitor, internalResult, config);
final antiPatterns = _checkOnAntiPatterns(
ignores,
internalResult,
Expand Down Expand Up @@ -280,11 +377,10 @@ class LintAnalyzer {
createAbsolutePatterns(rule.excludes, config.rootFolder),
))
.expand(
(rule) =>
rule.check(source).where((issue) => !ignores.isSuppressedAt(
issue.ruleId,
issue.location.start.line,
)),
(rule) => rule.check(source).where((issue) => !ignores.isSuppressedAt(
issue.ruleId,
issue.location.start.line,
)),
)
.toList();

Expand All @@ -302,12 +398,10 @@ class LintAnalyzer {
source.path,
createAbsolutePatterns(pattern.excludes, config.rootFolder),
))
.expand((pattern) => pattern
.check(source, classMetrics, functionMetrics)
.where((issue) => !ignores.isSuppressedAt(
issue.ruleId,
issue.location.start.line,
)))
.expand((pattern) => pattern.check(source, classMetrics, functionMetrics).where((issue) => !ignores.isSuppressedAt(
issue.ruleId,
issue.location.start.line,
)))
.toList();

Map<ScopedClassDeclaration, Report> _checkClassMetrics(
Expand Down Expand Up @@ -424,7 +518,5 @@ class LintAnalyzer {
}

bool _isSupported(FileResult result) =>
result.path.endsWith('.dart') &&
!result.path.endsWith('.g.dart') &&
!result.path.endsWith('.freezed.dart');
result.path.endsWith('.dart') && !result.path.endsWith('.g.dart') && !result.path.endsWith('.freezed.dart');
}
2 changes: 2 additions & 0 deletions lib/src/cli/cli_runner.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import 'commands/check_unnecessary_nullable_command.dart';
import 'commands/check_unused_code_command.dart';
import 'commands/check_unused_files_command.dart';
import 'commands/check_unused_l10n_command.dart';
import 'commands/fix_lints_command.dart';
import 'models/flag_names.dart';

/// Represents a cli runner responsible
Expand All @@ -29,6 +30,7 @@ class CliRunner extends CommandRunner<void> {
CheckUnusedL10nCommand(_logger),
CheckUnusedCodeCommand(_logger),
CheckUnnecessaryNullableCommand(_logger),
FixCommand(_logger),
].forEach(addCommand);

_usesVersionOption();
Expand Down
Loading

0 comments on commit 42bbf82

Please sign in to comment.