/-
Copyright (c) 2020 Kevin Buzzard. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kevin Buzzard, Bryan Gin-ge Chen, Robert Y. Lewis, Scott Morrison

! This file was ported from Lean 3 source module tactic.lean_core_docs
! leanprover-community/mathlib commit 7e3fa4c114f6f12380cf3b181fd4bd03a2f05b79
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Tactic.DocCommands

/-!

# Core tactic documentation

This file adds the majority of the interactive tactics from core Lean (i.e. pre-mathlib) to
the API documentation.

## TODO

* Make a PR to core changing core docstrings to the docstrings below,
and also changing the docstrings of `cc`, `simp` and `conv` to the ones
already in the API docs.

* SMT tactics are currently not documented.

* `rsimp` and `constructor_matching` are currently not documented.

* `dsimp` deserves better documentation.
-/

add_tactic_doc
  { Name := "abstract"
    category := DocCategory.tactic
    declNames := [`tactic.interactive.abstract]
    tags := ["core", "proof extraction"] }

/-- Proves a goal of the form `s = t` when `s` and `t` are expressions built up out of a binary
operation, and equality can be proved using associativity and commutativity of that operation. -/
add_tactic_doc
  { Name := "ac_refl"
    category := DocCategory.tactic
    declNames := [`tactic.interactive.ac_refl, `tactic.interactive.ac_reflexivity]
    tags := ["core", "lemma application", "finishing"] }

add_tactic_doc
  { Name := "all_goals"
    category := DocCategory.tactic
    declNames := [`tactic.interactive.all_goals]
    tags := ["core", "goal management"] }

add_tactic_doc
  { Name := "any_goals"
    category := DocCategory.tactic
    declNames := [`tactic.interactive.any_goals]
    tags := ["core", "goal management"] }

add_tactic_doc
  { Name := "apply"
    category := DocCategory.tactic
    declNames := [`tactic.interactive.apply]
    tags := ["core", "basic", "lemma application"] }

add_tactic_doc
  { Name := "apply_auto_param"
    category := DocCategory.tactic
    declNames := [`tactic.interactive.apply_auto_param]
    tags := ["core", "lemma application"] }

add_tactic_doc
  { Name := "apply_instance"
    category := DocCategory.tactic
    declNames := [`tactic.interactive.apply_instance]
    tags := ["core", "type class"] }

add_tactic_doc
  { Name := "apply_opt_param"
    category := DocCategory.tactic
    declNames := [`tactic.interactive.apply_opt_param]
    tags := ["core", "lemma application"] }

add_tactic_doc
  { Name := "apply_with"
    category := DocCategory.tactic
    declNames := [`tactic.interactive.apply_with]
    tags := ["core", "lemma application"] }

add_tactic_doc
  { Name := "assume"
    category := DocCategory.tactic
    declNames := [`tactic.interactive.assume]
    tags := ["core", "logic"] }

add_tactic_doc
  { Name := "assumption"
    category := DocCategory.tactic
    declNames := [`tactic.interactive.assumption]
    tags := ["core", "basic", "finishing"] }

add_tactic_doc
  { Name := "assumption'"
    category := DocCategory.tactic
    declNames := [`tactic.interactive.assumption']
    tags := ["core", "goal management"] }

add_tactic_doc
  { Name := "async"
    category := DocCategory.tactic
    declNames := [`tactic.interactive.async]
    tags := ["core", "goal management", "combinator", "proof extraction"] }

/-- `by_cases p` splits the main goal into two cases, assuming `h : p` in the first branch, and
`h : ¬ p` in the second branch. You can specify the name of the new hypothesis using the syntax
`by_cases h : p`.

If `p` is not already decidable, `by_cases` will use the instance `classical.prop_decidable p`.
-/
add_tactic_doc
  { Name := "by_cases"
    category := DocCategory.tactic
    declNames := [`tactic.interactive.by_cases]
    tags := ["core", "basic", "logic", "case bashing"] }

/-- If the target of the main goal is a proposition `p`, `by_contra h` reduces the goal to proving
`false` using the additional hypothesis `h : ¬ p`. If `h` is omitted, a name is generated
automatically.

This tactic requires that `p` is decidable. To ensure that all propositions are decidable via
classical reasoning, use `open_locale classical`
(or `local attribute [instance, priority 10] classical.prop_decidable` if you are not using
mathlib).
-/
add_tactic_doc
  { Name := "by_contra / by_contradiction"
    category := DocCategory.tactic
    declNames := [`tactic.interactive.by_contra, `tactic.interactive.by_contradiction]
    tags := ["core", "logic"] }

add_tactic_doc
  { Name := "case"
    category := DocCategory.tactic
    declNames := [`tactic.interactive.case]
    tags := ["core", "goal management"] }

add_tactic_doc
  { Name := "cases"
    category := DocCategory.tactic
    declNames := [`tactic.interactive.cases]
    tags := ["core", "basic", "induction"] }

/-- `cases_matching p` applies the `cases` tactic to a hypothesis `h : type`
if `type` matches the pattern `p`.

`cases_matching [p_1, ..., p_n]` applies the `cases` tactic to a hypothesis `h : type`
if `type` matches one of the given patterns.

`cases_matching* p` is a more efficient and compact version
of `focus1 { repeat { cases_matching p } }`.
It is more efficient because the pattern is compiled once.

`casesm` is shorthand for `cases_matching`.

Example: The following tactic destructs all conjunctions and disjunctions in the current context.
```
cases_matching* [_ ∨ _, _ ∧ _]
```
-/
add_tactic_doc
  { Name := "cases_matching / casesm"
    category := DocCategory.tactic
    declNames := [`tactic.interactive.cases_matching, `tactic.interactive.casesm]
    tags := ["core", "induction", "context management"] }

/-- * `cases_type I` applies the `cases` tactic to a hypothesis `h : (I ...)`
* `cases_type I_1 ... I_n` applies the `cases` tactic to a hypothesis
  `h : (I_1 ...)` or ... or `h : (I_n ...)`
* `cases_type* I` is shorthand for `focus1 { repeat { cases_type I } }`
* `cases_type! I` only applies `cases` if the number of resulting subgoals is <= 1.

Example: The following tactic destructs all conjunctions and disjunctions in the current context.
```
cases_type* or and
```
-/
add_tactic_doc
  { Name := "cases_type"
    category := DocCategory.tactic
    declNames := [`tactic.interactive.cases_type]
    tags := ["core", "induction", "context management"] }

add_tactic_doc
  { Name := "change"
    category := DocCategory.tactic
    declNames := [`tactic.interactive.change]
    tags := ["core", "basic", "renaming"] }

add_tactic_doc
  { Name := "clear"
    category := DocCategory.tactic
    declNames := [`tactic.interactive.clear]
    tags := ["core", "context management"] }

/-- Close goals of the form `n ≠ m` when `n` and `m` have type `nat`, `char`, `string`, `int`
or `fin sz`, and they are literals. It also closes goals of the form `n < m`, `n > m`, `n ≤ m` and
`n ≥ m` for `nat`. If the goal is of the form `n = m`, then it tries to close it using reflexivity.

In mathlib, consider using `norm_num` instead for numeric types.
-/
add_tactic_doc
  { Name := "comp_val"
    category := DocCategory.tactic
    declNames := [`tactic.interactive.comp_val]
    tags := ["core", "arithmetic"] }

/-- The `congr` tactic attempts to identify both sides of an equality goal `A = B`,
leaving as new goals the subterms of `A` and `B` which are not definitionally equal.
Example: suppose the goal is `x * f y = g w * f z`. Then `congr` will produce two goals:
`x = g w` and `y = z`.

If `x y : t`, and an instance `subsingleton t` is in scope, then any goals of the form
`x = y` are solved automatically.

Note that `congr` can be over-aggressive at times; the `congr'` tactic in mathlib
provides a more refined approach, by taking a parameter that limits the recursion depth.
-/
add_tactic_doc
  { Name := "congr"
    category := DocCategory.tactic
    declNames := [`tactic.interactive.congr]
    tags := ["core", "congruence"] }

add_tactic_doc
  { Name := "constructor"
    category := DocCategory.tactic
    declNames := [`tactic.interactive.constructor]
    tags := ["core", "logic"] }

add_tactic_doc
  { Name := "contradiction"
    category := DocCategory.tactic
    declNames := [`tactic.interactive.contradiction]
    tags := ["core", "basic", "finishing"] }

add_tactic_doc
  { Name := "delta"
    category := DocCategory.tactic
    declNames := [`tactic.interactive.delta]
    tags := ["core", "simplification"] }

add_tactic_doc
  { Name := "destruct"
    category := DocCategory.tactic
    declNames := [`tactic.interactive.destruct]
    tags := ["core", "induction"] }

add_tactic_doc
  { Name := "done"
    category := DocCategory.tactic
    declNames := [`tactic.interactive.done]
    tags := ["core", "goal management"] }

add_tactic_doc
  { Name := "dsimp"
    category := DocCategory.tactic
    declNames := [`tactic.interactive.dsimp]
    tags := ["core", "simplification"] }

add_tactic_doc
  { Name := "dunfold"
    category := DocCategory.tactic
    declNames := [`tactic.interactive.dunfold]
    tags := ["core", "simplification"] }

add_tactic_doc
  { Name := "eapply"
    category := DocCategory.tactic
    declNames := [`tactic.interactive.eapply]
    tags := ["core", "lemma application"] }

add_tactic_doc
  { Name := "econstructor"
    category := DocCategory.tactic
    declNames := [`tactic.interactive.econstructor]
    tags := ["core", "logic"] }

/-- A variant of `rw` that uses the unifier more aggressively, unfolding semireducible definitions.
-/
add_tactic_doc
  { Name := "erewrite / erw"
    category := DocCategory.tactic
    declNames := [`tactic.interactive.erewrite, `tactic.interactive.erw]
    tags := ["core", "rewriting"] }

add_tactic_doc
  { Name := "exact"
    category := DocCategory.tactic
    declNames := [`tactic.interactive.exact]
    tags := ["core", "basic", "finishing"] }

add_tactic_doc
  { Name := "exacts"
    category := DocCategory.tactic
    declNames := [`tactic.interactive.exacts]
    tags := ["core", "finishing"] }

add_tactic_doc
  { Name := "exfalso"
    category := DocCategory.tactic
    declNames := [`tactic.interactive.exfalso]
    tags := ["core", "basic", "logic"] }

/-- `existsi e` will instantiate an existential quantifier in the target with `e` and leave the
instantiated body as the new target. More generally, it applies to any inductive type with one
constructor and at least two arguments, applying the constructor with `e` as the first argument
and leaving the remaining arguments as goals.

`existsi [e₁, ..., eₙ]` iteratively does the same for each expression in the list.

Note: in mathlib, the `use` tactic is an equivalent tactic which sometimes is smarter with
unification.
-/
add_tactic_doc
  { Name := "existsi"
    category := DocCategory.tactic
    declNames := [`tactic.interactive.existsi]
    tags := ["core", "logic"] }

add_tactic_doc
  { Name := "fail_if_success"
    category := DocCategory.tactic
    declNames := [`tactic.interactive.fail_if_success]
    tags := ["core", "testing", "combinator"] }

add_tactic_doc
  { Name := "fapply"
    category := DocCategory.tactic
    declNames := [`tactic.interactive.fapply]
    tags := ["core", "lemma application"] }

add_tactic_doc
  { Name := "focus"
    category := DocCategory.tactic
    declNames := [`tactic.interactive.focus]
    tags := ["core", "goal management", "combinator"] }

add_tactic_doc
  { Name := "from"
    category := DocCategory.tactic
    declNames := [`tactic.interactive.from]
    tags := ["core", "finishing"] }

/-- Apply function extensionality and introduce new hypotheses.
The tactic `funext` will keep applying new the `funext` lemma until the goal target is not reducible
to
```
  |-  ((fun x, ...) = (fun x, ...))
```
The variant `funext h₁ ... hₙ` applies `funext` `n` times, and uses the given identifiers to name
the new hypotheses.

Note also the mathlib tactic `ext`, which applies as many extensionality lemmas as possible.
-/
add_tactic_doc
  { Name := "funext"
    category := DocCategory.tactic
    declNames := [`tactic.interactive.funext]
    tags := ["core", "logic"] }

add_tactic_doc
  { Name := "generalize"
    category := DocCategory.tactic
    declNames := [`tactic.interactive.generalize]
    tags := ["core", "context management"] }

add_tactic_doc
  { Name := "guard_hyp"
    category := DocCategory.tactic
    declNames := [`tactic.interactive.guard_hyp]
    tags := ["core", "testing", "context management"] }

add_tactic_doc
  { Name := "guard_target"
    category := DocCategory.tactic
    declNames := [`tactic.interactive.guard_target]
    tags := ["core", "testing", "goal management"] }

add_tactic_doc
  { Name := "have"
    category := DocCategory.tactic
    declNames := [`tactic.interactive.have]
    tags := ["core", "basic", "context management"] }

add_tactic_doc
  { Name := "induction"
    category := DocCategory.tactic
    declNames := [`tactic.interactive.induction]
    tags := ["core", "basic", "induction"] }

add_tactic_doc
  { Name := "injection"
    category := DocCategory.tactic
    declNames := [`tactic.interactive.injection]
    tags := ["core", "structures", "induction"] }

add_tactic_doc
  { Name := "injections"
    category := DocCategory.tactic
    declNames := [`tactic.interactive.injections]
    tags := ["core", "structures", "induction"] }

/-- If the current goal is a Pi/forall `∀ x : t, u` (resp. `let x := t in u`) then `intro` puts
`x : t` (resp. `x := t`) in the local context. The new subgoal target is `u`.

If the goal is an arrow `t → u`, then it puts `h : t` in the local context and the new goal
target is `u`.

If the goal is neither a Pi/forall nor begins with a let binder, the tactic `intro` applies the
tactic `whnf` until an introduction can be applied or the goal is not head reducible. In the latter
case, the tactic fails.

The variant `intro z` uses the identifier `z` to name the new hypothesis.

The variant `intros` will keep introducing new hypotheses until the goal target is not a Pi/forall
or let binder.

The variant `intros h₁ ... hₙ` introduces `n` new hypotheses using the given identifiers to name
them.
-/
add_tactic_doc
  { Name := "intro / intros"
    category := DocCategory.tactic
    declNames := [`tactic.interactive.intro, `tactic.interactive.intros]
    tags := ["core", "basic", "logic"] }

add_tactic_doc
  { Name := "introv"
    category := DocCategory.tactic
    declNames := [`tactic.interactive.introv]
    tags := ["core", "logic"] }

add_tactic_doc
  { Name := "iterate"
    category := DocCategory.tactic
    declNames := [`tactic.interactive.iterate]
    tags := ["core", "combinator"] }

/-- `left` applies the first constructor when the type of the target is an inductive data type with
two constructors.

Similarly, `right` applies the second constructor.
-/
add_tactic_doc
  { Name := "left / right"
    category := DocCategory.tactic
    declNames := [`tactic.interactive.left, `tactic.interactive.right]
    tags := ["core", "basic", "logic"] }

/-- `let h : t := p` adds the hypothesis `h : t := p` to the current goal if `p` a term of type `t`.
If `t` is omitted, it will be inferred.

`let h : t` adds the hypothesis `h : t := ?M` to the current goal and opens a new subgoal `?M : t`.
The new subgoal becomes the main goal. If `t` is omitted, it will be replaced by a fresh
metavariable.

If `h` is omitted, the name `this` is used.

Note the related mathlib tactic `set a := t with h`, which adds the hypothesis `h : a = t` to
the local context and replaces `t` with `a` everywhere it can.
-/
add_tactic_doc
  { Name := "let"
    category := DocCategory.tactic
    declNames := [`tactic.interactive.let]
    tags := ["core", "basic", "logic", "context management"] }

add_tactic_doc
  { Name := "mapply"
    category := DocCategory.tactic
    declNames := [`tactic.interactive.mapply]
    tags := ["core", "lemma application"] }

add_tactic_doc
  { Name := "match_target"
    category := DocCategory.tactic
    declNames := [`tactic.interactive.match_target]
    tags := ["core", "testing", "goal management"] }

add_tactic_doc
  { Name := "refine"
    category := DocCategory.tactic
    declNames := [`tactic.interactive.refine]
    tags := ["core", "basic", "lemma application"] }

/--
This tactic applies to a goal whose target has the form `t ~ u` where `~` is a reflexive relation,
that is, a relation which has a reflexivity lemma tagged with the attribute `[refl]`.
The tactic checks whether `t` and `u` are definitionally equal and then solves the goal.
-/
add_tactic_doc
  { Name := "refl / reflexivity"
    category := DocCategory.tactic
    declNames := [`tactic.interactive.refl, `tactic.interactive.reflexivity]
    tags := ["core", "basic", "finishing"] }

add_tactic_doc
  { Name := "rename"
    category := DocCategory.tactic
    declNames := [`tactic.interactive.rename]
    tags := ["core", "renaming"] }

add_tactic_doc
  { Name := "repeat"
    category := DocCategory.tactic
    declNames := [`tactic.interactive.repeat]
    tags := ["core", "combinator"] }

add_tactic_doc
  { Name := "revert"
    category := DocCategory.tactic
    declNames := [`tactic.interactive.revert]
    tags := ["core", "context management", "goal management"] }

/-- `rw e` applies an equation or iff `e` as a rewrite rule to the main goal. If `e` is preceded by
left arrow (`←` or `<-`), the rewrite is applied in the reverse direction. If `e` is a defined
constant, then the equational lemmas associated with `e` are used. This provides a convenient
way to unfold `e`.

`rw [e₁, ..., eₙ]` applies the given rules sequentially.

`rw e at l` rewrites `e` at location(s) `l`, where `l` is either `*` or a list of hypotheses
in the local context. In the latter case, a turnstile `⊢` or `|-` can also be used, to signify
the target of the goal.

`rewrite` is synonymous with `rw`.
-/
add_tactic_doc
  { Name := "rw / rewrite"
    category := DocCategory.tactic
    declNames := [`tactic.interactive.rw, `tactic.interactive.rewrite]
    tags := ["core", "basic", "rewriting"] }

add_tactic_doc
  { Name := "rwa"
    category := DocCategory.tactic
    declNames := [`tactic.interactive.rwa]
    tags := ["core", "rewriting"] }

add_tactic_doc
  { Name := "show"
    category := DocCategory.tactic
    declNames := [`tactic.interactive.show]
    tags := ["core", "goal management", "renaming"] }

add_tactic_doc
  { Name := "simp_intros"
    category := DocCategory.tactic
    declNames := [`tactic.interactive.simp_intros]
    tags := ["core", "simplification"] }

add_tactic_doc
  { Name := "skip"
    category := DocCategory.tactic
    declNames := [`tactic.interactive.skip]
    tags := ["core", "combinator"] }

add_tactic_doc
  { Name := "solve1"
    category := DocCategory.tactic
    declNames := [`tactic.interactive.solve1]
    tags := ["core", "combinator", "goal management"] }

add_tactic_doc
  { Name := "sorry / admit"
    category := DocCategory.tactic
    declNames := [`tactic.interactive.sorry, `tactic.interactive.admit]
    inheritDescriptionFrom := `tactic.interactive.sorry
    tags := ["core", "testing", "debugging"] }

add_tactic_doc
  { Name := "specialize"
    category := DocCategory.tactic
    declNames := [`tactic.interactive.specialize]
    tags := ["core", "context management", "lemma application"] }

add_tactic_doc
  { Name := "split"
    category := DocCategory.tactic
    declNames := [`tactic.interactive.split]
    tags := ["core", "basic", "logic"] }

add_tactic_doc
  { Name := "subst"
    category := DocCategory.tactic
    declNames := [`tactic.interactive.subst]
    tags := ["core", "rewriting"] }

add_tactic_doc
  { Name := "subst_vars"
    category := DocCategory.tactic
    declNames := [`tactic.interactive.subst_vars]
    tags := ["core", "rewriting"] }

add_tactic_doc
  { Name := "success_if_fail"
    category := DocCategory.tactic
    declNames := [`tactic.interactive.success_if_fail]
    tags := ["core", "testing", "combinator"] }

add_tactic_doc
  { Name := "suffices"
    category := DocCategory.tactic
    declNames := [`tactic.interactive.suffices]
    tags := ["core", "basic", "goal management"] }

add_tactic_doc
  { Name := "symmetry"
    category := DocCategory.tactic
    declNames := [`tactic.interactive.symmetry]
    tags := ["core", "basic", "lemma application"] }

add_tactic_doc
  { Name := "trace"
    category := DocCategory.tactic
    declNames := [`tactic.interactive.trace]
    tags := ["core", "debugging", "testing"] }

add_tactic_doc
  { Name := "trace_simp_set"
    category := DocCategory.tactic
    declNames := [`tactic.interactive.trace_simp_set]
    tags := ["core", "debugging", "testing"] }

add_tactic_doc
  { Name := "trace_state"
    category := DocCategory.tactic
    declNames := [`tactic.interactive.trace_state]
    tags := ["core", "debugging", "testing"] }

add_tactic_doc
  { Name := "transitivity"
    category := DocCategory.tactic
    declNames := [`tactic.interactive.transitivity]
    tags := ["core", "lemma application"] }

add_tactic_doc
  { Name := "trivial"
    category := DocCategory.tactic
    declNames := [`tactic.interactive.trivial]
    tags := ["core", "finishing"] }

add_tactic_doc
  { Name := "try"
    category := DocCategory.tactic
    declNames := [`tactic.interactive.try]
    tags := ["core", "combinator"] }

add_tactic_doc
  { Name := "type_check"
    category := DocCategory.tactic
    declNames := [`tactic.interactive.type_check]
    tags := ["core", "debugging", "testing"] }

add_tactic_doc
  { Name := "unfold"
    category := DocCategory.tactic
    declNames := [`tactic.interactive.unfold]
    tags := ["core", "basic", "rewriting"] }

add_tactic_doc
  { Name := "unfold1"
    category := DocCategory.tactic
    declNames := [`tactic.interactive.unfold1]
    tags := ["core", "rewriting"] }

add_tactic_doc
  { Name := "unfold_projs"
    category := DocCategory.tactic
    declNames := [`tactic.interactive.unfold_projs]
    tags := ["core", "rewriting"] }

add_tactic_doc
  { Name := "with_cases"
    category := DocCategory.tactic
    declNames := [`tactic.interactive.with_cases]
    tags := ["core", "combinator"] }

-- conv mode tactics
/-- Navigate to the left-hand-side of a relation.
A goal of `| a = b` will turn into the goal `| a`.
-/
add_tactic_doc
  { Name := "conv: to_lhs"
    category := DocCategory.tactic
    declNames := [`conv.interactive.to_lhs]
    tags := ["conv"] }

/-- Navigate to the right-hand-side of a relation.
A goal of `| a = b` will turn into the goal `| b`.
-/
add_tactic_doc
  { Name := "conv: to_rhs"
    category := DocCategory.tactic
    declNames := [`conv.interactive.to_rhs]
    tags := ["conv"] }

/-- Navigate into every argument of the current head function.
A target of `| (a * b) * c` will turn into the two targets `| a * b` and `| c`.
-/
add_tactic_doc
  { Name := "conv: congr"
    category := DocCategory.tactic
    declNames := [`conv.interactive.congr]
    tags := ["conv"] }

/-- Navigate into the contents of top-level `λ` binders.
A target of `| λ a, a + b` will turn into the target `| a + b` and introduce `a` into the local
context.
If there are multiple binders, all of them will be entered, and if there are none, this tactic is a
no-op.
-/
add_tactic_doc
  { Name := "conv: funext"
    category := DocCategory.tactic
    declNames := [`conv.interactive.funext]
    tags := ["conv"] }

/-- Navigate into the first scope matching the expression.

For a target of `| ∀ c, a + (b + c) = 1`, `find (b + _) { ... }` will run the tactics within the
`{}` with a target of `| b + c`.
-/
add_tactic_doc
  { Name := "conv: find"
    category := DocCategory.tactic
    declNames := [`conv.interactive.find]
    tags := ["conv"] }

/-- Navigate into the numbered scopes matching the expression.

For a target of `| λ c, 10 * c + 20 * c + 30 * c`, `for (_ * _) [1, 3] { ... }` will run the
tactics within the `{}` with first a target of `| 10 * c`, then a target of `| 30 * c`.
-/
add_tactic_doc
  { Name := "conv: for"
    category := DocCategory.tactic
    declNames := [`conv.interactive.for]
    tags := ["conv"] }

/-- End conversion of the current goal. This is often what is needed when muscle memory would type
`sorry`.
-/
add_tactic_doc
  { Name := "conv: skip"
    category := DocCategory.tactic
    declNames := [`conv.interactive.skip]
    tags := ["conv"] }


-- import the definition of the example maze
import maze_with_fire.definition

/-!

# Maze with fire.

You are in a maze of twisty passages, all distinct.

You can go north, south east or west.

If you fall into the fire, you will end up in the room of death,
room 5.
-/

namespace maze

/- Solver remark : there are 6 rooms. -/

/-- Use n,s,e,w to move around. When you're at the exit, type `out`. -/

example : goal :=
begin
  -- ready...
  unfold goal,
  -- go!
  s,
  n,
  s,
  e,
  n,
  sorry,
  -- Don't forget the commas.
  -- Don't bang into the walls -- those are errors.
  -- When you get there, the tactic to get you out is `out`.
  -- There is also a magic word, rumoured to be
  -- an ancient translation of the word `sorry`.
end

end maze


example : 2 + 3 = 5 :=
begin
  generalize h : 3 = x,
  rw ←h
end


/-
Copyright (c) 2020 Floris van Doorn. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Floris van Doorn, Robert Y. Lewis
-/
import tactic.lint.basic

/-!
# Various linters

This file defines several small linters:
  - `ge_or_gt` checks that `>` and `≥` do not occur in the statement of theorems.
  - `dup_namespace` checks that no declaration has a duplicated namespace such as `list.list.monad`.
  - `unused_arguments` checks that definitions and theorems do not have unused arguments.
  - `doc_blame` checks that every definition has a documentation string
  - `doc_blame_thm` checks that every theorem has a documentation string (not enabled by default)
  - `def_lemma` checks that a declaration is a lemma iff its type is a proposition.
-/

open tactic expr

/-!
## Linter against use of `>`/`≥`
-/
/-- The names of `≥` and `>`, mostly disallowed in lemma statements -/
private meta def illegal_ge_gt : list name := [`gt, `ge]

set_option eqn_compiler.max_steps 20000
/--
  Checks whether `≥` and `>` occurs in an illegal way in the expression.
  The main ways we legally use these orderings are:
  - `f (≥)`
  - `∃ x ≥ t, b`. This corresponds to the expression
    `@Exists α (fun (x : α), (@Exists (x > t) (λ (H : x > t), b)))`
  This function returns `tt` when it finds `ge`/`gt`, except in the following patterns
  (which are the same for `gt`):
  - `f (@ge _ _)`
  - `f (&0 ≥ y) (λ x : t, b)`
  - `λ H : &0 ≥ t, b`
  Here `&0` is the 0-th de Bruijn variable.
-/
private meta def contains_illegal_ge_gt : expr → bool
| (const nm us) := if nm ∈ illegal_ge_gt then tt else ff
| (app f e@(app (app (const nm us) tp) tc)) :=
  contains_illegal_ge_gt f || if nm ∈ illegal_ge_gt then ff else contains_illegal_ge_gt e
| (app (app custom_binder (app (app (app (app (const nm us) tp) tc) (var 0)) t))
    e@(lam var_name bi var_type body)) :=
  contains_illegal_ge_gt e || if nm ∈ illegal_ge_gt then ff else contains_illegal_ge_gt e
| (app f x) := contains_illegal_ge_gt f || contains_illegal_ge_gt x
| (lam `H bi type@(app (app (app (app (const nm us) tp) tc) (var 0)) t) body) :=
  contains_illegal_ge_gt body || if nm ∈ illegal_ge_gt then ff else contains_illegal_ge_gt type
| (lam var_name bi var_type body) := contains_illegal_ge_gt var_type || contains_illegal_ge_gt body
| (pi `H bi type@(app (app (app (app (const nm us) tp) tc) (var 0)) t) body) :=
  contains_illegal_ge_gt body || if nm ∈ illegal_ge_gt then ff else contains_illegal_ge_gt type
| (pi var_name bi var_type body) := contains_illegal_ge_gt var_type || contains_illegal_ge_gt body
| (elet var_name type assignment body) :=
  contains_illegal_ge_gt type || contains_illegal_ge_gt assignment || contains_illegal_ge_gt body
| _ := ff

/-- Checks whether a `>`/`≥` is used in the statement of `d`.

It first does a quick check to see if there is any `≥` or `>` in the statement, and then does a
slower check whether the occurrences of `≥` and `>` are allowed.
Currently it checks only the conclusion of the declaration, to eliminate false positive from
binders such as `∀ ε > 0, ...` -/
private meta def ge_or_gt_in_statement (d : declaration) : tactic (option string) :=
return $ if d.type.contains_constant (λ n, n ∈ illegal_ge_gt) &&
  contains_illegal_ge_gt d.type
  then some "the type contains ≥/>. Use ≤/< instead."
  else none

-- TODO: the commented out code also checks for classicality in statements, but needs fixing
-- TODO: this probably needs to also check whether the argument is a variable or @eq <var> _ _
-- meta def illegal_constants_in_statement (d : declaration) : tactic (option string) :=
-- return $ if d.type.contains_constant (λ n, (n.get_prefix = `classical ∧
--   n.last ∈ ["prop_decidable", "dec", "dec_rel", "dec_eq"]) ∨ n ∈ [`gt, `ge])
-- then
--   let illegal1 := [`classical.prop_decidable, `classical.dec, `classical.dec_rel, `classical.dec_eq],
--       illegal2 := [`gt, `ge],
--       occur1 := illegal1.filter (λ n, d.type.contains_constant (eq n)),
--       occur2 := illegal2.filter (λ n, d.type.contains_constant (eq n)) in
--   some $ sformat!"the type contains the following declarations: {occur1 ++ occur2}." ++
--     (if occur1 = [] then "" else " Add decidability type-class arguments instead.") ++
--     (if occur2 = [] then "" else " Use ≤/< instead.")
-- else none

/-- A linter for checking whether illegal constants (≥, >) appear in a declaration's type. -/
@[linter] meta def linter.ge_or_gt : linter :=
{ test := ge_or_gt_in_statement,
  auto_decls := ff,
  no_errors_found := "Not using ≥/> in declarations",
  errors_found := "The following declarations use ≥/>, probably in a way where we would prefer
  to use ≤/< instead. See note [nolint_ge] for more information.",
  is_fast := ff }

/--
Currently, the linter forbids the use of `>` and `≥` in definitions and
statements, as they cause problems in rewrites.
They are still allowed in statements such as `bounded (≥)` or `∀ ε > 0` or `⨆ n ≥ m`,
and the linter allows that.
If you write a pattern where you bind two or more variables, like `∃ n m > 0`, the linter will
flag this as illegal, but it is also allowed. In this case, add the line
```
@[nolint ge_or_gt] -- see Note [nolint_ge]
```
-/
library_note "nolint_ge"

/-!
## Linter for duplicate namespaces
-/

/-- Checks whether a declaration has a namespace twice consecutively in its name -/
private meta def dup_namespace (d : declaration) : tactic (option string) :=
is_instance d.to_name >>= λ is_inst,
return $ let nm := d.to_name.components in if nm.chain' (≠) ∨ is_inst then none
  else let s := (nm.find $ λ n, nm.count n ≥ 2).iget.to_string in
  some $ "The namespace `" ++ s ++ "` is duplicated in the name"

/-- A linter for checking whether a declaration has a namespace twice consecutively in its name. -/
@[linter] meta def linter.dup_namespace : linter :=
{ test := dup_namespace,
  auto_decls := ff,
  no_errors_found := "No declarations have a duplicate namespace",
  errors_found := "DUPLICATED NAMESPACES IN NAME" }

/-!
## Linter for unused arguments
-/

/-- Auxilliary definition for `check_unused_arguments` -/
private meta def check_unused_arguments_aux : list ℕ → ℕ → ℕ → expr → list ℕ | l n n_max e :=
if n > n_max then l else
if ¬ is_lambda e ∧ ¬ is_pi e then l else
  let b := e.binding_body in
  let l' := if b.has_var_idx 0 then l else n :: l in check_unused_arguments_aux l' (n+1) n_max b

/-- Check which arguments of a declaration are not used.
Prints a list of natural numbers corresponding to which arguments are not used (e.g.
  this outputs [1, 4] if the first and fourth arguments are unused).
Checks both the type and the value of `d` for whether the argument is used
(in rare cases an argument is used in the type but not in the value).
We return [] if the declaration was automatically generated.
We print arguments that are larger than the arity of the type of the declaration
(without unfolding definitions). -/
meta def check_unused_arguments (d : declaration) : option (list ℕ) :=
let l := check_unused_arguments_aux [] 1 d.type.pi_arity d.value in
if l = [] then none else
let l2 := check_unused_arguments_aux [] 1 d.type.pi_arity d.type in
(l.filter $ λ n, n ∈ l2).reverse

/-- Check for unused arguments, and print them with their position, variable name, type and whether
the argument is a duplicate.
See also `check_unused_arguments`.
This tactic additionally filters out all unused arguments of type `parse _`. -/
private meta def unused_arguments (d : declaration) : tactic (option string) := do
  let ns := check_unused_arguments d,
  if ¬ ns.is_some then return none else do
  let ns := ns.iget,
  (ds, _) ← get_pi_binders d.type,
  let ns := ns.map (λ n, (n, (ds.nth $ n - 1).iget)),
  let ns := ns.filter (λ x, x.2.type.get_app_fn ≠ const `interactive.parse []),
  if ns = [] then return none else do
  ds' ← ds.mmap pp,
  ns ← ns.mmap (λ ⟨n, b⟩, (λ s, to_fmt "argument " ++ to_fmt n ++ ": " ++ s ++
    (if ds.countp (λ b', b.type = b'.type) ≥ 2 then " (duplicate)" else "")) <$> pp b),
  return $ some $ ns.to_string_aux tt

/-- A linter object for checking for unused arguments. This is in the default linter set. -/
@[linter] meta def linter.unused_arguments : linter :=
{ test := unused_arguments,
  auto_decls := ff,
  no_errors_found := "No unused arguments",
  errors_found := "UNUSED ARGUMENTS" }

attribute [nolint unused_arguments] imp_intro

/-!
## Linter for documentation strings
-/

/-- Reports definitions and constants that are missing doc strings -/
private meta def doc_blame_report_defn : declaration → tactic (option string)
| (declaration.defn n _ _ _ _ _) := doc_string n >> return none <|> return "def missing doc string"
| (declaration.cnst n _ _ _) := doc_string n >> return none <|> return "constant missing doc string"
| _ := return none

/-- Reports definitions and constants that are missing doc strings -/
private meta def doc_blame_report_thm : declaration → tactic (option string)
| (declaration.thm n _ _ _) := doc_string n >> return none <|> return "theorem missing doc string"
| _ := return none

/-- A linter for checking definition doc strings -/
@[linter] meta def linter.doc_blame : linter :=
{ test := λ d, mcond (bnot <$> has_attribute' `instance d.to_name)
    (doc_blame_report_defn d) (return none),
  auto_decls := ff,
  no_errors_found := "No definitions are missing documentation.",
  errors_found := "DEFINITIONS ARE MISSING DOCUMENTATION STRINGS" }

/-- A linter for checking theorem doc strings. This is not in the default linter set. -/
meta def linter.doc_blame_thm : linter :=
{ test := doc_blame_report_thm,
  auto_decls := ff,
  no_errors_found := "No theorems are missing documentation.",
  errors_found := "THEOREMS ARE MISSING DOCUMENTATION STRINGS",
  is_fast := ff }

/-!
## Linter for correct usage of `lemma`/`def`
-/

/--
Checks whether the correct declaration constructor (definition or theorem) by
comparing it to its sort. Instances will not be printed.

This test is not very quick: maybe we can speed-up testing that something is a proposition?
This takes almost all of the execution time.
-/
private meta def incorrect_def_lemma (d : declaration) : tactic (option string) :=
  if d.is_constant ∨ d.is_axiom
  then return none else do
    is_instance_d ← is_instance d.to_name,
    if is_instance_d then return none else do
      -- the following seems to be a little quicker than `is_prop d.type`.
      expr.sort n ← infer_type d.type, return $
      if d.is_theorem ↔ n = level.zero then none
      else if (d.is_definition : bool) then "is a def, should be a lemma/theorem"
      else "is a lemma/theorem, should be a def"

/-- A linter for checking whether the correct declaration constructor (definition or theorem)
has been used. -/
@[linter] meta def linter.def_lemma : linter :=
{ test := incorrect_def_lemma,
  auto_decls := ff,
  no_errors_found := "All declarations correctly marked as def/lemma",
  errors_found := "INCORRECT DEF/LEMMA" }

attribute [nolint def_lemma] classical.dec classical.dec_pred classical.dec_rel classical.dec_eq


/-
Copyright (c) 2019 Patrick Massot. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Patrick Massot
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.tactic.interactive
import Mathlib.PostPort

namespace Mathlib

/-!
# Rename bound variable tactic

This files defines a tactic `rename_var` whose main purpose is to teach
renaming of bound variables.

* `rename_var old new` renames all bound variables named `old` to `new` in the goal.
* `rename_var old new at h` does the same in hypothesis `h`.

```lean
example (P : ℕ →  ℕ → Prop) (h : ∀ n, ∃ m, P n m) : ∀ l, ∃ m, P l m :=
begin
  rename_var n q at h, -- h is now ∀ (q : ℕ), ∃ (m : ℕ), P q m,
  rename_var m n, -- goal is now ∀ (l : ℕ), ∃ (n : ℕ), P k n,
  exact h -- Lean does not care about those bound variable names
end
```

## Tags

teaching, tactic
-/

/-- Rename bound variable `old` to `new` in an `expr`-/
namespace tactic

/-- Rename bound variable `old` to `new` in goal -/
/-- Rename bound variable `old` to `new` in assumption `h` -/
end tactic

namespace tactic.interactive

/--
`rename_var old new` renames all bound variables named `old` to `new` in the goal.
`rename_var old new at h` does the same in hypothesis `h`.
-/
end tactic.interactive

/--
`rename_var old new` renames all bound variables named `old` to `new` in the goal.