mirror of
				https://gitea.invidious.io/iv-org/shard-ameba.git
				synced 2024-08-15 00:53:29 +00:00 
			
		
		
		
	Merge pull request #291 from crystal-ameba/Sija/lint-not-nil-with-no-bang-rule
Add `Lint/NotNilAfterNoBang` rule
This commit is contained in:
		
						commit
						078c83c2d7
					
				
					 2 changed files with 136 additions and 0 deletions
				
			
		
							
								
								
									
										75
									
								
								spec/ameba/rule/lint/not_nil_after_no_bang_spec.cr
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										75
									
								
								spec/ameba/rule/lint/not_nil_after_no_bang_spec.cr
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,75 @@ | |||
| require "../../../spec_helper" | ||||
| 
 | ||||
| module Ameba::Rule::Lint | ||||
|   subject = NotNilAfterNoBang.new | ||||
| 
 | ||||
|   describe NotNilAfterNoBang do | ||||
|     it "passes for valid cases" do | ||||
|       expect_no_issues subject, <<-CRYSTAL | ||||
|         (1..3).index(1).not_nil!(:foo) | ||||
|         (1..3).index { |i| i > 2 }.not_nil!(:foo) | ||||
|         (1..3).find { |i| i > 2 }.not_nil!(:foo) | ||||
|         CRYSTAL | ||||
|     end | ||||
| 
 | ||||
|     it "reports if there is an `index` call followed by `not_nil!`" do | ||||
|       source = expect_issue subject, <<-CRYSTAL | ||||
|         (1..3).index(1).not_nil! | ||||
|              # ^^^^^^^^^^^^^^^^^ error: Use `index! {...}` instead of `index {...}.not_nil!` | ||||
|         CRYSTAL | ||||
| 
 | ||||
|       expect_correction source, <<-CRYSTAL | ||||
|         (1..3).index!(1) | ||||
|         CRYSTAL | ||||
|     end | ||||
| 
 | ||||
|     it "reports if there is an `index` call with block followed by `not_nil!`" do | ||||
|       source = expect_issue subject, <<-CRYSTAL | ||||
|         (1..3).index { |i| i > 2 }.not_nil! | ||||
|              # ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: Use `index! {...}` instead of `index {...}.not_nil!` | ||||
|         CRYSTAL | ||||
| 
 | ||||
|       expect_correction source, <<-CRYSTAL | ||||
|         (1..3).index! { |i| i > 2 } | ||||
|         CRYSTAL | ||||
|     end | ||||
| 
 | ||||
|     it "reports if there is a `find` call with block followed by `not_nil!`" do | ||||
|       source = expect_issue subject, <<-CRYSTAL | ||||
|         (1..3).find { |i| i > 2 }.not_nil! | ||||
|              # ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: Use `find! {...}` instead of `find {...}.not_nil!` | ||||
|         CRYSTAL | ||||
| 
 | ||||
|       expect_correction source, <<-CRYSTAL | ||||
|         (1..3).find! { |i| i > 2 } | ||||
|         CRYSTAL | ||||
|     end | ||||
| 
 | ||||
|     it "passes if there is a `find` call without block followed by `not_nil!`" do | ||||
|       expect_no_issues subject, <<-CRYSTAL | ||||
|         (1..3).find(1).not_nil! | ||||
|         CRYSTAL | ||||
|     end | ||||
| 
 | ||||
|     context "macro" do | ||||
|       it "doesn't report in macro scope" do | ||||
|         expect_no_issues subject, <<-CRYSTAL | ||||
|           {{ [1, 2, 3].index(1).not_nil! }} | ||||
|           CRYSTAL | ||||
|       end | ||||
|     end | ||||
| 
 | ||||
|     it "reports rule, pos and message" do | ||||
|       s = Source.new %( | ||||
|         (1..3).index(1).not_nil! | ||||
|       ), "source.cr" | ||||
|       subject.catch(s).should_not be_valid | ||||
|       issue = s.issues.first | ||||
| 
 | ||||
|       issue.rule.should_not be_nil | ||||
|       issue.location.to_s.should eq "source.cr:1:8" | ||||
|       issue.end_location.to_s.should eq "source.cr:1:24" | ||||
|       issue.message.should eq "Use `index! {...}` instead of `index {...}.not_nil!`" | ||||
|     end | ||||
|   end | ||||
| end | ||||
							
								
								
									
										61
									
								
								src/ameba/rule/lint/not_nil_after_no_bang.cr
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								src/ameba/rule/lint/not_nil_after_no_bang.cr
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,61 @@ | |||
| module Ameba::Rule::Lint | ||||
|   # This rule is used to identify usage of `index/find` calls followed by `not_nil!`. | ||||
|   # | ||||
|   # For example, this is considered a code smell: | ||||
|   # | ||||
|   # ``` | ||||
|   # %w[Alice Bob].find(&.match(/^A./)).not_nil! | ||||
|   # ``` | ||||
|   # | ||||
|   # And can be written as this: | ||||
|   # | ||||
|   # ``` | ||||
|   # %w[Alice Bob].find!(&.match(/^A./)) | ||||
|   # ``` | ||||
|   # | ||||
|   # YAML configuration example: | ||||
|   # | ||||
|   # ``` | ||||
|   # Lint/NotNilAfterNoBang: | ||||
|   #   Enabled: true | ||||
|   # ``` | ||||
|   class NotNilAfterNoBang < Base | ||||
|     include AST::Util | ||||
| 
 | ||||
|     properties do | ||||
|       description "Identifies usage of `index/find` calls followed by `not_nil!`" | ||||
|     end | ||||
| 
 | ||||
|     BLOCK_CALL_NAMES = %w(index find) | ||||
|     CALL_NAMES       = %w(index) | ||||
| 
 | ||||
|     NOT_NIL_NAME = "not_nil!" | ||||
|     MSG          = "Use `%s! {...}` instead of `%s {...}.not_nil!`" | ||||
| 
 | ||||
|     def test(source) | ||||
|       AST::NodeVisitor.new self, source, skip: [ | ||||
|         Crystal::Macro, | ||||
|         Crystal::MacroExpression, | ||||
|         Crystal::MacroIf, | ||||
|         Crystal::MacroFor, | ||||
|       ] | ||||
|     end | ||||
| 
 | ||||
|     def test(source, node : Crystal::Call) | ||||
|       return unless node.name == NOT_NIL_NAME && node.args.empty? | ||||
|       return unless (obj = node.obj).is_a?(Crystal::Call) | ||||
|       return unless obj.name.in?(obj.block ? BLOCK_CALL_NAMES : CALL_NAMES) | ||||
| 
 | ||||
|       return unless name_location = obj.name_location | ||||
|       return unless name_location_end = name_end_location(obj) | ||||
|       return unless end_location = name_end_location(node) | ||||
| 
 | ||||
|       msg = MSG % {obj.name, obj.name} | ||||
| 
 | ||||
|       issue_for name_location, end_location, msg do |corrector| | ||||
|         corrector.insert_after(name_location_end, '!') | ||||
|         corrector.remove_trailing(node, ".not_nil!".size) | ||||
|       end | ||||
|     end | ||||
|   end | ||||
| end | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue