Full Code of veelenga/ameba for AI

master 77bf37bb5d3f cached
362 files
1.0 MB
289.8k tokens
1 requests
Download .txt
Showing preview only (1,158K chars total). Download the full file or copy to clipboard to get everything.
Repository: veelenga/ameba
Branch: master
Commit: 77bf37bb5d3f
Files: 362
Total size: 1.0 MB

Directory structure:
gitextract_e8xowmtu/

├── .ameba.yml
├── .ameba.yml.schema.json
├── .dockerignore
├── .editorconfig
├── .gitattributes
├── .github/
│   ├── dependabot.yml
│   └── workflows/
│       ├── add-docs-version-to-json-index.yml
│       ├── build-and-deploy-docs.yml
│       ├── ci.yml
│       ├── compare-json-schema.yml
│       ├── docker-image.yml
│       ├── docs.yml
│       ├── release.yml
│       ├── typos.yml
│       └── update-json-schema.yml
├── .gitignore
├── .typos.toml
├── Dockerfile
├── LICENSE
├── Makefile
├── README.md
├── bench/
│   └── check_sources.cr
├── bin/
│   └── ameba.cr
├── shard.yml
├── spec/
│   ├── ameba/
│   │   ├── ast/
│   │   │   ├── flow_expression_spec.cr
│   │   │   ├── liveness_analyzer_spec.cr
│   │   │   ├── scope_spec.cr
│   │   │   ├── util_spec.cr
│   │   │   ├── variabling/
│   │   │   │   ├── argument_spec.cr
│   │   │   │   ├── assignment_spec.cr
│   │   │   │   ├── reference_spec.cr
│   │   │   │   ├── type_dec_variable_spec.cr
│   │   │   │   └── variable_spec.cr
│   │   │   └── visitors/
│   │   │       ├── counting_visitor_spec.cr
│   │   │       ├── elseif_aware_node_visitor_spec.cr
│   │   │       ├── flow_expression_visitor_spec.cr
│   │   │       ├── implicit_return_visitor_spec.cr
│   │   │       ├── node_visitor_spec.cr
│   │   │       ├── redundant_control_expression_visitor_spec.cr
│   │   │       ├── scope_calls_with_self_receiver_visitor_spec.cr
│   │   │       ├── scope_visitor_spec.cr
│   │   │       └── top_level_nodes_visitor_spec.cr
│   │   ├── base_spec.cr
│   │   ├── cli/
│   │   │   └── cmd_spec.cr
│   │   ├── config_spec.cr
│   │   ├── ext/
│   │   │   └── location_spec.cr
│   │   ├── formatter/
│   │   │   ├── disabled_formatter_spec.cr
│   │   │   ├── dot_formatter_spec.cr
│   │   │   ├── explain_formatter_spec.cr
│   │   │   ├── flycheck_formatter_spec.cr
│   │   │   ├── github_actions_formatter_spec.cr
│   │   │   ├── json_formatter_spec.cr
│   │   │   ├── todo_formatter_spec.cr
│   │   │   └── util_spec.cr
│   │   ├── glob_utils_spec.cr
│   │   ├── inline_comments_spec.cr
│   │   ├── issue_spec.cr
│   │   ├── presenter/
│   │   │   ├── rule_collection_presenter_spec.cr
│   │   │   ├── rule_presenter_spec.cr
│   │   │   └── rule_versions_presenter_spec.cr
│   │   ├── reportable_spec.cr
│   │   ├── rule/
│   │   │   ├── base_spec.cr
│   │   │   ├── documentation/
│   │   │   │   ├── admonition_spec.cr
│   │   │   │   └── documentation_spec.cr
│   │   │   ├── layout/
│   │   │   │   ├── line_length_spec.cr
│   │   │   │   ├── trailing_blank_lines_spec.cr
│   │   │   │   └── trailing_whitespace_spec.cr
│   │   │   ├── lint/
│   │   │   │   ├── ambiguous_assignment_spec.cr
│   │   │   │   ├── assignment_in_call_argument_spec.cr
│   │   │   │   ├── bad_directive_spec.cr
│   │   │   │   ├── comparison_to_boolean_spec.cr
│   │   │   │   ├── debug_calls_spec.cr
│   │   │   │   ├── debugger_statement_spec.cr
│   │   │   │   ├── duplicate_branch_spec.cr
│   │   │   │   ├── duplicate_enum_value_spec.cr
│   │   │   │   ├── duplicate_method_signature_spec.cr
│   │   │   │   ├── duplicate_when_condition_spec.cr
│   │   │   │   ├── duplicated_require_spec.cr
│   │   │   │   ├── else_nil_spec.cr
│   │   │   │   ├── empty_ensure_spec.cr
│   │   │   │   ├── empty_expression_spec.cr
│   │   │   │   ├── empty_loop_spec.cr
│   │   │   │   ├── enum_member_name_conflict_spec.cr
│   │   │   │   ├── formatting_spec.cr
│   │   │   │   ├── hash_duplicated_key_spec.cr
│   │   │   │   ├── literal_assignments_in_expressions_spec.cr
│   │   │   │   ├── literal_in_condition_spec.cr
│   │   │   │   ├── literal_in_interpolation_spec.cr
│   │   │   │   ├── literals_comparison_spec.cr
│   │   │   │   ├── missing_block_argument_spec.cr
│   │   │   │   ├── non_existent_rule_spec.cr
│   │   │   │   ├── not_nil_after_no_bang_spec.cr
│   │   │   │   ├── not_nil_spec.cr
│   │   │   │   ├── percent_arrays_spec.cr
│   │   │   │   ├── rand_zero_spec.cr
│   │   │   │   ├── redundant_string_cercion_spec.cr
│   │   │   │   ├── redundant_with_index_spec.cr
│   │   │   │   ├── redundant_with_object_spec.cr
│   │   │   │   ├── require_parentheses_spec.cr
│   │   │   │   ├── self_initialize_definition_spec.cr
│   │   │   │   ├── shadowed_argument_spec.cr
│   │   │   │   ├── shadowed_exception_spec.cr
│   │   │   │   ├── shadowing_outer_local_var_spec.cr
│   │   │   │   ├── shared_var_in_fiber_spec.cr
│   │   │   │   ├── signal_trap_spec.cr
│   │   │   │   ├── spec_eq_with_bool_or_nil_literal_spec.cr
│   │   │   │   ├── spec_filename_spec.cr
│   │   │   │   ├── spec_focus_spec.cr
│   │   │   │   ├── syntax_spec.cr
│   │   │   │   ├── top_level_operator_definition_spec.cr
│   │   │   │   ├── trailing_rescue_exception_spec.cr
│   │   │   │   ├── typos_spec.cr
│   │   │   │   ├── unneeded_disable_directive_spec.cr
│   │   │   │   ├── unreachable_code_spec.cr
│   │   │   │   ├── unused_argument_spec.cr
│   │   │   │   ├── unused_block_argument_spec.cr
│   │   │   │   ├── unused_expression_spec.cr
│   │   │   │   ├── unused_rescue_variable_spec.cr
│   │   │   │   ├── useless_assign_spec.cr
│   │   │   │   ├── useless_condition_in_when_spec.cr
│   │   │   │   ├── useless_visibility_modifier_spec.cr
│   │   │   │   ├── void_outside_lib_spec.cr
│   │   │   │   └── whitespace_around_macro_expression_spec.cr
│   │   │   ├── metrics/
│   │   │   │   └── cyclomatic_complexity_spec.cr
│   │   │   ├── naming/
│   │   │   │   ├── accessor_method_name_spec.cr
│   │   │   │   ├── ascii_identifiers_spec.cr
│   │   │   │   ├── binary_operator_parameter_name_spec.cr
│   │   │   │   ├── block_parameter_name_spec.cr
│   │   │   │   ├── constant_names_spec.cr
│   │   │   │   ├── filename_spec.cr
│   │   │   │   ├── method_names_spec.cr
│   │   │   │   ├── predicate_name_spec.cr
│   │   │   │   ├── query_bool_methods_spec.cr
│   │   │   │   ├── rescued_exceptions_variable_name_spec.cr
│   │   │   │   ├── type_names_spec.cr
│   │   │   │   └── variable_names_spec.cr
│   │   │   ├── performance/
│   │   │   │   ├── any_after_filter_spec.cr
│   │   │   │   ├── any_instead_of_present_spec.cr
│   │   │   │   ├── base_spec.cr
│   │   │   │   ├── chained_call_with_no_bang_spec.cr
│   │   │   │   ├── compact_after_map_spec.cr
│   │   │   │   ├── excessive_allocations_spec.cr
│   │   │   │   ├── first_last_after_filter_spec.cr
│   │   │   │   ├── flatten_after_map_spec.cr
│   │   │   │   ├── map_instead_of_block_spec.cr
│   │   │   │   ├── minmax_after_map_spec.cr
│   │   │   │   ├── size_after_filter_spec.cr
│   │   │   │   └── times_map_spec.cr
│   │   │   ├── style/
│   │   │   │   ├── array_literal_syntax_spec.cr
│   │   │   │   ├── call_parentheses_spec.cr
│   │   │   │   ├── elsif_spec.cr
│   │   │   │   ├── guard_clause_spec.cr
│   │   │   │   ├── hash_literal_syntax_spec.cr
│   │   │   │   ├── heredoc_escape_spec.cr
│   │   │   │   ├── heredoc_indent_spec.cr
│   │   │   │   ├── is_a_filter_spec.cr
│   │   │   │   ├── is_a_nil_spec.cr
│   │   │   │   ├── large_numbers_spec.cr
│   │   │   │   ├── multiline_curly_block_spec.cr
│   │   │   │   ├── multiline_string_literal_spec.cr
│   │   │   │   ├── negated_conditions_in_unless_spec.cr
│   │   │   │   ├── parentheses_around_condition_spec.cr
│   │   │   │   ├── percent_literal_delimiters_spec.cr
│   │   │   │   ├── redundant_begin_spec.cr
│   │   │   │   ├── redundant_next_spec.cr
│   │   │   │   ├── redundant_nil_in_control_expression_spec.cr
│   │   │   │   ├── redundant_return_spec.cr
│   │   │   │   ├── redundant_self_spec.cr
│   │   │   │   ├── unless_else_spec.cr
│   │   │   │   ├── verbose_block_spec.cr
│   │   │   │   ├── verbose_nil_type_spec.cr
│   │   │   │   └── while_true_spec.cr
│   │   │   └── typing/
│   │   │       ├── macro_call_argument_type_restriction_spec.cr
│   │   │       ├── method_parameter_type_restriction_spec.cr
│   │   │       ├── method_return_type_restriction_spec.cr
│   │   │       └── proc_literal_return_type_restriction_spec.cr
│   │   ├── runner_spec.cr
│   │   ├── severity_spec.cr
│   │   ├── source/
│   │   │   └── rewriter_spec.cr
│   │   ├── source_spec.cr
│   │   ├── spec/
│   │   │   └── annotated_source_spec.cr
│   │   ├── tokenizer_spec.cr
│   │   └── version_spec.cr
│   ├── ameba_spec.cr
│   ├── fixtures/
│   │   └── .ameba.yml
│   └── spec_helper.cr
└── src/
    ├── ameba/
    │   ├── ast/
    │   │   ├── flow_expression.cr
    │   │   ├── liveness_analyzer.cr
    │   │   ├── scope.cr
    │   │   ├── util.cr
    │   │   ├── variabling/
    │   │   │   ├── argument.cr
    │   │   │   ├── assignment.cr
    │   │   │   ├── ivariable.cr
    │   │   │   ├── reference.cr
    │   │   │   ├── type_dec_variable.cr
    │   │   │   └── variable.cr
    │   │   └── visitors/
    │   │       ├── base_visitor.cr
    │   │       ├── counting_visitor.cr
    │   │       ├── elseif_aware_node_visitor.cr
    │   │       ├── flow_expression_visitor.cr
    │   │       ├── implicit_return_visitor.cr
    │   │       ├── macro_reference_finder.cr
    │   │       ├── node_visitor.cr
    │   │       ├── redundant_control_expression_visitor.cr
    │   │       ├── scope_calls_with_self_receiver_visitor.cr
    │   │       ├── scope_visitor.cr
    │   │       └── top_level_nodes_visitor.cr
    │   ├── cli/
    │   │   └── cmd.cr
    │   ├── config/
    │   │   ├── loader.cr
    │   │   └── rule_config.cr
    │   ├── config.cr
    │   ├── ext/
    │   │   └── location.cr
    │   ├── formatter/
    │   │   ├── base_formatter.cr
    │   │   ├── disabled_formatter.cr
    │   │   ├── dot_formatter.cr
    │   │   ├── explain_formatter.cr
    │   │   ├── flycheck_formatter.cr
    │   │   ├── github_actions_formatter.cr
    │   │   ├── json_formatter.cr
    │   │   ├── todo_formatter.cr
    │   │   └── util.cr
    │   ├── glob_utils.cr
    │   ├── inline_comments.cr
    │   ├── issue.cr
    │   ├── json_schema/
    │   │   └── builder.cr
    │   ├── presenter/
    │   │   ├── base_presenter.cr
    │   │   ├── rule_collection_presenter.cr
    │   │   ├── rule_presenter.cr
    │   │   └── rule_versions_presenter.cr
    │   ├── reportable.cr
    │   ├── rule/
    │   │   ├── base.cr
    │   │   ├── documentation/
    │   │   │   ├── admonition.cr
    │   │   │   └── documentation.cr
    │   │   ├── layout/
    │   │   │   ├── line_length.cr
    │   │   │   ├── trailing_blank_lines.cr
    │   │   │   └── trailing_whitespace.cr
    │   │   ├── lint/
    │   │   │   ├── ambiguous_assignment.cr
    │   │   │   ├── assignment_in_call_argument.cr
    │   │   │   ├── bad_directive.cr
    │   │   │   ├── comparison_to_boolean.cr
    │   │   │   ├── debug_calls.cr
    │   │   │   ├── debugger_statement.cr
    │   │   │   ├── duplicate_branch.cr
    │   │   │   ├── duplicate_enum_value.cr
    │   │   │   ├── duplicate_method_signature.cr
    │   │   │   ├── duplicate_when_condition.cr
    │   │   │   ├── duplicated_require.cr
    │   │   │   ├── else_nil.cr
    │   │   │   ├── empty_ensure.cr
    │   │   │   ├── empty_expression.cr
    │   │   │   ├── empty_loop.cr
    │   │   │   ├── enum_member_name_conflict.cr
    │   │   │   ├── formatting.cr
    │   │   │   ├── hash_duplicated_key.cr
    │   │   │   ├── literal_assignments_in_expressions.cr
    │   │   │   ├── literal_in_condition.cr
    │   │   │   ├── literal_in_interpolation.cr
    │   │   │   ├── literals_comparison.cr
    │   │   │   ├── missing_block_argument.cr
    │   │   │   ├── non_existent_rule.cr
    │   │   │   ├── not_nil.cr
    │   │   │   ├── not_nil_after_no_bang.cr
    │   │   │   ├── percent_arrays.cr
    │   │   │   ├── rand_zero.cr
    │   │   │   ├── redundant_string_coercion.cr
    │   │   │   ├── redundant_with_index.cr
    │   │   │   ├── redundant_with_object.cr
    │   │   │   ├── require_parentheses.cr
    │   │   │   ├── self_initialize_definition.cr
    │   │   │   ├── shadowed_argument.cr
    │   │   │   ├── shadowed_exception.cr
    │   │   │   ├── shadowing_outer_local_var.cr
    │   │   │   ├── shared_var_in_fiber.cr
    │   │   │   ├── signal_trap.cr
    │   │   │   ├── spec_eq_with_bool_or_nil_literal.cr
    │   │   │   ├── spec_filename.cr
    │   │   │   ├── spec_focus.cr
    │   │   │   ├── syntax.cr
    │   │   │   ├── top_level_operator_definition.cr
    │   │   │   ├── trailing_rescue_exception.cr
    │   │   │   ├── typos.cr
    │   │   │   ├── unneeded_disable_directive.cr
    │   │   │   ├── unreachable_code.cr
    │   │   │   ├── unused_argument.cr
    │   │   │   ├── unused_block_argument.cr
    │   │   │   ├── unused_expression.cr
    │   │   │   ├── unused_rescue_variable.cr
    │   │   │   ├── useless_assign.cr
    │   │   │   ├── useless_condition_in_when.cr
    │   │   │   ├── useless_visibility_modifier.cr
    │   │   │   ├── void_outside_lib.cr
    │   │   │   └── whitespace_around_macro_expression.cr
    │   │   ├── metrics/
    │   │   │   └── cyclomatic_complexity.cr
    │   │   ├── naming/
    │   │   │   ├── accessor_method_name.cr
    │   │   │   ├── ascii_identifiers.cr
    │   │   │   ├── binary_operator_parameter_name.cr
    │   │   │   ├── block_parameter_name.cr
    │   │   │   ├── constant_names.cr
    │   │   │   ├── filename.cr
    │   │   │   ├── method_names.cr
    │   │   │   ├── predicate_name.cr
    │   │   │   ├── query_bool_methods.cr
    │   │   │   ├── rescued_exceptions_variable_name.cr
    │   │   │   ├── type_names.cr
    │   │   │   └── variable_names.cr
    │   │   ├── performance/
    │   │   │   ├── any_after_filter.cr
    │   │   │   ├── any_instead_of_present.cr
    │   │   │   ├── base.cr
    │   │   │   ├── chained_call_with_no_bang.cr
    │   │   │   ├── compact_after_map.cr
    │   │   │   ├── excessive_allocations.cr
    │   │   │   ├── first_last_after_filter.cr
    │   │   │   ├── flatten_after_map.cr
    │   │   │   ├── map_instead_of_block.cr
    │   │   │   ├── minmax_after_map.cr
    │   │   │   ├── size_after_filter.cr
    │   │   │   └── times_map.cr
    │   │   ├── style/
    │   │   │   ├── array_literal_syntax.cr
    │   │   │   ├── call_parentheses.cr
    │   │   │   ├── elsif.cr
    │   │   │   ├── guard_clause.cr
    │   │   │   ├── hash_literal_syntax.cr
    │   │   │   ├── heredoc_escape.cr
    │   │   │   ├── heredoc_indent.cr
    │   │   │   ├── is_a_filter.cr
    │   │   │   ├── is_a_nil.cr
    │   │   │   ├── large_numbers.cr
    │   │   │   ├── multiline_curly_block.cr
    │   │   │   ├── multiline_string_literal.cr
    │   │   │   ├── negated_conditions_in_unless.cr
    │   │   │   ├── parentheses_around_condition.cr
    │   │   │   ├── percent_literal_delimiters.cr
    │   │   │   ├── redundant_begin.cr
    │   │   │   ├── redundant_next.cr
    │   │   │   ├── redundant_nil_in_control_expression.cr
    │   │   │   ├── redundant_return.cr
    │   │   │   ├── redundant_self.cr
    │   │   │   ├── unless_else.cr
    │   │   │   ├── verbose_block.cr
    │   │   │   ├── verbose_nil_type.cr
    │   │   │   └── while_true.cr
    │   │   └── typing/
    │   │       ├── macro_call_argument_type_restriction.cr
    │   │       ├── method_parameter_type_restriction.cr
    │   │       ├── method_return_type_restriction.cr
    │   │       └── proc_literal_return_type_restriction.cr
    │   ├── runner.cr
    │   ├── severity.cr
    │   ├── source/
    │   │   ├── corrector.cr
    │   │   ├── rewriter/
    │   │   │   └── action.cr
    │   │   └── rewriter.cr
    │   ├── source.cr
    │   ├── spec/
    │   │   ├── annotated_source.cr
    │   │   ├── be_valid.cr
    │   │   ├── expect_issue.cr
    │   │   ├── support.cr
    │   │   └── util.cr
    │   ├── tokenizer.cr
    │   └── version.cr
    ├── ameba.cr
    ├── cli.cr
    ├── contrib/
    │   └── read_type_doc.cr
    └── json-schema-builder.cr

================================================
FILE CONTENTS
================================================

================================================
FILE: .ameba.yml
================================================
Lint/Typos:
  Enabled: true
  Excluded:
    - spec/ameba/rule/lint/typos_spec.cr

Style/Elsif:
  Enabled: true


