From eac93159622ac6527ea698da4351d65b03abdfd4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wojciech=20R=C3=B3=C5=BCowski?= Date: Wed, 1 Apr 2026 15:40:43 +0100 Subject: [PATCH] feat: add `deprecated_module` (#13002) This PR adds a `deprecated_module` command that marks the current module as deprecated. When another module imports a deprecated module, a warning is emitted during elaboration suggesting replacement imports. Example usage: ```lean deprecated_module "use NewModule instead" (since := "2026-03-30") ``` The warning message is optional but recommended. The `since` parameter is required. Warnings can be disabled, by setting `linter.deprecated.module` option to false in the command line. Because the check happens when importing , using `set_option linter.deprecated.module` in the source file won't affect the warnings. Instead, a whole file can be marked not to display depreciation warnings, by putting a comment `deprecated_module: ignore` next to `module` keyword. Similarly, individual keywords can be silenced. A `#show_deprecated_modules` command is also provided for inspecting which modules in the current environment are deprecated. `linter.deprecated.module` has no effect on this command, and hence one can view deprecated modules, even when having warnings silenced. --- src/Lean/DeprecatedModule.lean | 45 +++++++++++++++ src/Lean/Elab/BuiltinCommand.lean | 40 +++++++++++++ src/Lean/Elab/Import.lean | 57 +++++++++++++++++++ src/Lean/Language/Lean.lean | 5 +- src/Lean/Parser/Command.lean | 21 +++++++ tests/elab/deprecatedModule.lean | 30 ++++++++++ tests/elab/deprecated_module.lean | 38 +++++++++++++ .../DeprecatedModule/Consumer.lean | 4 ++ .../DeprecatedModule/ConsumerIgnoreAll.lean | 4 ++ .../ConsumerIgnoreLastImport.lean | 4 ++ .../DeprecatedModule/ConsumerIgnoreOne.lean | 4 ++ .../ConsumerIgnoreOnlyImport.lean | 3 + .../ConsumerIgnoreWhitespace.lean | 4 ++ .../ConsumerShowDeprecated.lean | 6 ++ .../DeprecatedModule/New.lean | 3 + .../DeprecatedModule/Old.lean | 5 ++ .../DeprecatedModule/OldDouble.lean | 6 ++ .../DeprecatedModule/OldNoMessage.lean | 3 + .../DeprecatedModule/Transitive.lean | 3 + .../DeprecatedModule/TransitiveConsumer.lean | 3 + tests/pkg/deprecated_module/lakefile.toml | 20 +++++++ tests/pkg/deprecated_module/run_test.sh | 37 ++++++++++++ 22 files changed, 343 insertions(+), 2 deletions(-) create mode 100644 src/Lean/DeprecatedModule.lean create mode 100644 tests/elab/deprecatedModule.lean create mode 100644 tests/elab/deprecated_module.lean create mode 100644 tests/pkg/deprecated_module/DeprecatedModule/Consumer.lean create mode 100644 tests/pkg/deprecated_module/DeprecatedModule/ConsumerIgnoreAll.lean create mode 100644 tests/pkg/deprecated_module/DeprecatedModule/ConsumerIgnoreLastImport.lean create mode 100644 tests/pkg/deprecated_module/DeprecatedModule/ConsumerIgnoreOne.lean create mode 100644 tests/pkg/deprecated_module/DeprecatedModule/ConsumerIgnoreOnlyImport.lean create mode 100644 tests/pkg/deprecated_module/DeprecatedModule/ConsumerIgnoreWhitespace.lean create mode 100644 tests/pkg/deprecated_module/DeprecatedModule/ConsumerShowDeprecated.lean create mode 100644 tests/pkg/deprecated_module/DeprecatedModule/New.lean create mode 100644 tests/pkg/deprecated_module/DeprecatedModule/Old.lean create mode 100644 tests/pkg/deprecated_module/DeprecatedModule/OldDouble.lean create mode 100644 tests/pkg/deprecated_module/DeprecatedModule/OldNoMessage.lean create mode 100644 tests/pkg/deprecated_module/DeprecatedModule/Transitive.lean create mode 100644 tests/pkg/deprecated_module/DeprecatedModule/TransitiveConsumer.lean create mode 100644 tests/pkg/deprecated_module/lakefile.toml create mode 100644 tests/pkg/deprecated_module/run_test.sh diff --git a/src/Lean/DeprecatedModule.lean b/src/Lean/DeprecatedModule.lean new file mode 100644 index 0000000000..0a3c5caed4 --- /dev/null +++ b/src/Lean/DeprecatedModule.lean @@ -0,0 +1,45 @@ +/- +Copyright (c) 2026 Lean FRO, LLC. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Wojciech Różowski +-/ +module + +prelude +public import Lean.Compiler.ModPkgExt + +public section + +namespace Lean + +structure DeprecatedModuleEntry where + message? : Option String := none + since? : Option String := none + deriving Inhabited + +register_builtin_option linter.deprecated.module : Bool := { + defValue := true + descr := "if true, generate warnings when importing deprecated modules" +} + +builtin_initialize deprecatedModuleExt : ModuleEnvExtension <| Option DeprecatedModuleEntry ← + registerModuleEnvExtension <| pure none + +def Environment.getDeprecatedModuleByIdx? (env : Environment) (idx : ModuleIdx) : Option DeprecatedModuleEntry := + deprecatedModuleExt.getStateByIdx? env idx |>.join + +def Environment.setDeprecatedModule (entry : Option DeprecatedModuleEntry) (env : Environment) : Environment := + deprecatedModuleExt.setState env entry + +def formatDeprecatedModuleWarning (env : Environment) (idx : ModuleIdx) (modName : Name) + (entry : DeprecatedModuleEntry) : String := + let msg := entry.message?.getD "" + let replacements := env.header.moduleData[idx.toNat]!.imports.filter fun imp => + imp.module != `Init + let lines := replacements.foldl (init := "") fun acc imp => + acc ++ s!"import {imp.module}\n" + s!"{msg}\n\ + '{modName}' has been deprecated: please replace this import by\n\n\ + {lines}" + +end Lean diff --git a/src/Lean/Elab/BuiltinCommand.lean b/src/Lean/Elab/BuiltinCommand.lean index 44a5dac01b..d4d439e453 100644 --- a/src/Lean/Elab/BuiltinCommand.lean +++ b/src/Lean/Elab/BuiltinCommand.lean @@ -14,6 +14,7 @@ public import Lean.Elab.Open import Init.Data.Nat.Order import Init.Data.Order.Lemmas import Init.System.Platform +import Lean.DeprecatedModule public section @@ -716,6 +717,45 @@ where let env ← getEnv IO.eprintln (← env.dbgFormatAsyncState) +/-- Elaborate `deprecated_module`, marking the current module as deprecated. -/ +@[builtin_command_elab Parser.Command.deprecated_module] +def elabDeprecatedModule : CommandElab + | `(Parser.Command.deprecated_module| deprecated_module $[$msg?]? $[(since := $since?)]?) => do + let message? := msg?.map TSyntax.getString + let since? := since?.map TSyntax.getString + if (deprecatedModuleExt.getState (← getEnv)).isSome then + logWarning "module is already marked as deprecated" + if since?.isNone then + logWarning "`deprecated_module` should specify the date or library version \ + at which the deprecation was introduced, using `(since := \"...\")`" + modifyEnv fun env => env.setDeprecatedModule (some { message?, since? }) + | _ => throwUnsupportedSyntax + +/-- Elaborate `#show_deprecated_modules`, displaying all deprecated modules. -/ +@[builtin_command_elab Parser.Command.showDeprecatedModules] +def elabShowDeprecatedModules : CommandElab := fun _ => do + let env ← getEnv + let mut parts : Array String := #["Deprecated modules\n"] + for h : idx in [:env.header.moduleNames.size] do + if let some entry := env.getDeprecatedModuleByIdx? idx then + let modName := env.header.moduleNames[idx] + let msg := match entry.message? with + | some str => s!"message '{str}'" + | none => "no message" + let replacements := env.header.moduleData[idx]!.imports.filter fun imp => + imp.module != `Init + parts := parts.push s!"'{modName}' deprecates to\n{replacements.map (·.module)}\nwith {msg}\n" + -- Also show the current module's deprecation if set. + if let some entry := deprecatedModuleExt.getState env then + let modName := env.mainModule + let msg := match entry.message? with + | some str => s!"message '{str}'" + | none => "no message" + let replacements := env.imports.filter fun imp => + imp.module != `Init + parts := parts.push s!"'{modName}' deprecates to\n{replacements.map (·.module)}\nwith {msg}\n" + logInfo (String.intercalate "\n" parts.toList) + @[builtin_command_elab Parser.Command.deprecatedSyntax] def elabDeprecatedSyntax : CommandElab := fun stx => do let id := stx[1] let kind ← liftCoreM <| checkSyntaxNodeKindAtNamespaces id.getId (← getCurrNamespace) diff --git a/src/Lean/Elab/Import.lean b/src/Lean/Elab/Import.lean index 5dd9debdda..8566a1c99c 100644 --- a/src/Lean/Elab/Import.lean +++ b/src/Lean/Elab/Import.lean @@ -9,6 +9,7 @@ prelude public import Lean.Parser.Module meta import Lean.Parser.Module import Lean.Compiler.ModPkgExt +public import Lean.DeprecatedModule public section @@ -42,12 +43,66 @@ def HeaderSyntax.toModuleHeader (stx : HeaderSyntax) : ModuleHeader where abbrev headerToImports := @HeaderSyntax.imports +/-- +Check imported modules for deprecation and emit warnings. + +The `-- deprecated_module: ignore` comment can be placed on the `module` keyword to suppress +all warnings, or on individual `import` statements to suppress specific ones. +This follows the same pattern as `-- shake: keep` in Lake shake. + +The `headerStx?` parameter carries the header syntax used for checking trailing comments. +When called from the Language Server, the main header syntax may have its trailing trivia +stripped by `unsetTrailing` for caching purposes, so `origHeaderStx?` can supply the original +(untrimmed) syntax to preserve `-- deprecated_module: ignore` annotations on the last import. +-/ +def checkDeprecatedImports + (env : Environment) (imports : Array Import) (opts : Options) + (inputCtx : Parser.InputContext) (startPos : String.Pos.Raw) (messages : MessageLog) + (headerStx? : Option HeaderSyntax := none) + (origHeaderStx? : Option HeaderSyntax := none) + : MessageLog := Id.run do + let mut opts := opts + let mut ignoreDeprecatedImports : NameSet := {} + if let some headerStx := origHeaderStx? <|> headerStx? then + match headerStx with + | `(Parser.Module.header| $[module%$moduleTk]? $[prelude%$_]? $importsStx*) => + if moduleTk.any (·.getTrailing?.any (·.toString.contains "deprecated_module: ignore")) then + opts := linter.deprecated.module.set opts false + for impStx in importsStx do + if impStx.raw.getTrailing?.any (·.toString.contains "deprecated_module: ignore") then + match impStx with + | `(Parser.Module.import| $[public%$_]? $[meta%$_]? import $[all%$_]? $n) => + ignoreDeprecatedImports := ignoreDeprecatedImports.insert n.getId + | _ => pure () + | _ => pure () + if !linter.deprecated.module.get opts then + return messages + imports.foldl (init := messages) fun messages imp => + if ignoreDeprecatedImports.contains imp.module then + messages + else + match env.getModuleIdx? imp.module with + | some idx => + match env.getDeprecatedModuleByIdx? idx with + | some entry => + let pos := inputCtx.fileMap.toPosition startPos + messages.add { + fileName := inputCtx.fileName + pos := pos + severity := .warning + data := .tagged ``deprecatedModuleExt <| formatDeprecatedModuleWarning env idx imp.module entry + } + | none => messages + | none => messages + def processHeaderCore (startPos : String.Pos.Raw) (imports : Array Import) (isModule : Bool) (opts : Options) (messages : MessageLog) (inputCtx : Parser.InputContext) (trustLevel : UInt32 := 0) (plugins : Array System.FilePath := #[]) (leakEnv := false) (mainModule := Name.anonymous) (package? : Option PkgId := none) (arts : NameMap ImportArtifacts := {}) + (headerStx? : Option HeaderSyntax := none) + (origHeaderStx? : Option HeaderSyntax := none) : IO (Environment × MessageLog) := do let level := if isModule then if Elab.inServer.get opts then @@ -66,6 +121,7 @@ def processHeaderCore let pos := inputCtx.fileMap.toPosition startPos pure (env, messages.add { fileName := inputCtx.fileName, data := toString e, pos := pos }) let env := env.setMainModule mainModule |>.setModulePackage package? + let messages := checkDeprecatedImports env imports opts inputCtx startPos messages headerStx? origHeaderStx? return (env, messages) /-- @@ -82,6 +138,7 @@ backwards compatibility measure not compatible with the module system. : IO (Environment × MessageLog) := do processHeaderCore header.startPos header.imports header.isModule opts messages inputCtx trustLevel plugins leakEnv mainModule + (headerStx? := header) def parseImports (input : String) (fileName : Option String := none) : IO (Array Import × Position × MessageLog) := do let fileName := fileName.getD "" diff --git a/src/Lean/Language/Lean.lean b/src/Lean/Language/Lean.lean index 124d4739bc..5c0d87caee 100644 --- a/src/Lean/Language/Lean.lean +++ b/src/Lean/Language/Lean.lean @@ -478,11 +478,11 @@ where } result? := some { parserState - processedSnap := (← processHeader ⟨trimmedStx⟩ parserState) + processedSnap := (← processHeader ⟨trimmedStx⟩ stx parserState) } } - processHeader (stx : HeaderSyntax) (parserState : Parser.ModuleParserState) : + processHeader (stx : HeaderSyntax) (origStx : HeaderSyntax) (parserState : Parser.ModuleParserState) : LeanProcessingM (SnapshotTask HeaderProcessedSnapshot) := do let ctx ← read SnapshotTask.ofIO none none (.some ⟨0, ctx.endPos⟩) <| @@ -498,6 +498,7 @@ where let (headerEnv, msgLog) ← Elab.processHeaderCore (leakEnv := true) stx.startPos setup.imports setup.isModule setup.opts .empty ctx.toInputContext setup.trustLevel setup.plugins setup.mainModuleName setup.package? setup.importArts + (headerStx? := stx) (origHeaderStx? := origStx) let stopTime := (← IO.monoNanosNow).toFloat / 1000000000 let diagnostics := (← Snapshot.Diagnostics.ofMessageLog msgLog) if msgLog.hasErrors then diff --git a/src/Lean/Parser/Command.lean b/src/Lean/Parser/Command.lean index 3ba6345d98..afeef0920f 100644 --- a/src/Lean/Parser/Command.lean +++ b/src/Lean/Parser/Command.lean @@ -638,6 +638,27 @@ An internal bootstrapping command that reinterprets a Markdown docstring as Vers -/ @[builtin_command_parser] def «docs_to_verso» := leading_parser "docs_to_verso " >> sepBy1 ident ", " +/-- +`deprecated_module` marks the current module as deprecated. +When another module imports a deprecated module, a warning is emitted during elaboration. + +``` +deprecated_module "use NewModule instead" (since := "2026-03-19") +``` + +The warning message is optional but recommended. +The warning can be disabled with `set_option linter.deprecated.module false` or +`-Dlinter.deprecated.module=false`. +-/ +@[builtin_command_parser] def «deprecated_module» := leading_parser + "deprecated_module" >> optional (ppSpace >> strLit) >> optional (" (" >> nonReservedSymbol "since" >> " := " >> strLit >> ")") + +/-- +`#show_deprecated_modules` displays all modules in the current environment that have been +marked with `deprecated_module`. +-/ +@[builtin_command_parser] def showDeprecatedModules := leading_parser + "#show_deprecated_modules" def optionValue := nonReservedSymbol "true" <|> nonReservedSymbol "false" <|> strLit <|> numLit /-- diff --git a/tests/elab/deprecatedModule.lean b/tests/elab/deprecatedModule.lean new file mode 100644 index 0000000000..9ff32e494a --- /dev/null +++ b/tests/elab/deprecatedModule.lean @@ -0,0 +1,30 @@ +module + +import Std.Data + + +deprecated_module (since := "30-03-2026") + +/-- +info: Deprecated modules + +'elab.deprecatedModule' deprecates to +#[Std.Data] +with no message +-/ +#guard_msgs in +#show_deprecated_modules + +/-- warning: module is already marked as deprecated -/ +#guard_msgs in +deprecated_module "custom message" (since := "30-03-2026") + +/-- +info: Deprecated modules + +'elab.deprecatedModule' deprecates to +#[Std.Data] +with message 'custom message' +-/ +#guard_msgs in +#show_deprecated_modules diff --git a/tests/elab/deprecated_module.lean b/tests/elab/deprecated_module.lean new file mode 100644 index 0000000000..fa168235f1 --- /dev/null +++ b/tests/elab/deprecated_module.lean @@ -0,0 +1,38 @@ +/- +Tests for the `deprecated_module` command. +-/ + +-- Missing since (message is optional) +/-- +warning: `deprecated_module` should specify the date or library version at which the deprecation was introduced, using `(since := "...")` +-/ +#guard_msgs in +deprecated_module + +-- Missing since with message (also warns about duplicate since module is already marked above) +/-- +warning: module is already marked as deprecated +--- +warning: `deprecated_module` should specify the date or library version at which the deprecation was introduced, using `(since := "...")` +-/ +#guard_msgs in +deprecated_module "use NewModule instead" + +-- No message, with since (also warns about duplicate) +/-- warning: module is already marked as deprecated -/ +#guard_msgs in +deprecated_module (since := "2026-03-19") + +-- Both message and since: only duplicate warning +/-- +warning: module is already marked as deprecated +-/ +#guard_msgs in +deprecated_module "use NewModule instead" (since := "2026-03-19") + +-- Duplicate deprecated_module: warns about already being marked (standalone confirmation) +/-- +warning: module is already marked as deprecated +-/ +#guard_msgs in +deprecated_module "use SomethingElse instead" (since := "2026-03-20") diff --git a/tests/pkg/deprecated_module/DeprecatedModule/Consumer.lean b/tests/pkg/deprecated_module/DeprecatedModule/Consumer.lean new file mode 100644 index 0000000000..f5fb659290 --- /dev/null +++ b/tests/pkg/deprecated_module/DeprecatedModule/Consumer.lean @@ -0,0 +1,4 @@ +module + +import DeprecatedModule.Old +import DeprecatedModule.OldNoMessage diff --git a/tests/pkg/deprecated_module/DeprecatedModule/ConsumerIgnoreAll.lean b/tests/pkg/deprecated_module/DeprecatedModule/ConsumerIgnoreAll.lean new file mode 100644 index 0000000000..7101a23d3e --- /dev/null +++ b/tests/pkg/deprecated_module/DeprecatedModule/ConsumerIgnoreAll.lean @@ -0,0 +1,4 @@ +module -- deprecated_module: ignore + +import DeprecatedModule.Old +import DeprecatedModule.OldNoMessage diff --git a/tests/pkg/deprecated_module/DeprecatedModule/ConsumerIgnoreLastImport.lean b/tests/pkg/deprecated_module/DeprecatedModule/ConsumerIgnoreLastImport.lean new file mode 100644 index 0000000000..800e8097e7 --- /dev/null +++ b/tests/pkg/deprecated_module/DeprecatedModule/ConsumerIgnoreLastImport.lean @@ -0,0 +1,4 @@ +module + +import DeprecatedModule.OldNoMessage +import DeprecatedModule.Old -- deprecated_module: ignore diff --git a/tests/pkg/deprecated_module/DeprecatedModule/ConsumerIgnoreOne.lean b/tests/pkg/deprecated_module/DeprecatedModule/ConsumerIgnoreOne.lean new file mode 100644 index 0000000000..92c34eba85 --- /dev/null +++ b/tests/pkg/deprecated_module/DeprecatedModule/ConsumerIgnoreOne.lean @@ -0,0 +1,4 @@ +module + +import DeprecatedModule.Old -- deprecated_module: ignore +import DeprecatedModule.OldNoMessage diff --git a/tests/pkg/deprecated_module/DeprecatedModule/ConsumerIgnoreOnlyImport.lean b/tests/pkg/deprecated_module/DeprecatedModule/ConsumerIgnoreOnlyImport.lean new file mode 100644 index 0000000000..63793be204 --- /dev/null +++ b/tests/pkg/deprecated_module/DeprecatedModule/ConsumerIgnoreOnlyImport.lean @@ -0,0 +1,3 @@ +module + +import DeprecatedModule.Old -- deprecated_module: ignore diff --git a/tests/pkg/deprecated_module/DeprecatedModule/ConsumerIgnoreWhitespace.lean b/tests/pkg/deprecated_module/DeprecatedModule/ConsumerIgnoreWhitespace.lean new file mode 100644 index 0000000000..f5e0e4f1ef --- /dev/null +++ b/tests/pkg/deprecated_module/DeprecatedModule/ConsumerIgnoreWhitespace.lean @@ -0,0 +1,4 @@ +module -- deprecated_module: ignore + +import DeprecatedModule.Old +import DeprecatedModule.OldNoMessage diff --git a/tests/pkg/deprecated_module/DeprecatedModule/ConsumerShowDeprecated.lean b/tests/pkg/deprecated_module/DeprecatedModule/ConsumerShowDeprecated.lean new file mode 100644 index 0000000000..79ee9a0288 --- /dev/null +++ b/tests/pkg/deprecated_module/DeprecatedModule/ConsumerShowDeprecated.lean @@ -0,0 +1,6 @@ +module -- deprecated_module: ignore + +import DeprecatedModule.Old -- deprecated_module: ignore +import DeprecatedModule.OldNoMessage + +#show_deprecated_modules diff --git a/tests/pkg/deprecated_module/DeprecatedModule/New.lean b/tests/pkg/deprecated_module/DeprecatedModule/New.lean new file mode 100644 index 0000000000..8a5c5fc755 --- /dev/null +++ b/tests/pkg/deprecated_module/DeprecatedModule/New.lean @@ -0,0 +1,3 @@ +module + +def newFunction := 42 diff --git a/tests/pkg/deprecated_module/DeprecatedModule/Old.lean b/tests/pkg/deprecated_module/DeprecatedModule/Old.lean new file mode 100644 index 0000000000..e63437633f --- /dev/null +++ b/tests/pkg/deprecated_module/DeprecatedModule/Old.lean @@ -0,0 +1,5 @@ +module + +import DeprecatedModule.New + +deprecated_module "use DeprecatedModule.New instead" (since := "2026-03-19") diff --git a/tests/pkg/deprecated_module/DeprecatedModule/OldDouble.lean b/tests/pkg/deprecated_module/DeprecatedModule/OldDouble.lean new file mode 100644 index 0000000000..bb934cd14e --- /dev/null +++ b/tests/pkg/deprecated_module/DeprecatedModule/OldDouble.lean @@ -0,0 +1,6 @@ +module + +import DeprecatedModule.New + +deprecated_module "first deprecation" (since := "2026-03-19") +deprecated_module "second deprecation" (since := "2026-03-20") diff --git a/tests/pkg/deprecated_module/DeprecatedModule/OldNoMessage.lean b/tests/pkg/deprecated_module/DeprecatedModule/OldNoMessage.lean new file mode 100644 index 0000000000..82524295a1 --- /dev/null +++ b/tests/pkg/deprecated_module/DeprecatedModule/OldNoMessage.lean @@ -0,0 +1,3 @@ +module + +deprecated_module (since := "2026-03-19") diff --git a/tests/pkg/deprecated_module/DeprecatedModule/Transitive.lean b/tests/pkg/deprecated_module/DeprecatedModule/Transitive.lean new file mode 100644 index 0000000000..5789168adf --- /dev/null +++ b/tests/pkg/deprecated_module/DeprecatedModule/Transitive.lean @@ -0,0 +1,3 @@ +module + +import DeprecatedModule.Old diff --git a/tests/pkg/deprecated_module/DeprecatedModule/TransitiveConsumer.lean b/tests/pkg/deprecated_module/DeprecatedModule/TransitiveConsumer.lean new file mode 100644 index 0000000000..e0da50c8ec --- /dev/null +++ b/tests/pkg/deprecated_module/DeprecatedModule/TransitiveConsumer.lean @@ -0,0 +1,3 @@ +module + +import DeprecatedModule.Transitive diff --git a/tests/pkg/deprecated_module/lakefile.toml b/tests/pkg/deprecated_module/lakefile.toml new file mode 100644 index 0000000000..2fe8fb46e3 --- /dev/null +++ b/tests/pkg/deprecated_module/lakefile.toml @@ -0,0 +1,20 @@ +name = "deprecated_module" +defaultTargets = ["Main"] + +[[lean_lib]] +name = "Main" +roots = [ + "DeprecatedModule.New", + "DeprecatedModule.Old", + "DeprecatedModule.OldNoMessage", + "DeprecatedModule.OldDouble", + "DeprecatedModule.Transitive", + "DeprecatedModule.Consumer", + "DeprecatedModule.TransitiveConsumer", + "DeprecatedModule.ConsumerIgnoreAll", + "DeprecatedModule.ConsumerIgnoreOne", + "DeprecatedModule.ConsumerIgnoreWhitespace", + "DeprecatedModule.ConsumerIgnoreOnlyImport", + "DeprecatedModule.ConsumerIgnoreLastImport", + "DeprecatedModule.ConsumerShowDeprecated", +] diff --git a/tests/pkg/deprecated_module/run_test.sh b/tests/pkg/deprecated_module/run_test.sh new file mode 100644 index 0000000000..e4d4cf6b12 --- /dev/null +++ b/tests/pkg/deprecated_module/run_test.sh @@ -0,0 +1,37 @@ +rm -rf .lake/build + +# Build Main library — includes all test modules +capture lake build Main + +# With-message format: custom message on its own line, then deprecation with replacement imports +check_out_contains "use DeprecatedModule.New instead" +check_out_contains "'DeprecatedModule.Old' has been deprecated: please replace this import by" +check_out_contains "import DeprecatedModule.New" + +# Without-message format: deprecation with replacement imports but no custom message +check_out_contains "'DeprecatedModule.OldNoMessage' has been deprecated: please replace this import by" + +# OldDouble has two deprecated_module commands — second triggers duplicate warning +check_out_contains "module is already marked as deprecated" + +# TransitiveConsumer only imports Transitive (which imports Old) — no direct import, no warning +# (covered implicitly: if transitive warnings leaked, we'd see extra output) + +# ConsumerIgnoreOne: "deprecated_module: ignore" on Old import only — OldNoMessage should still warn +check_out_contains "ConsumerIgnoreOne.lean:1:0: 'DeprecatedModule.OldNoMessage' has been deprecated" + +# ConsumerIgnoreOnlyImport: single import with "deprecated_module: ignore" — no warning +if grep -Fq "ConsumerIgnoreOnlyImport.lean" "$CAPTURED.out.produced"; then + fail "ConsumerIgnoreOnlyImport should not produce any deprecation warning" +fi + +# ConsumerIgnoreLastImport: "deprecated_module: ignore" on last import (Old) — OldNoMessage should +# still warn, but Old should be suppressed +check_out_contains "ConsumerIgnoreLastImport.lean:1:0: 'DeprecatedModule.OldNoMessage' has been deprecated" +if grep -Fq "ConsumerIgnoreLastImport.lean:1:0: 'DeprecatedModule.Old' has been deprecated" "$CAPTURED.out.produced"; then + fail "ConsumerIgnoreLastImport should not warn about Old (annotated with deprecated_module: ignore)" +fi + +# ConsumerShowDeprecated: #show_deprecated_modules should still list deprecated modules +# even when warnings are suppressed via "deprecated_module: ignore" +check_out_contains "ConsumerShowDeprecated.lean:6:0: Deprecated modules"