Skip to content
Merged
4 changes: 4 additions & 0 deletions src/Compiler/Driver/CompilerConfig.fs
Original file line number Diff line number Diff line change
Expand Up @@ -565,6 +565,8 @@ type TcConfigBuilder =

mutable noConditionalErasure: bool

mutable applyLineDirectives: bool

mutable pathMap: PathMap

mutable langVersion: LanguageVersion
Expand Down Expand Up @@ -739,6 +741,7 @@ type TcConfigBuilder =
internalTestSpanStackReferring = false
noConditionalErasure = false
pathMap = PathMap.empty
applyLineDirectives = true
langVersion = LanguageVersion.Default
implicitIncludeDir = implicitIncludeDir
defaultFSharpBinariesDir = defaultFSharpBinariesDir
Expand Down Expand Up @@ -1326,6 +1329,7 @@ type TcConfig private (data: TcConfigBuilder, validate: bool) =
member _.tryGetMetadataSnapshot = data.tryGetMetadataSnapshot
member _.internalTestSpanStackReferring = data.internalTestSpanStackReferring
member _.noConditionalErasure = data.noConditionalErasure
member _.applyLineDirectives = data.applyLineDirectives
member _.xmlDocInfoLoader = data.xmlDocInfoLoader

static member Create(builder, validate) =
Expand Down
6 changes: 6 additions & 0 deletions src/Compiler/Driver/CompilerConfig.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -466,6 +466,9 @@ type TcConfigBuilder =
/// Prevent erasure of conditional attributes and methods so tooling is able analyse them.
mutable noConditionalErasure: bool

/// Take '#line' into account? Defaults to true
mutable applyLineDirectives: bool

mutable pathMap: PathMap

mutable langVersion: LanguageVersion
Expand Down Expand Up @@ -800,6 +803,9 @@ type TcConfig =
/// Prevent erasure of conditional attributes and methods so tooling is able analyse them.
member noConditionalErasure: bool

/// Take '#line' into account? Defaults to true
member applyLineDirectives: bool

/// if true - 'let mutable x = Span.Empty', the value 'x' is a stack referring span. Used for internal testing purposes only until we get true stack spans.
member internalTestSpanStackReferring: bool

Expand Down
1 change: 1 addition & 0 deletions src/Compiler/Driver/CompilerOptions.fs
Original file line number Diff line number Diff line change
Expand Up @@ -1397,6 +1397,7 @@ let editorSpecificFlags (tcConfigB: TcConfigBuilder) =
CompilerOption("exename", tagNone, OptionString(fun s -> tcConfigB.exename <- Some s), None, None)
CompilerOption("maxerrors", tagInt, OptionInt(fun n -> tcConfigB.maxErrors <- n), None, None)
CompilerOption("noconditionalerasure", tagNone, OptionUnit(fun () -> tcConfigB.noConditionalErasure <- true), None, None)
CompilerOption("ignorelinedirectives", tagNone, OptionUnit(fun () -> tcConfigB.applyLineDirectives <- false), None, None)
]

let internalFlags (tcConfigB: TcConfigBuilder) =
Expand Down
10 changes: 9 additions & 1 deletion src/Compiler/Driver/ParseAndCheckInputs.fs
Original file line number Diff line number Diff line change
Expand Up @@ -563,7 +563,15 @@ let ParseOneInputLexbuf (tcConfig: TcConfig, lexResourceManager, lexbuf, fileNam

// Set up the initial lexer arguments
let lexargs =
mkLexargs (tcConfig.conditionalDefines, indentationSyntaxStatus, lexResourceManager, [], diagnosticsLogger, tcConfig.pathMap)
mkLexargs (
tcConfig.conditionalDefines,
indentationSyntaxStatus,
lexResourceManager,
[],
diagnosticsLogger,
tcConfig.pathMap,
tcConfig.applyLineDirectives
)

// Set up the initial lexer arguments
let shortFilename = SanitizeFileName fileName tcConfig.implicitIncludeDir
Expand Down
4 changes: 2 additions & 2 deletions src/Compiler/Interactive/fsi.fs
Original file line number Diff line number Diff line change
Expand Up @@ -2524,8 +2524,8 @@ type FsiStdinLexerProvider

resetLexbufPos sourceFileName lexbuf
let skip = true // don't report whitespace from lexer
let defines = tcConfigB.conditionalDefines
let lexargs = mkLexargs (defines, indentationSyntaxStatus, lexResourceManager, [], diagnosticsLogger, PathMap.empty)
let applyLineDirectives = true
let lexargs = mkLexargs (tcConfigB.conditionalDefines, indentationSyntaxStatus, lexResourceManager, [], diagnosticsLogger, PathMap.empty, applyLineDirectives)
let tokenizer = LexFilter.LexFilter(indentationSyntaxStatus, tcConfigB.compilingFSharpCore, Lexer.token lexargs skip, lexbuf)
tokenizer

Expand Down
19 changes: 13 additions & 6 deletions src/Compiler/Service/FSharpCheckerResults.fs
Original file line number Diff line number Diff line change
Expand Up @@ -2035,6 +2035,7 @@ type internal TypeCheckInfo
type FSharpParsingOptions =
{
SourceFiles: string[]
ApplyLineDirectives: bool
ConditionalDefines: string list
DiagnosticOptions: FSharpDiagnosticOptions
LangVersionText: string
Expand All @@ -2051,6 +2052,7 @@ type FSharpParsingOptions =
static member Default =
{
SourceFiles = Array.empty
ApplyLineDirectives = false
ConditionalDefines = []
DiagnosticOptions = FSharpDiagnosticOptions.Default
LangVersionText = LanguageVersion.Default.VersionText
Expand All @@ -2063,6 +2065,7 @@ type FSharpParsingOptions =
static member FromTcConfig(tcConfig: TcConfig, sourceFiles, isInteractive: bool) =
{
SourceFiles = sourceFiles
ApplyLineDirectives = tcConfig.applyLineDirectives
ConditionalDefines = tcConfig.conditionalDefines
DiagnosticOptions = tcConfig.diagnosticsOptions
LangVersionText = tcConfig.langVersion.VersionText
Expand All @@ -2075,6 +2078,7 @@ type FSharpParsingOptions =
static member FromTcConfigBuilder(tcConfigB: TcConfigBuilder, sourceFiles, isInteractive: bool) =
{
SourceFiles = sourceFiles
ApplyLineDirectives = tcConfigB.applyLineDirectives
ConditionalDefines = tcConfigB.conditionalDefines
DiagnosticOptions = tcConfigB.diagnosticsOptions
LangVersionText = tcConfigB.langVersion.VersionText
Expand Down Expand Up @@ -2183,12 +2187,15 @@ module internal ParseAndCheckFile =
// When analyzing files using ParseOneFile, i.e. for the use of editing clients, we do not apply line directives.
// TODO(pathmap): expose PathMap on the service API, and thread it through here
let lexargs =
mkLexargs (conditionalDefines, indentationSyntaxStatus, lexResourceManager, [], errHandler.DiagnosticsLogger, PathMap.empty)

let lexargs =
{ lexargs with
applyLineDirectives = false
}
mkLexargs (
conditionalDefines,
indentationSyntaxStatus,
lexResourceManager,
[],
errHandler.DiagnosticsLogger,
PathMap.empty,
options.ApplyLineDirectives
)

let tokenizer =
LexFilter.LexFilter(indentationSyntaxStatus, options.CompilingFSharpCore, Lexer.token lexargs true, lexbuf)
Expand Down
32 changes: 24 additions & 8 deletions src/Compiler/Service/FSharpCheckerResults.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -200,14 +200,30 @@ type public FSharpProjectContext =

/// Options used to determine active --define conditionals and other options relevant to parsing files in a project
type public FSharpParsingOptions =
{ SourceFiles: string[]
ConditionalDefines: string list
DiagnosticOptions: FSharpDiagnosticOptions
LangVersionText: string
IsInteractive: bool
IndentationAwareSyntax: bool option
CompilingFSharpCore: bool
IsExe: bool }
{
SourceFiles: string[]

/// Indicates if the ranges returned by parsing should have '#line' directives applied to them.
/// When compiling code, this should usually be 'true'. For editing tools, this is usually 'false.
/// The default for FSharpParsingOptions.ApplyLineDirectives is 'false'. The default for
/// FSharpParsingOptions arising from FSharpProjectOptions will be 'true' unless '--ignorelinedirectives' is used in the
/// parameters from which these are derived.
ApplyLineDirectives: bool

ConditionalDefines: string list

DiagnosticOptions: FSharpDiagnosticOptions

LangVersionText: string

IsInteractive: bool

IndentationAwareSyntax: bool option

CompilingFSharpCore: bool

IsExe: bool
}

static member Default: FSharpParsingOptions

Expand Down
24 changes: 16 additions & 8 deletions src/Compiler/Service/ServiceLexing.fs
Original file line number Diff line number Diff line change
Expand Up @@ -1183,14 +1183,18 @@ type FSharpSourceTokenizer(conditionalDefines: string list, fileName: string opt

let lexResourceManager = LexResourceManager()

let applyLineDirectives = false
let indentationSyntaxStatus = IndentationAwareSyntaxStatus(true, false)

let lexargs =
mkLexargs (
conditionalDefines,
IndentationAwareSyntaxStatus(true, false),
indentationSyntaxStatus,
lexResourceManager,
[],
DiscardErrorsLogger,
PathMap.empty
PathMap.empty,
applyLineDirectives
)

member _.CreateLineTokenizer(lineText: string) =
Expand Down Expand Up @@ -1813,14 +1817,18 @@ module FSharpLexerImpl =
UnicodeLexing.SourceTextAsLexbuf(reportLibraryOnlyFeatures, langVersion, text)

let indentationSyntaxStatus = IndentationAwareSyntaxStatus(isLightSyntaxOn, true)
let applyLineDirectives = isCompiling

let lexargs =
mkLexargs (conditionalDefines, indentationSyntaxStatus, LexResourceManager(0), [], diagnosticsLogger, pathMap)

let lexargs =
{ lexargs with
applyLineDirectives = isCompiling
}
mkLexargs (
conditionalDefines,
indentationSyntaxStatus,
LexResourceManager(0),
[],
diagnosticsLogger,
pathMap,
applyLineDirectives
)

let getNextToken =
let lexer = Lexer.token lexargs canSkipTrivia
Expand Down
13 changes: 11 additions & 2 deletions src/Compiler/SyntaxTree/LexHelpers.fs
Original file line number Diff line number Diff line change
Expand Up @@ -74,14 +74,23 @@ type LongUnicodeLexResult =
| SingleChar of uint16
| Invalid

let mkLexargs (conditionalDefines, indentationSyntaxStatus, resourceManager, ifdefStack, diagnosticsLogger, pathMap: PathMap) =
let mkLexargs
(
conditionalDefines,
indentationSyntaxStatus,
resourceManager,
ifdefStack,
diagnosticsLogger,
pathMap: PathMap,
applyLineDirectives
) =
{
conditionalDefines = conditionalDefines
ifdefStack = ifdefStack
indentationSyntaxStatus = indentationSyntaxStatus
resourceManager = resourceManager
diagnosticsLogger = diagnosticsLogger
applyLineDirectives = true
applyLineDirectives = applyLineDirectives
stringNest = []
pathMap = pathMap
}
Expand Down
3 changes: 2 additions & 1 deletion src/Compiler/SyntaxTree/LexHelpers.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,8 @@ val mkLexargs:
resourceManager: LexResourceManager *
ifdefStack: LexerIfdefStack *
diagnosticsLogger: DiagnosticsLogger *
pathMap: PathMap ->
pathMap: PathMap *
applyLineDirectives: bool ->
LexArgs

val reusingLexbufForParsing: Lexbuf -> (unit -> 'a) -> 'a
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2108,7 +2108,9 @@ FSharp.Compiler.CodeAnalysis.FSharpParsingOptions: System.String ToString()
FSharp.Compiler.CodeAnalysis.FSharpParsingOptions: System.String get_LangVersionText()
FSharp.Compiler.CodeAnalysis.FSharpParsingOptions: System.String[] SourceFiles
FSharp.Compiler.CodeAnalysis.FSharpParsingOptions: System.String[] get_SourceFiles()
FSharp.Compiler.CodeAnalysis.FSharpParsingOptions: Void .ctor(System.String[], Microsoft.FSharp.Collections.FSharpList`1[System.String], FSharp.Compiler.Diagnostics.FSharpDiagnosticOptions, System.String, Boolean, Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Boolean, Boolean)
FSharp.Compiler.CodeAnalysis.FSharpParsingOptions: Boolean ApplyLineDirectives
FSharp.Compiler.CodeAnalysis.FSharpParsingOptions: Boolean get_ApplyLineDirectives()
FSharp.Compiler.CodeAnalysis.FSharpParsingOptions: Void .ctor(System.String[], Boolean, Microsoft.FSharp.Collections.FSharpList`1[System.String], FSharp.Compiler.Diagnostics.FSharpDiagnosticOptions, System.String, Boolean, Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Boolean, Boolean)
FSharp.Compiler.CodeAnalysis.FSharpProjectContext
FSharp.Compiler.CodeAnalysis.FSharpProjectContext: FSharp.Compiler.CodeAnalysis.FSharpProjectOptions ProjectOptions
FSharp.Compiler.CodeAnalysis.FSharpProjectContext: FSharp.Compiler.CodeAnalysis.FSharpProjectOptions get_ProjectOptions()
Expand Down
7 changes: 4 additions & 3 deletions tests/FSharp.Compiler.UnitTests/HashIfExpression.fs
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,12 @@ type public HashIfExpression() =
member _.ErrorCount = errors.Count
}

let lightSyntax = IndentationAwareSyntaxStatus(true, false)
let indentationSyntaxStatus = IndentationAwareSyntaxStatus(true, false)
let resourceManager = LexResourceManager ()
let defines= []
let defines = []
let applyLineDirectives = true
let startPos = Position.Empty
let args = mkLexargs (defines, lightSyntax, resourceManager, [], diagnosticsLogger, PathMap.empty)
let args = mkLexargs (defines, indentationSyntaxStatus, resourceManager, [], diagnosticsLogger, PathMap.empty, applyLineDirectives)

DiagnosticsThreadStatics.DiagnosticsLogger <- diagnosticsLogger

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ type CompilerService() =
DiagnosticOptions = FSharpDiagnosticOptions.Default
LangVersionText = "default"
IsInteractive = false
ApplyLineDirectives = false
IndentationAwareSyntax = None
CompilingFSharpCore = false
IsExe = false
Expand Down
34 changes: 28 additions & 6 deletions tests/service/ProjectAnalysisTests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -5333,7 +5333,7 @@ let x = (1 = 3.0)
let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args)

[<Test>]
let ``Test line directives in foreground analysis`` () = // see https://github.com/dotnet/fsharp/issues/3317
let ``Test diagnostics with line directives active`` () =

// In background analysis and normal compiler checking, the errors are reported w.r.t. the line directives
let wholeProjectResults = checker.ParseAndCheckProject(ProjectLineDirectives.options) |> Async.RunImmediate
Expand All @@ -5342,17 +5342,39 @@ let ``Test line directives in foreground analysis`` () = // see https://github.c

[ for e in wholeProjectResults.Diagnostics -> e.Range.StartLine, e.Range.EndLine, e.Range.FileName ] |> shouldEqual [(10, 10, "Test.fsy")]

// In foreground analysis routines, used by visual editing tools, the errors are reported w.r.t. the source
// file, which is assumed to be in the editor, not the other files referred to by line directives.
let checkResults1 =
let checkResults =
checker.ParseAndCheckFileInProject(ProjectLineDirectives.fileName1, 0, ProjectLineDirectives.fileSource1, ProjectLineDirectives.options)
|> Async.RunImmediate
|> function _,FSharpCheckFileAnswer.Succeeded x -> x | _ -> failwith "unexpected aborted"

for e in checkResults1.Diagnostics do
for e in checkResults.Diagnostics do
printfn "ProjectLineDirectives checkResults1 error file: <<<%s>>>" e.Range.FileName

[ for e in checkResults1.Diagnostics -> e.Range.StartLine, e.Range.EndLine, e.Range.FileName ] |> shouldEqual [(5, 5, ProjectLineDirectives.fileName1)]
// No diagnostics for logical location "Test.fsy" are reported when checking the source since the filenames don't match. You need to pass --ignorelinedirectives to get them
[ for e in checkResults.Diagnostics -> e.Range.StartLine, e.Range.EndLine, e.Range.FileName ] |> shouldEqual [ ]

[<Test>]
let ``Test diagnostics with line directives ignored`` () =

// If you pass hidden IDE flag --ignorelinedirectives, the diagnostics are reported w.r.t. the source
// file, not the files referred to by line directives.
let options = { ProjectLineDirectives.options with OtherOptions = (Array.append ProjectLineDirectives.options.OtherOptions [| "--ignorelinedirectives" |]) }

let wholeProjectResults = checker.ParseAndCheckProject(options) |> Async.RunImmediate
for e in wholeProjectResults.Diagnostics do
printfn "ProjectLineDirectives wholeProjectResults error file: <<<%s>>>" e.Range.FileName

[ for e in wholeProjectResults.Diagnostics -> e.Range.StartLine, e.Range.EndLine, e.Range.FileName ] |> shouldEqual [(5, 5, ProjectLineDirectives.fileName1)]

let checkResults =
checker.ParseAndCheckFileInProject(ProjectLineDirectives.fileName1, 0, ProjectLineDirectives.fileSource1, options)
|> Async.RunImmediate
|> function _,FSharpCheckFileAnswer.Succeeded x -> x | _ -> failwith "unexpected aborted"

for e in checkResults.Diagnostics do
printfn "ProjectLineDirectives checkResults error file: <<<%s>>>" e.Range.FileName

[ for e in checkResults.Diagnostics -> e.Range.StartLine, e.Range.EndLine, e.Range.FileName ] |> shouldEqual [(5, 5, ProjectLineDirectives.fileName1)]

//------------------------------------------------------

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,11 @@ type private FSharpProjectOptionsReactor (checker: FSharpChecker) =
for x in project.ProjectReferences do
"-r:" + project.Solution.GetProject(x.ProjectId).OutputFilePath

// In the IDE we always ignore all #line directives for all purposes. This means
// IDE features work correctly within generated source files, but diagnostics are
// reported in the IDE with respect to the generated source, and will not unify with
// diagnostics from the build.
"--ignorelinedirectives"
|]

let! ver = project.GetDependentVersionAsync(ct) |> Async.AwaitTask
Expand Down Expand Up @@ -509,6 +514,7 @@ type internal FSharpProjectOptionsManager
| Some (_, parsingOptions, _) -> parsingOptions
| _ ->
{ FSharpParsingOptions.Default with
ApplyLineDirectives = false
IsInteractive = CompilerEnvironment.IsScriptFile document.Name
}
CompilerEnvironment.GetConditionalDefinesForEditing parsingOptions
Expand Down