================================================
FILE: .ameba.yml.schema.json
================================================
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://crystal-ameba.github.io/.ameba.yml.schema.json",
  "title": ".ameba.yml",
  "description": "Configuration rules for the Crystal language Ameba linter",
  "type": "object",
  "additionalProperties": false,
  "$defs": {
    "Severity": {
      "type": "string",
      "enum": [
        "Error",
        "Warning",
        "Convention"
      ]
    },
    "Globs": {
      "type": "array",
      "title": "Globbed files and paths",
      "description": "An array of wildcards (or paths) to include to the inspection",
      "items": {
        "type": "string",
        "examples": [
          "src/**/*.{cr,ecr}",
          "!lib"
        ]
      }
    },
    "Excluded": {
      "type": "array",
      "title": "Excluded files and paths",
      "description": "An array of wildcards (or paths) to exclude from the source list",
      "items": {
        "type": "string",
        "examples": [
          "spec/fixtures/**",
          "spec/**/*.manual_spec.cr"
        ]
      }
    },
    "BaseRule": {
      "type": "object",
      "properties": {
        "SinceVersion": {
          "type": "string"
        },
        "Enabled": {
          "type": "boolean",
          "default": true
        },
        "Severity": {
          "$ref": "#/$defs/Severity",
          "default": "Convention"
        },
        "Excluded": {
          "$ref": "#/$defs/Excluded"
        }
      }
    }
  },
  "properties": {
    "Version": {
      "type": "string",
      "description": "The version of Ameba to limit rules to",
      "examples": [
        "1.7.0",
        "1.6.4"
      ]
    },
    "Formatter": {
      "type": "object",
      "description": "The formatter to use for Ameba",
      "properties": {
        "Name": {
          "type": "string",
          "enum": [
            "progress",
            "todo",
            "flycheck",
            "silent",
            "disabled",
            "json",
            "github-actions"
          ]
        }
      }
    },
    "Globs": {
      "$ref": "#/$defs/Globs"
    },
    "Excluded": {
      "$ref": "#/$defs/Excluded"
    },
    "Documentation/Admonition": {
      "$ref": "#/$defs/BaseRule",
      "$comment": "https://crystal-ameba.github.io/ameba/master/Ameba/Rule/Documentation/Admonition.html",
      "title": "Documentation/Admonition",
      "description": "Reports documentation admonitions",
      "properties": {
        "SinceVersion": {
          "type": "string",
          "default": "1.6.0"
        },
        "Enabled": {
          "type": "boolean",
          "default": false
        },
        "Severity": {
          "$ref": "#/$defs/Severity",
          "default": "Warning"
        },
        "Admonitions": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "default": [
            "TODO",
            "FIXME",
            "BUG"
          ]
        },
        "Timezone": {
          "type": "string",
          "default": "UTC"
        }
      }
    },
    "Documentation/Documentation": {
      "$ref": "#/$defs/BaseRule",
      "$comment": "https://crystal-ameba.github.io/ameba/master/Ameba/Rule/Documentation/Documentation.html",
      "title": "Documentation/Documentation",
      "description": "Enforces public types to be documented",
      "properties": {
        "SinceVersion": {
          "type": "string",
          "default": "1.5.0"
        },
        "Enabled": {
          "type": "boolean",
          "default": false
        },
        "IgnoreClasses": {
          "type": "boolean",
          "default": false
        },
        "IgnoreModules": {
          "type": "boolean",
          "default": true
        },
        "IgnoreEnums": {
          "type": "boolean",
          "default": false
        },
        "IgnoreDefs": {
          "type": "boolean",
          "default": true
        },
        "IgnoreMacros": {
          "type": "boolean",
          "default": false
        },
        "IgnoreMacroHooks": {
          "type": "boolean",
          "default": true
        },
        "RequireExample": {
          "type": "boolean",
          "default": false
        }
      }
    },
    "Layout/LineLength": {
      "$ref": "#/$defs/BaseRule",
      "$comment": "https://crystal-ameba.github.io/ameba/master/Ameba/Rule/Layout/LineLength.html",
      "title": "Layout/LineLength",
      "description": "Disallows lines longer than `MaxLength` number of symbols",
      "properties": {
        "SinceVersion": {
          "type": "string",
          "default": "0.1.0"
        },
        "Enabled": {
          "type": "boolean",
          "default": false
        },
        "MaxLength": {
          "type": "number",
          "default": 140
        }
      }
    },
    "Layout/TrailingBlankLines": {
      "$ref": "#/$defs/BaseRule",
      "$comment": "https://crystal-ameba.github.io/ameba/master/Ameba/Rule/Layout/TrailingBlankLines.html",
      "title": "Layout/TrailingBlankLines",
      "description": "Disallows trailing blank lines",
      "properties": {
        "SinceVersion": {
          "type": "string",
          "default": "0.1.0"
        }
      }
    },
    "Layout/TrailingWhitespace": {
      "$ref": "#/$defs/BaseRule",
      "$comment": "https://crystal-ameba.github.io/ameba/master/Ameba/Rule/Layout/TrailingWhitespace.html",
      "title": "Layout/TrailingWhitespace",
      "description": "Disallows trailing whitespace",
      "properties": {
        "SinceVersion": {
          "type": "string",
          "default": "0.1.0"
        }
      }
    },
    "Lint/AmbiguousAssignment": {
      "$ref": "#/$defs/BaseRule",
      "$comment": "https://crystal-ameba.github.io/ameba/master/Ameba/Rule/Lint/AmbiguousAssignment.html",
      "title": "Lint/AmbiguousAssignment",
      "description": "Disallows ambiguous `=-/=+/=!`",
      "properties": {
        "SinceVersion": {
          "type": "string",
          "default": "1.0.0"
        },
        "Severity": {
          "$ref": "#/$defs/Severity",
          "default": "Warning"
        }
      }
    },
    "Lint/AssignmentInCallArgument": {
      "$ref": "#/$defs/BaseRule",
      "$comment": "https://crystal-ameba.github.io/ameba/master/Ameba/Rule/Lint/AssignmentInCallArgument.html",
      "title": "Lint/AssignmentInCallArgument",
      "description": "Disallows variable assignment in call arguments",
      "properties": {
        "SinceVersion": {
          "type": "string",
          "default": "1.7.0"
        },
        "Severity": {
          "$ref": "#/$defs/Severity",
          "default": "Warning"
        }
      }
    },
    "Lint/BadDirective": {
      "$ref": "#/$defs/BaseRule",
      "$comment": "https://crystal-ameba.github.io/ameba/master/Ameba/Rule/Lint/BadDirective.html",
      "title": "Lint/BadDirective",
      "description": "Reports bad comment directives",
      "properties": {
        "SinceVersion": {
          "type": "string",
          "default": "0.13.0"
        },
        "Severity": {
          "$ref": "#/$defs/Severity",
          "default": "Warning"
        }
      }
    },
    "Lint/ComparisonToBoolean": {
      "$ref": "#/$defs/BaseRule",
      "$comment": "https://crystal-ameba.github.io/ameba/master/Ameba/Rule/Lint/ComparisonToBoolean.html",
      "title": "Lint/ComparisonToBoolean",
      "description": "Disallows comparison to booleans",
      "properties": {
        "SinceVersion": {
          "type": "string",
          "default": "0.1.0"
        },
        "Enabled": {
          "type": "boolean",
          "default": false
        },
        "Severity": {
          "$ref": "#/$defs/Severity",
          "default": "Warning"
        }
      }
    },
    "Lint/DebugCalls": {
      "$ref": "#/$defs/BaseRule",
      "$comment": "https://crystal-ameba.github.io/ameba/master/Ameba/Rule/Lint/DebugCalls.html",
      "title": "Lint/DebugCalls",
      "description": "Disallows debug-related calls",
      "properties": {
        "SinceVersion": {
          "type": "string",
          "default": "1.0.0"
        },
        "MethodNames": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "default": [
            "p",
            "p!",
            "pp",
            "pp!"
          ]
        },
        "Severity": {
          "$ref": "#/$defs/Severity",
          "default": "Warning"
        }
      }
    },
    "Lint/DebuggerStatement": {
      "$ref": "#/$defs/BaseRule",
      "$comment": "https://crystal-ameba.github.io/ameba/master/Ameba/Rule/Lint/DebuggerStatement.html",
      "title": "Lint/DebuggerStatement",
      "description": "Disallows calls to `debugger`",
      "properties": {
        "SinceVersion": {
          "type": "string",
          "default": "0.1.0"
        },
        "Severity": {
          "$ref": "#/$defs/Severity",
          "default": "Warning"
        }
      }
    },
    "Lint/DuplicateBranch": {
      "$ref": "#/$defs/BaseRule",
      "$comment": "https://crystal-ameba.github.io/ameba/master/Ameba/Rule/Lint/DuplicateBranch.html",
      "title": "Lint/DuplicateBranch",
      "description": "Reports duplicated branch bodies",
      "properties": {
        "SinceVersion": {
          "type": "string",
          "default": "1.7.0"
        },
        "Enabled": {
          "type": "boolean",
          "default": false
        },
        "IgnoreLiteralBranches": {
          "type": "boolean",
          "default": false
        },
        "IgnoreConstantBranches": {
          "type": "boolean",
          "default": false
        },
        "IgnoreDuplicateElseBranch": {
          "type": "boolean",
          "default": false
        },
        "Severity": {
          "$ref": "#/$defs/Severity",
          "default": "Warning"
        }
      }
    },
    "Lint/DuplicateEnumValue": {
      "$ref": "#/$defs/BaseRule",
      "$comment": "https://crystal-ameba.github.io/ameba/master/Ameba/Rule/Lint/DuplicateEnumValue.html",
      "title": "Lint/DuplicateEnumValue",
      "description": "Reports duplicated `enum` member values",
      "properties": {
        "SinceVersion": {
          "type": "string",
          "default": "1.7.0"
        },
        "Severity": {
          "$ref": "#/$defs/Severity",
          "default": "Warning"
        }
      }
    },
    "Lint/DuplicateMethodSignature": {
      "$ref": "#/$defs/BaseRule",
      "$comment": "https://crystal-ameba.github.io/ameba/master/Ameba/Rule/Lint/DuplicateMethodSignature.html",
      "title": "Lint/DuplicateMethodSignature",
      "description": "Reports repeated method signatures",
      "properties": {
        "SinceVersion": {
          "type": "string",
          "default": "1.7.0"
        },
        "Severity": {
          "$ref": "#/$defs/Severity",
          "default": "Warning"
        }
      }
    },
    "Lint/DuplicateWhenCondition": {
      "$ref": "#/$defs/BaseRule",
      "$comment": "https://crystal-ameba.github.io/ameba/master/Ameba/Rule/Lint/DuplicateWhenCondition.html",
      "title": "Lint/DuplicateWhenCondition",
      "description": "Reports repeated conditions used in case `when` expressions",
      "properties": {
        "SinceVersion": {
          "type": "string",
          "default": "1.7.0"
        },
        "Severity": {
          "$ref": "#/$defs/Severity",
          "default": "Warning"
        }
      }
    },
    "Lint/DuplicatedRequire": {
      "$ref": "#/$defs/BaseRule",
      "$comment": "https://crystal-ameba.github.io/ameba/master/Ameba/Rule/Lint/DuplicatedRequire.html",
      "title": "Lint/DuplicatedRequire",
      "description": "Reports duplicated `require` statements",
      "properties": {
        "SinceVersion": {
          "type": "string",
          "default": "0.14.0"
        },
        "Severity": {
          "$ref": "#/$defs/Severity",
          "default": "Warning"
        }
      }
    },
    "Lint/ElseNil": {
      "$ref": "#/$defs/BaseRule",
      "$comment": "https://crystal-ameba.github.io/ameba/master/Ameba/Rule/Lint/ElseNil.html",
      "title": "Lint/ElseNil",
      "description": "Disallows `else` blocks with `nil` as their body",
      "properties": {
        "SinceVersion": {
          "type": "string",
          "default": "1.7.0"
        },
        "Severity": {
          "$ref": "#/$defs/Severity",
          "default": "Warning"
        }
      }
    },
    "Lint/EmptyEnsure": {
      "$ref": "#/$defs/BaseRule",
      "$comment": "https://crystal-ameba.github.io/ameba/master/Ameba/Rule/Lint/EmptyEnsure.html",
      "title": "Lint/EmptyEnsure",
      "description": "Disallows empty `ensure` statement",
      "properties": {
        "SinceVersion": {
          "type": "string",
          "default": "0.3.0"
        },
        "Severity": {
          "$ref": "#/$defs/Severity",
          "default": "Warning"
        }
      }
    },
    "Lint/EmptyExpression": {
      "$ref": "#/$defs/BaseRule",
      "$comment": "https://crystal-ameba.github.io/ameba/master/Ameba/Rule/Lint/EmptyExpression.html",
      "title": "Lint/EmptyExpression",
      "description": "Disallows empty expressions",
      "properties": {
        "SinceVersion": {
          "type": "string",
          "default": "0.2.0"
        },
        "Severity": {
          "$ref": "#/$defs/Severity",
          "default": "Warning"
        }
      }
    },
    "Lint/EmptyLoop": {
      "$ref": "#/$defs/BaseRule",
      "$comment": "https://crystal-ameba.github.io/ameba/master/Ameba/Rule/Lint/EmptyLoop.html",
      "title": "Lint/EmptyLoop",
      "description": "Disallows empty loops",
      "properties": {
        "SinceVersion": {
          "type": "string",
          "default": "0.12.0"
        },
        "Severity": {
          "$ref": "#/$defs/Severity",
          "default": "Warning"
        }
      }
    },
    "Lint/EnumMemberNameConflict": {
      "$ref": "#/$defs/BaseRule",
      "$comment": "https://crystal-ameba.github.io/ameba/master/Ameba/Rule/Lint/EnumMemberNameConflict.html",
      "title": "Lint/EnumMemberNameConflict",
      "description": "Reports conflicting enum member names",
      "properties": {
        "SinceVersion": {
          "type": "string",
          "default": "1.7.0"
        },
        "Severity": {
          "$ref": "#/$defs/Severity",
          "default": "Warning"
        }
      }
    },
    "Lint/Formatting": {
      "$ref": "#/$defs/BaseRule",
      "$comment": "https://crystal-ameba.github.io/ameba/master/Ameba/Rule/Lint/Formatting.html",
      "title": "Lint/Formatting",
      "description": "Reports not formatted sources",
      "properties": {
        "SinceVersion": {
          "type": "string",
          "default": "1.4.0"
        },
        "FailOnError": {
          "type": "boolean",
          "default": false
        },
        "Severity": {
          "$ref": "#/$defs/Severity",
          "default": "Warning"
        }
      }
    },
    "Lint/HashDuplicatedKey": {
      "$ref": "#/$defs/BaseRule",
      "$comment": "https://crystal-ameba.github.io/ameba/master/Ameba/Rule/Lint/HashDuplicatedKey.html",
      "title": "Lint/HashDuplicatedKey",
      "description": "Disallows duplicated keys in hash literals",
      "properties": {
        "SinceVersion": {
          "type": "string",
          "default": "0.3.0"
        },
        "Severity": {
          "$ref": "#/$defs/Severity",
          "default": "Warning"
        }
      }
    },
    "Lint/LiteralAssignmentsInExpressions": {
      "$ref": "#/$defs/BaseRule",
      "$comment": "https://crystal-ameba.github.io/ameba/master/Ameba/Rule/Lint/LiteralAssignmentsInExpressions.html",
      "title": "Lint/LiteralAssignmentsInExpressions",
      "description": "Disallows assignments with literal values in control expressions",
      "properties": {
        "SinceVersion": {
          "type": "string",
          "default": "1.4.0"
        },
        "Severity": {
          "$ref": "#/$defs/Severity",
          "default": "Warning"
        }
      }
    },
    "Lint/LiteralInCondition": {
      "$ref": "#/$defs/BaseRule",
      "$comment": "https://crystal-ameba.github.io/ameba/master/Ameba/Rule/Lint/LiteralInCondition.html",
      "title": "Lint/LiteralInCondition",
      "description": "Disallows useless conditional statements that contain a literal in place of a variable or predicate function",
      "properties": {
        "SinceVersion": {
          "type": "string",
          "default": "0.1.0"
        },
        "Severity": {
          "$ref": "#/$defs/Severity",
          "default": "Warning"
        }
      }
    },
    "Lint/LiteralInInterpolation": {
      "$ref": "#/$defs/BaseRule",
      "$comment": "https://crystal-ameba.github.io/ameba/master/Ameba/Rule/Lint/LiteralInInterpolation.html",
      "title": "Lint/LiteralInInterpolation",
      "description": "Disallows useless string interpolations",
      "properties": {
        "SinceVersion": {
          "type": "string",
          "default": "0.1.0"
        },
        "Severity": {
          "$ref": "#/$defs/Severity",
          "default": "Warning"
        }
      }
    },
    "Lint/LiteralsComparison": {
      "$ref": "#/$defs/BaseRule",
      "$comment": "https://crystal-ameba.github.io/ameba/master/Ameba/Rule/Lint/LiteralsComparison.html",
      "title": "Lint/LiteralsComparison",
      "description": "Identifies comparisons between literals",
      "properties": {
        "SinceVersion": {
          "type": "string",
          "default": "1.3.0"
        },
        "Severity": {
          "$ref": "#/$defs/Severity",
          "default": "Warning"
        }
      }
    },
    "Lint/MissingBlockArgument": {
      "$ref": "#/$defs/BaseRule",
      "$comment": "https://crystal-ameba.github.io/ameba/master/Ameba/Rule/Lint/MissingBlockArgument.html",
      "title": "Lint/MissingBlockArgument",
      "description": "Disallows yielding method definitions without block argument",
      "properties": {
        "SinceVersion": {
          "type": "string",
          "default": "1.4.0"
        },
        "Severity": {
          "$ref": "#/$defs/Severity",
          "default": "Warning"
        }
      }
    },
    "Lint/NonExistentRule": {
      "$ref": "#/$defs/BaseRule",
      "$comment": "https://crystal-ameba.github.io/ameba/master/Ameba/Rule/Lint/NonExistentRule.html",
      "title": "Lint/NonExistentRule",
      "description": "Reports non-existent rules in comment directives",
      "properties": {
        "SinceVersion": {
          "type": "string",
          "default": "0.13.0"
        },
        "Severity": {
          "$ref": "#/$defs/Severity",
          "default": "Warning"
        }
      }
    },
    "Lint/NotNil": {
      "$ref": "#/$defs/BaseRule",
      "$comment": "https://crystal-ameba.github.io/ameba/master/Ameba/Rule/Lint/NotNil.html",
      "title": "Lint/NotNil",
      "description": "Identifies usage of `not_nil!` calls",
      "properties": {
        "SinceVersion": {
          "type": "string",
          "default": "1.3.0"
        },
        "Severity": {
          "$ref": "#/$defs/Severity",
          "default": "Warning"
        }
      }
    },
    "Lint/NotNilAfterNoBang": {
      "$ref": "#/$defs/BaseRule",
      "$comment": "https://crystal-ameba.github.io/ameba/master/Ameba/Rule/Lint/NotNilAfterNoBang.html",
      "title": "Lint/NotNilAfterNoBang",
      "description": "Identifies usage of `index/rindex/find/match` calls followed by `not_nil!`",
      "properties": {
        "SinceVersion": {
          "type": "string",
          "default": "1.3.0"
        },
        "Severity": {
          "$ref": "#/$defs/Severity",
          "default": "Warning"
        }
      }
    },
    "Lint/PercentArrays": {
      "$ref": "#/$defs/BaseRule",
      "$comment": "https://crystal-ameba.github.io/ameba/master/Ameba/Rule/Lint/PercentArrays.html",
      "title": "Lint/PercentArrays",
      "description": "Disallows some unwanted symbols in percent array literals",
      "properties": {
        "SinceVersion": {
          "type": "string",
          "default": "0.3.0"
        },
        "StringArrayUnwantedSymbols": {
          "type": "string",
          "default": ",\""
        },
        "SymbolArrayUnwantedSymbols": {
          "type": "string",
          "default": ",:"
        },
        "Severity": {
          "$ref": "#/$defs/Severity",
          "default": "Warning"
        }
      }
    },
    "Lint/RandZero": {
      "$ref": "#/$defs/BaseRule",
      "$comment": "https://crystal-ameba.github.io/ameba/master/Ameba/Rule/Lint/RandZero.html",
      "title": "Lint/RandZero",
      "description": "Disallows `rand` zero calls",
      "properties": {
        "SinceVersion": {
          "type": "string",
          "default": "0.5.1"
        },
        "Severity": {
          "$ref": "#/$defs/Severity",
          "default": "Warning"
        }
      }
    },
    "Lint/RedundantStringCoercion": {
      "$ref": "#/$defs/BaseRule",
      "$comment": "https://crystal-ameba.github.io/ameba/master/Ameba/Rule/Lint/RedundantStringCoercion.html",
      "title": "Lint/RedundantStringCoercion",
      "description": "Disallows redundant string conversions in interpolation",
      "properties": {
        "SinceVersion": {
          "type": "string",
          "default": "0.12.0"
        },
        "Severity": {
          "$ref": "#/$defs/Severity",
          "default": "Warning"
        }
      }
    },
    "Lint/RedundantWithIndex": {
      "$ref": "#/$defs/BaseRule",
      "$comment": "https://crystal-ameba.github.io/ameba/master/Ameba/Rule/Lint/RedundantWithIndex.html",
      "title": "Lint/RedundantWithIndex",
      "description": "Disallows redundant `with_index` calls",
      "properties": {
        "SinceVersion": {
          "type": "string",
          "default": "0.11.0"
        },
        "Severity": {
          "$ref": "#/$defs/Severity",
          "default": "Warning"
        }
      }
    },
    "Lint/RedundantWithObject": {
      "$ref": "#/$defs/BaseRule",
      "$comment": "https://crystal-ameba.github.io/ameba/master/Ameba/Rule/Lint/RedundantWithObject.html",
      "title": "Lint/RedundantWithObject",
      "description": "Disallows redundant `with_object` calls",
      "properties": {
        "SinceVersion": {
          "type": "string",
          "default": "0.11.0"
        },
        "Severity": {
          "$ref": "#/$defs/Severity",
          "default": "Warning"
        }
      }
    },
    "Lint/RequireParentheses": {
      "$ref": "#/$defs/BaseRule",
      "$comment": "https://crystal-ameba.github.io/ameba/master/Ameba/Rule/Lint/RequireParentheses.html",
      "title": "Lint/RequireParentheses",
      "description": "Disallows method calls with no parentheses and a logical operator in the argument list",
      "properties": {
        "SinceVersion": {
          "type": "string",
          "default": "1.7.0"
        },
        "Severity": {
          "$ref": "#/$defs/Severity",
          "default": "Warning"
        }
      }
    },
    "Lint/SelfInitializeDefinition": {
      "$ref": "#/$defs/BaseRule",
      "$comment": "https://crystal-ameba.github.io/ameba/master/Ameba/Rule/Lint/SelfInitializeDefinition.html",
      "title": "Lint/SelfInitializeDefinition",
      "description": "Reports `initialize` method definitions with a `self` receiver",
      "properties": {
        "SinceVersion": {
          "type": "string",
          "default": "1.7.0"
        },
        "Severity": {
          "$ref": "#/$defs/Severity",
          "default": "Warning"
        }
      }
    },
    "Lint/ShadowedArgument": {
      "$ref": "#/$defs/BaseRule",
      "$comment": "https://crystal-ameba.github.io/ameba/master/Ameba/Rule/Lint/ShadowedArgument.html",
      "title": "Lint/ShadowedArgument",
      "description": "Disallows shadowed arguments",
      "properties": {
        "SinceVersion": {
          "type": "string",
          "default": "0.7.0"
        },
        "Severity": {
          "$ref": "#/$defs/Severity",
          "default": "Warning"
        }
      }
    },
    "Lint/ShadowedException": {
      "$ref": "#/$defs/BaseRule",
      "$comment": "https://crystal-ameba.github.io/ameba/master/Ameba/Rule/Lint/ShadowedException.html",
      "title": "Lint/ShadowedException",
      "description": "Disallows rescued exception that get shadowed",
      "properties": {
        "SinceVersion": {
          "type": "string",
          "default": "0.3.0"
        },
        "Severity": {
          "$ref": "#/$defs/Severity",
          "default": "Warning"
        }
      }
    },
    "Lint/ShadowingOuterLocalVar": {
      "$ref": "#/$defs/BaseRule",
      "$comment": "https://crystal-ameba.github.io/ameba/master/Ameba/Rule/Lint/ShadowingOuterLocalVar.html",
      "title": "Lint/ShadowingOuterLocalVar",
      "description": "Disallows the usage of the same name as outer local variables for block or proc arguments",
      "properties": {
        "SinceVersion": {
          "type": "string",
          "default": "0.7.0"
        },
        "Severity": {
          "$ref": "#/$defs/Severity",
          "default": "Warning"
        }
      }
    },
    "Lint/SharedVarInFiber": {
      "$ref": "#/$defs/BaseRule",
      "$comment": "https://crystal-ameba.github.io/ameba/master/Ameba/Rule/Lint/SharedVarInFiber.html",
      "title": "Lint/SharedVarInFiber",
      "description": "Disallows shared variables in fibers",
      "properties": {
        "SinceVersion": {
          "type": "string",
          "default": "0.12.0"
        },
        "Severity": {
          "$ref": "#/$defs/Severity",
          "default": "Warning"
        }
      }
    },
    "Lint/SignalTrap": {
      "$ref": "#/$defs/BaseRule",
      "$comment": "https://crystal-ameba.github.io/ameba/master/Ameba/Rule/Lint/SignalTrap.html",
      "title": "Lint/SignalTrap",
      "description": "Disallows `Signal::INT/HUP/TERM.trap` in favor of `Process.on_terminate`",
      "properties": {
        "SinceVersion": {
          "type": "string",
          "default": "1.7.0"
        },
        "Severity": {
          "$ref": "#/$defs/Severity",
          "default": "Warning"
        }
      }
    },
    "Lint/SpecEqWithBoolOrNilLiteral": {
      "$ref": "#/$defs/BaseRule",
      "$comment": "https://crystal-ameba.github.io/ameba/master/Ameba/Rule/Lint/SpecEqWithBoolOrNilLiteral.html",
      "title": "Lint/SpecEqWithBoolOrNilLiteral",
      "description": "Reports `eq(true|false|nil)` expectations in specs",
      "properties": {
        "SinceVersion": {
          "type": "string",
          "default": "1.7.0"
        },
        "Severity": {
          "$ref": "#/$defs/Severity",
          "default": "Warning"
        }
      }
    },
    "Lint/SpecFilename": {
      "$ref": "#/$defs/BaseRule",
      "$comment": "https://crystal-ameba.github.io/ameba/master/Ameba/Rule/Lint/SpecFilename.html",
      "title": "Lint/SpecFilename",
      "description": "Enforces spec filenames to have `_spec` suffix",
      "properties": {
        "SinceVersion": {
          "type": "string",
          "default": "1.6.0"
        },
        "IgnoredDirs": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "default": [
            "spec/support",
            "spec/fixtures",
            "spec/data"
          ]
        },
        "IgnoredFilenames": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "default": [
            "spec_helper"
          ]
        },
        "Severity": {
          "$ref": "#/$defs/Severity",
          "default": "Warning"
        }
      }
    },
    "Lint/SpecFocus": {
      "$ref": "#/$defs/BaseRule",
      "$comment": "https://crystal-ameba.github.io/ameba/master/Ameba/Rule/Lint/SpecFocus.html",
      "title": "Lint/SpecFocus",
      "description": "Reports focused spec items",
      "properties": {
        "SinceVersion": {
          "type": "string",
          "default": "0.14.0"
        },
        "Severity": {
          "$ref": "#/$defs/Severity",
          "default": "Warning"
        }
      }
    },
    "Lint/Syntax": {
      "$ref": "#/$defs/BaseRule",
      "$comment": "https://crystal-ameba.github.io/ameba/master/Ameba/Rule/Lint/Syntax.html",
      "title": "Lint/Syntax",
      "description": "Reports invalid Crystal syntax",
      "properties": {
        "SinceVersion": {
          "type": "string",
          "default": "0.4.2"
        },
        "Severity": {
          "$ref": "#/$defs/Severity",
          "default": "Error"
        }
      }
    },
    "Lint/TopLevelOperatorDefinition": {
      "$ref": "#/$defs/BaseRule",
      "$comment": "https://crystal-ameba.github.io/ameba/master/Ameba/Rule/Lint/TopLevelOperatorDefinition.html",
      "title": "Lint/TopLevelOperatorDefinition",
      "description": "Disallows top level operator method definitions",
      "properties": {
        "SinceVersion": {
          "type": "string",
          "default": "1.7.0"
        },
        "Severity": {
          "$ref": "#/$defs/Severity",
          "default": "Warning"
        }
      }
    },
    "Lint/TrailingRescueException": {
      "$ref": "#/$defs/BaseRule",
      "$comment": "https://crystal-ameba.github.io/ameba/master/Ameba/Rule/Lint/TrailingRescueException.html",
      "title": "Lint/TrailingRescueException",
      "description": "Disallows trailing `rescue` with a path",
      "properties": {
        "SinceVersion": {
          "type": "string",
          "default": "1.7.0"
        },
        "Severity": {
          "$ref": "#/$defs/Severity",
          "default": "Warning"
        }
      }
    },
    "Lint/Typos": {
      "$ref": "#/$defs/BaseRule",
      "$comment": "https://crystal-ameba.github.io/ameba/master/Ameba/Rule/Lint/Typos.html",
      "title": "Lint/Typos",
      "description": "Reports typos found in source files",
      "properties": {
        "SinceVersion": {
          "type": "string",
          "default": "1.6.0"
        },
        "Enabled": {
          "type": "boolean",
          "default": false
        },
        "BinPath": {
          "type": [
            "string",
            "null"
          ],
          "default": null
        },
        "FailOnMissingBin": {
          "type": "boolean",
          "default": false
        },
        "FailOnError": {
          "type": "boolean",
          "default": true
        },
        "Severity": {
          "$ref": "#/$defs/Severity",
          "default": "Warning"
        }
      }
    },
    "Lint/UnneededDisableDirective": {
      "$ref": "#/$defs/BaseRule",
      "$comment": "https://crystal-ameba.github.io/ameba/master/Ameba/Rule/Lint/UnneededDisableDirective.html",
      "title": "Lint/UnneededDisableDirective",
      "description": "Reports unneeded disable directives in comments",
      "properties": {
        "SinceVersion": {
          "type": "string",
          "default": "0.5.0"
        },
        "Severity": {
          "$ref": "#/$defs/Severity",
          "default": "Warning"
        }
      }
    },
    "Lint/UnreachableCode": {
      "$ref": "#/$defs/BaseRule",
      "$comment": "https://crystal-ameba.github.io/ameba/master/Ameba/Rule/Lint/UnreachableCode.html",
      "title": "Lint/UnreachableCode",
      "description": "Reports unreachable code",
      "properties": {
        "SinceVersion": {
          "type": "string",
          "default": "0.9.0"
        },
        "Severity": {
          "$ref": "#/$defs/Severity",
          "default": "Warning"
        }
      }
    },
    "Lint/UnusedArgument": {
      "$ref": "#/$defs/BaseRule",
      "$comment": "https://crystal-ameba.github.io/ameba/master/Ameba/Rule/Lint/UnusedArgument.html",
      "title": "Lint/UnusedArgument",
      "description": "Disallows unused arguments",
      "properties": {
        "SinceVersion": {
          "type": "string",
          "default": "0.6.0"
        },
        "IgnoreDefs": {
          "type": "boolean",
          "default": true
        },
        "IgnoreBlocks": {
          "type": "boolean",
          "default": false
        },
        "IgnoreProcs": {
          "type": "boolean",
          "default": false
        },
        "Severity": {
          "$ref": "#/$defs/Severity",
          "default": "Warning"
        }
      }
    },
    "Lint/UnusedBlockArgument": {
      "$ref": "#/$defs/BaseRule",
      "$comment": "https://crystal-ameba.github.io/ameba/master/Ameba/Rule/Lint/UnusedBlockArgument.html",
      "title": "Lint/UnusedBlockArgument",
      "description": "Disallows unused block arguments",
      "properties": {
        "SinceVersion": {
          "type": "string",
          "default": "1.4.0"
        },
        "Severity": {
          "$ref": "#/$defs/Severity",
          "default": "Warning"
        }
      }
    },
    "Lint/UnusedExpression": {
      "$ref": "#/$defs/BaseRule",
      "$comment": "https://crystal-ameba.github.io/ameba/master/Ameba/Rule/Lint/UnusedExpression.html",
      "title": "Lint/UnusedExpression",
      "description": "Disallows unused expressions",
      "properties": {
        "SinceVersion": {
          "type": "string",
          "default": "1.7.0"
        },
        "Severity": {
          "$ref": "#/$defs/Severity",
          "default": "Warning"
        }
      }
    },
    "Lint/UnusedRescueVariable": {
      "$ref": "#/$defs/BaseRule",
      "$comment": "https://crystal-ameba.github.io/ameba/master/Ameba/Rule/Lint/UnusedRescueVariable.html",
      "title": "Lint/UnusedRescueVariable",
      "description": "Disallows unused `rescue` variables",
      "properties": {
        "SinceVersion": {
          "type": "string",
          "default": "1.7.0"
        },
        "Severity": {
          "$ref": "#/$defs/Severity",
          "default": "Warning"
        }
      }
    },
    "Lint/UselessAssign": {
      "$ref": "#/$defs/BaseRule",
      "$comment": "https://crystal-ameba.github.io/ameba/master/Ameba/Rule/Lint/UselessAssign.html",
      "title": "Lint/UselessAssign",
      "description": "Disallows useless variable assignments",
      "properties": {
        "SinceVersion": {
          "type": "string",
          "default": "0.6.0"
        },
        "Severity": {
          "$ref": "#/$defs/Severity",
          "default": "Warning"
        }
      }
    },
    "Lint/UselessConditionInWhen": {
      "$ref": "#/$defs/BaseRule",
      "$comment": "https://crystal-ameba.github.io/ameba/master/Ameba/Rule/Lint/UselessConditionInWhen.html",
      "title": "Lint/UselessConditionInWhen",
      "description": "Disallows useless conditions in `when`",
      "properties": {
        "SinceVersion": {
          "type": "string",
          "default": "0.3.0"
        },
        "Severity": {
          "$ref": "#/$defs/Severity",
          "default": "Warning"
        }
      }
    },
    "Lint/UselessVisibilityModifier": {
      "$ref": "#/$defs/BaseRule",
      "$comment": "https://crystal-ameba.github.io/ameba/master/Ameba/Rule/Lint/UselessVisibilityModifier.html",
      "title": "Lint/UselessVisibilityModifier",
      "description": "Disallows top level `protected` method visibility modifier",
      "properties": {
        "SinceVersion": {
          "type": "string",
          "default": "1.7.0"
        },
        "Severity": {
          "$ref": "#/$defs/Severity",
          "default": "Warning"
        }
      }
    },
    "Lint/VoidOutsideLib": {
      "$ref": "#/$defs/BaseRule",
      "$comment": "https://crystal-ameba.github.io/ameba/master/Ameba/Rule/Lint/VoidOutsideLib.html",
      "title": "Lint/VoidOutsideLib",
      "description": "Disallows use of `Void` outside C lib bindings and `Pointer(Void)`",
      "properties": {
        "SinceVersion": {
          "type": "string",
          "default": "1.7.0"
        },
        "Severity": {
          "$ref": "#/$defs/Severity",
          "default": "Warning"
        }
      }
    },
    "Lint/WhitespaceAroundMacroExpression": {
      "$ref": "#/$defs/BaseRule",
      "$comment": "https://crystal-ameba.github.io/ameba/master/Ameba/Rule/Lint/WhitespaceAroundMacroExpression.html",
      "title": "Lint/WhitespaceAroundMacroExpression",
      "description": "Reports missing spaces around macro expressions",
      "properties": {
        "SinceVersion": {
          "type": "string",
          "default": "1.7.0"
        },
        "Severity": {
          "$ref": "#/$defs/Severity",
          "default": "Warning"
        }
      }
    },
    "Metrics/CyclomaticComplexity": {
      "$ref": "#/$defs/BaseRule",
      "$comment": "https://crystal-ameba.github.io/ameba/master/Ameba/Rule/Metrics/CyclomaticComplexity.html",
      "title": "Metrics/CyclomaticComplexity",
      "description": "Disallows methods with a cyclomatic complexity higher than `MaxComplexity`",
      "properties": {
        "SinceVersion": {
          "type": "string",
          "default": "0.9.1"
        },
        "MaxComplexity": {
          "type": "number",
          "default": 12
        },
        "Severity": {
          "$ref": "#/$defs/Severity",
          "default": "Warning"
        }
      }
    },
    "Naming/AccessorMethodName": {
      "$ref": "#/$defs/BaseRule",
      "$comment": "https://crystal-ameba.github.io/ameba/master/Ameba/Rule/Naming/AccessorMethodName.html",
      "title": "Naming/AccessorMethodName",
      "description": "Makes sure that accessor methods are named properly",
      "properties": {
        "SinceVersion": {
          "type": "string",
          "default": "1.6.0"
        }
      }
    },
    "Naming/AsciiIdentifiers": {
      "$ref": "#/$defs/BaseRule",
      "$comment": "https://crystal-ameba.github.io/ameba/master/Ameba/Rule/Naming/AsciiIdentifiers.html",
      "title": "Naming/AsciiIdentifiers",
      "description": "Disallows non-ascii characters in identifiers",
      "properties": {
        "SinceVersion": {
          "type": "string",
          "default": "1.6.0"
        },
        "IgnoreSymbols": {
          "type": "boolean",
          "default": false
        }
      }
    },
    "Naming/BinaryOperatorParameterName": {
      "$ref": "#/$defs/BaseRule",
      "$comment": "https://crystal-ameba.github.io/ameba/master/Ameba/Rule/Naming/BinaryOperatorParameterName.html",
      "title": "Naming/BinaryOperatorParameterName",
      "description": "Enforces that certain binary operator methods have their sole parameter name standardized",
      "properties": {
        "SinceVersion": {
          "type": "string",
          "default": "1.6.0"
        },
        "ExcludedOperators": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "default": [
            "[]",
            "[]?",
            "[]=",
            "<<",
            ">>",
            "`",
            "=~",
            "!~"
          ]
        },
        "AllowedNames": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "default": [
            "other"
          ]
        }
      }
    },
    "Naming/BlockParameterName": {
      "$ref": "#/$defs/BaseRule",
      "$comment": "https://crystal-ameba.github.io/ameba/master/Ameba/Rule/Naming/BlockParameterName.html",
      "title": "Naming/BlockParameterName",
      "description": "Disallows non-descriptive block parameter names",
      "properties": {
        "SinceVersion": {
          "type": "string",
          "default": "1.6.0"
        },
        "MinNameLength": {
          "type": "number",
          "default": 3
        },
        "AllowNamesEndingInNumbers": {
          "type": "boolean",
          "default": true
        },
        "AllowedNames": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "default": [
            "a",
            "b",
            "e",
            "i",
            "j",
            "k",
            "v",
            "x",
            "y",
            "k1",
            "k2",
            "v1",
            "v2",
            "db",
            "ex",
            "id",
            "io",
            "ip",
            "op",
            "tx",
            "wg",
            "ws"
          ]
        },
        "ForbiddenNames": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "default": []
        }
      }
    },
    "Naming/ConstantNames": {
      "$ref": "#/$defs/BaseRule",
      "$comment": "https://crystal-ameba.github.io/ameba/master/Ameba/Rule/Naming/ConstantNames.html",
      "title": "Naming/ConstantNames",
      "description": "Enforces constant names to be in screaming case",
      "properties": {
        "SinceVersion": {
          "type": "string",
          "default": "0.2.0"
        }
      }
    },
    "Naming/Filename": {
      "$ref": "#/$defs/BaseRule",
      "$comment": "https://crystal-ameba.github.io/ameba/master/Ameba/Rule/Naming/Filename.html",
      "title": "Naming/Filename",
      "description": "Enforces file names to be in underscored case",
      "properties": {
        "SinceVersion": {
          "type": "string",
          "default": "1.6.0"
        }
      }
    },
    "Naming/MethodNames": {
      "$ref": "#/$defs/BaseRule",
      "$comment": "https://crystal-ameba.github.io/ameba/master/Ameba/Rule/Naming/MethodNames.html",
      "title": "Naming/MethodNames",
      "description": "Enforces method names to be in underscored case",
      "properties": {
        "SinceVersion": {
          "type": "string",
          "default": "0.2.0"
        }
      }
    },
    "Naming/PredicateName": {
      "$ref": "#/$defs/BaseRule",
      "$comment": "https://crystal-ameba.github.io/ameba/master/Ameba/Rule/Naming/PredicateName.html",
      "title": "Naming/PredicateName",
      "description": "Disallows tautological predicate names",
      "properties": {
        "SinceVersion": {
          "type": "string",
          "default": "0.2.0"
        }
      }
    },
    "Naming/QueryBoolMethods": {
      "$ref": "#/$defs/BaseRule",
      "$comment": "https://crystal-ameba.github.io/ameba/master/Ameba/Rule/Naming/QueryBoolMethods.html",
      "title": "Naming/QueryBoolMethods",
      "description": "Reports boolean properties without the `?` suffix",
      "properties": {
        "SinceVersion": {
          "type": "string",
          "default": "1.4.0"
        }
      }
    },
    "Naming/RescuedExceptionsVariableName": {
      "$ref": "#/$defs/BaseRule",
      "$comment": "https://crystal-ameba.github.io/ameba/master/Ameba/Rule/Naming/RescuedExceptionsVariableName.html",
      "title": "Naming/RescuedExceptionsVariableName",
      "description": "Makes sure that rescued exceptions variables are named as expected",
      "properties": {
        "SinceVersion": {
          "type": "string",
          "default": "1.6.0"
        },
        "AllowedNames": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "default": [
            "e",
            "ex",
            "exception",
            "err",
            "error"
          ]
        }
      }
    },
    "Naming/TypeNames": {
      "$ref": "#/$defs/BaseRule",
      "$comment": "https://crystal-ameba.github.io/ameba/master/Ameba/Rule/Naming/TypeNames.html",
      "title": "Naming/TypeNames",
      "description": "Enforces type names in camelcase manner",
      "properties": {
        "SinceVersion": {
          "type": "string",
          "default": "0.2.0"
        }
      }
    },
    "Naming/VariableNames": {
      "$ref": "#/$defs/BaseRule",
      "$comment": "https://crystal-ameba.github.io/ameba/master/Ameba/Rule/Naming/VariableNames.html",
      "title": "Naming/VariableNames",
      "description": "Enforces variable names to be in underscored case",
      "properties": {
        "SinceVersion": {
          "type": "string",
          "default": "0.2.0"
        }
      }
    },
    "Performance/AnyAfterFilter": {
      "$ref": "#/$defs/BaseRule",
      "$comment": "https://crystal-ameba.github.io/ameba/master/Ameba/Rule/Performance/AnyAfterFilter.html",
      "title": "Performance/AnyAfterFilter",
      "description": "Identifies usage of `any?` calls that follow filters",
      "properties": {
        "SinceVersion": {
          "type": "string",
          "default": "0.8.1"
        },
        "FilterNames": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "default": [
            "select",
            "reject"
          ]
        },
        "Severity": {
          "$ref": "#/$defs/Severity",
          "default": "Warning"
        }
      }
    },
    "Performance/AnyInsteadOfPresent": {
      "$ref": "#/$defs/BaseRule",
      "$comment": "https://crystal-ameba.github.io/ameba/master/Ameba/Rule/Performance/AnyInsteadOfPresent.html",
      "title": "Performance/AnyInsteadOfPresent",
      "description": "Identifies usage of arg-less `any?` calls",
      "properties": {
        "SinceVersion": {
          "type": "string",
          "default": "1.7.0"
        },
        "Severity": {
          "$ref": "#/$defs/Severity",
          "default": "Warning"
        }
      }
    },
    "Performance/ChainedCallWithNoBang": {
      "$ref": "#/$defs/BaseRule",
      "$comment": "https://crystal-ameba.github.io/ameba/master/Ameba/Rule/Performance/ChainedCallWithNoBang.html",
      "title": "Performance/ChainedCallWithNoBang",
      "description": "Identifies usage of chained calls not utilizing the bang method variants",
      "properties": {
        "SinceVersion": {
          "type": "string",
          "default": "0.14.0"
        },
        "CallNames": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "default": [
            "uniq",
            "unstable_sort",
            "sort",
            "sort_by",
            "shuffle",
            "reverse"
          ]
        },
        "Severity": {
          "$ref": "#/$defs/Severity",
          "default": "Warning"
        }
      }
    },
    "Performance/CompactAfterMap": {
      "$ref": "#/$defs/BaseRule",
      "$comment": "https://crystal-ameba.github.io/ameba/master/Ameba/Rule/Performance/CompactAfterMap.html",
      "title": "Performance/CompactAfterMap",
      "description": "Identifies usage of `compact` calls that follow `map`",
      "properties": {
        "SinceVersion": {
          "type": "string",
          "default": "0.14.0"
        },
        "Severity": {
          "$ref": "#/$defs/Severity",
          "default": "Warning"
        }
      }
    },
    "Performance/ExcessiveAllocations": {
      "$ref": "#/$defs/BaseRule",
      "$comment": "https://crystal-ameba.github.io/ameba/master/Ameba/Rule/Performance/ExcessiveAllocations.html",
      "title": "Performance/ExcessiveAllocations",
      "description": "Identifies usage of excessive collection allocations",
      "properties": {
        "SinceVersion": {
          "type": "string",
          "default": "1.5.0"
        },
        "CallNames": {
          "type": "object",
          "properties": {
            "codepoints": {
              "type": "string",
              "default": "each_codepoint"
            },
            "graphemes": {
              "type": "string",
              "default": "each_grapheme"
            },
            "chars": {
              "type": "string",
              "default": "each_char"
            },
            "lines": {
              "type": "string",
              "default": "each_line"
            }
          }
        },
        "Severity": {
          "$ref": "#/$defs/Severity",
          "default": "Warning"
        }
      }
    },
    "Performance/FirstLastAfterFilter": {
      "$ref": "#/$defs/BaseRule",
      "$comment": "https://crystal-ameba.github.io/ameba/master/Ameba/Rule/Performance/FirstLastAfterFilter.html",
      "title": "Performance/FirstLastAfterFilter",
      "description": "Identifies usage of `first/last/first?/last?` calls that follow filters",
      "properties": {
        "SinceVersion": {
          "type": "string",
          "default": "0.8.1"
        },
        "FilterNames": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "default": [
            "select"
          ]
        },
        "Severity": {
          "$ref": "#/$defs/Severity",
          "default": "Warning"
        }
      }
    },
    "Performance/FlattenAfterMap": {
      "$ref": "#/$defs/BaseRule",
      "$comment": "https://crystal-ameba.github.io/ameba/master/Ameba/Rule/Performance/FlattenAfterMap.html",
      "title": "Performance/FlattenAfterMap",
      "description": "Identifies usage of `flatten` calls that follow `map`",
      "properties": {
        "SinceVersion": {
          "type": "string",
          "default": "0.14.0"
        },
        "Severity": {
          "$ref": "#/$defs/Severity",
          "default": "Warning"
        }
      }
    },
    "Performance/MapInsteadOfBlock": {
      "$ref": "#/$defs/BaseRule",
      "$comment": "https://crystal-ameba.github.io/ameba/master/Ameba/Rule/Performance/MapInsteadOfBlock.html",
      "title": "Performance/MapInsteadOfBlock",
      "description": "Identifies usage of `sum/product` calls that follow `map`",
      "properties": {
        "SinceVersion": {
          "type": "string",
          "default": "0.14.0"
        },
        "Severity": {
          "$ref": "#/$defs/Severity",
          "default": "Warning"
        }
      }
    },
    "Performance/MinMaxAfterMap": {
      "$ref": "#/$defs/BaseRule",
      "$comment": "https://crystal-ameba.github.io/ameba/master/Ameba/Rule/Performance/MinMaxAfterMap.html",
      "title": "Performance/MinMaxAfterMap",
      "description": "Identifies usage of `min/max/minmax` calls that follow `map`",
      "properties": {
        "SinceVersion": {
          "type": "string",
          "default": "1.5.0"
        },
        "Severity": {
          "$ref": "#/$defs/Severity",
          "default": "Warning"
        }
      }
    },
    "Performance/SizeAfterFilter": {
      "$ref": "#/$defs/BaseRule",
      "$comment": "https://crystal-ameba.github.io/ameba/master/Ameba/Rule/Performance/SizeAfterFilter.html",
      "title": "Performance/SizeAfterFilter",
      "description": "Identifies usage of `size` calls that follow filter",
      "properties": {
        "SinceVersion": {
          "type": "string",
          "default": "0.8.1"
        },
        "FilterNames": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "default": [
            "select",
            "reject"
          ]
        },
        "Severity": {
          "$ref": "#/$defs/Severity",
          "default": "Warning"
        }
      }
    },
    "Performance/TimesMap": {
      "$ref": "#/$defs/BaseRule",
      "$comment": "https://crystal-ameba.github.io/ameba/master/Ameba/Rule/Performance/TimesMap.html",
      "title": "Performance/TimesMap",
      "description": "Identifies usage of `times.map { ... }.to_a` calls",
      "properties": {
        "SinceVersion": {
          "type": "string",
          "default": "1.7.0"
        },
        "Severity": {
          "$ref": "#/$defs/Severity",
          "default": "Warning"
        }
      }
    },
    "Style/ArrayLiteralSyntax": {
      "$ref": "#/$defs/BaseRule",
      "$comment": "https://crystal-ameba.github.io/ameba/master/Ameba/Rule/Style/ArrayLiteralSyntax.html",
      "title": "Style/ArrayLiteralSyntax",
      "description": "Encourages the use of `Array(T).new` over `[] of T`",
      "properties": {
        "SinceVersion": {
          "type": "string",
          "default": "1.7.0"
        },
        "Enabled": {
          "type": "boolean",
          "default": false
        }
      }
    },
    "Style/CallParentheses": {
      "$ref": "#/$defs/BaseRule",
      "$comment": "https://crystal-ameba.github.io/ameba/master/Ameba/Rule/Style/CallParentheses.html",
      "title": "Style/CallParentheses",
      "description": "Enforces usage of parentheses in method calls",
      "properties": {
        "SinceVersion": {
          "type": "string",
          "default": "1.7.0"
        },
        "Enabled": {
          "type": "boolean",
          "default": false
        },
        "ExcludeTypeDeclarations": {
          "type": "boolean",
          "default": true
        },
        "ExcludeHeredocs": {
          "type": "boolean",
          "default": false
        },
        "ExcludedToplevelCallNames": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "default": [
            "spawn",
            "raise",
            "super",
            "previous_def",
            "exit",
            "abort",
            "sleep",
            "print",
            "printf",
            "puts",
            "p",
            "p!",
            "pp",
            "pp!",
            "record",
            "class_getter",
            "class_getter?",
            "class_getter!",
            "class_property",
            "class_property?",
            "class_property!",
            "class_setter",
            "getter",
            "getter?",
            "getter!",
            "property",
            "property?",
            "property!",
            "setter",
            "def_equals_and_hash",
            "def_equals",
            "def_hash",
            "delegate",
            "forward_missing_to",
            "describe",
            "context",
            "it",
            "pending",
            "fail",
            "use_json_discriminator"
          ]
        },
        "ExcludedCallNames": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "default": [
            "should",
            "should_not"
          ]
        }
      }
    },
    "Style/Elsif": {
      "$ref": "#/$defs/BaseRule",
      "$comment": "https://crystal-ameba.github.io/ameba/master/Ameba/Rule/Style/Elsif.html",
      "title": "Style/Elsif",
      "description": "Encourages the use of `case/when` syntax over `if/elsif`",
      "properties": {
        "SinceVersion": {
          "type": "string",
          "default": "1.7.0"
        },
        "Enabled": {
          "type": "boolean",
          "default": false
        },
        "IgnoreSuffix": {
          "type": "boolean",
          "default": true
        },
        "MaxBranches": {
          "type": "number",
          "default": 0
        }
      }
    },
    "Style/GuardClause": {
      "$ref": "#/$defs/BaseRule",
      "$comment": "https://crystal-ameba.github.io/ameba/master/Ameba/Rule/Style/GuardClause.html",
      "title": "Style/GuardClause",
      "description": "Check for conditionals that can be replaced with guard clauses",
      "properties": {
        "SinceVersion": {
          "type": "string",
          "default": "1.0.0"
        },
        "Enabled": {
          "type": "boolean",
          "default": false
        }
      }
    },
    "Style/HashLiteralSyntax": {
      "$ref": "#/$defs/BaseRule",
      "$comment": "https://crystal-ameba.github.io/ameba/master/Ameba/Rule/Style/HashLiteralSyntax.html",
      "title": "Style/HashLiteralSyntax",
      "description": "Encourages the use of `Hash(K, V).new` over `{} of K => V`",
      "properties": {
        "SinceVersion": {
          "type": "string",
          "default": "1.7.0"
        },
        "Enabled": {
          "type": "boolean",
          "default": false
        }
      }
    },
    "Style/HeredocEscape": {
      "$ref": "#/$defs/BaseRule",
      "$comment": "https://crystal-ameba.github.io/ameba/master/Ameba/Rule/Style/HeredocEscape.html",
      "title": "Style/HeredocEscape",
      "description": "Recommends using the heredoc variant that escapes interpolation or control chars in a heredoc body",
      "properties": {
        "SinceVersion": {
          "type": "string",
          "default": "1.7.0"
        }
      }
    },
    "Style/HeredocIndent": {
      "$ref": "#/$defs/BaseRule",
      "$comment": "https://crystal-ameba.github.io/ameba/master/Ameba/Rule/Style/HeredocIndent.html",
      "title": "Style/HeredocIndent",
      "description": "Recommends heredoc bodies are indented consistently",
      "properties": {
        "SinceVersion": {
          "type": "string",
          "default": "1.7.0"
        },
        "IndentBy": {
          "type": "number",
          "default": 2
        },
        "BodyAutoDedent": {
          "type": "boolean",
          "default": true
        }
      }
    },
    "Style/IsAFilter": {
      "$ref": "#/$defs/BaseRule",
      "$comment": "https://crystal-ameba.github.io/ameba/master/Ameba/Rule/Style/IsAFilter.html",
      "title": "Style/IsAFilter",
      "description": "Identifies usage of `is_a?/nil?` calls within filters",
      "properties": {
        "SinceVersion": {
          "type": "string",
          "default": "0.14.0"
        },
        "FilterNames": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "default": [
            "select",
            "reject",
            "any?",
            "all?",
            "none?",
            "one?"
          ]
        }
      }
    },
    "Style/IsANil": {
      "$ref": "#/$defs/BaseRule",
      "$comment": "https://crystal-ameba.github.io/ameba/master/Ameba/Rule/Style/IsANil.html",
      "title": "Style/IsANil",
      "description": "Disallows calls to `is_a?(Nil)` in favor of `nil?`",
      "properties": {
        "SinceVersion": {
          "type": "string",
          "default": "0.13.0"
        }
      }
    },
    "Style/LargeNumbers": {
      "$ref": "#/$defs/BaseRule",
      "$comment": "https://crystal-ameba.github.io/ameba/master/Ameba/Rule/Style/LargeNumbers.html",
      "title": "Style/LargeNumbers",
      "description": "Disallows usage of large numbers without underscore",
      "properties": {
        "SinceVersion": {
          "type": "string",
          "default": "0.2.0"
        },
        "Enabled": {
          "type": "boolean",
          "default": false
        },
        "IntMinDigits": {
          "type": "number",
          "default": 6
        }
      }
    },
    "Style/MultilineCurlyBlock": {
      "$ref": "#/$defs/BaseRule",
      "$comment": "https://crystal-ameba.github.io/ameba/master/Ameba/Rule/Style/MultilineCurlyBlock.html",
      "title": "Style/MultilineCurlyBlock",
      "description": "Disallows multi-line blocks using curly block syntax",
      "properties": {
        "SinceVersion": {
          "type": "string",
          "default": "1.7.0"
        }
      }
    },
    "Style/MultilineStringLiteral": {
      "$ref": "#/$defs/BaseRule",
      "$comment": "https://crystal-ameba.github.io/ameba/master/Ameba/Rule/Style/MultilineStringLiteral.html",
      "title": "Style/MultilineStringLiteral",
      "description": "Disallows multiline string literals not using `<<-HEREDOC` markers",
      "properties": {
        "SinceVersion": {
          "type": "string",
          "default": "1.7.0"
        },
        "AllowBackslashSplitStrings": {
          "type": "boolean",
          "default": true
        }
      }
    },
    "Style/NegatedConditionsInUnless": {
      "$ref": "#/$defs/BaseRule",
      "$comment": "https://crystal-ameba.github.io/ameba/master/Ameba/Rule/Style/NegatedConditionsInUnless.html",
      "title": "Style/NegatedConditionsInUnless",
      "description": "Disallows negated conditions in `unless`",
      "properties": {
        "SinceVersion": {
          "type": "string",
          "default": "0.2.0"
        }
      }
    },
    "Style/ParenthesesAroundCondition": {
      "$ref": "#/$defs/BaseRule",
      "$comment": "https://crystal-ameba.github.io/ameba/master/Ameba/Rule/Style/ParenthesesAroundCondition.html",
      "title": "Style/ParenthesesAroundCondition",
      "description": "Disallows redundant parentheses around control expressions",
      "properties": {
        "SinceVersion": {
          "type": "string",
          "default": "1.4.0"
        },
        "ExcludeTernary": {
          "type": "boolean",
          "default": false
        },
        "ExcludeMultiline": {
          "type": "boolean",
          "default": false
        },
        "AllowSafeAssignment": {
          "type": "boolean",
          "default": false
        }
      }
    },
    "Style/PercentLiteralDelimiters": {
      "$ref": "#/$defs/BaseRule",
      "$comment": "https://crystal-ameba.github.io/ameba/master/Ameba/Rule/Style/PercentLiteralDelimiters.html",
      "title": "Style/PercentLiteralDelimiters",
      "description": "Enforces the consistent usage of `%`-literal delimiters",
      "properties": {
        "SinceVersion": {
          "type": "string",
          "default": "1.7.0"
        },
        "DefaultDelimiters": {
          "type": [
            "string",
            "null"
          ],
          "default": "()"
        },
        "PreferredDelimiters": {
          "type": "object",
          "properties": {
            "%w": {
              "type": "string",
              "default": "[]"
            },
            "%i": {
              "type": "string",
              "default": "[]"
            },
            "%r": {
              "type": "string",
              "default": "{}"
            }
          }
        },
        "IgnoreLiteralsContainingDelimiters": {
          "type": "boolean",
          "default": false
        }
      }
    },
    "Style/RedundantBegin": {
      "$ref": "#/$defs/BaseRule",
      "$comment": "https://crystal-ameba.github.io/ameba/master/Ameba/Rule/Style/RedundantBegin.html",
      "title": "Style/RedundantBegin",
      "description": "Disallows redundant `begin` blocks",
      "properties": {
        "SinceVersion": {
          "type": "string",
          "default": "0.3.0"
        }
      }
    },
    "Style/RedundantNext": {
      "$ref": "#/$defs/BaseRule",
      "$comment": "https://crystal-ameba.github.io/ameba/master/Ameba/Rule/Style/RedundantNext.html",
      "title": "Style/RedundantNext",
      "description": "Reports redundant `next` expressions",
      "properties": {
        "SinceVersion": {
          "type": "string",
          "default": "0.12.0"
        },
        "AllowMultiNext": {
          "type": "boolean",
          "default": true
        },
        "AllowEmptyNext": {
          "type": "boolean",
          "default": true
        }
      }
    },
    "Style/RedundantNilInControlExpression": {
      "$ref": "#/$defs/BaseRule",
      "$comment": "https://crystal-ameba.github.io/ameba/master/Ameba/Rule/Style/RedundantNilInControlExpression.html",
      "title": "Style/RedundantNilInControlExpression",
      "description": "Disallows control expressions with `nil` argument",
      "properties": {
        "SinceVersion": {
          "type": "string",
          "default": "1.7.0"
        }
      }
    },
    "Style/RedundantReturn": {
      "$ref": "#/$defs/BaseRule",
      "$comment": "https://crystal-ameba.github.io/ameba/master/Ameba/Rule/Style/RedundantReturn.html",
      "title": "Style/RedundantReturn",
      "description": "Reports redundant `return` expressions",
      "properties": {
        "SinceVersion": {
          "type": "string",
          "default": "0.9.0"
        },
        "AllowMultiReturn": {
          "type": "boolean",
          "default": true
        },
        "AllowEmptyReturn": {
          "type": "boolean",
          "default": true
        }
      }
    },
    "Style/RedundantSelf": {
      "$ref": "#/$defs/BaseRule",
      "$comment": "https://crystal-ameba.github.io/ameba/master/Ameba/Rule/Style/RedundantSelf.html",
      "title": "Style/RedundantSelf",
      "description": "Disallows redundant uses of `self`",
      "properties": {
        "SinceVersion": {
          "type": "string",
          "default": "1.7.0"
        },
        "AllowedMethodNames": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "default": [
            "in?",
            "inspect",
            "not_nil!"
          ]
        }
      }
    },
    "Style/UnlessElse": {
      "$ref": "#/$defs/BaseRule",
      "$comment": "https://crystal-ameba.github.io/ameba/master/Ameba/Rule/Style/UnlessElse.html",
      "title": "Style/UnlessElse",
      "description": "Disallows the use of an `else` block with the `unless`",
      "properties": {
        "SinceVersion": {
          "type": "string",
          "default": "0.1.0"
        }
      }
    },
    "Style/VerboseBlock": {
      "$ref": "#/$defs/BaseRule",
      "$comment": "https://crystal-ameba.github.io/ameba/master/Ameba/Rule/Style/VerboseBlock.html",
      "title": "Style/VerboseBlock",
      "description": "Identifies usage of collapsible single expression blocks",
      "properties": {
        "SinceVersion": {
          "type": "string",
          "default": "0.14.0"
        },
        "ExcludeMultipleLineBlocks": {
          "type": "boolean",
          "default": true
        },
        "ExcludeCallsWithBlock": {
          "type": "boolean",
          "default": true
        },
        "ExcludePrefixOperators": {
          "type": "boolean",
          "default": true
        },
        "ExcludeOperators": {
          "type": "boolean",
          "default": true
        },
        "ExcludeSetters": {
          "type": "boolean",
          "default": false
        },
        "MaxLineLength": {
          "type": [
            "number",
            "null"
          ],
          "default": null
        },
        "MaxLength": {
          "type": [
            "number",
            "null"
          ],
          "default": 50
        }
      }
    },
    "Style/VerboseNilType": {
      "$ref": "#/$defs/BaseRule",
      "$comment": "https://crystal-ameba.github.io/ameba/master/Ameba/Rule/Style/VerboseNilType.html",
      "title": "Style/VerboseNilType",
      "description": "Enforces consistent naming of `Nil` in type unions",
      "properties": {
        "SinceVersion": {
          "type": "string",
          "default": "1.7.0"
        },
        "ExplicitNil": {
          "type": "boolean",
          "default": false
        }
      }
    },
    "Style/WhileTrue": {
      "$ref": "#/$defs/BaseRule",
      "$comment": "https://crystal-ameba.github.io/ameba/master/Ameba/Rule/Style/WhileTrue.html",
      "title": "Style/WhileTrue",
      "description": "Disallows `while` statements with a `true` literal as condition",
      "properties": {
        "SinceVersion": {
          "type": "string",
          "default": "0.3.0"
        }
      }
    },
    "Typing/MacroCallArgumentTypeRestriction": {
      "$ref": "#/$defs/BaseRule",
      "$comment": "https://crystal-ameba.github.io/ameba/master/Ameba/Rule/Typing/MacroCallArgumentTypeRestriction.html",
      "title": "Typing/MacroCallArgumentTypeRestriction",
      "description": "Recommends that call arguments to certain macros have type restrictions",
      "properties": {
        "SinceVersion": {
          "type": "string",
          "default": "1.7.0"
        },
        "Enabled": {
          "type": "boolean",
          "default": false
        },
        "DefaultValue": {
          "type": "boolean",
          "default": false
        },
        "MacroNames": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "default": [
            "getter",
            "getter?",
            "getter!",
            "class_getter",
            "class_getter?",
            "class_getter!",
            "setter",
            "setter?",
            "setter!",
            "class_setter",
            "class_setter?",
            "class_setter!",
            "property",
            "property?",
            "property!",
            "class_property",
            "class_property?",
            "class_property!",
            "record"
          ]
        }
      }
    },
    "Typing/MethodParameterTypeRestriction": {
      "$ref": "#/$defs/BaseRule",
      "$comment": "https://crystal-ameba.github.io/ameba/master/Ameba/Rule/Typing/MethodParameterTypeRestriction.html",
      "title": "Typing/MethodParameterTypeRestriction",
      "description": "Recommends that method parameters have type restrictions",
      "properties": {
        "SinceVersion": {
          "type": "string",
          "default": "1.7.0"
        },
        "Enabled": {
          "type": "boolean",
          "default": false
        },
        "DefaultValue": {
          "type": "boolean",
          "default": false
        },
        "BlockParameters": {
          "type": "boolean",
          "default": false
        },
        "PrivateMethods": {
          "type": "boolean",
          "default": false
        },
        "ProtectedMethods": {
          "type": "boolean",
          "default": false
        },
        "NodocMethods": {
          "type": "boolean",
          "default": false
        }
      }
    },
    "Typing/MethodReturnTypeRestriction": {
      "$ref": "#/$defs/BaseRule",
      "$comment": "https://crystal-ameba.github.io/ameba/master/Ameba/Rule/Typing/MethodReturnTypeRestriction.html",
      "title": "Typing/MethodReturnTypeRestriction",
      "description": "Recommends that methods have a return type restriction",
      "properties": {
        "SinceVersion": {
          "type": "string",
          "default": "1.7.0"
        },
        "Enabled": {
          "type": "boolean",
          "default": false
        },
        "PrivateMethods": {
          "type": "boolean",
          "default": false
        },
        "ProtectedMethods": {
          "type": "boolean",
          "default": false
        },
        "NodocMethods": {
          "type": "boolean",
          "default": false
        }
      }
    },
    "Typing/ProcLiteralReturnTypeRestriction": {
      "$ref": "#/$defs/BaseRule",
      "$comment": "https://crystal-ameba.github.io/ameba/master/Ameba/Rule/Typing/ProcLiteralReturnTypeRestriction.html",
      "title": "Typing/ProcLiteralReturnTypeRestriction",
      "description": "Disallows proc literals without return type restriction",
      "properties": {
        "SinceVersion": {
          "type": "string",
          "default": "1.7.0"
        },
        "Enabled": {
          "type": "boolean",
          "default": false
        }
      }
    }
  }
}

