Skip to content

Provide public API to determine if a symbol is accessible#26367

Closed
gafter wants to merge 16 commits intodotnet:masterfrom
gafter:master-IsAccessibleWithin
Closed

Provide public API to determine if a symbol is accessible#26367
gafter wants to merge 16 commits intodotnet:masterfrom
gafter:master-IsAccessibleWithin

Conversation

@gafter
Copy link
Copy Markdown
Member

@gafter gafter commented Apr 24, 2018

Fixes #24518
Fixes #23842

@dotnet/roslyn-compiler Please review this new proposed CodeAnalysis API
@dotnet/roslyn-ide I have removed the old Workspaces implementation (but not the tests)
@Pilchie @DustinCampbell @CyrusNajmabadi Feedback welcome.

@gafter gafter added Concept-API This issue involves adding, removing, clarification, or modification of an API. Area-Compilers Feature Request labels Apr 24, 2018
@gafter gafter added this to the 16.0 milestone Apr 24, 2018
@gafter gafter self-assigned this Apr 24, 2018
@gafter gafter requested review from a team as code owners April 24, 2018 21:39
@CyrusNajmabadi
Copy link
Copy Markdown
Contributor

CyrusNajmabadi commented Apr 24, 2018

Note: my only concern with this being at the compiler level is: does this accurately represent accessibility across both languages. In the IDE it was fine to have a common 'good enough' impl that behaved reasonably well for C#/VB. In other words, i never vetted it to make sure it was doing the right thing for VB, with the assumption that if it wasn't, it would likely be so corner case as to not matter for IDE features.

As a compiler API, i would like things to have a higher bar of accurately reflecting the language. If this does that for VB and C#, then i'm happy :) #Resolved

@@ -0,0 +1,291 @@
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
Copy link
Copy Markdown
Contributor

@CyrusNajmabadi CyrusNajmabadi Apr 24, 2018

Choose a reason for hiding this comment

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

i am very suprised github didn't see this as a file rename. Did you change a lot of code in here? #Resolved

Copy link
Copy Markdown
Member Author

@gafter gafter Apr 24, 2018

Choose a reason for hiding this comment

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

I didn't move it. I rewrote it from scratch to implement the language semantics. #Resolved

Copy link
Copy Markdown
Contributor

@CyrusNajmabadi CyrusNajmabadi Apr 24, 2018

Choose a reason for hiding this comment

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

I didn't move it. I rewrote it from scratch to implement the language semantics.

Great! Then that removes my concern about language semantics not matching. Can you confirm these are the right semantics for VB and for C#? Thanks! #Resolved

@gafter
Copy link
Copy Markdown
Member Author

gafter commented Apr 24, 2018

my only concern with this being at the compiler level is: does this accurately represent accessibility across both languages.

It accurately reflects C# and CLR accessibility. It is simpler than the compiler internal implementations because it doesn't have to protect from cycles that can occur when it is used within the compiler, and it doesn't have to distinguish between different reasons for access failure.

If VB differs, then it can't do both C# and VB at the same time. Having said that, I don't believe VB differs. #Resolved

@CyrusNajmabadi
Copy link
Copy Markdown
Contributor

CyrusNajmabadi commented Apr 24, 2018

If VB differs, then it can't do both C# and VB at the same time.

Yeah... that would be the downside (and would block usage from the IDE, thus defeating hte purpose).

Basically, i'm ok with this if VB is ok with this. #Resolved

@CyrusNajmabadi
Copy link
Copy Markdown
Contributor

CyrusNajmabadi commented Apr 24, 2018

and CLR accessibility

That's a strong sign this would be ok. Since the CLR is the common substrate between the languages, if this logic is at least providing those semantics, then i think this is fine no matter what. #Resolved

@gafter
Copy link
Copy Markdown
Member Author

gafter commented Apr 24, 2018

FYI, it looks like the VB implementation of accessibility mirrors the C# implementation, even when that conflicts with the language specification:

            ' Protected is quite confusing. It's a two-fold test:
            '    1) The class access is from ("withinType") is, or is inside, 
            '        a class derived from "originalContainingType"
            '    2) If there is a qualifier, and the member is not shared, 
            '       the qualifier must be an instance of the derived
            '       class in which the access occurred (any construction thereof)
            '
            ' The VB language spec describes a third test:
            '    3) If there is a qualifier, and the member is not shared, the qualifier
            '       must be of the exact construction of the derived class from which access occurred.
            ' But Dev10 actually implements something rather different:
            '    3) If there is a qualifier, for any member, shared or not, the construction of "originalContainingType" that
            '       the qualifier type inherits from is the same as the construction of "originalContainingType" that the 
            '       class through which access occurred.
            ' This rule (either the spec'd or Dev10 version) is intentionally not implemented here. See bug 4107.

#Resolved

Copy link
Copy Markdown
Contributor

@AlekseyTs AlekseyTs left a comment

Choose a reason for hiding this comment

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

I think the API should be implemented on Compilation and delegate to language specific implementation:

  • it should verify that the symbols it gets are matching the language of the compilation
  • it should probably verify that symbols "belong" to the compilation, otherwise the API is likely to produce false negative results
  • it should delegate to language specific implementation, which I am pretty sure is already available for use, instead of reinventing "a wheel"

@gafter
Copy link
Copy Markdown
Member Author

gafter commented Apr 25, 2018

@AlekseyTs I understand that you'd like more "verification" about compilations, but this API isn't intended to be bound to particular compilations (there are multiple symbol arguments and they can be from different compilations); compilations are not part of its contract. You say that the implementation as written "is likely to produce false negative results". Can you please identify specific scenarios in which it produces an incorrect result? #Resolved

@gafter
Copy link
Copy Markdown
Member Author

gafter commented Apr 25, 2018

test windows_release_vs-integration_prtest please #Resolved

@CyrusNajmabadi
Copy link
Copy Markdown
Contributor

CyrusNajmabadi commented Apr 25, 2018

Note: the IDE creates symbols that do not belong to any compilation. We do this as part of many features. For example, we synthesize up methods during 'generate method' that are a real IMethodSymbol that are not backed by a C# or VB MethodSymbol. These symbols then flow around many parts of our stack, including parts that want to potentially do accessibility checks.

Having a language agnostic API allows us to pass this data around without having to ensure that all parts of the stack have to be agreeing on the same language. And it allows accessibility checks to work even with symbols that we are literally synthesizing out of fresh air.

--

But even in the case where we are not creating totally new symbols, there are still cases where this functionality is valuable. here's a good end-to-end example with just C#/VB symbols.

Say you have two projects, a C# and a VB project. And you want to add 'add using with project reference'. The search finds a potentially applicable symbol in teh VB project, but then wants to know if this symbol would be accessible at the location you're at in the C# project. Note: we cannot 'map' the VB symbol into the C# project because there is literally no project reference between the two (that's what this feature is trying to decide if it should do). So it wants to just say "would this symbol be accessible to the target language?" in an efficient manner. i.e. we do not want to build up compilations, map symbols, and then ask queries in that fork when we can just ask a cheap question about two symbols.

#Resolved

@gafter
Copy link
Copy Markdown
Member Author

gafter commented Apr 25, 2018

The intent of this API is to answer accessibility questions on ISymbols, which contain enough information to answer accessibility questions. There is no need for us to delegate to language-specific symbol classes. #Resolved

@AlekseyTs
Copy link
Copy Markdown
Contributor

AlekseyTs commented Apr 26, 2018

The intent of this API is to answer accessibility questions on ISymbols, which contain enough information to answer accessibility questions. There is no need for us to delegate to language-specific symbol classes.

I think there is no need to write new code, a third implementation of accessibility check, which actually doesn't provide benefits it might appear to provide. It looks like the implementation relies on symbol comparison, symbols from different languages are never equal. So, what is the point in having shared implementation that simply isn't going to have consistent behavior across all the scenarios it is expected to handle? If we don't want to put the method on a Compilation, we can simply add it as a real member on ISymbol interface, we add new APIs to symbol interfaces all the time. Why this one is special? Then, as with other similar APIs, we check that input belongs to the language and call implementation we already have. This, however, won't allow us to check whether the symbols belong to the same "universe". Note, that symbols that belong to the same language and represent the same entities might come from different "universes" and, therefore, will not be considered equal. A type symbol for System.Console obtained from one compilation might not be equal to symbol for System.Console obtained from another compilation. The shape of the API is likely to lead people to believe that it is not important where symbols are coming from, but it actually is extremely important. Some very simple scenarios, with public symbols, are likely to produce expected results, but the failures will actually come for more interesting scenarios. That said, I believe it is better to structure the API in the way that sets right expectations and leads consumers down the path of success rather than failure. Therefore, it feels bad to make it appear as though the API works reliably across languages, when it doesn't. That should be clearly documented and consumers should be prevented to call this API while mixing symbols from different languages. I would also prefer us to go farther and actually do more verification, making sure that symbols actually belong to the same compilation, only then we can guarantee correctness of the result across all scenarios. #Resolved

return true;
}

return toAssembly.GivesAccessTo(fromAssembly);
Copy link
Copy Markdown
Contributor

@AlekseyTs AlekseyTs Apr 26, 2018

Choose a reason for hiding this comment

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

GivesAccessTo [](start = 30, length = 13)

It looks like this is never true when toAssembly and fromAssembly belong to different languages. #Resolved

Copy link
Copy Markdown
Member Author

@gafter gafter Apr 26, 2018

Choose a reason for hiding this comment

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

Sounds like a bug. Either an assembly in one language is permitted to give access to an assembly in another language, in which case the API is returning an incorrect result, or an assembly in one language is not permitted to give access to an assembly in another language, in which case this code is correct. Either way, if there is a problem it is in the implementation of toAssembly.GivesAccessTo, not here. #Resolved

Copy link
Copy Markdown
Contributor

@AlekseyTs AlekseyTs Apr 26, 2018

Choose a reason for hiding this comment

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

in which case this code is correct

Perhaps we should get clarity and agree on what does it mean for this component to behave correctly, what are the expected results in various scenarios. Your response makes me to believe that even you are not sure what to expect. #Resolved

Copy link
Copy Markdown
Contributor

@CyrusNajmabadi CyrusNajmabadi Apr 26, 2018

Choose a reason for hiding this comment

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

I agree it would be good to get clarity on all the relational cases. This will help ensure everyone is on the same page and we all think it makes sense and can actually be implemented. It will also be helpful toward driving a suitable test suite that then validates these expectations.

From my reading of the code, the areas that need clarity are:

  1. Internal, and how accessibilty across different-language-boundary should be defined.
  2. protected, and how inheritance across different-language-boundary be defined.

Technically, it looks like 'private' also does type checks. However, private seems well defined across languages/assemblies It's always false as something in another assembly (which you would always get if you had a different language) would never be able to see your privates.

@gafter would you be willing to go investigate these and to come back with a proposal that we can discuss? #Resolved

Copy link
Copy Markdown
Member Author

@gafter gafter Apr 26, 2018

Choose a reason for hiding this comment

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

Your response makes me to believe that even you are not sure what to expect.

I am not sure whether there is a bug in the language-specific Symbol APIs, since you reported them not obeying their specifications. This API assumes that the Symbol APIs are correct.

I expect the Symbol APIs to return correct results, in which case this API would return correct results. #Resolved

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

would you be willing to go investigate these and to come back with a proposal that we can discuss

Are you asking me to transcribe the CLR access rules? The only "variable" is how we define type and assembly identity. Assembly identity is supposed to be defined by IAssemblySymbol.Identity but @AlekseyTs points out that the compiler does it wrong. Type identity should be defined, recursively, by name and arity, and containing symbol. The rest is down to the CLR rules for the meaning of the access modes.


In reply to: 184489272 [](ancestors = 184489272)

Copy link
Copy Markdown
Contributor

