// Named, groupable warnings whose reporting level is configurable.
//
// Each warning has a stable hyphenated name and may belong to one or more
// groups; a policy maps each to a level, so the same warning can be silenced,
// shown, or made fatal depending on the invocation. See `-W`.
//
// Ported in FULL even though the front end emits none of these: the type
// checker is what generates warnings, and rebuilding this layer later would
// mean re-verifying every byte-exact renderer a second time.

///|
/// A named warning.
pub(all) enum Warning {
  /// A local that is declared but never read.
  UnusedLocal
  /// A module field nothing reachable references. Liveness is reachability
  /// from the roots, so a dead CYCLE of mutually recursive functions is
  /// reported too.
  UnusedField
  /// An imported field nothing reachable references.
  UnusedImport
  /// A block label that is declared but never branched to.
  UnusedLabel
  /// A global declared mutable but never assigned. Only a module-defined,
  /// non-exported global: an import's mutability is fixed by the linking
  /// contract, and an exported one can be assigned by the host.
  UnnecessaryMut
  /// A shift by a constant count at least the operand's bit width. Wasm masks
  /// the count modulo the width, so this is almost certainly not what was
  /// meant.
  ShiftOverflow
  /// An operation that always traps on a constant operand.
  ConstantTrap
  /// A comparison whose result does not depend on its variable operand.
  TautologicalComparison
  /// A branch, loop, or select condition that is constant.
  ConstantCondition
  /// A side-effect-free result computed and then discarded.
  UnusedResult
  /// A statement following an unconditional branch, return, or unreachable.
  DeadCode
  /// A cast or test whose operand can never have the target type.
  CastAlwaysFails
  /// A trapping or effectful operation inside a branch of `?:`, which compiles
  /// to a `select` and so evaluates BOTH branches.
  EagerSelect
  /// Operators whose relative precedence is easy to misremember, mixed without
  /// parentheses. The code is correct; a reader may misread the grouping.
  Precedence
  /// An operation with no effect on its result.
  RedundantOperation
  /// Path-sensitive validation gave up after too many configurations.
  TruncatedCoverage
  /// Converting from wasm, a name collided and was renamed.
  NamingConflict
  /// Converting from wasm, a name is a Wax reserved word and was renamed.
  ReservedWordRename
  /// Converting from wasm, an unnamed but referenced parameter was given a
  /// generated name.
  GeneratedName
  /// `x = x op e` could read as `x op= e`.
  CompoundAssignment
  /// `{x: x}` could use the punning shorthand `{x}`.
  FieldPunning
  /// A `let` annotation the inferred type already makes redundant.
  RedundantAnnotation
  /// A string holds a bidirectional control character that can make the source
  /// read differently than it runs -- a "Trojan Source" character.
  ConfusableUnicode
} derive(Eq, Debug)

///|
/// Every named warning.
pub let all : Array[Warning] = [
  UnusedLocal,
  UnusedField,
  UnusedImport,
  UnusedLabel,
  UnnecessaryMut,
  ShiftOverflow,
  ConstantTrap,
  TautologicalComparison,
  ConstantCondition,
  UnusedResult,
  DeadCode,
  CastAlwaysFails,
  EagerSelect,
  Precedence,
  RedundantOperation,
  TruncatedCoverage,
  NamingConflict,
  ReservedWordRename,
  GeneratedName,
  CompoundAssignment,
  FieldPunning,
  RedundantAnnotation,
  ConfusableUnicode,
]

///|
/// The stable, hyphenated name.
pub fn Warning::name(self : Warning) -> String {
  match self {
    UnusedLocal => "unused-local"
    UnusedField => "unused-field"
    UnusedImport => "unused-import"
    UnusedLabel => "unused-label"
    UnnecessaryMut => "unnecessary-mut"
    // The name does NOT track the constructor here.
    ShiftOverflow => "shift-count-overflow"
    ConstantTrap => "constant-trap"
    TautologicalComparison => "tautological-comparison"
    ConstantCondition => "constant-condition"
    UnusedResult => "unused-result"
    DeadCode => "dead-code"
    CastAlwaysFails => "cast-always-fails"
    EagerSelect => "eager-select"
    Precedence => "precedence"
    RedundantOperation => "redundant-operation"
    TruncatedCoverage => "truncated-coverage"
    NamingConflict => "naming-conflict"
    ReservedWordRename => "reserved-word-rename"
    GeneratedName => "generated-name"
    CompoundAssignment => "compound-assignment"
    FieldPunning => "field-punning"
    RedundantAnnotation => "redundant-annotation"
    ConfusableUnicode => "confusable-unicode"
  }
}

///|
/// A one-line description, for help text.
pub fn Warning::description(self : Warning) -> String {
  match self {
    UnusedLocal => "A local that is declared but never read."
    UnusedField => "A module field that is defined but never used."
    UnusedImport => "An imported module field that is never used."
    UnusedLabel => "A block label that is declared but never branched to."
    UnnecessaryMut =>
      "A mutable global that is never assigned, so it could be immutable."
    ShiftOverflow =>
      "A constant shift count is at least the operand's bit width (Wasm masks it)."
    ConstantTrap =>
      "An operation always traps on a constant operand (integer division by zero, or an out-of-range trapping conversion)."
    TautologicalComparison =>
      "A comparison whose result is constant (an unsigned comparison against zero, or identical operands)."
    ConstantCondition =>
      "A branch, loop, or select condition that is a constant."
    UnusedResult =>
      "The result of a side-effect-free expression is computed and then discarded."
    DeadCode =>
      "A statement is unreachable: it follows an unconditional branch, return, or unreachable."
    CastAlwaysFails =>
      "A reference cast or test whose operand can never have the target type, so it always traps (or is always false)."
    EagerSelect =>
      "A trapping or effectful operation in a branch of a '?:' (which compiles to a 'select', evaluating both branches unconditionally)."
    Precedence =>
      "Operators whose relative precedence is easy to misread are mixed without parentheses (a shift with arithmetic, or a comparison with a bitwise operator)."
    RedundantOperation =>
      "An operation with no effect on its result (an arithmetic identity, an absorbing operand, identical operands, a self-assignment, or a superfluous cast)."
    TruncatedCoverage =>
      "Path-sensitive validation gave up after too many configurations."
    NamingConflict => "A Wasm name collided with another and was renamed."
    ReservedWordRename => "A Wasm name is a reserved word and was renamed."
    GeneratedName => "An unnamed but used parameter was given a generated name."
    CompoundAssignment =>
      "A plain assignment 'x = x op e' could use the compound form 'x op= e'."
    FieldPunning => "A struct field 'x: x' could use the punning shorthand 'x'."
    RedundantAnnotation =>
      "A 'let' type annotation the inferred type already makes redundant."
    ConfusableUnicode =>
      "A string contains a bidirectional control character that can make the source read differently than it runs (a \"Trojan Source\" character)."
  }
}

///|
/// Whether this flags code removable with no change in behaviour.
///
/// An editor renders such a diagnostic faded -- LSP's
/// `DiagnosticTag.Unnecessary`. Kept beside the definitions so every consumer
/// classifies from one source.
pub fn Warning::is_unnecessary(self : Warning) -> Bool {
  match self {
    UnusedLocal | UnusedField | UnusedImport | UnusedLabel | DeadCode => true
    _ => false
  }
}