decorator -> annotation

This commit is contained in:
PgBiel 2024-07-16 16:51:00 -03:00
parent 0fb417ddb6
commit fb77128423
9 changed files with 101 additions and 101 deletions

View File

@ -81,33 +81,33 @@ impl<'a> Markup<'a> {
}
node! {
DecoratorName
AnnotationName
}
impl<'a> DecoratorName<'a> {
/// Get the decorator name.
impl<'a> AnnotationName<'a> {
/// Get the annotation name.
pub fn get(self) -> &'a EcoString {
self.0.text()
}
/// Get the decorator name as a string slice.
/// Get the annotation name as a string slice.
pub fn as_str(self) -> &'a str {
self.get()
}
}
node! {
/// A decorator: `/! allow("warning")`.
Decorator
/// An annotation: `/! allow("warning")`.
Annotation
}
impl<'a> Decorator<'a> {
/// The name of the decorator, e.g. `allow`.
pub fn name(self) -> DecoratorName<'a> {
impl<'a> Annotation<'a> {
/// The name of the annotation, e.g. `allow`.
pub fn name(self) -> AnnotationName<'a> {
self.0.cast_first_match().unwrap_or_default()
}
/// The decorator's arguments. Currently, they are always strings.
/// The annotation's arguments. Currently, they are always strings.
pub fn arguments(self) -> impl DoubleEndedIterator<Item = Str<'a>> {
self.0.children().filter_map(Str::from_untyped)
}

View File

