exhaustive_cases 진단: enum류 타입의 모든 상수에 case를 정의하세요

exhaustive_cases 진단: enum류 타입의 모든 상수에 case를 정의하세요

enum 같은 타입(enum-like type)의 값을 switch로 분기할 때, 알려진 모든 상수에 case 절을 정의하도록 안내하는 린트 규칙이에요. 빠뜨린 분기가 없도록 컴파일러가 도와주는 셈이죠.

출처: exhaustive_cases

본문

설명

enum류 타입(enum-like type)의 인스턴스를 switch로 분기할 때는 알려진 모든 상수를 다루어야 해요.

enum류 타입이란 다음 조건을 만족하는 클래스 또는 확장 타입(extension type)이에요.

  • private이고 팩토리(factory)가 아닌 생성자만 가진다.
  • 타입이 자기 자신(둘러싼 타입)인 static const 필드를 두 개 이상 가진다.

이에 더해 enum류 클래스는 구체적(concrete, 비추상) 이고, 정의된 라이브러리 안에 하위 클래스가 없어야 해요.

enum류 타입의 모든 상수에 case 절을 정의하도록(DO) 해요. 확장 타입도 enum류가 될 수 있어요.

extension type const E._(int value) {
  static const E a = E._(1);
  static const E b = E._(2);
}

void f(E e) {
  switch (e) { // LINT
    case E.a:
      print('a');
  }
}

BAD:

아래 코드는 어떤 case도 빠뜨리지 않으려면 세 상수 e, f, g를 모두 다뤄야 하는데, g를 빠뜨리고 있어요.

class EnumLike {
  final int i;
  const EnumLike._(this.i);

  static const e = EnumLike._(1);
  static const f = EnumLike._(2);
  static const g = EnumLike._(3);
}

void bad(EnumLike e) {
  // Missing case.
  switch(e) { // LINT
    case EnumLike.e :
      print('e');
      break;
    case EnumLike.f :
      print('f');
      break;
  }
}

GOOD:

모든 case를 다뤘으니 문제없어요.

class EnumLike {
  final int i;
  const EnumLike._(this.i);

  static const e = EnumLike._(1);
  static const f = EnumLike._(2);
  static const g = EnumLike._(3);
}

void ok(EnumLike e) {
  // All cases covered.
  switch(e) { // OK
    case EnumLike.e :
      print('e');
      break;
    case EnumLike.f :
      print('f');
      break;
    case EnumLike.g :
      print('g');
      break;
  }
}

활성화 방법

exhaustive_cases 규칙을 쓰려면 analysis_options.yaml 파일의 linter > rules 아래에 규칙을 추가하면 돼요.

linter:
  rules:
    - exhaustive_cases

대신 YAML 맵(map) 문법으로 린터 규칙을 설정한다면, linter > rules 아래에 exhaustive_cases: true를 추가해요.

linter:
  rules:
    exhaustive_cases: true

더 알아보기