본문 바로가기
모바일 APP/Flutter

린터 룰들 Linter rules

by 살길바라냐 2022. 3. 31.

always_use_package_imports

 

lib 디렉토리 안에 파일들에 상대경로를 못하게 하는 Error 룰
상대경로와 절대경로가 썩여 있으면 개발자끼리 혼란을 일으킬수 있어
절대 경로를  추천하는 룰임.

상대경로는 코드를 한번에 분석하거나 이해하기 어렵거나, 
파일의 경로가 변경시, 경로를 재설정하지 않으면 오류가 발생할수 있음.

// 아름다운 방법

import 'package:foo/bar.dart';

import 'package:foo/baz.dart';

import 'package:foo/src/baz.dart';

 

// 그지 같은 방법

import 'baz.dart';

import 'src/bag.dart'

import '../lib/baz.dart';

 

 

avoid_dynamic_calls

 

메서드를 호출하거나 속성에 접글할때 동적인 타입 피하는 룰
NoSuchMethodError, NullError 막아줌
dynamic은 typeScript의 any와 같은 개념

 

// 아름다운 방법

void explicitDynamicType(dynamic object) {
  print(object.foo());
}

void implicitDynamicType(object) {
  print(object.foo());
}

abstract class SomeWrapper {
  T doSomething<T>();
}

void inferredDynamicType(SomeWrapper wrapper) {
  var object = wrapper.doSomething();
  print(object.foo());
}

void callDynamic(dynamic function) {
  function();
}

void functionType(Function function) {
  function();
}

 

// 그지 같은 방법
void explicitType(Fooable object) {
  object.foo();
}

void castedType(dynamic object) {
  (object as Fooable).foo();
}

abstract class SomeWrapper {
  T doSomething<T>();
}

void inferredType(SomeWrapper wrapper) {
  var object = wrapper.doSomething<Fooable>();
  object.foo();
}

void functionTypeWithParameters(Function() function) {
  function();
}

 

avoid_empty_else

 

else 구문에 빈칸 피하는 룰

 

// 그지 같은 방법

if (x > y)
  print("1");
else ;
  print("2");

 

// 아름다운 방법

if (x > y)
  print("1");
else 
  print("2");

 

 

avoid_print

 

정식 출시앱에서는 print 호출 피하는 룰
굳이 출시하는 코드에서도 logging 이 필요하다면 
kDebugMode 확인하고 debugPrint 사용할길 추천한다. 

 

// 그지 같은 방법

void f(int x) {
  print('debug: $x');
  ...
}

 

// 아름다운 방법

void f(int x) {
  debugPrint('debug: $x');
  ...
}

void f(int x) {
  log('log: $x');
  ...
}

void f(int x) {
  if (kDebugMode) {
      print('debug: $x');
  }
  ...
}

 

 

avoid_relative_lib_imports

 

상대 경로 import 를 피하는 룰
always_use_package_imports와 같은 기능임

 

// 그지 같은 방법

import 'package:foo/bar.dart';

import 'baz.dart'

 

// 아름다운 방법

import 'package:foo/bar.dart';

import '../lib/baz.dart';

 

 

avoid_returning_null_for_future

future 에대한 null 반환 피하는 룰
개발자들 대부분이 함수에 async 넣는 것을 쉽게 잊어버린다. 

 

avoid_slow_async_io

비동기 느린 dart:io 메서드 피하기
아래 것들은 동기적 보다 많이 느리다. 

  • Directory.exists
  • Directory.stat
  • File.lastModified
  • File.exists
  • File.stat
  • FileSystemEntity.isDirectory
  • FileSystemEntity.isFile
  • FileSystemEntity.isLink
  • FileSystemEntity.type
// 그지 같은 방법

import 'dart:io';

Future<Null> someFunction() async {
  var file = File('/path/to/my/file');
  var now = DateTime.now();
  if ((await file.lastModified()).isBefore(now)) print('before'); // LINT
}

 

// 아름다운 방법

import 'dart:io';

Future<Null> someFunction() async {
  var file = File('/path/to/my/file');
  var now = DateTime.now();
  if (file.lastModifiedSync().isBefore(now)) print('before'); // OK
}

 

 

avoid_type_to_string

 

