kimera 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.kimera.yml.example +59 -0
- data/CHANGELOG.md +36 -0
- data/LICENSE.txt +21 -0
- data/README.md +528 -0
- data/exe/kimera +22 -0
- data/lib/kimera/audit/operator_audit.rb +71 -0
- data/lib/kimera/cli/argv.rb +15 -0
- data/lib/kimera/cli/baseline.rb +87 -0
- data/lib/kimera/cli/changed.rb +27 -0
- data/lib/kimera/cli/ci.rb +27 -0
- data/lib/kimera/cli/completion.rb +37 -0
- data/lib/kimera/cli/doctor.rb +115 -0
- data/lib/kimera/cli/flag.rb +55 -0
- data/lib/kimera/cli/help_text.rb +45 -0
- data/lib/kimera/cli/init.rb +105 -0
- data/lib/kimera/cli/mutant.rb +82 -0
- data/lib/kimera/cli/report_file.rb +20 -0
- data/lib/kimera/cli/run/arguments.rb +58 -0
- data/lib/kimera/cli/run/cycle.rb +78 -0
- data/lib/kimera/cli/run/digest.rb +49 -0
- data/lib/kimera/cli/run/emission.rb +45 -0
- data/lib/kimera/cli/run/gate.rb +43 -0
- data/lib/kimera/cli/run/options.rb +85 -0
- data/lib/kimera/cli/run/pass.rb +100 -0
- data/lib/kimera/cli/run/sources.rb +56 -0
- data/lib/kimera/cli/run.rb +52 -0
- data/lib/kimera/cli/skill.rb +23 -0
- data/lib/kimera/cli/suggestion.rb +36 -0
- data/lib/kimera/cli/survivors/panel.rb +81 -0
- data/lib/kimera/cli/survivors.rb +89 -0
- data/lib/kimera/cli/synthesize.rb +66 -0
- data/lib/kimera/cli/workflows.rb +8 -0
- data/lib/kimera/cli.rb +132 -0
- data/lib/kimera/error.rb +3 -0
- data/lib/kimera/execution/baseline_failure.rb +47 -0
- data/lib/kimera/execution/baseline_pass.rb +123 -0
- data/lib/kimera/execution/boot.rb +50 -0
- data/lib/kimera/execution/callback.rb +33 -0
- data/lib/kimera/execution/child_process.rb +48 -0
- data/lib/kimera/execution/composite_isolation.rb +24 -0
- data/lib/kimera/execution/harness.rb +150 -0
- data/lib/kimera/execution/isolated.rb +64 -0
- data/lib/kimera/execution/isolated_child.rb +15 -0
- data/lib/kimera/execution/isolated_child_command.rb +44 -0
- data/lib/kimera/execution/isolated_child_minitest.rb +34 -0
- data/lib/kimera/execution/isolated_plan.rb +54 -0
- data/lib/kimera/execution/isolated_scheduling.rb +54 -0
- data/lib/kimera/execution/isolated_test_selection.rb +22 -0
- data/lib/kimera/execution/isolated_verdict.rb +72 -0
- data/lib/kimera/execution/isolated_watchdog.rb +59 -0
- data/lib/kimera/execution/isolation.rb +17 -0
- data/lib/kimera/execution/null_progress.rb +17 -0
- data/lib/kimera/execution/overlay_guard_modules.rb +95 -0
- data/lib/kimera/execution/overlay_guards.rb +56 -0
- data/lib/kimera/execution/parallel_test_databases.rb +64 -0
- data/lib/kimera/execution/pool_driver.rb +101 -0
- data/lib/kimera/execution/priority.rb +23 -0
- data/lib/kimera/execution/reload.rb +104 -0
- data/lib/kimera/execution/rig.rb +41 -0
- data/lib/kimera/execution/schedule.rb +105 -0
- data/lib/kimera/execution/schemata.rb +88 -0
- data/lib/kimera/execution/shift/attempt.rb +34 -0
- data/lib/kimera/execution/shift/coverage_channel.rb +43 -0
- data/lib/kimera/execution/shift/killer_memory.rb +20 -0
- data/lib/kimera/execution/shift/leak_guard.rb +34 -0
- data/lib/kimera/execution/shift.rb +137 -0
- data/lib/kimera/execution/sweep.rb +30 -0
- data/lib/kimera/execution/transaction_isolation.rb +32 -0
- data/lib/kimera/execution/trial.rb +16 -0
- data/lib/kimera/execution/verdicts.rb +78 -0
- data/lib/kimera/execution/worker_pool/fleet.rb +104 -0
- data/lib/kimera/execution/worker_pool/stillborn_guard.rb +40 -0
- data/lib/kimera/execution/worker_pool/worker.rb +32 -0
- data/lib/kimera/execution/worker_pool.rb +101 -0
- data/lib/kimera/frameworks/adapter.rb +82 -0
- data/lib/kimera/frameworks/minitest_adapter.rb +102 -0
- data/lib/kimera/frameworks/rspec_adapter.rb +123 -0
- data/lib/kimera/frameworks/rspec_group_index.rb +45 -0
- data/lib/kimera/incremental/git_diff.rb +98 -0
- data/lib/kimera/incremental/selection.rb +27 -0
- data/lib/kimera/incremental/session.rb +109 -0
- data/lib/kimera/memoization.rb +104 -0
- data/lib/kimera/operators/argument_drop.rb +19 -0
- data/lib/kimera/operators/arithmetic.rb +12 -0
- data/lib/kimera/operators/audit.rb +3 -0
- data/lib/kimera/operators/base.rb +40 -0
- data/lib/kimera/operators/binary_swap.rb +23 -0
- data/lib/kimera/operators/boolean.rb +26 -0
- data/lib/kimera/operators/chain_link_deletion.rb +25 -0
- data/lib/kimera/operators/collection_literal.rb +18 -0
- data/lib/kimera/operators/comparison.rb +13 -0
- data/lib/kimera/operators/conditional.rb +36 -0
- data/lib/kimera/operators/default_argument.rb +17 -0
- data/lib/kimera/operators/element_drop.rb +31 -0
- data/lib/kimera/operators/index_fetch.rb +15 -0
- data/lib/kimera/operators/kernel_coercion.rb +17 -0
- data/lib/kimera/operators/method_unwrap.rb +20 -0
- data/lib/kimera/operators/negation.rb +15 -0
- data/lib/kimera/operators/node_swap.rb +20 -0
- data/lib/kimera/operators/numeric_literal.rb +39 -0
- data/lib/kimera/operators/op_assign.rb +18 -0
- data/lib/kimera/operators/plugins.rb +27 -0
- data/lib/kimera/operators/rails_association.rb +24 -0
- data/lib/kimera/operators/rails_callback.rb +49 -0
- data/lib/kimera/operators/rails_declaration.rb +16 -0
- data/lib/kimera/operators/rails_permit.rb +34 -0
- data/lib/kimera/operators/rails_validation.rb +17 -0
- data/lib/kimera/operators/range.rb +15 -0
- data/lib/kimera/operators/regexp_literal.rb +16 -0
- data/lib/kimera/operators/respond_to_guard.rb +24 -0
- data/lib/kimera/operators/return_value.rb +36 -0
- data/lib/kimera/operators/safe_navigation.rb +15 -0
- data/lib/kimera/operators/selector_swap.rb +24 -0
- data/lib/kimera/operators/statement_deletion.rb +26 -0
- data/lib/kimera/operators/string_literal.rb +22 -0
- data/lib/kimera/operators/symbol_literal.rb +26 -0
- data/lib/kimera/operators/vocabulary.rb +82 -0
- data/lib/kimera/operators.rb +141 -0
- data/lib/kimera/plugins.rb +3 -0
- data/lib/kimera/registry/builder.rb +59 -0
- data/lib/kimera/registry/mutation_point.rb +146 -0
- data/lib/kimera/registry/numbering.rb +15 -0
- data/lib/kimera/registry/registry.rb +120 -0
- data/lib/kimera/registry/source_file.rb +71 -0
- data/lib/kimera/registry/tally.rb +11 -0
- data/lib/kimera/registry/walking.rb +55 -0
- data/lib/kimera/report/actions.rb +38 -0
- data/lib/kimera/report/coloring.rb +25 -0
- data/lib/kimera/report/formats.rb +78 -0
- data/lib/kimera/report/live.rb +56 -0
- data/lib/kimera/report/log.rb +46 -0
- data/lib/kimera/report/progress.rb +55 -0
- data/lib/kimera/report/screen.rb +44 -0
- data/lib/kimera/report/sections.rb +50 -0
- data/lib/kimera/report/tally.rb +84 -0
- data/lib/kimera/report/text.rb +104 -0
- data/lib/kimera/results/result.rb +72 -0
- data/lib/kimera/results/run_report.rb +114 -0
- data/lib/kimera/rewrite/ast_walk.rb +20 -0
- data/lib/kimera/rewrite/directive.rb +61 -0
- data/lib/kimera/rewrite/directive_extra_handlers.rb +52 -0
- data/lib/kimera/rewrite/directive_handlers.rb +138 -0
- data/lib/kimera/rewrite/directive_kwarg_handlers.rb +44 -0
- data/lib/kimera/rewrite/numbered_params.rb +50 -0
- data/lib/kimera/runtime.rb +51 -0
- data/lib/kimera/scope/config.rb +90 -0
- data/lib/kimera/scope/file_set.rb +51 -0
- data/lib/kimera/scope/ignore_list.rb +55 -0
- data/lib/kimera/self_protection.rb +56 -0
- data/lib/kimera/support/duration.rb +13 -0
- data/lib/kimera/support/errors.rb +8 -0
- data/lib/kimera/support/operator_protocol.rb +10 -0
- data/lib/kimera/support/syntax.rb +68 -0
- data/lib/kimera/support/syntax_types.rb +20 -0
- data/lib/kimera/support/unparse.rb +15 -0
- data/lib/kimera/support/version.rb +5 -0
- data/lib/kimera/support/warnings.rb +15 -0
- data/lib/kimera/synthesis/file_weave.rb +78 -0
- data/lib/kimera/synthesis/guard_weaver.rb +77 -0
- data/lib/kimera/synthesis/guardrail_dispatch.rb +68 -0
- data/lib/kimera/synthesis/guardrail_interpolation.rb +59 -0
- data/lib/kimera/synthesis/guardrail_value_objects.rb +46 -0
- data/lib/kimera/synthesis/overlay.rb +52 -0
- data/lib/kimera/synthesis/overlay_splice.rb +101 -0
- data/lib/kimera/synthesis/project.rb +58 -0
- data/lib/kimera/synthesis/source_map.rb +42 -0
- data/lib/kimera/version.rb +3 -0
- data/lib/kimera.rb +13 -0
- data/skills/kimera/SKILL.md +118 -0
- metadata +259 -0
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "binary_swap"
|
|
4
|
+
|
|
5
|
+
class Kimera::Operators::Arithmetic < Kimera::Operators::BinarySwap
|
|
6
|
+
MUTATIONS = { :+ => [:-], :- => [:+], :* => [:/] }.merge({ :/ => [:*], :% => [:*], :** => [:*] }).freeze
|
|
7
|
+
DIRECTIVE = "op_swap"
|
|
8
|
+
|
|
9
|
+
class << self
|
|
10
|
+
def key = "arithmetic"
|
|
11
|
+
end
|
|
12
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../support/syntax_types"
|
|
4
|
+
require_relative "vocabulary"
|
|
5
|
+
|
|
6
|
+
class Kimera::Operators::Base
|
|
7
|
+
SYNTAX = ->(node) { Kimera::Syntax.view(node) }
|
|
8
|
+
include Kimera::SyntaxTypes
|
|
9
|
+
include Kimera::Operators::Vocabulary
|
|
10
|
+
|
|
11
|
+
class << self
|
|
12
|
+
def key = raise(NotImplementedError, "#{self} must define .key")
|
|
13
|
+
|
|
14
|
+
def statement? = false
|
|
15
|
+
|
|
16
|
+
def body? = false
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def key
|
|
20
|
+
self.class.key
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def statement?
|
|
24
|
+
self.class.statement?
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def body?
|
|
28
|
+
self.class.body?
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def variants(node, position:)
|
|
32
|
+
raise(NotImplementedError, "#{self} must define #variants")
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def pairs(node, position:)
|
|
36
|
+
Array(variants(node, position: position)).map { |variant| [key, variant] }
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def syntax(node) = self.class::SYNTAX.call(node)
|
|
40
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "base"
|
|
4
|
+
|
|
5
|
+
class Kimera::Operators::BinarySwap < Kimera::Operators::Base
|
|
6
|
+
def variants(node, **)
|
|
7
|
+
return unless swappable?(node)
|
|
8
|
+
name = node.name
|
|
9
|
+
self.class::MUTATIONS[name].map { |to| swap(name, to) }
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
private
|
|
13
|
+
|
|
14
|
+
def swappable?(node)
|
|
15
|
+
return false unless node.is_a?(CallNode)
|
|
16
|
+
return false unless arguments(node).length == 1
|
|
17
|
+
self.class::MUTATIONS.key?(node.name)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def swap(name, to)
|
|
21
|
+
Kimera::Operators::Variant.new(label: "#{name} => #{to}", directive: directive(self.class::DIRECTIVE, to: to.to_s))
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "node_swap"
|
|
4
|
+
|
|
5
|
+
module Kimera
|
|
6
|
+
module Operators
|
|
7
|
+
BOOLEAN_OPERATORS = {
|
|
8
|
+
BooleanConnective: [
|
|
9
|
+
"boolean_connective",
|
|
10
|
+
{
|
|
11
|
+
AndNode => { label: "&& => ||", to: "or" },
|
|
12
|
+
OrNode => { label: "|| => &&", to: "and" }
|
|
13
|
+
}
|
|
14
|
+
],
|
|
15
|
+
BooleanLiteral: [
|
|
16
|
+
"boolean_literal",
|
|
17
|
+
{
|
|
18
|
+
TrueNode => { label: "true => false", to: "false" },
|
|
19
|
+
FalseNode => { label: "false => true", to: "true" }
|
|
20
|
+
}
|
|
21
|
+
]
|
|
22
|
+
}.freeze
|
|
23
|
+
|
|
24
|
+
BOOLEAN_OPERATORS.each { |name, (key, swaps)| const_set(name, NodeSwap.define(key, swaps)) }
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "prism"
|
|
4
|
+
require_relative "base"
|
|
5
|
+
|
|
6
|
+
class Kimera::Operators::ChainLinkDeletion < Kimera::Operators::Base
|
|
7
|
+
class << self
|
|
8
|
+
def key = "chain_link_deletion"
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def variants(node, **)
|
|
12
|
+
return unless deletable?(node)
|
|
13
|
+
solo("drop chain link `.#{node.receiver.name}`", "drop_receiver_link")
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
private
|
|
17
|
+
|
|
18
|
+
def deletable?(node)
|
|
19
|
+
return false unless node.is_a?(CallNode)
|
|
20
|
+
link = node.receiver
|
|
21
|
+
return false unless link.is_a?(CallNode) && link.receiver
|
|
22
|
+
return false if link.block.is_a?(BlockNode)
|
|
23
|
+
identifier?(link)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "base"
|
|
4
|
+
|
|
5
|
+
class Kimera::Operators::CollectionLiteral < Kimera::Operators::Base
|
|
6
|
+
EMPTIED = { ArrayNode => "array => []", HashNode => "hash => {}" }.freeze
|
|
7
|
+
|
|
8
|
+
class << self
|
|
9
|
+
def key = "collection_literal"
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def variants(node, **)
|
|
13
|
+
case node
|
|
14
|
+
when ArrayNode, HashNode
|
|
15
|
+
solo(EMPTIED[node.class], "empty_collection") unless node.elements.empty?
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "binary_swap"
|
|
4
|
+
|
|
5
|
+
class Kimera::Operators::Comparison < Kimera::Operators::BinarySwap
|
|
6
|
+
MUTATIONS = { :> => %i[>= <], :>= => %i[> <=], :< => %i[<= >] }
|
|
7
|
+
.merge({ :<= => %i[< >=], :== => [:!=], :!= => [:==] }).freeze
|
|
8
|
+
DIRECTIVE = "comparison"
|
|
9
|
+
|
|
10
|
+
class << self
|
|
11
|
+
def key = "comparison"
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "base"
|
|
4
|
+
|
|
5
|
+
class Kimera::Operators::Conditional < Kimera::Operators::Base
|
|
6
|
+
class << self
|
|
7
|
+
def key = "conditional"
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def variants(node, **)
|
|
11
|
+
return unless conditional?(node)
|
|
12
|
+
return if elsif?(node)
|
|
13
|
+
return if memoized?(node)
|
|
14
|
+
%w[true false].map { |to| flip(to) }
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
private
|
|
18
|
+
|
|
19
|
+
def flip(to)
|
|
20
|
+
Kimera::Operators::Variant.new(label: "condition => #{to}", directive: directive("condition", to: to))
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def conditional?(node)
|
|
24
|
+
node.is_a?(IfNode) || node.is_a?(UnlessNode)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def elsif?(node)
|
|
28
|
+
node.is_a?(IfNode) && node.if_keyword_loc&.slice == "elsif"
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def memoized?(node)
|
|
32
|
+
predicate = node.predicate
|
|
33
|
+
predicate.is_a?(DefinedNode) &&
|
|
34
|
+
predicate.value.is_a?(InstanceVariableReadNode)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "base"
|
|
4
|
+
|
|
5
|
+
class Kimera::Operators::DefaultArgument < Kimera::Operators::Base
|
|
6
|
+
class << self
|
|
7
|
+
def key = "default_argument"
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def variants(node, **)
|
|
11
|
+
case node
|
|
12
|
+
when OptionalParameterNode, OptionalKeywordParameterNode
|
|
13
|
+
name = node.name
|
|
14
|
+
solo("default => required (#{name})", "required_argument", name: name.to_s)
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "base"
|
|
4
|
+
|
|
5
|
+
class Kimera::Operators::ElementDrop < Kimera::Operators::Base
|
|
6
|
+
SPLATS = { ArrayNode => SplatNode, HashNode => AssocSplatNode }.freeze
|
|
7
|
+
|
|
8
|
+
class << self
|
|
9
|
+
def key = "element_drop"
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def variants(node, **)
|
|
13
|
+
parts = collection(node)
|
|
14
|
+
drop(*parts) if parts
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
private
|
|
18
|
+
|
|
19
|
+
def collection(node)
|
|
20
|
+
syntax(node).collection(SPLATS)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def drop(elements, splat)
|
|
24
|
+
return unless droppable?(elements, splat)
|
|
25
|
+
drops(elements, "drop_element", label: "drop")
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def droppable?(elements, splat)
|
|
29
|
+
elements.length > 1 && elements.none?(splat)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "base"
|
|
4
|
+
|
|
5
|
+
class Kimera::Operators::IndexFetch < Kimera::Operators::Base
|
|
6
|
+
class << self
|
|
7
|
+
def key = "index_fetch"
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def variants(node, **)
|
|
11
|
+
return unless call(node).chained?(:[])
|
|
12
|
+
return unless unary?(node)
|
|
13
|
+
solo("[] => fetch", "index_to_fetch")
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "base"
|
|
4
|
+
|
|
5
|
+
class Kimera::Operators::KernelCoercion < Kimera::Operators::Base
|
|
6
|
+
COERCIONS = %i[Array String Integer Float].freeze
|
|
7
|
+
|
|
8
|
+
class << self
|
|
9
|
+
def key = "kernel_coercion"
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def variants(node, **)
|
|
13
|
+
return unless call(node).bare?(COERCIONS)
|
|
14
|
+
return unless unary?(node)
|
|
15
|
+
solo("delete #{node.name}()", "unwrap_argument")
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "base"
|
|
4
|
+
|
|
5
|
+
class Kimera::Operators::MethodUnwrap < Kimera::Operators::Base
|
|
6
|
+
UNWRAPPABLE = %i[
|
|
7
|
+
dup clone freeze to_s to_sym to_i to_f to_a to_h
|
|
8
|
+
strip chomp lstrip rstrip downcase upcase capitalize
|
|
9
|
+
uniq compact flatten sort reverse presence
|
|
10
|
+
].freeze
|
|
11
|
+
|
|
12
|
+
class << self
|
|
13
|
+
def key = "method_unwrap"
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def variants(node, **)
|
|
17
|
+
name = syntax(node).unwrap(UNWRAPPABLE)
|
|
18
|
+
solo("delete .#{name}", "unwrap_receiver") if name
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "base"
|
|
4
|
+
|
|
5
|
+
class Kimera::Operators::Negation < Kimera::Operators::Base
|
|
6
|
+
class << self
|
|
7
|
+
def key = "negation"
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def variants(node, **)
|
|
11
|
+
return unless call(node).chained?(:!)
|
|
12
|
+
return if node.arguments
|
|
13
|
+
solo("delete !", "unwrap_receiver")
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "base"
|
|
4
|
+
|
|
5
|
+
class Kimera::Operators::NodeSwap < Kimera::Operators::Base
|
|
6
|
+
class << self
|
|
7
|
+
def define(key, mutations)
|
|
8
|
+
Class.new(self) do
|
|
9
|
+
const_set(:MUTATIONS, mutations.freeze)
|
|
10
|
+
define_singleton_method(:key) { key }
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def variants(node, **)
|
|
16
|
+
swap = self.class::MUTATIONS[node.class]
|
|
17
|
+
return unless swap
|
|
18
|
+
solo(swap[:label], key, to: swap[:to])
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "base"
|
|
4
|
+
|
|
5
|
+
class Kimera::Operators::NumericLiteral < Kimera::Operators::Base
|
|
6
|
+
IntegerTargets =
|
|
7
|
+
Data.define(:value) do
|
|
8
|
+
include Enumerable
|
|
9
|
+
def each(&) = targets.uniq.each(&)
|
|
10
|
+
private
|
|
11
|
+
def targets
|
|
12
|
+
[value - 1, value + 1].tap { |all| all << 0 unless value.zero? }
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
class << self
|
|
16
|
+
def key = "numeric_literal"
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def variants(node, **)
|
|
20
|
+
case node
|
|
21
|
+
in IntegerNode(value:) then integers(value)
|
|
22
|
+
in FloatNode(value:) then floats(value)
|
|
23
|
+
else nil
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
private
|
|
28
|
+
|
|
29
|
+
def integers(value)
|
|
30
|
+
IntegerTargets.new(value).map do |to|
|
|
31
|
+
Kimera::Operators::Variant.new(label: "#{value} => #{to}", directive: directive("integer_literal", value: to))
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def floats(value)
|
|
36
|
+
shifted = value + 1.0
|
|
37
|
+
solo("#{value} => #{shifted}", "float_literal", value: shifted)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "base"
|
|
4
|
+
|
|
5
|
+
class Kimera::Operators::OpAssign < Kimera::Operators::Base
|
|
6
|
+
MUTATIONS = { :+ => :-, :- => :+, :* => :/ }.merge({ :/ => :*, :% => :* }).freeze
|
|
7
|
+
|
|
8
|
+
class << self
|
|
9
|
+
def key = "op_assign"
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def variants(node, **)
|
|
13
|
+
operator = syntax(node).operator
|
|
14
|
+
to = MUTATIONS[operator]
|
|
15
|
+
return unless to
|
|
16
|
+
solo("#{operator}= => #{to}=", "op_asgn_swap", to: to.to_s)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../error"
|
|
4
|
+
require_relative "../operators"
|
|
5
|
+
|
|
6
|
+
module Kimera
|
|
7
|
+
module Plugins
|
|
8
|
+
module_function
|
|
9
|
+
|
|
10
|
+
def load!(paths, root: ".")
|
|
11
|
+
Array(paths).each { |path| pull(path, root) }
|
|
12
|
+
Kimera::Operators.custom
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def pull(path, root)
|
|
16
|
+
require(resolve(path, root))
|
|
17
|
+
rescue Kimera::Error
|
|
18
|
+
raise
|
|
19
|
+
rescue ScriptError, StandardError => error
|
|
20
|
+
raise(UsageError, "cannot load #{path}: #{error.class}: #{error.message}")
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def resolve(path, root)
|
|
24
|
+
File.expand_path(path, File.expand_path(root))
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "rails_declaration"
|
|
4
|
+
|
|
5
|
+
class Kimera::Operators::RailsAssociation < Kimera::Operators::RailsDeclaration
|
|
6
|
+
NAMES = %i[has_many has_one belongs_to has_and_belongs_to_many].freeze
|
|
7
|
+
|
|
8
|
+
class << self
|
|
9
|
+
def key = "rails_association"
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
private
|
|
13
|
+
|
|
14
|
+
def declaration(node)
|
|
15
|
+
return unless dependent?(node)
|
|
16
|
+
solo("#{first(node)}: drop dependent:", "kwarg_pair_drop", key: "dependent")
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def dependent?(node)
|
|
20
|
+
keywords(node)&.elements.to_a.any? do |assoc|
|
|
21
|
+
assoc.is_a?(AssocNode) && unescaped(assoc.key) == "dependent"
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "rails_declaration"
|
|
4
|
+
|
|
5
|
+
class Kimera::Operators::RailsCallback < Kimera::Operators::RailsDeclaration
|
|
6
|
+
NAMES = %i[
|
|
7
|
+
before_action after_action around_action
|
|
8
|
+
before_save after_save before_create after_create
|
|
9
|
+
before_update after_update before_destroy after_destroy
|
|
10
|
+
before_validation after_validation after_commit after_rollback
|
|
11
|
+
after_initialize after_find after_touch
|
|
12
|
+
].freeze
|
|
13
|
+
|
|
14
|
+
class << self
|
|
15
|
+
def key = "rails_callback"
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
private
|
|
19
|
+
|
|
20
|
+
def declaration(node)
|
|
21
|
+
deletion(node) + scopes(node)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def scopes(node)
|
|
25
|
+
keywords(node)&.elements.to_a.flat_map { |assoc| scope(node, assoc) }
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def scope(node, assoc)
|
|
29
|
+
return [] unless scopable?(assoc)
|
|
30
|
+
indexed(node, unescaped(assoc.key), assoc.value.elements)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def scopable?(assoc)
|
|
34
|
+
return false unless assoc.is_a?(AssocNode)
|
|
35
|
+
return false unless %w[only except].include?(unescaped(assoc.key))
|
|
36
|
+
assoc.value.is_a?(ArrayNode)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def indexed(node, key, elements)
|
|
40
|
+
elements.each_index.map { |index| variant(node, key, elements, index) }
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def variant(node, key, elements, index)
|
|
44
|
+
Kimera::Operators::Variant.new(
|
|
45
|
+
label: "#{node.name}: drop #{elements[index].location.slice} from #{key}:",
|
|
46
|
+
directive: directive("kwarg_element_drop", key: key, index: index)
|
|
47
|
+
)
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "base"
|
|
4
|
+
|
|
5
|
+
class Kimera::Operators::RailsDeclaration < Kimera::Operators::Base
|
|
6
|
+
class << self
|
|
7
|
+
def body? = true
|
|
8
|
+
|
|
9
|
+
def statement? = true
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def variants(node, **)
|
|
13
|
+
return unless call(node).bare?(self.class::NAMES)
|
|
14
|
+
declaration(node)
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "base"
|
|
4
|
+
|
|
5
|
+
class Kimera::Operators::RailsPermit < Kimera::Operators::Base
|
|
6
|
+
class << self
|
|
7
|
+
def key = "rails_permit"
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def variants(node, **)
|
|
11
|
+
return unless call(node).chained?(:permit)
|
|
12
|
+
args = arguments(node)
|
|
13
|
+
return unless args.all? { |a| plain?(a) }
|
|
14
|
+
indexed(args)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
private
|
|
18
|
+
|
|
19
|
+
def indexed(args)
|
|
20
|
+
args.each_index.map { |index| variant(args, index) }
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def variant(args, index)
|
|
24
|
+
Kimera::Operators::Variant.new(
|
|
25
|
+
label: "permit: drop #{args[index].location.slice}",
|
|
26
|
+
directive: directive("drop_argument", index: index)
|
|
27
|
+
)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def plain?(node)
|
|
31
|
+
!node.is_a?(SplatNode) && !node.is_a?(KeywordHashNode) &&
|
|
32
|
+
!node.is_a?(BlockArgumentNode)
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "rails_declaration"
|
|
4
|
+
|
|
5
|
+
class Kimera::Operators::RailsValidation < Kimera::Operators::RailsDeclaration
|
|
6
|
+
NAMES = %i[validates validate validates_with validates_each].freeze
|
|
7
|
+
|
|
8
|
+
class << self
|
|
9
|
+
def key = "rails_validation"
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
private
|
|
13
|
+
|
|
14
|
+
def declaration(node)
|
|
15
|
+
deletion(node)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "base"
|
|
4
|
+
|
|
5
|
+
class Kimera::Operators::Range < Kimera::Operators::Base
|
|
6
|
+
class << self
|
|
7
|
+
def key = "range"
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def variants(node, **)
|
|
11
|
+
return unless node.is_a?(RangeNode)
|
|
12
|
+
return solo("... => ..", "range_flip", to: "irange") if node.exclude_end?
|
|
13
|
+
solo(".. => ...", "range_flip", to: "erange")
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "base"
|
|
4
|
+
|
|
5
|
+
class Kimera::Operators::RegexpLiteral < Kimera::Operators::Base
|
|
6
|
+
class << self
|
|
7
|
+
def key = "regexp"
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def variants(node, **)
|
|
11
|
+
return unless node.is_a?(RegularExpressionNode)
|
|
12
|
+
return if node.unescaped.empty?
|
|
13
|
+
solo("regexp => // (match all)", "regexp_literal", source: "") +
|
|
14
|
+
solo("regexp => /(?!)/ (match none)", "regexp_literal", source: "(?!)")
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "base"
|
|
4
|
+
|
|
5
|
+
class Kimera::Operators::RespondToGuard < Kimera::Operators::Base
|
|
6
|
+
class << self
|
|
7
|
+
def key = "respond_to_guard"
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def variants(node, **)
|
|
11
|
+
return unless guard?(node)
|
|
12
|
+
solo("delete .respond_to?", "unwrap_receiver")
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
private
|
|
16
|
+
|
|
17
|
+
def guard?(node)
|
|
18
|
+
return false unless call(node).chained?(:respond_to?) && !node.block
|
|
19
|
+
args = arguments(node)
|
|
20
|
+
return false unless (1..2).cover?(args.length)
|
|
21
|
+
target = args.first
|
|
22
|
+
target.is_a?(SymbolNode) || target.is_a?(StringNode)
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "base"
|
|
4
|
+
|
|
5
|
+
class Kimera::Operators::ReturnValue < Kimera::Operators::Base
|
|
6
|
+
class << self
|
|
7
|
+
def key = "return_value"
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
SKIPPED = [NilNode, ParenthesesNode].freeze
|
|
11
|
+
POSITIONS = Hash.new(->(_operator, _node) {}).merge(tail: ->(operator, node) { operator.tail(node) }).freeze
|
|
12
|
+
|
|
13
|
+
def variants(node, position:)
|
|
14
|
+
return returned(node) if node.is_a?(ReturnNode)
|
|
15
|
+
POSITIONS[position].call(self, node)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
private
|
|
19
|
+
|
|
20
|
+
def tail(node)
|
|
21
|
+
solo("tail => nil", Kimera::Operators::STATEMENT_DELETION) if nilable?(node)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
public :tail
|
|
25
|
+
|
|
26
|
+
def nilable?(node)
|
|
27
|
+
return false if identifier?(node)
|
|
28
|
+
SKIPPED.none? { |kind| node.is_a?(kind) }
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def returned(node)
|
|
32
|
+
args = arguments(node)
|
|
33
|
+
return if args.empty? || (args in [NilNode])
|
|
34
|
+
solo("return => return nil", "return_nil")
|
|
35
|
+
end
|
|
36
|
+
end
|