Skip to content

Commit

Permalink
Version 3.2.0-162.0.dev
Browse files Browse the repository at this point in the history
Merge 8cc1b54 into dev
  • Loading branch information
Dart CI committed Sep 14, 2023
2 parents 289f835 + 8cc1b54 commit d25e8d6
Show file tree
Hide file tree
Showing 30 changed files with 594 additions and 438 deletions.
67 changes: 67 additions & 0 deletions pkg/analyzer/lib/dart/ast/doc_comment.dart
Original file line number Diff line number Diff line change
Expand Up @@ -96,18 +96,60 @@ enum DocDirectiveType {
/// the URL. A named 'id' argument can also be given. For example:
///
/// `{@animation 600 400 https://www.example.com/example.mp4 id=video1}`
///
/// See documentation at
/// https://github.com/dart-lang/dartdoc/wiki/Doc-comment-directives#animations.
animation(
'animation',
positionalParameters: ['width', 'height', 'url'],
namedParameters: ['id'],
),

/// A [DocDirective] declaring the associated library is the "canonical"
/// location for a certain element.
///
/// Dartdoc uses some heuristics to decide what the public-facing libraries
/// are, and which public-facing library is the "canonical" location for an
/// element. When that heuristic needs to be overridden, a user can use this
/// directive. Example:
///
/// `{@canonicalFor some_library.SomeClass}`
///
/// See documentation at
/// https://github.com/dart-lang/dartdoc/wiki/Doc-comment-directives#canonicalization.
canonicalFor(
// TODO(srawlins): We have mostly used 'kebab-case' in directive names. This
// directive name is a rare departure from that style. Migrate users to use
// 'canonical-for'.
'canonicalFor',
positionalParameters: ['element'],
),

/// A [DocDirective] declaring a categorization into a named category.
///
/// This directive has one required argument: the category name. The category
/// name is allowed to contain whitespace.
// TODO(srawlins): I think allowing a category name, which is parsed as
// multiple positional arguments (or I guess named arguments if one contains)
// an equal sign!) is too loosy-goosey. We should just support quoting and
// require that a category name with spaces be wrapped in quotes.
///
/// See documentation at
/// https://github.com/dart-lang/dartdoc/wiki/Doc-comment-directives#categories.
category(
'category',
restParametersAllowed: true,
),

/// A [DocDirective] declaring an example file.
///
/// This directive has one required argument: the path. A named 'region'
/// argument, and a named 'lang' argument can also be given. For example:
///
/// `{@example abc/def/xyz_component.dart region=template lang=html}`
///
/// See documentation at
/// https://github.com/dart-lang/dartdoc/wiki/Doc-comment-directives#examples.
example(
'example',
positionalParameters: ['path'],
Expand All @@ -121,12 +163,30 @@ enum DocDirectiveType {
/// `{@macro some-macro}`
macro('macro', positionalParameters: ['name']),

/// A [DocDirective] declaring a categorization into a named sub-category.
///
/// This directive has one required argument: the sub-category name. The
/// sub-category name is allowed to contain whitespace.
///
/// See documentation at
/// https://github.com/dart-lang/dartdoc/wiki/Doc-comment-directives#categories.
subCategory(
// TODO(srawlins): We have mostly used 'kebab-case' in directive names. This
// directive name is the sole departure from that style. Migrate users to
// use 'sub-category'.
'subCategory',
restParametersAllowed: true,
),

/// A [DocDirective] declaring an embedded YouTube video.
///
/// This directive has three required arguments: the width, the height, and
/// the URL. For example:
///
/// `{@youtube 600 400 https://www.youtube.com/watch?v=abc123}`
///
/// See documentation at
/// https://github.com/dart-lang/dartdoc/wiki/Doc-comment-directives#youtube-videos.
youtube('youtube', positionalParameters: ['width', 'height', 'url']);

/// The name of the directive, as written in a doc comment.
Expand All @@ -138,10 +198,17 @@ enum DocDirectiveType {
/// The named parameter names, which are each optional.
final List<String> namedParameters;

/// Whether "rest" parameters are allowed.
///
/// In such a doc directive type, we do not enforce a maximum number of
/// arguments.
final bool restParametersAllowed;

const DocDirectiveType(
this.name, {
this.positionalParameters = const <String>[],
this.namedParameters = const <String>[],
this.restParametersAllowed = false,
});
}

Expand Down
6 changes: 6 additions & 0 deletions pkg/analyzer/lib/src/error/doc_comment_verifier.dart
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,12 @@ class DocCommentVerifier {
}
}

if (docDirective.type.restParametersAllowed) {
// TODO(srawlins): We probably want to enforce that at least one argument
// is given, particularly for 'category' and 'subCategory'.
return;
}

if (positionalArgumentCount > requiredCount) {
var errorOffset = docDirective.positionalArguments[requiredCount].offset;
var errorLength = docDirective.positionalArguments.last.end - errorOffset;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -546,6 +546,14 @@ class UnusedLocalElementsVerifier extends RecursiveAstVisitor<void> {
super.visitEnumDeclaration(node);
}

@override
void visitExtensionTypeDeclaration(ExtensionTypeDeclaration node) {
final declaredElement = node.declaredElement!;
_visitClassElement(declaredElement);

super.visitExtensionTypeDeclaration(node);
}

@override
void visitFieldDeclaration(FieldDeclaration node) {
for (final field in node.fields.variables) {
Expand Down
11 changes: 9 additions & 2 deletions pkg/analyzer/lib/src/fasta/doc_comment_builder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -286,15 +286,22 @@ class DocCommentBuilder {
case 'animation':
_docDirectives.add(parser.directive(DocDirectiveType.animation));
return true;
case 'canonicalFor':
_docDirectives.add(parser.directive(DocDirectiveType.canonicalFor));
return true;
case 'category':
_docDirectives.add(parser.directive(DocDirectiveType.category));
return true;
case 'example':
_docDirectives.add(parser.directive(DocDirectiveType.example));
return true;
case 'subCategory':
_docDirectives.add(parser.directive(DocDirectiveType.subCategory));
return true;
case 'youtube':
_docDirectives.add(parser.directive(DocDirectiveType.youtube));
return true;
}
// TODO(srawlins): Handle other doc directives: api?,
// canonicalFor?, category, macro, subCategory.
// TODO(srawlins): Handle block doc directives: inject-html, template.
// TODO(srawlins): Handle unknown (misspelled?) directive.
return false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,22 @@ class C {}
''');
}

test_canonicalFor_hasNoArguments() async {
await assertErrorsInCode('''
/// {@canonicalFor}
class C {}
''', [
error(WarningCode.DOC_DIRECTIVE_MISSING_ONE_ARGUMENT, 4, 16),
]);
}

test_canonicalFor_hasOneArguments() async {
await assertNoErrorsInCode('''
/// {@canonicalFor String}
class C {}
''');
}

test_example_hasNoArguments() async {
await assertErrorsInCode('''
/// {@example}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,15 @@ import 'package:meta/meta.dart';
assertNoErrorsInResult();
}

void test_annotationInTest_extensionType() async {
await resolveFileCode('$testPackageRootPath/test/foo_test.dart', r'''
import 'package:meta/meta.dart';
@internal extension type E(int i) {}
''');

assertNoErrorsInResult();
}

void test_privateClass() async {
await resolveFileCode(testPackageLibSrcFilePath, r'''
import 'package:meta/meta.dart';
Expand Down Expand Up @@ -188,6 +197,18 @@ import 'package:meta/meta.dart';
]);
}

void test_privateExtensionType() async {
await resolveFileCode(testPackageLibSrcFilePath, r'''
import 'package:meta/meta.dart';
@internal extension type _E(int i) {}
''');

assertErrorsInResult([
error(WarningCode.INVALID_INTERNAL_ANNOTATION, 33, 9),
error(WarningCode.UNUSED_ELEMENT, 58, 2),
]);
}

void test_privateField_instance() async {
await resolveFileCode(testPackageLibSrcFilePath, r'''
import 'package:meta/meta.dart';
Expand Down Expand Up @@ -332,4 +353,17 @@ class _C {
error(WarningCode.UNUSED_ELEMENT, 68, 1),
]);
}

void test_publicMethod_privateExtensionType() async {
await resolveFileCode(testPackageLibSrcFilePath, r'''
import 'package:meta/meta.dart';
extension type _E(int i) {
@internal void f() {}
}
''');

assertErrorsInResult([
error(WarningCode.UNUSED_ELEMENT, 48, 2),
]);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,12 @@ class InvalidUseOfInternalMemberTest extends PubPackageResolutionTest {
@override
void setUp() {
super.setUp();
writeTestPackagePubspecYamlFile(PubspecYamlFileConfig());
newAnalysisOptionsYamlFile(
fooPackageRootPath,
AnalysisOptionsFileConfig(
experiments: experiments,
).toContent(),
);
writeTestPackageConfig(
PackageConfigFileBuilder()
..add(
Expand All @@ -50,6 +55,22 @@ A a = A();
assertNoErrorsInResult();
}

test_insidePackage_extensionType() async {
newFile('$fooPackageRootPath/lib/src/a.dart', '''
import 'package:meta/meta.dart';
@internal
extension type E(int i) {}
''');
newFile('$fooPackageRootPath/lib/a.dart', '''
import 'src/a.dart';
E e = E(1);
''');
await resolveFile2('$fooPackageRootPath/lib/a.dart');

assertNoErrorsInResult();
}

test_outsidePackage_class() async {
newFile('$fooPackageRootPath/lib/src/a.dart', '''
import 'package:meta/meta.dart';
Expand Down Expand Up @@ -241,6 +262,22 @@ int a = 'hello'.f();
]);
}

test_outsidePackage_extensionType() async {
newFile('$fooPackageRootPath/lib/src/a.dart', '''
import 'package:meta/meta.dart';
@internal
extension type E(int i) {}
''');

await assertErrorsInCode('''
import 'package:foo/src/a.dart';
E e = E(1);
''', [
error(WarningCode.INVALID_USE_OF_INTERNAL_MEMBER, 34, 1),
]);
}

test_outsidePackage_field_inObjectPattern() async {
newFile('$fooPackageRootPath/lib/src/a.dart', '''
import 'package:meta/meta.dart';
Expand Down
52 changes: 52 additions & 0 deletions pkg/analyzer/test/src/diagnostics/unused_element_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,58 @@ extension on int? {
''');
}

test_extensionType_interfaceType_isUsed_typeName_typeArgument() async {
await assertNoErrorsInCode(r'''
extension type _E(int i) {}
void f() {
Map<_E, int>();
}
''');
}

test_extensionType_interfaceType_notUsed() async {
await assertErrorsInCode(r'''
extension type _E(int i) {}
''', [
error(WarningCode.UNUSED_ELEMENT, 15, 2),
]);
}

test_extensionType_member_notUsed() async {
await assertErrorsInCode('''
extension type E(int i) {
void _f() {}
}
''', [
error(WarningCode.UNUSED_ELEMENT, 33, 2),
]);
}

test_extensionType_notUsed_variableDeclaration() async {
await assertErrorsInCode('''
extension type _E(int i) {}
void f() {
_E? v;
print(v);
}
''', [
error(WarningCode.UNUSED_ELEMENT, 15, 2),
]);
}

test_extensionType_notUsed_variableDeclaration_typeArgument() async {
await assertErrorsInCode('''
extension type _E(int i) {}
main() {
List<_E>? v;
print(v);
}
''', [
error(WarningCode.UNUSED_ELEMENT, 15, 2),
]);
}

test_optionalParameter_isUsed_genericConstructor() async {
await assertNoErrorsInCode('''
class C<T> {
Expand Down
7 changes: 1 addition & 6 deletions pkg/analyzer/test/src/summary/elements_base.dart
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,7 @@ abstract class ElementsBaseTest extends PubPackageResolutionTest {
newFile(path, contents);
}

Future<LibraryElementImpl> buildLibrary(
String text, {
bool allowErrors = false,
bool dumpSummaries = false,
List<Set<String>>? preBuildSequence,
}) async {
Future<LibraryElementImpl> buildLibrary(String text) async {
final file = newFile(testFile.path, text);
final analysisContext = contextFor(file);
final analysisSession = analysisContext.currentSession;
Expand Down
Loading

0 comments on commit d25e8d6

Please sign in to comment.