Sui 린터 및 경고 업데이트로 코더 속도 향상

린터 및 컴파일러 경고를 포함한 새로운 개발자 환경 기능은 빌더가 코딩하는 동안 유용한 경고를 제공합니다.

Sui 린터 및 경고 업데이트로 코더 속도 향상

Sui 에 출시된 새로운 린터 지원과 향상된 경고 메시지는 Move 개발자 환경을 개선하여 많은 프로그래밍 에코시스템에서 공통적으로 사용되는 코딩 지원을 제공합니다. 주로 객체 처리를 다루는 6개의 새로운 린터는 Sui 특정 코드에서 잠재적으로 문제가 될 수 있는 패턴을 찾아냅니다. 또한 Move 컴파일러에는 이제 사용되지 않는 구조체에 대한 경고가 포함되어 Move 개발자에게 도움이 될 것입니다. 

이러한 추가 기능은 초보 개발자와 숙련된 개발자 모두 Sui 의 혁신적인 기술을 활용하여 앱을 빌드할 때 코드 관련 문제를 방지하는 데 도움이 됩니다. 린터와 경고 메시지 모두 억제 옵션이 포함되어 있어 개발자가 워크플로를 맞춤 설정할 수 있습니다.

린트 지원

특히 코드 리뷰가 드문 생산성 높은 환경에서는 많은 개발자가 작업 중 코드의 문제를 발견하기 위해 린터에 의존합니다. 하지만 Move 은 비교적 최근에 출시되었기 때문에 수년 동안 사용되어 온 언어에서 흔히 볼 수 있는 코딩 보조 도구의 지원을 받지 못했습니다.

Sui 에서 이 새로운 프레임워크의 린팅 지원은 현재 선택 사항입니다. 개발자는 린팅을 지원하려면 --lint 플래그를 빌드, 테스트, 게시 및 업그레이드 명령에 추가하면 린터 메시지를 볼 수 있습니다. 향후 릴리스에서는 이 프레임워크가 옵트아웃 모드로 전환됩니다(--보풀 없음)를 설정하여 기본적으로 린터 메시지가 표시되도록 합니다.

Sui 는 현재 아래에 설명된 6가지 린터를 지원하며, 더 많은 린터를 지원할 계획입니다. 커뮤니티 피드백 를 통해 향후 린터 개발에 참고할 수 있습니다.

1. 코인 필드 린터

이 분석에서는 sui::코인::코인 객체를 다른 객체 및 구조체의 필드에 사용할 수 있습니다. 대부분의 경우 개발자는 sui::균형::균형 를 사용하여 공간을 절약할 수 있습니다.

예를 들어, 다음과 같은 필드를 포함하는 객체를 정의하는 간단한 모듈을 생각해 보겠습니다. sui::코인::코인 유형입니다:

#[allow(unused_field)]
module coin_field::test {
    struct S1 {}

    struct S2 has key, store {
        id: sui::object::UID,
        c: sui::coin::Coin<S1>,
    }
}

이 모듈을 빌드하면 다음과 같은 린터 메시지가 표시됩니다:

warning[Lint W03001]: sub-optimal 'sui::coin::Coin' field type
  ┌─ ./sources/test.move:5:12
  │
