mirror of
https://github.com/dart-lang/sdk
synced 2024-09-16 04:06:59 +00:00
analyzer: Support animation doc directive; refactor classes
Work towards https://github.com/dart-lang/sdk/issues/52705 Change-Id: Idc77bf866beace85b40b30d464ff4c7c62e19735 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/325360 Commit-Queue: Samuel Rawlins <srawlins@google.com> Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
This commit is contained in:
parent
ba70bed261
commit
cdf016c6ab
|
@ -3450,18 +3450,20 @@ WarningCode.DEPRECATED_MIXIN_FUNCTION:
|
|||
The fix is to remove `Function` from where it's referenced.
|
||||
WarningCode.DEPRECATED_NEW_IN_COMMENT_REFERENCE:
|
||||
status: hasFix
|
||||
WarningCode.DOC_DIRECTIVE_HAS_EXTRA_ARGUMENTS:
|
||||
status: needsEvaluation
|
||||
WarningCode.DOC_DIRECTIVE_MISSING_CLOSING_BRACE:
|
||||
status: needsEvaluation
|
||||
WarningCode.DOC_DIRECTIVE_MISSING_ONE_ARGUMENT:
|
||||
status: noFix
|
||||
WarningCode.DOC_DIRECTIVE_MISSING_THREE_ARGUMENTS:
|
||||
status: noFix
|
||||
WarningCode.DOC_DIRECTIVE_MISSING_TWO_ARGUMENTS:
|
||||
status: noFix
|
||||
WarningCode.DOC_IMPORT_CANNOT_BE_DEFERRED:
|
||||
status: needsFix
|
||||
WarningCode.DOC_IMPORT_CANNOT_HAVE_CONFIGURATIONS:
|
||||
status: needsFix
|
||||
WarningCode.DOC_YOUTUBE_DIRECTIVE_MISSING_HEIGHT:
|
||||
status: needsEvaluation
|
||||
WarningCode.DOC_YOUTUBE_DIRECTIVE_MISSING_URL:
|
||||
status: needsEvaluation
|
||||
WarningCode.DOC_YOUTUBE_DIRECTIVE_MISSING_WIDTH:
|
||||
status: needsEvaluation
|
||||
WarningCode.DUPLICATE_EXPORT:
|
||||
status: needsFix
|
||||
notes: |-
|
||||
|
|
|
@ -13,19 +13,97 @@ import 'package:meta/meta.dart';
|
|||
/// Documentation directives are declared with `{@` at the start of a line of a
|
||||
/// documentation comment, followed the name of a doc directive, arguments, and
|
||||
/// finally a right curly brace (`}`).
|
||||
///
|
||||
/// Arguments are separated from the directive name, and from each other, by
|
||||
/// whitespace. There are two types of arguments: positional and named. Named
|
||||
/// arguments are written as `NAME=VALUE`, without any internal whitespace.
|
||||
/// Named arguments can be optional.
|
||||
@experimental
|
||||
sealed class DocDirective {
|
||||
final class DocDirective {
|
||||
/// The offset of the starting text, '@docImport'.
|
||||
final int offset;
|
||||
final int end;
|
||||
final int nameOffset;
|
||||
final int nameEnd;
|
||||
|
||||
final DocDirectiveName name;
|
||||
|
||||
final List<DocDirectiveArgument> positionalArguments;
|
||||
final List<DocDirectiveNamedArgument> namedArguments;
|
||||
|
||||
DocDirective({
|
||||
required this.offset,
|
||||
required this.end,
|
||||
required this.nameOffset,
|
||||
required this.nameEnd,
|
||||
required this.name,
|
||||
required this.positionalArguments,
|
||||
required this.namedArguments,
|
||||
});
|
||||
}
|
||||
|
||||
/// An argument in a doc directive. See [DocDirective] for their syntax.
|
||||
@experimental
|
||||
sealed class DocDirectiveArgument {
|
||||
/// The offset of the start of the argument, from the beginning of the
|
||||
/// compilation unit.
|
||||
final int offset;
|
||||
|
||||
/// The offset just after the end of the argument, from the beginning of the
|
||||
/// compilation unit.
|
||||
final int end;
|
||||
|
||||
/// The value of the argument.
|
||||
final String value;
|
||||
|
||||
DocDirectiveArgument({
|
||||
required this.offset,
|
||||
required this.end,
|
||||
required this.value,
|
||||
});
|
||||
}
|
||||
|
||||
enum DocDirectiveName {
|
||||
/// The name of a [DocDirective] declaring an embedded video with HTML video
|
||||
/// controls.
|
||||
///
|
||||
/// This directive has three required arguments: the width, the height, and
|
||||
/// the URL. A named 'id' argument can also be given. For example:
|
||||
///
|
||||
/// `{@animation 600 400 https://www.example.com/example.mp4 id=video1}`
|
||||
animation,
|
||||
|
||||
/// The name of 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}`
|
||||
youtube;
|
||||
}
|
||||
|
||||
/// A named argument in a doc directive. See [DocDirective] for their syntax.
|
||||
@experimental
|
||||
final class DocDirectiveNamedArgument extends DocDirectiveArgument {
|
||||
/// The name of the argument.
|
||||
final String name;
|
||||
|
||||
DocDirectiveNamedArgument({
|
||||
required super.offset,
|
||||
required super.end,
|
||||
required this.name,
|
||||
required super.value,
|
||||
});
|
||||
}
|
||||
|
||||
/// A positional argument in a doc directive. See [DocDirective] for their
|
||||
/// syntax.
|
||||
@experimental
|
||||
final class DocDirectivePositionalArgument extends DocDirectiveArgument {
|
||||
DocDirectivePositionalArgument({
|
||||
required super.offset,
|
||||
required super.end,
|
||||
required super.value,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -84,32 +162,3 @@ final class MdCodeBlockLine {
|
|||
|
||||
MdCodeBlockLine({required this.offset, required this.length});
|
||||
}
|
||||
|
||||
/// 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}`
|
||||
@experimental
|
||||
final class YouTubeDocDirective extends DocDirective {
|
||||
final int? widthOffset;
|
||||
final int? widthEnd;
|
||||
final int? heightOffset;
|
||||
final int? heightEnd;
|
||||
final int? urlOffset;
|
||||
final int? urlEnd;
|
||||
|
||||
YouTubeDocDirective({
|
||||
required super.offset,
|
||||
required super.end,
|
||||
required super.nameOffset,
|
||||
required super.nameEnd,
|
||||
required this.widthOffset,
|
||||
required this.widthEnd,
|
||||
required this.heightOffset,
|
||||
required this.heightEnd,
|
||||
required this.urlOffset,
|
||||
required this.urlEnd,
|
||||
});
|
||||
}
|
||||
|
|
|
@ -6055,12 +6055,56 @@ class WarningCode extends AnalyzerErrorCode {
|
|||
hasPublishedDocs: true,
|
||||
);
|
||||
|
||||
/// Parameters:
|
||||
/// 0: the actual number of arguments
|
||||
/// 1: the expected number of arguments
|
||||
static const WarningCode DOC_DIRECTIVE_HAS_EXTRA_ARGUMENTS = WarningCode(
|
||||
'DOC_DIRECTIVE_HAS_EXTRA_ARGUMENTS',
|
||||
"Doc directive has '{0}' arguments, but only '{1}' are expected.",
|
||||
correctionMessage: "Try removing the extra arguments.",
|
||||
);
|
||||
|
||||
static const WarningCode DOC_DIRECTIVE_MISSING_CLOSING_BRACE = WarningCode(
|
||||
'DOC_DIRECTIVE_MISSING_CLOSING_BRACE',
|
||||
"Doc directive is missing a closing curly brace ('}').",
|
||||
correctionMessage: "Try closing the directive with a curly brace.",
|
||||
);
|
||||
|
||||
/// Parameters:
|
||||
/// 0: the name of the doc directive
|
||||
/// 1: the name of the missing argument
|
||||
static const WarningCode DOC_DIRECTIVE_MISSING_ONE_ARGUMENT = WarningCode(
|
||||
'DOC_DIRECTIVE_MISSING_ARGUMENT',
|
||||
"The '{0}' directive is missing a '{1}' argument.",
|
||||
correctionMessage: "Try adding a '{1}' argument before the closing '}'.",
|
||||
uniqueName: 'DOC_DIRECTIVE_MISSING_ONE_ARGUMENT',
|
||||
);
|
||||
|
||||
/// Parameters:
|
||||
/// 0: the name of the doc directive
|
||||
/// 1: the name of the first missing argument
|
||||
/// 2: the name of the second missing argument
|
||||
/// 3: the name of the third missing argument
|
||||
static const WarningCode DOC_DIRECTIVE_MISSING_THREE_ARGUMENTS = WarningCode(
|
||||
'DOC_DIRECTIVE_MISSING_ARGUMENT',
|
||||
"The '{0}' directive is missing a '{1}', a '{2}', and a '{3}' argument.",
|
||||
correctionMessage:
|
||||
"Try adding the missing arguments before the closing '}'.",
|
||||
uniqueName: 'DOC_DIRECTIVE_MISSING_THREE_ARGUMENTS',
|
||||
);
|
||||
|
||||
/// Parameters:
|
||||
/// 0: the name of the doc directive
|
||||
/// 1: the name of the first missing argument
|
||||
/// 2: the name of the second missing argument
|
||||
static const WarningCode DOC_DIRECTIVE_MISSING_TWO_ARGUMENTS = WarningCode(
|
||||
'DOC_DIRECTIVE_MISSING_ARGUMENT',
|
||||
"The '{0}' directive is missing a '{1}' and a '{2}' argument.",
|
||||
correctionMessage:
|
||||
"Try adding the missing arguments before the closing '}'.",
|
||||
uniqueName: 'DOC_DIRECTIVE_MISSING_TWO_ARGUMENTS',
|
||||
);
|
||||
|
||||
static const WarningCode DOC_IMPORT_CANNOT_BE_DEFERRED = WarningCode(
|
||||
'DOC_IMPORT_CANNOT_BE_DEFERRED',
|
||||
"Doc imports can't be deferred.",
|
||||
|
@ -6073,24 +6117,6 @@ class WarningCode extends AnalyzerErrorCode {
|
|||
correctionMessage: "Try removing the configurations.",
|
||||
);
|
||||
|
||||
static const WarningCode DOC_YOUTUBE_DIRECTIVE_MISSING_HEIGHT = WarningCode(
|
||||
'DOC_YOUTUBE_DIRECTIVE_MISSING_HEIGHT',
|
||||
"YouTube directive is missing a height argument.",
|
||||
correctionMessage: "Try adding a height argument after the width.",
|
||||
);
|
||||
|
||||
static const WarningCode DOC_YOUTUBE_DIRECTIVE_MISSING_URL = WarningCode(
|
||||
'DOC_YOUTUBE_DIRECTIVE_MISSING_URL',
|
||||
"YouTube directive is missing a URL argument.",
|
||||
correctionMessage: "Try adding a URL after the width and height.",
|
||||
);
|
||||
|
||||
static const WarningCode DOC_YOUTUBE_DIRECTIVE_MISSING_WIDTH = WarningCode(
|
||||
'DOC_YOUTUBE_DIRECTIVE_MISSING_WIDTH',
|
||||
"YouTube directive is missing a width argument.",
|
||||
correctionMessage: "Try adding a width argument after '@youtube'.",
|
||||
);
|
||||
|
||||
/// Duplicate exports.
|
||||
///
|
||||
/// No parameters.
|
||||
|
|
|
@ -13,45 +13,106 @@ class DocCommentVerifier {
|
|||
DocCommentVerifier(this._errorReporter);
|
||||
|
||||
void docDirective(DocDirective docDirective) {
|
||||
switch (docDirective) {
|
||||
case YouTubeDocDirective():
|
||||
var widthOffset = docDirective.widthOffset;
|
||||
var widthEnd = docDirective.widthEnd;
|
||||
if (widthOffset == null || widthEnd == null) {
|
||||
var positionalArgumentCount = docDirective.positionalArguments.length;
|
||||
switch (docDirective.name) {
|
||||
case DocDirectiveName.animation:
|
||||
if (positionalArgumentCount == 0) {
|
||||
_errorReporter.reportErrorForOffset(
|
||||
WarningCode.DOC_YOUTUBE_DIRECTIVE_MISSING_WIDTH,
|
||||
WarningCode.DOC_DIRECTIVE_MISSING_THREE_ARGUMENTS,
|
||||
docDirective.offset,
|
||||
docDirective.end - docDirective.offset,
|
||||
['animation', 'width', 'height', 'url'],
|
||||
);
|
||||
return;
|
||||
} else {
|
||||
// TODO: Validate width.
|
||||
}
|
||||
|
||||
var heightOffset = docDirective.heightOffset;
|
||||
var heightEnd = docDirective.heightEnd;
|
||||
if (heightOffset == null || heightEnd == null) {
|
||||
if (positionalArgumentCount == 1) {
|
||||
_errorReporter.reportErrorForOffset(
|
||||
WarningCode.DOC_YOUTUBE_DIRECTIVE_MISSING_HEIGHT,
|
||||
WarningCode.DOC_DIRECTIVE_MISSING_TWO_ARGUMENTS,
|
||||
docDirective.offset,
|
||||
docDirective.end - docDirective.offset,
|
||||
['animation', 'width', 'height'],
|
||||
);
|
||||
return;
|
||||
} else {
|
||||
// TODO(srawlins): Validate height.
|
||||
}
|
||||
|
||||
var urlOffset = docDirective.urlOffset;
|
||||
var urlEnd = docDirective.urlEnd;
|
||||
if (urlOffset == null || urlEnd == null) {
|
||||
if (positionalArgumentCount == 2) {
|
||||
_errorReporter.reportErrorForOffset(
|
||||
WarningCode.DOC_YOUTUBE_DIRECTIVE_MISSING_URL,
|
||||
WarningCode.DOC_DIRECTIVE_MISSING_ONE_ARGUMENT,
|
||||
docDirective.offset,
|
||||
docDirective.end - docDirective.offset,
|
||||
['animation', 'url'],
|
||||
);
|
||||
} else {
|
||||
// TODO(srawlins): Validate URL.
|
||||
}
|
||||
|
||||
if (positionalArgumentCount > 3) {
|
||||
var errorOffset = docDirective.positionalArguments[3].offset;
|
||||
var errorLength =
|
||||
docDirective.positionalArguments.last.end - errorOffset;
|
||||
_errorReporter.reportErrorForOffset(
|
||||
WarningCode.DOC_DIRECTIVE_HAS_EXTRA_ARGUMENTS,
|
||||
errorOffset,
|
||||
errorLength,
|
||||
[positionalArgumentCount, 3],
|
||||
);
|
||||
}
|
||||
|
||||
// TODO(srawlins): Validate `@animation` named arguments (and report
|
||||
// unknown).
|
||||
|
||||
case DocDirectiveName.youtube:
|
||||
if (positionalArgumentCount == 0) {
|
||||
_errorReporter.reportErrorForOffset(
|
||||
WarningCode.DOC_DIRECTIVE_MISSING_THREE_ARGUMENTS,
|
||||
docDirective.offset,
|
||||
docDirective.end - docDirective.offset,
|
||||
['youtube', 'width', 'height', 'url'],
|
||||
);
|
||||
return;
|
||||
} else {
|
||||
// TODO: Validate width.
|
||||
}
|
||||
|
||||
if (positionalArgumentCount == 1) {
|
||||
_errorReporter.reportErrorForOffset(
|
||||
WarningCode.DOC_DIRECTIVE_MISSING_TWO_ARGUMENTS,
|
||||
docDirective.offset,
|
||||
docDirective.end - docDirective.offset,
|
||||
['youtube', 'width', 'height'],
|
||||
);
|
||||
return;
|
||||
} else {
|
||||
// TODO(srawlins): Validate height.
|
||||
}
|
||||
|
||||
if (positionalArgumentCount == 2) {
|
||||
_errorReporter.reportErrorForOffset(
|
||||
WarningCode.DOC_DIRECTIVE_MISSING_ONE_ARGUMENT,
|
||||
docDirective.offset,
|
||||
docDirective.end - docDirective.offset,
|
||||
['youtube', 'url'],
|
||||
);
|
||||
} else {
|
||||
// TODO(srawlins): Validate URL.
|
||||
}
|
||||
|
||||
if (positionalArgumentCount > 3) {
|
||||
var errorOffset = docDirective.positionalArguments[3].offset;
|
||||
var errorLength =
|
||||
docDirective.positionalArguments.last.end - errorOffset;
|
||||
_errorReporter.reportErrorForOffset(
|
||||
WarningCode.DOC_DIRECTIVE_HAS_EXTRA_ARGUMENTS,
|
||||
errorOffset,
|
||||
errorLength,
|
||||
[positionalArgumentCount, 3],
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -950,12 +950,13 @@ const List<ErrorCode> errorCodeValues = [
|
|||
WarningCode.DEPRECATED_IMPLEMENTS_FUNCTION,
|
||||
WarningCode.DEPRECATED_MIXIN_FUNCTION,
|
||||
WarningCode.DEPRECATED_NEW_IN_COMMENT_REFERENCE,
|
||||
WarningCode.DOC_DIRECTIVE_HAS_EXTRA_ARGUMENTS,
|
||||
WarningCode.DOC_DIRECTIVE_MISSING_CLOSING_BRACE,
|
||||
WarningCode.DOC_DIRECTIVE_MISSING_ONE_ARGUMENT,
|
||||
WarningCode.DOC_DIRECTIVE_MISSING_THREE_ARGUMENTS,
|
||||
WarningCode.DOC_DIRECTIVE_MISSING_TWO_ARGUMENTS,
|
||||
WarningCode.DOC_IMPORT_CANNOT_BE_DEFERRED,
|
||||
WarningCode.DOC_IMPORT_CANNOT_HAVE_CONFIGURATIONS,
|
||||
WarningCode.DOC_YOUTUBE_DIRECTIVE_MISSING_HEIGHT,
|
||||
WarningCode.DOC_YOUTUBE_DIRECTIVE_MISSING_URL,
|
||||
WarningCode.DOC_YOUTUBE_DIRECTIVE_MISSING_WIDTH,
|
||||
WarningCode.DUPLICATE_EXPORT,
|
||||
WarningCode.DUPLICATE_HIDDEN_NAME,
|
||||
WarningCode.DUPLICATE_IGNORE,
|
||||
|
|
|
@ -46,6 +46,8 @@ int _findCommentReferenceEnd(String comment, int index, int end) {
|
|||
return index;
|
||||
}
|
||||
|
||||
bool _isEqualSign(int character) => character == 0x3D /* '=' */;
|
||||
|
||||
/// Given that we have just found bracketed text within the given [comment],
|
||||
/// looks to see whether that text is (a) followed by a parenthesized link
|
||||
/// address, (b) followed by a colon, or (c) followed by optional whitespace
|
||||
|
@ -77,6 +79,22 @@ bool _isLinkText(String comment, int rightIndex) {
|
|||
return ch == 0x5B;
|
||||
}
|
||||
|
||||
bool _isRightCurlyBrace(int character) => character == 0x7D /* '}' */;
|
||||
|
||||
/// Reads past any opening whitespace in [content], returning the index after
|
||||
/// the last whitespace character.
|
||||
int _readWhitespace(String content, [int index = 0]) {
|
||||
var length = content.length;
|
||||
if (index >= length) return index;
|
||||
while (isWhitespace(content.codeUnitAt(index))) {
|
||||
index++;
|
||||
if (index >= length) {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
/// A class which temporarily stores data for a [CommentType.DOCUMENTATION]-type
|
||||
/// [Comment], which is ultimately built with [build].
|
||||
class DocCommentBuilder {
|
||||
|
@ -146,8 +164,6 @@ class DocCommentBuilder {
|
|||
}
|
||||
}
|
||||
|
||||
bool _isRightCurlyBrace(int character) => character == 0x7D /* '}' */;
|
||||
|
||||
/// Parses a documentation comment.
|
||||
///
|
||||
/// All parsed data is added to the fields on this builder.
|
||||
|
@ -255,17 +271,26 @@ class DocCommentBuilder {
|
|||
index = _readWhitespace(content, index);
|
||||
|
||||
var name = content.substring(nameIndex, nameEnd);
|
||||
if (name == 'youtube') {
|
||||
_parseYouTubeDirective(
|
||||
offset: startOffset,
|
||||
nameOffset: _characterSequence._offset + nameIndex,
|
||||
nameEnd: _characterSequence._offset + nameEnd,
|
||||
index: index,
|
||||
content: content,
|
||||
);
|
||||
|
||||
var parser = _DirectiveParser._(
|
||||
offset: startOffset,
|
||||
contentOffset: _characterSequence._offset,
|
||||
nameOffset: _characterSequence._offset + nameIndex,
|
||||
nameEnd: _characterSequence._offset + nameEnd,
|
||||
content: content,
|
||||
index: index,
|
||||
errorReporter: _errorReporter,
|
||||
);
|
||||
|
||||
if (name == 'animation') {
|
||||
_docDirectives.add(parser.animationDirective());
|
||||
return true;
|
||||
}
|
||||
// TODO(srawlins): Handle other doc directives: animation, api?,
|
||||
if (name == 'youtube') {
|
||||
_docDirectives.add(parser.youTubeDirective());
|
||||
return true;
|
||||
}
|
||||
// TODO(srawlins): Handle other doc directives: api?,
|
||||
// canonicalFor?, category, example, image, macro, samples?, subCategory.
|
||||
// TODO(srawlins): Handle block doc directives: inject-html, template.
|
||||
// TODO(srawlins): Handle unknown (misspelled?) directive.
|
||||
|
@ -603,115 +628,6 @@ class DocCommentBuilder {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Parses a YouTube doc directive, returning whether one was successfully
|
||||
/// parsed.
|
||||
void _parseYouTubeDirective({
|
||||
required int offset,
|
||||
required int nameOffset,
|
||||
required int nameEnd,
|
||||
required String content,
|
||||
required int index,
|
||||
}) {
|
||||
var contentOffset = _characterSequence._offset;
|
||||
var length = content.length;
|
||||
|
||||
int? end;
|
||||
if (index == length) {
|
||||
end = offset + index;
|
||||
}
|
||||
|
||||
(int?, int?) parseArgument() {
|
||||
int? argumentOffset;
|
||||
int? argumentEnd;
|
||||
if (end == null) {
|
||||
if (_isRightCurlyBrace(content.codeUnitAt(index))) {
|
||||
index++;
|
||||
end = offset + index;
|
||||
} else {
|
||||
argumentOffset = contentOffset + index;
|
||||
index = _readDirectiveArgument(content, index);
|
||||
argumentEnd = contentOffset + index;
|
||||
index = _readWhitespace(content, index);
|
||||
}
|
||||
}
|
||||
if (end == null && index == length) {
|
||||
// We've hit EOL without closing brace.
|
||||
end = offset + index;
|
||||
_errorReporter?.reportErrorForOffset(
|
||||
WarningCode.DOC_DIRECTIVE_MISSING_CLOSING_BRACE,
|
||||
index - 1,
|
||||
1,
|
||||
);
|
||||
}
|
||||
return (argumentOffset, argumentEnd);
|
||||
}
|
||||
|
||||
var (widthOffset, widthEnd) = parseArgument();
|
||||
var (heightOffset, heightEnd) = parseArgument();
|
||||
var (urlOffset, urlEnd) = parseArgument();
|
||||
if (end == null) {
|
||||
// Read until the closing delimiter, `}` is found.
|
||||
if (index >= length) {
|
||||
// Reached EOL without finding a `}`.
|
||||
end = offset + length;
|
||||
}
|
||||
while (!_isRightCurlyBrace(content.codeUnitAt(index))) {
|
||||
index++;
|
||||
if (index >= length) {
|
||||
// Reached EOL without finding a `}`.
|
||||
// TODO(srawlins): Minimal recovery and error-reporting for extra
|
||||
// arguments.
|
||||
return;
|
||||
}
|
||||
}
|
||||
index++;
|
||||
end = offset + index;
|
||||
}
|
||||
_docDirectives.add(YouTubeDocDirective(
|
||||
offset: offset,
|
||||
end: end!,
|
||||
nameOffset: nameOffset,
|
||||
nameEnd: nameEnd,
|
||||
widthOffset: widthOffset,
|
||||
widthEnd: widthEnd,
|
||||
heightOffset: heightOffset,
|
||||
heightEnd: heightEnd,
|
||||
urlOffset: urlOffset,
|
||||
urlEnd: urlEnd,
|
||||
));
|
||||
}
|
||||
|
||||
/// Reads past any directive argument text in [content], returning the index
|
||||
/// after the last character.
|
||||
///
|
||||
/// A directive argument is a sequence of non-whitespace,
|
||||
/// non-right-curly-brace characters.
|
||||
int _readDirectiveArgument(String content, int index) {
|
||||
var length = content.length;
|
||||
//if (index >= length) return index;
|
||||
while (index < length) {
|
||||
var character = content.codeUnitAt(index);
|
||||
if (isWhitespace(character)) return index;
|
||||
if (_isRightCurlyBrace(character)) return index;
|
||||
index++;
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
/// Reads past any opening whitespace in [content], returning the index after
|
||||
/// the last whitespace character.
|
||||
int _readWhitespace(String content, [int index = 0]) {
|
||||
var length = content.length;
|
||||
if (index >= length) return index;
|
||||
while (isWhitespace(content.codeUnitAt(index))) {
|
||||
index++;
|
||||
if (index >= length) {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
return index;
|
||||
}
|
||||
}
|
||||
|
||||
class DocImportStringScanner extends StringScanner {
|
||||
|
@ -892,6 +808,229 @@ class _CharacterSequenceFromSingleLineComment implements _CharacterSequence {
|
|||
}
|
||||
}
|
||||
|
||||
final class _DirectiveParser {
|
||||
/// The offset in the compilation unit at which [content] is found.
|
||||
final int _offset;
|
||||
|
||||
/// The offset of the opening `{@` of this directive.
|
||||
final int _contentOffset;
|
||||
|
||||
/// The offset in the compilation unit at which this directive's name is
|
||||
/// found.
|
||||
final int nameOffset;
|
||||
|
||||
/// The offset in the compilation unit immediately after the end of this
|
||||
/// directive's name.
|
||||
final int nameEnd;
|
||||
|
||||
/// The content of the doc comment.
|
||||
final String content;
|
||||
|
||||
/// The length of [content].
|
||||
final int _length;
|
||||
|
||||
final ErrorReporter? _errorReporter;
|
||||
|
||||
/// The current position in [content].
|
||||
int index;
|
||||
|
||||
/// The index immediately after the end of this directve.
|
||||
///
|
||||
/// This is the index immediately following the `}` if there is one, or
|
||||
/// the index after the end of the last character in [content], if not.
|
||||
int? _end;
|
||||
|
||||
_DirectiveParser._({
|
||||
required int offset,
|
||||
required int contentOffset,
|
||||
required this.nameOffset,
|
||||
required this.nameEnd,
|
||||
required this.content,
|
||||
required this.index,
|
||||
required ErrorReporter? errorReporter,
|
||||
}) : _contentOffset = contentOffset,
|
||||
_offset = offset,
|
||||
_length = content.length,
|
||||
_errorReporter = errorReporter;
|
||||
|
||||
/// Parses an animation doc directive.
|
||||
DocDirective animationDirective() {
|
||||
if (index == _length) {
|
||||
_end = _offset + index;
|
||||
}
|
||||
var (positionalArguments, namedArguments) = _parseArguments();
|
||||
_readClosingCurlyBrace();
|
||||
return DocDirective(
|
||||
offset: _offset,
|
||||
end: _end!,
|
||||
nameOffset: nameOffset,
|
||||
nameEnd: nameEnd,
|
||||
name: DocDirectiveName.animation,
|
||||
positionalArguments: positionalArguments,
|
||||
namedArguments: namedArguments,
|
||||
);
|
||||
}
|
||||
|
||||
/// Parses a YouTube doc directive.
|
||||
DocDirective youTubeDirective() {
|
||||
if (index == _length) {
|
||||
_end = _offset + index;
|
||||
}
|
||||
var (positionalArguments, namedArguments) = _parseArguments();
|
||||
_readClosingCurlyBrace();
|
||||
return DocDirective(
|
||||
offset: _offset,
|
||||
end: _end!,
|
||||
nameOffset: nameOffset,
|
||||
nameEnd: nameEnd,
|
||||
name: DocDirectiveName.youtube,
|
||||
positionalArguments: positionalArguments,
|
||||
namedArguments: namedArguments,
|
||||
);
|
||||
}
|
||||
|
||||
/// Parses and returns a positional or named doc directive argument.
|
||||
DocDirectiveArgument _parseArgument() {
|
||||
// An equal sign is parsed as a delimiter between a named argument's name
|
||||
// and value only if the name consists only of alphanumeric characters.
|
||||
// If other characters have been read, then the equal sign is treated just
|
||||
// as another character in the argument, allowing for unquoted arguments
|
||||
// like YouTube URLs.
|
||||
// TODO(srawlins): This rule is rather arbitrary; it would probably be
|
||||
// better to require positional-arguments-with-equal-signs to be surrounded
|
||||
// with quotes or something similar.
|
||||
var onlyLettersOrDigits = true;
|
||||
var argumentStart = index;
|
||||
while (index < _length) {
|
||||
var character = content.codeUnitAt(index);
|
||||
if (isWhitespace(character)) break;
|
||||
if (_isRightCurlyBrace(character)) break;
|
||||
if (_isEqualSign(character) && onlyLettersOrDigits) {
|
||||
// This is a valid named argument name/value delimiter.
|
||||
var argumentName = content.substring(argumentStart, index);
|
||||
index++;
|
||||
if (index == _length) {
|
||||
// Equal sign is followed by EOL.
|
||||
return DocDirectiveNamedArgument(
|
||||
offset: _contentOffset + argumentStart,
|
||||
end: _contentOffset + index,
|
||||
name: argumentName,
|
||||
// Recover an unterminated named argument as having an empty
|
||||
// argument value.
|
||||
value: '',
|
||||
);
|
||||
}
|
||||
var argumentValueStart = index;
|
||||
while (index < _length) {
|
||||
var character = content.codeUnitAt(index);
|
||||
if (isWhitespace(character)) break;
|
||||
if (_isRightCurlyBrace(character)) break;
|
||||
index++;
|
||||
}
|
||||
return DocDirectiveNamedArgument(
|
||||
offset: _contentOffset + argumentStart,
|
||||
end: _contentOffset + index,
|
||||
name: argumentName,
|
||||
value: content.substring(argumentValueStart, index),
|
||||
);
|
||||
}
|
||||
if (!isLetterOrDigit(character)) {
|
||||
onlyLettersOrDigits = false;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
var argumentValue = content.substring(argumentStart, index);
|
||||
return DocDirectivePositionalArgument(
|
||||
offset: _contentOffset + argumentStart,
|
||||
end: _contentOffset + index,
|
||||
value: argumentValue,
|
||||
);
|
||||
}
|
||||
|
||||
/// Parses both positional and named doc directive arguments until either a
|
||||
/// closing curly brace or EOL are reached.
|
||||
///
|
||||
/// Returns a record containing the list of positional arguments and a list of
|
||||
/// named arguments.
|
||||
///
|
||||
/// Reports a warning if EOL is reached before a closing curly brace is found.
|
||||
(List<DocDirectivePositionalArgument>, List<DocDirectiveNamedArgument>)
|
||||
_parseArguments() {
|
||||
if (_end != null) return (const [], const []);
|
||||
var positionalArguments = <DocDirectivePositionalArgument>[];
|
||||
var namedArguments = <DocDirectiveNamedArgument>[];
|
||||
while (index < _length) {
|
||||
if (_isRightCurlyBrace(content.codeUnitAt(index))) {
|
||||
index++;
|
||||
_end = _offset + index;
|
||||
return (positionalArguments, namedArguments);
|
||||
}
|
||||
var argument = _parseArgument();
|
||||
// Remove when https://github.com/dart-lang/linter/issues/4361 is closed.
|
||||
// ignore: unnecessary_parenthesis
|
||||
(switch (argument) {
|
||||
DocDirectivePositionalArgument() => positionalArguments.add(argument),
|
||||
DocDirectiveNamedArgument() => namedArguments.add(argument),
|
||||
});
|
||||
index = _readWhitespace(content, index);
|
||||
}
|
||||
|
||||
// We've hit EOL without closing brace.
|
||||
_end = _offset + index;
|
||||
_errorReporter?.reportErrorForOffset(
|
||||
WarningCode.DOC_DIRECTIVE_MISSING_CLOSING_BRACE,
|
||||
_offset + index - 1,
|
||||
1,
|
||||
);
|
||||
return (positionalArguments, namedArguments);
|
||||
}
|
||||
|
||||
/// Reads the closing curly brace (`}`) expected at the end of a doc
|
||||
/// directive.
|
||||
///
|
||||
/// Reports any extra, unexpected arguments that are found.
|
||||
///
|
||||
/// Reports a warning if EOL is reached before a closing curly brace is found.
|
||||
void _readClosingCurlyBrace() {
|
||||
if (_end != null) {
|
||||
return;
|
||||
}
|
||||
if (index >= _length) {
|
||||
// No extra arguments or closing brace.
|
||||
_end = _offset + _length;
|
||||
return;
|
||||
}
|
||||
if (_isRightCurlyBrace(content.codeUnitAt(index))) {
|
||||
index++;
|
||||
_end = _offset + index;
|
||||
return;
|
||||
}
|
||||
|
||||
var extraArgumentsOffset = _offset + index;
|
||||
|
||||
while (!_isRightCurlyBrace(content.codeUnitAt(index))) {
|
||||
index++;
|
||||
if (index == _length) {
|
||||
// Found extra arguments and no closing brace.
|
||||
_errorReporter?.reportErrorForOffset(
|
||||
WarningCode.DOC_DIRECTIVE_MISSING_CLOSING_BRACE,
|
||||
_offset + index - 1,
|
||||
1,
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
var errorLength = _offset + index - extraArgumentsOffset;
|
||||
_errorReporter?.reportErrorForOffset(
|
||||
WarningCode.DOC_DIRECTIVE_HAS_EXTRA_ARGUMENTS,
|
||||
extraArgumentsOffset,
|
||||
errorLength,
|
||||
);
|
||||
_end = _offset + index;
|
||||
}
|
||||
}
|
||||
|
||||
/// A canonicalized store of fenced code block info strings.
|
||||
///
|
||||
/// Across many doc comments with many fenced code blocks, there are likely
|
||||
|
|
|
@ -22124,21 +22124,48 @@ WarningCode:
|
|||
problemMessage: "Doc imports can't have configurations."
|
||||
correctionMessage: Try removing the configurations.
|
||||
hasPublishedDocs: false
|
||||
DOC_DIRECTIVE_HAS_EXTRA_ARGUMENTS:
|
||||
problemMessage: "Doc directive has '{0}' arguments, but only '{1}' are expected."
|
||||
correctionMessage: Try removing the extra arguments.
|
||||
hasPublishedDocs: false
|
||||
comment: |-
|
||||
Parameters:
|
||||
0: the actual number of arguments
|
||||
1: the expected number of arguments
|
||||
DOC_DIRECTIVE_MISSING_CLOSING_BRACE:
|
||||
problemMessage: "Doc directive is missing a closing curly brace ('}')."
|
||||
correctionMessage: "Try closing the directive with a curly brace."
|
||||
hasPublishedDocs: false
|
||||
DOC_YOUTUBE_DIRECTIVE_MISSING_HEIGHT:
|
||||
problemMessage: YouTube directive is missing a height argument.
|
||||
correctionMessage: Try adding a height argument after the width.
|
||||
DOC_YOUTUBE_DIRECTIVE_MISSING_URL:
|
||||
problemMessage: YouTube directive is missing a URL argument.
|
||||
correctionMessage: Try adding a URL after the width and height.
|
||||
DOC_DIRECTIVE_MISSING_ONE_ARGUMENT:
|
||||
sharedName: DOC_DIRECTIVE_MISSING_ARGUMENT
|
||||
problemMessage: "The '{0}' directive is missing a '{1}' argument."
|
||||
correctionMessage: "Try adding a '{1}' argument before the closing '}'."
|
||||
hasPublishedDocs: false
|
||||
DOC_YOUTUBE_DIRECTIVE_MISSING_WIDTH:
|
||||
problemMessage: YouTube directive is missing a width argument.
|
||||
correctionMessage: "Try adding a width argument after '@youtube'."
|
||||
comment: |-
|
||||
Parameters:
|
||||
0: the name of the doc directive
|
||||
1: the name of the missing argument
|
||||
DOC_DIRECTIVE_MISSING_THREE_ARGUMENTS:
|
||||
sharedName: DOC_DIRECTIVE_MISSING_ARGUMENT
|
||||
problemMessage: "The '{0}' directive is missing a '{1}', a '{2}', and a '{3}' argument."
|
||||
correctionMessage: "Try adding the missing arguments before the closing '}'."
|
||||
hasPublishedDocs: false
|
||||
comment: |-
|
||||
Parameters:
|
||||
0: the name of the doc directive
|
||||
1: the name of the first missing argument
|
||||
2: the name of the second missing argument
|
||||
3: the name of the third missing argument
|
||||
DOC_DIRECTIVE_MISSING_TWO_ARGUMENTS:
|
||||
sharedName: DOC_DIRECTIVE_MISSING_ARGUMENT
|
||||
problemMessage: "The '{0}' directive is missing a '{1}' and a '{2}' argument."
|
||||
correctionMessage: "Try adding the missing arguments before the closing '}'."
|
||||
hasPublishedDocs: false
|
||||
comment: |-
|
||||
Parameters:
|
||||
0: the name of the doc directive
|
||||
1: the name of the first missing argument
|
||||
2: the name of the second missing argument
|
||||
DUPLICATE_EXPORT:
|
||||
problemMessage: Duplicate export.
|
||||
correctionMessage: Try removing all but one export of the library.
|
||||
|
|
|
@ -16,6 +16,97 @@ main() {
|
|||
|
||||
@reflectiveTest
|
||||
class DocCommentParserTest extends ParserDiagnosticsTest {
|
||||
test_animationDirective_namedArgument_blankValue() {
|
||||
final parseResult = parseStringWithErrors(r'''
|
||||
int x = 0;
|
||||
|
||||
/// Text.
|
||||
/// {@animation 600 400 http://google.com arg=}
|
||||
class A {}
|
||||
''');
|
||||
parseResult.assertNoErrors();
|
||||
|
||||
final node = parseResult.findNode.comment('animation');
|
||||
assertParsedNodeText(node, r'''
|
||||
Comment
|
||||
tokens
|
||||
/// Text.
|
||||
/// {@animation 600 400 http://google.com arg=}
|
||||
docDirectives
|
||||
DocDirective
|
||||
offset: [26, 70]
|
||||
name: [DocDirectiveName.animation]
|
||||
positionalArguments
|
||||
600
|
||||
400
|
||||
http://google.com
|
||||
namedArguments
|
||||
arg=
|
||||
''');
|
||||
}
|
||||
|
||||
test_animationDirective_namedArgument_missingClosingBrace() {
|
||||
final parseResult = parseStringWithErrors(r'''
|
||||
int x = 0;
|
||||
|
||||
/// Text.
|
||||
/// {@animation 600 400 http://google.com arg=value
|
||||
class A {}
|
||||
''');
|
||||
parseResult.assertErrors([
|
||||
error(WarningCode.DOC_DIRECTIVE_MISSING_CLOSING_BRACE, 73, 1),
|
||||
]);
|
||||
|
||||
final node = parseResult.findNode.comment('animation');
|
||||
assertParsedNodeText(node, r'''
|
||||
Comment
|
||||
tokens
|
||||
/// Text.
|
||||
/// {@animation 600 400 http://google.com arg=value
|
||||
docDirectives
|
||||
DocDirective
|
||||
offset: [26, 74]
|
||||
name: [DocDirectiveName.animation]
|
||||
positionalArguments
|
||||
600
|
||||
400
|
||||
http://google.com
|
||||
namedArguments
|
||||
arg=value
|
||||
''');
|
||||
}
|
||||
|
||||
test_animationDirective_namedArgument_missingValueAndClosingBrace() async {
|
||||
final parseResult = parseStringWithErrors(r'''
|
||||
int x = 0;
|
||||
|
||||
/// Text.
|
||||
/// {@animation 600 400 http://google.com arg=
|
||||
class A {}
|
||||
''');
|
||||
parseResult.assertErrors([
|
||||
error(WarningCode.DOC_DIRECTIVE_MISSING_CLOSING_BRACE, 68, 1),
|
||||
]);
|
||||
|
||||
final node = parseResult.findNode.comment('animation');
|
||||
assertParsedNodeText(node, r'''
|
||||
Comment
|
||||
tokens
|
||||
/// Text.
|
||||
/// {@animation 600 400 http://google.com arg=
|
||||
docDirectives
|
||||
DocDirective
|
||||
offset: [26, 69]
|
||||
name: [DocDirectiveName.animation]
|
||||
positionalArguments
|
||||
600
|
||||
400
|
||||
http://google.com
|
||||
namedArguments
|
||||
arg=
|
||||
''');
|
||||
}
|
||||
|
||||
test_codeSpan() {
|
||||
final parseResult = parseStringWithErrors(r'''
|
||||
/// `a[i]` and [b].
|
||||
|
@ -1303,12 +1394,13 @@ Comment
|
|||
/// Text.
|
||||
/// {@youtube 600 400 http://google.com}
|
||||
docDirectives
|
||||
YouTubeDocDirective
|
||||
DocDirective
|
||||
offset: [26, 63]
|
||||
name: [28, 35]
|
||||
width: [36, 39]
|
||||
height: [40, 43]
|
||||
url: [44, 61]
|
||||
name: [DocDirectiveName.youtube]
|
||||
positionalArguments
|
||||
600
|
||||
400
|
||||
http://google.com
|
||||
''');
|
||||
}
|
||||
|
||||
|
@ -1318,7 +1410,7 @@ Comment
|
|||
class A {}
|
||||
''');
|
||||
parseResult.assertErrors([
|
||||
error(WarningCode.DOC_DIRECTIVE_MISSING_CLOSING_BRACE, 35, 1),
|
||||
error(WarningCode.DOC_DIRECTIVE_MISSING_CLOSING_BRACE, 39, 1),
|
||||
]);
|
||||
|
||||
final node = parseResult.findNode.comment('youtube');
|
||||
|
@ -1327,12 +1419,13 @@ Comment
|
|||
tokens
|
||||
/// {@youtube 600 400 http://google.com
|
||||
docDirectives
|
||||
YouTubeDocDirective
|
||||
DocDirective
|
||||
offset: [4, 40]
|
||||
name: [6, 13]
|
||||
width: [14, 17]
|
||||
height: [18, 21]
|
||||
url: [22, 39]
|
||||
name: [DocDirectiveName.youtube]
|
||||
positionalArguments
|
||||
600
|
||||
400
|
||||
http://google.com
|
||||
''');
|
||||
}
|
||||
|
||||
|
@ -1349,12 +1442,12 @@ Comment
|
|||
tokens
|
||||
/// {@youtube 600 400}
|
||||
docDirectives
|
||||
YouTubeDocDirective
|
||||
DocDirective
|
||||
offset: [4, 23]
|
||||
name: [6, 13]
|
||||
width: [14, 17]
|
||||
height: [18, 21]
|
||||
url: [null, null]
|
||||
name: [DocDirectiveName.youtube]
|
||||
positionalArguments
|
||||
600
|
||||
400
|
||||
''');
|
||||
}
|
||||
|
||||
|
@ -1371,12 +1464,11 @@ Comment
|
|||
tokens
|
||||
/// {@youtube 600}
|
||||
docDirectives
|
||||
YouTubeDocDirective
|
||||
DocDirective
|
||||
offset: [4, 19]
|
||||
name: [6, 13]
|
||||
width: [14, 17]
|
||||
height: [null, null]
|
||||
url: [null, null]
|
||||
name: [DocDirectiveName.youtube]
|
||||
positionalArguments
|
||||
600
|
||||
''');
|
||||
}
|
||||
|
||||
|
@ -1393,12 +1485,9 @@ Comment
|
|||
tokens
|
||||
/// {@youtube }
|
||||
docDirectives
|
||||
YouTubeDocDirective
|
||||
DocDirective
|
||||
offset: [4, 16]
|
||||
name: [6, 13]
|
||||
width: [null, null]
|
||||
height: [null, null]
|
||||
url: [null, null]
|
||||
name: [DocDirectiveName.youtube]
|
||||
''');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
// Copyright (c) 2023, 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/src/error/codes.g.dart';
|
||||
import 'package:test_reflective_loader/test_reflective_loader.dart';
|
||||
|
||||
import '../dart/resolution/context_collection_resolution.dart';
|
||||
|
||||
void main() {
|
||||
defineReflectiveSuite(() {
|
||||
defineReflectiveTests(DocDirectiveHasExtraArgumentsTest);
|
||||
});
|
||||
}
|
||||
|
||||
@reflectiveTest
|
||||
class DocDirectiveHasExtraArgumentsTest extends PubPackageResolutionTest {
|
||||
test_animation_hasExtraArgument() async {
|
||||
await assertErrorsInCode('''
|
||||
/// {@animation 600 400 http://google.com foo}
|
||||
class C {}
|
||||
''', [
|
||||
error(WarningCode.DOC_DIRECTIVE_HAS_EXTRA_ARGUMENTS, 42, 3),
|
||||
]);
|
||||
}
|
||||
|
||||
test_animation_noExtraArguments() async {
|
||||
await assertNoErrorsInCode('''
|
||||
/// {@animation 600 400 http://google.com}
|
||||
class C {}
|
||||
''');
|
||||
}
|
||||
|
||||
test_animation_optionalNamedArgument() async {
|
||||
await assertNoErrorsInCode('''
|
||||
/// {@animation 600 400 http://google.com id=my-id}
|
||||
class C {}
|
||||
''');
|
||||
}
|
||||
|
||||
test_youtube_hasExtraArgument() async {
|
||||
await assertErrorsInCode('''
|
||||
/// {@youtube 600 400 http://google.com foo}
|
||||
class C {}
|
||||
''', [
|
||||
error(WarningCode.DOC_DIRECTIVE_HAS_EXTRA_ARGUMENTS, 40, 3),
|
||||
]);
|
||||
}
|
||||
|
||||
test_youtube_hasExtraArgument_trailingWhitespace() async {
|
||||
await assertErrorsInCode('''
|
||||
/// {@youtube 600 400 http://google.com foo }
|
||||
class C {}
|
||||
''', [
|
||||
error(WarningCode.DOC_DIRECTIVE_HAS_EXTRA_ARGUMENTS, 40, 3),
|
||||
]);
|
||||
}
|
||||
|
||||
test_youtube_noExtraArguments() async {
|
||||
await assertNoErrorsInCode('''
|
||||
/// {@youtube 600 400 http://google.com}
|
||||
class C {}
|
||||
''');
|
||||
}
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
// Copyright (c) 2023, 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/src/error/codes.g.dart';
|
||||
import 'package:test_reflective_loader/test_reflective_loader.dart';
|
||||
|
||||
import '../dart/resolution/context_collection_resolution.dart';
|
||||
|
||||
void main() {
|
||||
defineReflectiveSuite(() {
|
||||
defineReflectiveTests(DocDirectiveMissingOneArgumentTest);
|
||||
});
|
||||
}
|
||||
|
||||
@reflectiveTest
|
||||
class DocDirectiveMissingOneArgumentTest extends PubPackageResolutionTest {
|
||||
test_animation_hasOptionalIdParameter() async {
|
||||
await assertNoErrorsInCode('''
|
||||
/// {@animation 600 400 http://google.com id=my-id}
|
||||
class C {}
|
||||
''');
|
||||
}
|
||||
|
||||
test_animation_hasThreeArguments() async {
|
||||
await assertNoErrorsInCode('''
|
||||
/// {@animation 600 400 http://google.com}
|
||||
class C {}
|
||||
''');
|
||||
}
|
||||
|
||||
test_youtube_hasThreeArguments() async {
|
||||
await assertNoErrorsInCode('''
|
||||
/// {@youtube 600 400 http://google.com}
|
||||
class C {}
|
||||
''');
|
||||
}
|
||||
|
||||
test_youtube_missingUrl() async {
|
||||
await assertErrorsInCode('''
|
||||
/// {@youtube 600 400}
|
||||
class C {}
|
||||
''', [
|
||||
error(WarningCode.DOC_DIRECTIVE_MISSING_ONE_ARGUMENT, 4, 19),
|
||||
]);
|
||||
}
|
||||
|
||||
test_youtube_missingUrl_andCurlyBrace() async {
|
||||
await assertErrorsInCode('''
|
||||
/// {@youtube 600 400
|
||||
class C {}
|
||||
''', [
|
||||
error(WarningCode.DOC_DIRECTIVE_MISSING_ONE_ARGUMENT, 4, 18),
|
||||
error(WarningCode.DOC_DIRECTIVE_MISSING_CLOSING_BRACE, 21, 1),
|
||||
]);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
// Copyright (c) 2023, 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/src/error/codes.g.dart';
|
||||
import 'package:test_reflective_loader/test_reflective_loader.dart';
|
||||
|
||||
import '../dart/resolution/context_collection_resolution.dart';
|
||||
|
||||
void main() {
|
||||
defineReflectiveSuite(() {
|
||||
defineReflectiveTests(DocDirectiveMissingThreeArgumentsTest);
|
||||
});
|
||||
}
|
||||
|
||||
@reflectiveTest
|
||||
class DocDirectiveMissingThreeArgumentsTest extends PubPackageResolutionTest {
|
||||
test_animation_hasWidth() async {
|
||||
await assertNoErrorsInCode('''
|
||||
/// {@animation 600 400 http://google.com}
|
||||
class C {}
|
||||
''');
|
||||
}
|
||||
|
||||
test_animation_missingWidth() async {
|
||||
await assertErrorsInCode('''
|
||||
/// {@animation}
|
||||
class C {}
|
||||
''', [
|
||||
error(WarningCode.DOC_DIRECTIVE_MISSING_THREE_ARGUMENTS, 4, 13),
|
||||
]);
|
||||
}
|
||||
|
||||
test_youtube_hasWidth() async {
|
||||
await assertNoErrorsInCode('''
|
||||
/// {@youtube 600 400 http://google.com}
|
||||
class C {}
|
||||
''');
|
||||
}
|
||||
|
||||
test_youtube_missingWidth() async {
|
||||
await assertErrorsInCode('''
|
||||
/// {@youtube}
|
||||
class C {}
|
||||
''', [
|
||||
error(WarningCode.DOC_DIRECTIVE_MISSING_THREE_ARGUMENTS, 4, 11),
|
||||
]);
|
||||
}
|
||||
|
||||
test_youtube_missingWidth_andCurlyBrace() async {
|
||||
await assertErrorsInCode('''
|
||||
/// {@youtube
|
||||
class C {}
|
||||
''', [
|
||||
error(WarningCode.DOC_DIRECTIVE_MISSING_THREE_ARGUMENTS, 4, 10),
|
||||
]);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
// Copyright (c) 2023, 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/src/error/codes.g.dart';
|
||||
import 'package:test_reflective_loader/test_reflective_loader.dart';
|
||||
|
||||
import '../dart/resolution/context_collection_resolution.dart';
|
||||
|
||||
void main() {
|
||||
defineReflectiveSuite(() {
|
||||
defineReflectiveTests(DocDirectiveMissingTwoArgumentsTest);
|
||||
});
|
||||
}
|
||||
|
||||
@reflectiveTest
|
||||
class DocDirectiveMissingTwoArgumentsTest extends PubPackageResolutionTest {
|
||||
test_animation_hasOptionalIdParameter() async {
|
||||
await assertNoErrorsInCode('''
|
||||
/// {@animation 600 400 http://google.com id=my-id}
|
||||
class C {}
|
||||
''');
|
||||
}
|
||||
|
||||
test_animation_hasThreeArguments() async {
|
||||
await assertNoErrorsInCode('''
|
||||
/// {@animation 600 400 http://google.com}
|
||||
class C {}
|
||||
''');
|
||||
}
|
||||
|
||||
test_animation_missingHeight() async {
|
||||
await assertErrorsInCode('''
|
||||
/// {@animation 600}
|
||||
class C {}
|
||||
''', [
|
||||
error(WarningCode.DOC_DIRECTIVE_MISSING_TWO_ARGUMENTS, 4, 17),
|
||||
]);
|
||||
}
|
||||
|
||||
test_youtube_hasThreeArguments() async {
|
||||
await assertNoErrorsInCode('''
|
||||
/// {@youtube 600 400 http://google.com}
|
||||
class C {}
|
||||
''');
|
||||
}
|
||||
|
||||
test_youtube_missingHeight() async {
|
||||
await assertErrorsInCode('''
|
||||
/// {@youtube 600}
|
||||
class C {}
|
||||
''', [
|
||||
error(WarningCode.DOC_DIRECTIVE_MISSING_TWO_ARGUMENTS, 4, 15),
|
||||
]);
|
||||
}
|
||||
|
||||
test_youtube_missingHeight_andCurlyBrace() async {
|
||||
await assertErrorsInCode('''
|
||||
/// {@youtube 600
|
||||
class C {}
|
||||
''', [
|
||||
error(WarningCode.DOC_DIRECTIVE_MISSING_TWO_ARGUMENTS, 4, 14),
|
||||
error(WarningCode.DOC_DIRECTIVE_MISSING_CLOSING_BRACE, 17, 1),
|
||||
]);
|
||||
}
|
||||
}
|
|
@ -1,43 +0,0 @@
|
|||
// Copyright (c) 2023, 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/src/error/codes.g.dart';
|
||||
import 'package:test_reflective_loader/test_reflective_loader.dart';
|
||||
|
||||
import '../dart/resolution/context_collection_resolution.dart';
|
||||
|
||||
void main() {
|
||||
defineReflectiveSuite(() {
|
||||
defineReflectiveTests(DocYouTubeDirectiveMissingHeightTest);
|
||||
});
|
||||
}
|
||||
|
||||
@reflectiveTest
|
||||
class DocYouTubeDirectiveMissingHeightTest extends PubPackageResolutionTest {
|
||||
test_hasHeight() async {
|
||||
await assertNoErrorsInCode('''
|
||||
/// {@youtube 600 400 http://google.com}
|
||||
class C {}
|
||||
''');
|
||||
}
|
||||
|
||||
test_missingHeight() async {
|
||||
await assertErrorsInCode('''
|
||||
/// {@youtube 600}
|
||||
class C {}
|
||||
''', [
|
||||
error(WarningCode.DOC_YOUTUBE_DIRECTIVE_MISSING_HEIGHT, 4, 15),
|
||||
]);
|
||||
}
|
||||
|
||||
test_missingHeight_andCurlyBrace() async {
|
||||
await assertErrorsInCode('''
|
||||
/// {@youtube 600
|
||||
class C {}
|
||||
''', [
|
||||
error(WarningCode.DOC_YOUTUBE_DIRECTIVE_MISSING_HEIGHT, 4, 14),
|
||||
error(WarningCode.DOC_DIRECTIVE_MISSING_CLOSING_BRACE, 13, 1),
|
||||
]);
|
||||
}
|
||||
}
|
|
@ -1,43 +0,0 @@
|
|||
// Copyright (c) 2023, 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/src/error/codes.g.dart';
|
||||
import 'package:test_reflective_loader/test_reflective_loader.dart';
|
||||
|
||||
import '../dart/resolution/context_collection_resolution.dart';
|
||||
|
||||
void main() {
|
||||
defineReflectiveSuite(() {
|
||||
defineReflectiveTests(DocYouTubeDirectiveMissingUrlTest);
|
||||
});
|
||||
}
|
||||
|
||||
@reflectiveTest
|
||||
class DocYouTubeDirectiveMissingUrlTest extends PubPackageResolutionTest {
|
||||
test_hasUrl() async {
|
||||
await assertNoErrorsInCode('''
|
||||
/// {@youtube 600 400 http://google.com}
|
||||
class C {}
|
||||
''');
|
||||
}
|
||||
|
||||
test_missingUrl() async {
|
||||
await assertErrorsInCode('''
|
||||
/// {@youtube 600 400}
|
||||
class C {}
|
||||
''', [
|
||||
error(WarningCode.DOC_YOUTUBE_DIRECTIVE_MISSING_URL, 4, 19),
|
||||
]);
|
||||
}
|
||||
|
||||
test_missingUrl_andCurlyBrace() async {
|
||||
await assertErrorsInCode('''
|
||||
/// {@youtube 600 400
|
||||
class C {}
|
||||
''', [
|
||||
error(WarningCode.DOC_YOUTUBE_DIRECTIVE_MISSING_URL, 4, 18),
|
||||
error(WarningCode.DOC_DIRECTIVE_MISSING_CLOSING_BRACE, 17, 1),
|
||||
]);
|
||||
}
|
||||
}
|
|
@ -1,42 +0,0 @@
|
|||
// Copyright (c) 2023, 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/src/error/codes.g.dart';
|
||||
import 'package:test_reflective_loader/test_reflective_loader.dart';
|
||||
|
||||
import '../dart/resolution/context_collection_resolution.dart';
|
||||
|
||||
void main() {
|
||||
defineReflectiveSuite(() {
|
||||
defineReflectiveTests(DocYouTubeDirectiveMissingWidthTest);
|
||||
});
|
||||
}
|
||||
|
||||
@reflectiveTest
|
||||
class DocYouTubeDirectiveMissingWidthTest extends PubPackageResolutionTest {
|
||||
test_hasWidth() async {
|
||||
await assertNoErrorsInCode('''
|
||||
/// {@youtube 600 400 http://google.com}
|
||||
class C {}
|
||||
''');
|
||||
}
|
||||
|
||||
test_missingWidth() async {
|
||||
await assertErrorsInCode('''
|
||||
/// {@youtube}
|
||||
class C {}
|
||||
''', [
|
||||
error(WarningCode.DOC_YOUTUBE_DIRECTIVE_MISSING_WIDTH, 4, 11),
|
||||
]);
|
||||
}
|
||||
|
||||
test_missingWidth_andCurlyBrace() async {
|
||||
await assertErrorsInCode('''
|
||||
/// {@youtube
|
||||
class C {}
|
||||
''', [
|
||||
error(WarningCode.DOC_YOUTUBE_DIRECTIVE_MISSING_WIDTH, 4, 10),
|
||||
]);
|
||||
}
|
||||
}
|
|
@ -167,16 +167,18 @@ import 'deprecated_implements_function_test.dart'
|
|||
import 'deprecated_member_use_test.dart' as deprecated_member_use;
|
||||
import 'deprecated_mixin_function_test.dart' as deprecated_mixin_function;
|
||||
import 'division_optimization_test.dart' as division_optimization;
|
||||
import 'doc_directive_has_extra_arguments_test.dart'
|
||||
as doc_directive_has_extra_arguments;
|
||||
import 'doc_directive_missing_one_argument_test.dart'
|
||||
as doc_directive_missing_one_argument;
|
||||
import 'doc_directive_missing_three_arguments_test.dart'
|
||||
as doc_directive_missing_three_arguments;
|
||||
import 'doc_directive_missing_two_arguments_test.dart'
|
||||
as doc_directive_missing_two_arguments;
|
||||
import 'doc_import_cannot_be_deferred_test.dart'
|
||||
as doc_import_cannot_be_deferred;
|
||||
import 'doc_import_cannot_have_configurations_test.dart'
|
||||
as doc_import_cannot_have_configurations;
|
||||
import 'doc_youtube_directive_missing_height_test.dart'
|
||||
as doc_youtube_directive_missing_height;
|
||||
import 'doc_youtube_directive_missing_url_test.dart'
|
||||
as doc_youtube_directive_missing_url;
|
||||
import 'doc_youtube_directive_missing_width_test.dart'
|
||||
as doc_youtube_directive_missing_width;
|
||||
import 'duplicate_augmentation_import_test.dart'
|
||||
as duplicate_augmentation_import;
|
||||
import 'duplicate_constructor_default_test.dart'
|
||||
|
@ -1022,11 +1024,12 @@ main() {
|
|||
deprecated_member_use.main();
|
||||
deprecated_mixin_function.main();
|
||||
division_optimization.main();
|
||||
doc_directive_has_extra_arguments.main();
|
||||
doc_directive_missing_one_argument.main();
|
||||
doc_directive_missing_three_arguments.main();
|
||||
doc_directive_missing_two_arguments.main();
|
||||
doc_import_cannot_be_deferred.main();
|
||||
doc_import_cannot_have_configurations.main();
|
||||
doc_youtube_directive_missing_height.main();
|
||||
doc_youtube_directive_missing_url.main();
|
||||
doc_youtube_directive_missing_width.main();
|
||||
duplicate_augmentation_import.main();
|
||||
duplicate_constructor_default.main();
|
||||
duplicate_constructor_name.main();
|
||||
|
|
|
@ -1708,22 +1708,29 @@ Expected parent: (${parent.runtimeType}) $parent
|
|||
}
|
||||
|
||||
void _writeDocDirective(DocDirective docDirective) {
|
||||
switch (docDirective) {
|
||||
case YouTubeDocDirective():
|
||||
_sink.writelnWithIndent('YouTubeDocDirective');
|
||||
_sink.writelnWithIndent('DocDirective');
|
||||
_sink.withIndent(() {
|
||||
_sink.writelnWithIndent(
|
||||
'offset: [${docDirective.offset}, ${docDirective.end}]');
|
||||
_sink.writelnWithIndent('name: [${docDirective.name}]');
|
||||
if (docDirective.positionalArguments.isNotEmpty) {
|
||||
_sink.writelnWithIndent('positionalArguments');
|
||||
_sink.withIndent(() {
|
||||
_sink.writelnWithIndent(
|
||||
'offset: [${docDirective.offset}, ${docDirective.end}]');
|
||||
_sink.writelnWithIndent(
|
||||
'name: [${docDirective.nameOffset}, ${docDirective.nameEnd}]');
|
||||
_sink.writelnWithIndent(
|
||||
'width: [${docDirective.widthOffset}, ${docDirective.widthEnd}]');
|
||||
_sink.writelnWithIndent(
|
||||
'height: [${docDirective.heightOffset}, ${docDirective.heightEnd}]');
|
||||
_sink.writelnWithIndent(
|
||||
'url: [${docDirective.urlOffset}, ${docDirective.urlEnd}]');
|
||||
for (var argument in docDirective.positionalArguments) {
|
||||
_sink.writelnWithIndent(argument.value);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
if (docDirective.namedArguments.isNotEmpty) {
|
||||
_sink.writelnWithIndent('namedArguments');
|
||||
_sink.withIndent(() {
|
||||
for (var argument in docDirective.namedArguments) {
|
||||
_sink.writeWithIndent(argument.name);
|
||||
_sink.writeln('=${argument.value}');
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void _writeDocImport(DocImport docImport) {
|
||||
|
|
Loading…
Reference in a new issue