Skip to content

Commit

Permalink
Implement NullableSourceGenerator
Browse files Browse the repository at this point in the history
  • Loading branch information
sharwell committed Dec 29, 2021
1 parent 79d2db2 commit 96a1bba
Show file tree
Hide file tree
Showing 6 changed files with 620 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .globalconfig
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ dotnet_diagnostic.SA1130.severity = silent
# SA1600: Elements should be documented
dotnet_diagnostic.SA1600.severity = silent

# SA1602: Enumeration items should be documented
dotnet_diagnostic.SA1602.severity = silent

# IDE0009: Member access should be qualified.
dotnet_diagnostic.IDE0009.severity = none

Expand Down
35 changes: 35 additions & 0 deletions THIRD-PARTY-NOTICES.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
Language Types uses third-party libraries or other resources that may be
distributed under licenses different than the Language Types software.

In the event that we accidentally failed to list a required notice, please
bring it to our attention by posting an issue.

The attached notices are provided for information only.


License notice for .NET Compiler Platform ("Roslyn")
----------------------------------------------------

The MIT License (MIT)

Copyright (c) .NET Foundation and Contributors

All rights reserved.

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
// Copyright (c) Tunnel Vision Laboratories, LLC. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

namespace TunnelVisionLabs.LanguageTypes.SourceGenerator
{
using Microsoft.CodeAnalysis;

internal static class CompilationExtensions
{
/// <summary>
/// Gets a type by its metadata name to use for code analysis within a <see cref="Compilation"/>. This method
/// attempts to find the "best" symbol to use for code analysis, which is the symbol matching the first of the
/// following rules.
///
/// <list type="number">
/// <item><description>
/// If only one type with the given name is found within the compilation and its referenced assemblies, that
/// type is returned regardless of accessibility.
/// </description></item>
/// <item><description>
/// If the current <paramref name="compilation"/> defines the symbol, that symbol is returned.
/// </description></item>
/// <item><description>
/// If exactly one referenced assembly defines the symbol in a manner that makes it visible to the current
/// <paramref name="compilation"/>, that symbol is returned.
/// </description></item>
/// <item><description>
/// Otherwise, this method returns <see langword="null"/>.
/// </description></item>
/// </list>
/// </summary>
/// <param name="compilation">The <see cref="Compilation"/> to consider for analysis.</param>
/// <param name="fullyQualifiedMetadataName">The fully-qualified metadata type name to find.</param>
/// <param name="requiresAccess"><see langword="true"/> to only consider accessible types; otherwise, <see langword="false"/> to consider all types.</param>
/// <returns>The symbol to use for code analysis; otherwise, <see langword="null"/>.</returns>
public static INamedTypeSymbol? GetBestTypeByMetadataName(this Compilation compilation, string fullyQualifiedMetadataName, bool requiresAccess)
{
// Try to get the unique type with this name, ignoring accessibility
var type = compilation.GetTypeByMetadataName(fullyQualifiedMetadataName);

// Otherwise, try to get the unique type with this name originally defined in 'compilation'
type ??= compilation.Assembly.GetTypeByMetadataName(fullyQualifiedMetadataName);

// Otherwise, try to get the unique accessible type with this name from a reference
if (type is null)
{
foreach (var module in compilation.Assembly.Modules)
{
foreach (var referencedAssembly in module.ReferencedAssemblySymbols)
{
var currentType = referencedAssembly.GetTypeByMetadataName(fullyQualifiedMetadataName);
if (currentType is null)
{
continue;
}

switch (currentType.GetResultantVisibility())
{
case SymbolVisibility.Public:
case SymbolVisibility.Internal when referencedAssembly.GivesAccessTo(compilation.Assembly):
break;

default:
continue;
}

if (type is object)
{
// Multiple visible types with the same metadata name are present
return null;
}

type = currentType;
}
}
}

if (requiresAccess && type is { DeclaredAccessibility: Accessibility.Internal } && !type.ContainingAssembly.GivesAccessTo(compilation.Assembly))
{
return null;
}

return type;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// Copyright (c) Tunnel Vision Laboratories, LLC. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

namespace TunnelVisionLabs.LanguageTypes.SourceGenerator
{
using Microsoft.CodeAnalysis;

internal static class ISymbolExtensions
{
public static SymbolVisibility GetResultantVisibility(this ISymbol symbol)
{
// Start by assuming it's visible.
var visibility = SymbolVisibility.Public;

switch (symbol.Kind)
{
case SymbolKind.Alias:
// Aliases are uber private. They're only visible in the same file that they
// were declared in.
return SymbolVisibility.Private;

case SymbolKind.Parameter:
// Parameters are only as visible as their containing symbol
return GetResultantVisibility(symbol.ContainingSymbol);

case SymbolKind.TypeParameter:
// Type Parameters are private.
return SymbolVisibility.Private;
}

while (symbol != null && symbol.Kind != SymbolKind.Namespace)
{
switch (symbol.DeclaredAccessibility)
{
// If we see anything private, then the symbol is private.
case Accessibility.NotApplicable:
case Accessibility.Private:
return SymbolVisibility.Private;

// If we see anything internal, then knock it down from public to
// internal.
case Accessibility.Internal:
case Accessibility.ProtectedAndInternal:
visibility = SymbolVisibility.Internal;
break;

// For anything else (Public, Protected, ProtectedOrInternal), the
// symbol stays at the level we've gotten so far.
}

symbol = symbol.ContainingSymbol;
}

return visibility;
}
}
}
Loading

0 comments on commit 96a1bba

Please sign in to comment.