Skip to content

Commit 0827571

Browse files
authored
ensure that line directives are applied to source identifiers (#18918)
1 parent aa69e48 commit 0827571

File tree

11 files changed

+102
-39
lines changed

11 files changed

+102
-39
lines changed

docs/release-notes/.FSharp.Compiler.Service/10.0.100.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
* Tests: set test source for range debug printing ([PR #18879](https://github.com/dotnet/fsharp/pull/18879))
3434
* Checker: fix declaring type for abbreviated types extensions ([PR #18909](https://github.com/dotnet/fsharp/pull/18909))
3535
* Caches: type subsumption cache key perf regression ([Issue #18925](https://github.com/dotnet/fsharp/issues/18925) [PR #18926](https://github.com/dotnet/fsharp/pull/18926))
36+
* Ensure that line directives are applied to source identifiers (issue [#18908](https://github.com/dotnet/fsharp/issues/18908), PR [#18918](https://github.com/dotnet/fsharp/pull/18918))
3637

3738
### Changed
3839
* Use `errorR` instead of `error` in `CheckDeclarations.fs` when possible. ([PR #18645](https://github.com/dotnet/fsharp/pull/18645))

src/Compiler/Checking/Expressions/CheckExpressions.fs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -926,10 +926,13 @@ let TcConst (cenv: cenv) (overallTy: TType) m env synConst =
926926
| SynConst.Char c ->
927927
unif g.char_ty
928928
Const.Char c
929-
| SynConst.String (s, _, _)
930-
| SynConst.SourceIdentifier (_, s, _) ->
929+
| SynConst.String (s, _, _) ->
931930
unif g.string_ty
932931
Const.String s
932+
| SynConst.SourceIdentifier (i, s, m) ->
933+
unif g.string_ty
934+
let s = applyLineDirectivesToSourceIdentifier i s m
935+
Const.String s
933936
| SynConst.UserNum _ -> error (InternalError(FSComp.SR.tcUnexpectedBigRationalConstant(), m))
934937
| SynConst.Measure _ -> error (Error(FSComp.SR.tcInvalidTypeForUnitsOfMeasure(), m))
935938
| SynConst.UInt16s _ -> error (InternalError(FSComp.SR.tcUnexpectedConstUint16Array(), m))
@@ -4890,8 +4893,10 @@ and TcStaticConstantParameter (cenv: cenv) (env: TcEnv) tpenv kind (StripParenTy
48904893
| SynConst.Single n when typeEquiv g g.float32_ty kind -> record(g.float32_ty); box (n: single)
48914894
| SynConst.Double n when typeEquiv g g.float_ty kind -> record(g.float_ty); box (n: double)
48924895
| SynConst.Char n when typeEquiv g g.char_ty kind -> record(g.char_ty); box (n: char)
4893-
| SynConst.String (s, _, _)
4894-
| SynConst.SourceIdentifier (_, s, _) when typeEquiv g g.string_ty kind -> record(g.string_ty); box (s: string)
4896+
| SynConst.String (s, _, _) when typeEquiv g g.string_ty kind -> record(g.string_ty); box (s: string)
4897+
| SynConst.SourceIdentifier (i, s, m) when typeEquiv g g.string_ty kind ->
4898+
let s = applyLineDirectivesToSourceIdentifier i s m
4899+
record(g.string_ty); box (s: string)
48954900
| SynConst.Bool b when typeEquiv g g.bool_ty kind -> record(g.bool_ty); box (b: bool)
48964901
| _ -> fail()
48974902
v, tpenv

src/Compiler/Driver/ParseAndCheckInputs.fs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,7 @@ let PostParseModuleSpec (_i, defaultNamespace, isLastCompiland, fileName, intf)
215215

216216
SynModuleOrNamespaceSig(lid, isRecursive, kind, decls, xmlDoc, attributes, None, range, trivia)
217217

218-
let private finishPreprocessing lexbuf diagnosticOptions isScript submoduleRanges =
218+
let FinishPreprocessing lexbuf diagnosticOptions isScript submoduleRanges =
219219
WarnScopes.MergeInto diagnosticOptions isScript submoduleRanges lexbuf
220220
LineDirectives.add lexbuf.StartPos.FileIndex (LineDirectiveStore.GetLineDirectives lexbuf)
221221

@@ -276,7 +276,7 @@ let PostParseModuleImpls
276276

277277
let isScript = IsScript fileName
278278

279-
finishPreprocessing lexbuf diagnosticOptions isScript (getImplSubmoduleRanges impls)
279+
FinishPreprocessing lexbuf diagnosticOptions isScript (getImplSubmoduleRanges impls)
280280

281281
let trivia = collectParsedInputTrivia lexbuf
282282

@@ -310,7 +310,7 @@ let PostParseModuleSpecs
310310
identifiers: Set<string>
311311
) =
312312

313-
finishPreprocessing lexbuf diagnosticOptions false (getSpecSubmoduleRanges specs)
313+
FinishPreprocessing lexbuf diagnosticOptions false (getSpecSubmoduleRanges specs)
314314

315315
let trivia = collectParsedInputTrivia lexbuf
316316

src/Compiler/Driver/ParseAndCheckInputs.fsi

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,9 @@ val ParseInputFiles:
136136
retryLocked: bool ->
137137
(ParsedInput * string) list
138138

139+
/// Process collected directives
140+
val FinishPreprocessing: Lexbuf -> FSharpDiagnosticOptions -> bool -> range list -> unit
141+
139142
/// Get the initial type checking environment including the loading of mscorlib/System.Core, FSharp.Core
140143
/// applying the InternalsVisibleTo in referenced assemblies and opening 'Checked' if requested.
141144
val GetInitialTcEnv: assemblyName: string * range * TcConfig * TcImports * TcGlobals -> TcEnv * OpenDeclaration list

src/Compiler/Interactive/fsi.fs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3692,7 +3692,7 @@ type FsiInteractionProcessor
36923692

36933693
Parser.interaction lexerWhichSavesLastToken tokenizer.LexBuffer)
36943694

3695-
WarnScopes.MergeInto diagnosticOptions false [] tokenizer.LexBuffer
3695+
FinishPreprocessing tokenizer.LexBuffer diagnosticOptions true []
36963696

36973697
Some input
36983698
with e ->

src/Compiler/SyntaxTree/LexHelpers.fs

Lines changed: 4 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,11 @@ open FSharp.Compiler.ParseHelpers
1616
open FSharp.Compiler.Parser
1717
open FSharp.Compiler.Syntax
1818
open FSharp.Compiler.Syntax.PrettyNaming
19+
open FSharp.Compiler.SyntaxTreeOps
1920
open FSharp.Compiler.Text
2021
open FSharp.Compiler.Text.Range
2122
open FSharp.Compiler.UnicodeLexing
2223

23-
/// The "mock" file name used by fsi.exe when reading from stdin.
24-
/// Has special treatment by the lexer, i.e. __SOURCE_DIRECTORY__ becomes GetCurrentDirectory()
25-
let stdinMockFileName = "stdin"
26-
2724
/// Lexer args: status of #light processing. Mutated when a #light
2825
/// directive is processed. This alters the behaviour of the lexfilter.
2926
[<Sealed>]
@@ -472,27 +469,9 @@ module Keywords =
472469
v
473470
| _ ->
474471
match s with
475-
| "__SOURCE_DIRECTORY__" ->
476-
let fileName = FileIndex.fileOfFileIndex lexbuf.StartPos.FileIndex
477-
478-
let dirname =
479-
if String.IsNullOrWhiteSpace(fileName) then
480-
String.Empty
481-
else if fileName = stdinMockFileName then
482-
System.IO.Directory.GetCurrentDirectory()
483-
else
484-
fileName
485-
|> FileSystem.GetFullPathShim (* asserts that path is already absolute *)
486-
|> System.IO.Path.GetDirectoryName
487-
|> (!!)
488-
489-
if String.IsNullOrEmpty dirname then
490-
dirname
491-
else
492-
PathMap.applyDir args.pathMap dirname
493-
|> fun dir -> KEYWORD_STRING(s, dir)
494-
| "__SOURCE_FILE__" -> KEYWORD_STRING(s, !!System.IO.Path.GetFileName(FileIndex.fileOfFileIndex lexbuf.StartPos.FileIndex))
495-
| "__LINE__" -> KEYWORD_STRING(s, string lexbuf.StartPos.Line)
472+
| "__SOURCE_DIRECTORY__"
473+
| "__SOURCE_FILE__"
474+
| "__LINE__" -> KEYWORD_STRING(s, getSourceIdentifierValue args.pathMap s lexbuf.LexemeRange)
496475
| _ -> IdentifierToken args lexbuf s
497476

498477
/// Arbitrary value

src/Compiler/SyntaxTree/LexHelpers.fsi

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,6 @@ open FSharp.Compiler.UnicodeLexing
1313
open FSharp.Compiler.Parser
1414
open FSharp.Compiler.Text
1515

16-
val stdinMockFileName: string
17-
1816
/// Lexer args: status of #light processing. Mutated when a #light
1917
/// directive is processed. This alters the behaviour of the lexfilter.
2018
[<Sealed>]

src/Compiler/SyntaxTree/SyntaxTreeOps.fs

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,18 @@
22

33
module FSharp.Compiler.SyntaxTreeOps
44

5+
open Internal.Utilities
56
open Internal.Utilities.Library
67
open FSharp.Compiler.DiagnosticsLogger
78
open FSharp.Compiler.Features
9+
open FSharp.Compiler.IO
810
open FSharp.Compiler.Syntax
911
open FSharp.Compiler.SyntaxTrivia
1012
open FSharp.Compiler.Syntax.PrettyNaming
1113
open FSharp.Compiler.Text
1214
open FSharp.Compiler.Text.Range
1315
open FSharp.Compiler.Xml
16+
open System
1417

1518
/// Generate implicit argument names in parsing
1619
type SynArgNameGenerator() =
@@ -992,11 +995,47 @@ let rec synExprContainsError inpExpr =
992995
let longIdentToString (ident: SynLongIdent) =
993996
System.String.Join(".", ident.LongIdent |> List.map (fun ident -> ident.idText.ToString()))
994997

998+
/// The "mock" file name used by fsi.exe when reading from stdin.
999+
/// Has special treatment, i.e. __SOURCE_DIRECTORY__ becomes GetCurrentDirectory()
1000+
let stdinMockFileName = "stdin"
1001+
1002+
let getSourceIdentifierValue pathMap s (m: range) =
1003+
match s with
1004+
| "__SOURCE_DIRECTORY__" ->
1005+
let fileName = FileIndex.fileOfFileIndex m.FileIndex
1006+
1007+
let dirname =
1008+
if String.IsNullOrWhiteSpace fileName then
1009+
String.Empty
1010+
else if fileName = stdinMockFileName then
1011+
System.IO.Directory.GetCurrentDirectory()
1012+
else
1013+
fileName
1014+
|> FileSystem.GetFullPathShim (* asserts that path is already absolute *)
1015+
|> System.IO.Path.GetDirectoryName
1016+
|> (!!)
1017+
1018+
if String.IsNullOrEmpty dirname then
1019+
dirname
1020+
else
1021+
PathMap.applyDir pathMap dirname
1022+
| "__SOURCE_FILE__" -> !!System.IO.Path.GetFileName(FileIndex.fileOfFileIndex m.FileIndex)
1023+
| "__LINE__" -> string m.StartLine
1024+
| _ -> failwith "getSourceIdentifierValue: unexpected identifier"
1025+
1026+
let applyLineDirectivesToSourceIdentifier s value (m: range) =
1027+
let mm = m.ApplyLineDirectives()
1028+
1029+
if mm = m then
1030+
value // keep the value that was assigned during lexing (when line directives were not yet evaluated)
1031+
else
1032+
getSourceIdentifierValue PathMap.empty s mm // update because of line directive
1033+
9951034
let parsedHashDirectiveArguments (input: ParsedHashDirectiveArgument list) (langVersion: LanguageVersion) =
9961035
List.choose
9971036
(function
9981037
| ParsedHashDirectiveArgument.String(s, _, _) -> Some s
999-
| ParsedHashDirectiveArgument.SourceIdentifier(_, v, _) -> Some v
1038+
| ParsedHashDirectiveArgument.SourceIdentifier(s, v, m) -> Some(applyLineDirectivesToSourceIdentifier s v m)
10001039
| ParsedHashDirectiveArgument.Int32(n, m) ->
10011040
match tryCheckLanguageFeatureAndRecover langVersion LanguageFeature.ParsedHashDirectiveArgumentNonQuotes m with
10021041
| true -> Some(string n)
@@ -1015,7 +1054,7 @@ let parsedHashDirectiveArgumentsNoCheck (input: ParsedHashDirectiveArgument list
10151054
List.map
10161055
(function
10171056
| ParsedHashDirectiveArgument.String(s, _, _) -> s
1018-
| ParsedHashDirectiveArgument.SourceIdentifier(_, v, _) -> v
1057+
| ParsedHashDirectiveArgument.SourceIdentifier(s, v, m) -> applyLineDirectivesToSourceIdentifier s v m
10191058
| ParsedHashDirectiveArgument.Int32(n, _) -> string n
10201059
| ParsedHashDirectiveArgument.Ident(ident, _) -> ident.idText
10211060
| ParsedHashDirectiveArgument.LongIdent(ident, _) -> longIdentToString ident)
@@ -1028,7 +1067,7 @@ let parsedHashDirectiveStringArguments (input: ParsedHashDirectiveArgument list)
10281067
| ParsedHashDirectiveArgument.Int32(n, m) ->
10291068
errorR (Error(FSComp.SR.featureParsedHashDirectiveUnexpectedInteger (n), m))
10301069
None
1031-
| ParsedHashDirectiveArgument.SourceIdentifier(_, v, _) -> Some v
1070+
| ParsedHashDirectiveArgument.SourceIdentifier(s, v, m) -> Some(applyLineDirectivesToSourceIdentifier s v m)
10321071
| ParsedHashDirectiveArgument.Ident(ident, m) ->
10331072
errorR (Error(FSComp.SR.featureParsedHashDirectiveUnexpectedIdentifier (ident.idText), m))
10341073
None

src/Compiler/SyntaxTree/SyntaxTreeOps.fsi

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ open FSharp.Compiler.Text
77
open FSharp.Compiler.Xml
88
open FSharp.Compiler.Syntax
99
open FSharp.Compiler.SyntaxTrivia
10+
open Internal.Utilities
1011

1112
[<Class>]
1213
type SynArgNameGenerator =
@@ -322,6 +323,12 @@ val unionBindingAndMembers: bindings: SynBinding list -> members: SynMemberDefn
322323

323324
val synExprContainsError: inpExpr: SynExpr -> bool
324325

326+
val stdinMockFileName: string
327+
328+
val getSourceIdentifierValue: PathMap -> string -> range -> string
329+
330+
val applyLineDirectivesToSourceIdentifier: string -> string -> range -> string
331+
325332
val parsedHashDirectiveArguments: ParsedHashDirectiveArgument list -> LanguageVersion -> string list
326333

327334
val parsedHashDirectiveArgumentsNoCheck: ParsedHashDirectiveArgument list -> string list

tests/FSharp.Compiler.ComponentTests/CompilerDirectives/Line.fs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -350,3 +350,26 @@ namespace CSharpLib
350350
|> compileExeAndRun
351351
|> shouldSucceed
352352

353+
let sourceIdSource = """
354+
#line 100 "/temp/target.fs"
355+
let dir = __SOURCE_DIRECTORY__
356+
let d = dir[dir.Length - 4 ..]
357+
printf $"{__LINE__} in {__SOURCE_FILE__} in {d}"
358+
"""
359+
360+
[<Fact>]
361+
let ``LineDirectivesAreAppliedToSourceIdentifiers`` () =
362+
let result =
363+
sourceIdSource
364+
|> FSharp
365+
|> withFileName "original.fs"
366+
|> compileExeAndRun
367+
|> shouldSucceed
368+
let expected = "102 in target.fs in temp"
369+
match result.RunOutput with
370+
| Some (ExecutionOutput r) ->
371+
Assert.Equal(expected, r.StdOut)
372+
| _ ->
373+
Assert.Fail "unexpected: no execution output"
374+
375+

0 commit comments

Comments
 (0)