속성 파싱
속성에는 비활성(또는 내장) 속성과 활성(비내장) 속성의 두 가지 유형이 있습니다. 비활성 속성은 HIR로 가는 AST 로워링 중에 파싱되고, 활성 속성은 확장 중에 확장됩니다. 둘의 차이에 대한 자세한 내용은 속성에 관한 페이지를 참조하십시오.
AST 로워링 중에 비활성 속성은 파싱되지 않은 TokenStream에서 파싱된 표현으로 변환됩니다. 파싱된 형태는 rustc_attr_ir 크레이트에 정의되어 있고, 파서는 rustc_attr_parsing 크레이트에 정의되어 있습니다.
새 비활성 속성 파서를 추가하는 단계별 안내
- BUILTIN_ATTRIBUTES에 속성 이름을 추가하십시오. 이 목록은 비활성 속성의 집합을 정의합니다.
- 속성 정의의
AttributeKind에 배리언트를 추가하십시오. 이 배리언트는 속성 파서가 생성할 속성의 파싱된 형태를 정의합니다. rustc_attr_ir/encode_cross_crate.rs의 match에 추가한 배리언트를 넣으십시오. 이 match는 해당 속성이 의존하는 크레이트에서 보여야 하는지 여부를 반환해야 합니다. 일반적으로 코드 생성 관련 속성은No이고, 분석 관련 속성은Yes입니다.rustc_attr_parsing/attributes/*.rs에 속성 파서의 상태를 보관할 새 구조체를 만드십시오. 대부분의 파서에서는 빈 구조체가 됩니다.- 이 구조체에 대해 다음 속성 파싱 트레이트 중 하나를 구현하십시오.
NoArgsAttributeParser는 항목당 한 번만 나타날 수 있고 인자를 받지 않는 속성에 사용합니다.#[no_mangle]이 그 예입니다.SingleAttributeParser는 항목당 한 번만 나타날 수 있고 인자를 받을 수 있는 속성에 사용합니다.#[inline(...)]이 그 예입니다.CombineAttributeParser는 항목당 여러 번 나타날 수 있고, 각각의 사용이 하나의 파싱된 속성으로 결합되는 속성에 사용합니다.#[feature(...)]이 그 예입니다.AttributeParser는 위의 트레이트 중 어느 것에도 깔끔하게 들어맞지 않는 속성에 사용합니다. 예를 들어 서로 다른 여러 속성(#[stable]과#[unstable]등)을 하나의 파싱된 속성으로 결합해야 하는 경우입니다,
- 선택한 속성 파서 트레이트의 연관 상수를 구현하십시오. 이 상수들이 모든 트레이트에 적용되는 것은 아닙니다.
PATH: 추가되는 속성의 이름입니다TEMPLATE: 속성의 구조를 설명하는 것으로, 진단을 안내하는 데에만 사용됩니다. 이 타입을 생성하려면 template! 매크로를 사용하십시오.STABILITY: 속성이 안정(게이트 없음)인지 불안정(기능 게이트 적용)인지를 나타냅니다.ALLOWED_TARGETS: 속성이 나타날 수 있는 대상입니다. 새 속성에서는 잘못된 대상에 모두 오류가 발생해야 하므로, 새 속성에는AllowedTargets::AllowList를 사용하고 대상을 허용할 때는Policy::Allow만 사용하십시오.ON_DUPLICATE: 속성이 잘못해서 여러 번 나타날 때의 동작을 제어합니다. 새 속성에는 보통OnDuplicate::Error를 사용해야 하며, 이것이 기본값입니다.SAFETY:#[unsafe(no_mangle)]처럼 속성을 사용할 때 unsafe로 표시해야 하는지를 제어합니다. 보통AttributeSafety::Normal이어야 하며, 이것이 기본값입니다.
- 관련 파싱 함수를 구현하십시오. 일반적으로 속성이 사용되는 컨텍스트를 제공하는
AcceptContext와, 속성에 전달된 인자를 담은ArgParser가 주어집니다. 인자가 잘못되었다면cx.adcx().expected_*계열 함수를 사용해 오류를 내보낸 다음return None을 해야 합니다.cx.expect_*계열 함수를 사용하면 이 두 단계를 하나로 합칠 수 있는 경우도 있으며, 이 함수들에는?연산자를 사용해 조기 반환할 수 있습니다. 속성 파서는 파싱된 속성을 반환하기 전에 속성이 유효한지 확인하기 위해 가능한 모든 검사를 수행해야 합니다. - 속성 파서를 속성 파서 목록에 추가하여 등록하십시오.
- 정보가 충분하지 않아 속성 파서에서 모든 검증을 수행할 수 없었다면,
rustc_passes/check_attr.rs에 추가 검증 단계를 넣으십시오. 이 코드는 HIR이 완전히 구축된 직후에 실행되므로, 분석에 전체 HIR을 사용할 수 있습니다.
속성 파서 사용하기
속성 파서를 사용하는 방법에는 두 가지가 있으며, 각각 Early 속성 파싱과 Late 속성 파싱이라고 부릅니다.
Late (일반적인 방법)
HIR이 구축된 후에 파싱된 표현을 사용하려면 여기에 정의된 find_attr! 매크로를 사용하십시오. 이 매크로는 다음과 같은 방식으로 사용할 수 있습니다.
find_attr!(tcx, <def_id>, Variant(...))는 def id에서 속성을 찾습니다.find_attr!(tcx, <hir_id>, Variant(...))는 HIR id에서 속성을 찾습니다.find_attr!(tcx, crate, Variant(...))는 현재 크레이트에서 속성을 찾습니다.find_attr!(attrs, Variant(...))는&[rustc_attr_ir::Attribute]타입인 attrs에서 속성을 찾습니다.
Variant는 AttributeKind 변형 중 하나와 패턴 매칭되는 패턴이며, 다음 형태 중 하나를 취할 수 있습니다:
find_attr!(..., Variant)는 속성이 존재하는지를 나타내는 불리언 값을 반환합니다.find_attr!(..., Variant(a, b, _) => (a, b))는 패턴으로 바인딩된 필드로 구성한,=>뒤의 값을 반환합니다.
Early (“limited“라고도 부릅니다)
HIR이 구축되기 전에 파싱된 표현을 사용하려면 보통 AttributeParser::parse_limited 함수를 사용해야 합니다. 이 함수는 &[ast::Attribute]의 슬라이스와 관심 있는 속성의 이름을 받아, 해당 속성이 존재하면 파싱합니다.
limited 파싱 중에는 어떤 진단도 내보내지 않습니다. 린트는 전혀 내보내지 않으며, 오류는 지연된 버그(delayed bug)로 내보냅니다. 다시 말해, parse_limited로 파싱된 속성은 이후 오류를 실제로 내보내는 AST 로워링 단계에서 다시 파싱될 것으로 예상합니다.