Skip to content

Commit 9d67cdc

Browse files
author
Illia Aihistov
committed
Merge remote-tracking branch 'upstream/master' into refactor/simplify-avoid-duplicate-code
2 parents 8c187fc + c9e5a6e commit 9d67cdc

51 files changed

Lines changed: 1977 additions & 297 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎CHANGELOG.md‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
## 1.0.0-dev.2
2+
3+
- Resolved false positives in `avoid_returning_widgets` rule.
4+
- Resolved false positives on constructors in `number_of_parameters` rule.
5+
- Improved `member_ordering` configuration.
6+
- Improved `avoid_duplicate_code` rule and parameter documentation.
7+
18
## 1.0.0-dev.1
29

310
- feat!: migrate to analyzer_server_plugin

‎README.md‎

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -50,12 +50,13 @@ You can customize individual rule settings in your `analysis_options.yaml`:
5050

5151
```yaml
5252
plugins:
53-
solid_lints:
54-
version: <INSERT LATEST VERSION>
55-
diagnostics:
56-
cyclomatic_complexity:
57-
max_complexity: 10
58-
avoid_non_null_assertion: true
53+
solid_lints: <INSERT LATEST VERSION>
54+
55+
solid_lints:
56+
diagnostics:
57+
cyclomatic_complexity:
58+
max_complexity: 10
59+
avoid_non_null_assertion: true
5960
```
6061

