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

Commit dc3c944

Browse files
authored
feat: add support for discovering analysis options file from contex (#396)
1 parent e3843a5 commit dc3c944

26 files changed

+326
-259
lines changed

CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Changelog
22

3+
## Unreleased
4+
5+
* Add support for analysis options auto discovery.
6+
37
## 4.0.1
48

59
* Improve static code diagnostic `always-remove-listener`.

analysis_options.yaml

+11-3
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ include: package:lints/recommended.yaml
22

33
analyzer:
44
exclude:
5-
- test/resources/**
6-
- test/resources/unused_files_analyzer/generated/**/**
5+
- test/resources/*
6+
- test/resources/unused_files_analyzer/**
77
- test/**/examples/**
88
plugins:
99
- dart_code_metrics
@@ -20,7 +20,15 @@ dart_code_metrics:
2020
maximum-nesting: 5
2121
number-of-parameters: 4
2222
metrics-exclude:
23-
- test/**
23+
- test/analyzer_plugin/**
24+
- test/analyzers/**
25+
- test/cli/**
26+
- test/config_builder/**
27+
- test/helpers/**
28+
- test/reporters/**
29+
- test/utils/**
30+
- test/resources/*
31+
- test/*
2432
rules:
2533
- avoid-unused-parameters
2634
- binary-expression-operand-order

example/example.dart

+2-5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import 'dart:io';
22

3-
import 'package:dart_code_metrics/config.dart';
43
import 'package:dart_code_metrics/lint_analyzer.dart';
54

65
Future<void> main() async {
@@ -11,7 +10,7 @@ Future<void> main() async {
1110
const rootFolder = 'projectRoot';
1211

1312
// First of all config has to be created for a checker
14-
const config = Config(
13+
const config = LintConfig(
1514
excludePatterns: ['test/resources/**'],
1615
excludeForMetricsPatterns: ['test/**'],
1716
metrics: {
@@ -25,12 +24,10 @@ Future<void> main() async {
2524
antiPatterns: {'long-method': {}},
2625
);
2726

28-
final lintConfig = ConfigBuilder.getLintConfig(config, rootFolder);
29-
3027
const analyzer = LintAnalyzer();
3128

3229
final result =
33-
await analyzer.runCliAnalysis(foldersToAnalyze, rootFolder, lintConfig);
30+
await analyzer.runCliAnalysis(foldersToAnalyze, rootFolder, config);
3431

3532
// Now runner.results() contains some insights about analyzed code. Let's report it!
3633
// For a simple example we would report results to terminal

lib/config.dart

-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,2 @@
11
export 'package:dart_code_metrics/src/config_builder/config_builder.dart';
22
export 'package:dart_code_metrics/src/config_builder/models/analysis_options.dart';
3-
export 'package:dart_code_metrics/src/config_builder/models/config.dart';

lib/src/analyzer_plugin/analyzer_plugin.dart

+5-5
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ import 'package:analyzer/src/dart/analysis/driver_based_analysis_context.dart';
1313
import 'package:analyzer_plugin/plugin/plugin.dart';
1414
import 'package:analyzer_plugin/protocol/protocol_generated.dart' as plugin;
1515

16+
import '../analyzers/lint_analyzer/lint_analysis_config.dart';
1617
import '../analyzers/lint_analyzer/lint_analyzer.dart';
17-
import '../analyzers/lint_analyzer/lint_config.dart';
1818
import '../analyzers/lint_analyzer/metrics/metrics_list/cyclomatic_complexity/cyclomatic_complexity_metric.dart';
1919
import '../analyzers/lint_analyzer/metrics/metrics_list/number_of_parameters_metric.dart';
2020
import '../config_builder/config_builder.dart';
@@ -25,7 +25,7 @@ import 'analyzer_plugin_utils.dart';
2525
class MetricsAnalyzerPlugin extends ServerPlugin {
2626
static const _analyzer = LintAnalyzer();
2727

28-
final _configs = <AnalysisDriverGeneric, LintConfig>{};
28+
final _configs = <AnalysisDriverGeneric, LintAnalysisConfig>{};
2929

3030
var _filesFromSetPriorityFilesRequest = <String>[];
3131

@@ -236,14 +236,14 @@ class MetricsAnalyzerPlugin extends ServerPlugin {
236236
return result;
237237
}
238238

239-
LintConfig? _createConfig(AnalysisDriver driver, String rootPath) {
239+
LintAnalysisConfig? _createConfig(AnalysisDriver driver, String rootPath) {
240240
final file = driver.analysisContext?.contextRoot.optionsFile;
241241
if (file != null && file.exists) {
242242
final options = AnalysisOptions(yamlMapToDartMap(
243243
AnalysisOptionsProvider(driver.sourceFactory).getOptionsFromFile(file),
244244
));
245-
final config = ConfigBuilder.getConfig(options);
246-
final lintConfig = ConfigBuilder.getLintConfig(
245+
final config = ConfigBuilder.getLintConfigFromOptions(options);
246+
final lintConfig = ConfigBuilder.getLintAnalysisConfig(
247247
config,
248248
rootPath,
249249
classMetrics: const [],

lib/src/analyzer_plugin/analyzer_plugin_utils.dart

+2-2
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import 'package:analyzer_plugin/protocol/protocol_common.dart' as plugin;
33
import 'package:analyzer_plugin/protocol/protocol_generated.dart' as plugin;
44
import 'package:source_span/source_span.dart';
55

6-
import '../analyzers/lint_analyzer/lint_config.dart';
6+
import '../analyzers/lint_analyzer/lint_analysis_config.dart';
77
import '../analyzers/lint_analyzer/models/issue.dart';
88
import '../analyzers/lint_analyzer/models/severity.dart';
99
import '../config_builder/models/deprecated_option.dart';
@@ -102,7 +102,7 @@ plugin.AnalysisErrorFixes metricReportToAnalysisErrorFixes(
102102
));
103103

104104
Iterable<plugin.AnalysisErrorFixes> checkConfigDeprecatedOptions(
105-
LintConfig config,
105+
LintAnalysisConfig config,
106106
Iterable<DeprecatedOption> deprecatedOptions,
107107
String analysisOptionPath,
108108
) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import 'package:glob/glob.dart';
2+
import 'package:meta/meta.dart';
3+
4+
import 'anti_patterns/models/obsolete_pattern.dart';
5+
import 'metrics/models/metric.dart';
6+
import 'rules/models/rule.dart';
7+
8+
/// Represents converted lint config which contains parsed entities.
9+
10+
@immutable
11+
class LintAnalysisConfig {
12+
final Iterable<Glob> globalExcludes;
13+
final Iterable<Rule> codeRules;
14+
final Iterable<ObsoletePattern> antiPatterns;
15+
final Iterable<Metric> classesMetrics;
16+
final Iterable<Metric> methodsMetrics;
17+
final Iterable<Glob> metricsExcludes;
18+
final Map<String, Object> metricsConfig;
19+
20+
const LintAnalysisConfig(
21+
this.globalExcludes,
22+
this.codeRules,
23+
this.antiPatterns,
24+
this.classesMetrics,
25+
this.methodsMetrics,
26+
this.metricsExcludes,
27+
this.metricsConfig,
28+
);
29+
}

lib/src/analyzers/lint_analyzer/lint_analyzer.dart

+26-26
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,15 @@ import 'package:analyzer/dart/analysis/analysis_context_collection.dart';
55
import 'package:analyzer/dart/analysis/results.dart';
66
import 'package:analyzer/dart/ast/ast.dart';
77
import 'package:analyzer/file_system/physical_file_system.dart';
8-
import 'package:file/local.dart';
9-
import 'package:glob/glob.dart';
108
import 'package:path/path.dart';
119

12-
import '../../config_builder/models/config.dart';
10+
import '../../config_builder/config_builder.dart';
11+
import '../../config_builder/models/analysis_options.dart';
1312
import '../../reporters/models/reporter.dart';
1413
import '../../utils/exclude_utils.dart';
14+
import '../../utils/file_utils.dart';
1515
import '../../utils/node_utils.dart';
16+
import 'lint_analysis_config.dart';
1617
import 'lint_config.dart';
1718
import 'metrics/halstead_volume_ast_visitor.dart';
1819
import 'metrics/metric_utils.dart';
@@ -38,7 +39,7 @@ class LintAnalyzer {
3839
const LintAnalyzer();
3940

4041
Reporter? getReporter({
41-
required Config config,
42+
required LintConfig config,
4243
required String name,
4344
required IOSink output,
4445
required String reportFolder,
@@ -52,7 +53,7 @@ class LintAnalyzer {
5253

5354
LintFileReport? runPluginAnalysis(
5455
ResolvedUnitResult result,
55-
LintConfig config,
56+
LintAnalysisConfig config,
5657
String rootFolder,
5758
) {
5859
if (!isExcluded(result.path, config.globalExcludes)) {
@@ -78,24 +79,23 @@ class LintAnalyzer {
7879
resourceProvider: PhysicalResourceProvider.INSTANCE,
7980
);
8081

81-
final filePaths = folders
82-
.expand((directory) => Glob('$directory/**.dart')
83-
.listFileSystemSync(
84-
const LocalFileSystem(),
85-
root: rootFolder,
86-
followLinks: false,
87-
)
88-
.whereType<File>()
89-
.where((entity) => !isExcluded(
90-
relative(entity.path, from: rootFolder),
91-
config.globalExcludes,
92-
))
93-
.map((entity) => entity.path))
94-
.toSet();
95-
9682
final analyzerResult = <LintFileReport>[];
9783

9884
for (final context in collection.contexts) {
85+
final analysisOptions = await analysisOptionsFromContext(context) ??
86+
await analysisOptionsFromFilePath(rootFolder);
87+
88+
final contextConfig =
89+
ConfigBuilder.getLintConfigFromOptions(analysisOptions).merge(config);
90+
final lintAnalysisConfig =
91+
ConfigBuilder.getLintAnalysisConfig(contextConfig, rootFolder);
92+
93+
final filePaths = extractDartFilesFromFolders(
94+
folders,
95+
rootFolder,
96+
lintAnalysisConfig.globalExcludes,
97+
);
98+
9999
final analyzedFiles =
100100
filePaths.intersection(context.contextRoot.analyzedFiles().toSet());
101101

@@ -104,7 +104,7 @@ class LintAnalyzer {
104104
if (unit is ResolvedUnitResult) {
105105
final result = _runAnalysisForFile(
106106
unit,
107-
config,
107+
lintAnalysisConfig,
108108
rootFolder,
109109
filePath: filePath,
110110
);
@@ -121,7 +121,7 @@ class LintAnalyzer {
121121

122122
LintFileReport? _runAnalysisForFile(
123123
ResolvedUnitResult result,
124-
LintConfig config,
124+
LintAnalysisConfig config,
125125
String rootFolder, {
126126
String? filePath,
127127
}) {
@@ -215,7 +215,7 @@ class LintAnalyzer {
215215
Iterable<Issue> _checkOnCodeIssues(
216216
Suppression ignores,
217217
InternalResolvedUnitResult source,
218-
LintConfig config,
218+
LintAnalysisConfig config,
219219
String filePath,
220220
String? rootFolder,
221221
) =>
@@ -240,7 +240,7 @@ class LintAnalyzer {
240240
Suppression ignores,
241241
InternalResolvedUnitResult source,
242242
Iterable<ScopedFunctionDeclaration> functions,
243-
LintConfig config,
243+
LintAnalysisConfig config,
244244
) =>
245245
config.antiPatterns
246246
.where((pattern) => !ignores.isSuppressed(pattern.id))
@@ -255,7 +255,7 @@ class LintAnalyzer {
255255
Map<ScopedClassDeclaration, Report> _checkClassMetrics(
256256
ScopeVisitor visitor,
257257
InternalResolvedUnitResult source,
258-
LintConfig config,
258+
LintAnalysisConfig config,
259259
) {
260260
final classRecords = <ScopedClassDeclaration, Report>{};
261261

@@ -292,7 +292,7 @@ class LintAnalyzer {
292292
Map<ScopedFunctionDeclaration, Report> _checkFunctionMetrics(
293293
ScopeVisitor visitor,
294294
InternalResolvedUnitResult source,
295-
LintConfig config,
295+
LintAnalysisConfig config,
296296
) {
297297
final functionRecords = <ScopedFunctionDeclaration, Report>{};
298298

Original file line numberDiff line numberDiff line change
@@ -1,27 +1,63 @@
1-
import 'package:glob/glob.dart';
21
import 'package:meta/meta.dart';
32

4-
import 'anti_patterns/models/obsolete_pattern.dart';
5-
import 'metrics/models/metric.dart';
6-
import 'rules/models/rule.dart';
3+
import '../../cli/models/parsed_arguments.dart';
4+
import '../../config_builder/analysis_options_utils.dart';
5+
import '../../config_builder/models/analysis_options.dart';
6+
import 'metrics/metrics_factory.dart';
77

8+
/// Represents raw lint config which can be merged with other raw configs.
89
@immutable
910
class LintConfig {
10-
final Iterable<Glob> globalExcludes;
11-
final Iterable<Rule> codeRules;
12-
final Iterable<ObsoletePattern> antiPatterns;
13-
final Iterable<Metric> classesMetrics;
14-
final Iterable<Metric> methodsMetrics;
15-
final Iterable<Glob> metricsExcludes;
16-
final Map<String, Object> metricsConfig;
11+
final Iterable<String> excludePatterns;
12+
final Iterable<String> excludeForMetricsPatterns;
13+
final Map<String, Object> metrics;
14+
final Map<String, Map<String, Object>> rules;
15+
final Map<String, Map<String, Object>> antiPatterns;
1716

18-
const LintConfig(
19-
this.globalExcludes,
20-
this.codeRules,
21-
this.antiPatterns,
22-
this.classesMetrics,
23-
this.methodsMetrics,
24-
this.metricsExcludes,
25-
this.metricsConfig,
26-
);
17+
const LintConfig({
18+
required this.excludePatterns,
19+
required this.excludeForMetricsPatterns,
20+
required this.metrics,
21+
required this.rules,
22+
required this.antiPatterns,
23+
});
24+
25+
factory LintConfig.fromAnalysisOptions(AnalysisOptions options) {
26+
const _rootKey = 'dart_code_metrics';
27+
28+
return LintConfig(
29+
excludePatterns: options.readIterableOfString(['analyzer', 'exclude']),
30+
excludeForMetricsPatterns:
31+
options.readIterableOfString([_rootKey, 'metrics-exclude']),
32+
metrics: options.readMap([_rootKey, 'metrics']),
33+
rules: options.readMapOfMap([_rootKey, 'rules']),
34+
antiPatterns: options.readMapOfMap([_rootKey, 'anti-patterns']),
35+
);
36+
}
37+
38+
factory LintConfig.fromArgs(ParsedArguments arguments) => LintConfig(
39+
excludePatterns: [arguments.excludePath],
40+
excludeForMetricsPatterns: const [],
41+
metrics: {
42+
for (final metric in getMetrics(config: {}))
43+
if (arguments.metricsConfig.containsKey(metric.id))
44+
metric.id: arguments.metricsConfig[metric.id]!,
45+
},
46+
rules: const {},
47+
antiPatterns: const {},
48+
);
49+
50+
LintConfig merge(LintConfig overrides) => LintConfig(
51+
excludePatterns: {...excludePatterns, ...overrides.excludePatterns},
52+
excludeForMetricsPatterns: {
53+
...excludeForMetricsPatterns,
54+
...overrides.excludeForMetricsPatterns,
55+
},
56+
metrics: mergeMaps(defaults: metrics, overrides: overrides.metrics),
57+
rules: mergeMaps(defaults: rules, overrides: overrides.rules)
58+
.cast<String, Map<String, Object>>(),
59+
antiPatterns:
60+
mergeMaps(defaults: antiPatterns, overrides: overrides.antiPatterns)
61+
.cast<String, Map<String, Object>>(),
62+
);
2763
}

0 commit comments

Comments
 (0)