최소한의 결과를 내기위해 ?(아마 메모리를 말하는듯)
앱 출시용 코드에서 .toString()  피하는 룰

.toString()는 타입의 사용자 정의 된 이름을 문법적으로 
반환하지 않기 때문에 호출을 피해야 한다.

개발모드에서 컴파일은 전체 이름의 코드 사이즈 고려 안해도 되지만 
출시모드에서는 기호같은 같은 최소화
하도록  종종 사용하기도 한다. 

 

// 그지 같은 방법

void bar(Object other) {
  if (other.runtimeType.toString() == 'Bar') {
    doThing();
  }
}

Object baz(Thing myThing) {
  return getThingFromDatabase(key: myThing.runtimeType.toString());
}

 

// 아름 다운 방법

void bar(Object other) {
  if (other is Bar) {
    doThing();
  }
}

class Thing {
  String get thingTypeKey => ...
}

Object baz(Thing myThing) {
  return getThingFromDatabase(key: myThing.thingTypeKey);
}

 

avoid_types_as_parameter_names

타입을 파라미터 이름 처럼 쓰는거 피하는 룰

 

// 그지 같은 방법

m(f(int));

 

// 아름 다운 방법 

m(f(int v));

 

 

avoid_web_libraries_in_flutter

 

Flutter 웹 플러그인 패키지 외부에 웹 전용 라이브러리(dart:html, dart:js and dart:js_util)
사용 피하기 

 

cancel_subscriptions

 

dart.async.StreamSubscription의 인스턴스의 cancel() 호출는
예기치하는 동작이나 행동으로 인한 메모리를 누수를 막아준다.

// 그지 같은 방법 

class A {
  StreamSubscription _subscriptionA; // LINT
  void init(Stream stream) {
    _subscriptionA = stream.listen((_) {});
  }
}

void someFunction() {
  StreamSubscription _subscriptionF; // LINT
}

 

// 아름다운 방법

class B {
  StreamSubscription _subscriptionB; // OK
  void init(Stream stream) {
    _subscriptionB = stream.listen((_) {});
  }

  void dispose(filename) {
    _subscriptionB.cancel();
  }
}

void someFunctionOK() {
  StreamSubscription _subscriptionB; // OK
  _subscriptionB.cancel();
}

 

close_sinks

dart.core.Sink 의 인스턴스의 close() 호출는
예기치하는 동작이나 행동으로 인한 메모리를 누수를 막아준다.

// 그지 같은 방법

class A {
  IOSink _sinkA;
  void init(filename) {
    _sinkA = File(filename).openWrite(); // LINT
  }
}

void someFunction() {
  IOSink _sinkF; // LINT
}

 

// 아름다운 방법
void someFunctionOK() {
  IOSink _sinkFOK; // OK
  _sinkFOK.close();
}

 

comment_references

 

doc comments의 범위에서만 참조 가능 

 

// 아름다운 방법

/// Return the larger of [a] or [b].
int max_int(int a, int b) { ... }

 

// 그지 같은 방법
// outOfScopeId 는 범위에서 벗어난 것으로 추상
/// Return true if [value] is larger than [outOfScopeId].
bool isOutOfRange(int value) { ... }

 

control_flow_in_finally

 

흐름 제어 구문에서 finally block 피하는 룰

finally block 은 디버깅을 어렵게 하는 원인이 될수 있다. 

// 아름다운 방법

class Ok {
  double compliantMethod() {
    var i = 5;
    try {
      i = 1 / 0;
    } catch (e) {
      print(e); // OK
    }
    return i;
  }
}

 

// 그지같은 방법

class BadReturn {
  double nonCompliantMethod() {
    try {
      return 1 / 0;
    } catch (e) {
      print(e);
    } finally {
      return 1.0; // LINT
    }
  }
}

class BadContinue {
  double nonCompliantMethod() {
    for (var o in [1, 2]) {
      try {
        print(o / 0);
      } catch (e) {
        print(e);
      } finally {
        continue; // LINT
      }
    }
    return 1.0;
  }
}

class BadBreak {
  double nonCompliantMethod() {
    for (var o in [1, 2]) {
      try {
        print(o / 0);
      } catch (e) {
        print(e);
      } finally {
        break; // LINT
      }
    }
    return 1.0;
  }
}

 

 

