# typed: strict# frozen_string_literal: truemoduleSpoommoduleSorbetmoduleErrorsDEFAULT_ERROR_URL_BASE="https://srb.help/"class<<selfextendT::Sigsig{params(errors: T::Array[Error]).returns(T::Array[Error])}defsort_errors_by_code(errors)errors.sort_by{|e|[e.code,e.file,e.line,e.message]}endend# Parse errors from Sorbet outputclassParserextendT::SigHEADER=T.let(["👋 Hey there! Heads up that this is not a release build of sorbet.","Release builds are faster and more well-supported by the Sorbet team.","Check out the README to learn how to build Sorbet in release mode.","To forcibly silence this error, either pass --silence-dev-message,","or set SORBET_SILENCE_DEV_MESSAGE=1 in your shell environment.",],T::Array[String],)class<<selfextendT::Sigsig{params(output: String,error_url_base: String).returns(T::Array[Error])}defparse_string(output,error_url_base: DEFAULT_ERROR_URL_BASE)parser=Spoom::Sorbet::Errors::Parser.new(error_url_base: error_url_base)parser.parse(output)endendsig{params(error_url_base: String).void}definitialize(error_url_base: DEFAULT_ERROR_URL_BASE)@errors=T.let([],T::Array[Error])@error_line_match_regex=T.let(error_line_match_regexp(error_url_base),Regexp)@current_error=T.let(nil,T.nilable(Error))endsig{params(output: String).returns(T::Array[Error])}defparse(output)output.each_linedo|line|breakif/^No errors! Great job\./.match?(line)breakif/^Errors: /.match?(line)nextifHEADER.include?(line.strip)nextifline=="\n"if(error=match_error_line(line))close_errorif@current_erroropen_error(error)nextendappend_error(line)if@current_errorendclose_errorif@current_error@errorsendprivatesig{params(error_url_base: String).returns(Regexp)}deferror_line_match_regexp(error_url_base)url=Regexp.escape(error_url_base)%r{
^ # match beginning of line
(\S[^:]*) # capture filename as something that starts with a non-space character
# followed by anything that is not a colon character
: # match the filename - line number seperator
(\d+) # capture the line number
:\s # match the line number - error message separator
(.*) # capture the error message
\s#{url} # match the error code url prefix
(\d+) # capture the error code
$ # match end of line
}xendsig{params(line: String).returns(T.nilable(Error))}defmatch_error_line(line)match=line.match(@error_line_match_regex)returnunlessmatchfile,line,message,code=match.capturesError.new(file,line&.to_i,message,code&.to_i)endsig{params(error: Error).void}defopen_error(error)raise"Error: Already parsing an error!"if@current_error@current_error=errorendsig{void}defclose_errorraise"Error: Not already parsing an error!"unless@current_error@errors<<@current_error@current_error=nilendsig{params(line: String).void}defappend_error(line)raise"Error: Not already parsing an error!"unless@current_errorfilepath_match=line.match(/^ (.*?):\d+/)iffilepath_match&&filepath_match[1]@current_error.files_from_error_sections<<T.must(filepath_match[1])end@current_error.more<<lineendendclassErrorincludeComparableextendT::Sigsig{returns(T.nilable(String))}attr_reader:file,:messagesig{returns(T.nilable(Integer))}attr_reader:line,:codesig{returns(T::Array[String])}attr_reader:more# Other files associated with the errorsig{returns(T::Set[String])}attr_reader:files_from_error_sectionssigdoparams(file: T.nilable(String),line: T.nilable(Integer),message: T.nilable(String),code: T.nilable(Integer),more: T::Array[String],).voidenddefinitialize(file,line,message,code,more=[])@file=file@line=line@message=message@code=code@more=more@files_from_error_sections=T.let(Set.new,T::Set[String])end# By default errors are sorted by locationsig{params(other: T.untyped).returns(Integer)}def<=>(other)return0unlessother.is_a?(Error)[file,line,code,message]<=>[other.file,other.line,other.code,other.message]endsig{returns(String)}defto_s"#{file}:#{line}: #{message} (#{code})"endendendendend