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

Commit

Permalink
fix: resolve package with imported analysis options (#887)
Browse files Browse the repository at this point in the history
* fix: resolve package with imported analysis options

* chore: add comment
  • Loading branch information
dkrutskikh committed Jun 13, 2022
1 parent 9a1e419 commit 0c0f76f
Show file tree
Hide file tree
Showing 14 changed files with 97 additions and 33 deletions.
1 change: 1 addition & 0 deletions .vscode/settings.json
Expand Up @@ -30,6 +30,7 @@
"mocktail",
"negatable",
"nullsafety",
"posix",
"pubignore",
"pubspec",
"semver",
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -5,6 +5,7 @@
* test: added test case in [`prefer-const-border-radius`](https://dartcodemetrics.dev/docs/rules/flutter/prefer-const-border-radius) rule.
* chore: restrict `analyzer` version to `>=2.4.0 <4.2.0`.
* fix: improve context root included files calculation.
* fix: resolve package with imported analysis options.

## 4.15.2

Expand Down
1 change: 1 addition & 0 deletions dart_dependency_validator.yaml
@@ -1,3 +1,4 @@
ignore:
- analyzer
- intl
- test_lints
4 changes: 2 additions & 2 deletions lib/src/analyzers/lint_analyzer/lint_analyzer.dart
Expand Up @@ -81,8 +81,8 @@ class LintAnalyzer {
final analyzerResult = <LintFileReport>[];

for (final context in collection.contexts) {
final analysisOptions = await analysisOptionsFromContext(context) ??
await analysisOptionsFromFilePath(rootFolder);
final analysisOptions = analysisOptionsFromContext(context) ??
analysisOptionsFromFilePath(rootFolder, context);

final excludesRootFolder = analysisOptions.folderPath ?? rootFolder;

Expand Down
10 changes: 5 additions & 5 deletions lib/src/analyzers/unused_code_analyzer/unused_code_analyzer.dart
Expand Up @@ -55,7 +55,7 @@ class UnusedCodeAnalyzer {

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

final filePaths = getFilePaths(
folders,
Expand Down Expand Up @@ -98,13 +98,13 @@ class UnusedCodeAnalyzer {
return _getReports(codeUsages, publicCode, rootFolder);
}

Future<UnusedCodeAnalysisConfig> _getAnalysisConfig(
UnusedCodeAnalysisConfig _getAnalysisConfig(
AnalysisContext context,
String rootFolder,
UnusedCodeConfig config,
) async {
final analysisOptions = await analysisOptionsFromContext(context) ??
await analysisOptionsFromFilePath(rootFolder);
) {
final analysisOptions = analysisOptionsFromContext(context) ??
analysisOptionsFromFilePath(rootFolder, context);

final contextConfig =
ConfigBuilder.getUnusedCodeConfigFromOption(analysisOptions)
Expand Down
Expand Up @@ -47,7 +47,7 @@ class UnusedFilesAnalyzer {

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

final filePaths = getFilePaths(
folders,
Expand Down Expand Up @@ -92,13 +92,13 @@ class UnusedFilesAnalyzer {
}
}

Future<UnusedFilesAnalysisConfig> _getAnalysisConfig(
UnusedFilesAnalysisConfig _getAnalysisConfig(
AnalysisContext context,
String rootFolder,
UnusedFilesConfig config,
) async {
final analysisOptions = await analysisOptionsFromContext(context) ??
await analysisOptionsFromFilePath(rootFolder);
) {
final analysisOptions = analysisOptionsFromContext(context) ??
analysisOptionsFromFilePath(rootFolder, context);

final contextConfig =
ConfigBuilder.getUnusedFilesConfigFromOption(analysisOptions)
Expand Down
Expand Up @@ -51,8 +51,8 @@ class UnusedL10nAnalyzer {
final localizationUsages = <ClassElement, Set<String>>{};

for (final context in collection.contexts) {
final analysisOptions = await analysisOptionsFromContext(context) ??
await analysisOptionsFromFilePath(rootFolder);
final analysisOptions = analysisOptionsFromContext(context) ??
analysisOptionsFromFilePath(rootFolder, context);

final contextConfig =
ConfigBuilder.getUnusedL10nConfigFromOption(analysisOptions)
Expand Down
44 changes: 31 additions & 13 deletions lib/src/config_builder/models/analysis_options.dart
@@ -1,7 +1,7 @@
import 'dart:io';
import 'dart:isolate';

import 'package:analyzer/dart/analysis/analysis_context.dart';
import 'package:analyzer/dart/analysis/uri_converter.dart';
import 'package:path/path.dart' as p;
import 'package:yaml/yaml.dart';

Expand Down Expand Up @@ -109,28 +109,43 @@ class AnalysisOptions {
}
}

Future<AnalysisOptions?> analysisOptionsFromContext(
AnalysisOptions? analysisOptionsFromContext(
AnalysisContext context,
) async {
) {
final optionsFilePath = context.contextRoot.optionsFile?.path;

return optionsFilePath == null
? null
: analysisOptionsFromFile(File(optionsFilePath));
: analysisOptionsFromFile(File(optionsFilePath), context);
}

Future<AnalysisOptions> analysisOptionsFromFilePath(String path) {
AnalysisOptions analysisOptionsFromFilePath(
String path,
AnalysisContext context,
) {
final analysisOptionsFile = File(p.absolute(path, _analysisOptionsFileName));

return analysisOptionsFromFile(analysisOptionsFile);
return analysisOptionsFromFile(analysisOptionsFile, context);
}

Future<AnalysisOptions> analysisOptionsFromFile(File? options) async =>
AnalysisOptions analysisOptionsFromFile(
File? options,
AnalysisContext context,
) =>
options != null && options.existsSync()
? AnalysisOptions(options.path, await _loadConfigFromYamlFile(options))
? AnalysisOptions(
options.path,
_loadConfigFromYamlFile(
options,
context.currentSession.uriConverter,
),
)
: const AnalysisOptions(null, {});

Future<Map<String, Object>> _loadConfigFromYamlFile(File options) async {
Map<String, Object> _loadConfigFromYamlFile(
File options,
UriConverter converter,
) {
try {
final node = options.existsSync()
? loadYamlNode(options.readAsStringSync())
Expand All @@ -141,12 +156,15 @@ Future<Map<String, Object>> _loadConfigFromYamlFile(File options) async {

final includeNode = optionsNode['include'];
if (includeNode is String) {
final resolvedUri = includeNode.startsWith('package:')
? await Isolate.resolvePackageUri(Uri.parse(includeNode))
: Uri.file(p.absolute(p.dirname(options.path), includeNode));
final packageImport = includeNode.startsWith('package:');

final resolvedUri = packageImport
? converter.uriToPath(Uri.parse(includeNode))
: p.absolute(p.dirname(options.path), includeNode);

if (resolvedUri != null) {
final resolvedYamlMap =
await _loadConfigFromYamlFile(File.fromUri(resolvedUri));
_loadConfigFromYamlFile(File(resolvedUri), converter);
optionsNode =
mergeMaps(defaults: resolvedYamlMap, overrides: optionsNode);
}
Expand Down
4 changes: 4 additions & 0 deletions pubspec.yaml
Expand Up @@ -32,5 +32,9 @@ dev_dependencies:
mocktail: ^0.3.0
test: ^1.16.8

# internal package, used only in tests data
test_lints:
path: ./test/resources/test_lints

executables:
metrics:
1 change: 1 addition & 0 deletions test/resources/analysis_options_with_import.yaml
@@ -0,0 +1 @@
include: package:test_lints/analysis_options.yaml
3 changes: 3 additions & 0 deletions test/resources/test_lints/lib/analysis_options.1.0.0.yaml
@@ -0,0 +1,3 @@
analyzer:
plugins:
- dart_code_metrics
1 change: 1 addition & 0 deletions test/resources/test_lints/lib/analysis_options.yaml
@@ -0,0 +1 @@
include: package:test_lints/analysis_options.1.0.0.yaml
6 changes: 6 additions & 0 deletions test/resources/test_lints/pubspec.yaml
@@ -0,0 +1,6 @@
name: test_lints
description: Lints for Dart and Flutter based on software industry standards and best practices.
version: 1.0.0

environment:
sdk: ">=2.14.0 <3.0.0"
40 changes: 34 additions & 6 deletions test/src/config_builder/models/analysis_options_test.dart
@@ -1,6 +1,7 @@
import 'dart:io';

import 'package:dart_code_metrics/src/config_builder/models/analysis_options.dart';
import 'package:dart_code_metrics/src/utils/analyzer_utils.dart';
import 'package:test/test.dart';

const _options = {
Expand Down Expand Up @@ -28,23 +29,35 @@ const _options = {
};

void main() {
final collection = createAnalysisContextCollection(
['test'],
Directory.current.path,
null,
);

group('analysisOptionsFromFile constructs AnalysisOptions from', () {
test('null', () async {
final options = await analysisOptionsFromFile(null);
test('null', () {
final options = analysisOptionsFromFile(null, collection.contexts.first);

expect(options.options, isEmpty);
});

test('invalid file', () async {
final options = await analysisOptionsFromFile(File('unavailable.yaml'));
test('invalid file', () {
final options = analysisOptionsFromFile(
File('unavailable.yaml'),
collection.contexts.first,
);

expect(options.options, isEmpty);
});

test('yaml file', () async {
test('yaml file', () {
const yamlFilePath = './test/resources/analysis_options_pkg.yaml';

final options = await analysisOptionsFromFile(File(yamlFilePath));
final options = analysisOptionsFromFile(
File(yamlFilePath),
collection.contexts.first,
);

expect(options.options, contains('linter'));
expect(options.options['linter'], contains('rules'));
Expand Down Expand Up @@ -97,6 +110,21 @@ void main() {
),
);
});

test('valid file with single import', () {
const yamlFilePath = './test/resources/analysis_options_with_import.yaml';

final options = analysisOptionsFromFile(
File(yamlFilePath),
collection.contexts.first,
);

expect(options.options['analyzer'], isNotEmpty);
expect(
(options.options['analyzer'] as Map<String, Object>)['plugins'],
equals(['dart_code_metrics']),
);
});
});

group('AnalysisOptions', () {
Expand Down

0 comments on commit 0c0f76f

Please sign in to comment.