mirror of
				https://gitea.invidious.io/iv-org/shard-ameba.git
				synced 2024-08-15 00:53:29 +00:00 
			
		
		
		
	Merge pull request #301 from crystal-ameba/Sija/style-redundant-parentheses-rule
Add `Style/RedundantParentheses` rule
This commit is contained in:
		
						commit
						28e2871165
					
				
					 3 changed files with 172 additions and 1 deletions
				
			
		
							
								
								
									
										101
									
								
								spec/ameba/rule/style/redundant_parentheses_spec.cr
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										101
									
								
								spec/ameba/rule/style/redundant_parentheses_spec.cr
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,101 @@ | ||||||
|  | require "../../../spec_helper" | ||||||
|  | 
 | ||||||
|  | module Ameba::Rule::Style | ||||||
|  |   subject = RedundantParentheses.new | ||||||
|  | 
 | ||||||
|  |   describe RedundantParentheses do | ||||||
|  |     {% for keyword in %w(if unless while until) %} | ||||||
|  |       context "{{ keyword.id }}" do | ||||||
|  |         it "reports if redundant parentheses are found" do | ||||||
|  |           source = expect_issue subject, <<-CRYSTAL, keyword: {{ keyword }} | ||||||
|  |             %{keyword}   (foo > 10) | ||||||
|  |             _{keyword} # ^^^^^^^^^^ error: Redundant parentheses | ||||||
|  |               foo | ||||||
|  |             end | ||||||
|  |             CRYSTAL | ||||||
|  | 
 | ||||||
|  |           expect_correction source, <<-CRYSTAL | ||||||
|  |             {{ keyword.id }}   foo > 10 | ||||||
|  |               foo | ||||||
|  |             end | ||||||
|  |             CRYSTAL | ||||||
|  |         end | ||||||
|  |       end | ||||||
|  |     {% end %} | ||||||
|  | 
 | ||||||
|  |     context "case" do | ||||||
|  |       it "reports if redundant parentheses are found" do | ||||||
|  |         source = expect_issue subject, <<-CRYSTAL | ||||||
|  |           case (foo = @foo) | ||||||
|  |              # ^^^^^^^^^^^^ error: Redundant parentheses | ||||||
|  |           when String then "string" | ||||||
|  |           when Symbol then "symbol" | ||||||
|  |           end | ||||||
|  |           CRYSTAL | ||||||
|  | 
 | ||||||
|  |         expect_correction source, <<-CRYSTAL | ||||||
|  |           case foo = @foo | ||||||
|  |           when String then "string" | ||||||
|  |           when Symbol then "symbol" | ||||||
|  |           end | ||||||
|  |           CRYSTAL | ||||||
|  |       end | ||||||
|  |     end | ||||||
|  | 
 | ||||||
|  |     context "properties" do | ||||||
|  |       context "#exclude_ternary=" do | ||||||
|  |         it "skips ternary control expressions by default" do | ||||||
|  |           expect_no_issues subject, <<-CRYSTAL | ||||||
|  |             (foo > bar) ? true : false | ||||||
|  |             CRYSTAL | ||||||
|  |         end | ||||||
|  | 
 | ||||||
|  |         it "allows to configure assignments" do | ||||||
|  |           rule = Rule::Style::RedundantParentheses.new | ||||||
|  |           rule.exclude_ternary = false | ||||||
|  | 
 | ||||||
|  |           expect_issue rule, <<-CRYSTAL | ||||||
|  |             (foo.empty?) ? true : false | ||||||
|  |             # ^^^^^^^^^^ error: Redundant parentheses | ||||||
|  |             CRYSTAL | ||||||
|  | 
 | ||||||
|  |           expect_no_issues subject, <<-CRYSTAL | ||||||
|  |             (foo && bar) ? true : false | ||||||
|  |             (foo || bar) ? true : false | ||||||
|  |             (foo = @foo) ? true : false | ||||||
|  |             foo == 42 ? true : false | ||||||
|  |             (foo = 42) ? true : false | ||||||
|  |             (foo > 42) ? true : false | ||||||
|  |             (foo >= 42) ? true : false | ||||||
|  |             (3 >= foo >= 42) ? true : false | ||||||
|  |             (3.in? 0..42) ? true : false | ||||||
|  |             (yield 42) ? true : false | ||||||
|  |             (foo rescue 42) ? true : false | ||||||
|  |             CRYSTAL | ||||||
|  |         end | ||||||
|  |       end | ||||||
|  | 
 | ||||||
|  |       context "#exclude_assignments=" do | ||||||
|  |         it "reports assignments by default" do | ||||||
|  |           expect_issue subject, <<-CRYSTAL | ||||||
|  |             if (foo = @foo) | ||||||
|  |              # ^^^^^^^^^^^^ error: Redundant parentheses | ||||||
|  |               foo | ||||||
|  |             end | ||||||
|  |             CRYSTAL | ||||||
|  |         end | ||||||
|  | 
 | ||||||
|  |         it "allows to configure assignments" do | ||||||
|  |           rule = Rule::Style::RedundantParentheses.new | ||||||
|  |           rule.exclude_assignments = true | ||||||
|  | 
 | ||||||
|  |           expect_no_issues rule, <<-CRYSTAL | ||||||
|  |             if (foo = @foo) | ||||||
|  |               foo | ||||||
|  |             end | ||||||
|  |             CRYSTAL | ||||||
|  |         end | ||||||
|  |       end | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  | end | ||||||
							
								
								
									
										70
									
								
								src/ameba/rule/style/redundant_parentheses.cr
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								src/ameba/rule/style/redundant_parentheses.cr
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,70 @@ | ||||||
|  | module Ameba::Rule::Style | ||||||
|  |   # A rule that disallows redundant parentheses around control expressions. | ||||||
|  |   # | ||||||
|  |   # For example, this is considered invalid: | ||||||
|  |   # | ||||||
|  |   # ``` | ||||||
|  |   # if (foo == 42) | ||||||
|  |   #   do_something | ||||||
|  |   # end | ||||||
|  |   # ``` | ||||||
|  |   # | ||||||
|  |   # And should be replaced by the following: | ||||||
|  |   # | ||||||
|  |   # ``` | ||||||
|  |   # if foo == 42 | ||||||
|  |   #   do_something | ||||||
|  |   # end | ||||||
|  |   # ``` | ||||||
|  |   # | ||||||
|  |   # YAML configuration example: | ||||||
|  |   # | ||||||
|  |   # ``` | ||||||
|  |   # Style/RedundantParentheses: | ||||||
|  |   #   Enabled: true | ||||||
|  |   #   ExcludeTernary: false | ||||||
|  |   #   ExcludeAssignments: false | ||||||
|  |   # ``` | ||||||
|  |   class RedundantParentheses < Base | ||||||
|  |     properties do | ||||||
|  |       description "Disallows redundant parentheses around control expressions" | ||||||
|  | 
 | ||||||
|  |       exclude_ternary false | ||||||
|  |       exclude_assignments false | ||||||
|  |     end | ||||||
|  | 
 | ||||||
|  |     MSG = "Redundant parentheses" | ||||||
|  | 
 | ||||||
|  |     protected def strip_parentheses?(node, in_ternary) : Bool | ||||||
|  |       case node | ||||||
|  |       when Crystal::BinaryOp, Crystal::ExceptionHandler | ||||||
|  |         !in_ternary | ||||||
|  |       when Crystal::Call | ||||||
|  |         !in_ternary || node.has_parentheses? || node.args.empty? | ||||||
|  |       when Crystal::Yield | ||||||
|  |         !in_ternary || node.has_parentheses? || node.exps.empty? | ||||||
|  |       when Crystal::Assign, Crystal::OpAssign, Crystal::MultiAssign | ||||||
|  |         !in_ternary && !exclude_assignments | ||||||
|  |       else | ||||||
|  |         true | ||||||
|  |       end | ||||||
|  |     end | ||||||
|  | 
 | ||||||
|  |     def test(source, node : Crystal::If | Crystal::Unless | Crystal::Case | Crystal::While | Crystal::Until) | ||||||
|  |       is_ternary = node.is_a?(Crystal::If) && node.ternary? | ||||||
|  | 
 | ||||||
|  |       return if is_ternary && exclude_ternary | ||||||
|  | 
 | ||||||
|  |       return unless (cond = node.cond).is_a?(Crystal::Expressions) | ||||||
|  |       return unless cond.keyword.paren? | ||||||
|  | 
 | ||||||
|  |       return unless exp = cond.single_expression? | ||||||
|  |       return unless strip_parentheses?(exp, is_ternary) | ||||||
|  | 
 | ||||||
|  |       issue_for cond, MSG do |corrector| | ||||||
|  |         corrector.remove_trailing(cond, 1) | ||||||
|  |         corrector.remove_leading(cond, 1) | ||||||
|  |       end | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  | end | ||||||
|  | @ -73,7 +73,7 @@ class Ameba::Spec::AnnotatedSource | ||||||
|   private def message_to_regex(expected_annotation) |   private def message_to_regex(expected_annotation) | ||||||
|     String.build do |io| |     String.build do |io| | ||||||
|       offset = 0 |       offset = 0 | ||||||
|       while (index = expected_annotation.index(ABBREV, offset)) |       while index = expected_annotation.index(ABBREV, offset) | ||||||
|         io << Regex.escape(expected_annotation[offset...index]) |         io << Regex.escape(expected_annotation[offset...index]) | ||||||
|         io << ".*?" |         io << ".*?" | ||||||
|         offset = index + ABBREV.size |         offset = index + ABBREV.size | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue