Skip to content

Conversation

@genlu
Copy link
Member

@genlu genlu commented Oct 22, 2025

fix #78515, #51629

@genlu genlu requested a review from a team as a code owner October 22, 2025 00:53
if (typeMembers.Any(t => t.DeclaredAccessibility == Accessibility.Public))
return true;

return typeMembers.Any(t => t.DeclaredAccessibility == Accessibility.Internal) &&
Copy link
Member Author

Choose a reason for hiding this comment

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

Maybe we could make it even less aggressive (and simpler) in auto-completing () by only checking for types declared public, thoughts?

Copy link
Member

Choose a reason for hiding this comment

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

We have an IsAccessibleTo helper. Can you use that?

Copy link
Member Author

Choose a reason for hiding this comment

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

@CyrusNajmabadi Done. Could you pls take another look?

else if (context.IsObjectCreationTypeContext)
{
// If this is a type symbol/alias symbol, also consider appending parenthesis when later, it is committed by using special characters,
// and the type is used as constructor
Copy link
Member

Choose a reason for hiding this comment

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

Is this comment stale now? I can't quite tell. But it does allude to what I'm a bit confused about -- should we be explicitly adding the parenthesis only when there isn't a commit character, or the user typed a paren? I guess this original behavior is making me scratch my head in the first place...

Copy link
Member Author

@genlu genlu Oct 22, 2025

Choose a reason for hiding this comment

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

I think this comment still applies, especially when combined with the new comment a couple of lines below. I don't remember the original design, but this behavior doesn't feel counter intuitive to me. Since this is after new, the type name can only be used as a constructor (except the nested type scenario, which is what this PR's about), if you commit with . or ;, you'd need to move cursor back and type the ( otherwise, and when you commit TAB, you can keep on typing ( without moving cursor

{
foreach (var typeMember in typeSymbol.GetTypeMembers())
{
if (typeMember.DeclaredAccessibility <= Accessibility.Protected)
Copy link
Member

Choose a reason for hiding this comment

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

note: this is not really accurate. for example, you can have protected/private nested types that are then referencable from within the inner type. i think you want if (typeMember.IsAccessibleWithin( and then pass the .ContainingType for where the operation was invokved from. Which i think is on hte syntax context. So basically, get rid of the first two 'if' checks, and update the bottom one.

these are good to test as well.

Copy link
Member Author

@genlu genlu Oct 23, 2025

Choose a reason for hiding this comment

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

yea, this is intended to be a heuristic to cover common cases. Because we might be handed a speculative member model here (see this), I think we can't reliably do something like below, otherwise it might throw

var containingType = syntaxContext.SemanticModel.GetDeclaredSymbol(syntaxContext.ContainingTypeDeclaration!, cancellationToken);
if (typeMember.IsAccessibleWithin(containingType))
...

you can have protected/private nested types that are then referencable from within the inner type

I think this is fine. The change is only relevant when you are typing the outer type name, and I think people typically don't access the nested ones via the outer one when they are within the inner or derived type. For example

class Outer
{
    class Inner1
    {
        void M()
        {
            // you could use `Inner` instead of `Outer.Inner` here
        }
    }
    
}

Copy link
Member

Choose a reason for hiding this comment

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

I think this is fine. The change is only relevant when you are typing the outer type name, and I think people typically don't access the nested ones via the outer one when they are within the inner or derived type. For example

That's a reasonable point. Though i think you'll still just be in an easier scenario simplifying, and using hte above approach. it's a case where, IMO, it's simpler and more correct.

From offline convo, you should be fine to call GetDeclaredSymbol even for speculative scenarios.

Copy link
Member Author

Choose a reason for hiding this comment

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

Talked offline, the comment about speculative member model above stands so merging as is.
added more comments in the code to explain this.

Copy link
Member

@CyrusNajmabadi CyrusNajmabadi left a comment

Choose a reason for hiding this comment

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

Preemptively signing off if you make the suggested change. if you can't, please let me know why. thanks.

@CyrusNajmabadi
Copy link
Member

Specifically, the suggested change about checking accessibility properly. not the minor suggestion to use an expression-bodied member.

@genlu genlu enabled auto-merge (squash) October 23, 2025 21:25
@genlu genlu merged commit e078eb7 into dotnet:main Oct 24, 2025
24 of 25 checks passed
@dotnet-policy-service dotnet-policy-service bot added this to the Next milestone Oct 24, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

new Type.$$ inserts unwanted parens

3 participants