diff options
author | metagn <metagngn@gmail.com> | 2023-05-02 12:28:52 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-05-02 11:28:52 +0200 |
commit | e5d0907a42112ce28ae2cbd13d095c0d7c23fd91 (patch) | |
tree | eec33c6690fee50dacf1a7421b21df18eb9f86f0 | |
parent | ca82b4ea16eb7d48b6851110bcb4667570a97f52 (diff) | |
download | Nim-e5d0907a42112ce28ae2cbd13d095c0d7c23fd91.tar.gz |
line info for strformat + fix issue with typed templates (#21761)
* line info in strformat * also fix #20381
-rw-r--r-- | lib/pure/strformat.nim | 30 | ||||
-rw-r--r-- | tests/stdlib/tstrformat.nim | 10 | ||||
-rw-r--r-- | tests/stdlib/tstrformatlineinfo.nim | 8 |
3 files changed, 40 insertions, 8 deletions
diff --git a/lib/pure/strformat.nim b/lib/pure/strformat.nim index 216c1ff11..3fedff07b 100644 --- a/lib/pure/strformat.nim +++ b/lib/pure/strformat.nim @@ -577,7 +577,8 @@ template formatValue(result: var string; value: char; specifier: string) = template formatValue(result: var string; value: cstring; specifier: string) = result.add value -proc strformatImpl(f: string; openChar, closeChar: char): NimNode = +proc strformatImpl(f: string; openChar, closeChar: char, + lineInfoNode: NimNode = nil): NimNode = template missingCloseChar = error("invalid format string: missing closing character '" & closeChar & "'") @@ -585,7 +586,7 @@ proc strformatImpl(f: string; openChar, closeChar: char): NimNode = error "openChar and closeChar must not be ':'" var i = 0 let res = genSym(nskVar, "fmtRes") - result = newNimNode(nnkStmtListExpr) + result = newNimNode(nnkStmtListExpr, lineInfoNode) # XXX: https://github.com/nim-lang/Nim/issues/8405 # When compiling with -d:useNimRtl, certain procs such as `count` from the strutils # module are not accessible at compile-time: @@ -644,6 +645,7 @@ proc strformatImpl(f: string; openChar, closeChar: char): NimNode = x = parseExpr(subexpr) except ValueError as e: error("could not parse `$#` in `$#`.\n$#" % [subexpr, f, e.msg]) + x.copyLineInfo(lineInfoNode) let formatSym = bindSym("formatValue", brOpen) var options = "" if f[i] == ':': @@ -669,10 +671,22 @@ proc strformatImpl(f: string; openChar, closeChar: char): NimNode = if strlit.len > 0: result.add newCall(bindSym"add", res, newLit(strlit)) result.add res + # workaround for #20381 + var blockExpr = newNimNode(nnkBlockExpr, lineInfoNode) + blockExpr.add(newEmptyNode()) + blockExpr.add(result) + result = blockExpr when defined(debugFmtDsl): echo repr result -macro fmt*(pattern: static string; openChar: static char, closeChar: static char): string = +macro fmt(pattern: static string; openChar: static char, closeChar: static char, lineInfoNode: untyped): string = + ## version of `fmt` with dummy untyped param for line info + strformatImpl(pattern, openChar, closeChar, lineInfoNode) + +when not defined(nimHasCallsitePragma): + {.pragma: callsite.} + +template fmt*(pattern: static string; openChar: static char, closeChar: static char): string {.callsite.} = ## Interpolates `pattern` using symbols in scope. runnableExamples: let x = 7 @@ -689,13 +703,13 @@ macro fmt*(pattern: static string; openChar: static char, closeChar: static char assert "<x>".fmt('<', '>') == "7" assert "<<<x>>>".fmt('<', '>') == "<7>" assert "`x`".fmt('`', '`') == "7" - strformatImpl(pattern, openChar, closeChar) + fmt(pattern, openChar, closeChar, dummyForLineInfo) -template fmt*(pattern: static string): untyped = +template fmt*(pattern: static string): untyped {.callsite.} = ## Alias for `fmt(pattern, '{', '}')`. - fmt(pattern, '{', '}') + fmt(pattern, '{', '}', dummyForLineInfo) -macro `&`*(pattern: string{lit}): string = +template `&`*(pattern: string{lit}): string {.callsite.} = ## `&pattern` is the same as `pattern.fmt`. ## For a specification of the `&` macro, see the module level documentation. # pending bug #18275, bug #18278, use `pattern: static string` @@ -707,4 +721,4 @@ macro `&`*(pattern: string{lit}): string = assert &"{x}\n" == "7\n" # regular string literal assert &"{x}\n" == "{x}\n".fmt # `fmt` can be used instead assert &"{x}\n" != fmt"{x}\n" # see `fmt` docs, this would use a raw string literal - strformatImpl(pattern.strVal, '{', '}') + fmt(pattern, '{', '}', dummyForLineInfo) diff --git a/tests/stdlib/tstrformat.nim b/tests/stdlib/tstrformat.nim index 0b163125b..3c0d55c1d 100644 --- a/tests/stdlib/tstrformat.nim +++ b/tests/stdlib/tstrformat.nim @@ -562,6 +562,16 @@ proc main() = doAssert &"""{(if true: "'" & "'" & ')' else: "")}""" == "'')" doAssert &"{(if true: \"\'\" & \"'\" & ')' else: \"\")}" == "'')" doAssert fmt"""{(if true: "'" & ')' else: "")}""" == "')" + + block: # issue #20381 + var ss: seq[string] + template myTemplate(s: string) = + ss.add s + ss.add s + proc foo() = + myTemplate fmt"hello" + foo() + doAssert ss == @["hello", "hello"] static: main() main() diff --git a/tests/stdlib/tstrformatlineinfo.nim b/tests/stdlib/tstrformatlineinfo.nim new file mode 100644 index 000000000..3a7bf0d33 --- /dev/null +++ b/tests/stdlib/tstrformatlineinfo.nim @@ -0,0 +1,8 @@ +# issue #21759 + +{.hint[ConvFromXToItselfNotNeeded]: on.} + +import std/strformat + +echo fmt"{string ""abc""}" #[tt.Hint + ^ conversion from string to itself is pointless]# |