diagnostic_describe_all_properties

 

디버그 메소드안에서 모든 공용 속성을 참조 
runtime시 디버그 가능성 개선을 위한 debugFillProperties(...) 또는 
debugDescribeChildren(...) 메서드의
Diagnosticable 실행은  모든 공용 속성의 참조 한다.  

공용 속성들은 fields와 getters 처럼 정의 되어 있다. 

 

// 그지 같은 방법

class Absorber extends Widget {
  bool get absorbing => _absorbing;
  bool _absorbing;
  bool get ignoringSemantics => _ignoringSemantics;
  bool _ignoringSemantics;
  @override
  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
    properties.add(DiagnosticsProperty<bool>('absorbing', absorbing));
    // Missing reference to ignoringSemantics
  }
}

 

// 아름다운 방법

class Absorber extends Widget {
  bool get absorbing => _absorbing;
  bool _absorbing;
  bool get ignoringSemantics => _ignoringSemantics;
  bool _ignoringSemantics;
  @override
  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
    properties.add(DiagnosticsProperty<bool>('absorbing', absorbing));
    properties.add(DiagnosticsProperty<bool>('ignoringSemantics', ignoringSemantics));
  }
}

 

 

empty_statements

 

빈공간 피하는 룰 
거의 모든 버그는 이 빈공간에서 발생한다. 

 

// 그지 같은 방법

if (complicated.expression.foo());
  bar();
  
  if (complicated.expression.foo()) ;
bar();

 

// 아름다운 방법

if (complicated.expression.foo())
  bar();

 

 

hash_and_equals

 

overriding ==  면 override hashCode를  
overriding 
hashCode면  overriding == 선호

dart 언어에서의 모든 객체는 hashCode를 가지고 있다. 
== 연산자와 객체의 속성의 hashCode 둘다 
해시 맵 실행이 올바르게 기능하려면 , 일관성이 있어야 하는데 

따라서 ==를 재정의할 때는 일관성을 유지하기 위해 hashCode도 재정의해야 한다.
마찬가지로, hashCode가 재정의되면 ==도 재정의되어야 한다. 

 

// 그지 같은 방법

class Bad {
  final int value;
  Bad(this.value);

  @override
  bool operator ==(Object other) => other is Bad && other.value == value;
}

 

// 아름다운 방법

class Better {
  final int value;
  Better(this.value);

  @override
  bool operator ==(Object other) =>
      other is Better &&
      other.runtimeType == runtimeType &&
      other.value == value;

  @override
  int get hashCode => value.hashCode;
}

 

invariant_booleans

 

이기능은 아직 테스트중

조건문이 false만 또는 true만 평가할수 있는 불필요한 구문들을 
찾아서 알려준다. 

 

// 그지 같은 방법

// 조건문에 foo가 bar 같거나 틀리거나를 동시에 사용 불가
if(foo == bar && something && foo != bar) {...}


void compute(int foo) {
  if (foo == 4) {
    doSomething();
    // foo가 4와 같은데 다음조건은 당연히 false 대한 처리가 올거라 알고 있다. 
    if (foo > 4) {...}
    ...
  }
  ...
}

void compute(bool foo) {
  if (foo) {
    return;
  }
  doSomething();
  // foo 는 여기서 항상 false 이다. 
  if (foo){...}
  ...
}

 

// 아름다운 방법

void nestedOK() {
  if (foo == bar) {
    foo = baz;
    if (foo != bar) {...}
  }
}

void nestedOk2() {
  if (foo == bar) {
    return;
  }

  foo = baz;
  if (foo == bar) {...} // OK
}

void nestedOk5() {
  if (foo != null) {
    if (bar != null) {
      return;
    }
  }

  if (bar != null) {...} // OK
}

 

 

iterable_contains_unrelated_type

 

Iterable contains 가 파라미터 타입과 다르면 경고 

 

// 그지 같은 방법

void someFunction() {
  var list = <int>[];
  if (list.contains('1')) print('someFunction'); // LINT
}

void someFunction3() {
  List<int> list = <int>[];
  if (list.contains('1')) print('someFunction3'); // LINT
}

