summary refs log tree commit diff stats
diff options
context:
space:
mode:
authormetagn <metagngn@gmail.com>2023-05-02 12:28:52 +0300
committerGitHub <noreply@github.com>2023-05-02 11:28:52 +0200
commite5d0907a42112ce28ae2cbd13d095c0d7c23fd91 (patch)
treeeec33c6690fee50dacf1a7421b21df18eb9f86f0
parentca82b4ea16eb7d48b6851110bcb4667570a97f52 (diff)
downloadNim-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.nim30
-rw-r--r--tests/stdlib/tstrformat.nim10
-rw-r--r--tests/stdlib/tstrformatlineinfo.nim8
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]#