@ -286,9 +286,9 @@ pub fn highlight(node: &LinkedNode) -> Option<Tag> {
SyntaxKind::Destructuring => None,
SyntaxKind::DestructAssignment => None,
SyntaxKind::Decorator => None,
SyntaxKind::DecoratorMarker => None,
SyntaxKind::DecoratorName => None,
SyntaxKind::Annotation => None,
SyntaxKind::AnnotationMarker => None,
SyntaxKind::AnnotationName => None,
SyntaxKind::LineComment => Some(Tag::Comment),
SyntaxKind::BlockComment => Some(Tag::Comment),

View File

@ -13,8 +13,8 @@ pub enum SyntaxKind {
LineComment,
/// A block comment: `/* ... */`.
BlockComment,
/// A decorator: `// @allow("warning")`.
Decorator,
/// An annotation: `// @allow("warning")`.
Annotation,
/// The contents of a file or content block.
Markup,
@ -281,10 +281,10 @@ pub enum SyntaxKind {
/// A destructuring assignment expression: `(x, y) = (1, 2)`.
DestructAssignment,
/// A decorator's marker: `// @`.
DecoratorMarker,
/// A decorator's name: `allow`.
DecoratorName,
/// An annotation's marker: `// @`.
AnnotationMarker,
/// An annotation's name: `allow`.
AnnotationName,
}
impl SyntaxKind {
@ -366,7 +366,7 @@ impl SyntaxKind {
| Self::BlockComment
| Self::Space
| Self::Parbreak
| Self::Decorator
| Self::Annotation
)
}
@ -382,7 +382,7 @@ impl SyntaxKind {
Self::Error => "syntax error",
Self::LineComment => "line comment",
Self::BlockComment => "block comment",
Self::Decorator => "decorator",
Self::Annotation => "annotation",
Self::Markup => "markup",
Self::Text => "text",
Self::Space => "space",
@ -510,8 +510,8 @@ impl SyntaxKind {
Self::FuncReturn => "`return` expression",
Self::Destructuring => "destructuring pattern",
Self::DestructAssignment => "destructuring assignment expression",
Self::DecoratorMarker => "decorator marker",
Self::DecoratorName => "decorator name",
Self::AnnotationMarker => "annotation marker",
Self::AnnotationName => "annotation name",
}
}
}

View File

@ -103,7 +103,7 @@ impl Lexer<'_> {
/// syntax node returned might not always be a leaf, but could actually
/// come with a subtree (could be an inner node). This happens when it is
/// preferred to perform parsing at the character level instead of at the
/// token level, as seen, for example, in [`decorator`](Lexer::decorator).
/// token level, as seen, for example, in [`annotation`](Lexer::annotation).
pub fn next(&mut self) -> SyntaxNode {
if self.mode == LexMode::Raw {
let Some((kind, end)) = self.raw.pop() else {
@ -120,7 +120,7 @@ impl Lexer<'_> {
let token = match self.s.eat() {
Some(c) if is_space(c, self.mode) => self.whitespace(start, c),
Some('/') if self.s.eat_if('/') => {
return self.line_comment_or_decorator(start);
return self.line_comment_or_annotation(start);
}
Some('/') if self.s.eat_if('*') => self.block_comment(),
Some('*') if self.s.eat_if('/') => {
@ -185,14 +185,14 @@ impl Lexer<'_> {
}
}
/// Parses a decorator if the line comment has the form
/// Parses an annotation if the line comment has the form
/// `// @something`
///
/// Otherwise, parses a regular line comment.
fn line_comment_or_decorator(&mut self, start: usize) -> SyntaxNode {
fn line_comment_or_annotation(&mut self, start: usize) -> SyntaxNode {
self.s.eat_while(is_inline_whitespace);
if self.s.eat_if('@') {
return self.decorator(start);
return self.annotation(start);
}
self.s.eat_until(is_newline);
self.emit_token(SyntaxKind::LineComment, start)
@ -224,18 +224,18 @@ impl Lexer<'_> {
}
}
/// Decorator lexing and auxiliary methods.
/// Annotation lexing and auxiliary methods.
impl Lexer<'_> {
/// Lexes and parses a decorator into a complete syntax subtree.
/// Lexes and parses an annotation into a complete syntax subtree.
///
/// The lexer is fully responsible for the decorator, as it is simpler to
/// The lexer is fully responsible for the annotation, as it is simpler to
/// parse them at the character level, given they follow a very simple
/// and rigid structure, in the form
/// `/! decorator-name("string argument1", "string argument2")`
/// `/! annotation-name("string argument1", "string argument2")`
/// with optional whitespaces and comments between arguments.
fn decorator(&mut self, start: usize) -> SyntaxNode {
fn annotation(&mut self, start: usize) -> SyntaxNode {
// Start by lexing the marker.
let marker = self.emit_token(SyntaxKind::DecoratorMarker, start);
let marker = self.emit_token(SyntaxKind::AnnotationMarker, start);
let mut subtree = vec![marker];
let current_start = self.s.cursor();
@ -245,35 +245,35 @@ impl Lexer<'_> {
subtree.push(self.emit_token(SyntaxKind::Space, current_start));
}
// Lex the decorator name.
// Lex the annotation name.
let current_start = self.s.cursor();
if !self.s.eat_if(is_id_start) {
self.s.eat_until(is_newline);
subtree.push(self.emit_error("expected identifier", current_start));
// Return a single error node until the end of the decorator.
return SyntaxNode::inner(SyntaxKind::Decorator, subtree);
// Return a single error node until the end of the annotation.
return SyntaxNode::inner(SyntaxKind::Annotation, subtree);
}
let decorator_name = self.decorator_name(current_start);
subtree.push(self.emit_token(decorator_name, current_start));
let annotation_name = self.annotation_name(current_start);
subtree.push(self.emit_token(annotation_name, current_start));
// Left parenthesis before decorator arguments.
// Left parenthesis before annotation arguments.
let current_start = self.s.cursor();
if !self.s.eat_if('(') {
self.s.eat_until(is_newline);
subtree.push(self.emit_error("expected opening paren", current_start));
// Return a single error node until the end of the decorator.
return SyntaxNode::inner(SyntaxKind::Decorator, subtree);
// Return a single error node until the end of the annotation.
return SyntaxNode::inner(SyntaxKind::Annotation, subtree);
}
subtree.push(self.emit_token(SyntaxKind::LeftParen, current_start));
// Decorator arguments:
// Annotation arguments:
// Keep reading until we find a right parenthesis or newline. We have
// to check the newline before eating (through '.peek()') to ensure it
// is not considered part of the decorator.
// is not considered part of the annotation.
let mut current_start = self.s.cursor();
let mut expecting_comma = false;
let mut finished = false;
@ -284,9 +284,9 @@ impl Lexer<'_> {
SyntaxKind::Space
}
Some('/') if self.s.eat_if('/') => {
let node = self.line_comment_or_decorator(current_start);
if node.kind() == SyntaxKind::Decorator {
self.error("cannot have multiple decorators per line")
let node = self.line_comment_or_annotation(current_start);
if node.kind() == SyntaxKind::Annotation {
self.error("cannot have multiple annotations per line")
} else {
subtree.push(node);
current_start = self.s.cursor();
@ -298,7 +298,7 @@ impl Lexer<'_> {
// After we finished specifying arguments, there must only
// be whitespaces until the line ends.
self.s.eat_until(char::is_whitespace);
self.error("expected end of decorator")
self.error("expected end of annotation")
}
Some('"') if expecting_comma => {
self.s.eat_until(|c| c == ',' || c == ')' || is_newline(c));
@ -306,7 +306,7 @@ impl Lexer<'_> {
}
Some('"') => {
expecting_comma = true;
self.decorator_string()
self.annotation_string()
}
Some(',') if expecting_comma => {
expecting_comma = false;
@ -318,7 +318,7 @@ impl Lexer<'_> {
SyntaxKind::RightParen
}
Some(c) => self.error(eco_format!(
"the character '{c}' is not valid in a decorator"
"the character '{c}' is not valid in an annotation"
)),
None => break,
};
@ -334,34 +334,34 @@ impl Lexer<'_> {
subtree.push(self.emit_error("expected closing paren", self.s.cursor()));
}
SyntaxNode::inner(SyntaxKind::Decorator, subtree)
SyntaxNode::inner(SyntaxKind::Annotation, subtree)
}
/// Lexes a decorator name.
/// Lexes an annotation name.
///
/// A decorator name is an identifier within a specific subset of allowed
/// identifiers. Currently, `allow` is the only valid decorator name.
fn decorator_name(&mut self, start: usize) -> SyntaxKind {
/// An annotation name is an identifier within a specific subset of allowed
/// identifiers. Currently, `allow` is the only valid annotation name.
fn annotation_name(&mut self, start: usize) -> SyntaxKind {
self.s.eat_while(is_id_continue);
let ident = self.s.from(start);
if ident == "allow" {
SyntaxKind::DecoratorName
SyntaxKind::AnnotationName
} else {
let error = self.error(eco_format!("invalid decorator name"));
let error = self.error(eco_format!("invalid annotation name"));
self.hint("must be 'allow'");
error
}
}
/// Lexes a string in a decorator.
/// Lexes a string in an annotation.
///
/// Currently, such strings only allow a very restricted set of characters.
/// These restrictions may be lifted in the future.
fn decorator_string(&mut self) -> SyntaxKind {
// TODO: Allow more characters in decorators' strings, perhaps allowing
fn annotation_string(&mut self) -> SyntaxKind {
// TODO: Allow more characters in annotations' strings, perhaps allowing
// newlines somehow.
// Could perhaps use one //! per line so we can break a decorator into
// Could perhaps use one //! per line so we can break an annotation into
// multiple lines in a sensible way.
let start = self.s.cursor();
self.s.eat_while(|c| !is_newline(c) && c != '"');
@ -371,9 +371,9 @@ impl Lexer<'_> {
return self.error("unclosed string");
}
if let Some(c) = content.chars().find(|c| !is_valid_in_decorator_string(*c)) {
if let Some(c) = content.chars().find(|c| !is_valid_in_annotation_string(*c)) {
return self
.error(eco_format!("invalid character '{c}' in a decorator's string"));
.error(eco_format!("invalid character '{c}' in an annotation's string"));
}
SyntaxKind::Str
@ -1160,9 +1160,9 @@ fn is_valid_in_label_literal(c: char) -> bool {
is_id_continue(c) || matches!(c, ':' | '.')
}
/// Whether a character can be part of a string in a decorator.
/// Whether a character can be part of a string in an annotation.
#[inline]
fn is_valid_in_decorator_string(c: char) -> bool {
fn is_valid_in_annotation_string(c: char) -> bool {
is_id_continue(c) || c == '@' || c == '/'
}

View File

@ -818,15 +818,15 @@ impl<'a> LinkedNode<'a> {
}
}
/// Get the first sibling decorator node at the line above this node.
/// This is done by moving backwards, checking for decorators, until we hit
/// Get the first sibling annotation node at the line above this node.
/// This is done by moving backwards, checking for annotations, until we hit
/// a second newline (that is, we only check, at most, the line before this
/// node).
pub fn prev_attached_decorator(&self) -> Option<Self> {
pub fn prev_attached_annotation(&self) -> Option<Self> {
let mut cursor = self.prev_sibling_inner()?;
let mut newlines = cursor.capped_newlines();
while newlines < 2 {
if cursor.kind() == SyntaxKind::Decorator {
if cursor.kind() == SyntaxKind::Annotation {
return Some(cursor);
}
@ -834,8 +834,8 @@ impl<'a> LinkedNode<'a> {
newlines += cursor.capped_newlines();
}
// Decorators are attached if they're in the previous line.
// If we counted at least two newlines, no decorators are attached to
// Annotations are attached if they're in the previous line.
// If we counted at least two newlines, no annotations are attached to
// this node.
None
}

View File

@ -109,7 +109,7 @@ fn markup_expr(p: &mut Parser, at_start: &mut bool) {
| SyntaxKind::Parbreak
| SyntaxKind::LineComment
| SyntaxKind::BlockComment
| SyntaxKind::Decorator => {
| SyntaxKind::Annotation => {
p.eat();
return;
}

View File

@ -54,7 +54,7 @@ pub const MARKUP_EXPR: SyntaxSet = SyntaxSet::new()
.add(SyntaxKind::Parbreak)
.add(SyntaxKind::LineComment)
.add(SyntaxKind::BlockComment)
.add(SyntaxKind::Decorator)
.add(SyntaxKind::Annotation)
.add(SyntaxKind::Text)
.add(SyntaxKind::Linebreak)
.add(SyntaxKind::Escape)

View File

@ -542,7 +542,7 @@ pub fn deduplicate_and_suppress_warnings(
/// Checks if a given warning is suppressed given one span it has a tracepoint
/// in. If one of the ancestors of the node where the warning occurred has a
/// warning suppression decorator sibling right before it suppressing this
/// warning suppression annotation sibling right before it suppressing this
/// particular warning, the warning is considered suppressed.
fn is_warning_suppressed(span: Span, world: &dyn World, identifier: &Identifier) -> bool {
// Don't suppress detached warnings.
@ -556,13 +556,13 @@ fn is_warning_suppressed(span: Span, world: &dyn World, identifier: &Identifier)
// Walk the parent nodes to check for a warning suppression in the
// previous line.
while let Some(node) = searched_node {
let mut searched_decorator = node.prev_attached_decorator();
while let Some(sibling) = searched_decorator {
let decorator = sibling.cast::<ast::Decorator>().unwrap();
if check_decorator_suppresses_warning(decorator, identifier) {
let mut searched_annotation = node.prev_attached_annotation();
while let Some(sibling) = searched_annotation {
let annotation = sibling.cast::<ast::Annotation>().unwrap();
if check_annotation_suppresses_warning(annotation, identifier) {
return true;
}
searched_decorator = sibling.prev_attached_decorator();
searched_annotation = sibling.prev_attached_annotation();
}
searched_node = node.parent();
}
@ -570,17 +570,17 @@ fn is_warning_suppressed(span: Span, world: &dyn World, identifier: &Identifier)
false
}
/// Checks if an 'allow' decorator would cause a warning with a particular
/// Checks if an 'allow' annotation would cause a warning with a particular
/// identifier to be suppressed.
fn check_decorator_suppresses_warning(
decorator: ast::Decorator,
fn check_annotation_suppresses_warning(
annotation: ast::Annotation,
warning: &Identifier,
) -> bool {
if decorator.name().as_str() != "allow" {
if annotation.name().as_str() != "allow" {
return false;
}
for argument in decorator.arguments() {
for argument in annotation.arguments() {
if warning.name() == argument.get() {
return true;
}

View File

@ -1,6 +1,6 @@
// Test decorators.
// Test annotations.
--- basic-decorators ---
--- basic-annotations ---
// @allow()
// @allow("A")
@ -19,7 +19,7 @@
h(#0em)
$
--- decorator-comments ---
--- annotation-comments ---
// @allow("abc") // this is ok
@ -31,22 +31,22 @@ $
this is ok
*/, "abc")
--- decorator-strings ---
--- annotation-strings ---
// @allow("@some/thing-there123")
--- unknown-decorator ---
// Error: 2:5-2:13 invalid decorator name
--- unknown-annotation ---
// Error: 2:5-2:13 invalid annotation name
// Hint: 2:5-2:13 must be 'allow'
// @whatever()
--- invalid-decorator-syntax ---
// Error: 2:11-2:12 the character '*' is not valid in a decorator
--- invalid-annotation-syntax ---
// Error: 2:11-2:12 the character '*' is not valid in an annotation
// @allow(*)
// Error: 2:11-2:12 the character '5' is not valid in a decorator
// Error: 2:11-2:12 the character '5' is not valid in an annotation
// @allow(5)
@ -63,12 +63,12 @@ this is ok
// @allow("abc
// Error: 2:18-2:21 expected end of decorator
// Error: 2:18-2:21 expected end of annotation
// @allow("abc") abc
// Error: 2:17-2:22 expected comma
// Error: 2:24-2:27 expected end of decorator
// Error: 2:24-2:27 expected end of annotation
// @allow("abc" "abc") abc
@ -80,23 +80,23 @@ this is ok
// @allow(, "abc", "abc", "abc")
--- invalid-decorator-strings ---
--- invalid-annotation-strings ---
// Error: 2:11-2:16 invalid character ' ' in a decorator's string
// Error: 2:11-2:16 invalid character ' ' in an annotation's string
// @allow("a b")
// Error: 2:11-2:19 invalid character '|' in a decorator's string
// Error: 2:11-2:19 invalid character '|' in an annotation's string
// @allow("aaaaa|")
// TODO: Why does this print / instead of \?
// Error: 2:11-2:19 invalid character '/' in a decorator's string
// Error: 2:11-2:19 invalid character '/' in an annotation's string
// @allow("aaaaa\")
--- invalid-decorator-in-decorator ---
// Error: 2:18-2:34 cannot have multiple decorators per line
--- invalid-annotation-in-annotation ---
// Error: 2:18-2:34 cannot have multiple annotations per line
// @allow("aaa") // @allow("bbb")