Skip to main content

luaur_ast/methods/
parser_validate_attribute.rs

1//! Node: `cxx:Method:Luau.Ast:Ast/src/Parser.cpp:934:Parser::validateAttribute`
2//!
3//! Validate an `@attr` against the known attribute tables (`kAttributeEntries`
4//! plus the FFlag-gated `kDebugAttributeEntries`): resolve its `AstAttr::Type`,
5//! reject duplicates, and run the per-attribute argument validator (only
6//! `@deprecated` has one). The C++ static tables are inlined here as a `match`
7//! rather than a sentinel-terminated array.
8
9use crate::records::ast_array::AstArray;
10use crate::records::ast_attr::{AstAttr, AstAttrType};
11use crate::records::ast_expr::AstExpr;
12use crate::records::location::Location;
13use crate::records::parser::Parser;
14use crate::records::temp_vector::TempVector;
15
16impl Parser {
17    #[allow(non_snake_case)]
18    pub(crate) fn validate_attribute(
19        &mut self,
20        loc: Location,
21        attribute_name: &str,
22        attributes: &TempVector<'_, *mut AstAttr>,
23        args: &AstArray<*mut AstExpr>,
24    ) -> Option<AstAttrType> {
25        // kAttributeEntries (Parser.cpp): name -> (type, optional args validator).
26        // Only "deprecated" carries a validator (deprecatedArgsValidator).
27        let mut r#type: Option<AstAttrType> = None;
28        let mut has_deprecated_validator = false;
29
30        match attribute_name {
31            "checked" => r#type = Some(AstAttrType::Checked),
32            "native" => r#type = Some(AstAttrType::Native),
33            "deprecated" => {
34                r#type = Some(AstAttrType::Deprecated);
35                has_deprecated_validator = true;
36            }
37            _ => {}
38        }
39
40        // kDebugAttributeEntries: FFlag-gated debug-only attributes.
41        if r#type.is_none()
42            && attribute_name == "debugnoinline"
43            && luaur_common::FFlag::DebugLuauNoInline.get()
44        {
45            r#type = Some(AstAttrType::DebugNoinline);
46        }
47
48        if let Some(attr_type) = r#type {
49            // check that attribute is not duplicated
50            for i in 0..attributes.size_ {
51                let attr_ptr =
52                    unsafe { *(*attributes.storage).as_ptr().add(attributes.offset + i) };
53                unsafe {
54                    if (*attr_ptr).r#type == attr_type {
55                        self.report(
56                            loc,
57                            format_args!("Cannot duplicate attribute '@{}'", attribute_name),
58                        );
59                    }
60                }
61            }
62
63            if has_deprecated_validator {
64                let errors_to_report =
65                    crate::functions::deprecated_args_validator::deprecated_args_validator(
66                        loc, *args,
67                    );
68                for (error_loc, msg) in errors_to_report {
69                    self.report(error_loc, format_args!("{}", msg));
70                }
71            }
72        } else if attribute_name.is_empty() {
73            self.report(loc, format_args!("Attribute name is missing"));
74        } else {
75            self.report(loc, format_args!("Invalid attribute '@{}'", attribute_name));
76        }
77
78        r#type
79    }
80}
81
82// Free-function node surface delegating to the method.
83#[allow(non_snake_case)]
84pub fn parser_validate_attribute(
85    parser: &mut Parser,
86    loc: Location,
87    attribute_name: &str,
88    attributes: &TempVector<'_, *mut AstAttr>,
89    args: &AstArray<*mut AstExpr>,
90) -> Option<AstAttrType> {
91    parser.validate_attribute(loc, attribute_name, attributes, args)
92}