Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add IsInteractive to parsing options for script load closures #4169

Merged
merged 2 commits into from
Jan 16, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/fsharp/service/ServiceUntypedParse.fs
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ module SourceFileImpl =
0 = String.Compare(".fsi",ext,StringComparison.OrdinalIgnoreCase)

/// Additional #defines that should be in place when editing a file in a file editor such as VS.
let AdditionalDefinesForUseInEditor(filename) =
if CompileOps.IsScript(filename) then ["INTERACTIVE";"EDITING"] // This is still used by the foreground parse
let AdditionalDefinesForUseInEditor(isInteractive: bool) =
if isInteractive then ["INTERACTIVE";"EDITING"] // This is still used by the foreground parse
else ["COMPILED";"EDITING"]

type CompletionPath = string list * string option // plid * residue
Expand Down
2 changes: 1 addition & 1 deletion src/fsharp/service/ServiceUntypedParse.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -112,5 +112,5 @@ module public UntypedParseImpl =
// implementation details used by other code in the compiler
module internal SourceFileImpl =
val IsInterfaceFile : string -> bool
val AdditionalDefinesForUseInEditor : string -> string list
val AdditionalDefinesForUseInEditor: isInteractive: bool -> string list

46 changes: 23 additions & 23 deletions src/fsharp/service/service.fs
Original file line number Diff line number Diff line change
Expand Up @@ -1394,14 +1394,13 @@ type TypeCheckInfo
override __.ToString() = "TypeCheckInfo(" + mainInputFileName + ")"

type FSharpParsingOptions =
{
SourceFiles: string []
{ SourceFiles: string []
ConditionalCompilationDefines: string list
ErrorSeverityOptions: FSharpErrorSeverityOptions
IsInteractive: bool
LightSyntax: bool option
CompilingFsLib: bool
IsExe: bool
}
IsExe: bool }

member x.LastFileName =
Debug.Assert(not (Array.isEmpty x.SourceFiles), "Parsing options don't contain any file")
Expand All @@ -1411,26 +1410,26 @@ type FSharpParsingOptions =
{ SourceFiles = Array.empty
ConditionalCompilationDefines = []
ErrorSeverityOptions = FSharpErrorSeverityOptions.Default
IsInteractive = false
LightSyntax = None
CompilingFsLib = false
IsExe = false
}
IsExe = false }

static member FromTcConfig(tcConfig: TcConfig, sourceFiles) =
{
SourceFiles = sourceFiles
static member FromTcConfig(tcConfig: TcConfig, sourceFiles, isInteractive: bool) =
{ SourceFiles = sourceFiles
ConditionalCompilationDefines = tcConfig.conditionalCompilationDefines
ErrorSeverityOptions = tcConfig.errorSeverityOptions
IsInteractive = isInteractive
LightSyntax = tcConfig.light
CompilingFsLib = tcConfig.compilingFslib
IsExe = tcConfig.target.IsExe
}
IsExe = tcConfig.target.IsExe }

static member FromTcConfigBuidler(tcConfigB: TcConfigBuilder, sourceFiles) =
static member FromTcConfigBuidler(tcConfigB: TcConfigBuilder, sourceFiles, isInteractive: bool) =
{
SourceFiles = sourceFiles
ConditionalCompilationDefines = tcConfigB.conditionalCompilationDefines
ErrorSeverityOptions = tcConfigB.errorSeverityOptions
IsInteractive = isInteractive
LightSyntax = tcConfigB.light
CompilingFsLib = tcConfigB.compilingFslib
IsExe = tcConfigB.target.IsExe
Expand Down Expand Up @@ -1502,7 +1501,7 @@ module internal Parser =

// If we're editing a script then we define INTERACTIVE otherwise COMPILED.
// Since this parsing for intellisense we always define EDITING.
let defines = SourceFileImpl.AdditionalDefinesForUseInEditor(fileName) @ options.ConditionalCompilationDefines
let defines = (SourceFileImpl.AdditionalDefinesForUseInEditor options.IsInteractive) @ options.ConditionalCompilationDefines

// Note: we don't really attempt to intern strings across a large scope.
let lexResourceManager = new Lexhelp.LexResourceManager()
Expand Down Expand Up @@ -2514,7 +2513,7 @@ type BackgroundCompiler(legacyReferenceResolver, projectCacheSize, keepAssemblyC
let! tcErrors, tcFileResult =
Parser.CheckOneFile(parseResults, source, fileName, options.ProjectFileName, tcPrior.TcConfig, tcPrior.TcGlobals, tcPrior.TcImports,
tcPrior.TcState, loadClosure, tcPrior.Errors, reactorOps, (fun () -> builder.IsAlive), textSnapshotInfo, userOpName)
let parsingOptions = FSharpParsingOptions.FromTcConfig(tcPrior.TcConfig, Array.ofList builder.SourceFiles)
let parsingOptions = FSharpParsingOptions.FromTcConfig(tcPrior.TcConfig, Array.ofList builder.SourceFiles, options.UseScriptResolutionRules)
let checkAnswer = MakeCheckFileAnswer(fileName, tcFileResult, options, builder, Array.ofList tcPrior.TcDependencyFiles, creationErrors, parseResults.Errors, tcErrors)
bc.RecordTypeCheckFileInProjectResults(fileName, options, parsingOptions, parseResults, fileVersion, tcPrior.TimeStamp, Some checkAnswer, source)
return checkAnswer
Expand Down Expand Up @@ -2628,7 +2627,7 @@ type BackgroundCompiler(legacyReferenceResolver, projectCacheSize, keepAssemblyC
let! tcPrior = execWithReactorAsync <| fun ctok -> builder.GetCheckResultsBeforeFileInProject (ctok, filename)

// Do the parsing.
let parsingOptions = FSharpParsingOptions.FromTcConfig(builder.TcConfig, Array.ofList (builder.SourceFiles))
let parsingOptions = FSharpParsingOptions.FromTcConfig(builder.TcConfig, Array.ofList (builder.SourceFiles), options.UseScriptResolutionRules)
let parseErrors, parseTreeOpt, anyErrors = Parser.parseFile (source, filename, parsingOptions, userOpName)
let parseTreeOpt = parseTreeOpt |> Option.map builder.DeduplicateParsedInputModuleNameInProject
let parseResults = FSharpParseFileResults(parseErrors, parseTreeOpt, anyErrors, builder.AllDependenciesDeprecated)
Expand Down Expand Up @@ -2908,7 +2907,7 @@ type FSharpChecker(legacyReferenceResolver, projectCacheSize, keepAssemblyConten
member ic.GetParsingOptionsFromProjectOptions(options): FSharpParsingOptions * _ =
let sourceFiles = List.ofArray options.SourceFiles
let argv = List.ofArray options.OtherOptions
ic.GetParsingOptionsFromCommandLineArgs(sourceFiles, argv)
ic.GetParsingOptionsFromCommandLineArgs(sourceFiles, argv, options.UseScriptResolutionRules)

member ic.MatchBraces(filename, source, options: FSharpProjectOptions, ?userOpName: string) =
let userOpName = defaultArg userOpName "Unknown"
Expand Down Expand Up @@ -3111,16 +3110,17 @@ type FSharpChecker(legacyReferenceResolver, projectCacheSize, keepAssemblyConten
ExtraProjectInfo=extraProjectInfo
Stamp = None }

member ic.GetParsingOptionsFromCommandLineArgs(initialSourceFiles, argv) =
member ic.GetParsingOptionsFromCommandLineArgs(initialSourceFiles, argv, ?isInteractive) =
let isInteractive = defaultArg isInteractive false
use errorScope = new ErrorScope()
let tcConfigBuilder = TcConfigBuilder.Initial

// Apply command-line arguments and collect more source files if they are in the arguments
let sourceFilesNew = ApplyCommandLineArgs(tcConfigBuilder, initialSourceFiles, argv)
FSharpParsingOptions.FromTcConfigBuidler(tcConfigBuilder, Array.ofList sourceFilesNew), errorScope.Diagnostics
FSharpParsingOptions.FromTcConfigBuidler(tcConfigBuilder, Array.ofList sourceFilesNew, isInteractive), errorScope.Diagnostics

member ic.GetParsingOptionsFromCommandLineArgs(argv) =
ic.GetParsingOptionsFromCommandLineArgs([], argv)
member ic.GetParsingOptionsFromCommandLineArgs(argv, ?isInteractive: bool) =
ic.GetParsingOptionsFromCommandLineArgs([], argv, ?isInteractive=isInteractive)

/// Begin background parsing the given project.
member ic.StartBackgroundCompile(options, ?userOpName) =
Expand Down Expand Up @@ -3191,7 +3191,7 @@ type FsiInteractiveChecker(legacyReferenceResolver, reactorOps: IReactorOperatio
let userOpName = defaultArg userOpName "Unknown"
let filename = Path.Combine(tcConfig.implicitIncludeDir, "stdin.fsx")
// Note: projectSourceFiles is only used to compute isLastCompiland, and is ignored if Build.IsScript(mainInputFileName) is true (which it is in this case).
let parsingOptions = FSharpParsingOptions.FromTcConfig(tcConfig, [| filename |])
let parsingOptions = FSharpParsingOptions.FromTcConfig(tcConfig, [| filename |], true)
let parseErrors, parseTreeOpt, anyErrors = Parser.parseFile (source, filename, parsingOptions, userOpName)
let dependencyFiles = [| |] // interactions have no dependencies
let parseResults = FSharpParseFileResults(parseErrors, parseTreeOpt, parseHadErrors = anyErrors, dependencyFiles = dependencyFiles)
Expand Down Expand Up @@ -3234,8 +3234,8 @@ module CompilerEnvironment =
let DefaultReferencesForOrphanSources(assumeDotNetFramework) = DefaultReferencesForScriptsAndOutOfProjectSources(assumeDotNetFramework)

/// Publish compiler-flags parsing logic. Must be fast because its used by the colorizer.
let GetCompilationDefinesForEditing(filename:string, parsingOptions: FSharpParsingOptions) =
SourceFileImpl.AdditionalDefinesForUseInEditor(filename) @
let GetCompilationDefinesForEditing (parsingOptions: FSharpParsingOptions) =
SourceFileImpl.AdditionalDefinesForUseInEditor(parsingOptions.IsInteractive) @
parsingOptions.ConditionalCompilationDefines

/// Return true if this is a subcategory of error or warning message that the language service can emit
Expand Down
11 changes: 6 additions & 5 deletions src/fsharp/service/service.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,7 @@ type public FSharpParsingOptions =
SourceFiles: string[]
ConditionalCompilationDefines: string list
ErrorSeverityOptions: FSharpErrorSeverityOptions
IsInteractive: bool
LightSyntax: bool option
CompilingFsLib: bool
IsExe: bool
Expand Down Expand Up @@ -533,14 +534,14 @@ type public FSharpChecker =
///
/// <param name="sourceFiles">Initial source files list. Additional files may be added during argv evaluation.</param>
/// <param name="argv">The command line arguments for the project build.</param>
member GetParsingOptionsFromCommandLineArgs: sourceFiles: string list * argv: string list -> FSharpParsingOptions * FSharpErrorInfo list
member GetParsingOptionsFromCommandLineArgs: sourceFiles: string list * argv: string list * ?isInteractive: bool -> FSharpParsingOptions * FSharpErrorInfo list

/// <summary>
/// <para>Get the FSharpParsingOptions implied by a set of command line arguments.</para>
/// </summary>
///
/// <param name="argv">The command line arguments for the project build.</param>
member GetParsingOptionsFromCommandLineArgs: argv: string list -> FSharpParsingOptions * FSharpErrorInfo list
member GetParsingOptionsFromCommandLineArgs: argv: string list * ?isInteractive: bool -> FSharpParsingOptions * FSharpErrorInfo list

/// <summary>
/// <para>Get the FSharpParsingOptions implied by a FSharpProjectOptions.</para>
Expand Down Expand Up @@ -727,11 +728,11 @@ type public CompilerEnvironment =
module public CompilerEnvironment =
/// These are the names of assemblies that should be referenced for .fs or .fsi files that
/// are not associated with a project.
val DefaultReferencesForOrphanSources : assumeDotNetFramework: bool -> string list
val DefaultReferencesForOrphanSources: assumeDotNetFramework: bool -> string list
/// Return the compilation defines that should be used when editing the given file.
val GetCompilationDefinesForEditing : filename : string * parsingOptions : FSharpParsingOptions -> string list
val GetCompilationDefinesForEditing: parsingOptions: FSharpParsingOptions -> string list
/// Return true if this is a subcategory of error or warning message that the language service can emit
val IsCheckerSupportedSubcategory : string -> bool
val IsCheckerSupportedSubcategory: string -> bool

/// Information about the debugging environment
module public DebuggerEnvironment =
Expand Down
43 changes: 42 additions & 1 deletion tests/service/ProjectOptionsTests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ open System
open System.IO
open NUnit.Framework
open FsUnit
open Microsoft.FSharp.Compiler.Ast
open Microsoft.FSharp.Compiler.SourceCodeServices

open FSharp.Compiler.Service.Tests.Common
Expand Down Expand Up @@ -518,6 +519,46 @@ let ``Test SourceFiles order for GetProjectOptionsFromScript`` () = // See #594
test "Main4" [|"BaseLib2"; "Lib5"; "BaseLib1"; "Lib1"; "Lib2"; "Main4"|]
test "MainBad" [|"MainBad"|]

[<Test>]
let ``Script load closure project`` () =
let fileName1 = Path.GetTempPath() + Path.DirectorySeparatorChar.ToString() + "Impl.fs"
let fileName2 = Path.ChangeExtension(Path.GetTempFileName(), ".fsx")

let fileSource1 = """
module ImplFile


#if INTERACTIVE
let x = 42
#endif
"""

let fileSource2 = """
#load "Impl.fs"
ImplFile.x
"""

File.WriteAllText(fileName1, fileSource1)
File.WriteAllText(fileName2, fileSource2)

let projectOptions, diagnostics =
checker.GetProjectOptionsFromScript(fileName2, fileSource2) |> Async.RunSynchronously
diagnostics.IsEmpty |> shouldEqual true

let _, checkResults =
checker.ParseAndCheckFileInProject(fileName2, 0, fileSource2, projectOptions) |> Async.RunSynchronously

match checkResults with
| FSharpCheckFileAnswer.Succeeded results ->
results.Errors |> shouldEqual [| |]
| _ -> failwith "type check was aborted"

let parsingOptions, diagnostics = checker.GetParsingOptionsFromProjectOptions(projectOptions)
diagnostics.IsEmpty |> shouldEqual true

let parseResults = checker.ParseFile(fileName1, fileSource1, parsingOptions) |> Async.RunSynchronously
parseResults.ParseTree.IsSome |> shouldEqual true
match parseResults.ParseTree.Value with
| ParsedInput.ImplFile (ParsedImplFileInput (_, _, _, _, _, modules, _)) ->
let (SynModuleOrNamespace (_, _, _, decls, _, _, _, _)) = modules.Head
decls.Length |> shouldEqual 1
| _ -> failwith "got sig file"
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ type internal FSharpAddOpenCodeFixProvider
let! _, parsedInput, checkResults = checker.ParseAndCheckDocument(document, projectOptions, allowStaleResults = true, sourceText = sourceText, userOpName = userOpName)
let line = sourceText.Lines.GetLineFromPosition(context.Span.End)
let linePos = sourceText.Lines.GetLinePosition(context.Span.End)
let defines = CompilerEnvironment.GetCompilationDefinesForEditing(document.Name, parsingOptions)
let defines = CompilerEnvironment.GetCompilationDefinesForEditing parsingOptions

let! symbol =
asyncMaybe {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ type internal FSharpImplementInterfaceCodeFixProvider
let! sourceText = context.Document.GetTextAsync(cancellationToken)
let! _, parsedInput, checkFileResults = checker.ParseAndCheckDocument(context.Document, projectOptions, sourceText = sourceText, allowStaleResults = true, userOpName = userOpName)
let textLine = sourceText.Lines.GetLineFromPosition context.Span.Start
let defines = CompilerEnvironment.GetCompilationDefinesForEditing(context.Document.FilePath, parsingOptions)
let defines = CompilerEnvironment.GetCompilationDefinesForEditing parsingOptions
// Notice that context.Span doesn't return reliable ranges to find tokens at exact positions.
// That's why we tokenize the line and try to find the last successive identifier token
let tokens = Tokenizer.tokenizeLine(context.Document.Id, sourceText, context.Span.Start, context.Document.FilePath, defines)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ type internal FSharpRenameUnusedValueCodeFixProvider
let! parsingOptions, projectOptions = projectInfoManager.TryGetOptionsForEditingDocumentOrProject document
let! _, _, checkResults = checker.ParseAndCheckDocument(document, projectOptions, allowStaleResults = true, sourceText = sourceText, userOpName=userOpName)
let m = RoslynHelpers.TextSpanToFSharpRange(document.FilePath, context.Span, sourceText)
let defines = CompilerEnvironment.GetCompilationDefinesForEditing (document.FilePath, parsingOptions)
let defines = CompilerEnvironment.GetCompilationDefinesForEditing parsingOptions
let! lexerSymbol = Tokenizer.getSymbolAtPosition (document.Id, sourceText, context.Span.Start, document.FilePath, defines, SymbolLookupKind.Greedy, false)
let lineText = (sourceText.Lines.GetLineFromPosition context.Span.Start).ToString()
let! symbolUse = checkResults.GetSymbolUseAtLocation(m.StartLine, m.EndColumn, lineText, lexerSymbol.FullIsland, userOpName=userOpName)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ type internal FSharpDocumentHighlightsService [<ImportingConstructor>] (checkerP
let! parsingOptions, projectOptions = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document)
let! sourceText = document.GetTextAsync(cancellationToken)
let! textVersion = document.GetTextVersionAsync(cancellationToken)
let defines = CompilerEnvironment.GetCompilationDefinesForEditing(document.Name, parsingOptions)
let defines = CompilerEnvironment.GetCompilationDefinesForEditing parsingOptions
let! spans = FSharpDocumentHighlightsService.GetDocumentHighlights(checkerProvider.Checker, document.Id, sourceText, document.FilePath,
position, defines, projectOptions, textVersion.GetHashCode())
let highlightSpans =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ type internal FSharpEditorFormattingService

let line = sourceText.Lines.[sourceText.Lines.IndexOf position]

let defines = CompilerEnvironment.GetCompilationDefinesForEditing(filePath, parsingOptions)
let defines = CompilerEnvironment.GetCompilationDefinesForEditing parsingOptions

let tokens = Tokenizer.tokenizeLine(documentId, sourceText, line.Start, filePath, defines)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ type internal FSharpIndentationService

let rec tryFindLastNonWhitespaceOrCommentToken (line: TextLine) = maybe {
let! parsingOptions, _projectOptions = options
let defines = CompilerEnvironment.GetCompilationDefinesForEditing(filePath, parsingOptions)
let defines = CompilerEnvironment.GetCompilationDefinesForEditing parsingOptions
let tokens = Tokenizer.tokenizeLine(documentId, sourceText, line.Start, filePath, defines)

return!
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ type internal InlineRenameService
asyncMaybe {
let! parsingOptions, projectOptions = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document)
let! sourceText = document.GetTextAsync(cancellationToken)
let defines = CompilerEnvironment.GetCompilationDefinesForEditing(document.Name, parsingOptions)
let defines = CompilerEnvironment.GetCompilationDefinesForEditing parsingOptions
return! InlineRenameService.GetInlineRenameInfo(checkerProvider.Checker, projectInfoManager, document, sourceText, position, defines, projectOptions)
}
|> Async.map (Option.defaultValue FailureInlineRenameInfo.Instance)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -162,8 +162,8 @@ type internal FSharpProjectOptionsManager
let parsingOptions =
match projectOptionsOpt with
| Some (parsingOptions, _site, _projectOptions) -> parsingOptions
| _ -> FSharpParsingOptions.Default
CompilerEnvironment.GetCompilationDefinesForEditing(document.Name, parsingOptions)
| _ -> { FSharpParsingOptions.Default with IsInteractive = IsScript document.Name }
CompilerEnvironment.GetCompilationDefinesForEditing parsingOptions

/// Try and get the Options for a project
member this.TryGetOptionsForProject(projectId:ProjectId) = projectOptionsTable.TryGetOptionsForProject(projectId)
Expand Down
Loading