오픈 소스 모험: 에피소드 76: Ameba linter for Crystal
13289 단어 crystal
최근에 약간의 Crystal 코드를 작성했으므로 어떻게 진행되는지 살펴보겠습니다.
기본 설정과
--all
모두에서 실행하겠습니다. 기본 설정으로 잘못된 긍정이 거의 없기를 바랍니다.Shebang 문제
ameba
는 #!/usr/bin/env crystal
로 시작하는 Crystal 스크립트를 무시했습니다. 확장자가 .cr
인 파일만 확인했습니다. 이것은 Crystal의 주요 사용 사례는 아니지만 유효한 사용 사례입니다.Crystal의 VSCode 확장에는 동일한 문제가 있습니다.
이 문제는
.ameba.yml
구성 파일로 해결할 수 있습니다.crystal-z3 및 Lint/UselessAssign
기본 설정에서 한 가지 문제를 찾습니다.
$ ameba
Inspecting 25 files
......................F..
src/z3/api.cr:89:7
[W] Lint/UselessAssign: Useless assignment to variable `var`
> var = LibZ3.mk_const(Context, name_sym, sort)
^-^
Finished in 223.71 milliseconds
25 inspected, 1 failure
컨텍스트를 표시하지 않지만 다음 방법에서 가져온 것입니다.
def mk_const(name, sort)
name_sym = LibZ3.mk_string_symbol(Context, name)
var = LibZ3.mk_const(Context, name_sym, sort)
end
그것은 확실히 진짜입니다.
--all 및 Lint/ComparisonToBoolean이 있는 crystal-z3
이로 인해 엄청난 수의
Lint/ComparisonToBoolean
문제가 발생하며 몇 가지 예는 다음과 같습니다.spec/bool_spec.cr:10:6 [Correctable]
[W] Lint/ComparisonToBoolean: Comparison to a boolean is pointless
> [a == true, b == true, c == (a & b)].should have_solution({c => true})
^--------^
spec/model_spec.cr:15:19 [Correctable]
[W] Lint/ComparisonToBoolean: Comparison to a boolean is pointless
> solver.assert a == true
^-------^
[W] Lint/ComparisonToBoolean: Comparison to a boolean is pointless
> raise Z3::Exception.new("Cannot evaluate") unless result == true
^------------^
음,
crystal-z3
는 Z3::BoolExpr#==
를 재정의하므로 의미상 이 보푸라기 규칙은 누락되었지만 이러한 비정상적인 코드를 지원하지 않는 것에 대해 ameba
를 비난할 수는 없습니다.이것이 일반적으로 좋은 규칙인지 잘 모르겠습니다.
x
가 부울 이외의 것이 될 수 있는 한 x == true
는 x
와 완전히 동일하지 않습니다.Thue Interptetter 및 성능/CompactAfterMap
시리즈의 Thue 인터프리터에서 발견된 두 가지 문제가 있습니다.
./episode-65-crystal-thue-randomized-finite-automaton/thue_rfa.cr:162:27
[C] Performance/CompactAfterMap: Use `compact_map {...}` instead of `map {...}.compact`
> next_tries = active.map{|t| t[char]}.compact
^-----------------------^
compact_map
는 이러한 병합된 공통 작업이 더 성능이 좋은 경향이 있기 때문에 적절한 제안입니다. 그러나 이와 같은 방법은 모든 언어의 모든 부 버전마다 계속 추가되므로 어떤 언어에 어떤 조합이 있는지 기억하기 어렵습니다../episode-65-crystal-thue-randomized-finite-automaton/thue_rfa.cr:191:13
[W] Lint/UselessAssign: Useless assignment to variable `line_no`
> line, line_no = lines.shift
^-----^
나는 이것을 좋아하지 않는다. 쓸모없는 단일 변수 할당을 제거할 수 있으므로 의미가 있지만 배열 구조 분해에서 대안은 다음 중 하나와 같은 보기 흉한 코드입니다.
line, _ = lines.shift
line, _line_no = lines.shift
line = lines.shift[0]
차라리 원작을 고수하겠습니다.
그러나 그것은
ameba
에만 국한되지 않으며 대부분의 린터의 "쓸모없는 변수 할당"검사에는 다중 할당에 대한 예외가 없습니다.참고로 명명된 튜플인 경우 다음과 같은 작업을 수행할 수 있습니다(JavaScript here).
let {line} = lines.shift()
해시/명명된 튜플/객체를 분해하기 위해 이러한 추가 항목을 포함할 이유가 없습니다.
인터프리터와 --all
--all
를 사용하지 않으므로 오탐지가 예상됩니다. 여기에 한 가지 예가 있습니다. 이와 같은 경우가 더 있습니다../episode-65-crystal-thue-randomized-finite-automaton/thue_rfa.cr:221:5 [Correctable]
[C] Style/GuardClause: Use a guard clause (`return unless debug`) instead of wrapping the code inside a conditional expression.
> if debug
^^
그래서 linter는 우리가 이것을 대체하기를 원합니다:
def run(debug)
@state = @initial
if debug
@rules.each do |rule|
STDERR.puts rule
end
end
while match = @rfa.not_nil!.random_match(@state)
rule = match[:rule]
idx = match[:idx]
if debug
STDERR.puts "Applying rule #{rule} at #{idx} to #{@state.inspect}"
end
@state = rule.apply(@state, idx)
end
if debug
STDERR.puts "No more matches. Final state: #{@state.inspect}"
end
end
이것으로:
def run(debug)
@state = @initial
if debug
@rules.each do |rule|
STDERR.puts rule
end
end
while match = @rfa.not_nil!.random_match(@state)
rule = match[:rule]
idx = match[:idx]
if debug
STDERR.puts "Applying rule #{rule} at #{idx} to #{@state.inspect}"
end
@state = rule.apply(@state, idx)
end
return unless debug
STDERR.puts "No more matches. Final state: #{@state.inspect}"
end
그것은 개선되지 않을 것입니다. 메서드 또는 루프 본문의 시작 부분에 있는 가드 절은 일반적인 패턴이지만 본문 뒤에 배치하는 것은 이상합니다.
오픈 소스 모험과 스타일/VerboseBlock
이상하게 실행해야 하는 모든 파일을 보려면
ameba
를 얻으려면:$ ameba `git grep -l '#!/usr/bin/env crystal'` `git ls "*.cr"`
다음은 몇 가지 제안 사항입니다.
episode-65/minesweeper.cr:40:25 [Correctable]
[C] Style/VerboseBlock: Use short block notation instead: `map(&.ite(1, 0))`
> neighbourhood(x, y).map{|v| v.ite(1, 0)}.reduce{|a,b| a+b}
^------------------^
이것은 Ruby에서 직접 수행할 수 없는 Crystal 코드입니다. 합리적인 제안입니다.
episode-70/aquarium:17:25 [Correctable]
[C] Style/VerboseBlock: Use short block notation instead: `map(&.[2..].chars)`
> @board = lines[2..].map{|l| l[2..].chars}
^-------------------^
이것은 조금 많은 것 같지만 익숙해 질 수 있습니다.
episode-72/dominosa:65:23
[W] Lint/UnusedArgument: Unused argument `type`. If it's necessary, use `_` as an argument name to indicate that it won't be used.
> @dominos.each do |type, vars|
^
이것은
.each_value
를 사용할 수 있지만 다중 할당과 마찬가지로 @dominos.each do |_type, vars|
또는 @dominos.each do |_, vars|
의 제안이 마음에 들지 않습니다.episode-68/switches:50:67 [Correctable]
[C] Performance/ChainedCallWithNoBang: Use bang method variant `sort!` after chained `select` call
> puts @nodes.select{|n| model[@switches[n]].to_s == "true" }.sort.join(" ")
^--^
성능 면에서 왜 이치에 맞는지 알겠지만 체인 중간에 있는
.sort!
가 너무 이상해서 안 하는 편이 낫습니다.--all
와 관련된 새로운 유형의 제안이 없었습니다.당신은 아메바를 사용해야합니까?
전반적으로 괜찮은 linter처럼 보이며 기본값은 너무 많은 오 탐지를 생성하지 않습니다.
어떤 종류의 심층 분석도 수행하지 않습니다. 예를 들어
if
유형 문제로 인해 항상 true
인 일부 LBool
수표가 있다는 것을 알고 있습니다. 순전히 구문 검사에는 표시되지 않기 때문입니다.다음에 온다
좋아요, 지금은 Crystal로 충분합니다. 다음 에피소드에서는 약속대로 다른 기술을 시도하겠습니다.
Reference
이 문제에 관하여(오픈 소스 모험: 에피소드 76: Ameba linter for Crystal), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/taw/open-source-adventures-episode-76-ameba-linter-for-crystal-19nn텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)