Skip to content
This repository was archived by the owner on Nov 20, 2024. It is now read-only.

Commit aea57c5

Browse files
committed
add no_top_public_members_in_executable_libraries
1 parent a91b233 commit aea57c5

File tree

7 files changed

+195
-0
lines changed

7 files changed

+195
-0
lines changed

example/all.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ linter:
9696
- no_runtimeType_toString
9797
- non_constant_identifier_names
9898
- noop_primitive_operations
99+
- no_top_public_members_in_executable_libraries
99100
- null_check_on_nullable_type_parameter
100101
- null_closures
101102
- omit_local_variable_types

lib/src/rules.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ import 'rules/no_leading_underscores_for_library_prefixes.dart';
9696
import 'rules/no_leading_underscores_for_local_identifiers.dart';
9797
import 'rules/no_logic_in_create_state.dart';
9898
import 'rules/no_runtimeType_toString.dart';
99+
import 'rules/no_top_public_members_in_executable_libraries.dart';
99100
import 'rules/non_constant_identifier_names.dart';
100101
import 'rules/noop_primitive_operations.dart';
101102
import 'rules/null_check_on_nullable_type_parameter.dart';
@@ -311,6 +312,7 @@ void registerLintRules({bool inTestMode = false}) {
311312
..register(NoLogicInCreateState())
312313
..register(NoopPrimitiveOperations())
313314
..register(NoRuntimeTypeToString())
315+
..register(NoTopPublicMembersInExecutableLibraries())
314316
..register(NullCheckOnNullableTypeParameter())
315317
..register(NullClosures())
316318
..register(OmitLocalVariableTypes())
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
import 'package:analyzer/dart/ast/ast.dart';
6+
import 'package:analyzer/dart/ast/visitor.dart';
7+
8+
import '../analyzer.dart';
9+
10+
const _desc = 'No top public members in executable libraries.';
11+
12+
const _details = r'''
13+
14+
Top-level members in an executable library should be private (or relocated to
15+
within main), to prevent unused members.
16+
17+
**BAD:**
18+
19+
```dart
20+
main() {}
21+
void f() {}
22+
```
23+
24+
**GOOD:**
25+
26+
```dart
27+
main() {}
28+
void _f() {}
29+
``
30+
31+
''';
32+
33+
class NoTopPublicMembersInExecutableLibraries extends LintRule {
34+
NoTopPublicMembersInExecutableLibraries()
35+
: super(
36+
name: 'no_top_public_members_in_executable_libraries',
37+
description: _desc,
38+
details: _details,
39+
group: Group.style,
40+
);
41+
42+
@override
43+
void registerNodeProcessors(
44+
NodeLintRegistry registry,
45+
LinterContext context,
46+
) {
47+
var visitor = _Visitor(this);
48+
registry.addCompilationUnit(this, visitor);
49+
}
50+
}
51+
52+
class _Visitor extends SimpleAstVisitor<void> {
53+
_Visitor(this.rule);
54+
55+
final LintRule rule;
56+
57+
@override
58+
void visitCompilationUnit(CompilationUnit node) {
59+
if (!_isInsideExecutableLibrary(node)) return;
60+
for (var member in node.declarations) {
61+
if (member is FunctionDeclaration && member.name.name == 'main') continue;
62+
if (member is TopLevelVariableDeclaration) {
63+
member.variables.variables.forEach(_visitDeclaration);
64+
} else {
65+
_visitDeclaration(member);
66+
}
67+
}
68+
}
69+
70+
void _visitDeclaration(Declaration node) {
71+
if (node.declaredElement?.isPublic ?? false) {
72+
rule.reportLint(node);
73+
}
74+
}
75+
76+
bool _isInsideExecutableLibrary(AstNode node) {
77+
var root = node.root;
78+
if (root is! CompilationUnit) return false;
79+
var library = root.declaredElement?.library;
80+
return library != null &&
81+
library.exportNamespace.definedNames.containsKey('main');
82+
}
83+
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
import 'dart:io';
6+
7+
import 'package:analyzer/src/lint/io.dart';
8+
import 'package:linter/src/analyzer.dart';
9+
import 'package:linter/src/cli.dart' as cli;
10+
import 'package:path/path.dart';
11+
import 'package:test/test.dart';
12+
13+
import '../mocks.dart';
14+
import '../test_constants.dart';
15+
16+
void main() {
17+
group('no_top_public_members_in_executable_libraries', () {
18+
var currentOut = outSink;
19+
var collectingOut = CollectingSink();
20+
setUp(() {
21+
exitCode = 0;
22+
outSink = collectingOut;
23+
});
24+
tearDown(() {
25+
collectingOut.buffer.clear();
26+
outSink = currentOut;
27+
exitCode = 0;
28+
});
29+
30+
test('detects lints', () async {
31+
await cli.runLinter([
32+
'$integrationTestDir/no_top_public_members_in_executable_libraries',
33+
'--rules=no_top_public_members_in_executable_libraries',
34+
], LinterOptions());
35+
var files = Directory(
36+
'$integrationTestDir/no_top_public_members_in_executable_libraries')
37+
.listSync()
38+
.whereType<File>()
39+
.toList();
40+
var lintsByFile = <File, List<int>>{
41+
for (var file in files)
42+
file: file
43+
.readAsLinesSync()
44+
.asMap()
45+
.entries
46+
.where((e) => e.value.endsWith('// LINT'))
47+
.map((e) => e.key + 1)
48+
.toList()
49+
};
50+
expect(
51+
collectingOut.trim(),
52+
stringContainsInOrder([
53+
for (var entry in lintsByFile.entries) ...[
54+
for (var line in entry.value) '${basename(entry.key.path)} $line:',
55+
],
56+
'${lintsByFile.length} file${lintsByFile.length == 1 ? '' : 's'} analyzed, ${lintsByFile.values.expand((e) => e).length} issues found, in',
57+
]),
58+
);
59+
expect(exitCode, lintsByFile.values.expand((e) => e).isEmpty ? 0 : 1);
60+
});
61+
});
62+
}

test/integration_test.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ import 'integration/exhaustive_cases.dart' as exhaustive_cases;
3131
import 'integration/flutter_style_todos.dart' as flutter_style_todos;
3232
import 'integration/lines_longer_than_80_chars.dart'
3333
as lines_longer_than_80_chars;
34+
import 'integration/no_top_public_members_in_executable_libraries.dart'
35+
as no_top_public_members_in_executable_libraries;
3436
import 'integration/only_throw_errors.dart' as only_throw_errors;
3537
import 'integration/overridden_fields.dart' as overridden_fields;
3638
import 'integration/packages_file_test.dart' as packages_file_test;
@@ -207,6 +209,7 @@ void ruleTests() {
207209
prefer_mixin.main();
208210
use_build_context_synchronously.main();
209211
prefer_const_constructors.main();
212+
no_top_public_members_in_executable_libraries.main();
210213
});
211214
}
212215

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
// test w/ `dart test -N no_top_public_members_in_executable_libraries`
6+
7+
const a = 1; // OK
8+
9+
int v = 1; // OK
10+
11+
typedef A = String; // OK
12+
13+
class C {} // OK
14+
15+
mixin M {} // OK
16+
17+
enum E { e } // OK
18+
19+
void f() {} // OK
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
// test w/ `dart test -N no_top_public_members_in_executable_libraries`
6+
7+
main() {} // OK
8+
9+
const a = 1; // LINT
10+
11+
int v = 1; // LINT
12+
13+
typedef A = String; // LINT
14+
15+
class C {} // LINT
16+
17+
mixin M {} // LINT
18+
19+
enum E { e } // LINT
20+
21+
void f() {} // LINT
22+
23+
_insideFunction() {
24+
inner() {} // OK
25+
}

0 commit comments

Comments
 (0)