@CyrusNajmabadi CyrusNajmabadi Apr 27, 2018

Choose a reason for hiding this comment

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

@gafter Thanks! #Resolved

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

We don't have tests for interfaces, because we don't have implementations in the interfaces.


In reply to: 184506449 [](ancestors = 184506449)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

The code no longer compares assemblies using ==. We compare assembly identities instead, which is the CLR rule.


In reply to: 184566431 [](ancestors = 184566431,184278437)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

The implementation now compares assemblies by comparing their identities.


In reply to: 184567028 [](ancestors = 184567028,184489272)

@AlekseyTs
Copy link
Copy Markdown
Contributor

AlekseyTs commented Apr 26, 2018

@CyrusNajmabadi

Having a language agnostic API allows us to pass this data around without having to ensure that all parts of the stack have to be agreeing on the same language. And it allows accessibility checks to work even with symbols that we are literally synthesizing out of fresh air.

I do not believe the proposed implementation is going to give you all that. Also, keep in mind that this is a public API on symbols, the fault tolerance is much lower for it and correctness and usability requirements are much higher. As long as the code is internal implementation in IDE layer, the amount of consumers "suffering" from its incorrect/unreliable behavior is relatively small, but we are about to change that, making "promises" that are not fulfilled. #Resolved

@CyrusNajmabadi
Copy link
Copy Markdown
Contributor

CyrusNajmabadi commented Apr 26, 2018

I do not believe the proposed implementation is going to give you all that.

Note, as part of the PR Neal did remove the near-equivalent version in teh IDE layer and replaced those existing calls with all the calls to his new API. So it does appear to give us this sufficient level of accessibility testing.

Therefore, it feels bad to make it appear as though the API works reliably across languages, when it doesn't.

Note: the primary use case of this API was intended to support answering these questions for cross project scenarios. That's how the IDE has been using the existing extension for many years now at this point.

Also, keep in mind that this is a public API on symbols, the fault tolerance is much lower for it and correctness and usability requirements are much higher.

I agree with this.

making "promises" that are not fulfilled.

This part i'm less sure about, but i will leave neal to answer. Specifically, the satisfactory part for me was that "It accurately reflects ... CLR accessibility." If these are the rules for CLR accessibility, it feels like they can hopefully be exposed without actually needing or depending on specific symbol implementations. If specific symbol implementations are necessary in your opinion, it would be good to understand where.

From your response on the PR one good example of a strange place is return toAssembly.GivesAccessTo(fromAssembly); It would be interesting to actually enumerate those cases and see if they can actually be written in a way that is truly symbol agnostic. If so, then we can accomplish both sets of overall goals as far as i can tell.

#Resolved

@CyrusNajmabadi
Copy link
Copy Markdown
Contributor

CyrusNajmabadi commented Apr 26, 2018

It looks like the implementation relies on symbol comparison, symbols from different languages are never equal.

That looks like a critique that doesn't torpedo the ship, but merely needs to be something addressed as part of the PR. One question is: is it fundamental to accessibility checking that one be able to do symbol comparison? Or can it be done without. If the former, this appraoch may not be viable at all. If the latter, that just indicates work to be done here to improve things.

Note: a concern of mine would be protected scenarios. That's definitely a case that seems like it could be problematic.

Some very simple scenarios, with public symbols, are likely to produce expected results, but the failures will actually come for more interesting scenarios.

IT would be interesting to enumerate those scenarios. That would help brainstorm to see if they really are deal-breakers, or if there are simple and effective solutions that could be created to validate that they are working properly. #Resolved

@AlekseyTs
Copy link
Copy Markdown
Contributor

AlekseyTs commented Apr 26, 2018

It looks like this PR lacks adequate testing and this is a show stopper on itself #Resolved

@gafter
Copy link
Copy Markdown
Member Author

gafter commented Apr 26, 2018

@AlekseyTs Not sure what you mean about testing - there is test coverage of every branch in the implementation. However, I will add a layer of testing based on mock symbols to ensure we do not depend on language-specific implementations. #Resolved

@AlekseyTs
Copy link
Copy Markdown
Contributor

AlekseyTs commented Apr 26, 2018

there is test coverage of every branch in the implementation

This on its own is not an indication of presence of adequate testing.

Here some of the things that I think should be present and is a good start:

  • All scenarios should be covered by targeted tests under Compilers.
  • All branches should be covered for all language specific symbols. Mock symbols might be useful, but this is lower in priority
  • Cross language scenarios should be covered in all possible combinations. There are three inputs, the test matrix should be covered.
  • Cross compilation scenarios should be covered when symbols are not shared between them, the same test matrix should be covered. #Resolved

@CyrusNajmabadi
Copy link
Copy Markdown
Contributor

CyrusNajmabadi commented Apr 26, 2018

Cross language scenarios should be covered in all possible combinations. There are three inputs, the test matrix should be covered.

I think this is a very good idea. At the very least it will uncover bugs/issues elsewhere that we can drive solutions for. For example, it would have found that our IAssembly symbols are not behaving as currently doc'ed.

Even if these bugs have existed before, it would still be good to use this time as an opportunity to identify them and squash them so that we can have a veyr high quality implementation that people can know they can depend on.

Note: we can discuss the necessity of fixing any bugs we find in other areas of the codebase if/when we find anything. It could be acceptable to just file bugs on those parts and accept that those bugs have leaked into higher level APIs with the expectation that they could be fixed at some later point in time. But it would still be good to know about them and have a good test suite that will appropriately hit those codepaths. #Resolved

@DustinCampbell DustinCampbell self-requested a review April 26, 2018 19:45
bool sameNamespace(INamespaceSymbol n1, INamespaceSymbol n2)
{
Debug.Assert(n1 != null);
// Note that the same symbol is expected to have the identical name as itself, despite VB language rules.
Copy link
Copy Markdown
Contributor

@AlekseyTs AlekseyTs Apr 30, 2018

Choose a reason for hiding this comment

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

// Note that the same symbol is expected to have the identical name as itself, despite VB language rules. [](start = 20, length = 105)

I do not think this is going to work well for cross language queries, and probably even for VB to VB comparison across different compilation. Not to mention that it might probably produce false positives as well. #Resolved

Copy link
Copy Markdown
Member Author

@gafter gafter May 1, 2018

Choose a reason for hiding this comment

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

Your comment is too vague to be actionable. Please identify specific scenarios that you do not think will work well for cross-language queries. Please identify specific scenarios that you don't think will work well for VB to VB comparison across different compilations. Please identify specific scenarios that you believe might probably produce false positives. #Resolved

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Your comment is too vague to be actionable. Please identify specific scenarios that you do not think will work well for cross-language queries. Please identify specific scenarios that you don't think will work well for VB to VB comparison across different compilations. Please identify specific scenarios that you believe might probably produce false positives.

Clearly you already identified the inaccuracy of this check, hence the comment. VB namespaces are merged case-insensitively, it is obvious that the name of the namespace doesn't always reflect the actual casing of the name, therefore this comparison could produce both false negatives and false positives as the result. Since you are the author of the change, I leave it to you to come up with relevant unit-tests to demonstrate that. If you have trouble with those, I can help you offline with constructing scenarios.


In reply to: 185161899 [](ancestors = 185161899)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Added a doc comment reflecting that, a test showing that, and filed an issue for the VB compiler not obeying the contract of MetadataName.


In reply to: 185126619 [](ancestors = 185126619)

// We expect a given named type definition to satisfy reference identity.
// But we relax this to permit a type to be represented by multiple symbols (e.g. separate compilations).
// Note that the same symbol is expected to have the identical name as itself, despite VB language rules.
return t1 == t2 || t2 != null && t1.Name == t2.Name && t1.Arity == t2.Arity && sameOriginalSymbol(t1.ContainingSymbol, t2.ContainingSymbol);
Copy link
Copy Markdown
Member Author

@gafter gafter May 1, 2018

Choose a reason for hiding this comment

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

t1.Name == t2.Name && t1.Arity == t2.Arity [](start = 49, length = 42)

To match the CLR rules, it is possible this should be t1.MetadataName == t2.MetadataName. #Resolved

/// an optional qualifier of type 'throughTypeOpt' to be used to resolve
/// protected access.
/// </summary>
public static bool IsAccessibleWithin(
Copy link
Copy Markdown
Contributor

@AlekseyTs AlekseyTs May 1, 2018

Choose a reason for hiding this comment

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

IsAccessibleWithin [](start = 27, length = 18)

Given the many "interesting" and controversial qualities of this API, I do not believe this API belongs in CodeAnalysis assembly as an extension method with such an innocent name. Compilers should not be using it under no circumstances in their internal implementation and since all symbols implement ISymbol, it is very easy to fall into this trap. Ideally this API should be pulled out into an assembly, which is not referenced by assemblies in the compiler layer, including this one. #ByDesign

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Added an Obsolete instance method to Symbol to interfere with the use of this extension method in the compilers.


In reply to: 185284396 [](ancestors = 185284396)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Added an Obsolete instance method to Symbol to interfere with the use of this extension method in the compilers.

This is not sufficient, sometimes compilers operate with interfaces


In reply to: 185310868 [](ancestors = 185310868,185284396)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I am not aware of any scenarios where the compilers use interfaces when performing access checking.


In reply to: 185311211 [](ancestors = 185311211,185310868,185284396)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I am not aware of any scenarios where the compilers use interfaces when performing access checking.

My concern was not limited to current code, and it is no addressed by your recent changes. Please do not mark this thread as resolved bases on that.


In reply to: 185311434 [](ancestors = 185311434,185311211,185310868,185284396)

public static partial class ISymbolExtensions
{
/// <summary>
/// Checks if 'symbol' is accessible from within 'within', with
Copy link
Copy Markdown
Contributor

@AlekseyTs AlekseyTs May 1, 2018

Choose a reason for hiding this comment

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

Checks if 'symbol' is accessible from within 'within', with [](start = 12, length = 59)

The doc comment sounds so innocent and doesn't provide any clues about inaccuracies built into this component and how they can affect the result. I think that at least all possibilities to get false positive and false negative results should be clearly enumerated with specific examples, etc. So that potential consumers clearly understand what they are getting into. #Resolved

Copy link
Copy Markdown
Member Author

@gafter gafter May 1, 2018

Choose a reason for hiding this comment

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

I'm not aware of any false positive or false negative scenarios. Please identify specific scenarios that you theorize could be incorrect. #Resolved

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I'm not aware of any false positive or false negative scenarios.

You are not aware or are you claiming there are not any?


In reply to: 185302839 [](ancestors = 185302839)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

The code now uses metadata name to compare names, and .Equals to compare assemblies.

Please identify specific scenarios that you theorize could be incorrect, or else stop making this claim.


In reply to: 185303824 [](ancestors = 185303824,185302839)

Copy link
Copy Markdown
Contributor

@AlekseyTs AlekseyTs left a comment

Choose a reason for hiding this comment

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

I don't believe this PR should be merged without serious offline discussion about what guarantees should be provided by this API and whether it belongs in CodeAnalysis assembly.

…nsitive languages.

Compare assemblies using .Equals to check identity.
@gafter gafter dismissed AlekseyTs’s stale review May 1, 2018 19:21

An API review team has been reviewing the design and placement of this API.

…thod

for accessibility from being used on language-specific symbols, e.g. in the compilers.
@gafter
Copy link
Copy Markdown
Member Author

gafter commented May 1, 2018

One thing I should mention is that interactive and scripting implementations relax (i.e. ignore some of) the CLR rules for access checking. This implements the rules corresponding to what the compilers do.


In reply to: 384100119 [](ancestors = 384100119)


bool sameAssembly(IAssemblySymbol a1, IAssemblySymbol a2)
{
return a1.Equals(a2);
Copy link
Copy Markdown
Contributor

@AlekseyTs AlekseyTs May 1, 2018

Choose a reason for hiding this comment

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

a1.Equals(a2) [](start = 27, length = 13)

I believe, this code will not give accurate answers across languages #Resolved

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I'll file a bug on the compilers.


In reply to: 185312547 [](ancestors = 185312547)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I'll file a bug on the compilers.

This is definitely not a bug. This is by design


In reply to: 185313035 [](ancestors = 185313035,185312547)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Filed as #26542.


In reply to: 185313534 [](ancestors = 185313534,185313035,185312547)

{
Debug.Assert(n1 != null);
// Note that the same symbol is expected to have the identical name as itself, despite VB language rules.
return n2 != null && n1.MetadataName == n2.MetadataName && sameOriginalSymbol(n1.ContainingSymbol, n2.ContainingSymbol);
Copy link
Copy Markdown
Contributor

@AlekseyTs AlekseyTs May 1, 2018

Choose a reason for hiding this comment

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

MetadataName [](start = 44, length = 12)

I don't believe the switch from Name to MetadataName makes any observable difference for namespaces. It also doesn't look like this change is backed by any targeted tests. #Resolved

Copy link
Copy Markdown
Member Author

@gafter gafter May 1, 2018

Choose a reason for hiding this comment

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

Yes, I've filed an issue against the VB compiler. #Resolved

Copy link
Copy Markdown
Member Author

@gafter gafter May 1, 2018

Choose a reason for hiding this comment

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

Adding a test for the change from Name to MetadataName, and at the same time filing #26546 for the VB issue that prevents MetadataName working as specified. #Resolved

@gafter
Copy link
Copy Markdown
Member Author

gafter commented May 1, 2018

Adding the following doc comments:

        /// <remarks>
        /// The following areas of imprecision may exist in the results of this API:
        /// <para>For assembly identity, we depend on Equals <see cref="IEquatable{T}.Equals(T)"/>/>.
        /// Assembly symbols that represent the same assembly imported into different language compilers
        /// may not compare equal, and this may prevent them from appearing to be related through
        /// this API. See https://github.com/dotnet/roslyn/issues/26542 .</para>
        /// <para>We compare <see cref="INamedTypeSymbol"/> based on the identity of the containing
        /// assembly (see above) and their metadata name, which includes the metadata name of the enclosing
        /// namespaces. Due to the behavior of the VB compiler (https://github.com/dotnet/roslyn/issues/26546)
        /// it merges namespaces when importing an assembly. Consequently, the metadata name of the namespace
        /// may not be correct for some of the contained types, and types that are distinct
        /// may appear to have the same fully-qualified name. In that case this API may treat them as the same type.</para>
        /// </remarks>

#Closed

@gafter
Copy link
Copy Markdown
Member Author

gafter commented May 1, 2018

An alternative would be to compare (original) type symbols using object.Equals() rather than comparing metadata names. That would change issue (2) in the doc comments. #WontFix

@gafter
Copy link
Copy Markdown
Member Author

gafter commented May 2, 2018

I will be changing the test for assembly identity to use IAssembly.Identity.Equals, since that is what is specified and used by the compilers and the runtime to determine when assemblies are the same. #Closed

return withinType != null && isNestedWithinOriginalCDeclaringType(withinType, declaringType.OriginalDefinition);
}

bool isNestedWithinOriginalCDeclaringType(INamedTypeSymbol type, INamedTypeSymbol originalDeclatingType)
Copy link
Copy Markdown
Member Author

@gafter gafter May 28, 2018

Choose a reason for hiding this comment

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

I think the extra C in the name of this local function is a typo. #Resolved

Also adjust comments.
@gafter
Copy link
Copy Markdown
Member Author

gafter commented May 30, 2018

API review yesterday moved IsAccessibleWithing to Compilation. That is a large change from this API, so I'll close this and reopen a new PR.

@gafter gafter closed this May 30, 2018
@gafter gafter removed 4 - In Review A fix for the issue is submitted for review. labels Dec 18, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Area-Compilers Concept-API This issue involves adding, removing, clarification, or modification of an API. Feature Request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants