ryo-suggest 0.1.0

[experimental] Pattern-based suggestion engine for RYO
Documentation
# Builtin lint rules for ryo-suggest
#
# These rules are shipped with the ryo binary and provide
# sensible defaults for common code quality checks.
#
# Users can override these rules by creating rules with the
# same ID in ~/.ryo/rules/custom/ or <project>/.ryo/rules/

# ============================================================
# Error Handling Rules
# ============================================================

- id: "RL001"
  name: "no-unwrap-in-public"
  severity: Warning
  category: "error-handling"
  scope: [lib, bin]
  query:
    kind: Function
    match:
      vis: Public
    body:
      contains:
        - node: MethodCall
          method: "unwrap"
  message: "Avoid unwrap() in public function"
  suggestion: "Use ? operator or expect() with a descriptive message"
  fix:
    type: UnwrapToQuestion
    include_expect: false

- id: "RL002"
  name: "no-expect-in-public"
  severity: Info
  category: "error-handling"
  scope: [lib, bin]
  query:
    kind: Function
    match:
      vis: Public
    body:
      contains:
        - node: MethodCall
          method: "expect"
  message: "Consider using ? operator instead of expect() in public function"
  suggestion: "Return Result/Option to let the caller handle the error"
  fix:
    type: UnwrapToQuestion
    include_expect: true

- id: "RL003"
  name: "no-indexing"
  severity: Warning
  category: "error-handling"
  scope: [lib, bin]
  query:
    kind: Function
    body:
      contains:
        - node: Index
  message: "Direct indexing can panic if index is out of bounds"
  suggestion: "Use .get(i) or .get_mut(i) for safe access, or ensure bounds checking"

- id: "RL004"
  name: "unwrap-or-default"
  severity: Info
  category: "idiom"
  query:
    kind: Function
  message: "Consider using unwrap_or_default() instead of unwrap_or(Default::default())"
  suggestion: "Replace .unwrap_or(Default::default()) with .unwrap_or_default()"
  fix:
    type: UnwrapOrDefault

# ============================================================
# Panic Prevention Rules
# ============================================================

- id: "RL010"
  name: "no-panic-macro"
  severity: Warning
  category: "panic-prevention"
  scope: [lib]
  query:
    kind: Function
    body:
      contains:
        - node: MacroCall
          macro: "panic"
  message: "Avoid panic! macro in library code"
  suggestion: "Return an error type instead of panicking"

- id: "RL011"
  name: "no-todo-macro"
  severity: Info
  category: "panic-prevention"
  scope: [lib, bin]
  query:
    kind: Function
    body:
      contains:
        - node: MacroCall
          macro: "todo"
  message: "Found todo!() macro - incomplete implementation"
  suggestion: "Implement the missing functionality or return a proper error"

- id: "RL012"
  name: "no-unimplemented-macro"
  severity: Info
  category: "panic-prevention"
  scope: [lib, bin]
  query:
    kind: Function
    body:
      contains:
        - node: MacroCall
          macro: "unimplemented"
  message: "Found unimplemented!() macro"
  suggestion: "Implement the functionality or return NotImplemented error"

# ============================================================
# Debug Code Detection
# ============================================================

- id: "RL020"
  name: "no-dbg-macro"
  severity: Warning
  category: "debug-code"
  scope: [lib, bin]
  query:
    kind: Function
    body:
      contains:
        - node: MacroCall
          macro: "dbg"
  message: "Found dbg!() macro - debug code should be removed"
  suggestion: "Remove dbg!() before committing"
  fix:
    type: RemoveStatement
    pattern: "dbg!(..)"
    remove_all: true

- id: "RL021"
  name: "no-println-in-lib"
  severity: Info
  category: "debug-code"
  scope: [lib]
  query:
    kind: Function
    body:
      contains:
        - node: MacroCall
          macro: "println"
  message: "Found println!() in library code"
  suggestion: "Use tracing or log macros for production logging"
  fix:
    type: RemoveStatement
    pattern: "println!(..)"
    remove_all: true

# ============================================================
# Idiom Simplification Rules
# ============================================================

- id: "RL030"
  name: "bool-comparison"
  severity: Info
  category: "idiom"
  query:
    kind: Function
    body:
      contains:
        - node: BinaryOp
          op: "=="
          right:
            node: Literal
            value: "true"
  message: "Comparison with boolean literal can be simplified"
  suggestion: "Use `x` instead of `x == true`, or `!x` instead of `x == false`"
  fix:
    type: BoolSimplify

- id: "RL031"
  name: "bool-comparison-false"
  severity: Info
  category: "idiom"
  query:
    kind: Function
    body:
      contains:
        - node: BinaryOp
          op: "=="
          right:
            node: Literal
            value: "false"
  message: "Comparison with false can be simplified"
  suggestion: "Use `!x` instead of `x == false`"
  fix:
    type: BoolSimplify

- id: "RL032"
  name: "bool-not-equal-true"
  severity: Info
  category: "idiom"
  query:
    kind: Function
    body:
      contains:
        - node: BinaryOp
          op: "!="
          right:
            node: Literal
            value: "true"
  message: "Comparison != true can be simplified"
  suggestion: "Use `!x` instead of `x != true`"
  fix:
    type: BoolSimplify

- id: "RL033"
  name: "manual-is-some"
  severity: Info
  category: "idiom"
  query:
    kind: Function
    body:
      contains:
        - node: Match
          arm_count: 2
          arms:
            - pattern_path: "Some"
              body:
                node: Literal
                value: "true"
            - pattern_path: "None"
              body:
                node: Literal
                value: "false"
  message: "Manual Option::is_some() implementation detected"
  suggestion: "Replace with .is_some() for clarity"

- id: "RL034"
  name: "manual-is-none"
  severity: Info
  category: "idiom"
  query:
    kind: Function
    body:
      contains:
        - node: Match
          arm_count: 2
          arms:
            - pattern_path: "Some"
              body:
                node: Literal
                value: "false"
            - pattern_path: "None"
              body:
                node: Literal
                value: "true"
  message: "Manual Option::is_none() implementation detected"
  suggestion: "Replace with .is_none() for clarity"

- id: "RL040"
  name: "collapsible-if"
  severity: Info
  category: "idiom"
  query:
    kind: Function
    body:
      contains:
        - node: If
  message: "Nested if statements may be collapsible"
  suggestion: "Consider merging nested if into single if with && condition"
  fix:
    type: CollapsibleIf

- id: "RL043"
  name: "needless-return"
  severity: Info
  category: "idiom"
  query:
    kind: Function
    body:
      contains:
        - node: Return
  message: "Redundant return statement at end of function"
  suggestion: "Remove the return keyword for more idiomatic Rust"

- id: "RL050"
  name: "underscore-noop-arm"
  severity: Warning
  category: "error-handling"
  scope: [lib, bin]
  query:
    kind: Function
  message: "Empty wildcard arm silently discards unmatched variants"
  suggestion: "Expand `_ => {}` into explicit variant arms so each case is handled or intentionally marked with unreachable!()/todo!()"

# ============================================================
# Refactoring Opportunity Rules
# ============================================================

- id: "RL060"
  name: "trait-inline-candidate"
  severity: Hint
  category: "refactor"
  scope: [lib, bin]
  query:
    kind: Trait
  message: "Trait definition found - consider InlineTrait if only one struct implements this"
  suggestion: "If this trait has a single implementation, consider inlining it with `ryo run` using InlineTrait spec"

# ============================================================
# Code Complexity Rules
# ============================================================

- id: "RL090"
  name: "large-function"
  severity: Info
  category: "complexity"
  scope: [lib, bin]
  query:
    kind: Function
  message: "Function has too many statements - consider splitting into smaller functions"
  suggestion: "Extract logical blocks into separate functions for better readability and testability"

- id: "RL091"
  name: "too-many-args"
  severity: Info
  category: "complexity"
  scope: [lib, bin]
  query:
    kind: Function
  message: "Function has too many parameters"
  suggestion: "Consider using a struct to group related parameters, or split the function"

- id: "RL061"
  name: "impl-extract-candidate"
  severity: Hint
  category: "refactor"
  scope: [lib, bin]
  query:
    kind: Impl
  message: "Impl block found - consider ExtractTrait to create reusable trait"
  suggestion: "If multiple structs need similar methods, consider extracting a trait with `ryo run` using ExtractTrait spec"

- id: "RL070"
  name: "enum-to-trait-candidate"
  severity: Hint
  category: "refactor"
  scope: [lib, bin]
  query:
    kind: Enum
  message: "Enum found - consider converting to trait + structs for extensibility"
  suggestion: "If this enum represents polymorphic behavior, consider EnumToTrait refactoring"