5 │     struct S2 has key, store {
  │            ^^ The field 'c' of 'S2' has type 'sui::coin::Coin'
6 │         id: sui::object::UID,
7 │         c: sui::coin::Coin<S1>,
  │         - Storing 'sui::balance::Balance' in this field will typically be more space-efficient
  │
  = This warning can be suppressed with '#[lint_allow(coin_field)]' applied to the 'module' or module member ('const', 'fun', or 'struct')

참고 #[allow(unused_field)]주석을 사용하면 사용하지 않는 필드 경고의 출력을 억제하여 출력을 더욱 간결하게 만들 수 있습니다. 아래의 이 문서의 향상된 경고 섹션에서는 새로운 경고와 경고 및 린터 메시지 억제에 대해 자세히 설명합니다. 

2. 수집 평등 린터

이 린터는 다음과 같은 인스턴스가 발생하는 상황을 플래그합니다. sui::table::Table, sui::table_vec::TableVec, sui::bag::Bag 의 (내)동등성을 비교하고 있습니다. 이러한 유형의 비교는 그다지 유용하지 않으며 구조적 (내)평등을 고려하지 않는다는 것이 그 이유입니다.

예를 들어, 두 개의 서로 다른 인스턴스에 대한 참조를 비교하려는 함수가 포함된 이 기본 모듈을 고려해 보겠습니다. sui::가방::가방:

module collection_eq::test {
    public fun bag_eq(bag1: &sui::bag::Bag, bag2: &sui::bag::Bag): bool {
        bag1 == bag2
    }
}

이 모듈을 빌드하면 다음과 같은 린터 메시지가 표시됩니다:

경고[Lint W05001]: 쓸모없는 컬렉션 비교 가능
  ┌─ ./sources/test.move:3:14
  │
3 │ bag1 == bag2
  │ ^^ 'sui::bag::Bag' 타입의 컬렉션을 비교하면 예기치 않은 결과가 나올 수 있습니다.
  │
  = 'sui::bag::Bag' 유형의 컬렉션에 대한 동일성은 콘텐츠에 기반한 구조 검사가 아닙니다.
  = 이 경고는 '모듈' 또는 모듈 멤버('const', 'fun', 'struct')에 '#[lint_allow(collection_equality)]'를 적용하면 억제할 수 있습니다.

3. 사용자 지정 상태 변경 링터

이 린터는 이미 전송, 공유 및 동결 호출이 있는 객체에 대한 잠재적인 사용자 정의 구현에 플래그를 지정합니다. store 기능과 개발자가 이러한 호출의 공개 변형을 사용할 수 있는 위치에 대해 설명합니다. 이러한 호출을 사용하면 사용자 지정 전송, 공유 및 동결 작업을 적용할 수 없게 되므로 위험할 수 있습니다. 함수가 주어진 모듈에 정의된 구조체 유형의 인스턴스를 매개변수로 받는 경우 함수는 잠재적인 사용자 정의 구현으로 간주됩니다. store 함수를 호출하고 이를 비공개 착신 전환, 공유 또는 통화 동결에 대한 인수로 전달합니다.

예를 들어, 공개를 사용하려는 함수가 포함된 이 간단한 모듈을 고려해 보겠습니다. sui::전송::전송 함수를 사용하여 객체를 전송하는 store 어빌리티를 인수로 전달합니다:

#[allow(unused_field)]
module custom_state_change::test {
    struct S1 has key, store {
        id: sui::object::UID
    }

    public fun custom_transfer(o: S1, a: address) {
        sui::transfer::transfer(o, a)
    }
}

이 모듈을 빌드하면 다음과 같은 린터 메시지가 표시됩니다:

warning[Lint W02001]: potentially unenforceable custom transfer/share/freeze policy
  ┌─ ./sources/test.move:7:16
  │
7 │     public fun custom_transfer(o: S1, a: address) {
  │                ^^^^^^^^^^^^^^^ - An instance of a module-private type with a store ability to be transferred coming from here
  │                │                
  │                Potential unintended implementation of a custom transfer function.
8 │         sui::transfer::transfer(o, a)
  │                        -------- Instances of a type with a store ability can be transferred using the public_transfer function which often negates the intent of enforcing a custom transfer policy
  │
  = A custom transfer policy for a given type is implemented through calling the private transfer function variant in the module defining this type
  = This warning can be suppressed with '#[lint_allow(custom_state_change)]' applied to the 'module' or module member ('const', 'fun', or 'struct')

4. 포장된 찌꺼기 얼리기

이 린터는 내부 객체를 (일시적으로 또는 그렇지 않게) 포함하는 객체의 프리징을 플래그합니다. 즉, 필드가 (직접적으로든 아니든) 다른 객체를 감싸는 객체의 동결 플래그를 지정합니다. 이러한 객체를 고정하면 내부 객체의 래핑이 해제되지 않습니다.

예를 들어, 다른 객체 유형의 필드를 포함하는 Wrapper 유형의 객체를 고정하려는 함수가 포함된 이 기본 모듈을 고려해 보겠습니다. 내부:

#[allow(unused_field)]
module freeze_wrapped::test {
    struct Inner has key, store {
        id: sui::object::UID
    }

    struct Wrapper has key, store {
        id: sui::object::UID,
        inner: Inner,
    }

    public fun freeze(w: Wrapper) {
        sui::transfer::public_freeze_object(w);
    }
}

이 모듈을 빌드하면 다음과 같은 린터 메시지가 표시됩니다:

경고[Lint W04001]: 래핑된 객체를 고정하려고 함
   ┌─ ./sources/test.move:13:45
   │
 9 │ 내부: 내부,
   ----- 이 유형의 필드는 래핑된 객체입니다.
   -
13 │ sui::transfer::public_freeze_object(w);
   │ ^ 'Wrapper' 타입의 객체를 동결하면 해당 필드 'inner'에 래핑된 모든 객체도 동결됩니다.
   │
   = 이 경고는 '모듈' 또는 모듈 멤버('const', 'fun', 'struct')에 '#[lint_allow(freeze_wrapped)]'를 적용하면 억제할 수 있습니다.

5. 셀프 전송 린터

이 린터는 트랜잭션 발신자에게 오브젝트의 전송을 플래그합니다. sui::TX_CONTEXT::SENDER() 호출을 호출합니다. 이 링터의 목표는 개발자가 트랜잭션 발신자에게 객체를 전송하는 대신 함수에서 객체를 반환하도록 장려하는 것입니다. 함수에서 객체를 반환하면 다음에서 함수의 컴포저블성이 향상됩니다. 프로그래밍 가능한 트랜잭션 블록 호출자가 반환된 객체를 직접 사용할 수 있도록 허용합니다.

예를 들어, 새로 생성된 객체를 트랜잭션 발신자에게 전송하려는 함수가 포함된 간단한 모듈을 생각해 보겠습니다:

module self_transfer::test {
    struct S1 has key, store {
        id: sui::object::UID
    }

    public fun public_transfer(ctx: &mut sui::tx_context::TxContext) {
        let o = S1 { id: sui::object::new(ctx) };
        sui::transfer::public_transfer(o, sui::tx_context::sender(ctx))
    }
}

이 모듈을 빌드하면 다음과 같은 린터 메시지가 표시됩니다:

warning[Lint W01001]: non-composable transfer to sender
  ┌─ ./sources/test.move:8:9
  │
6 │     public fun public_transfer(ctx: &mut sui::tx_context::TxContext) {
  │                --------------- Returning an object from a function, allows a caller to use the object and enables composability via programmable transactions.
7 │         let o = S1 { id: sui::object::new(ctx) };
8 │         sui::transfer::public_transfer(o, sui::tx_context::sender(ctx))
  │         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  │         │                                 │
  │         │                                 Transaction sender address coming from here
  │         Transfer of an object to transaction sender address in function public_transfer
  │
  = This warning can be suppressed with '#[lint_allow(self_transfer)]' applied to the 'module' or module member ('const', 'fun', or 'struct')

6. 소유 린터 공유

이 린터는 함수 매개변수로 전달된 객체를 만들거나 공유 가능한 객체(이미 소유하고 있을 가능성이 높음)를 언패킹하는 과정에서 발생하는 플래그를 지정하여 중단으로 이어질 수 있습니다. 권장되는 패턴은 새로운 객체를 생성하여 동일한 함수 내에서 공유하는 것입니다. 일반적으로 함수에 값으로 전달되는 모든 객체는 소유된 객체입니다.

인자로 전달된 객체를 공유하려는 함수가 포함된 기본 모듈을 예로 들어 보겠습니다(이 객체에 대한 데이터 흐름은 함수에서 추적됩니다):

#[allow(unused_field)]
module unused::test {
    struct Obj has key, store {
        id: sui::object::UID
    }

    public fun arg_object(o: Obj) {
        let arg = o;
        sui::transfer::public_share_object(arg);
    }
}

이 모듈을 빌드하면 다음과 같은 린터 메시지가 표시됩니다:

warning[Lint W00001]: possible owned object share
  ┌─ ./sources/test.move:9:9
  │
7 │     public fun arg_object(o: Obj) {
  │                           - A potentially owned object coming from here
8 │         let arg = o;
9 │         sui::transfer::public_share_object(arg);
  │         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  │         │                                  │
  │         │                                  Creating a fresh object and sharing it within the same function will ensure this does not abort.
  │         Potential abort from a (potentially) owned object created by a different transaction.
  │
  = This warning can be suppressed with '#[lint_allow(share_owned)]' applied to the 'module' or module member ('const', 'fun', or 'struct')

강화된 경고

이제 Move 컴파일러는 린터 및 경고와 함께 사용되지 않는 함수, 상수, (함수) 유형 매개변수, 구조체/객체 필드 등 사용되지 않는 구조체에 대한 경고를 표시하여 개발자를 돕습니다. 많은 개발자가 이러한 경고가 유용하다고 생각하지만, 특히 이전에 경고 없이 컴파일된 코드의 경우 이러한 경고가 반갑지 않을 수 있습니다. 특정 코드 요소에 대해 이러한 경고를 표시하지 않으려는 개발자는 다음과 같은 형식의 주석을 지정할 수 있습니다. #[...])를 모듈, 상수, 유형 정의 또는 함수에 사용할 수 있습니다.

경고 억제 예시

다음 코드는 새 경고를 생성하는 예제를 제공합니다. 이 기본 모듈은 사용되지 않는 상수를 정의합니다:

module unused::test {
    const UNUSED_CONST: u64 = 42;
}

이 모듈을 빌드하면 다음과 같은 경고가 표시됩니다:

경고[W09011]: 사용되지 않은 상수
  ┌─ ./sources/test.move:2:11
  │
2 │ const UNUSED_CONST: u64 = 42;
  ^^^^^^^^^^^^ 상수 'UNUSED_CONST'는 사용되지 않습니다. 제거하는 것이 좋습니다.
  │
  = '모듈' 또는 모듈 멤버('const', 'fun', 'struct')에 '#[allow(unused_const)]'를 적용하면 이 경고를 표시하지 않을 수 있습니다.

컴파일러는 특정 경고를 표시하지 않는 방법에 대한 제안을 반환합니다. 이 경우 #[allow(unused_const)] 어노테이션을 상수 레벨에서 다음과 같이 추가합니다:

module unused::test {
    #[allow(unused_const)]
    const UNUSED_CONST: u64 = 42;
}

모듈 수준에서 주석을 포함하면 같은 종류의 경고가 여러 개 표시되지 않습니다:

#[allow(unused_const)]
module unused::test {
    const UNUSED_CONST: u64 = 42;
}

린터 억제

개발자는 표준 컴파일러 경고와 유사한 방식으로 린터 메시지를 억제할 수 있습니다. 인터프리터 메시지에는 개발자가 이를 억제하는 데 사용할 수 있는 어노테이션에 대한 설명이 포함됩니다. 어노테이션에서 인터프리터 경고를 억제하는 데 사용되는 키워드는 다음과 같습니다. lint_allow 대신 허용와 같이 표준 경고를 억제하는 데 사용됩니다. 개발자가 린터 억제를 선택하면 컴파일러는 억제된 메시지 수와 종류에 대한 간단한 통계도 출력합니다.

개발 중 Sui

Move 프로그래밍 언어가 등장한 것은 불과 4년 전입니다. 이 언어의 안전성과 유용성 덕분에 빌더 커뮤니티에서 즉각적인 인기를 얻었지만, 개발자 도구의 양은 더 성숙한 언어에 비해 뒤쳐져 있습니다. Sui 에 린터와 컴파일러 경고를 추가한 것은 개발자 환경을 개선하기 위한 여러 노력 중 하나에 불과하며, 일부는 Move 에 특별히 맞춤화된 것입니다.

그러나 Move 의 오픈 소스 특성은 전체 빌더 커뮤니티가 개발자 경험을 개선하는 데 참여할 수 있고 참여해야 한다는 것을 의미합니다. 사람들이 기여하는 방법은 포럼이나 기타 장소에서 필요 사항을 표현하는 것부터 새로운 링터 구현 제안이나 기타 언어 개선 사항을 리포지토리에 풀 리퀘스트를 제출하는 것까지 다양합니다. 커뮤니티가 어떤 방식으로 기여하든 Move 개발을 돕기 위한 첫 번째 단계는 Sui 포럼.