-
Notifications
You must be signed in to change notification settings - Fork 815
Support CallerArgumentExpression
(without #line
)
#17519
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
base: main
Are you sure you want to change the base?
Conversation
❗ Release notes required
|
…Tangent-90/fsharp into SupportCallerArgumentExpression
Here I encounter a problem about #1 "C:\\Program.fs"
System.ArgumentNullException.ThrowIfNullOrWhiteSpace(" ") // will failed to build
// And more complicated case, repeat the file name and line number
#1 "C:\\Program.fs"
System.ArgumentNullException.ThrowIfNullOrWhiteSpace(" ") // will failed to build So here I want to get the original |
Simplify code
add warning read the new line mark
…Tangent-90/fsharp into SupportCallerArgumentExpression
src/Compiler/Utilities/range.fs
Outdated
| _ -> String.Empty | ||
} | ||
|
||
let getCodeText (m: range) = |
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 wonder whether it would be possible to use ISourceText.GetSubTextString
instead of all of this. I think the ISourceText
for a given file will usually already be cached when this functionality is needed.
I think in theory the source text is available on cenv.tcSink.CurrentSink.Value.CurrentSourceText.Value
, but maybe there's a better way to get it, or a better way to bring it in scope for this change.
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.
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.
Maybe it would make sense to pass it in, then, as is done when checking format strings:
fsharp/src/Compiler/Checking/NameResolution.fs
Lines 1777 to 1779 in 0a715a5
type FormatStringCheckContext = | |
{ SourceText: ISourceText | |
LineStartPositions: int[] } |
fsharp/src/Compiler/Checking/CheckFormatStrings.fs
Lines 67 to 73 in 0a715a5
let makeFmts (context: FormatStringCheckContext) (fragRanges: range list) (fmt: string) = | |
// Splits the string on interpolation holes based on fragment ranges. | |
// Returns a list of tuples in the form of: offset * fragment as a string * original range of the fragment | |
// where "offset" is the offset between beginning of the original range and where the string content begins | |
let numFrags = fragRanges.Length | |
let sourceText = context.SourceText |
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.
Maybe it would make sense to pass it in, then, as is done when checking format strings:
I guess maybe that's the same source text as in the sink... But still, maybe there's somewhere else we could put it.
src/Compiler/Utilities/range.fs
Outdated
@@ -581,3 +581,83 @@ module Range = | |||
| None -> mkRange file (mkPos 1 0) (mkPos 1 80) | |||
with _ -> | |||
mkRange file (mkPos 1 0) (mkPos 1 80) | |||
|
|||
module internal FileContent = |
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 feel uneasy with this module.
- It adds a global resource for a rather local feature.
- It creates another dependency on the file system.
- It re-reads sources from the file system that have been read before.
Getting the source text out of the checking environment as proposed by @brianrourkeboll would be much cleaner.
A (probably too ambitious) alternative would be to make this a central place for source access, to give ISourceText the role that it was meant to have and to remove file system access elsewhere.
Hi there, I've make some efforts to make it get the source code from the |
@@ -325,6 +325,9 @@ and [<Sealed>] internal LexBuffer<'Char> | |||
with get () = bufferAcceptAction | |||
and set v = bufferAcceptAction <- v | |||
|
|||
member val private SourceTextBuilder = System.Text.StringBuilder() |
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.
Put data to rest of the fields above of this type.
The style here is - declaration, fields, private let functions, then members.
Make it obvious by design that the StringBuilder is being cleared when appropriate (where does buffer cleanup happen now when lexes moves to a different file?)
@@ -325,6 +325,9 @@ and [<Sealed>] internal LexBuffer<'Char> | |||
with get () = bufferAcceptAction | |||
and set v = bufferAcceptAction <- v | |||
|
|||
member val private SourceTextBuilder = System.Text.StringBuilder() | |||
member lexbuf.SourceText = lexbuf.SourceTextBuilder.ToString() |> SourceText.ofString | |||
|
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.
Consider either pooling of the StringBuilder instances or a starting buffer that would reflect average size of an F# source file (the buffer above uses 4096 for example)
// F# view of attributes (these get converted to AbsIL attributes in ilxgen) | ||
let IsMatchingFSharpAttribute g (AttribInfo(_, tcref)) (Attrib(tcref2, _, _, _, _, _, _)) = tyconRefEq g tcref tcref2 | ||
let HasFSharpAttribute g tref attrs = List.exists (IsMatchingFSharpAttribute g tref) attrs | ||
let TryFindFSharpAttribute g tref attrs = List.tryFind (IsMatchingFSharpAttribute g tref) attrs | ||
let TryFindFSharpAttributeOpt g tref attrs = match tref with None -> None | Some tref -> List.tryFind (IsMatchingFSharpAttribute g tref) attrs | ||
|
||
let TryFindFSharpAttributeByName nm attrs = |
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.
Consider: Existing mechanism via TryFindFSharpAttribute
and having a reference to the attribute in TcGlobals
, i.e. work on strongly typed referenced to attribute definitions and not on strings.
@@ -3513,12 +3513,25 @@ let HasILAttribute tref (attrs: ILAttributes) = | |||
let TryDecodeILAttribute tref (attrs: ILAttributes) = | |||
attrs.AsArray() |> Array.tryPick (fun x -> if isILAttrib tref x then Some(decodeILAttribData x) else None) | |||
|
|||
let TryDecodeILAttributeByName nm (attrs: ILAttributes) = |
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.
Wouldn't approach like this one work here:
let compiledNameAttrib = TryFindFSharpStringAttribute g g.attrib_CompiledNameAttribute attrs
|
||
module CustomAttributes_CallerArgumentExpression = | ||
|
||
[<FactForNETCOREAPP>] |
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.
Is there a particular reason for all added tests being FactForNETCOREAPP
and not Fact
that would also test Desktop framework?
...e/BasicGrammarElements/CustomAttributes/CallerArgumentExpression/CallerArgumentExpression.fs
Show resolved
Hide resolved
A.B("abc" | ||
#line 1 | ||
: string) | ||
|> assertEqual "\"abc\" | ||
#line 1 | ||
: string" | ||
|
||
|
||
A.B((+) 1 | ||
#line 1 | ||
123) | ||
|> assertEqual "(+) 1 | ||
#line 1 | ||
123" | ||
|
||
|
||
A.B(#line 1 | ||
(+) 1 | ||
123) | ||
|> assertEqual "(+) 1 |
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.
For future maintainers, I think this test setup needs more explanation of the context.
open FSharp.Test.Compiler | ||
open FSharp.Test | ||
|
||
module CustomAttributes_CallerArgumentExpression = |
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.
Did you consider a de-generate scenario of a huge file being a single expression (e.g. a massive collection builder, or a massive async block) passed into a single CallerArgumentExpressionAttribute
member?
Description
Implements fsharp/fslang-suggestions#966
RFC fsharp/fslang-design#798
Checklist