mirror of
				https://gitea.invidious.io/iv-org/shard-ameba.git
				synced 2024-08-15 00:53:29 +00:00 
			
		
		
		
	Merge pull request #312 from crystal-ameba/Sija/refactors-round-3
This commit is contained in:
		
						commit
						be65ba2a92
					
				
					 26 changed files with 114 additions and 127 deletions
				
			
		|  | @ -59,7 +59,7 @@ module Ameba | |||
|         source = Source.new "a = 42", "source.cr" | ||||
|         output = explanation(source) | ||||
|         output.should contain "DETAILED DESCRIPTION" | ||||
|         output.should contain "TO BE DONE..." | ||||
|         output.should contain "Rule extended description" | ||||
|       end | ||||
| 
 | ||||
|       it "writes nothing if location not found" do | ||||
|  |  | |||
|  | @ -24,6 +24,7 @@ module Ameba | |||
|     end | ||||
|   end | ||||
| 
 | ||||
|   # Rule extended description | ||||
|   class ErrorRule < Rule::Base | ||||
|     properties do | ||||
|       description "Always adds an error at 1:1" | ||||
|  |  | |||
|  | @ -175,8 +175,9 @@ module Ameba::AST | |||
|         node.accept self | ||||
|       end | ||||
| 
 | ||||
|       def references?(node : Crystal::Var) | ||||
|         @macro_literals.any?(&.value.includes?(node.name)) | ||||
|       # @[AlwaysInline] | ||||
|       private def includes_reference?(val) | ||||
|         val.to_s.includes?(@reference) | ||||
|       end | ||||
| 
 | ||||
|       def visit(node : Crystal::ASTNode) | ||||
|  | @ -184,23 +185,22 @@ module Ameba::AST | |||
|       end | ||||
| 
 | ||||
|       def visit(node : Crystal::MacroLiteral) | ||||
|         !(self.references ||= node.value.includes?(@reference)) | ||||
|         !(@references ||= includes_reference?(node.value)) | ||||
|       end | ||||
| 
 | ||||
|       def visit(node : Crystal::MacroExpression) | ||||
|         !(self.references ||= node.exp.to_s.includes?(@reference)) | ||||
|         !(@references ||= includes_reference?(node.exp)) | ||||
|       end | ||||
| 
 | ||||
|       def visit(node : Crystal::MacroFor) | ||||
|         exp, body = node.exp, node.body | ||||
|         !(self.references ||= exp.to_s.includes?(@reference) || | ||||
|                               body.to_s.includes?(@reference)) | ||||
|         !(@references ||= includes_reference?(node.exp) || | ||||
|                           includes_reference?(node.body)) | ||||
|       end | ||||
| 
 | ||||
|       def visit(node : Crystal::MacroIf) | ||||
|         !(self.references ||= node.cond.to_s.includes?(@reference) || | ||||
|                               node.then.to_s.includes?(@reference) || | ||||
|                               node.else.to_s.includes?(@reference)) | ||||
|         !(@references ||= includes_reference?(node.cond) || | ||||
|                           includes_reference?(node.then) || | ||||
|                           includes_reference?(node.else)) | ||||
|       end | ||||
|     end | ||||
| 
 | ||||
|  |  | |||
|  | @ -12,15 +12,17 @@ module Ameba::AST | |||
|     # :nodoc: | ||||
|     def visit(node : Crystal::Require) | ||||
|       require_nodes << node | ||||
|       true | ||||
|     end | ||||
| 
 | ||||
|     # If a top level node is Crystal::Expressions traverse the children. | ||||
|     # If a top level node is `Crystal::Expressions`, | ||||
|     # then always traverse the children. | ||||
|     def visit(node : Crystal::Expressions) | ||||
|       true | ||||
|     end | ||||
| 
 | ||||
|     # A general visit method for rest of the nodes. | ||||
|     # Returns false meaning all child nodes will not be traversed. | ||||
|     # Returns `false`, meaning all child nodes will not be traversed. | ||||
|     def visit(node : Crystal::ASTNode) | ||||
|       false | ||||
|     end | ||||
|  |  | |||
|  | @ -111,7 +111,6 @@ class Ameba::Config | |||
|   # config.formatter = custom_formatter | ||||
|   # config.formatter | ||||
|   # ``` | ||||
|   # | ||||
|   property formatter : Formatter::BaseFormatter do | ||||
|     Formatter::DotFormatter.new | ||||
|   end | ||||
|  | @ -249,17 +248,17 @@ class Ameba::Config | |||
|         {% end %} | ||||
|       {% end %} | ||||
| 
 | ||||
|       {% if properties["enabled".id] == nil %} | ||||
|       {% unless properties["enabled".id] %} | ||||
|         @[YAML::Field(key: "Enabled")] | ||||
|         property? enabled = true | ||||
|       {% end %} | ||||
| 
 | ||||
|       {% if properties["severity".id] == nil %} | ||||
|       {% unless properties["severity".id] %} | ||||
|         @[YAML::Field(key: "Severity", converter: Ameba::SeverityYamlConverter)] | ||||
|         property severity = {{ @type }}.default_severity | ||||
|       {% end %} | ||||
| 
 | ||||
|       {% if properties["excluded".id] == nil %} | ||||
|       {% unless properties["excluded".id] %} | ||||
|         @[YAML::Field(key: "Excluded")] | ||||
|         property excluded : Array(String)? | ||||
|       {% end %} | ||||
|  |  | |||
|  | @ -5,11 +5,11 @@ module Ameba::Formatter | |||
|       output << "Disabled rules using inline directives:\n\n" | ||||
| 
 | ||||
|       sources.each do |source| | ||||
|         source.issues.select(&.disabled?).each do |e| | ||||
|           next unless loc = e.location | ||||
|         source.issues.select(&.disabled?).each do |issue| | ||||
|           next unless loc = issue.location | ||||
| 
 | ||||
|           output << "#{source.path}:#{loc.line_number}".colorize(:cyan) | ||||
|           output << " #{e.rule.name}\n" | ||||
|           output << " #{issue.rule.name}\n" | ||||
|         end | ||||
|       end | ||||
|     end | ||||
|  |  | |||
|  | @ -4,8 +4,7 @@ module Ameba::Formatter | |||
|   # A formatter that shows the detailed explanation of the issue at | ||||
|   # a specific location. | ||||
|   class ExplainFormatter | ||||
|     HEADING = "## " | ||||
|     PREFIX  = " " | ||||
|     HEADING_MARKER = "## " | ||||
| 
 | ||||
|     include Util | ||||
| 
 | ||||
|  | @ -18,13 +17,18 @@ module Ameba::Formatter | |||
|     # Second argument is *location* which indicates the location to explain. | ||||
|     # | ||||
|     # ``` | ||||
|     # ExplainFormatter.new output, | ||||
|     # ExplainFormatter.new output, { | ||||
|     #   file:   path, | ||||
|     #   line:   line_number, | ||||
|     #   column: column_number | ||||
|     #   column: column_number, | ||||
|     # } | ||||
|     # ``` | ||||
|     def initialize(@output, location) | ||||
|       @location = Crystal::Location.new(location[:file], location[:line], location[:column]) | ||||
|       @location = Crystal::Location.new( | ||||
|         location[:file], | ||||
|         location[:line], | ||||
|         location[:column] | ||||
|       ) | ||||
|     end | ||||
| 
 | ||||
|     # Reports the explanations at the *@location*. | ||||
|  | @ -39,42 +43,59 @@ module Ameba::Formatter | |||
|     end | ||||
| 
 | ||||
|     private def explain(source, issue) | ||||
|       rule = issue.rule | ||||
| 
 | ||||
|       return unless location = issue.location | ||||
| 
 | ||||
|       output_title "ISSUE INFO" | ||||
|       output << '\n' | ||||
|       output_title "Issue info" | ||||
|       output_paragraph [ | ||||
|         issue.message.colorize(:red).to_s, | ||||
|         location.to_s.colorize(:cyan).to_s, | ||||
|         issue.message.colorize(:red), | ||||
|         location.to_s.colorize(:cyan), | ||||
|       ] | ||||
| 
 | ||||
|       if affected_code = affected_code(issue, context_lines: 3) | ||||
|         output_title "AFFECTED CODE" | ||||
|         output_title "Affected code" | ||||
|         output_paragraph affected_code | ||||
|       end | ||||
| 
 | ||||
|       rule = issue.rule | ||||
| 
 | ||||
|       output_title "Rule info" | ||||
|       output_paragraph "%s of a %s severity" % { | ||||
|         rule.name.colorize(:magenta), | ||||
|         rule.severity.to_s.colorize(rule.severity.color), | ||||
|       } | ||||
| 
 | ||||
|       if rule.responds_to?(:description) | ||||
|         output_title "RULE INFO" | ||||
|         output_paragraph [rule.severity.to_s, rule.name, rule.description] | ||||
|         output_paragraph rule.description | ||||
|       end | ||||
| 
 | ||||
|       output_title "DETAILED DESCRIPTION" | ||||
|       output_paragraph(rule.class.parsed_doc || "TO BE DONE...") | ||||
|       rule_doc = colorize_code_fences(rule.class.parsed_doc) | ||||
|       return unless rule_doc | ||||
| 
 | ||||
|       output_title "Detailed description" | ||||
|       output_paragraph rule_doc | ||||
|     end | ||||
| 
 | ||||
|     private def colorize_code_fences(string) | ||||
|       return unless string | ||||
|       string | ||||
|         .gsub(/```(.+?)```/m, &.colorize(:dark_gray)) | ||||
|         .gsub(/`(?!`)(.+?)`/, &.colorize(:dark_gray)) | ||||
|     end | ||||
| 
 | ||||
|     private def output_title(title) | ||||
|       output << HEADING.colorize(:yellow) << title.colorize(:yellow) << '\n' | ||||
|       output << '\n' | ||||
|       output << HEADING_MARKER.colorize(:yellow) | ||||
|       output << title.upcase.colorize(:yellow) | ||||
|       output << "\n\n" | ||||
|     end | ||||
| 
 | ||||
|     private def output_paragraph(paragraph : String) | ||||
|       output_paragraph(paragraph.lines) | ||||
|     end | ||||
| 
 | ||||
|     private def output_paragraph(paragraph : Array(String)) | ||||
|     private def output_paragraph(paragraph : Array) | ||||
|       paragraph.each do |line| | ||||
|         output << PREFIX << line << '\n' | ||||
|         output << ' ' << line << '\n' | ||||
|       end | ||||
|       output << '\n' | ||||
|     end | ||||
|  |  | |||
|  | @ -3,16 +3,16 @@ module Ameba::Formatter | |||
|     @mutex = Mutex.new | ||||
| 
 | ||||
|     def source_finished(source : Source) | ||||
|       source.issues.each do |e| | ||||
|         next if e.disabled? | ||||
|         next if e.correctable? && config[:autocorrect]? | ||||
|       source.issues.each do |issue| | ||||
|         next if issue.disabled? | ||||
|         next if issue.correctable? && config[:autocorrect]? | ||||
| 
 | ||||
|         next unless loc = e.location | ||||
|         next unless loc = issue.location | ||||
| 
 | ||||
|         @mutex.synchronize do | ||||
|           output.printf "%s:%d:%d: %s: [%s] %s\n", | ||||
|             source.path, loc.line_number, loc.column_number, e.rule.severity.symbol, | ||||
|             e.rule.name, e.message.gsub('\n', " ") | ||||
|             source.path, loc.line_number, loc.column_number, issue.rule.severity.symbol, | ||||
|             issue.rule.name, issue.message.gsub('\n', " ") | ||||
|         end | ||||
|       end | ||||
|     end | ||||
|  |  | |||
|  | @ -24,7 +24,9 @@ module Ameba::Rule::Layout | |||
|       return if source_lines_size == 1 && last_source_line.empty? | ||||
| 
 | ||||
|       last_line_empty = last_source_line.empty? | ||||
|       return if source_lines_size.zero? || (source_lines.last(2).join.presence && last_line_empty) | ||||
|       return if source_lines_size.zero? || | ||||
|                 (source_lines.last(2).join.presence && last_line_empty) | ||||
| 
 | ||||
|       if last_line_empty | ||||
|         issue_for({source_lines_size, 1}, MSG) | ||||
|       else | ||||
|  |  | |||
|  | @ -41,6 +41,7 @@ module Ameba::Rule::Lint | |||
|         op_end_location.column_number - 1 | ||||
|       ) | ||||
|       op_text = source_between(op_location, op_end_location, source.lines) | ||||
| 
 | ||||
|       return unless op_text | ||||
|       return unless MISTAKES.has_key?(op_text) | ||||
| 
 | ||||
|  |  | |||
|  | @ -52,11 +52,7 @@ module Ameba::Rule::Lint | |||
|       check_node(source, node, node.block) | ||||
|     end | ||||
| 
 | ||||
|     def test(source, node : Crystal::While) | ||||
|       check_node(source, node, node.body) if literal?(node.cond) | ||||
|     end | ||||
| 
 | ||||
|     def test(source, node : Crystal::Until) | ||||
|     def test(source, node : Crystal::While | Crystal::Until) | ||||
|       check_node(source, node, node.body) if literal?(node.cond) | ||||
|     end | ||||
| 
 | ||||
|  |  | |||
|  | @ -35,7 +35,7 @@ module Ameba::Rule::Lint | |||
|     private def duplicated_keys(entries) | ||||
|       entries.map(&.key) | ||||
|         .group_by(&.itself) | ||||
|         .tap(&.select! { |_, v| v.size > 1 }) | ||||
|         .select! { |_, v| v.size > 1 } | ||||
|         .map { |k, _| k } | ||||
|     end | ||||
|   end | ||||
|  |  | |||
|  | @ -33,15 +33,7 @@ module Ameba::Rule::Lint | |||
|       issue_for node, MSG if static_literal?(node.cond) | ||||
|     end | ||||
| 
 | ||||
|     def test(source, node : Crystal::If) | ||||
|       check_node source, node | ||||
|     end | ||||
| 
 | ||||
|     def test(source, node : Crystal::Unless) | ||||
|       check_node source, node | ||||
|     end | ||||
| 
 | ||||
|     def test(source, node : Crystal::Case) | ||||
|     def test(source, node : Crystal::If | Crystal::Unless | Crystal::Case) | ||||
|       check_node source, node | ||||
|     end | ||||
|   end | ||||
|  |  | |||
|  | @ -26,8 +26,8 @@ module Ameba::Rule::Lint | |||
| 
 | ||||
|     def test(source, node : Crystal::StringInterpolation) | ||||
|       node.expressions | ||||
|         .select { |e| !e.is_a?(Crystal::StringLiteral) && literal?(e) } | ||||
|         .each { |n| issue_for n, MSG } | ||||
|         .select { |exp| !exp.is_a?(Crystal::StringLiteral) && literal?(exp) } | ||||
|         .each { |exp| issue_for exp, MSG } | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  |  | |||
|  | @ -36,12 +36,12 @@ module Ameba::Rule::Lint | |||
|     end | ||||
| 
 | ||||
|     private def string_coercion_nodes(node) | ||||
|       node.expressions.select do |e| | ||||
|         e.is_a?(Crystal::Call) && | ||||
|           e.name == "to_s" && | ||||
|           e.args.size.zero? && | ||||
|           e.named_args.nil? && | ||||
|           e.obj | ||||
|       node.expressions.select do |exp| | ||||
|         exp.is_a?(Crystal::Call) && | ||||
|           exp.name == "to_s" && | ||||
|           exp.args.size.zero? && | ||||
|           exp.named_args.nil? && | ||||
|           exp.obj | ||||
|       end | ||||
|     end | ||||
|   end | ||||
|  |  | |||
|  | @ -45,13 +45,16 @@ module Ameba::Rule::Lint | |||
| 
 | ||||
|       return if rescues.nil? | ||||
| 
 | ||||
|       shadowed(rescues).each { |tp| issue_for tp, MSG % tp.names.join("::") } | ||||
|       shadowed(rescues).each do |path| | ||||
|         issue_for path, MSG % path.names.join("::") | ||||
|       end | ||||
|     end | ||||
| 
 | ||||
|     private def shadowed(rescues, catch_all = false) | ||||
|       traversed_types = Set(String).new | ||||
| 
 | ||||
|       filter_rescues(rescues).each_with_object([] of Crystal::Path) do |types, shadowed| | ||||
|       rescues = filter_rescues(rescues) | ||||
|       rescues.each_with_object([] of Crystal::Path) do |types, shadowed| | ||||
|         case | ||||
|         when catch_all | ||||
|           shadowed.concat(types) | ||||
|  | @ -62,7 +65,7 @@ module Ameba::Rule::Lint | |||
|           catch_all = true | ||||
|           next | ||||
|         else | ||||
|           nodes = types.select { |tp| traverse(tp.to_s, traversed_types) } | ||||
|           nodes = types.select { |path| traverse(path.to_s, traversed_types) } | ||||
|           shadowed.concat(nodes) unless nodes.empty? | ||||
|         end | ||||
|       end | ||||
|  |  | |||
|  | @ -45,11 +45,7 @@ module Ameba::Rule::Lint | |||
|       ] | ||||
|     end | ||||
| 
 | ||||
|     def test(source, node : Crystal::ProcLiteral, scope : AST::Scope) | ||||
|       find_shadowing source, scope | ||||
|     end | ||||
| 
 | ||||
|     def test(source, node : Crystal::Block, scope : AST::Scope) | ||||
|     def test(source, node : Crystal::ProcLiteral | Crystal::Block, scope : AST::Scope) | ||||
|       find_shadowing source, scope | ||||
|     end | ||||
| 
 | ||||
|  |  | |||
|  | @ -21,7 +21,7 @@ module Ameba::Rule::Lint | |||
|   class Syntax < Base | ||||
|     properties do | ||||
|       description "Reports invalid Crystal syntax" | ||||
|       severity Ameba::Severity::Error | ||||
|       severity :error | ||||
|     end | ||||
| 
 | ||||
|     def test(source) | ||||
|  |  | |||
|  | @ -61,12 +61,7 @@ module Ameba::Rule::Lint | |||
|         @parent.accept self | ||||
|       end | ||||
| 
 | ||||
|       def visit(node : Crystal::If) | ||||
|         @rule.check_node(@source, @parent, node.cond) | ||||
|         true | ||||
|       end | ||||
| 
 | ||||
|       def visit(node : Crystal::Unless) | ||||
|       def visit(node : Crystal::If | Crystal::Unless) | ||||
|         @rule.check_node(@source, @parent, node.cond) | ||||
|         true | ||||
|       end | ||||
|  |  | |||
|  | @ -20,10 +20,12 @@ module Ameba::Rule::Metrics | |||
| 
 | ||||
|     def test(source, node : Crystal::Def) | ||||
|       complexity = AST::CountingVisitor.new(node).count | ||||
|       return unless complexity > max_complexity | ||||
| 
 | ||||
|       return unless complexity > max_complexity && (location = node.name_location) | ||||
|       issue_for location, name_end_location(node), | ||||
|         MSG % {complexity, max_complexity} | ||||
|       return unless location = node.name_location | ||||
|       end_location = name_end_location(node) | ||||
| 
 | ||||
|       issue_for location, end_location, MSG % {complexity, max_complexity} | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  |  | |||
|  | @ -79,6 +79,7 @@ module Ameba::Rule::Style | |||
|       old = OLD % node.name | ||||
|       new = NEW % {node.name, name} | ||||
|       msg = MSG % {new, old} | ||||
| 
 | ||||
|       if end_location | ||||
|         issue_for(filter_location, end_location, msg) do |corrector| | ||||
|           corrector.replace(filter_location, end_location, new) | ||||
|  |  | |||
|  | @ -39,11 +39,12 @@ module Ameba::Rule::Style | |||
|       Tokenizer.new(source).run do |token| | ||||
|         next unless token.type.number? && decimal?(token.raw) | ||||
| 
 | ||||
|         parsed = parse_number token.raw | ||||
|         parsed = parse_number(token.raw) | ||||
| 
 | ||||
|         if allowed?(*parsed) && (expected = underscored *parsed) != token.raw | ||||
|           location = token.location | ||||
|           end_location = location.adjust(column_number: token.raw.size - 1) | ||||
| 
 | ||||
|           issue_for location, end_location, MSG % expected do |corrector| | ||||
|             corrector.replace(location, end_location, expected) | ||||
|           end | ||||
|  | @ -58,21 +59,21 @@ module Ameba::Rule::Style | |||
|     private def allowed?(_sign, value, fraction, _suffix) | ||||
|       return true if fraction && fraction.size > 3 | ||||
| 
 | ||||
|       digits = value.chars.select &.to_s.=~ /[0-9]/ | ||||
|       digits = value.chars.select(&.number?) | ||||
|       digits.size >= int_min_digits | ||||
|     end | ||||
| 
 | ||||
|     private def underscored(sign, value, fraction, suffix) | ||||
|       value = slice_digits(value.reverse) { |slice| slice }.reverse | ||||
|       fraction = "." + slice_digits(fraction) { |slice| slice } if fraction | ||||
|       value = slice_digits(value.reverse).reverse | ||||
|       fraction = "." + slice_digits(fraction) if fraction | ||||
| 
 | ||||
|       "#{sign}#{value}#{fraction}#{suffix}" | ||||
|     end | ||||
| 
 | ||||
|     private def slice_digits(value, by = 3) | ||||
|       ([] of String).tap do |slices| | ||||
|       %w[].tap do |slices| | ||||
|         value.chars.reject(&.== '_').each_slice(by) do |slice| | ||||
|           slices << (yield slice).join | ||||
|           slices << slice.join | ||||
|         end | ||||
|       end.join('_') | ||||
|     end | ||||
|  |  | |||
|  | @ -42,7 +42,7 @@ module Ameba::Rule::Style | |||
|       when Crystal::BinaryOp | ||||
|         negated_condition?(node.left) || negated_condition?(node.right) | ||||
|       when Crystal::Expressions | ||||
|         node.expressions.any? { |e| negated_condition?(e) } | ||||
|         node.expressions.any? { |exp| negated_condition?(exp) } | ||||
|       when Crystal::Not | ||||
|         true | ||||
|       else | ||||
|  |  | |||
|  | @ -66,23 +66,7 @@ module Ameba::Rule::Style | |||
|       issue_for node, MSG % {expected, name} | ||||
|     end | ||||
| 
 | ||||
|     def test(source, node : Crystal::ClassDef) | ||||
|       check_node(source, node) | ||||
|     end | ||||
| 
 | ||||
|     def test(source, node : Crystal::Alias) | ||||
|       check_node(source, node) | ||||
|     end | ||||
| 
 | ||||
|     def test(source, node : Crystal::LibDef) | ||||
|       check_node(source, node) | ||||
|     end | ||||
| 
 | ||||
|     def test(source, node : Crystal::EnumDef) | ||||
|       check_node(source, node) | ||||
|     end | ||||
| 
 | ||||
|     def test(source, node : Crystal::ModuleDef) | ||||
|     def test(source, node : Crystal::Alias | Crystal::ClassDef | Crystal::ModuleDef | Crystal::LibDef | Crystal::EnumDef) | ||||
|       check_node(source, node) | ||||
|     end | ||||
|   end | ||||
|  |  | |||
|  | @ -39,15 +39,7 @@ module Ameba::Rule::Style | |||
|       VarVisitor.new self, source | ||||
|     end | ||||
| 
 | ||||
|     def test(source, node : Crystal::Var) | ||||
|       check_node source, node | ||||
|     end | ||||
| 
 | ||||
|     def test(source, node : Crystal::InstanceVar) | ||||
|       check_node source, node | ||||
|     end | ||||
| 
 | ||||
|     def test(source, node : Crystal::ClassVar) | ||||
|     def test(source, node : Crystal::Var | Crystal::InstanceVar | Crystal::ClassVar) | ||||
|       check_node source, node | ||||
|     end | ||||
| 
 | ||||
|  |  | |||
|  | @ -33,6 +33,7 @@ module Ameba | |||
|       @code = corrected_code | ||||
|       @lines = nil | ||||
|       @ast = nil | ||||
| 
 | ||||
|       true | ||||
|     end | ||||
| 
 | ||||
|  | @ -46,7 +47,6 @@ module Ameba | |||
|     # source = Ameba::Source.new "a = 1\nb = 2", path | ||||
|     # source.lines # => ["a = 1", "b = 2"] | ||||
|     # ``` | ||||
|     # | ||||
|     getter lines : Array(String) { code.split('\n') } | ||||
| 
 | ||||
|     # Returns AST nodes constructed by `Crystal::Parser`. | ||||
|  | @ -55,7 +55,6 @@ module Ameba | |||
|     # source = Ameba::Source.new code, path | ||||
|     # source.ast | ||||
|     # ``` | ||||
|     # | ||||
|     getter ast : Crystal::ASTNode do | ||||
|       Crystal::Parser.new(code) | ||||
|         .tap(&.wants_doc = true) | ||||
|  | @ -72,9 +71,9 @@ module Ameba | |||
|       path.ends_with?("_spec.cr") | ||||
|     end | ||||
| 
 | ||||
|     # Returns `true` if *filepath* matches the source's path, `false` if it does not. | ||||
|     # Returns `true` if *filepath* matches the source's path, `false` otherwise. | ||||
|     def matches_path?(filepath) | ||||
|       path == filepath || path == File.expand_path(filepath) | ||||
|       path.in?(filepath, File.expand_path(filepath)) | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue