@@ -12,13 +12,13 @@ open FSharp.Compiler.Syntax
12
12
open FSharp.Compiler .Text
13
13
open FSharp.Compiler .UnicodeLexing
14
14
open FSharp.Test .Compiler
15
+ open System.IO
15
16
16
17
module Line =
17
18
18
- let parse ( source : string ) =
19
+ let parse ( source : string ) sourceFileName =
19
20
let checker = FSharpChecker.Create()
20
21
let langVersion = " preview"
21
- let sourceFileName = " Line.fs"
22
22
let parsingOptions =
23
23
{ FSharpParsingOptions.Default with
24
24
SourceFiles = [| sourceFileName |]
@@ -30,7 +30,7 @@ module Line =
30
30
31
31
[<Literal>]
32
32
let private case1 = """ module A
33
- #line 1 "xyz .fs"
33
+ #line 1 "xyz1 .fs"
34
34
(
35
35
printfn ""
36
36
)
@@ -39,24 +39,24 @@ printfn ""
39
39
[<Literal>]
40
40
let private case2 = """ module A
41
41
(
42
- #line 1 "xyz .fs"
42
+ #line 1 "xyz2 .fs"
43
43
printfn ""
44
44
)
45
45
"""
46
46
47
47
[<Literal>]
48
48
let private case3 = """ module A
49
49
(
50
- #line 1 "xyz .fs"
50
+ #line 1 "xyz3 .fs"
51
51
)
52
52
"""
53
53
54
54
[<Theory>]
55
- [<InlineData( 1 , case1, " xyz .fs:(1,0--3,1)" ) >]
56
- [<InlineData( 2 , case2, " Line .fs:(2,0--5,1)" ) >]
57
- [<InlineData( 3 , case3, " Line .fs:(2,0--4,1)" ) >]
55
+ [<InlineData( 1 , case1, " xyz1 .fs:(1,0--3,1)" ) >]
56
+ [<InlineData( 2 , case2, " Line2 .fs:(2,0--5,1)" ) >]
57
+ [<InlineData( 3 , case3, " Line3 .fs:(2,0--4,1)" ) >]
58
58
let ``check expr range interacting with line directive`` ( case , source , expectedRange ) =
59
- let parseResults = parse source
59
+ let parseResults = parse source $ " Line{case}.fs "
60
60
if parseResults.ParseHadErrors then failwith " unexpected: parse error"
61
61
let exprRange =
62
62
match parseResults.ParseTree with
@@ -111,12 +111,12 @@ printfn ""
111
111
let errors =
112
112
source
113
113
|> FSharp
114
- |> withFileName " test .fs"
114
+ |> withFileName " testn .fs"
115
115
|> compile
116
116
|> shouldFail
117
117
|> fun cr -> cr.Output.PerFileErrors |> List.map ( fun ( fn , d ) -> fn, d.Range.StartLine)
118
118
let expectedErrors =
119
- [( " test .fs" , 3 ); ( " test .fs" , 4 )]
119
+ [( " testn .fs" , 3 ); ( " testn .fs" , 4 )]
120
120
Assert.True(
121
121
List.forall2 ( fun ( e_fn , e_line ) ( fn , line ) -> e_ fn = fn && e_ line = line) expectedErrors errors,
122
122
sprintf " Expected: %A , Found: %A " expectedErrors errors
@@ -137,7 +137,7 @@ printfn ""
137
137
true
138
138
)
139
139
let lexbuf = StringAsLexbuf( true , langVersion, None, sourceText)
140
- resetLexbufPos " test .fs" lexbuf
140
+ resetLexbufPos " testt .fs" lexbuf
141
141
let tokenizer _ =
142
142
let t = Lexer.token lexargs true lexbuf
143
143
let p = lexbuf.StartPos
@@ -149,14 +149,14 @@ printfn ""
149
149
1
150
150
#line 5 "other.fs"
151
151
2
152
- #line 10 "test .fs"
152
+ #line 10 "testt .fs"
153
153
3
154
154
"""
155
155
156
156
let private expected = [
157
- " test .fs" , 2
158
- " test .fs" , 4
159
- " test .fs" , 6
157
+ " testt .fs" , 2
158
+ " testt .fs" , 4
159
+ " testt .fs" , 6
160
160
]
161
161
162
162
[<Fact>]
@@ -166,3 +166,187 @@ printfn ""
166
166
for (( e_ idx, e_ line), (_, idx, line)) in List.zip expected tokens do
167
167
Assert.Equal( e_ idx, idx)
168
168
Assert.Equal( e_ line, line)
169
+
170
+ let callerInfoSource = """
171
+ open System.Runtime.CompilerServices
172
+ open System.Runtime.InteropServices
173
+
174
+ type C() =
175
+ static member M (
176
+ [<CallerLineNumber; Optional; DefaultParameterValue 0>]c: int,
177
+ [<CallerFilePath; Optional; DefaultParameterValue "no value">]d: string) =
178
+ c, d
179
+
180
+ #line 1 "file1.fs"
181
+ let line1, file1 = C.M()
182
+ #line 551 "file2.fs"
183
+ let line2, file2 = C.M()
184
+
185
+ printfn $"{file1} {line1} {file2} {line2}"
186
+ """
187
+
188
+ [<Fact>]
189
+ let ``CallerLineNumber and CallerFilePath work with line directives`` () =
190
+ let results =
191
+ callerInfoSource
192
+ |> FSharp
193
+ |> withFileName " CallerInfo.fs"
194
+ |> withLangVersion " preview"
195
+ |> compileExeAndRun
196
+ match results.RunOutput with
197
+ | Some ( ExecutionOutput output) ->
198
+ let words = output.StdOut.Trim() .Split( ' ' )
199
+ let file1 = Path.GetFileName( words.[ 0 ])
200
+ let line1 = int words.[ 1 ]
201
+ let file2 = Path.GetFileName( words.[ 2 ])
202
+ let line2 = int words.[ 3 ]
203
+ Assert.Equal( " file1.fs" , file1)
204
+ Assert.Equal( 1 , line1)
205
+ Assert.Equal( " file2.fs" , file2)
206
+ Assert.Equal( 551 , line2)
207
+ | _ -> failwith " Unexpected: no run output"
208
+
209
+
210
+
211
+ let csharpLibSource = """
212
+ using System;
213
+ using System.Reflection;
214
+ using System.Runtime.CompilerServices;
215
+
216
+ namespace CSharpLib
217
+ {
218
+ public class CallerInfoTest
219
+ {
220
+ public static int LineNumber([CallerLineNumber] int line = 777)
221
+ {
222
+ return line;
223
+ }
224
+
225
+ public static string FilePath([CallerFilePath] string filePath = "dummy1")
226
+ {
227
+ return filePath;
228
+ }
229
+
230
+ public static string MemberName([CallerMemberName] string memberName = "dummy1")
231
+ {
232
+ return memberName;
233
+ }
234
+
235
+ public static Tuple<string, int, string> AllInfo(int normalArg, [CallerFilePath] string filePath = "dummy2", [CallerLineNumber] int line = 778, [CallerMemberName] string memberName = "dummy3")
236
+ {
237
+ return new Tuple<string, int, string>(filePath, line, memberName);
238
+ }
239
+ }
240
+
241
+ public class MyCallerInfoAttribute : Attribute
242
+ {
243
+ public int LineNumber { get; set; }
244
+
245
+ public MyCallerInfoAttribute([CallerLineNumber] int lineNumber = -1)
246
+ {
247
+ LineNumber = lineNumber;
248
+ }
249
+ }
250
+
251
+ public class MyCallerMemberNameAttribute : Attribute
252
+ {
253
+ public string MemberName { get; set; }
254
+
255
+ public MyCallerMemberNameAttribute([CallerMemberName] string member = "dflt")
256
+ {
257
+ MemberName = member;
258
+ }
259
+ }
260
+ }
261
+ """
262
+
263
+ let fsharpSource = """
264
+
265
+ open System.Runtime.CompilerServices
266
+ open CSharpLib
267
+
268
+ type MyTy([<CallerFilePath>] ?p0 : string) =
269
+ let mutable p = p0
270
+
271
+ member x.Path with get() = p
272
+
273
+ static member GetCallerFilePath([<CallerFilePath>] ?path : string) =
274
+ path
275
+
276
+ module Program =
277
+ let doubleSeparator = "##".Replace('#', System.IO.Path.DirectorySeparatorChar)
278
+ let sameDirectory = "#.#".Replace('#', System.IO.Path.DirectorySeparatorChar)
279
+ let parentDirectory = ".."
280
+ let matchesPath (path : string) (s : string) =
281
+ s.EndsWith(path.Replace('#', System.IO.Path.DirectorySeparatorChar))
282
+ && not (s.Contains(doubleSeparator))
283
+ && not (s.Contains(sameDirectory))
284
+ && not (s.Contains(parentDirectory))
285
+
286
+
287
+ [<EntryPoint>]
288
+ let main (_:string[]) =
289
+ printfn "starting main"
290
+ let o = MyTy()
291
+ let o1 = MyTy("42")
292
+
293
+ match o.Path with
294
+ | Some(path) when matchesPath "CallerInfo.fs" path -> ()
295
+ | Some(path) -> failwithf "Unexpected (1): %s " path
296
+ | None -> failwith "Unexpected (1): None"
297
+
298
+ match o1.Path with
299
+ | Some(path) when matchesPath "42" path -> ()
300
+ | Some(path) -> failwithf "Unexpected (2): %s " path
301
+ | None -> failwith "Unexpected (2): None"
302
+
303
+ match MyTy.GetCallerFilePath() with
304
+ | Some(path) when matchesPath "CallerInfo.fs" path -> ()
305
+ | Some(path) -> failwithf "Unexpected (3): %s " path
306
+ | None -> failwith "Unexpected (3): None"
307
+
308
+ match MyTy.GetCallerFilePath("42") with
309
+ | Some("42") -> ()
310
+ | Some(path) -> failwithf "Unexpected (4): %s " path
311
+ | None -> failwith "Unexpected (4): None"
312
+
313
+ match CallerInfoTest.FilePath() with
314
+ | path when matchesPath "CallerInfo.fs" path -> ()
315
+ | path -> failwithf "Unexpected (5): %s " path
316
+
317
+ match CallerInfoTest.FilePath("xyz") with
318
+ | "xyz" -> ()
319
+ | path -> failwithf "Unexpected (6): %s " path
320
+
321
+ match CallerInfoTest.AllInfo(21) with
322
+ | (path, _, _) when matchesPath "CallerInfo.fs" path -> ()
323
+ | (path, _, _) -> failwithf "Unexpected (7): %s " path
324
+
325
+ # 345 "qwerty.fsy"
326
+ match CallerInfoTest.AllInfo(123) with
327
+ | (path, _, _) when matchesPath "qwerty.fsy" path -> ()
328
+ | (path, _, _) -> failwithf "Unexpected (8): %s " path
329
+
330
+ # 456 "qwerty.fsl"
331
+ match CallerInfoTest.AllInfo(123) with
332
+ | (path, _, _) when matchesPath "qwerty.fsl" path -> ()
333
+ | (path, _, _) -> failwithf "Unexpected (9): %s " path
334
+
335
+ 0
336
+ """
337
+
338
+ [<Fact>]
339
+ let ``C # CallerLineNumber and CallerFilePath work with line directives`` () =
340
+ let csharp =
341
+ csharpLibSource
342
+ |> CSharp
343
+ |> withFileName " CallerInfoLib.cs"
344
+
345
+ fsharpSource
346
+ |> FSharp
347
+ |> withFileName " CallerInfo.fs"
348
+ |> withLangVersion " preview"
349
+ |> withReferences [ csharp]
350
+ |> compileExeAndRun
351
+ |> shouldSucceed
352
+
0 commit comments