6162
# Badge
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
---
2+
sidebar_label: Other notable packages
3+
sidebar_position: 3
4+
---
5+
6+
# Other Notable Packages
7+
8+
Notable third-party tools that complement `solid_lints` in keeping Dart and
9+
Flutter codebases clean, robust, and maintainable.
10+
11+
## [undead](https://pub.dev/packages/undead)
12+
13+
[![pub package](https://img.shields.io/pub/v/undead.svg)](https://pub.dev/packages/undead)
14+
15+
Deterministic reachability and dead/unused declaration analysis for Dart and
16+
Flutter packages.
17+
18+
It performs whole-package AST analysis using `package:analyzer` to build a
19+
reachability graph from known entrypoints to all internal declarations,
20+
identifying unused top-level declarations, classes, functions, and variables.
21+
22+
- **Links**: [pub.dev](https://pub.dev/packages/undead) ·
23+
[GitHub](https://github.com/kevmoo/analytica.dart/tree/main/packages/undead)
24+
25+
---
26+
27+
## [cognitive_complexity](https://pub.dev/packages/cognitive_complexity)
28+
29+
[![pub package](https://img.shields.io/pub/v/cognitive_complexity.svg)](https://pub.dev/packages/cognitive_complexity)
30+
31+
Algorithmic Cognitive Complexity calculation and Data-Flow analysis library and
32+
CLI tools for Dart and Flutter.
33+
34+
It implements the Cognitive Complexity principles articulated by SonarSource,
35+
providing an objective way to find and fix overly complex logic and routines.
36+
37+
- **Links**: [pub.dev](https://pub.dev/packages/cognitive_complexity) ·
38+
[GitHub](https://github.com/kevmoo/analytica.dart/tree/main/packages/cognitive_complexity)

‎example/analysis_options.yaml‎

Lines changed: 27 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -3,28 +3,30 @@ include: package:solid_lints/analysis_options.yaml
33
plugins:
44
solid_lints:
55
path: ../
6-
diagnostics:
7-
cyclomatic_complexity:
8-
max_complexity: 4
9-
number_of_parameters:
10-
max_parameters: 2
11-
function_lines_of_code:
12-
max_lines: 50
13-
avoid_non_null_assertion: true
14-
avoid_late_keyword: true
15-
avoid_global_state: true
16-
avoid_returning_widgets: true
17-
avoid_unnecessary_setstate: true
18-
double_literal_format: true
19-
avoid_unnecessary_type_assertions: true
20-
avoid_debug_print_in_release: true
21-
avoid_using_api:
22-
severity: info
23-
entries:
24-
- class_name: Future
25-
identifier: wait
26-
source: dart:async
27-
reason: >-
28-
Future.wait should be avoided because it loses type safety for
29-
the results. Use a Record's `wait` method instead.
30-
severity: warning
6+
7+
solid_lints:
8+
diagnostics:
9+
cyclomatic_complexity:
10+
max_complexity: 4
11+
number_of_parameters:
12+
max_parameters: 2
13+
function_lines_of_code:
14+
max_lines: 50
15+
avoid_non_null_assertion: true
16+
avoid_late_keyword: true
17+
avoid_global_state: true
18+
avoid_returning_widgets: true
19+
avoid_unnecessary_setstate: true
20+
double_literal_format: true
21+
avoid_unnecessary_type_assertions: true
22+
avoid_debug_print_in_release: true
23+
avoid_using_api:
24+
severity: info
25+
entries:
26+
- class_name: Future
27+
identifier: wait
28+
source: dart:async
29+
reason: >-
30+
Future.wait should be avoided because it loses type safety for
31+
the results. Use a Record's `wait` method instead.
32+
severity: warning

‎lib/analysis_options.yaml‎

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,7 @@ solid_lints:
4646
allow_initialized: true
4747
ignored_types:
4848
- AnimationController
49-
avoid_non_null_assertion:
50-
ignored_types:
51-
- IMap
52-
- BuiltMap
49+
avoid_non_null_assertion: true
5350
avoid_returning_widgets: true
5451
avoid_similar_names: true
5552
avoid_unnecessary_return_variable: true
@@ -95,7 +92,8 @@ solid_lints:
9592
newline_before_return: true
9693
no_empty_block: true
9794
no_equal_then_else: true
98-
prefer_early_return: true
95+
# Disabled by default for now. Will be considered for future inclusion.
96+
prefer_early_return: false
9997

10098
no_magic_number:
10199
allowed_in_widget_params: true
@@ -112,7 +110,9 @@ solid_lints:
112110
prefer_last: true
113111
prefer_match_file_name: true
114112
proper_super_calls: true
115-
named_parameters_ordering: true
113+
# Disabled for now due to conflict with
114+
# `always_put_required_named_parameters_first`.
115+
named_parameters_ordering: false
116116
use_nearest_context: true
117117
use_descriptive_names_for_type_parameters: true
118118

‎lib/src/common/parameter_parser/analysis_options_loader.dart‎

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,34 @@ class AnalysisOptionsLoader {
7272
) ??
7373
false;
7474

75+
/// Checks if a file is excluded by the analysis options configuration.
76+
bool isFileExcluded(RuleContext context) {
77+
final targetPath = (context.currentUnit ?? context.definingUnit).file.path;
78+
79+
return isFileExcludedForFile(targetPath);
80+
}
81+
82+
/// Checks if a specific file at [filePath] is excluded by the nearest
83+
/// analysis options file.
84+
bool isFileExcludedForFile(String filePath) {
85+
final pathContext = _resourceProvider.pathContext;
86+
if (!pathContext.isAbsolute(filePath)) return false;
87+
88+
final dirPath = pathContext.dirname(filePath);
89+
final yamlPath = _findNearestAnalysisOptionsFilePath(dirPath);
90+
if (yamlPath == null) return false;
91+
92+
_loadRulesOptionsIfNewer(yamlPath);
93+
final rootDir = pathContext.dirname(yamlPath);
94+
95+
return _rulesCache[yamlPath]?.isPathExcluded(
96+
filePath,
97+
pathContext,
98+
rootDir,
99+
) ??
100+
false;
101+
}
102+
75103
/// Loads lint rules from the analysis options file for all rules
76104
/// using the provided [RuleContext].
77105
void loadRulesOptionsFromContext(RuleContext context) =>
@@ -84,8 +112,11 @@ class AnalysisOptionsLoader {
84112
RuleContext context,
85113
T Function(String) f,
86114
) {
87-
final filePath = context.definingUnit.file.path;
88-
final dirPath = _resourceProvider.pathContext.dirname(filePath);
115+
final filePath = (context.currentUnit ?? context.definingUnit).file.path;
116+
final pathContext = _resourceProvider.pathContext;
117+
if (!pathContext.isAbsolute(filePath)) return null;
118+
119+
final dirPath = pathContext.dirname(filePath);
89120
final yamlPath = _findNearestAnalysisOptionsFilePath(dirPath);
90121

91122
if (yamlPath == null) return null;
@@ -107,11 +138,14 @@ class AnalysisOptionsLoader {
107138
modificationStamp: modificationStamp,
108139
rules: rulesData.rules,
109140
disabledRules: rulesData.disabledRules,
141+
excludedPatterns: rulesData.excludedPatterns,
110142
);
111143
}
112144

113145
String? _findNearestAnalysisOptionsFilePath(String startDirectoryPath) {
114146
final pathContext = _resourceProvider.pathContext;
147+
if (!pathContext.isAbsolute(startDirectoryPath)) return null;
148+
115149
var currentDirectoryPath = startDirectoryPath;
116150

117151
while (currentDirectoryPath.isNotEmpty) {

‎lib/src/common/parameter_parser/analysis_options_parser.dart‎

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ class AnalysisOptionsParser {
3636

3737
final mergedRules = <String, Map<String, Object?>>{};
3838
final disabledRules = <String>{};
39+
final excludedPatterns = <String>{};
3940

4041
final nextSeenPaths = {...seenPaths, path};
4142
_resolveAndMergeIncludes(
@@ -44,11 +45,17 @@ class AnalysisOptionsParser {
4445
nextSeenPaths,
4546
mergedRules,
4647
disabledRules,
48+
excludedPatterns,
4749
);
4850
_parseRuleOptions(yaml, mergedRules, disabledRules);
4951
_parseSuppressedErrors(yaml, mergedRules, disabledRules);
52+
_parseExcludedPatterns(yaml, excludedPatterns);
5053

51-
return RulesData(rules: mergedRules, disabledRules: disabledRules);
54+
return RulesData(
55+
rules: mergedRules,
56+
disabledRules: disabledRules,
57+
excludedPatterns: excludedPatterns,
58+
);
5259
}
5360

5461
Map<String, Object?>? _parseYaml(File file) {
@@ -79,6 +86,7 @@ class AnalysisOptionsParser {
7986
Set<String> seenPaths,
8087
Map<String, Map<String, Object?>> mergedRules,
8188
Set<String> disabledRules,
89+
Set<String> excludedPatterns,
8290
) {
8391
final includeOption = yaml['include'];
8492
switch (includeOption) {
@@ -89,6 +97,7 @@ class AnalysisOptionsParser {
8997
seenPaths,
9098
mergedRules,
9199
disabledRules,
100+
excludedPatterns,
92101
);
93102
case List():
94103
for (final include in includeOption) {
@@ -99,6 +108,7 @@ class AnalysisOptionsParser {
99108
seenPaths,
100109
mergedRules,
101110
disabledRules,
111+
excludedPatterns,
102112
);
103113
}
104114
}
@@ -111,6 +121,7 @@ class AnalysisOptionsParser {
111121
Set<String> seenPaths,
112122
Map<String, Map<String, Object?>> mergedRules,
113123
Set<String> disabledRules,
124+
Set<String> excludedPatterns,
114125
) {
115126
final includedFile = _resolveIncludedFile(baseFile, includePath);
116127
if (includedFile == null) return;
@@ -124,6 +135,7 @@ class AnalysisOptionsParser {
124135
};
125136
}
126137
disabledRules.addAll(includedData.disabledRules);
138+
excludedPatterns.addAll(includedData.excludedPatterns);
127139
}
128140

129141
File? _resolveIncludedFile(File baseFile, String includePath) {
@@ -228,4 +240,21 @@ class AnalysisOptionsParser {
228240
}
229241
}
230242
}
243+
244+
/// Parses file exclusion patterns configured under `analyzer: exclude:`.
245+
void _parseExcludedPatterns(
246+
Map<String, Object?> yaml,
247+
Set<String> excludedPatterns,
248+
) {
249+
final analyzer = yaml['analyzer'];
250+
if (analyzer is! Map) return;
251+
252+
final exclude = analyzer['exclude'];
253+
switch (exclude) {
254+
case String():
255+
excludedPatterns.add(exclude);
256+
case Iterable():
257+
excludedPatterns.addAll(exclude.whereType<String>());
258+
}
259+
}
231260
}

‎lib/src/common/parameter_parser/cached_package_rules.dart‎

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
import 'package:glob/glob.dart';
2+
import 'package:path/path.dart' as p;
3+
import 'package:solid_lints/src/utils/function_utils.dart';
4+
15
/// Cached rules for a dart package
26
class CachedPackageRules {
37
/// The last modification stamp of the analysis options file
@@ -9,10 +13,44 @@ class CachedPackageRules {
913
/// Rules that are explicitly disabled
1014
final Set<String> disabledRules;
1115

16+
/// Patterns of excluded files
17+
final Set<String> excludedPatterns;
18+
19+
final List<Glob> _compiledGlobs;
20+
final Map<String, bool> _pathExclusionCache = {};
21+
1222
/// Creates an instance of [CachedPackageRules]
13-
const CachedPackageRules({
23+
CachedPackageRules({
1424
required this.modificationStamp,
1525
required this.rules,
1626
required this.disabledRules,
17-
});
27+
required this.excludedPatterns,
28+
}) : _compiledGlobs = _compileGlobs(excludedPatterns);
29+
30+
static List<Glob> _compileGlobs(Set<String> patterns) => patterns
31+
.map(
32+
(pattern) => FunctionUtils.tryOrNull(
33+
() => Glob(pattern, context: p.posix),
34+
),
35+
)
36+
.nonNulls
37+
.toList();
38+
39+
/// Checks if [filePath] matches any of the excluded patterns.
40+
bool isPathExcluded(
41+
String filePath,
42+
p.Context pathContext,
43+
String rootDir,
44+
) {
45+
if (excludedPatterns.isEmpty) return false;
46+
47+
return _pathExclusionCache.putIfAbsent(filePath, () {
48+
final relativePath = pathContext.isWithin(rootDir, filePath)
49+
? pathContext.relative(filePath, from: rootDir)
50+
: filePath;
51+
final normalizedPath = p.posix.joinAll(pathContext.split(relativePath));
52+
53+
return _compiledGlobs.any((glob) => glob.matches(normalizedPath));
54+
});
55+
}
1856
}

‎lib/src/common/parameter_parser/rules_data.dart‎

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,19 @@ class RulesData {
66
/// The set of explicitly disabled rules.
77
final Set<String> disabledRules;
88

9+
/// The set of file paths or glob patterns excluded from analysis.
10+
final Set<String> excludedPatterns;
11+
912
/// Creates a new instance of [RulesData].
10-
const RulesData({required this.rules, required this.disabledRules});
13+
const RulesData({
14+
required this.rules,
15+
required this.disabledRules,
16+
required this.excludedPatterns,
17+
});
1118

1219
/// Creates a new empty instance of [RulesData].
13-
const RulesData.empty() : rules = const {}, disabledRules = const {};
20+
const RulesData.empty()
21+
: rules = const {},
22+
disabledRules = const {},
23+
excludedPatterns = const {};
1424
}

0 commit comments

Comments
 (0)