Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

진단 항목

린트를 작성할 때는 특정 타입, 트레이트, 함수를 검사하는 경우가 많습니다. 그렇다면 이것들을 어떻게 검사해야 하는지에 대한 의문이 생깁니다. 타입은 전체 타입 경로로 검사할 수 있습니다. 하지만 이 방법은 경로를 하드 코딩해야 하며, 일부 엣지 케이스에서는 잘못 분류될 수 있습니다. 이를 해결하기 위해 rustc는 Symbol을 통해 타입을 식별하는 데 사용하는 진단 항목을 도입했습니다.

진단 항목 찾기

진단 항목은 rustc_diagnostic_item 속성을 사용하여 rustc/std/core/alloc 내부의 항목에 추가됩니다. 특정 타입에 대한 항목은 문서에서 소스 코드를 열어 이 속성을 찾아보면 확인할 수 있습니다. 테스트 중 컴파일 오류를 피하기 위해 이 속성은 cfg_attr 속성과 함께 추가되는 경우가 많다는 점에 유의하십시오. 정의는 보통 다음과 같은 형태입니다:

// This is the diagnostic item for this type   vvvvvvv
#[cfg_attr(not(test), rustc_diagnostic_item = "Penguin")]
struct Penguin;

진단 항목은 일반적으로 트레이트, 타입, 독립 함수에만 추가됩니다. 연관 타입이나 메서드를 검사하는 것이 목적이라면, 해당 항목의 진단 항목을 사용하고 진단 항목 사용하기를 참고하십시오.

진단 항목 추가하기

새 진단 항목은 다음 두 단계로 추가할 수 있습니다:

  1. Rust 저장소에서 대상 항목을 찾으십시오. 이제 rustc_diagnostic_item 속성을 통해 진단 항목을 문자열로 추가하십시오. 이로 인해 테스트를 실행하는 동안 컴파일 오류가 발생하는 경우가 있습니다. 이러한 오류는 not(test) 조건과 함께 cfg_attr 속성을 사용하면 피할 수 있습니다(예방 차원에서 모든 rustc_diagnostic_item 속성에 이렇게 추가해도 괜찮습니다). 최종적으로는 다음과 같은 형태가 되어야 합니다:

    // This will be the new diagnostic item        vvv
    #[cfg_attr(not(test), rustc_diagnostic_item = "Cat")]
    struct Cat;
    

    진단 항목의 명명 규칙에 대해서는 명명 규칙을 참고하십시오.

  2. 코드에서 진단 항목은 rustc_span::symbol::sym에 있는 심볼을 통해 접근합니다. 새로 만든 진단 항목을 추가하려면 모듈 파일을 열고 목록의 올바른 위치에 이름(이 경우 Cat)을 추가하기만 하면 됩니다.

이제 변경 사항으로 풀 리퀘스트를 생성할 수 있습니다.

참고: Clippy와 같은 다른 프로젝트에서 진단 항목을 사용하는 경우, 저장소가 동기화될 때까지 시간이 걸릴 수 있습니다.

명명 규칙

진단 항목에는 아직 명명 규칙이 없습니다. 다음은 앞으로 따라야 할 몇 가지 지침이지만, 기존 이름과는 다를 수 있습니다:

  • 타입, 트레이트, 열거형은 UpperCamelCase로 이름을 짓습니다(예: Iterator와 HashMap)
  • Writer처럼 여러 번 사용되는 타입 이름은 모듈 이름을 덧붙이는 등의 방법으로 더 정확한 이름을 선택하는 것이 좋습니다(예: IoWriter)
  • 연관 항목에는 별도의 진단 항목을 부여하지 않아야 하며, 대신 해당 항목이 속한 타입의 진단 항목을 통해 간접적으로 접근해야 합니다.
  • std::mem::swap()과 같은 독립 함수는 중요한 (내보내기) 모듈 하나를 접두사로 붙여 snake_case로 이름을 지어야 합니다(예: mem_swap과 cmp_max)
  • 모듈에는 보통 진단 항목을 붙이지 않아야 합니다. 진단 항목은 경로 사용을 피하기 위해 추가되었으므로, 모듈에 진단 항목을 사용하면 오히려 역효과가 날 가능성이 큽니다.

진단 항목 사용하기

rustc에서 진단 항목은 rustc_span::symbol::sym 모듈 안의 Symbol을 통해 조회합니다. 그런 다음 TyCtxt::get_diagnostic_item()을 사용해 DefId에 매핑하거나, TyCtxt::is_diagnostic_item()을 사용해 DefId와 일치하는지 확인할 수 있습니다. 진단 항목을 DefId에 매핑할 때, 이 메서드는 Option<DefId>를 반환합니다. 심볼이 진단 항목이 아니거나, 예를 들어 #[no_std]로 컴파일할 때처럼 타입이 등록되지 않은 경우에는 이 값이 None일 수 있습니다. 이어지는 모든 예제는 DefId와 그 사용법을 바탕으로 합니다.

예제: 타입 확인하기

#![allow(unused)]
fn main() {
use rustc_span::symbol::sym;

/// This example checks if the given type (`ty`) has the type `HashMap` using
/// `TyCtxt::is_diagnostic_item()`
fn example_1(cx: &LateContext<'_>, ty: Ty<'_>) -> bool {
    match ty.kind() {
        ty::Adt(adt, _) => cx.tcx.is_diagnostic_item(sym::HashMap, adt.did()),
        _ => false,
    }
}
}

예제: 트레이트 구현 확인하기

#![allow(unused)]
fn main() {
/// This example checks if a given [`DefId`] from a method is part of a trait
/// implementation defined by a diagnostic item.
fn is_diag_trait_item(
    cx: &LateContext<'_>,
    def_id: DefId,
    diag_item: Symbol
) -> bool {
    if let Some(trait_did) = cx.tcx.trait_of_item(def_id) {
        return cx.tcx.is_diagnostic_item(diag_item, trait_did);
    }
    false
}
}

연관 타입

진단 항목의 연관 타입은 먼저 트레이트의 DefId를 얻은 다음 TyCtxt::associated_items()를 호출하는 방식으로 간접적으로 접근할 수 있습니다. 이 메서드는 추가 확인에 사용할 수 있는 AssocItems 객체를 반환합니다. 사용 예시는 clippy_utils::ty::get_iterator_item_ty()를 참고하십시오.

Clippy에서의 사용

Clippy는 가능한 곳에서는 진단 항목을 사용하려 하며, 몇 가지 래퍼 함수와 유틸리티 함수를 개발했습니다. Clippy에서 진단 항목을 사용할 때는 해당 문서도 함께 참고하십시오. (린트 작성을 위한 공통 도구를 참고하십시오.)

관련 이슈

이 내용은 아마 이 주제를 정말 깊이 파고들고 싶은 분들에게만 흥미로울 것입니다 :)

  • rust#60966: 진단 항목을 도입한 Rust PR
  • rust-clippy#5393: 하드코딩된 경로 대신 진단 항목을 사용하도록 전환하기 위한 Clippy의 추적 이슈