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

Redesign XmlDoc collecting mechanism #11973

Merged
merged 27 commits into from
Sep 17, 2021
Merged

Conversation

DedSec256
Copy link
Contributor

@DedSec256 DedSec256 commented Aug 14, 2021

This PR contains a redesign of the XmlDoc collecting mechanism to allow more predictable and C# consistent positions for XmlDoc.

Current approach

At the moment, doc-comments are sequentially collected and attached to the nearest (after comments) declaration that supports XmlDoc. This leads to errors when comments that should not be considered at all are attached to declarations:

image

Also at the moment, it is allowed to leave comments up to the name of the binding/type definition, which seems redundant:

image

Redesign

The basic idea is to set the grab points for doc-comments after the non-comment expressions encountered. Then we can assume that the comments block belongs to the declaration if its grab point coincides with the correct position in the declaration (for example the beginning of the declaration).

This PR offers to collect comments according to the following rules

1. Delimiter expressions

Similar to C#, if another expression (expression, simple comment, (*), etc.) is encountered when collecting doc-comments, doc-comments before this expression cannot be attached to the declaration

/// A1
1 + 1
/// A2
type A     // xmlDoc: A2

2. Attributes

Similar to C#, if the declaration has attributes, then the comments before the attributes are attached to the declaration

/// A1
[<Attribute>]
/// A2
type A     // xmlDoc: A1

3. type/let-and

Since there is a real use case #11489 (comment), it is allowed to add doc-comments before and

type A

/// B
and B     // xmlDoc: B 

Also, doc-comments are allowed to be left after and

type A

and /// B
       B     // xmlDoc: B 

However, if comments before and after and are present at the same time, then the priority is given to the comments before and

type A

/// B1
and /// B2
       B     // xmlDoc: B1

4. Declaration parts

It is allowed to leave comments only at the beginning of the declarations

let rec f x = g x

/// A
and /// B
    inline /// C
           private
                  g x = f x     // xmlDoc: A
type A =
    /// B1
    member
            ///B2
            private x.B = ()     // xmlDoc: B1

Fix for #11488

Support XmlDoc for union case without bar

type MyUnion =
    /// A
    A      // xmlDoc: A
    | B

TODO

  • Support let-bindings (and other unsupported constructions)
  • Extend XmlDoc-alalyzer with checks for invalid documentation location (probably in a separate PR)
  • More tests
  • Fix docs in FSharp.Core & FSharp.Compiler.Service
  • Include XmlDoc range to the declaration range? (in a separate PR)

Discussion

PR in progress, we will be glad to hear your opinion, join the discussion (:

@dsyme
Copy link
Contributor

dsyme commented Aug 14, 2021

Yes this is all reasonable.

Support XmlDoc for union case without bar

I'm not particularly concerned to support this case - omitting the first | from the first union case is very non-idiomatic F#

Similar to C#, if the declaration has attributes, then the comments before the attributes are attached to the declaration

What if the declaration doesn't have attributes? I think I've authored types where the attributes come first

@auduchinok
Copy link
Member

I'm not particularly concerned to support this case - omitting the first | from the first union case is very non-idiomatic F#

We want to have it properly marked in tree anyway, so the tooling is able to analyze it correctly.

@dsyme
Copy link
Contributor

dsyme commented Aug 14, 2021

We want to have it properly marked in tree anyway, so the tooling is able to analyze it correctly.

Got it

Copy link
Member

@auduchinok auduchinok left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a few small comments.

src/fsharp/XmlDoc.fs Outdated Show resolved Hide resolved
"""
checkResults
|> checkXml "A" [|"B"|]

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please also add a test case for attributes before an xml doc in a type declaration?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, sorry, I didn't spot it there, thanks!

@DedSec256
Copy link
Contributor Author

@dsyme,

What if the declaration doesn't have attributes? I think I've authored types where the attributes come first

Do I understand correctly that you mean the case

[<Attr>]
/// A
type A

In this case, type A will not have XmlDoc

@DedSec256
Copy link
Contributor Author

DedSec256 commented Aug 14, 2021

I would also like to discuss the XmlDoc collecting mechanism for get/set

At the moment, it is allowed to leave additional doc-comments for get/set.
Then the documentation for get (get_X) / set (set_X) is merged with the documentation for the property itself, and the documentation for the property is merged with the documentation for get

/// A
member x.A with ///GET
                get() = 5
            and /// SET
                set (y: int) = ()
                
// A     xmlDoc: "A GET"
// get_A xmlDoc: "A GET"
// set_A xmlDoc: "A SET"

Which seems overcomplicated unnecessarily.
Are there any cases when it is essential to add documentation for accessors?

In this PR, it is proposed to leave the possibility of adding documentation only to the declaration of the property itself,
property documentation will also apply to accessors.

@dsyme
Copy link
Contributor

dsyme commented Aug 15, 2021

Are there any cases when it is essential to add documentation for accessors?

What is the C# spec here? Can you document them separately? We should follow what they do.

@DedSec256
Copy link
Contributor Author

For C#, the documentation can be added only for properties

Documentation comments must immediately precede a user-defined type (such as a class, delegate, or interface) or a member (such as a field, event, property, or method)

https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/documentation-comments

image
https://sharplab.io/#v2:EYLgxg9gTgpgtADwGwBYA+ABAzAAgwJhwGEAbAQwGcKBGAWACgBvBnVnAek5wB4KBXALYCyUAJ4A+Fm07tWABSgQADlNYye7fkJETVeXAEsAdgBccC5Xub02tjlwDiAUQAqe2wHMYZxngDsOACsANw4AL4M7tJcAMquUawU3ji+ETZsaWFAA

@dsyme
Copy link
Contributor

dsyme commented Aug 15, 2021

For C#, the documentation can be added only for properties

OK, yes, we should follow that

@auduchinok
Copy link
Member

Extend XmlDoc-alalyzer with checks for invalid documentation location (probably in a separate PR)

We can mark each comment block when grabbing it (add a bool field to a block?), and then report syntax warnings for blocks that haven't been grabbed after parsing finishes.

@DedSec256
Copy link
Contributor Author

I think it is more or less ready.

@DedSec256
Copy link
Contributor Author

@dsyme, can you please take a look at this 👀

@dsyme
Copy link
Contributor

dsyme commented Aug 27, 2021

@dsyme, can you please take a look at this 👀

Will do!

| h :: _ -> h.Range
LexbufLocalXmlDocStore.GrabXmlDocBeforeMarker(parseState.LexBuffer, grabPoint)

let grabXmlDoc(parseState: IParseState, optAttributes: SynAttributeList list, elemIdx) =
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generally best to move all helper functions outside pars.fsy, at least for new code or whenever we touch this code.

@dsyme
Copy link
Contributor

dsyme commented Aug 31, 2021

I'm still uneasy about us dropping the XML doc on this case: #11973 (comment)

Am I right in thinking the XML doc is just dropped on the floor? Is it possible to emit an informational warning in this case, indeed whenever any /// doc is discarded, unattached?

@DedSec256
Copy link
Contributor Author

DedSec256 commented Aug 31, 2021

Yes, in the described case, the comment is discarded.
We want to add an analyzer that generates warnings (as described here #11973 (comment)) in a separate PR.

Or would you like the analyzer to be part of this PR?

@dsyme
Copy link
Contributor

dsyme commented Aug 31, 2021

Or would you like the analyzer to be part of this PR?

Yes I think we should have an informational warning informationalWarning(...) on discarded /// as part of this PR unless it's technically really difficult. Even for FSharp.Core there may be cases where we're now dropping docs, and I'd like that checked. I was going to ask you to check manually but we should really just automate it, it's better. So yes, please add the informational warning and also add --warnon:NNN for the informational message number you choose to FSharp.Core.fsproj

(Aside: It can't be an "analyzer" in the sense of #11057 and it should be on for the command-line compiler by default)

Copy link
Contributor

@dsyme dsyme left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Request for informational warning on dropped XML doc

@dsyme dsyme added the Theme-Simple-F# A cross-community initiative called "Simple F#", keeping people in the sweet spot of the language. label Aug 31, 2021
@@ -305,7 +305,7 @@ namespace Microsoft.FSharp.Control
contents.aux.ccont (OperationCanceledException (contents.aux.token))

/// Check for trampoline hijacking.
// Note, this must make tailcalls, so may not be an instance member taking a byref argument,
/// Note, this must make tailcalls, so may not be an instance member taking a byref argument,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like an internal comment to me. @dsyme Could you advice?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@auduchinok, here it is rather suspicious since the line below is a continuation of this one and it begins with ///

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I thought maybe it was better to change all comments below to // here?

Copy link
Contributor Author

@DedSec256 DedSec256 Sep 8, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh, below in the same file there is a similar comment that uses //

image

@DedSec256
Copy link
Contributor Author

@dsyme, FSharp.Compiler.Service has a lot of documentation, where the part of the documentation after // looks internal. We can replace all comments after // with /// -> //, then they will disappear from public access. Or how can these conflicts be resolved?

image

@dsyme
Copy link
Contributor

dsyme commented Sep 8, 2021

@dsyme, FSharp.Compiler.Service has a lot of documentation, where the part of the documentation after // looks internal. We can replace all comments after // with /// -> //, then they will disappear from public access. Or how can these conflicts be resolved?

As long as it isn't public FCS API I don't really mind

So yes, replace all comments after // with /// -> // is fine

@DedSec256 DedSec256 requested a review from dsyme September 9, 2021 09:29
@DedSec256
Copy link
Contributor Author

@dsyme , it's done! (:

@dsyme
Copy link
Contributor

dsyme commented Sep 17, 2021

@DedSec256 This is fantastic work, thank you

@dsyme dsyme merged commit 85fe280 into dotnet:main Sep 17, 2021
@dsyme
Copy link
Contributor

dsyme commented Sep 17, 2021

@DedSec256 Thank you for everything you did on this, including the extensive testing and multiple iterations to take into account code feedback. It's really great to have this addressed

@DedSec256
Copy link
Contributor Author

@dsyme, I thought about the fact that we currently have XmlDoc allowed for local bindings. They do not participate in the generation of the documentation file, and XmlDoc is not allowed for local variables in C#. How do you feel about disallowing XmlDoc for local bindings in the same way?

@dsyme
Copy link
Contributor

dsyme commented Nov 8, 2021

@dsyme, I thought about the fact that we currently have XmlDoc allowed for local bindings. They do not participate in the generation of the documentation file, and XmlDoc is not allowed for local variables in C#. How do you feel about disallowing XmlDoc for local bindings in the same way?

XML doc is allowed for local bindings in F#. They should show in autocomplete/intellisense. I assume this PR didn't change that?

@DedSec256
Copy link
Contributor Author

XML doc is allowed for local bindings in F#. They should show in autocomplete/intellisense. I assume this PR didn't change that?

yes, in this PR the XmlDoc is still allowed for local bindings.

@auduchinok
Copy link
Member

@dsyme Could you tell more about why XmlDoc is preferred over normal comments in local scopes?
Maybe you have any insight why it wasn't enabled in C#? 🙂

@dsyme
Copy link
Contributor

dsyme commented Nov 8, 2021

The only rationale is to have them show in tooling.

I guess it wasn't enabled in C# because scopes were generally much smaller, especially in C# 1.0 coding styles. In F# you can easily have a class 500 lines long and in that situation it pays to document a let function that has scope over the whole class.

@dsyme
Copy link
Contributor

dsyme commented Nov 8, 2021

(Also, in F# XML doc comments are much lighter with the implicit <summary>)

@auduchinok
Copy link
Member

auduchinok commented Nov 8, 2021

(Also, in F# XML doc comments are much lighter with the implicit <summary>)

(Hm, sounds just like in modern C#. 🙂)

@dsyme
Copy link
Contributor

dsyme commented Nov 8, 2021

I wonder where they got that idea!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Theme-Simple-F# A cross-community initiative called "Simple F#", keeping people in the sweet spot of the language.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants