-
Notifications
You must be signed in to change notification settings - Fork 789
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
Scoped nowarn #18049
base: main
Are you sure you want to change the base?
Scoped nowarn #18049
Conversation
❗ Release notes required
|
Hi @Martin521 - thanks for the contribution. It's a substantial effort and we appreciate it. The PR is on our radar - just keep in mind that it's big and specific, and it will take time to find capacity for it. If anyone from the community gets to thoroughly review it, that would be valuable as well. Thanks for your diligence and patience :) |
@@ -877,6 +878,14 @@ type TcConfigBuilder = | |||
compilationMode = TcGlobals.CompilationMode.Unset | |||
} | |||
|
|||
member tcConfigB.SetLangVersion v = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Adding a mutator for langVersion is fine but mostly unnecessary, but please don't let the langVersion mutator set other values in TcConfig that is not the paradigm we use. Yes SetPrimaryAssembly and SetUseSdkRefs also set tcConfigB.fxResolver, they probably shouldn't, actually I think I may fix them so they don't.
TcConfigBuilder is a mutable record and so modifying values is kind of what it's about, after building this is passed to the constructor of TcConfig which then creates the immutable TcConfig that is used to access these values. Perhaps the best thing is to mutate fsharpDiagnosticOptions with the language version when constructing the TcConfig object. Then you would plumb the langversion to the places where FSharpDiagnosticOptions.Default is used and add a langVersion argument to FSharpDiagnosticOptions.Default which would enable the error and warning defaults to change on a per language version basis which is probably usefull longer term. Unfortunately plumbing data about is never fun.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When tcConfigB.diagnosticsOptions
is set to FSharpDiagnosticOptions.Default
, the langversion is not yet known. The langversion comes only much later with ParseCompilerOpions
.
There are two alternatives that I considered but didn't like.
The first was to add to tcConfigB
an updated FSharpDiagnosticOptions
(updated with the langversion) in all the places where ParseCompilerOptions
is called (in fsc.fs, fsi.fs, BackgroundCompiler.fs, TransparentCompiler.fs and FSharpCheckerResults.fs).
The second was to thread the whole tcConfig
through all the function calls to the places where I need only the diagnosticsOptions.
But let me check if I find still another way.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I still have a long way to go, this is an interesting feature.
@@ -20,6 +20,8 @@ type FSharpDiagnosticOptions = | |||
WarnOn: int list | |||
WarnAsError: int list | |||
WarnAsWarn: int list | |||
WarnScopesFeatureIsSupported: bool | |||
mutable WarnScopeData: obj option |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
DiagnosticOptions should remain immutable. Perhaps collect this data separately and pass it to where it needs to go.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I did not really like to add a mutable field, but here are my "excuses".
- I believe that the information about the warn scopes belongs into
FSharpDiagnosticsOptions
as much as all the other fields. All these fields are used only in one single place in the whole compiler, namely inPhasedDiagnostics.AddSeverity
. Passing the information separately does not fit its purpose and would add an additional parameter to many functions. - This information needs to be updated during postparse for every file. This is in the nature of the data: the #nowarn/#warnon directives change the way warnings are emitted.
- I modeled this in some way after the
BufferLocalStore
oflexbuf
. Just a way to store data for a certain aspect for the current compiler phase. WarnScopeData
is accessed only via private functionsWarnScopes.getWarnScopeData
andWarnScopes.setWarnScopeData
in theWarnScopes
module.
I will look into alternatives and see if I find something acceptable.
(For fsc, we could probably go back to tcConfigB and update the diagnosticsOptions there. But this may not work well for the other use cases. And it would not be safer or nicer or more idiosyncratic than the mutable field.)
/// a list of mapped sections (surrogate and original start lines). | ||
type private LineMaps = Map<FileIndex, FileIndex * (LineNumber * LineNumber) list> | ||
|
||
type private TempData = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Name is a bit generic.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree. I had actually made a note about it already :-)
I will change it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I renamed it.
ns |> removeQuotes |> Option.bind removePrefix |> Option.bind parseInt | ||
|
||
let private regex = | ||
Regex(""" *#(nowarn|warnon|\S+)(?: +([^ \r\n/;]+))*(?:;;)? *(\/\/.*)?$""", RegexOptions.CultureInvariant) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe this regex is slightly incorrect:
for better or worse the F# syntax for directives is
[optional spaces]#[optional spaces]directive[space][arguments]
so this code will turn off error 44
# nowarn 44
For example paste this code into dotnet fsi
open System
[<Obsolete("This function is obsolete. Use newFunction instead.")>]
let oldFunction x =
printfn "This is the old function with value %d" x
let newFunction x =
printfn "This is the new function with value %d" x
oldFunction 10;;
# nowarn 44;;
oldFunction 30;;
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh and perhaps a description of the syntax the regex implements.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would consider the fact that the compiler accepts spaces after the hash sign a bug.
But it can certainly be replicated in the new version (it has to happen here and in the lex grammar then).
I guess this should become another RFC discussion.
@@ -2236,9 +2236,6 @@ FSharp.Compiler.CodeAnalysis.FSharpParsingOptions: FSharp.Compiler.CodeAnalysis. | |||
FSharp.Compiler.CodeAnalysis.FSharpParsingOptions: FSharp.Compiler.CodeAnalysis.FSharpParsingOptions get_Default() | |||
FSharp.Compiler.CodeAnalysis.FSharpParsingOptions: FSharp.Compiler.Diagnostics.FSharpDiagnosticOptions DiagnosticOptions | |||
FSharp.Compiler.CodeAnalysis.FSharpParsingOptions: FSharp.Compiler.Diagnostics.FSharpDiagnosticOptions get_DiagnosticOptions() | |||
FSharp.Compiler.CodeAnalysis.FSharpParsingOptions: Int32 CompareTo(FSharp.Compiler.CodeAnalysis.FSharpParsingOptions) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we understand why these public Apis were removed? is it likely to impact existing projects?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These methods are no longer auto-generated because FSharpDiagnosticsOptions
has now a obj
member and thus is no longer comparable.
FSharp.Compiler.Diagnostics.FSharpDiagnosticOptions: System.String ToString() | ||
FSharp.Compiler.Diagnostics.FSharpDiagnosticOptions: Void .ctor(Int32, Boolean, Microsoft.FSharp.Collections.FSharpList`1[System.Int32], Microsoft.FSharp.Collections.FSharpList`1[System.Int32], Microsoft.FSharp.Collections.FSharpList`1[System.Int32], Microsoft.FSharp.Collections.FSharpList`1[System.Int32]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we understand why these public Apis were removed? is it likely to impact existing projects?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These methods are no longer auto-generated because FSharpDiagnosticsOptions has now a obj member and thus is no longer comparable.
@@ -1,9 +1,10 @@ | |||
// #Conformance #FSI | |||
#load "ThisProject.fsx" | |||
#nowarn "44" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This test case change looks very specific.
It appears to verify that #nowarn in an imported source file named "ThisProject.fsx" will impact the currently compiling sourcefile
I have just tested with the released code and the #nowarn does indeed flow.
By all means add a new test case to ensure that #nowarn "44" works, although probably there are plenty. But this test case needs to pass unmodified. Or this feature introduces a breaking change, and if it does, we will have to discuss that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is implementing the RFC, so it should be discussed over there.
See also this comment.
@@ -30,4 +30,5 @@ ImplFile | |||
Expr (Do (Const (Unit, (3,3--3,5)), (3,0--3,5)), (3,0--3,5))], | |||
PreXmlDocEmpty, [], None, (2,0--4,0), { LeadingKeyword = None })], | |||
(true, true), { ConditionalDirectives = [] | |||
WarnDirectives = [] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be nice if we didn't have this right now, it is very hard to use github web interface with 900 baseline files that have just this WarnDirectives = []. Perhaps remove the WarnDirectives addition to the syntax tree right now so that what we have is reviewable? We can add it back when we are confident the core changes are acceptable?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's why I spent effort to create the successive commits. Almost all of the baseline changes are introduced only in commit 6. (See the PR description above)
|> shouldFail | ||
|> withDiagnostics [ | ||
if languageVersion = "8.0" then | ||
(Error 3350, Line 4, Col 5, Line 4, Col 7, "Feature '# directives with non-quoted string arguments' is not available in F# 8.0. Please use language version 9.0 or greater.") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I assume that testing # nowarn with each argument on a separate line is tested elsewhere can you point me to the test cases that make this test redundent
Thanks
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is implementing the RFC, which assumes that compiler directives are single-line.
@@ -1080,6 +1082,13 @@ rule token (args: LexArgs) (skip: bool) = parse | |||
lexbuf.StartPos <- lexbuf.StartPos.ShiftColumnBy(n) | |||
HASH_IDENT(lexemeTrimLeft lexbuf (n+1)) } | |||
|
|||
| anywhite* ("#nowarn" | "#warnon") anystring |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So you are not using the existing lexing for directives? will any existing programs break, or is this implementation compatible?
For example:
#nowarn
#nowarn
"1"
44 // This construct is deprecated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The directives were processed in the parser. This is no longer possible if we want to enable them everywhere (not only in top-level modules. Therefore I had to pull it forward into the lexer. (Commit 2 includes these changes).
open FSharp.Compiler.Text | ||
open FSharp.Compiler.UnicodeLexing | ||
|
||
module internal WarnScopes = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any reason to make it internal?
We should somehow make the scopes information accessible to FCS users, given that the previous implementation is removed from SyntaxTree.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The directives are available as trivia (now in ParsedImplFileInputTrivia
/ ParsedSigFileInputTrivia
), see commit 5 of this PR.
But if more information is useful for tooling, please let me know what I should make available.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Helpers like IsWarnon
and IsNowarn
are definitely useful.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I made IsWarnon
and IsNowarn
public.
I would recommend, though, to rather rely on the filtering mechanism of the diagnostics loggers of the compiler (which use these two functions internally).
changed a private name to make it more readable
Description
Implements Scoped Nowarn according to draft RFC FS-1146.
This PR has taken a while. I had to deal with much more complexity than I imagined when I naively volunteered to tackle the feature request. Anyway, here we are.
I have split the PR into 7 commits that can be reviewed in sequence.
All of them compile, 1 and 4 - 7 also pass all tests locally.
Add the feature flag, baseline tests, and the core
WarnScopes
module. Seesrc/Compiler/SyntaxTree/WarnScopes.fsi
and the RFC for the functionality of the module.Add the necessary changes to lexing and parsing. Note that the warn directives can no longer be collected during parsing (since they can now appear not only in top-level modules, but anywhere). So we collect them during lexing, similar to the processing of #if/#else/#endif directives.
Remove legacy #nowarn processing (but hold off AST changes)
Integrate the WarnScopes functionality and test it
Add warn directive trivia (but hold off AST changes)
Enable warn directive trivia (which means AST changes)
Remove defunct types and parameters related to former #nowarn processing (more AST changes)
There is also a separate commit for the IlVerify baseline updates (change in line numbers only)
Checklist