void someFunction8() {
  List<DerivedClass2> list = <DerivedClass2>[];
  DerivedClass3 instance;
  if (list.contains(instance)) print('someFunction8'); // LINT
}

abstract class SomeIterable<E> implements Iterable<E> {}

abstract class MyClass implements SomeIterable<int> {
  bool badMethod(String thing) => this.contains(thing); // LINT
}

 

 

// 아름다운 방법

void someFunction10() {
  var list = [];
  if (list.contains(1)) print('someFunction10'); // OK
}

void someFunction1() {
  var list = <int>[];
  if (list.contains(1)) print('someFunction1'); // OK
}

void someFunction4() {
  List<int> list = <int>[];
  if (list.contains(1)) print('someFunction4'); // OK
}

void someFunction5() {
  List<ClassBase> list = <ClassBase>[];
  DerivedClass1 instance;
  if (list.contains(instance)) print('someFunction5'); // OK
}

abstract class ClassBase {}

class DerivedClass1 extends ClassBase {}

void someFunction6() {
  List<Mixin> list = <Mixin>[];
  DerivedClass2 instance;
  if (list.contains(instance)) print('someFunction6'); // OK
}

abstract class ClassBase {}

abstract class Mixin {}

class DerivedClass2 extends ClassBase with Mixin {}

void someFunction7() {
  List<Mixin> list = <Mixin>[];
  DerivedClass3 instance;
  if (list.contains(instance)) print('someFunction7'); // OK
}

abstract class ClassBase {}

abstract class Mixin {}

class DerivedClass3 extends ClassBase implements Mixin {}

 

list_remove_unrelated_type

 

Iterable remove 가 파라미터 타입과 다르면 경고 

 

// 그지 같은 방법

void someFunction() {
  var list = <int>[];
  if (list.remove('1')) print('someFunction'); // LINT
}

void someFunction3() {
  List<int> list = <int>[];
  if (list.remove('1')) print('someFunction3'); // LINT
}

void someFunction8() {
  List<DerivedClass2> list = <DerivedClass2>[];
  DerivedClass3 instance;
  if (list.remove(instance)) print('someFunction8'); // LINT
}

abstract class SomeList<E> implements List<E> {}

abstract class MyClass implements SomeList<int> {
  bool badMethod(String thing) => this.remove(thing); // LINT
}

 

// 아름다운 방법

void someFunction10() {
  var list = [];
  if (list.remove(1)) print('someFunction10'); // OK
}

void someFunction1() {
  var list = <int>[];
  if (list.remove(1)) print('someFunction1'); // OK
}

void someFunction4() {
  List<int> list = <int>[];
  if (list.remove(1)) print('someFunction4'); // OK
}

void someFunction5() {
  List<ClassBase> list = <ClassBase>[];
  DerivedClass1 instance;
  if (list.remove(instance)) print('someFunction5'); // OK
}

abstract class ClassBase {}

class DerivedClass1 extends ClassBase {}

void someFunction6() {
  List<Mixin> list = <Mixin>[];
  DerivedClass2 instance;
  if (list.remove(instance)) print('someFunction6'); // OK
}

abstract class ClassBase {}

abstract class Mixin {}

class DerivedClass2 extends ClassBase with Mixin {}

void someFunction7() {
  List<Mixin> list = <Mixin>[];
  DerivedClass3 instance;
  if (list.remove(instance)) print('someFunction7'); // OK
}

abstract class ClassBase {}

abstract class Mixin {}

class DerivedClass3 extends ClassBase implements Mixin {}

 

literal_only_boolean_expressions

 

boolean 표현식은 literals만 가능하다

void bad() {
  if (1 != 0 || 3 < 4 && true) {} // LINT
}

 

단 while 문은 예외 

void good() {
  while (true) {
    // Do stuff.
  }
}

 

no_adjacent_strings_in_list

 

list에 인접 string 사용 불가
',' 를 깜빡 할수 있는 신호라..

 

// 아름다운 방법

ist<String> list = <String>[
  'a' +
  'b',
  'c',
];

 

// 그지같은 방법

List<String> list = <String>[
  'a'
  'b',
  'c',
];
728x90
반응형

'모바일 APP > Flutter' 카테고리의 다른 글

코드 컨밴션 Code Convention (명명 규칙)  (0) 2022.03.31