================================================
FILE: .dockerignore
================================================
.*
!LICENSE
!Dockerfile
!Makefile
!shard.yml
!src


================================================
FILE: .editorconfig
================================================
[*.cr]
charset = utf-8
end_of_line = lf
insert_final_newline = true
indent_style = space
indent_size = 2
trim_trailing_whitespace = true


================================================
FILE: .gitattributes
================================================
* text=auto eol=lf

## Generated files

.ameba.yml.schema.json linguist-generated


================================================
FILE: .github/dependabot.yml
================================================
version: 2
updates:
  # Maintain dependencies for GitHub Actions
  - package-ecosystem: github-actions
    directory: /
    schedule:
      interval: daily


================================================
FILE: .github/workflows/add-docs-version-to-json-index.yml
================================================
name: Add docs version to JSON index

on:
  workflow_call:
    inputs:
      version:
        required: true
        type: string
      git-branch:
        type: string
        default: gh-pages

permissions:
  contents: write

jobs:
  add-version-to-json-index:
    concurrency: ci-${{ github.ref }}
    runs-on: ubuntu-latest

    steps:
      - name: Download source
        uses: actions/checkout@v6
        with:
          ref: ${{ inputs.git-branch }}

      - name: Update version list
        run: |
          VERSION="${{ inputs.version }}"

          if [[ $VERSION =~ ^[0-9] ]]; then
            RELEASED="true"
          else
            RELEASED="false"
          fi

          JQ_EXPRESSION="
            .versions |= (
              if ( . | map(.name) | index(\"${VERSION}\") | not ) then
                . += [ {\"name\": \"${VERSION}\", \"url\": \"/ameba/${VERSION}/\", \"released\": ${RELEASED}} ]
              else
                .
              end
            )"

          cat versions.json | \
            jq "$JQ_EXPRESSION" > versions.json.tmp && \
            mv versions.json.tmp versions.json

      - name: Check for changes
        id: git-diff-changes
        run: |
          git diff --color

          if [[ -n "$(git diff --exit-code)" ]]; then
            echo "Changes detected."
            echo "has-changes=true" >> $GITHUB_OUTPUT
          else
            echo "No changes detected."
            echo "has-changes=false" >> $GITHUB_OUTPUT
          fi

      - name: Configure Git identity
        if: steps.git-diff-changes.outputs.has-changes == 'true'
        run: |
          git config --global user.name "github-actions[bot]"
          git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com"

      - name: Commit changes to Git
        if: steps.git-diff-changes.outputs.has-changes == 'true'
        run: |
          git commit -am "Update versions.json"
          git push -u origin HEAD


================================================
FILE: .github/workflows/build-and-deploy-docs.yml
================================================
name: Build and deploy docs

on:
  workflow_dispatch:
    inputs: &inputs
      ref:
        required: true
        type: string
        description: The tag or branch to deploy
      version:
        type: string
        description: Version to deploy
      git-branch:
        type: string
        description: Git branch to deploy to
        default: gh-pages
  workflow_call:
    inputs: *inputs

permissions:
  contents: write

jobs:
  build-and-deploy:
    concurrency: ci-${{ github.ref }}
    runs-on: ubuntu-latest

    steps:
      - name: Inject slug/short variables
        uses: rlespinasse/github-slug-action@v5

      - name: Install Crystal
        uses: crystal-lang/install-crystal@v1

      - name: Download source
        uses: actions/checkout@v6
        with:
          ref: ${{ inputs.ref }}

      - name: Get ref SHA
        id: ref-sha
        run: |
          REF_SHA="$(git rev-parse --short HEAD)"
          echo "REF_SHA: ${REF_SHA}"
          echo "sha=${REF_SHA}" >> $GITHUB_OUTPUT

      - name: Install dependencies
        run: shards install

      - name: Build docs
        run: |
          crystal docs \
            --project-version="${{ inputs.version || inputs.ref }}" \
            --source-refname="${{ steps.ref-sha.outputs.sha }}" \
            --json-config-url="/ameba/versions.json"

      - name: Deploy docs 🚀
        uses: JamesIves/github-pages-deploy-action@v4
        with:
          branch: ${{ inputs.git-branch }}
          folder: docs
          target-folder: ${{ inputs.version || inputs.ref }}
          clean: true

  add-version-to-json-index:
    uses: ./.github/workflows/add-docs-version-to-json-index.yml
    with:
      git-branch: ${{ inputs.git-branch }}
      version: ${{ inputs.version || inputs.ref }}
    needs:
      - build-and-deploy


================================================
FILE: .github/workflows/ci.yml
================================================
name: CI

on:
  workflow_dispatch:
  push:
    branches: [master]
  pull_request:
    types: [opened, synchronize, reopened]
  schedule:
    - cron: "0 3 * * 1" # Every monday at 3 AM

permissions:
  contents: read

jobs:
  test:
    strategy:
      fail-fast: false
      matrix:
        os: [ubuntu-latest, macos-latest, windows-latest]
        crystal: [latest, nightly]
    runs-on: ${{ matrix.os }}

    steps:
      - name: Set timezone to UTC
        uses: szenius/set-timezone@v2.0

      - name: Install Crystal
        uses: crystal-lang/install-crystal@v1
        with:
          crystal: ${{ matrix.crystal }}

      - name: Download source
        uses: actions/checkout@v6

      - name: Install dependencies
        run: shards install

      - name: Install typos-cli
        if: matrix.os == 'macos-latest'
        run: brew install typos-cli

      - name: Run specs
        run: make spec

      - name: Build ameba binary
        run: make build

      - name: Run ameba linter
        run: make lint


================================================
FILE: .github/workflows/compare-json-schema.yml
================================================
name: Compare JSON Schema

on:
  pull_request:
    types: [opened, synchronize, reopened]
    paths:
      - ".github/workflows/compare-json-schema.yml"
      - "**/*.cr"

permissions:
  contents: read

jobs:
  check-for-changes:
    runs-on: ubuntu-latest

    steps:
      - name: Set timezone to UTC
        uses: szenius/set-timezone@v2.0

      - name: Install Crystal
        uses: crystal-lang/install-crystal@v1

      - name: Download source
        uses: actions/checkout@v6

      - name: Install dependencies
        run: shards install

      - name: Run JSON Schema builder
        run: shards run json-schema-builder

      - name: Check for changes
        run: git diff --color --exit-code
        continue-on-error: true


================================================
FILE: .github/workflows/docker-image.yml
================================================
name: Docker image build and deploy

on:
  workflow_dispatch:
  push:
    branches: [master]
  release:
    types: [published]

env:
  # Use docker.io for Docker Hub if empty
  REGISTRY: ghcr.io
  # github.repository as <account>/<repo>
  IMAGE_NAME: ${{ github.repository }}

jobs:
  build:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write
      # This is used to complete the identity challenge
      # with sigstore/fulcio when running outside of PRs.
      id-token: write

    steps:
      - name: Checkout repository
        uses: actions/checkout@v6

      - name: Set up QEMU
        uses: docker/setup-qemu-action@v4

      - name: Setup Docker Buildx
        uses: docker/setup-buildx-action@v4

      # Login against a Docker registry except on PR
      # https://github.com/docker/login-action
      - name: Log into ${{ env.REGISTRY }} registry
        uses: docker/login-action@v4
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      # Extract metadata (tags, labels) for Docker
      # https://github.com/docker/metadata-action
      - name: Extract Docker metadata
        id: meta
        uses: docker/metadata-action@v6
        with:
          images: |
            ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
          tags: |
            type=ref,event=tag
            type=ref,event=branch
            type=ref,event=pr
            type=sha,format=long
            type=semver,pattern={{version}}
            type=semver,pattern={{major}}.{{minor}}

      # Build and push Docker image with Buildx
      # https://github.com/docker/build-push-action
      - name: Build and push Docker image
        id: build-and-push
        uses: docker/build-push-action@v7
        with:
          context: .
          push: true
          cache-from: type=gha
          cache-to: type=gha,mode=max
          build-args: |
            CRFLAGS=-Dpreview_mt --release
          platforms: |
            linux/amd64
            linux/arm64
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}


================================================
FILE: .github/workflows/docs.yml
================================================
name: Docs

on:
  push:
    branches:
      - master
    tags:
      - "v*.*.*"

permissions:
  contents: write

jobs:
  setup:
    runs-on: ubuntu-latest
    outputs:
      version: ${{ steps.variables.outputs.version }}
      ref: ${{ steps.variables.outputs.ref }}

    steps:
      - name: Inject slug/short variables
        uses: rlespinasse/github-slug-action@v5

      - name: Set output variables
        id: variables
        run: |
          VERSION="${GITHUB_REF_NAME}"
          if [[ $VERSION =~ ^v[0-9].* ]]; then
            VERSION=${VERSION#"v"}
          fi
          echo "VERSION: ${VERSION}"
          echo "version=${VERSION}" >> $GITHUB_OUTPUT

          REF="${GITHUB_REF_POINT}"
          echo "REF: ${REF}"
          echo "ref=${REF}" >> $GITHUB_OUTPUT

  build-and-deploy:
    uses: ./.github/workflows/build-and-deploy-docs.yml
    with:
      version: ${{ needs.setup.outputs.version }}
      ref: ${{ needs.setup.outputs.ref }}
    needs:
      - setup


================================================
FILE: .github/workflows/release.yml
================================================
name: Release

on:
  push:
    tags:
      - "v*.*.*"

permissions:
  contents: write

env:
  GH_TOKEN: ${{ github.token }}

jobs:
  draft:
    runs-on: ubuntu-latest
    steps:
      - run: gh release -R ${{ github.repository }} create ${{ github.ref_name }} --draft --generate-notes

  linux-musl:
    strategy:
      fail-fast: false
      matrix:
        os: [ubuntu-24.04, ubuntu-24.04-arm]
    runs-on: ${{ matrix.os }}
    steps:
      - uses: actions/checkout@v6
      - run: docker run --rm -v $PWD:/mnt -w /mnt crystallang/crystal:latest-alpine shards build ameba --static -Dpreview_mt --release
      - run: tar zcf ameba-${{ github.ref_name }}-$(uname -m)-linux-musl.tar.gz -C bin ameba
      - run: gh release -R ${{ github.repository }} upload ${{ github.ref_name }} ameba-${{ github.ref_name }}-$(uname -m)-linux-musl.tar.gz

  darwin:
    strategy:
      fail-fast: false
      matrix:
        include:
          - {os: macos-15, arch: aarch64}
          - {os: macos-15-intel, arch: x86_64}
    runs-on: ${{ matrix.os }}
    steps:
      - uses: crystal-lang/install-crystal@v1
      - uses: actions/checkout@v6
      - run: shards build ameba -Dpreview_mt --release
      - run: tar zcf ameba-${{ github.ref_name }}-${{ matrix.arch }}-darwin.tar.gz -C bin ameba ameba.dwarf
      - run: gh release -R ${{ github.repository }} upload ${{ github.ref_name }} ameba-${{ github.ref_name }}-${{ matrix.arch }}-darwin.tar.gz

  x86_64-windows-msvc:
    runs-on: windows-2025
    steps:
      - uses: crystal-lang/install-crystal@v1
      - uses: actions/checkout@v6
      - run: shards build ameba --static -Dpreview_mt --release
      - run: cd bin; 7z a ../ameba-${{ github.ref_name }}-x86_64-windows-msvc.zip ameba.exe ameba.pdb
      - run: gh release -R ${{ github.repository }} upload ${{ github.ref_name }} ameba-${{ github.ref_name }}-x86_64-windows-msvc.zip



================================================
FILE: .github/workflows/typos.yml
================================================
name: Spell checker

on:
  push:
    branches: [master]
  pull_request:
    types: [opened, synchronize, reopened]

permissions:
  contents: read

jobs:
  typos:
    runs-on: ubuntu-latest

    steps:
      - name: Download source
        uses: actions/checkout@v6

      - name: Run `typos` spell checker
        uses: crate-ci/typos@v1


================================================
FILE: .github/workflows/update-json-schema.yml
================================================
name: Update JSON Schema

on:
  workflow_dispatch:
  push:
    branches: [master]
    paths:
      - ".github/workflows/update-json-schema.yml"
      - "shard.yml"
      - "**/*.cr"

permissions:
  contents: write

jobs:
  update-json-schema:
    concurrency: ci-${{ github.ref }}
    runs-on: ubuntu-latest

    steps:
      - name: Set timezone to UTC
        uses: szenius/set-timezone@v2.0

      - name: Install Crystal
        uses: crystal-lang/install-crystal@v1

      - name: Download source
        uses: actions/checkout@v6
        with:
          ssh-key: ${{ secrets.MASTER_PUSHER_SSH_KEY }}

      - name: Install dependencies
        run: shards install

      - name: Run JSON Schema builder
        run: shards run json-schema-builder

      - name: Check for changes
        id: git-diff-changes
        run: |
          if [[ -n "$(git diff --exit-code)" ]]; then
            echo "Changes detected."
            echo "has-changes=true" >> $GITHUB_OUTPUT
          else
            echo "No changes detected."
            echo "has-changes=false" >> $GITHUB_OUTPUT
          fi

      - name: Configure Git identity
        if: steps.git-diff-changes.outputs.has-changes == 'true'
        run: |
          git config --global user.name "github-actions[bot]"
          git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com"

      - name: Commit and push changes
        if: steps.git-diff-changes.outputs.has-changes == 'true'
        run: |
          git commit -am "Update JSON Schema"
          git push -u origin HEAD


================================================
FILE: .gitignore
================================================
# Documentation
/docs/

# Installed shards
/lib/

# Built binaries
/bin/**/*
!/bin/ameba.cr

# Libraries don't need dependency lock
# Dependencies will be locked in application that uses them
/shard.lock

# Workspace settings used by common text-editors
/.vscode
/.zed


================================================
FILE: .typos.toml
================================================
[default]
extend-ignore-re = [
  # numeric literals
  '0x[0-9a-fA-F_\.\+]+([fiu](8|16|32|64|128))?',
  '\\u\{[0-9a-fA-F]+\}',
  # fixed test values
  '[Ff][Oo][Oo]+',
  # words including a number are likely some kind of identifier
  "[a-zA-Z]+[0-9]+",
]

[files]
extend-exclude = [
  # git and dependencies
  ".git/**",
  "lib/**",
  # individual files to exclude
  "spec/ameba/rule/lint/typos_spec.cr",
]


================================================
FILE: Dockerfile
================================================
FROM alpine:edge AS builder
ARG CRFLAGS="-Dpreview_mt"
RUN apk add --update crystal shards yaml-dev musl-dev make
RUN mkdir /ameba
WORKDIR /ameba
COPY . /ameba/
RUN crystal -v
RUN make clean && make CRFLAGS="$CRFLAGS"

FROM alpine:latest
RUN apk add --update yaml pcre2 gc libevent libgcc
RUN mkdir /src
WORKDIR /src
COPY --from=builder /ameba/bin/ameba /usr/bin/
RUN ameba -v
ENTRYPOINT [ "/usr/bin/ameba" ]


================================================
FILE: LICENSE
================================================
The MIT License (MIT)

Copyright (c) 2018-2020 Vitalii Elenhaupt

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.


================================================
FILE: Makefile
================================================
.POSIX:
all:

# Recipes

## Build ameba
##   $ make
##
## Run tests
##   $ make test
##
## Install ameba
##   $ sudo make install

-include Makefile.local # for optional local options

BUILD_TARGET := bin/ameba

DESTDIR ?=          ## Install destination dir
PREFIX ?= /usr/local## Install path prefix
BINDIR ?= $(DESTDIR)$(PREFIX)/bin

# The crystal command to use
CRYSTAL_BIN ?= crystal
# The shards command to use
SHARDS_BIN ?= shards
# The install command to use
INSTALL_BIN ?= /usr/bin/install

CRFLAGS ?= -Dpreview_mt

SRC_SOURCES := $(shell find src -name '*.cr' 2>/dev/null)

.PHONY: all
all: build

.PHONY: build
build: ## Build the application binary
build: $(BUILD_TARGET)

$(BUILD_TARGET): $(SRC_SOURCES)
	$(SHARDS_BIN) build ameba $(CRFLAGS)

.PHONY: docs
docs: ## Generate API docs
docs: $(SRC_SOURCES)
	$(CRYSTAL_BIN) docs

.PHONY: spec
spec: ## Run the spec suite
spec:
	$(CRYSTAL_BIN) spec

.PHONY: schema
schema: ## Build the latest schema
schema:
	$(SHARDS_BIN) run schema

.PHONY: lint
lint: ## Run ameba on its own code base
lint: $(BUILD_TARGET)
	$(BUILD_TARGET)

.PHONY: test
test: ## Run the spec suite and linter
test: spec lint

.PHONY: clean
clean: ## Remove application binary and API docs
clean:
	@rm -f "$(BUILD_TARGET)" "$(BUILD_TARGET).dwarf"
	@rm -rf docs

.PHONY: install
install: ## Install application binary into $DESTDIR
install: $(BUILD_TARGET)
	mkdir -p "$(BINDIR)"
	$(INSTALL_BIN) -m 0755 "$(BUILD_TARGET)" "$(BINDIR)/ameba"

.PHONY: help
help: ## Show this help
	@printf '\033[34mtargets:\033[0m\n'
	@grep -hE '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) |\
		sort |\
		awk 'BEGIN {FS = ":.*?## "}; {printf "  \033[36m%-15s\033[0m %s\n", $$1, $$2}'
	@echo
	@printf '\033[34moptional variables:\033[0m\n'
	@grep -hE '^[a-zA-Z_-]+ \?=.*?## .*$$' $(MAKEFILE_LIST) |\
		sort |\
		awk 'BEGIN {FS = " \\?=.*?## "}; {printf "  \033[36m%-15s\033[0m %s\n", $$1, $$2}'
	@echo
	@printf '\033[34mrecipes:\033[0m\n'
	@grep -hE '^##.*$$' $(MAKEFILE_LIST) |\
		awk 'BEGIN {FS = "## "}; /^## [a-zA-Z_-]/ {printf "  \033[36m%s\033[0m\n", $$2}; /^##  / {printf "  %s\n", $$2}'


================================================
FILE: README.md
================================================
<p align="center">
  <img src="https://raw.githubusercontent.com/veelenga/bin/master/ameba/logo.png" width="800">
  <h3 align="center">Ameba</h3>
  <p align="center">Code style linter for Crystal<p>
  <p align="center">
    <sup>
      <i>(a single-celled animal that catches food and moves about by extending fingerlike projections of protoplasm)</i>
    </sup>
  </p>
  <p align="center">
    <a href="https://github.com/crystal-ameba/ameba/actions/workflows/ci.yml"><img src="https://github.com/crystal-ameba/ameba/actions/workflows/ci.yml/badge.svg"></a>
    <a href="https://github.com/crystal-ameba/ameba/releases"><img src="https://img.shields.io/github/release/crystal-ameba/ameba.svg?maxAge=360"></a>
    <a href="https://github.com/crystal-ameba/ameba/blob/master/LICENSE"><img src="https://img.shields.io/github/license/crystal-ameba/ameba.svg"></a>
  </p>
</p>

- [About](#about)
- [Usage](#usage)
  - [Watch a tutorial](#watch-a-tutorial)
  - [Autocorrection](#autocorrection)
  - [Explain issues](#explain-issues)
  - [Run in parallel](#run-in-parallel)
- [Installation](#installation)
  - [As a project dependency:](#as-a-project-dependency)
  - [OS X](#os-x)
  - [Docker](#docker)
  - [From sources](#from-sources)
- [Configuration](#configuration)
  - [Sources](#sources)
  - [Rules](#rules)
  - [Inline disabling](#inline-disabling)
- [Editors \& integrations](#editors--integrations)
- [Credits \& inspirations](#credits--inspirations)
- [Contributors](#contributors)

## About

Ameba is a static code analysis tool for the Crystal language.
It enforces a consistent [Crystal code style](https://crystal-lang.org/reference/conventions/coding_style.html),
also catches code smells and wrong code constructions.

See also [Roadmap](https://github.com/crystal-ameba/ameba/wiki).

## Usage

Run `ameba` binary within your project directory to catch code issues:

```sh
$ ameba
Inspecting 107 files

...............F.....................FF....................................................................

src/ameba/formatter/flycheck_formatter.cr:6:37
[W] Lint/UnusedArgument: Unused argument `location`. If it's necessary, use `_` as an argument name to indicate that it won't be used.
> source.issues.each do |issue, location|
                                ^

src/ameba/formatter/base_formatter.cr:16:14
[W] Lint/UselessAssign: Useless assignment to variable `s`
> return s += issues.size
         ^

src/ameba/formatter/base_formatter.cr:16:7 [Correctable]
[C] Style/RedundantReturn: Redundant `return` detected
> return s += issues.size
  ^---------------------^

Finished in 389.45 milliseconds
107 inspected, 3 failures
```

### Watch a tutorial

<a href="https://luckycasts.com/videos/ameba"><img src="https://i.imgur.com/uOETQlM.png" title="Write Better Crystal Code with the Ameba Shard" width="500" /></a>

[🎬 Watch the LuckyCast showing how to use Ameba](https://luckycasts.com/videos/ameba)

### Autocorrection

Rules that are marked as `[Correctable]` in the output can be automatically corrected using `--fix` flag:

```sh
$ ameba --fix
```

### Explain issues

Ameba allows you to dig deeper into an issue, by showing you details about the issue
and the reasoning by it being reported.

To be convenient, you can just copy-paste the `PATH:line:column` string from the
report and paste behind the `ameba` command to check it out.

```sh
$ ameba crystal/command/format.cr:26:83           # show explanation for the issue
$ ameba --explain crystal/command/format.cr:26:83 # same thing
```

### Run in parallel

Some quick benchmark results measured while running Ameba on Crystal repo:

```sh
$ CRYSTAL_WORKERS=1 ameba #=> 29.11 seconds
$ CRYSTAL_WORKERS=2 ameba #=> 19.49 seconds
$ CRYSTAL_WORKERS=4 ameba #=> 13.48 seconds
$ CRYSTAL_WORKERS=8 ameba #=> 10.14 seconds
```

## Installation

### As a project dependency

Add this to your application's `shard.yml`:

```yaml
development_dependencies:
  ameba:
    github: crystal-ameba/ameba
```

To prioritize runtime performance over compilation time, you can add `ameba`
target to the `shard.yml` file:

```yaml
targets:
  ameba:
    main: lib/ameba/bin/ameba.cr
```

And then run:

```sh
$ shards build ameba -Dpreview_mt
```

Alternatively, skip adding `ameba` target and use `crystal build` command directly:

```sh
$ crystal build -Dpreview_mt -o bin/ameba lib/ameba/bin/ameba.cr
```

Both of these will result in a compiled binary placed under `bin/ameba` path.

You can also just run the `lib/ameba/bin/ameba.cr` file, compiling it on the fly,
which is the slowest option:

```sh
$ lib/ameba/bin/ameba.cr
```

### OS X

```sh
$ brew tap crystal-ameba/ameba
$ brew install ameba
```

### Docker

Build the image:

```sh
$ docker build -t ghcr.io/crystal-ameba/ameba .
```

To use the resulting image on a local source folder, mount the current (or target) directory into `/src`:

```sh
$ docker run -v $(pwd):/src ghcr.io/crystal-ameba/ameba
```

Also available on GitHub: https://github.com/crystal-ameba/ameba/pkgs/container/ameba

### From sources

```sh
$ git clone https://github.com/crystal-ameba/ameba && cd ameba
$ make install
```

## Configuration

Default configuration file is `.ameba.yml`.
It allows to configure rule properties, disable specific rules and exclude sources from the rules.

Generate new file by running `ameba --gen-config`.

### Sources

**List of sources to run Ameba on can be configured globally via:**

- `Globs` section - an array of wildcards (or paths) to include to the
  inspection. Defaults to `%w[**/*.cr **/*.ecr]`, meaning it includes all project
  files with `*.cr` and `*.ecr` extensions.
- `Excluded` section - an array of wildcards (or paths) to exclude from the
  source list defined by `Globs`. Defaults to `%w[lib]`, meaning it excludes the
  `lib` folder.

In this example we define default globs and exclude `lib` and `src/compiler` folders:

``` yaml
Globs:
  - "**/*.cr"
  - "**/*.ecr"

Excluded:
  - lib
  - src/compiler
```

**Specific sources can be excluded at rule level**:

``` yaml
Style/RedundantBegin:
  Excluded:
    - src/server/processor.cr
    - src/server/api.cr
```

### Rules

One or more rules, or a one or more group of rules can be included or excluded
via command line arguments:

```sh
$ ameba --only   Lint/Syntax # runs only Lint/Syntax rule
$ ameba --only   Style,Lint  # runs only rules from Style and Lint groups
$ ameba --except Lint/Syntax # runs all rules except Lint/Syntax
$ ameba --except Style,Lint  # runs all rules except rules in Style and Lint groups
```

Or through the configuration file:

``` yaml
Style/RedundantBegin:
  Enabled: false
```

### Inline disabling

One or more rules or one or more group of rules can be disabled using inline directives:

```crystal
# ameba:disable Style/LargeNumbers
time = Time.epoch(1483859302)

time = Time.epoch(1483859302) # ameba:disable Style/LargeNumbers, Lint/UselessAssign
time = Time.epoch(1483859302) # ameba:disable Style, Lint
```

## Editors & integrations

- Vim: [vim-crystal](https://github.com/rhysd/vim-crystal), [Ale](https://github.com/w0rp/ale)
- Emacs: [ameba.el](https://github.com/crystal-ameba/ameba.el)
- Sublime Text: [Sublime Linter Ameba](https://github.com/epergo/SublimeLinter-contrib-ameba)
- VSCode: [vscode-crystal-ameba](https://github.com/crystal-ameba/vscode-crystal-ameba)
- Codacy: [codacy-ameba](https://github.com/codacy/codacy-ameba)
- GitHub Actions: [github-action](https://github.com/crystal-ameba/github-action)

## Credits & inspirations

- [Crystal Language](https://crystal-lang.org)
- [Rubocop](https://rubocop.org)
- [Credo](http://credo-ci.org)
- [Dogma](https://github.com/lpil/dogma)

## Contributors

- [veelenga](https://github.com/veelenga) Vitalii Elenhaupt - creator, maintainer
- [Sija](https://github.com/Sija) Sijawusz Pur Rahnama - contributor, maintainer


================================================
FILE: bench/check_sources.cr
================================================
require "../src/ameba"
require "benchmark"

private def get_files(n)
  Dir["src/**/*.cr"].first(n)
end

puts "== Compare:"
Benchmark.ips do |x|
  [
    1,
    3,
    5,
    10,
    20,
    30,
    40,
  ].each do |n| # ameba:disable Naming/BlockParameterName
    config = Ameba::Config.load
    config.formatter = Ameba::Formatter::BaseFormatter.new
    config.globs = get_files(n)
    s = n == 1 ? "" : "s"
    x.report("#{n} source#{s}") { Ameba.run config }
  end
end

puts "== Measure:"
config = Ameba::Config.load
config.formatter = Ameba::Formatter::BaseFormatter.new
puts Benchmark.measure { Ameba.run config }


================================================
FILE: bin/ameba.cr
================================================
#!/usr/bin/env crystal

# Require ameba extensions here which are added as project dependencies.
# Example:
#
# require "ameba-performance"

# Require ameba cli which starts the inspection.
require "ameba/cli"


================================================
FILE: shard.yml
================================================
name: ameba
version: 1.7.0-dev

authors:
  - Vitalii Elenhaupt <velenhaupt@gmail.com>
  - Sijawusz Pur Rahnama <sija@sija.pl>

targets:
  ameba:
    main: src/cli.cr
  json-schema-builder:
    main: src/json-schema-builder.cr

crystal: ~> 1.19

license: MIT


================================================
FILE: spec/ameba/ast/flow_expression_spec.cr
================================================
require "../../spec_helper"

module Ameba::AST
  describe FlowExpression do
    describe "#initialize" do
      it "creates a new flow expression" do
        node = as_node("return 22")
        flow_expression = FlowExpression.new node, false
        flow_expression.node.should_not be_nil
        flow_expression.in_loop?.should be_false
      end

      describe "#delegation" do
        it "delegates to_s to @node" do
          node = as_node("return 22")
          flow_expression = FlowExpression.new node, false
          flow_expression.to_s.should eq node.to_s
        end

        it "delegates locations to @node" do
          node = as_node("break if true")
          flow_expression = FlowExpression.new node, false
          flow_expression.location.should eq node.location
          flow_expression.end_location.should eq node.end_location
        end
      end

      describe "#unreachable_nodes" do
        it "returns unreachable nodes" do
          nodes = as_nodes <<-CRYSTAL
            def foobar
              return
              a = 1
              a = 2
            end
            CRYSTAL
          node = nodes.expressions_nodes.first
          flow_expression = FlowExpression.new node, false
          flow_expression.unreachable_nodes.should eq nodes.assign_nodes
        end

        it "returns nil if there is no unreachable node after loop" do
          nodes = as_nodes <<-CRYSTAL
            def run
              idx = items.size - 1
              while 0 <= idx
                return
              end

              puts "foo"
            end
            CRYSTAL
          node = nodes.expressions_nodes.first
          flow_expression = FlowExpression.new node, false
          flow_expression.unreachable_nodes.empty?.should be_true
        end

        it "returns nil if there is no unreachable node" do
          nodes = as_nodes <<-CRYSTAL
            def foobar
              a = 1
              return a
            end
            CRYSTAL
          node = nodes.expressions_nodes.first
          flow_expression = FlowExpression.new node, false
          flow_expression.unreachable_nodes.empty?.should be_true
        end
      end
    end
  end
end


================================================
FILE: spec/ameba/ast/liveness_analyzer_spec.cr
================================================
require "../../spec_helper"

private def scopes_for(code)
  rule = Ameba::ScopeRule.new
  source = Ameba::Source.new(code)
  Ameba::AST::ScopeVisitor.new(rule, source)
  rule.scopes
end

private def def_scope(code)
  scopes_for(code).find! { |scope| scope.node.is_a?(Crystal::Def) }
end

private def top_scope(code)
  scopes_for(code).find! { |scope| scope.node.is_a?(Crystal::Expressions) }
end

private def block_scope(code)
  scopes_for(code).find! { |scope| scope.node.is_a?(Crystal::Block) }
end

private def dead_store_names(scope)
  Ameba::AST::LivenessAnalyzer.new(scope).dead_stores.map(&.variable.name)
end

module Ameba::AST
  describe LivenessAnalyzer do
    context "basic assignments" do
      it "detects unused assignment as dead store" do
        scope = def_scope <<-CRYSTAL
          def foo
            a = 1
          end
          CRYSTAL
        dead_store_names(scope).should eq ["a"]
      end

      it "does not report assignment that is used" do
        scope = def_scope <<-CRYSTAL
          def foo
            a = 1
            a
          end
          CRYSTAL
        dead_store_names(scope).should be_empty
      end

      it "detects first assignment as dead when overwritten before use" do
        scope = def_scope <<-CRYSTAL
          def foo
            a = 1
            a = 2
            a
          end
          CRYSTAL
        dead_store_names(scope).should eq ["a"]
      end

      it "detects all assignments as dead when none are used" do
        scope = def_scope <<-CRYSTAL
          def foo
            a = 1
            a = 2
          end
          CRYSTAL
        dead_store_names(scope).should eq ["a", "a"]
      end

      it "does not report assignment used in a condition" do
        scope = def_scope <<-CRYSTAL
          def foo
            a = 1
            if a
              nil
            end
          end
          CRYSTAL
        dead_store_names(scope).should be_empty
      end

      it "reports second assignment when value is not used" do
        scope = def_scope <<-CRYSTAL
          def foo
            a = 1
            a = a + 1
          end
          CRYSTAL
        dead_store_names(scope).should eq ["a"]
      end

      it "does not report assignment used in another assignment" do
        scope = def_scope <<-CRYSTAL
          def foo
            if f = get_something
              @f = f
            end
          end
          CRYSTAL
        dead_store_names(scope).should be_empty
      end

      it "reports last assignment when not used after reassignment" do
        scope = def_scope <<-CRYSTAL
          def foo
            a = 1
            puts a
            a = 2
          end
          CRYSTAL
        dead_store_names(scope).should eq ["a"]
      end
    end

    context "op assignments" do
      it "does not report op-assign when result is used" do
        scope = def_scope <<-CRYSTAL
          def foo
            a = 1
            a += 1
            a
          end
          CRYSTAL
        dead_store_names(scope).should be_empty
      end

      it "reports op-assign when result is not used" do
        scope = def_scope <<-CRYSTAL
          def foo
            a = 1
            a += 1
          end
          CRYSTAL
        dead_store_names(scope).should eq ["a"]
      end

      it "does not report chained op-assigns when result is used" do
        scope = def_scope <<-CRYSTAL
          def foo
            a = 1
            a += 1
            a += 1
            a = a + 1
            a
          end
          CRYSTAL
        dead_store_names(scope).should be_empty
      end
    end

    context "if/unless branches" do
      it "reports initial assignment as dead when overwritten in both branches" do
        scope = def_scope <<-CRYSTAL
          def foo
            a = 0
            if something
              a = 1
            else
              a = 2
            end
            a
          end
          CRYSTAL
        dead_store_names(scope).should eq ["a"]
      end

      it "does not report when assigned in one branch and used after" do
        scope = def_scope <<-CRYSTAL
          def foo
            a = 0
            if something
              a = 1
            end
            a
          end
          CRYSTAL
        dead_store_names(scope).should be_empty
      end

      it "does not report when assigned in one branch with else nil and used after" do
        scope = def_scope <<-CRYSTAL
          def foo
            a = 0
            if something
              a = 1
            else
              nil
            end
            a
          end
          CRYSTAL
        dead_store_names(scope).should be_empty
      end

      it "reports useless assignment in branch when not used after" do
        scope = def_scope <<-CRYSTAL
          def foo(a)
            if a
              a = 2
            end
          end
          CRYSTAL
        dead_store_names(scope).should eq ["a"]
      end

      it "reports first dead assignment in branch when overwritten" do
        scope = def_scope <<-CRYSTAL
          def foo(a)
            a = 1
            if a
              a = 2
              a = 3
            end
            a
          end
          CRYSTAL
        dead_store_names(scope).should eq ["a"]
      end

      it "reports initial assignment as dead when overwritten in all branches" do
        scope = def_scope <<-CRYSTAL
          def foo
            has_newline = false

            if something
              do_something unless false
              has_newline = false
            else
              do_something if true
              has_newline = true
            end

            has_newline
          end
          CRYSTAL
        dead_store_names(scope).should eq ["has_newline"]
      end

      it "does not report unless with consumed branches" do
        scope = def_scope <<-CRYSTAL
          def foo
            a = 0
            unless something
              a = 1
            else
              a = 2
            end
            a
          end
          CRYSTAL
        dead_store_names(scope).should eq ["a"]
      end

      it "reports dead assignment in unless branch" do
        scope = def_scope <<-CRYSTAL
          def foo
            a = 0
            unless something
              a = 1
              a = 2
            else
              a = 2
            end
            a
          end
          CRYSTAL
        dead_store_names(scope).should eq ["a", "a"]
      end

      it "does not report one-line if assignment used after" do
        scope = def_scope <<-CRYSTAL
          def foo
            a = 0
            a = 1 if something
            a
          end
          CRYSTAL
        dead_store_names(scope).should be_empty
      end
    end

    context "while loops" do
      it "does not report assignment used across iterations" do
        scope = def_scope <<-CRYSTAL
          def foo(a)
            while a < 10
              a = a + 1
            end
            a
          end
          CRYSTAL
        dead_store_names(scope).should be_empty
      end

      it "reports assignment not used outside loop" do
        scope = def_scope <<-CRYSTAL
          def foo(a)
            while a < 10
              b = a
            end
          end
          CRYSTAL
        dead_store_names(scope).should eq ["b"]
      end

      it "does not report assignment used in loop with accumulator" do
        scope = def_scope <<-CRYSTAL
          def foo
            a = 3
            result = 0

            while result < 10
              result += a
              a = a + 1
            end
            result
          end
          CRYSTAL
        dead_store_names(scope).should be_empty
      end

      it "does not report parameter assignment used in loop" do
        scope = def_scope <<-CRYSTAL
          def foo(a)
            result = 0

            while result < 10
              result += a
              a = a + 1
            end
            result
          end
          CRYSTAL
        dead_store_names(scope).should be_empty
      end

      it "does not report assignment in loop with inner branch" do
        scope = def_scope <<-CRYSTAL
          def foo(a)
            result = 0

            while result < 10
              result += a
              if result > 0
                a = a + 1
              else
                a = 3
              end
            end
            result
          end
          CRYSTAL
        dead_store_names(scope).should be_empty
      end

      it "handles branch with blank node in loop" do
        scope = def_scope <<-CRYSTAL
          def foo
            count = 0
            while true
              break if count == 1
              case something
              when :any
              else
                :anything_else
              end
              count += 1
            end
          end
          CRYSTAL
        dead_store_names(scope).should be_empty
      end

      it "does not report assignment used after break" do
        scope = def_scope <<-CRYSTAL
          def foo
            found = false
            while true
              if something
                found = true
                break
              end
            end
            found
          end
          CRYSTAL
        dead_store_names(scope).should be_empty
      end

      it "does not report assignment before next used in subsequent iteration" do
        scope = def_scope <<-CRYSTAL
          def foo
            atomic = parse_atomic
            while true
              if @token.instance_var?
                atomic = parse_ivar(atomic)
                next
              end
              break
            end
            atomic
          end
          CRYSTAL
        dead_store_names(scope).should be_empty
      end

      it "does not report assignment used after break in nested loops" do
        scope = def_scope <<-CRYSTAL
          def foo
            found = false
            while outer_cond
              while inner_cond
                if something
                  found = true
                  break
                end
              end
            end
            found
          end
          CRYSTAL
        dead_store_names(scope).should be_empty
      end

      it "does not report assignment inside conditional break" do
        scope = def_scope <<-CRYSTAL
          def foo
            options = 0
            while true
              if done?
                options = compute_options
                break
              end
              process
            end
            options
          end
          CRYSTAL
        dead_store_names(scope).should be_empty
      end
    end

    context "until loops" do
      it "does not report assignment used across until iterations" do
        scope = def_scope <<-CRYSTAL
          def foo(a)
            until a > 10
              a = a + 1
            end
            a
          end
          CRYSTAL
        dead_store_names(scope).should be_empty
      end

      it "reports useless assignment in until loop" do
        scope = def_scope <<-CRYSTAL
          def foo(a)
            until a > 10
              b = a + 1
            end
          end
          CRYSTAL
        dead_store_names(scope).should eq ["b"]
      end
    end

    context "exception handlers" do
      it "does not report assignment used in rescue" do
        scope = def_scope <<-CRYSTAL
          def foo(a)
            a = 2
          rescue
            a
          end
          CRYSTAL
        dead_store_names(scope).should be_empty
      end

      it "does not report assignment used in ensure" do
        scope = def_scope <<-CRYSTAL
          def foo(a)
            a = 2
          ensure
            a
          end
          CRYSTAL
        dead_store_names(scope).should be_empty
      end

      it "does not report assignment used in else" do
        scope = def_scope <<-CRYSTAL
          def foo(a)
            a = 2
          rescue
          else
            a
          end
          CRYSTAL
        dead_store_names(scope).should be_empty
      end

      it "reports useless assignment in rescue" do
        scope = def_scope <<-CRYSTAL
          def foo(a)
          rescue
            a = 2
          end
          CRYSTAL
        dead_store_names(scope).should eq ["a"]
      end

      it "does not report assignment used in rescue when body has break" do
        scope = block_scope <<-CRYSTAL
          3.times do
            start = 1
            begin
              perform_foo
              break
            rescue
              start
            end
          end
          CRYSTAL
        dead_store_names(scope).should be_empty
      end

      it "does not report assignment used in rescue when body has return" do
        scope = def_scope <<-CRYSTAL
          def foo
            start = 1
            begin
              perform_foo
              return
            rescue
              start
            end
          end
          CRYSTAL
        dead_store_names(scope).should be_empty
      end

      it "does not report assignment used in rescue when body has next" do
        scope = block_scope <<-CRYSTAL
          3.times do
            start = 1
            begin
              perform_foo
              next
            rescue
              start
            end
          end
          CRYSTAL
        dead_store_names(scope).should be_empty
      end
    end

    context "binary operators" do
      it "does not report when both sides of && are used" do
        scope = def_scope <<-CRYSTAL
          def foo(a)
            (a = 1) && (b = 1)
            a + b
          end
          CRYSTAL
        dead_store_names(scope).should be_empty
      end

      it "reports unused side of ||" do
        scope = def_scope <<-CRYSTAL
          def foo(a)
            (a = 1) || (b = 1)
            a
          end
          CRYSTAL
        dead_store_names(scope).should eq ["b"]
      end
    end

    context "case" do
      it "does not report when used after case" do
        scope = def_scope <<-CRYSTAL
          def foo(a)
            case a
            when /foo/
              a = 1
            when /bar/
              a = 2
            end
            puts a
          end
          CRYSTAL
        dead_store_names(scope).should be_empty
      end

      it "reports when not used after case" do
        scope = def_scope <<-CRYSTAL
          def foo(a)
            case a
            when /foo/
              a = 1
            when /bar/
              a = 2
            end
          end
          CRYSTAL
        dead_store_names(scope).should eq ["a", "a"]
      end

      it "does not report assignment used in case condition" do
        scope = def_scope <<-CRYSTAL
          def foo
            a = 2
            case a
            when /foo/
            end
          end
          CRYSTAL
        dead_store_names(scope).should be_empty
      end

      context "when" do
        it "does not report when assignment in when condition is used" do
          scope = def_scope <<-CRYSTAL
            def foo(a)
              case
              when a = foo_call
              when a = bar_call
              end
              puts a
            end
            CRYSTAL
          dead_store_names(scope).should be_empty
        end

        it "reports when assignment in when condition is not used" do
          scope = def_scope <<-CRYSTAL
            def foo(a)
              case
              when a = foo_call
              when a = bar_call
              end
            end
            CRYSTAL
          dead_store_names(scope).should eq ["a", "a"]
        end
      end
    end

    context "multi assignments" do
      it "does not report when all targets are used" do
        scope = def_scope <<-CRYSTAL
          def foo
            a, b = {1, 2}
            a + b
          end
          CRYSTAL
        dead_store_names(scope).should be_empty
      end

      it "reports unused multi-assign target" do
        scope = def_scope <<-CRYSTAL
          def foo
            a, b = {1, 2}
            a
          end
          CRYSTAL
        dead_store_names(scope).should eq ["b"]
      end

      it "reports all unused multi-assign targets" do
        scope = def_scope <<-CRYSTAL
          def foo
            a, b = {1, 2}
          end
          CRYSTAL
        dead_store_names(scope).should eq ["b", "a"]
      end

      it "reports reassigned multi-assign targets" do
        scope = def_scope <<-CRYSTAL
          def foo
            a, b = {1, 2}
            a, b = {3, 4}
          end
          CRYSTAL
        dead_store_names(scope).should eq ["b", "a", "b", "a"]
      end

      it "reports multi-assign target overwritten at loop start" do
        scope = def_scope <<-CRYSTAL
          def foo
            while true
              word = get_word
              if (word & 0xFF) == 0
                word, success = compare_and_set(word, word + 1)
                return if success
              end
            end
          end
          CRYSTAL
        dead_store_names(scope).should eq ["word"]
      end

      it "does not report multi-assign target used in next loop iteration" do
        scope = def_scope <<-CRYSTAL
          def foo
            while true
              word = get_word
              if (word & 0xFF) == 0
                word, success = compare_and_set(word, word + 1)
                if success
                  return
                end
              else
                puts word
              end
            end
            word
          end
          CRYSTAL
        dead_store_names(scope).should be_empty
      end
    end

    context "top level scope" do
      it "detects dead stores at top level" do
        scope = top_scope <<-CRYSTAL
          a = 1
          a = 2
          CRYSTAL
        dead_store_names(scope).should eq ["a", "a"]
      end

      it "does not report referenced top-level assignments" do
        scope = top_scope <<-CRYSTAL
          a = 1
          a += 1
          a
          CRYSTAL
        dead_store_names(scope).should be_empty
      end
    end

    context "type declarations" do
      it "does not report unused type declaration without value" do
        scope = def_scope <<-CRYSTAL
          def foo
            a : String?
          end
          CRYSTAL
        dead_store_names(scope).should be_empty
      end

      it "reports unused type declaration with value" do
        scope = def_scope <<-CRYSTAL
          def foo
            a : String? = "foo"
          end
          CRYSTAL
        dead_store_names(scope).should eq ["a"]
      end

      it "does not report used type declaration" do
        scope = def_scope <<-CRYSTAL
          def foo
            a : String?
            a
          end
          CRYSTAL
        dead_store_names(scope).should be_empty
      end
    end

    context "uninitialized" do
      it "reports unused uninitialized assignment" do
        scope = def_scope <<-CRYSTAL
          def foo
            a = uninitialized UInt8
          end
          CRYSTAL
        dead_store_names(scope).should eq ["a"]
      end

      it "does not report used uninitialized assignment" do
        scope = def_scope <<-CRYSTAL
          def foo
            a = uninitialized UInt8
            a
          end
          CRYSTAL
        dead_store_names(scope).should be_empty
      end
    end
    context "super and previous_def" do
      it "treats bare super as reading all arguments" do
        scope = def_scope <<-CRYSTAL
          def foo(a, b)
            a = super
            a
          end
          CRYSTAL
        dead_store_names(scope).should be_empty
      end

      it "treats bare previous_def as reading all arguments" do
        scope = def_scope <<-CRYSTAL
          def foo(a)
            a = previous_def
            a
          end
          CRYSTAL
        dead_store_names(scope).should be_empty
      end

      it "does not treat super() with parens as reading arguments" do
        scope = def_scope <<-CRYSTAL
          def foo(a)
            b = super()
          end
          CRYSTAL
        dead_store_names(scope).should eq ["b"]
      end

      it "does not treat super with explicit args as reading all arguments" do
        scope = def_scope <<-CRYSTAL
          def foo(a)
            b = super(1)
          end
          CRYSTAL
        dead_store_names(scope).should eq ["b"]
      end
    end
  end
end


================================================
FILE: spec/ameba/ast/scope_spec.cr
================================================
require "../../spec_helper"

module Ameba::AST
  describe Scope do
    describe "#initialize" do
      source = "a = 2"

      it "assigns outer scope" do
        root = Scope.new as_node(source)
        child = Scope.new as_node(source), root
        child.outer_scope.should_not be_nil
      end

      it "assigns node" do
        scope = Scope.new as_node(source)
        scope.node.should_not be_nil
      end
    end
  end

  describe "delegation" do
    it "delegates to_s to node" do
      node = as_node("def foo; end")
      scope = Scope.new node
      scope.to_s.should eq node.to_s
    end

    it "delegates locations to node" do
      node = as_node("def foo; end")
      scope = Scope.new node
      scope.location.should eq node.location
      scope.end_location.should eq node.end_location
    end
  end

  describe "#references" do
    it "can return an empty list of references" do
      scope = Scope.new as_node("")
      scope.references.should be_empty
    end

    it "allows to add variable references" do
      scope = Scope.new as_node("")
      nodes = as_nodes "a = 2"
      scope.references << Reference.new(nodes.var_nodes.first, scope)
      scope.references.size.should eq 1
    end
  end

  describe "#references?" do
    it "returns true if current scope references variable" do
      nodes = as_nodes <<-CRYSTAL
        def method
          a = 2
          block do
            3.times { |i| a = a + i }
          end
        end
        CRYSTAL

      var_node = nodes.var_nodes.first

      scope = Scope.new nodes.def_nodes.first
      scope.add_variable(var_node)
      scope.inner_scopes << Scope.new(nodes.block_nodes.first, scope)

      variable = Variable.new(var_node, scope)
      variable.reference(nodes.var_nodes.first, scope.inner_scopes.first)

      scope.references?(variable).should be_true
    end

    it "returns false if inner scopes are not checked" do
      nodes = as_nodes <<-CRYSTAL
        def method
          a = 2
          block do
            3.times { |i| a = a + i }
          end
        end
        CRYSTAL

      var_node = nodes.var_nodes.first

      scope = Scope.new nodes.def_nodes.first
      scope.add_variable(var_node)
      scope.inner_scopes << Scope.new(nodes.block_nodes.first, scope)

      variable = Variable.new(var_node, scope)
      variable.reference(nodes.var_nodes.first, scope.inner_scopes.first)

      scope.references?(variable, check_inner_scopes: false).should be_false
    end

    it "returns false if current scope does not reference variable" do
      nodes = as_nodes <<-CRYSTAL
        def method
          a = 2
          block do
            b = 3
            3.times { |i| b = b + i }
          end
        end
        CRYSTAL

      var_node = nodes.var_nodes.first

      scope = Scope.new nodes.def_nodes.first
      scope.add_variable(var_node)
      scope.inner_scopes << Scope.new(nodes.block_nodes.first, scope)

      variable = Variable.new(var_node, scope)

      scope.inner_scopes.first.references?(variable).should be_false
    end
  end

  describe "#add_variable" do
    it "adds a new variable to the scope" do
      scope = Scope.new as_node("")
      scope.add_variable(Crystal::Var.new "foo")
      scope.variables.empty?.should be_false
    end
  end

  describe "#find_variable" do
    it "returns the variable in the scope by name" do
      scope = Scope.new as_node("foo = 1")
      scope.add_variable(Crystal::Var.new "foo")
      scope.find_variable("foo").should_not be_nil
    end

    it "returns nil if variable not exist in this scope" do
      scope = Scope.new as_node("foo = 1")
      scope.find_variable("bar").should be_nil
    end
  end

  describe "#assign_variable" do
    it "creates a new assignment" do
      scope = Scope.new as_node("foo = 1")
      scope.add_variable(Crystal::Var.new "foo")
      scope.assign_variable("foo", Crystal::Var.new "foo")
      var = scope.find_variable("foo").should_not be_nil
      var.assignments.size.should eq 1
    end

    it "does not create the assignment if variable is wrong" do
      scope = Scope.new as_node("foo = 1")
      scope.add_variable(Crystal::Var.new "foo")
      scope.assign_variable("bar", Crystal::Var.new "bar")
      var = scope.find_variable("foo").should_not be_nil
      var.assignments.size.should eq 0
    end
  end

  describe "#block?" do
    it "returns true if Crystal::Block" do
      nodes = as_nodes("3.times {}")
      scope = Scope.new nodes.block_nodes.first
      scope.block?.should be_true
    end

    it "returns false otherwise" do
      scope = Scope.new as_node("a = 1")
      scope.block?.should be_false
    end
  end

  describe "#spawn_block?" do
    it "returns true if a node is a spawn block" do
      nodes = as_nodes("spawn {}")
      scope = Scope.new nodes.block_nodes.first
      scope.spawn_block?.should be_true
    end

    it "returns false otherwise" do
      scope = Scope.new as_node("a = 1")
      scope.spawn_block?.should be_false
    end
  end

  describe "#def?" do
    context "when check_outer_scopes: true" do
      it "returns true if outer scope is Crystal::Def" do
        nodes = as_nodes("def foo; 3.times {}; end")
        outer_scope = Scope.new nodes.def_nodes.first
        scope = Scope.new nodes.block_nodes.first, outer_scope
        scope.def?(check_outer_scopes: true).should be_true
        scope.def?.should be_false
      end
    end

    it "returns true if Crystal::Def" do
      nodes = as_nodes("def foo; end")
      scope = Scope.new nodes.def_nodes.first
      scope.def?.should be_true
    end

    it "returns false otherwise" do
      scope = Scope.new as_node("a = 1")
      scope.def?.should be_false
    end
  end

  describe "#in_macro?" do
    it "returns true if Crystal::Macro" do
      nodes = as_nodes <<-CRYSTAL
        macro included
        end
        CRYSTAL
      scope = Scope.new nodes.macro_nodes.first
      scope.in_macro?.should be_true
    end

    it "returns true if node is nested to Crystal::Macro" do
      nodes = as_nodes <<-CRYSTAL
        macro included
          {{ @type.each do |type| a = type end }}
        end
        CRYSTAL
      outer_scope = Scope.new nodes.macro_nodes.first
      scope = Scope.new nodes.block_nodes.first, outer_scope
      scope.in_macro?.should be_true
    end

    it "returns false otherwise" do
      scope = Scope.new as_node("a = 1")
      scope.in_macro?.should be_false
    end
  end
end


================================================
FILE: spec/ameba/ast/util_spec.cr
================================================
require "../../spec_helper"

module Ameba::AST
  struct Test
    include Util
  end

  describe Util do
    subject = Test.new

    describe "#literal?" do
      [
        Crystal::ArrayLiteral.new,
        Crystal::BoolLiteral.new(false),
        Crystal::CharLiteral.new('a'),
        Crystal::HashLiteral.new,
        Crystal::NamedTupleLiteral.new,
        Crystal::NilLiteral.new,
        Crystal::NumberLiteral.new(42),
        Crystal::RegexLiteral.new(Crystal::StringLiteral.new("")),
        Crystal::StringLiteral.new(""),
        Crystal::SymbolLiteral.new(""),
        Crystal::TupleLiteral.new([] of Crystal::ASTNode),
        Crystal::RangeLiteral.new(
          Crystal::NilLiteral.new,
          Crystal::NilLiteral.new,
          true),
      ].each do |literal|
        it "returns true if node is #{literal}" do
          subject.literal?(literal).should be_true
        end
      end

      it "returns false if node is not a literal" do
        subject.literal?(Crystal::Nop).should be_false
      end
    end

    describe "#static/dynamic_literal?" do
      [
        Crystal::ArrayLiteral.new,
        Crystal::ArrayLiteral.new([
          Crystal::StringLiteral.new("foo"),
        ] of Crystal::ASTNode),
        Crystal::BoolLiteral.new(false),
        Crystal::CharLiteral.new('a'),
        Crystal::HashLiteral.new,
        Crystal::NamedTupleLiteral.new,
        Crystal::NilLiteral.new,
        Crystal::NumberLiteral.new(42),
        Crystal::RegexLiteral.new(Crystal::StringLiteral.new("foo")),
        Crystal::RegexLiteral.new(Crystal::StringInterpolation.new([
          Crystal::StringLiteral.new("foo"),
        ] of Crystal::ASTNode)),
        Crystal::StringLiteral.new("foo"),
        Crystal::StringInterpolation.new([
          Crystal::StringLiteral.new("foo"),
        ] of Crystal::ASTNode),
        Crystal::SymbolLiteral.new("foo"),
        Crystal::TupleLiteral.new([] of Crystal::ASTNode),
        Crystal::TupleLiteral.new([
          Crystal::StringLiteral.new("foo"),
        ] of Crystal::ASTNode),
        Crystal::RangeLiteral.new(
          Crystal::NumberLiteral.new(0),
          Crystal::NumberLiteral.new(10),
          true),
      ].each do |literal|
        it "properly identifies static node #{literal}" do
          subject.static_literal?(literal).should be_true
          subject.dynamic_literal?(literal).should be_false
        end
      end

      [
        Crystal::StringInterpolation.new([Crystal::Path.new(%w[Foo])] of Crystal::ASTNode),
        Crystal::ArrayLiteral.new([Crystal::Path.new(%w[Foo])] of Crystal::ASTNode),
        Crystal::TupleLiteral.new([Crystal::Path.new(%w[Foo])] of Crystal::ASTNode),
        Crystal::RegexLiteral.new(Crystal::StringInterpolation.new([
          Crystal::StringLiteral.new("foo"),
          Crystal::Path.new(%w[Foo]),
        ] of Crystal::ASTNode)),
        Crystal::RangeLiteral.new(
          Crystal::Path.new(%w[Foo]),
          Crystal::NumberLiteral.new(10),
          true),
        Crystal::RangeLiteral.new(
          Crystal::NumberLiteral.new(10),
          Crystal::Path.new(%w[Foo]),
          true),
      ].each do |literal|
        it "properly identifies dynamic node #{literal}" do
          subject.dynamic_literal?(literal).should be_true
          subject.static_literal?(literal).should be_false
        end
      end
    end

    describe "#node_source" do
      it "returns original source of the node" do
        s = <<-CRYSTAL
          a = 1
          CRYSTAL
        node = Crystal::Parser.new(s).parse
        source = subject.node_source node, s.split("\n")
        source.should eq "a = 1"
      end

      it "returns original source of multiline node" do
        s = <<-CRYSTAL
          if ()
            :ok
          end
          CRYSTAL
        node = Crystal::Parser.new(s).parse
        source = subject.node_source node, s.split("\n")
        source.should eq <<-CRYSTAL
          if ()
            :ok
          end
          CRYSTAL
      end

      it "does not report source of node which has incorrect location" do
        s = <<-CRYSTAL
          module MyModule
            macro conditional_error_for_inline_callbacks
              {%
                raise ""
              %}
            end

            macro before_save(x = nil)
            end
          end
          CRYSTAL
        node = as_nodes(s).nil_literal_nodes.first
        source = subject.node_source node, s.split("\n")
        source.should eq "nil"
      end
    end

    describe "#flow_command?" do
      it "returns true if this is return" do
        node = as_node("return 22")
        subject.flow_command?(node, false).should be_true
      end

      it "returns true if this is a break in a loop" do
        node = as_node("break")
        subject.flow_command?(node, true).should be_true
      end

      it "returns false if this is a break out of loop" do
        node = as_node("break")
        subject.flow_command?(node, false).should be_false
      end

      it "returns true if this is a next in a loop" do
        node = as_node("next")
        subject.flow_command?(node, true).should be_true
      end

      it "returns false if this is a next out of loop" do
        node = as_node("next")
        subject.flow_command?(node, false).should be_false
      end

      it "returns true if this is raise" do
        node = as_node("raise e")
        subject.flow_command?(node, false).should be_true
      end

      it "returns true if this is exit" do
        node = as_node("exit")
        subject.flow_command?(node, false).should be_true
      end

      it "returns true if this is abort" do
        node = as_node("abort")
        subject.flow_command?(node, false).should be_true
      end

      it "returns false otherwise" do
        node = as_node("foobar")
        subject.flow_command?(node, false).should be_false
      end
    end

    describe "#flow_expression?" do
      it "returns true if this is a flow command" do
        node = as_node("return")
        subject.flow_expression?(node, true).should be_true
      end

      it "returns true if this is if-else consumed by flow expressions" do
        node = as_node <<-CRYSTAL
          if foo
            return :foo
          else
            return :bar
          end
          CRYSTAL
        subject.flow_expression?(node, false).should be_true
      end

      it "returns true if this is unless-else consumed by flow expressions" do
        node = as_node <<-CRYSTAL
          unless foo
            return :foo
          else
            return :bar
          end
          CRYSTAL
        subject.flow_expression?(node).should be_true
      end

      it "returns true if this is case consumed by flow expressions" do
        node = as_node <<-CRYSTAL
          case
          when 1
            return 1
          when 2
            return 2
          else
            return 3
          end
          CRYSTAL
        subject.flow_expression?(node).should be_true
      end

      it "returns true if this is select consumed by flow expressions" do
        node = as_node <<-CRYSTAL
          select
          when a = foo
            return 1
          when a = bar
            return 2
          else
            return 3
          end
          CRYSTAL
        subject.flow_expression?(node).should be_true
      end

      it "returns true if this is exception handler consumed by flow expressions" do
        node = as_node <<-CRYSTAL
          begin
            raise "exp"
          rescue ex
            return ex
          end
          CRYSTAL
        subject.flow_expression?(node).should be_true
      end

      it "returns true if this while consumed by flow expressions" do
        node = as_node <<-CRYSTAL
          while true
            return
          end
          CRYSTAL
        subject.flow_expression?(node).should be_true
      end

      it "returns false if this while with break" do
        node = as_node <<-CRYSTAL
          while true
            break
          end
          CRYSTAL
        subject.flow_expression?(node).should be_false
      end

      it "returns true if this until consumed by flow expressions" do
        node = as_node <<-CRYSTAL
          until false
            return
          end
          CRYSTAL
        subject.flow_expression?(node).should be_true
      end

      it "returns false if this until with break" do
        node = as_node <<-CRYSTAL
          until false
            break
          end
          CRYSTAL
        subject.flow_expression?(node).should be_false
      end

      it "returns true if this expressions consumed by flow expressions" do
        node = as_node <<-CRYSTAL
          exp1
          exp2
          return
          CRYSTAL
        subject.flow_expression?(node).should be_true
      end

      it "returns false otherwise" do
        node = as_node <<-CRYSTAL
          exp1
          exp2
          CRYSTAL
        subject.flow_expression?(node).should be_false
      end
    end

    describe "#suffix?" do
      it "returns true if the node is a suffix `if`" do
        node = as_node("foo if bar")
        subject.suffix?(node).should be_true
      end

      it "returns true if the node is a suffix `if` (2)" do
        node = as_node("foo if bar.end")
        subject.suffix?(node).should be_true
      end

      it "returns true if the node is a suffix `unless`" do
        node = as_node("foo unless bar")
        subject.suffix?(node).should be_true
      end

      it "returns true if the node is a suffix `rescue`" do
        node = as_node("foo rescue bar")
        subject.suffix?(node).should be_true
      end

      it "returns true if the node is a suffix `ensure`" do
        node = as_node("foo ensure bar")
        subject.suffix?(node).should be_true
      end

      it "returns false if the node is not a suffix `if` or `unless`" do
        node = as_node("foo")
        subject.suffix?(node).should be_false
      end

      it "returns false if the node is a ternary `if`" do
        node = as_node("foo ? bar : baz")
        subject.suffix?(node).should be_false
      end

      it "returns false if the node is a non-suffix `if`" do
        node = as_node("if foo; bar; end")
        subject.suffix?(node).should be_false
      end

      it "returns false if the node is a non-suffix `if` (2)" do
        node = as_node("if foo;bar;end")
        subject.suffix?(node).should be_false
      end

      it "returns false if the node is a non-suffix `if` (3)" do
        node = as_node <<-CRYSTAL
          if foo
            bar
          end
          CRYSTAL
        subject.suffix?(node).should be_false
      end

      it "returns false if the node is a non-suffix `unless`" do
        node = as_node("unless foo; bar; end")
        subject.suffix?(node).should be_false
      end

      it "returns false if the node is a non-suffix `rescue`" do
        node = as_node("begin; foo; rescue; end")
        subject.suffix?(node).should be_false
      end

      it "returns false if the node is a non-suffix `ensure`" do
        node = as_node("begin; foo; ensure; end")
        subject.suffix?(node).should be_false
      end
    end

    describe "#has_short_block?" do
      it "returns true if the node has a short block variant" do
        source = Source.new "foo :bar, &.baz"
        node = as_node(source.code)
        subject.has_short_block?(node, source.lines).should be_true
      end

      it "returns false if the node has a one line block" do
        source = Source.new "foo :bar { |x| x.baz? }"
        node = as_node(source.code)
        subject.has_short_block?(node, source.lines).should be_false
      end

      it "returns false if the node does not have a short block variant" do
        source = Source.new "foo :bar { |x| x.baz? }"
        node = as_node(source.code)
        subject.has_short_block?(node, source.lines).should be_false
      end

      it "returns false if the node does not have a block" do
        source = Source.new "foo :bar"
        node = as_node(source.code)
        subject.has_short_block?(node, source.lines).should be_false
      end
    end

    describe "#has_block?" do
      it "returns true if the node has a block" do
        node = as_node("%w[foo bar].first { :baz }")
        subject.has_block?(node).should be_true
      end

      it "returns true if the node has a block (shorthand)" do
        node = as_node("%w[foo bar].find(&.empty?)")
        subject.has_block?(node).should be_true
      end

      it "returns true if the node has a block (block argument)" do
        node = as_node("%w[foo bar].find(&block)")
        subject.has_block?(node).should be_true
      end

      it "returns true if the node has a block (forwarded block argument)" do
        node = as_node("%w[foo bar].find(&->foo)")
        subject.has_block?(node).should be_true
      end

      it "returns false if the node does not have a block" do
        node = as_node("%w[foo bar].first")
        subject.has_block?(node).should be_false
      end
    end

    describe "#has_arguments?" do
      it "returns false if the node has no arguments" do
        node = as_node("foo.bar")
        subject.has_arguments?(node).should be_false
      end

      it "returns true if the node has positional arguments" do
        node = as_node("foo.bar(1)")
        subject.has_arguments?(node).should be_true
      end

      it "returns true if the node has named arguments" do
        node = as_node("foo.bar(baz: 1)")
        subject.has_arguments?(node).should be_true
      end

      it "returns true if the node has splat arguments" do
        node = as_node("foo.bar(*baz)")
        subject.has_arguments?(node).should be_true
      end

      it "returns true if the node has double splat arguments" do
        node = as_node("foo.bar(**baz)")
        subject.has_arguments?(node).should be_true
      end
    end

    describe "#takes_arguments?" do
      it "returns false if the node takes no arguments" do
        node = as_node("def foo; end")
        subject.takes_arguments?(node).should be_false
      end

      it "returns true if the node takes positional arguments" do
        node = as_node("def foo(bar); end")
        subject.takes_arguments?(node).should be_true
      end

      it "returns true if the node takes named arguments" do
        node = as_node("def foo(*, bar); end")
        subject.takes_arguments?(node).should be_true
      end

      it "returns true if the node has splat index" do
        node = as_node("def foo(*args); end")
        subject.takes_arguments?(node).should be_true
      end

      it "returns true if the node has double splat" do
        node = as_node("def foo(**kwargs); end")
        subject.takes_arguments?(node).should be_true
      end
    end

    describe "#raise?" do
      it "returns true if this is a raise method call" do
        node = as_node "raise e"
        subject.raise?(node).should be_true
      end

      it "returns false if it has a receiver" do
        node = as_node "obj.raise e"
        subject.raise?(node).should be_false
      end

      it "returns false if size of the arguments doesn't match" do
        node = as_node "raise"
        subject.raise?(node).should be_false
      end
    end

    describe "#exit?" do
      it "returns true if this is a exit method call" do
        node = as_node "exit"
        subject.exit?(node).should be_true
      end

      it "returns true if this is a exit method call with one argument" do
        node = as_node "exit 1"
        subject.exit?(node).should be_true
      end

      it "returns false if it has a receiver" do
        node = as_node "obj.exit"
        subject.exit?(node).should be_false
      end

      it "returns false if size of the arguments doesn't match" do
        node = as_node "exit 1, 1"
        subject.exit?(node).should be_false
      end
    end

    describe "#abort?" do
      it "returns true if this is an abort method call" do
        node = as_node "abort"
        subject.abort?(node).should be_true
      end

      it "returns true if this is an abort method call with one argument" do
        node = as_node "abort \"message\""
        subject.abort?(node).should be_true
      end

      it "returns true if this is an abort method call with two arguments" do
        node = as_node "abort \"message\", 1"
        subject.abort?(node).should be_true
      end

      it "returns false if it has a receiver" do
        node = as_node "obj.abort"
        subject.abort?(node).should be_false
      end

      it "returns false if size of the arguments doesn't match" do
        node = as_node "abort 1, 1, 1"
        subject.abort?(node).should be_false
      end
    end

    describe "#loop?" do
      it "returns true if this is a loop method call" do
        node = as_node "loop"
        subject.loop?(node).should be_true
      end

      it "returns false if it has a receiver" do
        node = as_node "obj.loop"
        subject.loop?(node).should be_false
      end

      it "returns false if size of the arguments doesn't match" do
        node = as_node "loop 1"
        subject.loop?(node).should be_false
      end
    end

    describe "#operator_method_name?" do
      it "returns true for operator method names" do
        %w[+ - * / == != ~= !~ <=>].each do |op|
          subject.operator_method_name?(op).should be_true
        end
      end

      it "returns false for non-operator method names" do
        %w[foo? foo! ->].each do |op|
          subject.operator_method_name?(op).should be_false
        end
      end
    end

    describe "#operator_method?" do
      it "returns true for operator method definitions" do
        node = as_node "def +(other); end"
        subject.operator_method?(node).should be_true
      end

      it "returns false for other method definitions" do
        node = as_node "def method; end"
        subject.operator_method?(node).should be_false
      end

      it "returns true for operator method calls" do
        node = as_node "obj + 1"
        subject.operator_method?(node).should be_true
      end

      it "returns false for other method calls" do
        node = as_node "obj.method"
        subject.operator_method?(node).should be_false
      end

      it "returns false for procs" do
        node = as_node "-> { nil }"
        subject.operator_method?(node).should be_false
      end
    end

    describe "#setter_method_name?" do
      it "returns true for setter method names" do
        %w[foo= []=].each do |op|
          subject.setter_method_name?(op).should be_true
        end
      end

      it "returns false for operator method names" do
        %w[== !=].each do |op|
          subject.setter_method_name?(op).should be_false
        end
      end

      it "returns false for non-operator method names" do
        %w[foo foo? foo!].each do |op|
          subject.setter_method_name?(op).should be_false
        end
      end
    end

    describe "#setter_method?" do
      it "returns true for setter method definitions" do
        node = as_node "def foo=(@foo); end"
        subject.setter_method?(node).should be_true
      end

      it "returns false for other method definitions" do
        node = as_node "def foo; end"
        subject.setter_method?(node).should be_false
      end

      it "returns true for setter method calls" do
        node = as_node "foo.bar = 123"
        subject.setter_method?(node).should be_true
      end

      it "returns false for regular method calls" do
        node = as_node "obj.method"
        subject.setter_method?(node).should be_false
      end

      it "returns false for procs" do
        node = as_node "-> { nil }"
        subject.setter_method?(node).should be_false
      end
    end

    describe "#nodoc?" do
      it "returns true if a node has a single `:nodoc:` annotation" do
        node = as_node <<-CRYSTAL, wants_doc: true
          # :nodoc:
          def foo; end
          CRYSTAL

        subject.nodoc?(node).should be_true
      end

      it "returns true if a node has a `:nodoc:` annotation in the first line" do
        node = as_node <<-CRYSTAL, wants_doc: true
          # :nodoc:
          #
          # foo
          def foo; end
          CRYSTAL

        subject.nodoc?(node).should be_true
      end

      it "returns false if a node has a `:nodoc:` annotation in the middle" do
        node = as_node <<-CRYSTAL, wants_doc: true
          # foo
          # :nodoc:
          # bar
          def foo; end
          CRYSTAL

        subject.nodoc?(node).should be_false
      end
    end

    describe "#heredoc?" do
      it "returns true if a node is a heredoc string" do
        source = Source.new <<-CRYSTAL
          <<-FOO
            This is a heredoc
            FOO
          CRYSTAL

        subject.heredoc?(source.ast, source).should be_true
      end

      it "returns true if a node is a heredoc string interpolation" do
        source = Source.new <<-'CRYSTAL'
          <<-FOO
            This is a heredoc #{1 + 2}
            FOO
          CRYSTAL

        subject.heredoc?(source.ast, source).should be_true
      end

      it "returns false if a node is a regular string interpolation" do
        source = Source.new <<-'CRYSTAL'
          "This is not a heredoc #{1 + 2}"
          CRYSTAL

        subject.heredoc?(source.ast, source).should be_false
      end
    end

    describe "#control_exp_code" do
      it "returns the exp code of a control expression" do
        s = "return 1"
        node = as_node(s).as Crystal::ControlExpression
        exp_code = subject.control_exp_code node, [s]
        exp_code.should eq "1"
      end

      it "wraps implicit tuple literal with curly brackets" do
        s = "return 1, 2"
        node = as_node(s).as Crystal::ControlExpression
        exp_code = subject.control_exp_code node, [s]
        exp_code.should eq "{1, 2}"
      end

      it "accepts explicit tuple literal" do
        s = "return {1, 2}"
        node = as_node(s).as Crystal::ControlExpression
        exp_code = subject.control_exp_code node, [s]
        exp_code.should eq "{1, 2}"
      end
    end

    describe "#name_location_or" do
      it "adjusts location column number by a given value" do
        node = as_node("def foo; end").as Crystal::Def
        subject.name_location_or(node, adjust_location_column_number: 10).to_s
          .should eq "{:1:15, :1:17}"
      end

      it "works on method call" do
        node = as_node("def foo; end").as Crystal::Def
        subject.name_location_or(node).to_s.should eq "{:1:5, :1:7}"
      end

      it "works on class definition" do
        node = as_node("class Foo; end").as Crystal::ClassDef
        subject.name_location_or(node).to_s.should eq "{:1:7, :1:9}"
      end

      it "works on module definition" do
        node = as_node("module Foo; end").as Crystal::ModuleDef
        subject.name_location_or(node).to_s.should eq "{:1:8, :1:10}"
      end
    end

    describe "#name_end_location" do
      it "works on method call" do
        node = as_node("name(foo)").as Crystal::Call
        subject.name_end_location(node).to_s.should eq ":1:4"
      end

      it "works on method definition" do
        node = as_node("def name; end").as Crystal::Def
        subject.name_end_location(node).to_s.should eq ":1:8"
      end

      it "works on macro definition" do
        node = as_node("macro name; end").as Crystal::Macro
        subject.name_end_location(node).to_s.should eq ":1:10"
      end

      it "works on class definition" do
        node = as_node("class Name; end").as Crystal::ClassDef
        subject.name_end_location(node).to_s.should eq ":1:10"
      end

      it "works on module definition" do
        node = as_node("module Name; end").as Crystal::ModuleDef
        subject.name_end_location(node).to_s.should eq ":1:11"
      end

      it "works on annotation definition" do
        node = as_node("annotation Name; end").as Crystal::AnnotationDef
        subject.name_end_location(node).to_s.should eq ":1:15"
      end

      it "works on enum definition" do
        node = as_node("enum Name; end").as Crystal::EnumDef
        subject.name_end_location(node).to_s.should eq ":1:9"
      end

      it "works on alias definition" do
        node = as_node("alias Name = Foo").as Crystal::Alias
        subject.name_end_location(node).to_s.should eq ":1:10"
      end

      it "works on generic" do
        node = as_node("Name(Foo)").as Crystal::Generic
        subject.name_end_location(node).to_s.should eq ":1:4"
      end

      it "works on include" do
        node = as_node("include Name").as Crystal::Include
        subject.name_end_location(node).to_s.should eq ":1:12"
      end

      it "works on extend" do
        node = as_node("extend Name").as Crystal::Extend
        subject.name_end_location(node).to_s.should eq ":1:11"
      end

      it "works on variable type declaration" do
        node = as_node("name : Foo").as Crystal::TypeDeclaration
        subject.name_end_location(node).to_s.should eq ":1:4"
      end

      it "works on uninitialized variable" do
        node = as_node("name = uninitialized Foo").as Crystal::UninitializedVar
        subject.name_end_location(node).to_s.should eq ":1:4"
      end

      it "works on lib definition" do
        node = as_node("lib Name; end").as Crystal::LibDef
        subject.name_end_location(node).to_s.should eq ":1:8"
      end

      it "works on lib type definition" do
        node = as_node("lib Foo; type Name = Bar; end").as(Crystal::LibDef).body
        node.class.should eq Crystal::TypeDef
        subject.name_end_location(node).to_s.should eq ":1:18"
      end

      it "works on metaclass" do
        node = as_node("foo : Name.class").as(Crystal::TypeDeclaration).declared_type
        node.class.should eq Crystal::Metaclass
        subject.name_end_location(node).to_s.should eq ":1:10"
      end
    end
  end
end


================================================
FILE: spec/ameba/ast/variabling/argument_spec.cr
================================================
require "../../../spec_helper"

module Ameba::AST
  describe Argument do
    arg = Crystal::Arg.new "a"
    scope = Scope.new as_node "foo = 1"
    variable = Variable.new(Crystal::Var.new("foo"), scope)

    describe "#initialize" do
      it "creates a new argument" do
        argument = Argument.new(arg, variable)
        argument.node.should_not be_nil
      end
    end

    describe "delegation" do
      it "delegates locations to node" do
        argument = Argument.new(arg, variable)
        argument.location.should eq arg.location
        argument.end_location.should eq arg.end_location
      end

      it "delegates to_s to node" do
        argument = Argument.new(arg, variable)
        argument.to_s.should eq arg.to_s
      end
    end

    describe "#ignored?" do
      it "is true if arg starts with _" do
        argument = Argument.new(Crystal::Arg.new("_a"), variable)
        argument.ignored?.should be_true
      end

      it "is false otherwise" do
        argument = Argument.new(arg, variable)
        argument.ignored?.should be_false
      end
    end
  end
end


================================================
FILE: spec/ameba/ast/variabling/assignment_spec.cr
================================================
require "../../../spec_helper"

module Ameba::AST
  describe Assignment do
    node = Crystal::NilLiteral.new
    scope = Scope.new as_node "foo = 1"
    variable = Variable.new(Crystal::Var.new("foo"), scope)

    describe "#initialize" do
      it "creates a new assignment with node and var" do
        assignment = Assignment.new(node, variable, scope)
        assignment.node.should_not be_nil
      end
    end

    describe "delegation" do
      it "delegates locations" do
        assignment = Assignment.new(node, variable, scope)
        assignment.location.should eq node.location
        assignment.end_location.should eq node.end_location
      end

      it "delegates to_s" do
        assignment = Assignment.new(node, variable, scope)
        assignment.to_s.should eq node.to_s
      end

      it "delegates scope" do
        assignment = Assignment.new(node, variable, scope)
        assignment.scope.should eq variable.scope
      end
    end
  end
end


================================================
FILE: spec/ameba/ast/variabling/reference_spec.cr
================================================
require "../../../spec_helper"

module Ameba::AST
  describe Reference do
    it "is derived from a Variable" do
      node = Crystal::Var.new "foo"
      ref = Reference.new(node, Scope.new as_node "foo = 1")
      ref.should be_a Variable
    end
  end
end


================================================
FILE: spec/ameba/ast/variabling/type_dec_variable_spec.cr
================================================
require "../../../spec_helper"

module Ameba::AST
  describe TypeDecVariable do
    var = Crystal::Var.new("foo")
    declared_type = Crystal::Path.new("String")
    type_dec = Crystal::TypeDeclaration.new(var, declared_type)

    describe "#initialize" do
      it "creates a new type dec variable" do
        variable = TypeDecVariable.new(type_dec)
        variable.node.should_not be_nil
      end
    end

    describe "#name" do
      it "returns var name" do
        variable = TypeDecVariable.new(type_dec)
        variable.name.should eq var.name
      end

      it "raises if type declaration is incorrect" do
        type_dec = Crystal::TypeDeclaration.new(declared_type, declared_type)

        expect_raises(Exception, "Unsupported var node type: Crystal::Path") do
          TypeDecVariable.new(type_dec).name
        end
      end
    end
  end
end


================================================
FILE: spec/ameba/ast/variabling/variable_spec.cr
================================================
require "../../../spec_helper"

module Ameba::AST
  describe Variable do
    var_node = Crystal::Var.new("foo")
    scope = Scope.new as_node "foo = 1"

    describe "#initialize" do
      it "creates a new variable" do
        variable = Variable.new(var_node, scope)
        variable.node.should_not be_nil
      end
    end

    describe "delegation" do
      it "delegates locations" do
        variable = Variable.new(var_node, scope)
        variable.location.should eq var_node.location
        variable.end_location.should eq var_node.end_location
      end

      it "delegates name" do
        variable = Variable.new(var_node, scope)
        variable.name.should eq var_node.name
      end

      it "delegates to_s" do
        variable = Variable.new(var_node, scope)
        variable.to_s.should eq var_node.to_s
      end
    end

    describe "#special?" do
      it "returns truthy if it is a special `$?` var" do
        variable = Variable.new Crystal::Var.new("$?"), scope
        variable.special?.should be_truthy
      end

      it "returns falsey otherwise" do
        variable = Variable.new Crystal::Var.new("a"), scope
        variable.special?.should be_falsey
      end
    end

    describe "#assign" do
      assign_node = as_node("foo=1")

      it "assigns the variable (creates a new assignment)" do
        variable = Variable.new(var_node, scope)
        variable.assign(assign_node, scope)
        variable.assignments.empty?.should be_false
      end

      it "can create multiple assignments" do
        variable = Variable.new(var_node, scope)
        variable.assign(assign_node, scope)
        variable.assign(assign_node, scope)
        variable.assignments.size.should eq 2
      end
    end

    describe "#reference" do
      it "references the existed assignment" do
        variable = Variable.new(var_node, scope)
        variable.assign(as_node("foo=1"), scope)
        variable.reference(var_node, scope)
        variable.references.empty?.should be_false
      end

      it "adds a reference to the scope" do
        scope = Scope.new as_node "foo = 1"
        variable = Variable.new(var_node, scope)
        variable.assign(as_node("foo=1"), scope)
        variable.reference(var_node, scope)
        scope.references.size.should eq 1
        scope.references.first.node.to_s.should eq "foo"
      end
    end

    describe "#captured_by_block?" do
      it "returns truthy if the variable is captured by block" do
        nodes = as_nodes <<-CRYSTAL
          def method
            a = 2
            3.times { |i| a = a + i }
          end
          CRYSTAL

        var_node = nodes.var_nodes.first

        scope = Scope.new(nodes.def_nodes.first)
        scope.add_variable(var_node)
        scope.inner_scopes << Scope.new(nodes.block_nodes.first, scope)

        variable = Variable.new(var_node, scope)
        variable.reference(nodes.var_nodes.last, scope.inner_scopes.last)

        variable.captured_by_block?.should be_truthy
      end

      it "returns falsy if the variable is not captured by the block" do
        scope = Scope.new as_node <<-CRYSTAL
          def method
            a = 1
          end
          CRYSTAL

        scope.add_variable(Crystal::Var.new "a")
        variable = scope.variables.first

        variable.captured_by_block?.should be_falsey
      end
    end

    describe "#target_of?" do
      it "returns true if the variable is a target of Crystal::Assign node" do
        assign_node = as_nodes("foo=1").assign_nodes.last
        variable = Variable.new assign_node.target.as(Crystal::Var), scope
        variable.target_of?(assign_node).should be_true
      end

      it "returns true if the variable is a target of Crystal::OpAssign node" do
        assign_node = as_nodes("foo=1;foo+=1").op_assign_nodes.last
        variable = Variable.new assign_node.target.as(Crystal::Var), scope
        variable.target_of?(assign_node).should be_true
      end

      it "returns true if the variable is a target of Crystal::MultiAssign node" do
        assign_node = as_nodes("a,b,c={1,2,3}").multi_assign_nodes.last
        assign_node.targets.size.should_not eq 0
        assign_node.targets.each do |target|
          variable = Variable.new target.as(Crystal::Var), scope
          variable.target_of?(assign_node).should be_true
        end
      end

      it "returns false if the node is not assign" do
        variable = Variable.new(Crystal::Var.new("v"), scope)
        variable.target_of?(as_node "nil").should be_false
      end

      it "returns false if the variable is not a target of the assign" do
        variable = Variable.new(Crystal::Var.new("foo"), scope)
        variable.target_of?(as_node("bar = 1")).should be_false
      end
    end

    describe "#ignored?" do
      it "is true if variable is ignored" do
        variable = Variable.new(Crystal::Var.new("_var"), scope)
        variable.ignored?.should be_true
      end

      it "is false if variable is not ignored" do
        variable = Variable.new(Crystal::Var.new("v_ar"), scope)
        variable.ignored?.should be_false
      end

      it "is true if variable is a black hole" do
        variable = Variable.new(Crystal::Var.new("_"), scope)
        variable.ignored?.should be_true
      end
    end

    describe "#eql?" do
      var = Crystal::Var.new("foo").at(Crystal::Location.new(nil, 1, 2))
      variable = Variable.new var, scope

      it "is false if node is not a Crystal::Var" do
        variable.eql?(as_node("nil")).should be_false
      end

      it "is false if node name is different" do
        variable.eql?(Crystal::Var.new "bar").should be_false
      end

      it "is false if node has a different location" do
        variable.eql?(Crystal::Var.new "foo").should be_false
      end

      it "is true otherwise" do
        variable.eql?(variable.node).should be_true
      end
    end

    describe "#declared_before?" do
      it "is falsey if variable doesn't have location" do
        var1 = Crystal::Var.new("foo")
        var2 = Crystal::Var.new("bar").at(Crystal::Location.new(nil, 1, 2))
        Variable.new(var1, scope).declared_before?(var2).should be_falsey
      end

      it "is falsey if node doesn't have location" do
        var1 = Crystal::Var.new("foo").at(Crystal::Location.new(nil, 1, 2))
        var2 = Crystal::Var.new("bar")
        Variable.new(var1, scope).declared_before?(var2).should be_falsey
      end

      it "is true if var's line_number below the node" do
        var1 = Crystal::Var.new("foo").at(Crystal::Location.new(nil, 1, 2))
        var2 = Crystal::Var.new("bar").at(Crystal::Location.new(nil, 2, 2))
        Variable.new(var1, scope).declared_before?(var2).should be_true
      end

      it "is true if var's column_number is after the node" do
        var1 = Crystal::Var.new("foo").at(Crystal::Location.new(nil, 1, 2))
        var2 = Crystal::Var.new("bar").at(Crystal::Location.new(nil, 1, 3))
        Variable.new(var1, scope).declared_before?(var2).should be_true
      end

      it "is false if var's location is before the node" do
        var1 = Crystal::Var.new("foo").at(Crystal::Location.new(nil, 2, 2))
        var2 = Crystal::Var.new("bar").at(Crystal::Location.new(nil, 1, 3))
        Variable.new(var1, scope).declared_before?(var2).should be_false
      end

      it "is false is the node is the same var" do
        var = Crystal::Var.new("foo").at(Crystal::Location.new(nil, 2, 2))
        Variable.new(var, scope).declared_before?(var).should be_false
      end
    end
  end
end


================================================
FILE: spec/ameba/ast/visitors/counting_visitor_spec.cr
================================================
require "../../../spec_helper"

module Ameba::AST
  describe CountingVisitor do
    describe "#visit" do
      it "allow to visit ASTNode" do
        node = Crystal::Parser.new("").parse
        visitor = CountingVisitor.new node
        node.accept visitor
      end
    end

    describe "#count" do
      it "is 1 for an empty method" do
        node = Crystal::Parser.new("def hello; end").parse
        visitor = CountingVisitor.new node

        visitor.count.should eq 1
      end

      it "is 1 if there is Macro::For" do
        code = <<-CRYSTAL
          def initialize
            {% for c in ALL_NODES %}
              true || false
            {% end %}
          end
          CRYSTAL
        node = Crystal::Parser.new(code).parse
        visitor = CountingVisitor.new node
        visitor.count.should eq 1
      end

      it "is 1 if there is Macro::If" do
        code = <<-CRYSTAL
          def initialize
            {% if foo.bar? %}
              true || false
            {% end %}
          end
          CRYSTAL
        node = Crystal::Parser.new(code).parse
        visitor = CountingVisitor.new node
        visitor.count.should eq 1
      end

      it "increases count for every exhaustive case" do
        code = <<-CRYSTAL
          def hello(a : Int32 | Int64 | Float32 | Float64)
            case a
            in Int32   then "int32"
            in Int64   then "int64"
            in Float32 then "float32"
            in Float64 then "float64"
            end
          end
          CRYSTAL
        node = Crystal::Parser.new(code).parse
        visitor = CountingVisitor.new node
        visitor.count.should eq 2
      end

      {% for pair in [
                       {code: "if true; end", description: "conditional"},
                       {code: "while true; end", description: "while loop"},
                       {code: "until 1 < 2; end", description: "until loop"},
                       {code: "begin; rescue; end", description: "rescue"},
                       {code: "true || false", description: "or"},
                       {code: "true && false", description: "and"},
                       {
                         code:        "a : String | Int32 = 1; case a when true; end",
                         description: "inexhaustive when",
                       },
                       {
                         code:        "select; when a = foo; end",
                         description: "select",
                       },
                     ] %}
        it "increases count for every {{ pair[:description].id }}" do
          node = Crystal::Parser.new("def hello; {{ pair[:code].id }} end").parse
          visitor = CountingVisitor.new node

          visitor.count.should eq 2
        end
      {% end %}
    end
  end
end


================================================
FILE: spec/ameba/ast/visitors/elseif_aware_node_visitor_spec.cr
================================================
require "../../../spec_helper"

module Ameba::AST
  describe ElseIfAwareNodeVisitor do
    rule = ElseIfRule.new
    subject = ElseIfAwareNodeVisitor.new rule, Source.new <<-CRYSTAL
      # rule.ifs[0]
      foo ? bar : baz

      def foo
        # rule.ifs[2]
        if :one
          1
        elsif :two
          2
        elsif :three
          3
        else
          %w[].each do
            # rule.ifs[1]
            if true
              'a'
            elsif false
              'b'
            else
              'c'
            end
          end
        end
      end
      CRYSTAL

    it "inherits a logic from `NodeVisitor`" do
      subject.should be_a(NodeVisitor)
    end

    it "fires a callback for every `if` node, excluding `elsif` branches" do
      rule.ifs.size.should eq 3
    end

    it "fires a callback with an array containing an `if` node without an `elsif` branches" do
      if_node, ifs = rule.ifs[0]
      if_node.to_s.should eq "foo ? bar : baz"

      ifs.should be_nil
    end

    it "fires a callback with an array containing an `if` node with multiple `elsif` branches" do
      if_node, ifs = rule.ifs[2]
      if_node.cond.to_s.should eq ":one"

      ifs = ifs.should_not be_nil
      ifs.size.should eq 3
      ifs.first.should be if_node
      ifs.map(&.then.to_s).should eq %w[1 2 3]
    end

    it "fires a callback with an array containing an `if` node with the `else` branch as the last item" do
      if_node, ifs = rule.ifs[1]
      if_node.cond.to_s.should eq "true"

      ifs = ifs.should_not be_nil
      ifs.size.should eq 2
      ifs.first.should be if_node
      ifs.map(&.then.to_s).should eq %w['a' 'b']
      ifs.last.else.to_s.should eq %('c')
    end
  end
end


================================================
FILE: spec/ameba/ast/visitors/flow_expression_visitor_spec.cr
================================================
require "../../../spec_helper"

module Ameba::AST
  describe FlowExpressionVisitor do
    it "creates an expression for return" do
      rule = FlowExpressionRule.new
      FlowExpressionVisitor.new rule, Source.new <<-CRYSTAL
        def foo
          return :bar
        end
        CRYSTAL
      rule.expressions.size.should eq 1
    end

    it "can create multiple expressions" do
      rule = FlowExpressionRule.new
      FlowExpressionVisitor.new rule, Source.new <<-CRYSTAL
        def foo
          if bar
            return :baz
          else
            return :foobar
          end
        end
        CRYSTAL
      rule.expressions.size.should eq 3
    end

    it "properly creates nested flow expressions" do
      rule = FlowExpressionRule.new
      FlowExpressionVisitor.new rule, Source.new <<-CRYSTAL
        def foo
          return (
            loop do
              break if a > 1
              return a
            end
          )
        end
        CRYSTAL
      rule.expressions.size.should eq 4
    end

    it "creates an expression for break" do
      rule = FlowExpressionRule.new
      FlowExpressionVisitor.new rule, Source.new <<-CRYSTAL
        while true
          break
        end
        CRYSTAL
      rule.expressions.size.should eq 1
    end

    it "creates an expression for next" do
      rule = FlowExpressionRule.new
      FlowExpressionVisitor.new rule, Source.new <<-CRYSTAL
        while true
          next if something
        end
        CRYSTAL
      rule.expressions.size.should eq 1
    end
  end
end


================================================
FILE: spec/ameba/ast/visitors/implicit_return_visitor_spec.cr
================================================
require "../../../spec_helper"

private def implicit_return_visit(code)
  Ameba::ImplicitReturnRule.new.tap do |rule|
    Ameba::AST::ImplicitReturnVisitor.new rule, Ameba::Source.new(code)
  end
end

private def has_unused_expression?(rule, str)
  rule.unused_expressions.any?(&.to_s.== str)
end

private def has_unused_expression?(rule, node_type : Crystal::ASTNode.class)
  rule.unused_expressions.any?(node_type)
end

private def has_unused_call?(rule, name)
  rule.unused_expressions.any? do |node|
    node.is_a?(Crystal::Call) && node.name == name
  end
end

module Ameba::AST
  describe ImplicitReturnVisitor do
    context "Crystal::Expressions" do
      it "reports all non-last expressions as unused" do
        rule = implicit_return_visit(<<-CRYSTAL)
          def method
            foo
            bar
            baz
          end
          CRYSTAL

        has_unused_expression?(rule, "foo").should be_true
        has_unused_expression?(rule, "bar").should be_true
        has_unused_expression?(rule, "baz").should be_false
      end

      it "reports non-last expression even when parent captures result" do
        rule = implicit_return_visit(<<-CRYSTAL)
          x = begin
            foo
            bar
          end
          CRYSTAL

        has_unused_expression?(rule, "foo").should be_true
        has_unused_expression?(rule, "bar").should be_false
        has_unused_expression?(rule, "x").should be_false
      end

      it "stops processing after control expressions" do
        rule = implicit_return_visit(<<-CRYSTAL)
          foo
          return bar
          baz
          CRYSTAL

        has_unused_expression?(rule, "foo").should be_true
        has_unused_expression?(rule, "bar").should be_false
        has_unused_expression?(rule, "baz").should be_false
      end

      it "handles nested expressions correctly" do
        rule = implicit_return_visit(<<-CRYSTAL)
          begin
            foo
            begin
              bar
              baz
            end
          end
          CRYSTAL

        has_unused_expression?(rule, "foo").should be_true
        has_unused_expression?(rule, "bar").should be_true
      end
    end

    context "assignments" do
      it "marks assigned value as captured" do
        rule = implicit_return_visit(<<-CRYSTAL)
          foo
          x = bar
          CRYSTAL

        has_unused_expression?(rule, "foo").should be_true
        has_unused_expression?(rule, "bar").should be_false
        has_unused_expression?(rule, "x").should be_false
      end

      it "reports assignments when not captured" do
        rule = implicit_return_visit(<<-CRYSTAL)
          def method
            x = 1
            y = 2
          end
          CRYSTAL

        assigns = rule.unused_expressions.select(Crystal::Assign)
        assigns.size.should eq 1
        assigns.first.to_s.should start_with "x ="
      end

      it "marks op-assigned values as captured" do
        rule = implicit_return_visit(<<-CRYSTAL)
          x = 0
          x += foo
          bar
          CRYSTAL

        has_unused_expression?(rule, "foo").should be_false
      end

      it "marks multi-assigned values as captured" do
        rule = implicit_return_visit(<<-CRYSTAL)
          a, b = foo, bar
          baz
          CRYSTAL

        has_unused_expression?(rule, "foo").should be_false
        has_unused_expression?(rule, "bar").should be_false
      end
    end

    context "Crystal::Call" do
      it "marks all call arguments as captured" do
        rule = implicit_return_visit(<<-CRYSTAL)
          foo(bar, baz)
          qux
          CRYSTAL

        has_unused_expression?(rule, "bar").should be_false
        has_unused_expression?(rule, "baz").should be_false
      end

      it "reports the call itself when not captured" do
        rule = implicit_return_visit(<<-CRYSTAL)
          foo(1)
          bar
          CRYSTAL

        has_unused_call?(rule, "foo").should be_true
        has_unused_expression?(rule, "bar").should be_true
      end

      it "handles method calls with blocks" do
        rule = implicit_return_visit(<<-CRYSTAL)
          foo.map { |x| x + 1 }
          bar
          CRYSTAL

        has_unused_call?(rule, "map").should be_true
        has_unused_expression?(rule, "bar").should be_true
      end
    end

    context "Crystal::If and Crystal::Unless" do
      it "marks condition as captured" do
        rule = implicit_return_visit(<<-CRYSTAL)
          if foo
            bar
          else
            baz
          end
          CRYSTAL

        has_unused_expression?(rule, "foo").should be_false
        has_unused_expression?(rule, "bar").should be_true
        has_unused_expression?(rule, "baz").should be_true
      end

      it "reports if statement when not captured" do
        rule = implicit_return_visit(<<-CRYSTAL)
          if true
            1
          end
          2
          CRYSTAL

        has_unused_expression?(rule, Crystal::If).should be_true
        has_unused_expression?(rule, "1").should be_true
        has_unused_expression?(rule, "2").should be_true
      end

      it "captures last line of branches when if is captured" do
        rule = implicit_return_visit(<<-CRYSTAL)
          x = if foo
            bar
            baz
          else
            qux
          end
          CRYSTAL

        has_unused_expression?(rule, "bar").should be_true
        has_unused_expression?(rule, "baz").should be_false
        has_unused_expression?(rule, "qux").should be_false
      end
    end

    context "Crystal::While and Crystal::Until" do
      it "marks condition as captured" do
        rule = implicit_return_visit(<<-CRYSTAL)
          while foo
            bar
          end
          CRYSTAL

        has_unused_expression?(rule, "foo").should be_false
        has_unused_expression?(rule, "bar").should be_true
      end

      it "does not capture loop body by default" do
        rule = implicit_return_visit(<<-CRYSTAL)
          while true
            foo
            bar
          end
          CRYSTAL

        has_unused_expression?(rule, "foo").should be_true
        has_unused_expression?(rule, "bar").should be_true
      end
    end

    context "Crystal::Case" do
      it "marks case condition as captured" do
        rule = implicit_return_visit(<<-CRYSTAL)
          case foo
          when 1
            bar
          end
          CRYSTAL

        has_unused_expression?(rule, "foo").should be_false
      end

      it "marks when conditions as captured" do
        rule = implicit_return_visit(<<-CRYSTAL)
          case x
          when foo, bar
            baz
          end
          CRYSTAL

        has_unused_expression?(rule, "foo").should be_false
        has_unused_expression?(rule, "bar").should be_false
      end

      it "inherits parent capture state for when bodies" do
        rule = implicit_return_visit(<<-CRYSTAL)
          result = case x
          when 1
            foo
            bar
          when 2
            baz
          end
          CRYSTAL

        has_unused_expression?(rule, "foo").should be_true
      end
    end

    context "Crystal::Def" do
      it "marks method arguments as captured" do
        rule = implicit_return_visit(<<-CRYSTAL)
          def method(x = foo)
          end
          CRYSTAL

        has_unused_expression?(rule, "foo").should be_false
      end

      it "captures method body last line by default" do
        rule = implicit_return_visit(<<-CRYSTAL)
          def method
            foo
            bar
          end
          CRYSTAL

        has_unused_expression?(rule, "foo").should be_true
        has_unused_expression?(rule, "bar").should be_false
      end

      it "does not capture body when return type is Nil" do
        rule = implicit_return_visit(<<-CRYSTAL)
          def method : Nil
            foo
            bar
          end
          CRYSTAL

        has_unused_expression?(rule, "foo").should be_true
        has_unused_expression?(rule, "bar").should be_true
      end

      it "does not capture body in initialize methods" do
        rule = implicit_return_visit(<<-CRYSTAL)
          class Foo
            def initialize
              foo
              bar
            end
          end
          CRYSTAL

        has_unused_expression?(rule, "foo").should be_true
        has_unused_expression?(rule, "bar").should be_true
      end

      it "handles method with complex body" do
        rule = implicit_return_visit(<<-CRYSTAL)
          def outer
            foo
            if condition
              bar
            end
            baz
          end
          CRYSTAL

        has_unused_expression?(rule, "foo").should be_true
        has_unused_expression?(rule, "bar").should be_true
        has_unused_expression?(rule, "baz").should be_false
      end
    end

    context "literals" do
      it "marks array elements as captured" do
        rule = implicit_return_visit(<<-CRYSTAL)
          [foo, bar]
          CRYSTAL

        has_unused_expression?(rule, "foo").should be_false
        has_unused_expression?(rule, "bar").should be_false
      end

      it "marks hash values as captured" do
        rule = implicit_return_visit(<<-CRYSTAL)
          {a: foo, b: bar}
          CRYSTAL

        has_unused_expression?(rule, "foo").should be_false
        has_unused_expression?(rule, "bar").should be_false
      end

      it "marks range bounds as captured" do
        rule = implicit_return_visit(<<-CRYSTAL)
          foo..bar
          CRYSTAL

        has_unused_expression?(rule, "foo").should be_false
        has_unused_expression?(rule, "bar").should be_false
      end

      it "marks string interpolation expressions as captured" do
        rule = implicit_return_visit(<<-'CRYSTAL')
          "hello #{foo} world"
          CRYSTAL

        has_unused_expression?(rule, "foo").should be_false
      end

      it "marks regex contents as captured" do
        rule = implicit_return_visit(<<-'CRYSTAL')
          /#{foo}/
          CRYSTAL

        has_unused_expression?(rule, "foo").should be_false
      end
    end

    context "Crystal::BinaryOp" do
      it "marks left side as captured when right is a Call" do
        rule = implicit_return_visit(<<-CRYSTAL)
          foo + bar()
          CRYSTAL

        has_unused_expression?(rule, "foo").should be_false
      end

      it "marks left side as captured when right is ControlExpression" do
        rule = implicit_return_visit(<<-CRYSTAL)
          foo || return bar
          CRYSTAL

        has_unused_expression?(rule, "foo").should be_false
      end

      it "does not mark left side as captured for simple ops" do
        rule = implicit_return_visit(<<-CRYSTAL)
          foo + bar
          CRYSTAL

        has_unused_expression?(rule, "foo + bar").should be_true
      end
    end

    context "type operations" do
      it "marks casted object as captured" do
        rule = implicit_return_visit(<<-CRYSTAL)
          foo.as(Bar)
          CRYSTAL

        has_unused_expression?(rule, "foo").should be_false
      end

      it "marks tested object as captured" do
        rule = implicit_return_visit(<<-CRYSTAL)
          foo.is_a?(Bar)
          baz.responds_to?(:method)
          CRYSTAL

        has_unused_expression?(rule, "foo").should be_false
        has_unused_expression?(rule, "baz").should be_false
      end

      it "marks typeof argument as captured" do
        rule = implicit_return_visit(<<-CRYSTAL)
          typeof(foo)
          CRYSTAL

        has_unused_expression?(rule, "foo").should be_false
      end

      it "marks declared value as captured" do
        rule = implicit_return_visit(<<-CRYSTAL)
          x : Int32 = foo
          CRYSTAL

        has_unused_expression?(rule, "foo").should be_false
      end
    end

    context "Crystal::ExceptionHandler" do
      it "does not capture body last line when else is present" do
        rule = implicit_return_visit(<<-CRYSTAL)
          begin
            foo
            bar
          rescue
            baz
          else
            qux
          end
          CRYSTAL

        has_unused_expression?(rule, "bar").should be_true
      end

      it "captures body last line when no else is present" do
        rule = implicit_return_visit(<<-CRYSTAL)
          x = begin
            foo
            bar
          rescue
            baz
          end
          CRYSTAL

        has_unused_expression?(rule, "foo").should be_true
        has_unused_expression?(rule, "bar").should be_false
        has_unused_expression?(rule, "baz").should be_false
      end

      it "does not capture ensure block" do
        rule = implicit_return_visit(<<-CRYSTAL)
          begin
            foo
          ensure
            bar
            baz
          end
          CRYSTAL

        has_unused_expression?(rule, "bar").should be_true
        has_unused_expression?(rule, "baz").should be_true
      end

      it "inherits capture state for rescue bodies" do
        rule = implicit_return_visit(<<-CRYSTAL)
          x = begin
            foo
          rescue
            bar
            baz
          end
          CRYSTAL

        has_unused_expression?(rule, "bar").should be_true
        has_unused_expression?(rule, "baz").should be_false
      end
    end

    context "Crystal::Block" do
      it "inherits parent capture state for block body" do
        rule = implicit_return_visit(<<-CRYSTAL)
          [1, 2].map do |x|
            foo
            x + 1
          end
          CRYSTAL

        has_unused_expression?(rule, "foo").should be_true
      end

      it "processes block when block itself is unused" do
        rule = implicit_return_visit(<<-CRYSTAL)
          3.times do
            foo
            bar
          end
          baz
          CRYSTAL

        has_unused_expression?(rule, "foo").should be_true
        has_unused_expression?(rule, "bar").should be_false
        has_unused_expression?(rule, "baz").should be_true
      end
    end

    context "Crystal::ControlExpression" do
      it "marks control expression arguments as captured" do
        rule = implicit_return_visit(<<-CRYSTAL)
          return foo
          CRYSTAL

        has_unused_expression?(rule, "foo").should be_false
      end

      it "reports the control expression itself" do
        rule = implicit_return_visit(<<-CRYSTAL)
          def method : Nil
            return 1
          end
          CRYSTAL

        has_unused_expression?(rule, Crystal::Return).should be_true
      end

      it "handles break with value" do
        rule = implicit_return_visit(<<-CRYSTAL)
          loop do
            break foo if condition
          end
          CRYSTAL

        has_unused_expression?(rule, "foo").should be_false
      end

      it "handles next with value" do
        rule = implicit_return_visit(<<-CRYSTAL)
          [1, 2].each do |x|
            next foo if x == 1
            bar
          end
          CRYSTAL

        has_unused_expression?(rule, "foo").should be_false
      end
    end

    context "macros" do
      it "marks macro arguments as captured" do
        rule = implicit_return_visit(<<-CRYSTAL)
          macro method(arg = foo)
          end
          CRYSTAL

        has_unused_expression?(rule, "foo").should be_false
      end

      it "captures macro body" do
        rule = implicit_return_visit(<<-CRYSTAL)
          macro method
            foo
            bar
          end
          CRYSTAL

        has_unused_expression?(rule, "foo").should be_false
        has_unused_expression?(rule, "bar").should be_false
      end

      it "sets in_macro flag for macro body" do
        rule = implicit_return_visit(<<-CRYSTAL)
          macro method
            {% foo %}
          end
          CRYSTAL

        # ameba:disable Performance/AnyInsteadOfPresent
        rule.macro_flags.any?.should be_true
        has_unused_expression?(rule, "foo").should be_true
      end

      it "captures output macro expressions" do
        rule = implicit_return_visit(<<-CRYSTAL)
          {{ foo }}
          CRYSTAL

        has_unused_expression?(rule, "foo").should be_false
      end

      it "does not capture non-output macro expressions" do
        rule = implicit_return_visit(<<-CRYSTAL)
          {% foo %}
          CRYSTAL

        has_unused_expression?(rule, "foo").should be_true
      end

      it "sets in_macro flag for macro expression" do
        rule = implicit_return_visit(<<-CRYSTAL)
          {% foo %}
          CRYSTAL

        # ameba:disable Performance/AnyInsteadOfPresent
        rule.macro_flags.any?.should be_true
      end

      it "marks macro if condition as captured" do
        rule = implicit_return_visit(<<-CRYSTAL)
          {% if foo %}
          {% end %}
          CRYSTAL

        has_unused_expression?(rule, "foo").should be_false
      end

      it "does not capture macro if branches" do
        rule = implicit_return_visit(<<-CRYSTAL)
          {% if true %}
            foo
          {% else %}
            bar
          {% end %}
          CRYSTAL

        has_unused_expression?(rule, "foo").should be_false
        has_unused_expression?(rule, "bar").should be_false
      end

      it "does not capture macro for body" do
        rule = implicit_return_visit(<<-CRYSTAL)
          {% for x in [1, 2] %}
            foo
          {% end %}
          CRYSTAL

        has_unused_expression?(rule, "foo").should be_false
      end
    end

    context "other node types" do
      it "visits enum members" do
        rule = implicit_return_visit(<<-CRYSTAL)
          enum Color
            Red
            Green
            Blue
          end
          CRYSTAL

        has_unused_expression?(rule, "Red").should be_false
        has_unused_expression?(rule, "Green").should be_false
        has_unused_expression?(rule, "Blue").should be_false
      end

      it "visits class and module bodies" do
        rule = implicit_return_visit(<<-CRYSTAL)
          class Foo
            foo
            bar
          end
          CRYSTAL

        has_unused_expression?(rule, "foo").should be_true
        has_unused_expression?(rule, "bar").should be_true
      end

      it "marks function body as captured" do
        rule = implicit_return_visit(<<-CRYSTAL)
          fun foo : Int32
            return 1
          end
          CRYSTAL

        has_unused_expression?(rule, Crystal::FunDef).should be_true
      end

      it "marks unary operand as captured" do
        rule = implicit_return_visit(<<-CRYSTAL)
          !foo
          CRYSTAL

        has_unused_expression?(rule, "foo").should be_false
        has_unused_expression?(rule, "!foo").should be_true
      end

      it "marks yielded values as captured" do
        rule = implicit_return_visit(<<-CRYSTAL)
          yield foo, bar
          CRYSTAL

        has_unused_expression?(rule, "foo").should be_false
        has_unused_expression?(rule, "bar").should be_false
      end

      it "marks default argument value as captured" do
        rule = implicit_return_visit(<<-CRYSTAL)
          def method(x = foo)
          end
          CRYSTAL

        has_unused_expression?(rule, "foo").should be_false
      end

      it "marks annotation arguments as captured" do
        rule = implicit_return_visit(<<-CRYSTAL)
          @[Foo(bar)]
          def method
          end
          CRYSTAL

        has_unused_expression?(rule, "bar").should be_false
      end

      it "visits select whens" do
        rule = implicit_return_visit(<<-CRYSTAL)
          select
          when x = foo
            bar
          end
          CRYSTAL

        has_unused_expression?(rule, Crystal::Select).should be_true
        has_unused_expression?(rule, "bar").should be_true
      end
    end

    context "edge cases" do
      it "handles deeply nested scopes" do
        rule = implicit_return_visit(<<-CRYSTAL)
          class Outer
            class Inner
              def method
                if true
                  foo
                  bar
                end
              end
            end
          end
          CRYSTAL

        has_unused_expression?(rule, "foo").should be_true
        has_unused_expression?(rule, "bar").should be_false
      end

      it "handles control expressions in method body" do
        rule = implicit_return_visit(<<-CRYSTAL)
          def method
            return foo if condition
            bar
          end
          CRYSTAL

        has_unused_expression?(rule, "foo").should be_false
        has_unused_expression?(rule, "bar").should be_false
      end

      it "handles multiple assignment targets" do
        rule = implicit_return_visit(<<-CRYSTAL)
          a, b, c = foo, bar, baz
          CRYSTAL

        has_unused_expression?(rule, "foo").should be_false
        has_unused_expression?(rule, "bar").should be_false
        has_unused_expression?(rule, "baz").should be_false
      end

      it "handles nested exception handlers" do
        rule = implicit_return_visit(<<-CRYSTAL)
          begin
            begin
              foo
            rescue
              bar
            end
          rescue
            baz
          end
          CRYSTAL

        has_unused_expression?(rule, "foo").should be_true
        has_unused_expression?(rule, "bar").should be_true
        has_unused_expression?(rule, "baz").should be_true
      end

      it "handles case with multiple when conditions" do
        rule = implicit_return_visit(<<-CRYSTAL)
          case x
          when foo, bar, baz
            qux
          end
          CRYSTAL

        has_unused_expression?(rule, "foo").should be_false
        has_unused_expression?(rule, "bar").should be_false
        has_unused_expression?(rule, "baz").should be_false
      end

      it "handles trailing control expressions" do
        rule = implicit_return_visit(<<-CRYSTAL)
          begin
            foo
            bar
            return baz
          end
          CRYSTAL

        has_unused_expression?(rule, "foo").should be_true
        has_unused_expression?(rule, "bar").should be_true
      end
    end

    context "integration scenarios" do
      it "handles complex method with multiple features" do
        rule = implicit_return_visit(<<-CRYSTAL)
          def process(items)
            return nil if items.empty?

            result = items.map do |item|
              if item.valid?
                item.process
              else
                item.skip
              end
            end

            log(result)
            result
          end
          CRYSTAL

        has_unused_call?(rule, "log").should be_true
      end

      it "handles initialize with side effects" do
        rule = implicit_return_visit(<<-CRYSTAL)
          class Foo
            def initialize(@x : Int32)
              validate!
              setup_hooks
            end
          end
          CRYSTAL

        has_unused_call?(rule, "validate!").should be_true
        has_unused_call?(rule, "setup_hooks").should be_true
      end
    end
  end
end


================================================
FILE: spec/ameba/ast/visitors/node_visitor_spec.cr
================================================
require "../../../spec_helper"

module Ameba::AST
  describe NodeVisitor do
    describe "visit" do
      it "allow to visit ASTNode" do
        rule = DummyRule.new
        visitor = NodeVisitor.new rule, Source.new

        nodes = Crystal::Parser.new("").parse
        nodes.accept visitor
      end
    end
  end
end


================================================
FILE: spec/ameba/ast/visitors/redundant_control_expression_visitor_spec.cr
================================================
require "../../../spec_helper"

module Ameba::AST
  describe RedundantControlExpressionVisitor do
    source = Source.new
    rule = RedundantControlExpressionRule.new

    node = as_node <<-CRYSTAL
      a = 1
      b = 2
      return a + b
      CRYSTAL
    subject = RedundantControlExpressionVisitor.new(rule, source, node)

    it "assigns valid attributes" do
      subject.rule.should eq rule
      subject.source.should eq source
      subject.node.should eq node
    end

    it "fires a callback with a valid node" do
      rule.nodes.size.should eq 1
      rule.nodes.first.to_s.should eq "return a + b"
    end
  end
end


================================================
FILE: spec/ameba/ast/visitors/scope_calls_with_self_receiver_visitor_spec.cr
================================================
require "../../../spec_helper"

module Ameba::AST
  describe ScopeCallsWithSelfReceiverVisitor do
    {% for type in %w[class module].map(&.id) %}
      it "creates a scope for the {{ type }} def" do
        rule = SelfCallsRule.new
        visitor = ScopeCallsWithSelfReceiverVisitor.new rule, Source.new <<-CRYSTAL
          {{ type }} Foo
            self.foo
          end
          CRYSTAL

        call_queue = visitor.scope_call_queue
        call_queue.size.should eq 1
        call_queue.should eq rule.call_queue

        scope, calls = call_queue.first

        node = scope.node.should be_a(Crystal::{{ type.capitalize }}Def)
        node.name.should be_a(Crystal::Path)
        node.name.to_s.should eq "Foo"

        calls.size.should eq 1
        calls.first.name.should eq "foo"
      end
    {% end %}

    it "creates a scope for the def" do
      rule = SelfCallsRule.new
      visitor = ScopeCallsWithSelfReceiverVisitor.new rule, Source.new <<-CRYSTAL
        class Foo
          def method
            self.foo :a, :b, :c
            self.bar
            baz :x, :y, :z
            it_is_a.bat "country"
            self
          end
        end
        CRYSTAL

      call_queue = visitor.scope_call_queue
      call_queue.size.should eq 1
      call_queue.should eq rule.call_queue

      scope, calls = call_queue.first

      node = scope.node.should be_a(Crystal::Def)
      node.name.should eq "method"

      calls.size.should eq 2
      calls.first.name.should eq "foo"
      calls.last.name.should eq "bar"
    end

    it "creates a scope for the proc" do
      rule = SelfCallsRule.new
      visitor = ScopeCallsWithSelfReceiverVisit
Download .txt
gitextract_e8xowmtu/

├── .ameba.yml
├── .ameba.yml.schema.json
├── .dockerignore
├── .editorconfig
├── .gitattributes
├── .github/
│   ├── dependabot.yml
│   └── workflows/
│       ├── add-docs-version-to-json-index.yml
│       ├── build-and-deploy-docs.yml
│       ├── ci.yml
│       ├── compare-json-schema.yml
│       ├── docker-image.yml
│       ├── docs.yml
│       ├── release.yml
│       ├── typos.yml
│       └── update-json-schema.yml
├── .gitignore
├── .typos.toml
├── Dockerfile
├── LICENSE
├── Makefile
├── README.md
├── bench/
│   └── check_sources.cr
├── bin/
│   └── ameba.cr
├── shard.yml
├── spec/
│   ├── ameba/
│   │   ├── ast/
│   │   │   ├── flow_expression_spec.cr
│   │   │   ├── liveness_analyzer_spec.cr
│   │   │   ├── scope_spec.cr
│   │   │   ├── util_spec.cr
│   │   │   ├── variabling/
│   │   │   │   ├── argument_spec.cr
│   │   │   │   ├── assignment_spec.cr
│   │   │   │   ├── reference_spec.cr
│   │   │   │   ├── type_dec_variable_spec.cr
│   │   │   │   └── variable_spec.cr
│   │   │   └── visitors/
│   │   │       ├── counting_visitor_spec.cr
│   │   │       ├── elseif_aware_node_visitor_spec.cr
│   │   │       ├── flow_expression_visitor_spec.cr
│   │   │       ├── implicit_return_visitor_spec.cr
│   │   │       ├── node_visitor_spec.cr
│   │   │       ├── redundant_control_expression_visitor_spec.cr
│   │   │       ├── scope_calls_with_self_receiver_visitor_spec.cr
│   │   │       ├── scope_visitor_spec.cr
│   │   │       └── top_level_nodes_visitor_spec.cr
│   │   ├── base_spec.cr
│   │   ├── cli/
│   │   │   └── cmd_spec.cr
│   │   ├── config_spec.cr
│   │   ├── ext/
│   │   │   └── location_spec.cr
│   │   ├── formatter/
│   │   │   ├── disabled_formatter_spec.cr
│   │   │   ├── dot_formatter_spec.cr
│   │   │   ├── explain_formatter_spec.cr
│   │   │   ├── flycheck_formatter_spec.cr
│   │   │   ├── github_actions_formatter_spec.cr
│   │   │   ├── json_formatter_spec.cr
│   │   │   ├── todo_formatter_spec.cr
│   │   │   └── util_spec.cr
│   │   ├── glob_utils_spec.cr
│   │   ├── inline_comments_spec.cr
│   │   ├── issue_spec.cr
│   │   ├── presenter/
│   │   │   ├── rule_collection_presenter_spec.cr
│   │   │   ├── rule_presenter_spec.cr
│   │   │   └── rule_versions_presenter_spec.cr
│   │   ├── reportable_spec.cr
│   │   ├── rule/
│   │   │   ├── base_spec.cr
│   │   │   ├── documentation/
│   │   │   │   ├── admonition_spec.cr
│   │   │   │   └── documentation_spec.cr
│   │   │   ├── layout/
│   │   │   │   ├── line_length_spec.cr
│   │   │   │   ├── trailing_blank_lines_spec.cr
│   │   │   │   └── trailing_whitespace_spec.cr
│   │   │   ├── lint/
│   │   │   │   ├── ambiguous_assignment_spec.cr
│   │   │   │   ├── assignment_in_call_argument_spec.cr
│   │   │   │   ├── bad_directive_spec.cr
│   │   │   │   ├── comparison_to_boolean_spec.cr
│   │   │   │   ├── debug_calls_spec.cr
│   │   │   │   ├── debugger_statement_spec.cr
│   │   │   │   ├── duplicate_branch_spec.cr
│   │   │   │   ├── duplicate_enum_value_spec.cr
│   │   │   │   ├── duplicate_method_signature_spec.cr
│   │   │   │   ├── duplicate_when_condition_spec.cr
│   │   │   │   ├── duplicated_require_spec.cr
│   │   │   │   ├── else_nil_spec.cr
│   │   │   │   ├── empty_ensure_spec.cr
│   │   │   │   ├── empty_expression_spec.cr
│   │   │   │   ├── empty_loop_spec.cr
│   │   │   │   ├── enum_member_name_conflict_spec.cr
│   │   │   │   ├── formatting_spec.cr
│   │   │   │   ├── hash_duplicated_key_spec.cr
│   │   │   │   ├── literal_assignments_in_expressions_spec.cr
│   │   │   │   ├── literal_in_condition_spec.cr
│   │   │   │   ├── literal_in_interpolation_spec.cr
│   │   │   │   ├── literals_comparison_spec.cr
│   │   │   │   ├── missing_block_argument_spec.cr
│   │   │   │   ├── non_existent_rule_spec.cr
│   │   │   │   ├── not_nil_after_no_bang_spec.cr
│   │   │   │   ├── not_nil_spec.cr
│   │   │   │   ├── percent_arrays_spec.cr
│   │   │   │   ├── rand_zero_spec.cr
│   │   │   │   ├── redundant_string_cercion_spec.cr
│   │   │   │   ├── redundant_with_index_spec.cr
│   │   │   │   ├── redundant_with_object_spec.cr
│   │   │   │   ├── require_parentheses_spec.cr
│   │   │   │   ├── self_initialize_definition_spec.cr
│   │   │   │   ├── shadowed_argument_spec.cr
│   │   │   │   ├── shadowed_exception_spec.cr
│   │   │   │   ├── shadowing_outer_local_var_spec.cr
│   │   │   │   ├── shared_var_in_fiber_spec.cr
│   │   │   │   ├── signal_trap_spec.cr
│   │   │   │   ├── spec_eq_with_bool_or_nil_literal_spec.cr
│   │   │   │   ├── spec_filename_spec.cr
│   │   │   │   ├── spec_focus_spec.cr
│   │   │   │   ├── syntax_spec.cr
│   │   │   │   ├── top_level_operator_definition_spec.cr
│   │   │   │   ├── trailing_rescue_exception_spec.cr
│   │   │   │   ├── typos_spec.cr
│   │   │   │   ├── unneeded_disable_directive_spec.cr
│   │   │   │   ├── unreachable_code_spec.cr
│   │   │   │   ├── unused_argument_spec.cr
│   │   │   │   ├── unused_block_argument_spec.cr
│   │   │   │   ├── unused_expression_spec.cr
│   │   │   │   ├── unused_rescue_variable_spec.cr
│   │   │   │   ├── useless_assign_spec.cr
│   │   │   │   ├── useless_condition_in_when_spec.cr
│   │   │   │   ├── useless_visibility_modifier_spec.cr
│   │   │   │   ├── void_outside_lib_spec.cr
│   │   │   │   └── whitespace_around_macro_expression_spec.cr
│   │   │   ├── metrics/
│   │   │   │   └── cyclomatic_complexity_spec.cr
│   │   │   ├── naming/
│   │   │   │   ├── accessor_method_name_spec.cr
│   │   │   │   ├── ascii_identifiers_spec.cr
│   │   │   │   ├── binary_operator_parameter_name_spec.cr
│   │   │   │   ├── block_parameter_name_spec.cr
│   │   │   │   ├── constant_names_spec.cr
│   │   │   │   ├── filename_spec.cr
│   │   │   │   ├── method_names_spec.cr
│   │   │   │   ├── predicate_name_spec.cr
│   │   │   │   ├── query_bool_methods_spec.cr
│   │   │   │   ├── rescued_exceptions_variable_name_spec.cr
│   │   │   │   ├── type_names_spec.cr
│   │   │   │   └── variable_names_spec.cr
│   │   │   ├── performance/
│   │   │   │   ├── any_after_filter_spec.cr
│   │   │   │   ├── any_instead_of_present_spec.cr
│   │   │   │   ├── base_spec.cr
│   │   │   │   ├── chained_call_with_no_bang_spec.cr
│   │   │   │   ├── compact_after_map_spec.cr
│   │   │   │   ├── excessive_allocations_spec.cr
│   │   │   │   ├── first_last_after_filter_spec.cr
│   │   │   │   ├── flatten_after_map_spec.cr
│   │   │   │   ├── map_instead_of_block_spec.cr
│   │   │   │   ├── minmax_after_map_spec.cr
│   │   │   │   ├── size_after_filter_spec.cr
│   │   │   │   └── times_map_spec.cr
│   │   │   ├── style/
│   │   │   │   ├── array_literal_syntax_spec.cr
│   │   │   │   ├── call_parentheses_spec.cr
│   │   │   │   ├── elsif_spec.cr
│   │   │   │   ├── guard_clause_spec.cr
│   │   │   │   ├── hash_literal_syntax_spec.cr
│   │   │   │   ├── heredoc_escape_spec.cr
│   │   │   │   ├── heredoc_indent_spec.cr
│   │   │   │   ├── is_a_filter_spec.cr
│   │   │   │   ├── is_a_nil_spec.cr
│   │   │   │   ├── large_numbers_spec.cr
│   │   │   │   ├── multiline_curly_block_spec.cr
│   │   │   │   ├── multiline_string_literal_spec.cr
│   │   │   │   ├── negated_conditions_in_unless_spec.cr
│   │   │   │   ├── parentheses_around_condition_spec.cr
│   │   │   │   ├── percent_literal_delimiters_spec.cr
│   │   │   │   ├── redundant_begin_spec.cr
│   │   │   │   ├── redundant_next_spec.cr
│   │   │   │   ├── redundant_nil_in_control_expression_spec.cr
│   │   │   │   ├── redundant_return_spec.cr
│   │   │   │   ├── redundant_self_spec.cr
│   │   │   │   ├── unless_else_spec.cr
│   │   │   │   ├── verbose_block_spec.cr
│   │   │   │   ├── verbose_nil_type_spec.cr
│   │   │   │   └── while_true_spec.cr
│   │   │   └── typing/
│   │   │       ├── macro_call_argument_type_restriction_spec.cr
│   │   │       ├── method_parameter_type_restriction_spec.cr
│   │   │       ├── method_return_type_restriction_spec.cr
│   │   │       └── proc_literal_return_type_restriction_spec.cr
│   │   ├── runner_spec.cr
│   │   ├── severity_spec.cr
│   │   ├── source/
│   │   │   └── rewriter_spec.cr
│   │   ├── source_spec.cr
│   │   ├── spec/
│   │   │   └── annotated_source_spec.cr
│   │   ├── tokenizer_spec.cr
│   │   └── version_spec.cr
│   ├── ameba_spec.cr
│   ├── fixtures/
│   │   └── .ameba.yml
│   └── spec_helper.cr
└── src/
    ├── ameba/
    │   ├── ast/
    │   │   ├── flow_expression.cr
    │   │   ├── liveness_analyzer.cr
    │   │   ├── scope.cr
    │   │   ├── util.cr
    │   │   ├── variabling/
    │   │   │   ├── argument.cr
    │   │   │   ├── assignment.cr
    │   │   │   ├── ivariable.cr
    │   │   │   ├── reference.cr
    │   │   │   ├── type_dec_variable.cr
    │   │   │   └── variable.cr
    │   │   └── visitors/
    │   │       ├── base_visitor.cr
    │   │       ├── counting_visitor.cr
    │   │       ├── elseif_aware_node_visitor.cr
    │   │       ├── flow_expression_visitor.cr
    │   │       ├── implicit_return_visitor.cr
    │   │       ├── macro_reference_finder.cr
    │   │       ├── node_visitor.cr
    │   │       ├── redundant_control_expression_visitor.cr
    │   │       ├── scope_calls_with_self_receiver_visitor.cr
    │   │       ├── scope_visitor.cr
    │   │       └── top_level_nodes_visitor.cr
    │   ├── cli/
    │   │   └── cmd.cr
    │   ├── config/
    │   │   ├── loader.cr
    │   │   └── rule_config.cr
    │   ├── config.cr
    │   ├── ext/
    │   │   └── location.cr
    │   ├── formatter/
    │   │   ├── base_formatter.cr
    │   │   ├── disabled_formatter.cr
    │   │   ├── dot_formatter.cr
    │   │   ├── explain_formatter.cr
    │   │   ├── flycheck_formatter.cr
    │   │   ├── github_actions_formatter.cr
    │   │   ├── json_formatter.cr
    │   │   ├── todo_formatter.cr
    │   │   └── util.cr
    │   ├── glob_utils.cr
    │   ├── inline_comments.cr
    │   ├── issue.cr
    │   ├── json_schema/
    │   │   └── builder.cr
    │   ├── presenter/
    │   │   ├── base_presenter.cr
    │   │   ├── rule_collection_presenter.cr
    │   │   ├── rule_presenter.cr
    │   │   └── rule_versions_presenter.cr
    │   ├── reportable.cr
    │   ├── rule/
    │   │   ├── base.cr
    │   │   ├── documentation/
    │   │   │   ├── admonition.cr
    │   │   │   └── documentation.cr
    │   │   ├── layout/
    │   │   │   ├── line_length.cr
    │   │   │   ├── trailing_blank_lines.cr
    │   │   │   └── trailing_whitespace.cr
    │   │   ├── lint/
    │   │   │   ├── ambiguous_assignment.cr
    │   │   │   ├── assignment_in_call_argument.cr
    │   │   │   ├── bad_directive.cr
    │   │   │   ├── comparison_to_boolean.cr
    │   │   │   ├── debug_calls.cr
    │   │   │   ├── debugger_statement.cr
    │   │   │   ├── duplicate_branch.cr
    │   │   │   ├── duplicate_enum_value.cr
    │   │   │   ├── duplicate_method_signature.cr
    │   │   │   ├── duplicate_when_condition.cr
    │   │   │   ├── duplicated_require.cr
    │   │   │   ├── else_nil.cr
    │   │   │   ├── empty_ensure.cr
    │   │   │   ├── empty_expression.cr
    │   │   │   ├── empty_loop.cr
    │   │   │   ├── enum_member_name_conflict.cr
    │   │   │   ├── formatting.cr
    │   │   │   ├── hash_duplicated_key.cr
    │   │   │   ├── literal_assignments_in_expressions.cr
    │   │   │   ├── literal_in_condition.cr
    │   │   │   ├── literal_in_interpolation.cr
    │   │   │   ├── literals_comparison.cr
    │   │   │   ├── missing_block_argument.cr
    │   │   │   ├── non_existent_rule.cr
    │   │   │   ├── not_nil.cr
    │   │   │   ├── not_nil_after_no_bang.cr
    │   │   │   ├── percent_arrays.cr
    │   │   │   ├── rand_zero.cr
    │   │   │   ├── redundant_string_coercion.cr
    │   │   │   ├── redundant_with_index.cr
    │   │   │   ├── redundant_with_object.cr
    │   │   │   ├── require_parentheses.cr
    │   │   │   ├── self_initialize_definition.cr
    │   │   │   ├── shadowed_argument.cr
    │   │   │   ├── shadowed_exception.cr
    │   │   │   ├── shadowing_outer_local_var.cr
    │   │   │   ├── shared_var_in_fiber.cr
    │   │   │   ├── signal_trap.cr
    │   │   │   ├── spec_eq_with_bool_or_nil_literal.cr
    │   │   │   ├── spec_filename.cr
    │   │   │   ├── spec_focus.cr
    │   │   │   ├── syntax.cr
    │   │   │   ├── top_level_operator_definition.cr
    │   │   │   ├── trailing_rescue_exception.cr
    │   │   │   ├── typos.cr
    │   │   │   ├── unneeded_disable_directive.cr
    │   │   │   ├── unreachable_code.cr
    │   │   │   ├── unused_argument.cr
    │   │   │   ├── unused_block_argument.cr
    │   │   │   ├── unused_expression.cr
    │   │   │   ├── unused_rescue_variable.cr
    │   │   │   ├── useless_assign.cr
    │   │   │   ├── useless_condition_in_when.cr
    │   │   │   ├── useless_visibility_modifier.cr
    │   │   │   ├── void_outside_lib.cr
    │   │   │   └── whitespace_around_macro_expression.cr
    │   │   ├── metrics/
    │   │   │   └── cyclomatic_complexity.cr
    │   │   ├── naming/
    │   │   │   ├── accessor_method_name.cr
    │   │   │   ├── ascii_identifiers.cr
    │   │   │   ├── binary_operator_parameter_name.cr
    │   │   │   ├── block_parameter_name.cr
    │   │   │   ├── constant_names.cr
    │   │   │   ├── filename.cr
    │   │   │   ├── method_names.cr
    │   │   │   ├── predicate_name.cr
    │   │   │   ├── query_bool_methods.cr
    │   │   │   ├── rescued_exceptions_variable_name.cr
    │   │   │   ├── type_names.cr
    │   │   │   └── variable_names.cr
    │   │   ├── performance/
    │   │   │   ├── any_after_filter.cr
    │   │   │   ├── any_instead_of_present.cr
    │   │   │   ├── base.cr
    │   │   │   ├── chained_call_with_no_bang.cr
    │   │   │   ├── compact_after_map.cr
    │   │   │   ├── excessive_allocations.cr
    │   │   │   ├── first_last_after_filter.cr
    │   │   │   ├── flatten_after_map.cr
    │   │   │   ├── map_instead_of_block.cr
    │   │   │   ├── minmax_after_map.cr
    │   │   │   ├── size_after_filter.cr
    │   │   │   └── times_map.cr
    │   │   ├── style/
    │   │   │   ├── array_literal_syntax.cr
    │   │   │   ├── call_parentheses.cr
    │   │   │   ├── elsif.cr
    │   │   │   ├── guard_clause.cr
    │   │   │   ├── hash_literal_syntax.cr
    │   │   │   ├── heredoc_escape.cr
    │   │   │   ├── heredoc_indent.cr
    │   │   │   ├── is_a_filter.cr
    │   │   │   ├── is_a_nil.cr
    │   │   │   ├── large_numbers.cr
    │   │   │   ├── multiline_curly_block.cr
    │   │   │   ├── multiline_string_literal.cr
    │   │   │   ├── negated_conditions_in_unless.cr
    │   │   │   ├── parentheses_around_condition.cr
    │   │   │   ├── percent_literal_delimiters.cr
    │   │   │   ├── redundant_begin.cr
    │   │   │   ├── redundant_next.cr
    │   │   │   ├── redundant_nil_in_control_expression.cr
    │   │   │   ├── redundant_return.cr
    │   │   │   ├── redundant_self.cr
    │   │   │   ├── unless_else.cr
    │   │   │   ├── verbose_block.cr
    │   │   │   ├── verbose_nil_type.cr
    │   │   │   └── while_true.cr
    │   │   └── typing/
    │   │       ├── macro_call_argument_type_restriction.cr
    │   │       ├── method_parameter_type_restriction.cr
    │   │       ├── method_return_type_restriction.cr
    │   │       └── proc_literal_return_type_restriction.cr
    │   ├── runner.cr
    │   ├── severity.cr
    │   ├── source/
    │   │   ├── corrector.cr
    │   │   ├── rewriter/
    │   │   │   └── action.cr
    │   │   └── rewriter.cr
    │   ├── source.cr
    │   ├── spec/
    │   │   ├── annotated_source.cr
    │   │   ├── be_valid.cr
    │   │   ├── expect_issue.cr
    │   │   ├── support.cr
    │   │   └── util.cr
    │   ├── tokenizer.cr
    │   └── version.cr
    ├── ameba.cr
    ├── cli.cr
    ├── contrib/
    │   └── read_type_doc.cr
    └── json-schema-builder.cr
Condensed preview — 362 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (1,166K chars).
[
  {
    "path": ".ameba.yml",
    "chars": 111,
    "preview": "Lint/Typos:\n  Enabled: true\n  Excluded:\n    - spec/ameba/rule/lint/typos_spec.cr\n\nStyle/Elsif:\n  Enabled: true\n"
  },
  {
    "path": ".ameba.yml.schema.json",
    "chars": 70464,
    "preview": "{\n  \"$schema\": \"https://json-schema.org/draft/2020-12/schema\",\n  \"$id\": \"https://crystal-ameba.github.io/.ameba.yml.sche"
  },
  {
    "path": ".dockerignore",
    "chars": 50,
    "preview": ".*\n!LICENSE\n!Dockerfile\n!Makefile\n!shard.yml\n!src\n"
  },
  {
    "path": ".editorconfig",
    "chars": 137,
    "preview": "[*.cr]\ncharset = utf-8\nend_of_line = lf\ninsert_final_newline = true\nindent_style = space\nindent_size = 2\ntrim_trailing_w"
  },
  {
    "path": ".gitattributes",
    "chars": 82,
    "preview": "* text=auto eol=lf\n\n## Generated files\n\n.ameba.yml.schema.json linguist-generated\n"
  },
  {
    "path": ".github/dependabot.yml",
    "chars": 156,
    "preview": "version: 2\nupdates:\n  # Maintain dependencies for GitHub Actions\n  - package-ecosystem: github-actions\n    directory: /\n"
  },
  {
    "path": ".github/workflows/add-docs-version-to-json-index.yml",
    "chars": 1973,
    "preview": "name: Add docs version to JSON index\n\non:\n  workflow_call:\n    inputs:\n      version:\n        required: true\n        typ"
  },
  {
    "path": ".github/workflows/build-and-deploy-docs.yml",
    "chars": 1814,
    "preview": "name: Build and deploy docs\n\non:\n  workflow_dispatch:\n    inputs: &inputs\n      ref:\n        required: true\n        type"
  },
  {
    "path": ".github/workflows/ci.yml",
    "chars": 1021,
    "preview": "name: CI\n\non:\n  workflow_dispatch:\n  push:\n    branches: [master]\n  pull_request:\n    types: [opened, synchronize, reope"
  },
  {
    "path": ".github/workflows/compare-json-schema.yml",
    "chars": 739,
    "preview": "name: Compare JSON Schema\n\non:\n  pull_request:\n    types: [opened, synchronize, reopened]\n    paths:\n      - \".github/wo"
  },
  {
    "path": ".github/workflows/docker-image.yml",
    "chars": 2175,
    "preview": "name: Docker image build and deploy\n\non:\n  workflow_dispatch:\n  push:\n    branches: [master]\n  release:\n    types: [publ"
  },
  {
    "path": ".github/workflows/docs.yml",
    "chars": 984,
    "preview": "name: Docs\n\non:\n  push:\n    branches:\n      - master\n    tags:\n      - \"v*.*.*\"\n\npermissions:\n  contents: write\n\njobs:\n "
  },
  {
    "path": ".github/workflows/release.yml",
    "chars": 1879,
    "preview": "name: Release\n\non:\n  push:\n    tags:\n      - \"v*.*.*\"\n\npermissions:\n  contents: write\n\nenv:\n  GH_TOKEN: ${{ github.token"
  },
  {
    "path": ".github/workflows/typos.yml",
    "chars": 338,
    "preview": "name: Spell checker\n\non:\n  push:\n    branches: [master]\n  pull_request:\n    types: [opened, synchronize, reopened]\n\nperm"
  },
  {
    "path": ".github/workflows/update-json-schema.yml",
    "chars": 1574,
    "preview": "name: Update JSON Schema\n\non:\n  workflow_dispatch:\n  push:\n    branches: [master]\n    paths:\n      - \".github/workflows/"
  },
  {
    "path": ".gitignore",
    "chars": 269,
    "preview": "# Documentation\n/docs/\n\n# Installed shards\n/lib/\n\n# Built binaries\n/bin/**/*\n!/bin/ameba.cr\n\n# Libraries don't need depe"
  },
  {
    "path": ".typos.toml",
    "chars": 406,
    "preview": "[default]\nextend-ignore-re = [\n  # numeric literals\n  '0x[0-9a-fA-F_\\.\\+]+([fiu](8|16|32|64|128))?',\n  '\\\\u\\{[0-9a-fA-F]"
  },
  {
    "path": "Dockerfile",
    "chars": 409,
    "preview": "FROM alpine:edge AS builder\nARG CRFLAGS=\"-Dpreview_mt\"\nRUN apk add --update crystal shards yaml-dev musl-dev make\nRUN mk"
  },
  {
    "path": "LICENSE",
    "chars": 1089,
    "preview": "The MIT License (MIT)\n\nCopyright (c) 2018-2020 Vitalii Elenhaupt\n\nPermission is hereby granted, free of charge, to any p"
  },
  {
    "path": "Makefile",
    "chars": 2099,
    "preview": ".POSIX:\nall:\n\n# Recipes\n\n## Build ameba\n##   $ make\n##\n## Run tests\n##   $ make test\n##\n## Install ameba\n##   $ sudo mak"
  },
  {
    "path": "README.md",
    "chars": 7836,
    "preview": "<p align=\"center\">\n  <img src=\"https://raw.githubusercontent.com/veelenga/bin/master/ameba/logo.png\" width=\"800\">\n  <h3 "
  },
  {
    "path": "bench/check_sources.cr",
    "chars": 618,
    "preview": "require \"../src/ameba\"\nrequire \"benchmark\"\n\nprivate def get_files(n)\n  Dir[\"src/**/*.cr\"].first(n)\nend\n\nputs \"== Compare"
  },
  {
    "path": "bin/ameba.cr",
    "chars": 210,
    "preview": "#!/usr/bin/env crystal\n\n# Require ameba extensions here which are added as project dependencies.\n# Example:\n#\n# require "
  },
  {
    "path": "shard.yml",
    "chars": 258,
    "preview": "name: ameba\nversion: 1.7.0-dev\n\nauthors:\n  - Vitalii Elenhaupt <velenhaupt@gmail.com>\n  - Sijawusz Pur Rahnama <sija@sij"
  },
  {
    "path": "spec/ameba/ast/flow_expression_spec.cr",
    "chars": 2202,
    "preview": "require \"../../spec_helper\"\n\nmodule Ameba::AST\n  describe FlowExpression do\n    describe \"#initialize\" do\n      it \"crea"
  },
  {
    "path": "spec/ameba/ast/liveness_analyzer_spec.cr",
    "chars": 20482,
    "preview": "require \"../../spec_helper\"\n\nprivate def scopes_for(code)\n  rule = Ameba::ScopeRule.new\n  source = Ameba::Source.new(cod"
  },
  {
    "path": "spec/ameba/ast/scope_spec.cr",
    "chars": 6450,
    "preview": "require \"../../spec_helper\"\n\nmodule Ameba::AST\n  describe Scope do\n    describe \"#initialize\" do\n      source = \"a = 2\"\n"
  },
  {
    "path": "spec/ameba/ast/util_spec.cr",
    "chars": 26036,
    "preview": "require \"../../spec_helper\"\n\nmodule Ameba::AST\n  struct Test\n    include Util\n  end\n\n  describe Util do\n    subject = Te"
  },
  {
    "path": "spec/ameba/ast/variabling/argument_spec.cr",
    "chars": 1096,
    "preview": "require \"../../../spec_helper\"\n\nmodule Ameba::AST\n  describe Argument do\n    arg = Crystal::Arg.new \"a\"\n    scope = Scop"
  },
  {
    "path": "spec/ameba/ast/variabling/assignment_spec.cr",
    "chars": 973,
    "preview": "require \"../../../spec_helper\"\n\nmodule Ameba::AST\n  describe Assignment do\n    node = Crystal::NilLiteral.new\n    scope "
  },
  {
    "path": "spec/ameba/ast/variabling/reference_spec.cr",
    "chars": 259,
    "preview": "require \"../../../spec_helper\"\n\nmodule Ameba::AST\n  describe Reference do\n    it \"is derived from a Variable\" do\n      n"
  },
  {
    "path": "spec/ameba/ast/variabling/type_dec_variable_spec.cr",
    "chars": 865,
    "preview": "require \"../../../spec_helper\"\n\nmodule Ameba::AST\n  describe TypeDecVariable do\n    var = Crystal::Var.new(\"foo\")\n    de"
  },
  {
    "path": "spec/ameba/ast/variabling/variable_spec.cr",
    "chars": 7563,
    "preview": "require \"../../../spec_helper\"\n\nmodule Ameba::AST\n  describe Variable do\n    var_node = Crystal::Var.new(\"foo\")\n    scop"
  },
  {
    "path": "spec/ameba/ast/visitors/counting_visitor_spec.cr",
    "chars": 2799,
    "preview": "require \"../../../spec_helper\"\n\nmodule Ameba::AST\n  describe CountingVisitor do\n    describe \"#visit\" do\n      it \"allow"
  },
  {
    "path": "spec/ameba/ast/visitors/elseif_aware_node_visitor_spec.cr",
    "chars": 1730,
    "preview": "require \"../../../spec_helper\"\n\nmodule Ameba::AST\n  describe ElseIfAwareNodeVisitor do\n    rule = ElseIfRule.new\n    sub"
  },
  {
    "path": "spec/ameba/ast/visitors/flow_expression_visitor_spec.cr",
    "chars": 1557,
    "preview": "require \"../../../spec_helper\"\n\nmodule Ameba::AST\n  describe FlowExpressionVisitor do\n    it \"creates an expression for "
  },
  {
    "path": "spec/ameba/ast/visitors/implicit_return_visitor_spec.cr",
    "chars": 23138,
    "preview": "require \"../../../spec_helper\"\n\nprivate def implicit_return_visit(code)\n  Ameba::ImplicitReturnRule.new.tap do |rule|\n  "
  },
  {
    "path": "spec/ameba/ast/visitors/node_visitor_spec.cr",
    "chars": 321,
    "preview": "require \"../../../spec_helper\"\n\nmodule Ameba::AST\n  describe NodeVisitor do\n    describe \"visit\" do\n      it \"allow to v"
  },
  {
    "path": "spec/ameba/ast/visitors/redundant_control_expression_visitor_spec.cr",
    "chars": 633,
    "preview": "require \"../../../spec_helper\"\n\nmodule Ameba::AST\n  describe RedundantControlExpressionVisitor do\n    source = Source.ne"
  },
  {
    "path": "spec/ameba/ast/visitors/scope_calls_with_self_receiver_visitor_spec.cr",
    "chars": 3262,
    "preview": "require \"../../../spec_helper\"\n\nmodule Ameba::AST\n  describe ScopeCallsWithSelfReceiverVisitor do\n    {% for type in %w["
  },
  {
    "path": "spec/ameba/ast/visitors/scope_visitor_spec.cr",
    "chars": 3258,
    "preview": "require \"../../../spec_helper\"\n\nmodule Ameba::AST\n  describe ScopeVisitor do\n    {% for type in %w[class module enum].ma"
  },
  {
    "path": "spec/ameba/ast/visitors/top_level_nodes_visitor_spec.cr",
    "chars": 466,
    "preview": "require \"../../../spec_helper\"\n\nmodule Ameba::AST\n  describe TopLevelNodesVisitor do\n    describe \"#require_nodes\" do\n  "
  },
  {
    "path": "spec/ameba/base_spec.cr",
    "chars": 2796,
    "preview": "require \"../spec_helper\"\n\nmodule Ameba::Rule\n  root = Path[__DIR__, \"..\", \"..\"].expand\n\n  describe Base do\n    context \""
  },
  {
    "path": "spec/ameba/cli/cmd_spec.cr",
    "chars": 8028,
    "preview": "require \"../../spec_helper\"\nrequire \"../../../src/ameba/cli/cmd\"\n\nmodule Ameba::CLI\n  root = Path[__DIR__, \"..\", \"..\", \""
  },
  {
    "path": "spec/ameba/config_spec.cr",
    "chars": 7601,
    "preview": "require \"../spec_helper\"\n\nmodule Ameba\n  root = Path[__DIR__, \"..\", \"..\"].expand\n\n  describe Config do\n    config_sample"
  },
  {
    "path": "spec/ameba/ext/location_spec.cr",
    "chars": 1630,
    "preview": "require \"../../spec_helper\"\n\ndescribe Crystal::Location do\n  subject = Crystal::Location.new(nil, 2, 3)\n\n  describe \"#wi"
  },
  {
    "path": "spec/ameba/formatter/disabled_formatter_spec.cr",
    "chars": 1328,
    "preview": "require \"../../spec_helper\"\n\nmodule Ameba::Formatter\n  describe DisabledFormatter do\n    output = IO::Memory.new\n    sub"
  },
  {
    "path": "spec/ameba/formatter/dot_formatter_spec.cr",
    "chars": 3273,
    "preview": "require \"../../spec_helper\"\n\nmodule Ameba::Formatter\n  describe DotFormatter do\n    output = IO::Memory.new\n    subject "
  },
  {
    "path": "spec/ameba/formatter/explain_formatter_spec.cr",
    "chars": 2013,
    "preview": "require \"../../spec_helper\"\n\nprivate LOCATION = Crystal::Location.new(\"source.cr\", 1, 1)\n\nprivate def explanation(source"
  },
  {
    "path": "spec/ameba/formatter/flycheck_formatter_spec.cr",
    "chars": 1337,
    "preview": "require \"../../spec_helper\"\n\nmodule Ameba::Formatter\n  describe FlycheckFormatter do\n    output = IO::Memory.new\n    sub"
  },
  {
    "path": "spec/ameba/formatter/github_actions_formatter_spec.cr",
    "chars": 3942,
    "preview": "require \"../../spec_helper\"\n\nmodule Ameba::Formatter\n  describe GitHubActionsFormatter do\n    output = IO::Memory.new\n  "
  },
  {
    "path": "spec/ameba/formatter/json_formatter_spec.cr",
    "chars": 3118,
    "preview": "require \"../../spec_helper\"\n\nprivate def get_result(sources = [Ameba::Source.new])\n  output = IO::Memory.new\n  formatter"
  },
  {
    "path": "spec/ameba/formatter/todo_formatter_spec.cr",
    "chars": 5141,
    "preview": "require \"../../spec_helper\"\nrequire \"file_utils\"\n\nprivate CONFIG_PATH =\n  Path[Dir.tempdir] / Ameba::Config::Loader::FIL"
  },
  {
    "path": "spec/ameba/formatter/util_spec.cr",
    "chars": 6514,
    "preview": "require \"../../spec_helper\"\n\nmodule Ameba::Formatter\n  class Subject\n    include Util\n  end\n\n  describe Util do\n    subj"
  },
  {
    "path": "spec/ameba/glob_utils_spec.cr",
    "chars": 2308,
    "preview": "require \"../spec_helper\"\n\nmodule Ameba\n  subject = GlobUtils\n  root = Path[__DIR__, \"..\", \"..\"].expand\n\n  current_file_p"
  },
  {
    "path": "spec/ameba/inline_comments_spec.cr",
    "chars": 5519,
    "preview": "require \"../spec_helper\"\n\nmodule Ameba\n  describe InlineComments do\n    describe InlineComments::COMMENT_DIRECTIVE_REGEX"
  },
  {
    "path": "spec/ameba/issue_spec.cr",
    "chars": 1677,
    "preview": "require \"../spec_helper\"\n\nmodule Ameba\n  describe Issue do\n    it \"accepts rule and message\" do\n      issue = Issue.new "
  },
  {
    "path": "spec/ameba/presenter/rule_collection_presenter_spec.cr",
    "chars": 777,
    "preview": "require \"../../spec_helper\"\n\nmodule Ameba\n  private def with_rule_collection_presenter(&)\n    rules = Config.load.rules\n"
  },
  {
    "path": "spec/ameba/presenter/rule_presenter_spec.cr",
    "chars": 616,
    "preview": "require \"../../spec_helper\"\n\nmodule Ameba\n  private def rule_presenter_each_rule(&)\n    rules = Config.load.rules\n\n    r"
  },
  {
    "path": "spec/ameba/presenter/rule_versions_presenter_spec.cr",
    "chars": 788,
    "preview": "require \"../../spec_helper\"\n\nmodule Ameba\n  private def with_rule_versions_presenter(&)\n    rules = Config.load.rules\n\n "
  },
  {
    "path": "spec/ameba/reportable_spec.cr",
    "chars": 1149,
    "preview": "require \"../spec_helper\"\n\nmodule Ameba\n  describe Reportable do\n    describe \"#add_issue\" do\n      it \"adds a new issue "
  },
  {
    "path": "spec/ameba/rule/base_spec.cr",
    "chars": 689,
    "preview": "require \"../../spec_helper\"\n\nmodule Ameba\n  describe Rule::Base do\n    describe \"#catch\" do\n      it \"accepts and return"
  },
  {
    "path": "spec/ameba/rule/documentation/admonition_spec.cr",
    "chars": 4054,
    "preview": "require \"../../../spec_helper\"\n\nmodule Ameba::Rule::Documentation\n  describe Admonition do\n    subject = Admonition.new\n"
  },
  {
    "path": "spec/ameba/rule/documentation/documentation_spec.cr",
    "chars": 3858,
    "preview": "require \"../../../spec_helper\"\n\nmodule Ameba::Rule::Documentation\n  describe Documentation do\n    subject = Documentatio"
  },
  {
    "path": "spec/ameba/rule/layout/line_length_spec.cr",
    "chars": 1221,
    "preview": "require \"../../../spec_helper\"\n\nmodule Ameba::Rule::Layout\n  describe LineLength do\n    subject = LineLength.new\n    lon"
  },
  {
    "path": "spec/ameba/rule/layout/trailing_blank_lines_spec.cr",
    "chars": 1888,
    "preview": "require \"../../../spec_helper\"\n\nmodule Ameba::Rule::Layout\n  describe TrailingBlankLines do\n    subject = TrailingBlankL"
  },
  {
    "path": "spec/ameba/rule/layout/trailing_whitespace_spec.cr",
    "chars": 1209,
    "preview": "require \"../../../spec_helper\"\n\nmodule Ameba::Rule::Layout\n  describe TrailingWhitespace do\n    subject = TrailingWhites"
  },
  {
    "path": "spec/ameba/rule/lint/ambiguous_assignment_spec.cr",
    "chars": 3323,
    "preview": "require \"../../../spec_helper\"\n\nmodule Ameba::Rule::Lint\n  describe AmbiguousAssignment do\n    subject = AmbiguousAssign"
  },
  {
    "path": "spec/ameba/rule/lint/assignment_in_call_argument_spec.cr",
    "chars": 3443,
    "preview": "require \"../../../spec_helper\"\n\nmodule Ameba::Rule::Lint\n  describe AssignmentInCallArgument do\n    subject = Assignment"
  },
  {
    "path": "spec/ameba/rule/lint/bad_directive_spec.cr",
    "chars": 853,
    "preview": "require \"../../../spec_helper\"\n\nmodule Ameba::Rule::Lint\n  describe BadDirective do\n    subject = BadDirective.new\n\n    "
  },
  {
    "path": "spec/ameba/rule/lint/comparison_to_boolean_spec.cr",
    "chars": 3493,
    "preview": "require \"../../../spec_helper\"\n\nmodule Ameba::Rule::Lint\n  describe ComparisonToBoolean do\n    subject = ComparisonToBoo"
  },
  {
    "path": "spec/ameba/rule/lint/debug_calls_spec.cr",
    "chars": 748,
    "preview": "require \"../../../spec_helper\"\n\nmodule Ameba::Rule::Lint\n  describe DebugCalls do\n    subject = DebugCalls.new\n\n    it \""
  },
  {
    "path": "spec/ameba/rule/lint/debugger_statement_spec.cr",
    "chars": 813,
    "preview": "require \"../../../spec_helper\"\n\nmodule Ameba::Rule::Lint\n  describe DebuggerStatement do\n    subject = DebuggerStatement"
  },
  {
    "path": "spec/ameba/rule/lint/duplicate_branch_spec.cr",
    "chars": 7118,
    "preview": "require \"../../../spec_helper\"\n\nmodule Ameba::Rule::Lint\n  describe DuplicateBranch do\n    subject = DuplicateBranch.new"
  },
  {
    "path": "spec/ameba/rule/lint/duplicate_enum_value_spec.cr",
    "chars": 1138,
    "preview": "require \"../../../spec_helper\"\n\nmodule Ameba::Rule::Lint\n  describe DuplicateEnumValue do\n    subject = DuplicateEnumVal"
  },
  {
    "path": "spec/ameba/rule/lint/duplicate_method_signature_spec.cr",
    "chars": 3029,
    "preview": "require \"../../../spec_helper\"\n\nmodule Ameba::Rule::Lint\n  describe DuplicateMethodSignature do\n    subject = DuplicateM"
  },
  {
    "path": "spec/ameba/rule/lint/duplicate_when_condition_spec.cr",
    "chars": 1557,
    "preview": "require \"../../../spec_helper\"\n\nmodule Ameba::Rule::Lint\n  describe DuplicateWhenCondition do\n    subject = DuplicateWhe"
  },
  {
    "path": "spec/ameba/rule/lint/duplicated_require_spec.cr",
    "chars": 625,
    "preview": "require \"../../../spec_helper\"\n\nmodule Ameba::Rule::Lint\n  describe DuplicatedRequire do\n    subject = DuplicatedRequire"
  },
  {
    "path": "spec/ameba/rule/lint/else_nil_spec.cr",
    "chars": 1657,
    "preview": "require \"../../../spec_helper\"\n\nmodule Ameba::Rule::Lint\n  describe ElseNil do\n    subject = ElseNil.new\n\n    {% for key"
  },
  {
    "path": "spec/ameba/rule/lint/empty_ensure_spec.cr",
    "chars": 1368,
    "preview": "require \"../../../spec_helper\"\n\nmodule Ameba::Rule::Lint\n  describe EmptyEnsure do\n    subject = EmptyEnsure.new\n\n    it"
  },
  {
    "path": "spec/ameba/rule/lint/empty_expression_spec.cr",
    "chars": 2246,
    "preview": "require \"../../../spec_helper\"\n\nprivate def it_detects_empty_expression(code, *, file = __FILE__, line = __LINE__)\n  it "
  },
  {
    "path": "spec/ameba/rule/lint/empty_loop_spec.cr",
    "chars": 1419,
    "preview": "require \"../../../spec_helper\"\n\nmodule Ameba::Rule::Lint\n  describe EmptyLoop do\n    subject = EmptyLoop.new\n\n    it \"do"
  },
  {
    "path": "spec/ameba/rule/lint/enum_member_name_conflict_spec.cr",
    "chars": 981,
    "preview": "require \"../../../spec_helper\"\n\nmodule Ameba::Rule::Lint\n  describe EnumMemberNameConflict do\n    subject = EnumMemberNa"
  },
  {
    "path": "spec/ameba/rule/lint/formatting_spec.cr",
    "chars": 1284,
    "preview": "require \"../../../spec_helper\"\n\nmodule Ameba::Rule::Lint\n  describe Formatting do\n    subject = Formatting.new\n\n    it \""
  },
  {
    "path": "spec/ameba/rule/lint/hash_duplicated_key_spec.cr",
    "chars": 1276,
    "preview": "require \"../../../spec_helper\"\n\nmodule Ameba::Rule::Lint\n  describe HashDuplicatedKey do\n    subject = HashDuplicatedKey"
  },
  {
    "path": "spec/ameba/rule/lint/literal_assignments_in_expressions_spec.cr",
    "chars": 1314,
    "preview": "require \"../../../spec_helper\"\n\nLITERAL_SAMPLES = {\n  nil, true, 42, 4.2, 'c', \"foo\", :foo, /foo/,\n  0..42, [1, 2, 3], {"
  },
  {
    "path": "spec/ameba/rule/lint/literal_in_condition_spec.cr",
    "chars": 5172,
    "preview": "require \"../../../spec_helper\"\n\nmodule Ameba::Rule::Lint\n  describe LiteralInCondition do\n    subject = LiteralInConditi"
  },
  {
    "path": "spec/ameba/rule/lint/literal_in_interpolation_spec.cr",
    "chars": 1180,
    "preview": "require \"../../../spec_helper\"\n\nmodule Ameba::Rule::Lint\n  describe LiteralInInterpolation do\n    subject = LiteralInInt"
  },
  {
    "path": "spec/ameba/rule/lint/literals_comparison_spec.cr",
    "chars": 2139,
    "preview": "require \"../../../spec_helper\"\n\nmodule Ameba::Rule::Lint\n  describe LiteralsComparison do\n    subject = LiteralsComparis"
  },
  {
    "path": "spec/ameba/rule/lint/missing_block_argument_spec.cr",
    "chars": 1053,
    "preview": "require \"../../../spec_helper\"\n\nmodule Ameba::Rule::Lint\n  describe MissingBlockArgument do\n    subject = MissingBlockAr"
  },
  {
    "path": "spec/ameba/rule/lint/non_existent_rule_spec.cr",
    "chars": 1053,
    "preview": "require \"../../../spec_helper\"\n\nmodule Ameba::Rule::Lint\n  describe NonExistentRule do\n    subject = NonExistentRule.new"
  },
  {
    "path": "spec/ameba/rule/lint/not_nil_after_no_bang_spec.cr",
    "chars": 3242,
    "preview": "require \"../../../spec_helper\"\n\nmodule Ameba::Rule::Lint\n  describe NotNilAfterNoBang do\n    subject = NotNilAfterNoBang"
  },
  {
    "path": "spec/ameba/rule/lint/not_nil_spec.cr",
    "chars": 909,
    "preview": "require \"../../../spec_helper\"\n\nmodule Ameba::Rule::Lint\n  describe NotNil do\n    subject = NotNil.new\n\n    it \"passes f"
  },
  {
    "path": "spec/ameba/rule/lint/percent_arrays_spec.cr",
    "chars": 1647,
    "preview": "require \"../../../spec_helper\"\n\nmodule Ameba::Rule::Lint\n  describe PercentArrays do\n    subject = PercentArrays.new\n\n  "
  },
  {
    "path": "spec/ameba/rule/lint/rand_zero_spec.cr",
    "chars": 623,
    "preview": "require \"../../../spec_helper\"\n\nmodule Ameba::Rule::Lint\n  describe RandZero do\n    subject = RandZero.new\n\n    it \"pass"
  },
  {
    "path": "spec/ameba/rule/lint/redundant_string_cercion_spec.cr",
    "chars": 1532,
    "preview": "require \"../../../spec_helper\"\n\nmodule Ameba::Rule::Lint\n  describe RedundantStringCoercion do\n    subject = RedundantSt"
  },
  {
    "path": "spec/ameba/rule/lint/redundant_with_index_spec.cr",
    "chars": 3519,
    "preview": "require \"../../../spec_helper\"\n\nmodule Ameba::Rule::Lint\n  describe RedundantWithIndex do\n    subject = RedundantWithInd"
  },
  {
    "path": "spec/ameba/rule/lint/redundant_with_object_spec.cr",
    "chars": 1742,
    "preview": "require \"../../../spec_helper\"\n\nmodule Ameba::Rule::Lint\n  describe RedundantWithObject do\n    subject = RedundantWithOb"
  },
  {
    "path": "spec/ameba/rule/lint/require_parentheses_spec.cr",
    "chars": 1753,
    "preview": "require \"../../../spec_helper\"\n\nmodule Ameba::Rule::Lint\n  describe RequireParentheses do\n    subject = RequireParenthes"
  },
  {
    "path": "spec/ameba/rule/lint/self_initialize_definition_spec.cr",
    "chars": 1656,
    "preview": "require \"../../../spec_helper\"\n\nmodule Ameba::Rule::Lint\n  describe SelfInitializeDefinition do\n    subject = SelfInitia"
  },
  {
    "path": "spec/ameba/rule/lint/shadowed_argument_spec.cr",
    "chars": 5103,
    "preview": "require \"../../../spec_helper\"\n\nmodule Ameba::Rule::Lint\n  describe ShadowedArgument do\n    subject = ShadowedArgument.n"
  },
  {
    "path": "spec/ameba/rule/lint/shadowed_exception_spec.cr",
    "chars": 5463,
    "preview": "require \"../../../spec_helper\"\n\nmodule Ameba::Rule::Lint\n  describe ShadowedException do\n    subject = ShadowedException"
  },
  {
    "path": "spec/ameba/rule/lint/shadowing_outer_local_var_spec.cr",
    "chars": 7268,
    "preview": "require \"../../../spec_helper\"\n\nmodule Ameba::Rule::Lint\n  describe ShadowingOuterLocalVar do\n    subject = ShadowingOut"
  },
  {
    "path": "spec/ameba/rule/lint/shared_var_in_fiber_spec.cr",
    "chars": 4954,
    "preview": "require \"../../../spec_helper\"\n\nmodule Ameba::Rule::Lint\n  describe SharedVarInFiber do\n    subject = SharedVarInFiber.n"
  },
  {
    "path": "spec/ameba/rule/lint/signal_trap_spec.cr",
    "chars": 1244,
    "preview": "require \"../../../spec_helper\"\n\nmodule Ameba::Rule::Lint\n  describe SignalTrap do\n    subject = SignalTrap.new\n\n    it \""
  },
  {
    "path": "spec/ameba/rule/lint/spec_eq_with_bool_or_nil_literal_spec.cr",
    "chars": 2502,
    "preview": "require \"../../../spec_helper\"\n\nmodule Ameba::Rule::Lint\n  describe SpecEqWithBoolOrNilLiteral do\n    subject = SpecEqWi"
  },
  {
    "path": "spec/ameba/rule/lint/spec_filename_spec.cr",
    "chars": 1545,
    "preview": "require \"../../../spec_helper\"\n\nmodule Ameba::Rule::Lint\n  describe SpecFilename do\n    subject = SpecFilename.new\n\n    "
  },
  {
    "path": "spec/ameba/rule/lint/spec_focus_spec.cr",
    "chars": 3509,
    "preview": "require \"../../../spec_helper\"\n\nmodule Ameba::Rule::Lint\n  describe SpecFocus do\n    subject = SpecFocus.new\n\n    it \"do"
  },
  {
    "path": "spec/ameba/rule/lint/syntax_spec.cr",
    "chars": 696,
    "preview": "require \"../../../spec_helper\"\n\nmodule Ameba::Rule::Lint\n  describe Syntax do\n    subject = Syntax.new\n\n    it \"passes i"
  },
  {
    "path": "spec/ameba/rule/lint/top_level_operator_definition_spec.cr",
    "chars": 1754,
    "preview": "require \"../../../spec_helper\"\n\nmodule Ameba::Rule::Lint\n  describe TopLevelOperatorDefinition do\n    subject = TopLevel"
  },
  {
    "path": "spec/ameba/rule/lint/trailing_rescue_exception_spec.cr",
    "chars": 776,
    "preview": "require \"../../../spec_helper\"\n\nmodule Ameba::Rule::Lint\n  describe TrailingRescueException do\n    subject = TrailingRes"
  },
  {
    "path": "spec/ameba/rule/lint/typos_spec.cr",
    "chars": 817,
    "preview": "require \"../../../spec_helper\"\n\nprivate def check_typos_bin!\n  unless Ameba::Rule::Lint::Typos::BIN_PATH\n    pending! \"`"
  },
  {
    "path": "spec/ameba/rule/lint/unneeded_disable_directive_spec.cr",
    "chars": 2762,
    "preview": "require \"../../../spec_helper\"\n\nmodule Ameba::Rule::Lint\n  describe UnneededDisableDirective do\n    subject = UnneededDi"
  },
  {
    "path": "spec/ameba/rule/lint/unreachable_code_spec.cr",
    "chars": 14357,
    "preview": "require \"../../../spec_helper\"\n\nmodule Ameba::Rule::Lint\n  describe UnreachableCode do\n    subject = UnreachableCode.new"
  },
  {
    "path": "spec/ameba/rule/lint/unused_argument_spec.cr",
    "chars": 8022,
    "preview": "require \"../../../spec_helper\"\n\nmodule Ameba::Rule::Lint\n  describe UnusedArgument do\n    subject = UnusedArgument.new\n "
  },
  {
    "path": "spec/ameba/rule/lint/unused_block_argument_spec.cr",
    "chars": 3277,
    "preview": "require \"../../../spec_helper\"\n\nmodule Ameba::Rule::Lint\n  describe UnusedBlockArgument do\n    subject = UnusedBlockArgu"
  },
  {
    "path": "spec/ameba/rule/lint/unused_expression_spec.cr",
    "chars": 31720,
    "preview": "require \"../../../spec_helper\"\n\nmodule Ameba::Rule::Lint\n  describe UnusedExpression do\n    subject = UnusedExpression.n"
  },
  {
    "path": "spec/ameba/rule/lint/unused_rescue_variable_spec.cr",
    "chars": 5116,
    "preview": "require \"../../../spec_helper\"\n\nmodule Ameba::Rule::Lint\n  describe UnusedRescueVariable do\n    subject = UnusedRescueVa"
  },
  {
    "path": "spec/ameba/rule/lint/useless_assign_spec.cr",
    "chars": 37931,
    "preview": "require \"../../../spec_helper\"\n\nmodule Ameba::Rule::Lint\n  describe UselessAssign do\n    subject = UselessAssign.new\n\n  "
  },
  {
    "path": "spec/ameba/rule/lint/useless_condition_in_when_spec.cr",
    "chars": 670,
    "preview": "require \"../../../spec_helper\"\n\nmodule Ameba::Rule::Lint\n  describe UselessConditionInWhen do\n    subject = UselessCondi"
  },
  {
    "path": "spec/ameba/rule/lint/useless_visibility_modifier_spec.cr",
    "chars": 1501,
    "preview": "require \"../../../spec_helper\"\n\nmodule Ameba::Rule::Lint\n  describe UselessVisibilityModifier do\n    subject = UselessVi"
  },
  {
    "path": "spec/ameba/rule/lint/void_outside_lib_spec.cr",
    "chars": 2704,
    "preview": "require \"../../../spec_helper\"\n\nmodule Ameba::Rule::Lint\n  describe VoidOutsideLib do\n    subject = VoidOutsideLib.new\n\n"
  },
  {
    "path": "spec/ameba/rule/lint/whitespace_around_macro_expression_spec.cr",
    "chars": 1692,
    "preview": "require \"../../../spec_helper\"\n\nmodule Ameba::Rule::Lint\n  describe WhitespaceAroundMacroExpression do\n    subject = Whi"
  },
  {
    "path": "spec/ameba/rule/metrics/cyclomatic_complexity_spec.cr",
    "chars": 1206,
    "preview": "require \"../../../spec_helper\"\n\nmodule Ameba::Rule::Metrics\n  describe CyclomaticComplexity do\n    subject = CyclomaticC"
  },
  {
    "path": "spec/ameba/rule/naming/accessor_method_name_spec.cr",
    "chars": 2999,
    "preview": "require \"../../../spec_helper\"\n\nmodule Ameba::Rule::Naming\n  describe AccessorMethodName do\n    subject = AccessorMethod"
  },
  {
    "path": "spec/ameba/rule/naming/ascii_identifiers_spec.cr",
    "chars": 4958,
    "preview": "require \"../../../spec_helper\"\n\nmodule Ameba::Rule::Naming\n  describe AsciiIdentifiers do\n    subject = AsciiIdentifiers"
  },
  {
    "path": "spec/ameba/rule/naming/binary_operator_parameter_name_spec.cr",
    "chars": 2301,
    "preview": "require \"../../../spec_helper\"\n\nmodule Ameba::Rule::Naming\n  describe BinaryOperatorParameterName do\n    subject = Binar"
  },
  {
    "path": "spec/ameba/rule/naming/block_parameter_name_spec.cr",
    "chars": 3050,
    "preview": "require \"../../../spec_helper\"\n\nmodule Ameba::Rule::Naming\n  describe BlockParameterName do\n    subject = BlockParameter"
  },
  {
    "path": "spec/ameba/rule/naming/constant_names_spec.cr",
    "chars": 1281,
    "preview": "require \"../../../spec_helper\"\n\nprivate def it_reports_constant(name, value, expected, *, file = __FILE__, line = __LINE"
  },
  {
    "path": "spec/ameba/rule/naming/filename_spec.cr",
    "chars": 511,
    "preview": "require \"../../../spec_helper\"\n\nmodule Ameba::Rule::Naming\n  describe Filename do\n    subject = Filename.new\n\n    it \"pa"
  },
  {
    "path": "spec/ameba/rule/naming/method_names_spec.cr",
    "chars": 1049,
    "preview": "require \"../../../spec_helper\"\n\nprivate def it_reports_method_name(name, expected, *, file = __FILE__, line = __LINE__)\n"
  },
  {
    "path": "spec/ameba/rule/naming/predicate_name_spec.cr",
    "chars": 1101,
    "preview": "require \"../../../spec_helper\"\n\nmodule Ameba::Rule::Naming\n  describe PredicateName do\n    subject = PredicateName.new\n\n"
  },
  {
    "path": "spec/ameba/rule/naming/query_bool_methods_spec.cr",
    "chars": 2050,
    "preview": "require \"../../../spec_helper\"\n\nmodule Ameba::Rule::Naming\n  describe QueryBoolMethods do\n    subject = QueryBoolMethods"
  },
  {
    "path": "spec/ameba/rule/naming/rescued_exceptions_variable_name_spec.cr",
    "chars": 1457,
    "preview": "require \"../../../spec_helper\"\n\nmodule Ameba::Rule::Naming\n  describe RescuedExceptionsVariableName do\n    subject = Res"
  },
  {
    "path": "spec/ameba/rule/naming/type_names_spec.cr",
    "chars": 1476,
    "preview": "require \"../../../spec_helper\"\n\nprivate def it_reports_name(type, name, expected, *, file = __FILE__, line = __LINE__)\n "
  },
  {
    "path": "spec/ameba/rule/naming/variable_names_spec.cr",
    "chars": 2172,
    "preview": "require \"../../../spec_helper\"\n\nprivate def it_reports_var_name(name, value, expected, *, file = __FILE__, line = __LINE"
  },
  {
    "path": "spec/ameba/rule/performance/any_after_filter_spec.cr",
    "chars": 2203,
    "preview": "require \"../../../spec_helper\"\n\nmodule Ameba::Rule::Performance\n  describe AnyAfterFilter do\n    subject = AnyAfterFilte"
  },
  {
    "path": "spec/ameba/rule/performance/any_instead_of_present_spec.cr",
    "chars": 1116,
    "preview": "require \"../../../spec_helper\"\n\nmodule Ameba::Rule::Performance\n  describe AnyInsteadOfPresent do\n    subject = AnyInste"
  },
  {
    "path": "spec/ameba/rule/performance/base_spec.cr",
    "chars": 454,
    "preview": "require \"../../../spec_helper\"\n\nmodule Ameba::Rule::Performance\n  describe Base do\n    subject = PerfRule.new\n\n    descr"
  },
  {
    "path": "spec/ameba/rule/performance/chained_call_with_no_bang_spec.cr",
    "chars": 2046,
    "preview": "require \"../../../spec_helper\"\n\nmodule Ameba::Rule::Performance\n  describe ChainedCallWithNoBang do\n    subject = Chaine"
  },
  {
    "path": "spec/ameba/rule/performance/compact_after_map_spec.cr",
    "chars": 1283,
    "preview": "require \"../../../spec_helper\"\n\nmodule Ameba::Rule::Performance\n  describe CompactAfterMap do\n    subject = CompactAfter"
  },
  {
    "path": "spec/ameba/rule/performance/excessive_allocations_spec.cr",
    "chars": 1944,
    "preview": "require \"../../../spec_helper\"\n\nmodule Ameba::Rule::Performance\n  describe ExcessiveAllocations do\n    subject = Excessi"
  },
  {
    "path": "spec/ameba/rule/performance/first_last_after_filter_spec.cr",
    "chars": 2791,
    "preview": "require \"../../../spec_helper\"\n\nmodule Ameba::Rule::Performance\n  describe FirstLastAfterFilter do\n    subject = FirstLa"
  },
  {
    "path": "spec/ameba/rule/performance/flatten_after_map_spec.cr",
    "chars": 1117,
    "preview": "require \"../../../spec_helper\"\n\nmodule Ameba::Rule::Performance\n  describe FlattenAfterMap do\n    subject = FlattenAfter"
  },
  {
    "path": "spec/ameba/rule/performance/map_instead_of_block_spec.cr",
    "chars": 1590,
    "preview": "require \"../../../spec_helper\"\n\nmodule Ameba::Rule::Performance\n  describe MapInsteadOfBlock do\n    subject = MapInstead"
  },
  {
    "path": "spec/ameba/rule/performance/minmax_after_map_spec.cr",
    "chars": 1723,
    "preview": "require \"../../../spec_helper\"\n\nmodule Ameba::Rule::Performance\n  describe MinMaxAfterMap do\n    subject = MinMaxAfterMa"
  },
  {
    "path": "spec/ameba/rule/performance/size_after_filter_spec.cr",
    "chars": 2082,
    "preview": "require \"../../../spec_helper\"\n\nmodule Ameba::Rule::Performance\n  describe SizeAfterFilter do\n    subject = SizeAfterFil"
  },
  {
    "path": "spec/ameba/rule/performance/times_map_spec.cr",
    "chars": 1732,
    "preview": "require \"../../../spec_helper\"\n\nmodule Ameba::Rule::Performance\n  describe TimesMap do\n    subject = TimesMap.new\n\n    i"
  },
  {
    "path": "spec/ameba/rule/style/array_literal_syntax_spec.cr",
    "chars": 1434,
    "preview": "require \"../../../spec_helper\"\n\nmodule Ameba::Rule::Style\n  describe ArrayLiteralSyntax do\n    subject = ArrayLiteralSyn"
  },
  {
    "path": "spec/ameba/rule/style/call_parentheses_spec.cr",
    "chars": 8847,
    "preview": "require \"../../../spec_helper\"\n\nmodule Ameba::Rule::Style\n  describe CallParentheses do\n    subject = CallParentheses.ne"
  },
  {
    "path": "spec/ameba/rule/style/elsif_spec.cr",
    "chars": 2598,
    "preview": "require \"../../../spec_helper\"\n\nmodule Ameba::Rule::Style\n  describe Elsif do\n    subject = Elsif.new\n\n    it \"does not "
  },
  {
    "path": "spec/ameba/rule/style/guard_clause_spec.cr",
    "chars": 10300,
    "preview": "require \"../../../spec_helper\"\n\nprivate def it_reports_body(body, *, file = __FILE__, line = __LINE__)\n  rule = Ameba::R"
  },
  {
    "path": "spec/ameba/rule/style/hash_literal_syntax_spec.cr",
    "chars": 1218,
    "preview": "require \"../../../spec_helper\"\n\nmodule Ameba::Rule::Style\n  describe HashLiteralSyntax do\n    subject = HashLiteralSynta"
  },
  {
    "path": "spec/ameba/rule/style/heredoc_escape_spec.cr",
    "chars": 3724,
    "preview": "require \"../../../spec_helper\"\n\nmodule Ameba::Rule::Style\n  describe HeredocEscape do\n    subject = HeredocEscape.new\n\n "
  },
  {
    "path": "spec/ameba/rule/style/heredoc_indent_spec.cr",
    "chars": 5249,
    "preview": "require \"../../../spec_helper\"\n\nmodule Ameba::Rule::Style\n  describe HeredocIndent do\n    subject = HeredocIndent.new\n\n "
  },
  {
    "path": "spec/ameba/rule/style/is_a_filter_spec.cr",
    "chars": 1755,
    "preview": "require \"../../../spec_helper\"\n\nmodule Ameba::Rule::Style\n  describe IsAFilter do\n    subject = IsAFilter.new\n\n    it \"p"
  },
  {
    "path": "spec/ameba/rule/style/is_a_nil_spec.cr",
    "chars": 938,
    "preview": "require \"../../../spec_helper\"\n\nmodule Ameba::Rule::Style\n  describe IsANil do\n    subject = IsANil.new\n\n    it \"doesn't"
  },
  {
    "path": "spec/ameba/rule/style/large_numbers_spec.cr",
    "chars": 3240,
    "preview": "require \"../../../spec_helper\"\n\nprivate def it_transforms(number, expected, *, file = __FILE__, line = __LINE__)\n  it \"t"
  },
  {
    "path": "spec/ameba/rule/style/multiline_curly_block_spec.cr",
    "chars": 1133,
    "preview": "require \"../../../spec_helper\"\n\nmodule Ameba::Rule::Style\n  describe MultilineCurlyBlock do\n    subject = MultilineCurly"
  },
  {
    "path": "spec/ameba/rule/style/multiline_string_literal_spec.cr",
    "chars": 3652,
    "preview": "require \"../../../spec_helper\"\n\nmodule Ameba::Rule::Style\n  describe MultilineStringLiteral do\n    subject = MultilineSt"
  },
  {
    "path": "spec/ameba/rule/style/negated_conditions_in_unless_spec.cr",
    "chars": 1366,
    "preview": "require \"../../../spec_helper\"\n\nmodule Ameba::Rule::Style\n  describe NegatedConditionsInUnless do\n    subject = NegatedC"
  },
  {
    "path": "spec/ameba/rule/style/parentheses_around_condition_spec.cr",
    "chars": 5805,
    "preview": "require \"../../../spec_helper\"\n\nmodule Ameba::Rule::Style\n  describe ParenthesesAroundCondition do\n    subject = Parenth"
  },
  {
    "path": "spec/ameba/rule/style/percent_literal_delimiters_spec.cr",
    "chars": 5080,
    "preview": "require \"../../../spec_helper\"\n\nmodule Ameba::Rule::Style\n  describe PercentLiteralDelimiters do\n    subject = PercentLi"
  },
  {
    "path": "spec/ameba/rule/style/redundant_begin_spec.cr",
    "chars": 6627,
    "preview": "require \"../../../spec_helper\"\n\nmodule Ameba::Rule::Style\n  describe RedundantBegin do\n    subject = RedundantBegin.new\n"
  },
  {
    "path": "spec/ameba/rule/style/redundant_next_spec.cr",
    "chars": 6000,
    "preview": "require \"../../../spec_helper\"\n\nmodule Ameba::Rule::Style\n  describe RedundantNext do\n    subject = RedundantNext.new\n\n "
  },
  {
    "path": "spec/ameba/rule/style/redundant_nil_in_control_expression_spec.cr",
    "chars": 2478,
    "preview": "require \"../../../spec_helper\"\n\nmodule Ameba::Rule::Style\n  describe RedundantNilInControlExpression do\n    subject = Re"
  },
  {
    "path": "spec/ameba/rule/style/redundant_return_spec.cr",
    "chars": 8249,
    "preview": "require \"../../../spec_helper\"\n\nmodule Ameba::Rule::Style\n  describe RedundantReturn do\n    subject = RedundantReturn.ne"
  },
  {
    "path": "spec/ameba/rule/style/redundant_self_spec.cr",
    "chars": 8721,
    "preview": "require \"../../../spec_helper\"\n\nmodule Ameba::Rule::Style\n  describe RedundantSelf do\n    subject = RedundantSelf.new\n\n "
  },
  {
    "path": "spec/ameba/rule/style/unless_else_spec.cr",
    "chars": 678,
    "preview": "require \"../../../spec_helper\"\n\nmodule Ameba::Rule::Style\n  describe UnlessElse do\n    subject = UnlessElse.new\n\n    it "
  },
  {
    "path": "spec/ameba/rule/style/verbose_block_spec.cr",
    "chars": 10266,
    "preview": "require \"../../../spec_helper\"\n\nmodule Ameba::Rule::Style\n  describe VerboseBlock do\n    subject = VerboseBlock.new\n\n   "
  },
  {
    "path": "spec/ameba/rule/style/verbose_nil_type_spec.cr",
    "chars": 4216,
    "preview": "require \"../../../spec_helper\"\n\nmodule Ameba::Rule::Style\n  describe VerboseNilType do\n    subject = VerboseNilType.new\n"
  },
  {
    "path": "spec/ameba/rule/style/while_true_spec.cr",
    "chars": 742,
    "preview": "require \"../../../spec_helper\"\n\nmodule Ameba::Rule::Style\n  describe WhileTrue do\n    subject = WhileTrue.new\n\n    it \"p"
  },
  {
    "path": "spec/ameba/rule/typing/macro_call_argument_type_restriction_spec.cr",
    "chars": 2037,
    "preview": "require \"../../../spec_helper\"\n\nmodule Ameba::Rule::Typing\n  describe MacroCallArgumentTypeRestriction do\n    subject = "
  },
  {
    "path": "spec/ameba/rule/typing/method_parameter_type_restriction_spec.cr",
    "chars": 6651,
    "preview": "require \"../../../spec_helper\"\n\nmodule Ameba::Rule::Typing\n  describe MethodParameterTypeRestriction do\n    subject = Me"
  },
  {
    "path": "spec/ameba/rule/typing/method_return_type_restriction_spec.cr",
    "chars": 4682,
    "preview": "require \"../../../spec_helper\"\n\nmodule Ameba::Rule::Typing\n  describe MethodReturnTypeRestriction do\n    subject = Metho"
  },
  {
    "path": "spec/ameba/rule/typing/proc_literal_return_type_restriction_spec.cr",
    "chars": 613,
    "preview": "require \"../../../spec_helper\"\n\nmodule Ameba::Rule::Typing\n  describe ProcLiteralReturnTypeRestriction do\n    subject = "
  },
  {
    "path": "spec/ameba/runner_spec.cr",
    "chars": 12182,
    "preview": "require \"../spec_helper\"\n\nmodule Ameba\n  private def runner(files = [__FILE__], formatter = DummyFormatter.new)\n    conf"
  },
  {
    "path": "spec/ameba/severity_spec.cr",
    "chars": 3229,
    "preview": "require \"../spec_helper\"\n\nmodule Ameba\n  describe Severity do\n    describe \"#symbol\" do\n      it \"returns the symbol for"
  },
  {
    "path": "spec/ameba/source/rewriter_spec.cr",
    "chars": 3241,
    "preview": "require \"../../spec_helper\"\n\nclass Ameba::Source\n  describe Rewriter do\n    code = \"puts(:hello, :world)\"\n    hello = {5"
  },
  {
    "path": "spec/ameba/source_spec.cr",
    "chars": 3009,
    "preview": "require \"../spec_helper\"\n\nmodule Ameba\n  describe Source do\n    describe \".new\" do\n      it \"allows to create a source b"
  },
  {
    "path": "spec/ameba/spec/annotated_source_spec.cr",
    "chars": 6875,
    "preview": "require \"../../spec_helper\"\n\nprivate def dummy_issue(code,\n                        message,\n                        posi"
  },
  {
    "path": "spec/ameba/tokenizer_spec.cr",
    "chars": 1449,
    "preview": "require \"../spec_helper\"\n\nmodule Ameba\n  private def it_tokenizes(str, expected, *, file = __FILE__, line = __LINE__)\n  "
  },
  {
    "path": "spec/ameba/version_spec.cr",
    "chars": 2554,
    "preview": "require \"../spec_helper\"\n\nprivate def build_ameba_version(string)\n  Ameba::Version.new(SemanticVersion.parse(string))\nen"
  },
  {
    "path": "spec/ameba_spec.cr",
    "chars": 419,
    "preview": "require \"./spec_helper\"\n\ndescribe Ameba do\n  context \"VERSION\" do\n    it \"contains a version-like string\" do\n      Ameba"
  },
  {
    "path": "spec/fixtures/.ameba.yml",
    "chars": 89,
    "preview": "Version: \"1.5.0\"\n\nFormatter:\n  Name: flycheck\n\nLint/ComparisonToBoolean:\n  Enabled: true\n"
  },
  {
    "path": "spec/spec_helper.cr",
    "chars": 8828,
    "preview": "require \"spec\"\nrequire \"../src/ameba\"\nrequire \"../src/ameba/spec/support\"\n\nmodule Ameba\n  # Dummy Rule which does nothin"
  },
  {
    "path": "src/ameba/ast/flow_expression.cr",
    "chars": 1642,
    "preview": "require \"./util\"\n\nmodule Ameba::AST\n  # Represents a flow expression in Crystal code.\n  # For example,\n  #\n  # ```\n  # d"
  },
  {
    "path": "src/ameba/ast/liveness_analyzer.cr",
    "chars": 14700,
    "preview": "module Ameba::AST\n  # Performs backward dataflow liveness analysis on a scope's AST to detect\n  # dead stores (assignmen"
  },
  {
    "path": "src/ameba/ast/scope.cr",
    "chars": 6854,
    "preview": "require \"./variabling/*\"\n\nmodule Ameba::AST\n  # Represents a context of the local variable visibility.\n  # This is where"
  },
  {
    "path": "src/ameba/ast/util.cr",
    "chars": 11528,
    "preview": "# Utility module for Ameba's rules.\nmodule Ameba::AST::Util\n  extend self\n\n  # Returns tuple with two bool flags:\n  #\n  "
  },
  {
    "path": "src/ameba/ast/variabling/argument.cr",
    "chars": 1119,
    "preview": "module Ameba::AST\n  # Represents the argument of some node.\n  # Holds the reference to the variable, thus to scope.\n  #\n"
  },
  {
    "path": "src/ameba/ast/variabling/assignment.cr",
    "chars": 1254,
    "preview": "require \"./reference\"\nrequire \"./variable\"\n\nmodule Ameba::AST\n  # Represents the assignment to the variable.\n  # Holds t"
  },
  {
    "path": "src/ameba/ast/variabling/ivariable.cr",
    "chars": 193,
    "preview": "module Ameba::AST\n  class InstanceVariable\n    getter node : Crystal::InstanceVar\n\n    delegate location, end_location, "
  },
  {
    "path": "src/ameba/ast/variabling/reference.cr",
    "chars": 246,
    "preview": "require \"./variable\"\n\nmodule Ameba::AST\n  # Represents a reference to the variable.\n  # It behaves like a variable is us"
  },
  {
    "path": "src/ameba/ast/variabling/type_dec_variable.cr",
    "chars": 415,
    "preview": "module Ameba::AST\n  class TypeDecVariable\n    getter node : Crystal::TypeDeclaration\n\n    delegate location, end_locatio"
  },
  {
    "path": "src/ameba/ast/variabling/variable.cr",
    "chars": 4119,
    "preview": "module Ameba::AST\n  # Represents the existence of the local variable.\n  # Holds the var node and variable assignments.\n "
  },
  {
    "path": "src/ameba/ast/visitors/base_visitor.cr",
    "chars": 780,
    "preview": "require \"compiler/crystal/syntax/*\"\n\n# A module that helps to traverse Crystal AST using `Crystal::Visitor`.\nmodule Ameb"
  },
  {
    "path": "src/ameba/ast/visitors/counting_visitor.cr",
    "chars": 1568,
    "preview": "module Ameba::AST\n  # AST Visitor that counts occurrences of certain keywords\n  class CountingVisitor < Crystal::Visitor"
  },
  {
    "path": "src/ameba/ast/visitors/elseif_aware_node_visitor.cr",
    "chars": 1841,
    "preview": "require \"./node_visitor\"\n\nmodule Ameba::AST\n  # A class that utilizes a logic inherited from `NodeVisitor` to traverse A"
  },
  {
    "path": "src/ameba/ast/visitors/flow_expression_visitor.cr",
    "chars": 1051,
    "preview": "require \"../util\"\nrequire \"./base_visitor\"\n\nmodule Ameba::AST\n  # AST Visitor that traverses all the flow expressions.\n "
  }
]

// ... and 162 more files (download for full content)

About this extraction

This page contains the full source code of the veelenga/ameba GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 362 files (1.0 MB), approximately 289.8k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!