Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions PolySharp.slnx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
<Project Path="tests/PolySharp.MinimumCSharpVersion.Tests/PolySharp.MinimumCSharpVersion.Tests.csproj" />
<Project Path="tests/PolySharp.PolySharpUseTypeAliasForUnmanagedCallersOnlyAttribute.Tests/PolySharp.PolySharpUseTypeAliasForUnmanagedCallersOnlyAttribute.Tests.csproj" />
<Project Path="tests/PolySharp.Tests.UsePublicAccessibility/PolySharp.Tests.UsePublicAccessibility.csproj" />
<Project Path="tests/PolySharp.InternalsVisibleTo.Tests/PolySharp.InternalsVisibleTo.Tests.csproj" />
<Project Path="tests/PolySharp.Tests/PolySharp.Tests.csproj" />
<Project Path="tests/PolySharp.TypeForwards.Tests/PolySharp.TypeForwards.Tests.csproj" />
</Folder>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System;
using System.Collections.Immutable;
using System.Diagnostics.CodeAnalysis;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System.IO;
using System.Reflection;

Expand Down
29 changes: 23 additions & 6 deletions src/PolySharp.SourceGenerators/Extensions/CompilationExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;

Expand Down Expand Up @@ -47,16 +43,37 @@ public static bool HasLanguageVersionAtLeastEqualTo(this Compilation compilation
/// <returns>Whether a type with the specified metadata name can be accessed from the given compilation.</returns>
public static bool HasAccessibleTypeWithMetadataName(this Compilation compilation, string fullyQualifiedMetadataName)
{
// Helper to check for accessiblity and '[Embedded]' at the same time (Roslyn doesn't check the latter here)
static bool IsSymbolAccessibleAndNotEmbedded(Compilation compilation, INamedTypeSymbol symbol)
{
// First use the built-in Roslyn check (this doesn't check for '[Embedded]').
// If the symbol is already not embedded (e.g. it's internal), stop here.
if (!compilation.IsSymbolAccessibleWithin(symbol, compilation.Assembly))
{
return false;
}

// If the symbol is defined in this same assembly, then it's considered accessible
if (SymbolEqualityComparer.Default.Equals(compilation.Assembly, symbol.ContainingAssembly))
{
return true;
}

// If the type has '[Embedded]' on it, then it is not accessible, because Roslyn
// will just ignore it completely (even if its accessiblity is 'public' or similar).
return !symbol.HasEmbeddedAttribute();
}

// If there is only a single matching symbol, check its accessibility
if (compilation.GetTypeByMetadataName(fullyQualifiedMetadataName) is INamedTypeSymbol typeSymbol)
{
return compilation.IsSymbolAccessibleWithin(typeSymbol, compilation.Assembly);
return IsSymbolAccessibleAndNotEmbedded(compilation, typeSymbol);
}

// Otherwise, check all available types
foreach (INamedTypeSymbol currentTypeSymbol in compilation.GetTypesByMetadataName(fullyQualifiedMetadataName))
{
if (compilation.IsSymbolAccessibleWithin(currentTypeSymbol, compilation.Assembly))
if (IsSymbolAccessibleAndNotEmbedded(compilation, currentTypeSymbol))
{
return true;
}
Expand Down
42 changes: 42 additions & 0 deletions src/PolySharp.SourceGenerators/Extensions/ISymbolExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
using Microsoft.CodeAnalysis;

namespace PolySharp.SourceGenerators.Extensions;

/// <summary>
/// Extensions for <see cref="ISymbol"/>.
/// </summary>
internal static class ISymbolExtensions
{
/// <summary>
/// Checks whether a type has the <c>Microsoft.CodeAnalysis.EmbeddedAttribute</c> annotation.
/// </summary>
/// <param name="symbol">The <see cref="ISymbol"/> instance to check for the attribute.</param>
/// <returns>Whether or not <paramref name="symbol"/> has the embedded attribute.</returns>
public static bool HasEmbeddedAttribute(this ISymbol symbol)
{
foreach (AttributeData attribute in symbol.GetAttributes())
{
// The '[Embedded]' attribute is special, so we just match it by name.
// There could be any number of copies of it across different assemblies.
if (attribute.AttributeClass is
{
ContainingNamespace:
{
ContainingNamespace:
{
ContainingNamespace.IsGlobalNamespace: true,
Name: "Microsoft",
},
Name: "CodeAnalysis",

},
Name: "EmbeddedAttribute",
})
{
return true;
}
}

return false;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>net472;net48;net481;netstandard2.0;netstandard2.1;net8.0;net9.0</TargetFrameworks>
<PolySharpIncludeRuntimeSupportedAttributes>true</PolySharpIncludeRuntimeSupportedAttributes>

<!-- Define a constant to change the namespace of the test files -->
<DefineConstants>$(DefineConstants);INTERNALS_VISIBLE_TO_TESTS</DefineConstants>
</PropertyGroup>

<ItemGroup>
<CompilerVisibleProperty Include="PolySharpIncludeRuntimeSupportedAttributes" />
</ItemGroup>

<ItemGroup Condition="!$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', 'net8.0'))">
<PackageReference Include="System.Memory" Version="4.5.5" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\src\PolySharp.SourceGenerators\PolySharp.SourceGenerators.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" PrivateAssets="contentfiles;build" />
<ProjectReference Include="..\PolySharp.Tests\PolySharp.Tests.csproj" />
</ItemGroup>

<ItemGroup>
<Compile Include="..\PolySharp.Tests\LanguageFeatures.cs" Link="LanguageFeatures.cs" />
<Compile Include="..\PolySharp.Tests\RuntimeSupport.cs" Link="RuntimeSupport.cs" />
</ItemGroup>

</Project>
4 changes: 4 additions & 0 deletions tests/PolySharp.Tests/LanguageFeatures.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,11 @@

#pragma warning disable CA2255

#if INTERNALS_VISIBLE_TO_TESTS
namespace PolySharp.InternalsVisbleTo.Tests;
#else
namespace PolySharp.Tests;
#endif

internal class TestClass
{
Expand Down
10 changes: 9 additions & 1 deletion tests/PolySharp.Tests/PolySharp.Tests.csproj
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>net472;net48;net481;netstandard2.0;netstandard2.1;net8.0;net9.0</TargetFrameworks>
Expand All @@ -17,4 +17,12 @@
<ProjectReference Include="..\..\src\PolySharp.SourceGenerators\PolySharp.SourceGenerators.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" PrivateAssets="contentfiles;build" />
</ItemGroup>

<!--
Declare internal accessibility for the 'PolySharp.InternalsVisibleTo.Tests' project, which
is used to validate that '[Embedded]' is correctly filtered when detecting accessible types.
-->
<ItemGroup>
<InternalsVisibleTo Include="PolySharp.InternalsVisibleTo.Tests" />
</ItemGroup>

</Project>
4 changes: 4 additions & 0 deletions tests/PolySharp.Tests/RuntimeSupport.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,11 @@

[assembly: DisableRuntimeMarshalling]

#if INTERNALS_VISIBLE_TO_TESTS
namespace PolySharp.InternalsVisbleTo.Tests;
#else
namespace PolySharp.Tests;
#endif

internal class RandomApis
{
Expand Down
Loading