From 9a8231ec5d73fe22000baa2fa55db14b1bd9ccd0 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Thu, 15 Jul 2021 23:13:41 -0700 Subject: [PATCH 1/6] add {.migrated(nimMigratedFoo, "foo changed its semantics: ...")} --- lib/system.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/system.nim b/lib/system.nim index 57d620e57ff19..5c01fa08dc698 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -2134,7 +2134,7 @@ const import system/dollars export dollars -proc delete*[T](x: var seq[T], i: Natural) {.noSideEffect.} = +proc delete*[T](x: var seq[T], i: Natural) {.noSideEffect, deprecated: "foobar".} = ## Deletes the item at index `i` by moving all `x[i+1..^1]` items by one position. ## ## This is an `O(n)` operation. From 0b6cdf1326d6dbc62660c6562f00757037aca037 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Sat, 17 Jul 2021 11:32:21 -0700 Subject: [PATCH 2/6] PRTEMP1 --- compiler/condsyms.nim | 1 + compiler/pragmas.nim | 4 +++- compiler/wordrecg.nim | 1 + lib/pure/os.nim | 2 +- lib/system.nim | 5 ++++- lib/system/inclrtl.nim | 5 +++++ 6 files changed, 15 insertions(+), 3 deletions(-) diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim index 33cd1818b628f..108e096481fef 100644 --- a/compiler/condsyms.nim +++ b/compiler/condsyms.nim @@ -137,3 +137,4 @@ proc initDefines*(symbols: StringTableRef) = defineSymbol("nimHasDragonBox") defineSymbol("nimHasHintAll") defineSymbol("nimHasTrace") + defineSymbol("nimHasCustomWarning") diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index 8ae931d84e6da..2c4121e6c8e49 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -22,7 +22,7 @@ const const declPragmas = {wImportc, wImportObjC, wImportCpp, wImportJs, wExportc, wExportCpp, - wExportNims, wExtern, wDeprecated, wNodecl, wError, wUsed} + wExportNims, wExtern, wDeprecated, wNodecl, wError, wUsed, wCustom} ## common pragmas for declarations, to a good approximation procPragmas* = declPragmas + {FirstCallConv..LastCallConv, wMagic, wNoSideEffect, wSideEffect, wNoreturn, wNosinks, wDynlib, wHeader, @@ -977,6 +977,8 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, # whole module elif it.kind in nkPragmaCallKinds: deprecatedStmt(c, it) else: incl(c.module.flags, sfDeprecated) + of wCustom: + of wVarargs: noVal(c, it) if sym.typ == nil: invalidPragma(c, it) diff --git a/compiler/wordrecg.nim b/compiler/wordrecg.nim index c3625c5f39150..a9fa96fdab773 100644 --- a/compiler/wordrecg.nim +++ b/compiler/wordrecg.nim @@ -110,6 +110,7 @@ type wInOut = "inout", wByCopy = "bycopy", wByRef = "byref", wOneWay = "oneway", wBitsize = "bitsize", wImportHidden = "all", + wCustom = "custom", TSpecialWords* = set[TSpecialWord] diff --git a/lib/pure/os.nim b/lib/pure/os.nim index 213da2aedff99..00f89dc8165ff 100644 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -887,7 +887,7 @@ when not defined(nimscript): include "includes/osenv" proc getHomeDir*(): string {.rtl, extern: "nos$1", - tags: [ReadEnvEffect, ReadIOEffect].} = + tags: [ReadEnvEffect, ReadIOEffect], migration(bar, "baz").} = ## Returns the home directory of the current user. ## ## This proc is wrapped by the `expandTilde proc <#expandTilde,string>`_ diff --git a/lib/system.nim b/lib/system.nim index 5c01fa08dc698..f983f14fa8431 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -111,6 +111,9 @@ proc compileOption*(option, arg: string): bool {. when compileOption("opt", "size") and compileOption("gc", "boehm"): discard "compiled with optimization for size and uses Boehm's GC" +when not defined(nimHasCustomWarning): + {.pragma: custom.} + {.push warning[GcMem]: off, warning[Uninit]: off.} # {.push hints: off.} @@ -2134,7 +2137,7 @@ const import system/dollars export dollars -proc delete*[T](x: var seq[T], i: Natural) {.noSideEffect, deprecated: "foobar".} = +proc delete*[T](x: var seq[T], i: Natural) {.noSideEffect.} = ## Deletes the item at index `i` by moving all `x[i+1..^1]` items by one position. ## ## This is an `O(n)` operation. diff --git a/lib/system/inclrtl.nim b/lib/system/inclrtl.nim index ca41f39c6915f..cd8fdced85b21 100644 --- a/lib/system/inclrtl.nim +++ b/lib/system/inclrtl.nim @@ -49,3 +49,8 @@ else: when defined(nimHasSinkInference): {.push sinkInference: on.} + +# when defined(nimMigrationLints): +# {.pragma: migration, deprecated: "see nimLegacyHomeDir".} +# else: +# {.pragma: migration.} From 39ee52de6b82e2ad5058cc7234f9b855a136a983 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Sat, 17 Jul 2021 13:11:22 -0700 Subject: [PATCH 3/6] works --- compiler/lineinfos.nim | 2 ++ compiler/pragmas.nim | 10 ++++++++-- compiler/suggest.nim | 11 +++++++++-- lib/pure/os.nim | 3 ++- lib/system.nim | 3 --- lib/system/inclrtl.nim | 6 ++---- 6 files changed, 23 insertions(+), 12 deletions(-) diff --git a/compiler/lineinfos.nim b/compiler/lineinfos.nim index 801c20d2557ea..0cb82f59f1e0e 100644 --- a/compiler/lineinfos.nim +++ b/compiler/lineinfos.nim @@ -46,6 +46,7 @@ type warnCannotOpenFile = "CannotOpenFile", warnOctalEscape = "OctalEscape", warnXIsNeverRead = "XIsNeverRead", warnXmightNotBeenInit = "XmightNotBeenInit", warnDeprecated = "Deprecated", warnConfigDeprecated = "ConfigDeprecated", + warnMigrated = "Migrated", warnSmallLshouldNotBeUsed = "SmallLshouldNotBeUsed", warnUnknownMagic = "UnknownMagic", warnRedefinitionOfLabel = "RedefinitionOfLabel", warnUnknownSubstitutionX = "UnknownSubstitutionX", warnLanguageXNotSupported = "LanguageXNotSupported", @@ -109,6 +110,7 @@ const warnXmightNotBeenInit: "'$1' might not have been initialized", warnDeprecated: "$1", warnConfigDeprecated: "config file '$1' is deprecated", + warnMigrated: "$1", warnSmallLshouldNotBeUsed: "'l' should not be used as an identifier; may look like '1' (one)", warnUnknownMagic: "unknown magic '$1' might crash the compiler", warnRedefinitionOfLabel: "redefinition of label '$1'", diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index 2c4121e6c8e49..d50a6aa4580d7 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -977,8 +977,14 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, # whole module elif it.kind in nkPragmaCallKinds: deprecatedStmt(c, it) else: incl(c.module.flags, sfDeprecated) - of wCustom: - + # of wCustom: + # let cond = considerQuotedIdent(c, it[1]) + # if c.config.isDefined(): + # if sym != nil and sym.kind in routineKinds + {skType, skVar, skLet}: + # if it.kind in nkPragmaCallKinds: discard getStrLitNode(c, it) + # incl(sym.flags, sfDeprecated) + # else: + # localError(c.config, it.info, "unsupported") of wVarargs: noVal(c, it) if sym.typ == nil: invalidPragma(c, it) diff --git a/compiler/suggest.nim b/compiler/suggest.nim index eaa30040a8d30..a0bf2c4cc46fd 100644 --- a/compiler/suggest.nim +++ b/compiler/suggest.nim @@ -32,7 +32,7 @@ # included from sigmatch.nim -import algorithm, sets, prefixmatches, lineinfos, parseutils, linter, tables +import algorithm, sets, prefixmatches, lineinfos, parseutils, linter, tables, astmsgs from wordrecg import wDeprecated, wError, wAddr, wYield when defined(nimsuggest): @@ -547,7 +547,14 @@ proc warnAboutDeprecated(conf: ConfigRef; info: TLineInfo; s: PSym) = for it in pragmaNode: if whichPragma(it) == wDeprecated and it.safeLen == 2 and it[1].kind in {nkStrLit..nkTripleStrLit}: - message(conf, info, warnDeprecated, it[1].strVal & "; " & name & " is deprecated") + let msg = it[1].strVal + const migratedMagic = "migrated:" + if msg.startsWith migratedMagic: + var msg2 = msg[migratedMagic.len .. ^1] & "; " & name & " was migrated" + addDeclaredLoc(msg2, conf, s) + message(conf, info, warnMigrated, msg2) + else: + message(conf, info, warnDeprecated, msg & "; " & name & " is deprecated") return message(conf, info, warnDeprecated, name & " is deprecated") diff --git a/lib/pure/os.nim b/lib/pure/os.nim index 00f89dc8165ff..771adaf694966 100644 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -887,7 +887,8 @@ when not defined(nimscript): include "includes/osenv" proc getHomeDir*(): string {.rtl, extern: "nos$1", - tags: [ReadEnvEffect, ReadIOEffect], migration(bar, "baz").} = + tags: [ReadEnvEffect, ReadIOEffect].} = + # tags: [ReadEnvEffect, ReadIOEffect], custom(nimOsDirsTrailingSlash, "getHomeDir now does not include trailing DirSep").} = ## Returns the home directory of the current user. ## ## This proc is wrapped by the `expandTilde proc <#expandTilde,string>`_ diff --git a/lib/system.nim b/lib/system.nim index f983f14fa8431..57d620e57ff19 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -111,9 +111,6 @@ proc compileOption*(option, arg: string): bool {. when compileOption("opt", "size") and compileOption("gc", "boehm"): discard "compiled with optimization for size and uses Boehm's GC" -when not defined(nimHasCustomWarning): - {.pragma: custom.} - {.push warning[GcMem]: off, warning[Uninit]: off.} # {.push hints: off.} diff --git a/lib/system/inclrtl.nim b/lib/system/inclrtl.nim index cd8fdced85b21..bd41346b27f28 100644 --- a/lib/system/inclrtl.nim +++ b/lib/system/inclrtl.nim @@ -50,7 +50,5 @@ else: when defined(nimHasSinkInference): {.push sinkInference: on.} -# when defined(nimMigrationLints): -# {.pragma: migration, deprecated: "see nimLegacyHomeDir".} -# else: -# {.pragma: migration.} +when not defined(nimHasCustomWarning): + {.pragma: custom.} From 3eef4454d66b5e3718c94e46b45f6a062f99a7b7 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Sat, 17 Jul 2021 13:54:52 -0700 Subject: [PATCH 4/6] migrated now renders correctly in docs --- compiler/condsyms.nim | 1 - compiler/docgen.nim | 10 ++++++++-- compiler/pragmas.nim | 10 +--------- compiler/suggest.nim | 6 +++--- compiler/wordrecg.nim | 1 - config/config.nims | 4 ++++ lib/core/macros.nim | 22 +++++++++++++++++++++- lib/pure/os.nim | 15 +++++++-------- lib/std/private/constants.nim | 5 +++++ lib/system/inclrtl.nim | 3 --- 10 files changed, 49 insertions(+), 28 deletions(-) create mode 100644 lib/std/private/constants.nim diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim index 108e096481fef..33cd1818b628f 100644 --- a/compiler/condsyms.nim +++ b/compiler/condsyms.nim @@ -137,4 +137,3 @@ proc initDefines*(symbols: StringTableRef) = defineSymbol("nimHasDragonBox") defineSymbol("nimHasHintAll") defineSymbol("nimHasTrace") - defineSymbol("nimHasCustomWarning") diff --git a/compiler/docgen.nim b/compiler/docgen.nim index 1ae00e22e3afa..eee927aaca774 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -21,6 +21,7 @@ import from uri import encodeUrl from std/private/globs import nativeToUnixPath from nodejs import findNodeJs +import std/private/constants const exportSection = skField @@ -809,8 +810,13 @@ proc genDeprecationMsg(d: PDoc, n: PNode): string = "label" , "Deprecated", "message", ""] of 2: # Deprecated w/ a message if n[1].kind in {nkStrLit..nkTripleStrLit}: - result = getConfigVar(d.conf, "doc.deprecationmsg") % [ - "label", "Deprecated:", "message", xmltree.escape(n[1].strVal)] + let msg = n[1].strVal + if msg.startsWith migratedPrefix: + result = getConfigVar(d.conf, "doc.deprecationmsg") % [ + "label", "Migrated:", "message", xmltree.escape(msg[migratedPrefix.len..^1])] + else: + result = getConfigVar(d.conf, "doc.deprecationmsg") % [ + "label", "Deprecated:", "message", xmltree.escape(msg)] else: doAssert false diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index d50a6aa4580d7..8ae931d84e6da 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -22,7 +22,7 @@ const const declPragmas = {wImportc, wImportObjC, wImportCpp, wImportJs, wExportc, wExportCpp, - wExportNims, wExtern, wDeprecated, wNodecl, wError, wUsed, wCustom} + wExportNims, wExtern, wDeprecated, wNodecl, wError, wUsed} ## common pragmas for declarations, to a good approximation procPragmas* = declPragmas + {FirstCallConv..LastCallConv, wMagic, wNoSideEffect, wSideEffect, wNoreturn, wNosinks, wDynlib, wHeader, @@ -977,14 +977,6 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, # whole module elif it.kind in nkPragmaCallKinds: deprecatedStmt(c, it) else: incl(c.module.flags, sfDeprecated) - # of wCustom: - # let cond = considerQuotedIdent(c, it[1]) - # if c.config.isDefined(): - # if sym != nil and sym.kind in routineKinds + {skType, skVar, skLet}: - # if it.kind in nkPragmaCallKinds: discard getStrLitNode(c, it) - # incl(sym.flags, sfDeprecated) - # else: - # localError(c.config, it.info, "unsupported") of wVarargs: noVal(c, it) if sym.typ == nil: invalidPragma(c, it) diff --git a/compiler/suggest.nim b/compiler/suggest.nim index a0bf2c4cc46fd..27bad37877dae 100644 --- a/compiler/suggest.nim +++ b/compiler/suggest.nim @@ -33,6 +33,7 @@ # included from sigmatch.nim import algorithm, sets, prefixmatches, lineinfos, parseutils, linter, tables, astmsgs +import std/private/constants from wordrecg import wDeprecated, wError, wAddr, wYield when defined(nimsuggest): @@ -548,9 +549,8 @@ proc warnAboutDeprecated(conf: ConfigRef; info: TLineInfo; s: PSym) = if whichPragma(it) == wDeprecated and it.safeLen == 2 and it[1].kind in {nkStrLit..nkTripleStrLit}: let msg = it[1].strVal - const migratedMagic = "migrated:" - if msg.startsWith migratedMagic: - var msg2 = msg[migratedMagic.len .. ^1] & "; " & name & " was migrated" + if msg.startsWith migratedPrefix: + var msg2 = msg[migratedPrefix.len .. ^1] & "; " & name & " was migrated" addDeclaredLoc(msg2, conf, s) message(conf, info, warnMigrated, msg2) else: diff --git a/compiler/wordrecg.nim b/compiler/wordrecg.nim index a9fa96fdab773..c3625c5f39150 100644 --- a/compiler/wordrecg.nim +++ b/compiler/wordrecg.nim @@ -110,7 +110,6 @@ type wInOut = "inout", wByCopy = "bycopy", wByRef = "byref", wOneWay = "oneway", wBitsize = "bitsize", wImportHidden = "all", - wCustom = "custom", TSpecialWords* = set[TSpecialWord] diff --git a/config/config.nims b/config/config.nims index 2ae86012c68fe..aa8f8880f6ee4 100644 --- a/config/config.nims +++ b/config/config.nims @@ -14,3 +14,7 @@ when defined(nimStrictMode): # switch("hint", "ConvFromXtoItselfNotNeeded") switch("hintAsError", "ConvFromXtoItselfNotNeeded") # future work: XDeclaredButNotUsed + +switch("define", "nimMigratedGetHomeDir") +switch("define", "nimMigratedGetConfigDir") +switch("define", "nimMigratedGetTempDir") diff --git a/lib/core/macros.nim b/lib/core/macros.nim index c09fae6b3ac0e..c01704da839cc 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -8,7 +8,7 @@ # include "system/inclrtl" -import std/private/since +import std/private/[since, constants] ## This module contains the interface to the compiler's abstract syntax ## tree (`AST`:idx:). Macros operate on this tree. @@ -1800,3 +1800,23 @@ proc extractDocCommentsAndRunnables*(n: NimNode): NimNode = result.add ni else: break else: break + +macro customImpl(msg, body): untyped = + result = body + addPragma(result, newCall(ident"deprecated", msg)) + +template migrated*(ident, msg, body): untyped = + ## Conditionally generate a migration warning for APIs that were migrated to + ## different semantics, to help existing code migrate. + runnableExamples("--warningAsError:migrated -d:nimMigratedExample1"): + proc fn*(a: int) {.migrated(nimMigratedExample1, "fn can now raise a Defect").} = + doAssert a >= 0 + assert not compiles(fn(10)) # would generate a warning (and ) + runnableExamples("--warningAsError:migrated"): + # With `-d:nimMigratedExample1`, would generate: + # Warning: fn can now raise a Defect; fn was migrated [proc declared in ...] [Migrated] + proc fn*(a: int) {.migrated(nimMigratedExample1, "fn can now raise a Defect").} = + doAssert a >= 0 + fn(10) + when defined(ident): customImpl(migratedPrefix & msg, body) + else: body diff --git a/lib/pure/os.nim b/lib/pure/os.nim index 771adaf694966..ff374f4241868 100644 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -44,7 +44,7 @@ include system/inclrtl import std/private/since -import strutils, pathnorm +import strutils, pathnorm, macros const weirdTarget = defined(nimscript) or defined(js) @@ -886,9 +886,8 @@ include "includes/oserr" when not defined(nimscript): include "includes/osenv" -proc getHomeDir*(): string {.rtl, extern: "nos$1", - tags: [ReadEnvEffect, ReadIOEffect].} = - # tags: [ReadEnvEffect, ReadIOEffect], custom(nimOsDirsTrailingSlash, "getHomeDir now does not include trailing DirSep").} = +proc getHomeDir*(): string {.rtl, extern: "nos$1", tags: [ReadEnvEffect, ReadIOEffect], + migrated(nimMigratedGetHomeDir, "`getHomeDir` now does not end in DirSep, see `normalizePathEnd`").} = ## Returns the home directory of the current user. ## ## This proc is wrapped by the `expandTilde proc <#expandTilde,string>`_ @@ -911,8 +910,8 @@ proc getHomeDir*(): string {.rtl, extern: "nos$1", else: result = getEnv("HOME") result.normalizePathEnd(trailingSep = defined(nimLegacyHomeDir)) -proc getConfigDir*(): string {.rtl, extern: "nos$1", - tags: [ReadEnvEffect, ReadIOEffect].} = +proc getConfigDir*(): string {.rtl, extern: "nos$1", tags: [ReadEnvEffect, ReadIOEffect], + migrated(nimMigratedGetConfigDir, "`getConfigDir` now does not end in DirSep, see `normalizePathEnd`").} = ## Returns the config directory of the current user for applications. ## ## On non-Windows OSs, this proc conforms to the XDG Base Directory @@ -990,8 +989,8 @@ template getTempDirImpl(result: var string) = else: getEnvImpl(result, ["TMPDIR", "TEMP", "TMP", "TEMPDIR"]) -proc getTempDir*(): string {.rtl, extern: "nos$1", - tags: [ReadEnvEffect, ReadIOEffect].} = +proc getTempDir*(): string {.rtl, extern: "nos$1", tags: [ReadEnvEffect, ReadIOEffect], + migrated(nimMigratedGetTempDir, "`getTempDir` now does not end in DirSep, see `normalizePathEnd`").} = ## Returns the temporary directory of the current user for applications to ## save temporary files in. ## diff --git a/lib/std/private/constants.nim b/lib/std/private/constants.nim new file mode 100644 index 0000000000000..ae76e4bb85259 --- /dev/null +++ b/lib/std/private/constants.nim @@ -0,0 +1,5 @@ +##[ +This module includes constants that are needed across modules, to avoid redefining them. +]## + +const migratedPrefix* = "migrated: " diff --git a/lib/system/inclrtl.nim b/lib/system/inclrtl.nim index bd41346b27f28..ca41f39c6915f 100644 --- a/lib/system/inclrtl.nim +++ b/lib/system/inclrtl.nim @@ -49,6 +49,3 @@ else: when defined(nimHasSinkInference): {.push sinkInference: on.} - -when not defined(nimHasCustomWarning): - {.pragma: custom.} From 07bdd3dce3331975045c5e95bc9d985b755fb0ad Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Sat, 17 Jul 2021 13:57:15 -0700 Subject: [PATCH 5/6] changelog --- changelog.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/changelog.md b/changelog.md index 289202587319b..f7e732eb446e8 100644 --- a/changelog.md +++ b/changelog.md @@ -351,6 +351,7 @@ - Deprecated `sequtils.delete` and added an overload taking a `Slice` that raises a defect if the slice is out of bounds. + ## Language changes - `nimscript` now handles `except Exception as e`. @@ -391,6 +392,9 @@ - Added a new module `std/importutils`, and an API `privateAccess`, which allows access to private fields for an object type in the current scope. +- APIs can now indicate they've changed their semantics via macros.migrated, e.g.: + `{.migrated(nimMigratedGetHomeDir, "`getHomeDir` now does not end in DirSep, see `normalizePathEnd`").}` + - `typeof(voidStmt)` now works and returns `void`. - The `gc:orc` algorithm was refined so that custom container types can participate in the From b8d874772d95285b8e739065726abc4c02298e64 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Sat, 17 Jul 2021 14:10:25 -0700 Subject: [PATCH 6/6] fixup --- changelog.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/changelog.md b/changelog.md index f7e732eb446e8..ceb23dbfe8a74 100644 --- a/changelog.md +++ b/changelog.md @@ -351,7 +351,6 @@ - Deprecated `sequtils.delete` and added an overload taking a `Slice` that raises a defect if the slice is out of bounds. - ## Language changes - `nimscript` now handles `except Exception as e`. @@ -392,7 +391,7 @@ - Added a new module `std/importutils`, and an API `privateAccess`, which allows access to private fields for an object type in the current scope. -- APIs can now indicate they've changed their semantics via macros.migrated, e.g.: +- APIs can now indicate they've changed their semantics via `macros.migrated`, e.g.: `{.migrated(nimMigratedGetHomeDir, "`getHomeDir` now does not end in DirSep, see `normalizePathEnd`").}` - `typeof(voidStmt)` now works and returns `void`.