blob: 21bec48724c0f26a8b574d23a6933baacfe44ce3 [file] [log] [blame]
// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/ast/visitor.dart';
import 'package:analyzer/dart/element/element.dart';
import '../analyzer.dart';
const _desc = r'Avoid using private types in public APIs.';
const _details = r'''
**AVOID** using library private types in public APIs.
For the purposes of this lint, a public API is considered to be any top-level or
member declaration unless the declaration is library private or contained in a
declaration that's library private. The following uses of types are checked:
- the return type of a function or method,
- the type of any parameter of a function or method,
- the bound of a type parameter to any function, method, class, mixin,
extension's extended type, or type alias,
- the type of any top level variable or field,
- any type used in the declaration of a type alias (for example
`typedef F = _Private Function();`), or
- any type used in the `on` clause of an extension or a mixin
**GOOD:**
```dart
f(String s) { ... }
```
**BAD:**
```dart
f(_Private p) { ... }
class _Private {}
```
''';
class LibraryPrivateTypesInPublicAPI extends LintRule implements NodeLintRule {
LibraryPrivateTypesInPublicAPI()
: super(
name: 'library_private_types_in_public_api',
description: _desc,
details: _details,
group: Group.style);
@override
void registerNodeProcessors(
NodeLintRegistry registry, LinterContext context) {
var visitor = Visitor(this);
registry.addCompilationUnit(this, visitor);
}
}
class Validator extends SimpleAstVisitor<void> {
LintRule rule;
Validator(this.rule);
@override
void visitClassDeclaration(ClassDeclaration node) {
if (Identifier.isPrivateName(node.name.name)) {
return;
}
node.typeParameters?.accept(this);
node.members.accept(this);
}
@override
void visitClassTypeAlias(ClassTypeAlias node) {
if (Identifier.isPrivateName(node.name.name)) {
return;
}
node.superclass.accept(this);
node.typeParameters?.accept(this);
}
@override
void visitConstructorDeclaration(ConstructorDeclaration node) {
var name = node.name;
if (name != null && Identifier.isPrivateName(name.name)) {
return;
}
node.parameters.accept(this);
}
@override
void visitDefaultFormalParameter(DefaultFormalParameter node) {
node.parameter.accept(this);
}
@override
void visitExtensionDeclaration(ExtensionDeclaration node) {
var name = node.name;
if (name == null || Identifier.isPrivateName(name.name)) {
return;
}
node.extendedType.accept(this);
node.typeParameters?.accept(this);
node.members.accept(this);
}
@override
void visitFieldDeclaration(FieldDeclaration node) {
if (node.fields.variables
.any((field) => !Identifier.isPrivateName(field.name.name))) {
node.fields.type?.accept(this);
}
}
@override
void visitFieldFormalParameter(FieldFormalParameter node) {
if (node.isNamed && Identifier.isPrivateName(node.identifier.name)) {
return;
}
node.type?.accept(this);
}
@override
void visitFormalParameterList(FormalParameterList node) {
node.parameters.accept(this);
}
@override
void visitFunctionDeclaration(FunctionDeclaration node) {
if (Identifier.isPrivateName(node.name.name)) {
return;
}
node.returnType?.accept(this);
node.functionExpression.typeParameters?.accept(this);
node.functionExpression.parameters?.accept(this);
}
@override
void visitFunctionTypeAlias(FunctionTypeAlias node) {
if (Identifier.isPrivateName(node.name.name)) {
return;
}
node.returnType?.accept(this);
node.typeParameters?.accept(this);
node.parameters.accept(this);
}
@override
void visitFunctionTypedFormalParameter(FunctionTypedFormalParameter node) {
if (node.isNamed && Identifier.isPrivateName(node.identifier.name)) {
return;
}
node.returnType?.accept(this);
node.typeParameters?.accept(this);
node.parameters.accept(this);
}
@override
void visitGenericFunctionType(GenericFunctionType node) {
node.returnType?.accept(this);
node.typeParameters?.accept(this);
node.parameters.accept(this);
}
@override
void visitGenericTypeAlias(GenericTypeAlias node) {
if (Identifier.isPrivateName(node.name.name)) {
return;
}
node.typeParameters?.accept(this);
node.functionType?.accept(this);
}
@override
void visitMethodDeclaration(MethodDeclaration node) {
if (Identifier.isPrivateName(node.name.name)) {
return;
}
node.returnType?.accept(this);
node.typeParameters?.accept(this);
node.parameters?.accept(this);
}
@override
void visitMixinDeclaration(MixinDeclaration node) {
if (Identifier.isPrivateName(node.name.name)) {
return;
}
node.onClause?.superclassConstraints.accept(this);
node.typeParameters?.accept(this);
node.members.accept(this);
}
@override
void visitSimpleFormalParameter(SimpleFormalParameter node) {
var name = node.identifier;
if (name != null && node.isNamed && Identifier.isPrivateName(name.name)) {
return;
}
node.type?.accept(this);
}
@override
void visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) {
if (node.variables.variables
.any((field) => !Identifier.isPrivateName(field.name.name))) {
node.variables.type?.accept(this);
}
}
@override
void visitTypeArgumentList(TypeArgumentList node) {
node.arguments.accept(this);
}
@override
void visitTypeName(TypeName node) {
var element = node.name.staticElement;
if (element != null && isPrivate(element)) {
rule.reportLint(node.name);
}
node.typeArguments?.accept(this);
}
@override
void visitTypeParameter(TypeParameter node) {
node.bound?.accept(this);
}
@override
void visitTypeParameterList(TypeParameterList node) {
node.typeParameters.accept(this);
}
/// Return `true` if the given [element] is private or is defined in a private
/// library.
static bool isPrivate(Element element) {
var name = element.name;
return name != null && Identifier.isPrivateName(name);
}
}
class Visitor extends SimpleAstVisitor {
LintRule rule;
Visitor(this.rule);
@override
void visitCompilationUnit(CompilationUnit node) {
var element = node.declaredElement;
if (element != null && !Validator.isPrivate(element)) {
var validator = Validator(rule);
node.declarations.accept(validator);
}
}
}