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

Add support for required keyword #10021

Merged
merged 5 commits into from
Jul 15, 2022
Merged
Show file tree
Hide file tree
Changes from 3 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
Original file line number Diff line number Diff line change
Expand Up @@ -805,6 +805,11 @@ public static bool HasNativeIntegerAttribute(this IEnumerable<ICustomAttribute>
return attributes.HasAttributeOfType("System.Runtime.CompilerServices.NativeIntegerAttribute");
}

public static bool HasRequiredMemberAttribute(this IEnumerable<ICustomAttribute> attributes)
{
return attributes.HasAttributeOfType("System.Runtime.CompilerServices.RequiredMemberAttribute");
}

public static string[] GetValueTupleNames(this IEnumerable<ICustomAttribute> attributes)
{
string[] names = null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -376,15 +376,18 @@ private static bool ExcludeSpecialAttribute(ICustomAttribute c)

switch (typeName)
333fred marked this conversation as resolved.
Show resolved Hide resolved
{
case "System.Runtime.CompilerServices.FixedBufferAttribute": return true;
case "System.Diagnostics.CodeAnalysis.SetsRequiredMembersAttribute": return true;
ericstj marked this conversation as resolved.
Show resolved Hide resolved
case "System.ParamArrayAttribute": return true;
case "System.Reflection.DefaultMemberAttribute": return true;
case "System.Reflection.AssemblyKeyFileAttribute": return true;
case "System.Reflection.AssemblyDelaySignAttribute": return true;
case "System.Runtime.CompilerServices.ExtensionAttribute": return true;
case "System.Reflection.AssemblyKeyFileAttribute": return true;
case "System.Reflection.DefaultMemberAttribute": return true;
case "System.Runtime.CompilerServices.CompilerFeatureRequiredAttribute": return true;
case "System.Runtime.CompilerServices.DynamicAttribute": return true;
case "System.Runtime.CompilerServices.ExtensionAttribute": return true;
case "System.Runtime.CompilerServices.FixedBufferAttribute": return true;
case "System.Runtime.CompilerServices.IsByRefLikeAttribute": return true;
case "System.Runtime.CompilerServices.IsReadOnlyAttribute": return true;
case "System.Runtime.CompilerServices.RequiredMemberAttribute": return true;
case "System.Runtime.CompilerServices.TupleElementNamesAttribute": return true;
case "System.ObsoleteAttribute":
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ private void WriteFieldDefinition(IFieldDefinition field)
WriteVisibility(field.Visibility);
WriteCustomModifiers(field.CustomModifiers);

if (field.Attributes.HasRequiredMemberAttribute())
{
WriteKeyword("required");
}

if (field.Type.IsUnsafeType())
WriteKeyword("unsafe");

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,11 @@ private void WritePropertyDefinition(IPropertyDefinition property)
WriteVisibility(property.Visibility);
}

if (property.Attributes.HasRequiredMemberAttribute())
{
WriteKeyword("required");
}

// Getter and Setter modifiers should be the same
WriteMethodModifiers(accessor);

Expand Down
7 changes: 5 additions & 2 deletions src/Microsoft.DotNet.GenFacades/GenPartialFacadeSource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

namespace Microsoft.DotNet.GenFacades
{
public class GenPartialFacadeSource : BuildTask
public class GenPartialFacadeSource : RoslynBuildTask
{
[Required]
public ITaskItem[] ReferencePaths { get; set; }
Expand All @@ -21,6 +21,8 @@ public class GenPartialFacadeSource : BuildTask

public string DefineConstants { get; set; }

public string LangVersion { get; set; }

public bool IgnoreMissingTypes { get; set; }

public string[] IgnoreMissingTypesList { get; set; }
Expand All @@ -32,7 +34,7 @@ public class GenPartialFacadeSource : BuildTask
[Required]
public string OutputSourcePath { get; set; }

public override bool Execute()
public override bool ExecuteCore()
{
bool result = true;
try
Expand All @@ -42,6 +44,7 @@ public override bool Execute()
ReferenceAssembly,
CompileFiles?.Select(item => item.ItemSpec).ToArray(),
DefineConstants,
LangVersion,
OutputSourcePath,
Log,
IgnoreMissingTypes,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ public static bool Execute(
string contractAssembly,
string[] compileFiles,
string defineConstants,
string langVersion,
string outputSourcePath,
ILog logger,
bool ignoreMissingTypes = false,
Expand Down Expand Up @@ -52,7 +53,7 @@ public static bool Execute(
referenceTypes = referenceTypes.Where(type => !OmitTypes.Contains(type));

var sourceGenerator = new SourceGenerator(referenceTypes, seedTypes, seedTypePreferences, outputSourcePath, ignoreMissingTypesList, logger);
return sourceGenerator.GenerateSource(compileFiles, ParseDefineConstants(defineConstants), ignoreMissingTypes);
return sourceGenerator.GenerateSource(compileFiles, ParseDefineConstants(defineConstants), langVersion, ignoreMissingTypes);
}

private static IEnumerable<string> ParseDefineConstants(string defineConstants)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
<ItemGroup>
<PackageReference Include="Microsoft.Build" Version="$(MicrosoftBuildVersion)" Publish="false" />
<PackageReference Include="Microsoft.Build.Tasks.Core" Version="$(MicrosoftBuildTasksCoreVersion)" Publish="false" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="$(MsbuildTaskMicrosoftCodeAnalysisCSharpVersion)" ExcludeAssets="analyzers" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="$(MsbuildTaskMicrosoftCodeAnalysisCSharpVersion)" ExcludeAssets="analyzers" Publish="false" />
</ItemGroup>

<!-- When building offline we need to bump the version of System.Reflection.Metadata that CodeAnalysis package depends on to match what the source build tarball expects. -->
Expand Down
14 changes: 11 additions & 3 deletions src/Microsoft.DotNet.GenFacades/NotSupportedAssemblyGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,19 @@ namespace Microsoft.DotNet.GenFacades
/// <summary>
/// The class generates an NotSupportedAssembly from the reference sources.
/// </summary>
public class NotSupportedAssemblyGenerator : BuildTask
public class NotSupportedAssemblyGenerator : RoslynBuildTask
{
[Required]
public ITaskItem[] SourceFiles { get; set; }

[Required]
public string Message { get; set; }

public string LangVersion { get; set; }

public string ApiExclusionListPath { get; set; }

public override bool Execute()
public override bool ExecuteCore()
{
if (SourceFiles == null || SourceFiles.Length == 0)
{
Expand Down Expand Up @@ -68,7 +70,13 @@ private void GenerateNotSupportedAssemblyForSourceFile(string sourceFile, string

try
{
syntaxTree = CSharpSyntaxTree.ParseText(File.ReadAllText(sourceFile));
LanguageVersion languageVersion = LanguageVersion.Default;
if (!String.IsNullOrEmpty(LangVersion) && !LanguageVersionFacts.TryParse(LangVersion, out languageVersion))
{
Log.LogError($"Invalid LangVersion value '{LangVersion}'");
return;
}
syntaxTree = CSharpSyntaxTree.ParseText(File.ReadAllText(sourceFile), new CSharpParseOptions(languageVersion));
}
catch(Exception ex)
{
Expand Down
82 changes: 82 additions & 0 deletions src/Microsoft.DotNet.GenFacades/RoslynBuildTask.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
using System;
using System.IO;
using System.Reflection;
#if NETCOREAPP
using System.Runtime.Loader;
#endif

namespace Microsoft.DotNet.Build.Tasks
{
public abstract partial class RoslynBuildTask : BuildTask
{
[Required]
public string RoslynAssembliesPath { get; set; }

public override bool Execute()
{
#if NETCOREAPP
AssemblyLoadContext currentContext = AssemblyLoadContext.GetLoadContext(Assembly.GetExecutingAssembly())!;
currentContext.Resolving += ResolverForRoslyn;
#else
AppDomain.CurrentDomain.AssemblyResolve += ResolverForRoslyn;
#endif
try
{
return ExecuteCore();
}
finally
{
#if NETCOREAPP
currentContext.Resolving -= ResolverForRoslyn;
#else
AppDomain.CurrentDomain.AssemblyResolve -= ResolverForRoslyn;
#endif
}
}

public abstract bool ExecuteCore();

#if NETCOREAPP
private Assembly ResolverForRoslyn(AssemblyLoadContext context, AssemblyName assemblyName)
{
return LoadRoslyn(assemblyName, path => context.LoadFromAssemblyPath(path));
}
#else
private Assembly ResolverForRoslyn(object sender, ResolveEventArgs args)
{
AssemblyName name = new(args.Name);
return LoadRoslyn(name, path => Assembly.LoadFrom(path));
}
#endif

private Assembly LoadRoslyn(AssemblyName name, Func<string, Assembly> loadFromPath)
{
const string codeAnalysisName = "Microsoft.CodeAnalysis";
const string codeAnalysisCsharpName = "Microsoft.CodeAnalysis.CSharp";
if (name.Name == codeAnalysisName || name.Name == codeAnalysisCsharpName)
{
Assembly asm = loadFromPath(Path.Combine(RoslynAssembliesPath!, $"{name.Name}.dll"));
Version resolvedVersion = asm.GetName().Version;
if (resolvedVersion < name.Version)
{
throw new Exception($"The minimum version required of Roslyn is '{name.Version}' and you are using '{resolvedVersion}' version of the Roslyn. You can update the sdk to get the latest version.");
}

// Being extra defensive but we want to avoid that we accidentally load two different versions of either
// of the roslyn assemblies from a different location, so let's load them both on the first request.
Assembly _ = name.Name == codeAnalysisName ?
loadFromPath(Path.Combine(RoslynAssembliesPath!, $"{codeAnalysisCsharpName}.dll")) :
loadFromPath(Path.Combine(RoslynAssembliesPath!, $"{codeAnalysisName}.dll"));

return asm;
}

return null;
}
}
}
3 changes: 2 additions & 1 deletion src/Microsoft.DotNet.GenFacades/SourceGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ ILog logger
public bool GenerateSource(
IEnumerable<string> compileFiles,
IEnumerable<string> constants,
string langVersion,
bool ignoreMissingTypes)
{
List<string> externAliases = new List<string>();
Expand All @@ -51,7 +52,7 @@ public bool GenerateSource(

bool result = true;

HashSet<string> existingTypes = compileFiles != null ? TypeParser.GetAllPublicTypes(compileFiles, constants) : null;
HashSet<string> existingTypes = compileFiles != null ? TypeParser.GetAllPublicTypes(compileFiles, constants, langVersion) : null;
IEnumerable<string> typesToForward = compileFiles == null ? _referenceTypes : _referenceTypes.Where(id => !existingTypes.Contains(id));

foreach (string type in typesToForward.OrderBy(s => s))
Expand Down
16 changes: 12 additions & 4 deletions src/Microsoft.DotNet.GenFacades/TypeParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
Expand All @@ -12,10 +13,17 @@ namespace Microsoft.DotNet.GenFacades
{
internal class TypeParser
{
public static HashSet<string> GetAllPublicTypes(IEnumerable<string> files, IEnumerable<string> constants)
public static HashSet<string> GetAllPublicTypes(IEnumerable<string> files, IEnumerable<string> constants, string langVersion)
{
HashSet<string> types = new HashSet<string>();
var syntaxTreeCollection = GetSourceTrees(files, constants);

LanguageVersion languageVersion = LanguageVersion.Default;
if (!string.IsNullOrEmpty(langVersion) && !LanguageVersionFacts.TryParse(langVersion, out languageVersion))
{
throw new ArgumentException($"Invalid C# language version value '{langVersion}'", nameof(langVersion));
}

var syntaxTreeCollection = GetSourceTrees(files, constants, languageVersion);

foreach (var tree in syntaxTreeCollection)
{
Expand Down Expand Up @@ -122,9 +130,9 @@ private static string GetNamespaceName(NamespaceDeclarationSyntax namespaceSynta
return namespaceSyntax.Name.ToFullString().Trim();
}

private static IEnumerable<SyntaxTree> GetSourceTrees(IEnumerable<string> sourceFiles, IEnumerable<string> constants)
private static IEnumerable<SyntaxTree> GetSourceTrees(IEnumerable<string> sourceFiles, IEnumerable<string> constants, LanguageVersion languageVersion)
{
CSharpParseOptions options = new CSharpParseOptions().WithPreprocessorSymbols(constants);
CSharpParseOptions options = new CSharpParseOptions(languageVersion: languageVersion, preprocessorSymbols: constants);
List<SyntaxTree> result = new List<SyntaxTree>();
foreach (string sourceFile in sourceFiles)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,17 @@
<GenFacadesTargetAssemblyPath Condition="'$(MSBuildRuntimeType)' != 'Core'">$(MSBuildThisFileDirectory)..\tools\net472\Microsoft.DotNet.GenFacades.dll</GenFacadesTargetAssemblyPath>
</PropertyGroup>

<Target Name="_GetRoslynAssembliesPath">
<PropertyGroup Condition="'$(RoslynAssembliesPath)' == ''">
<RoslynAssembliesPath>$(RoslynTargetsPath)</RoslynAssembliesPath>
<_packageReferenceList>@(PackageReference)</_packageReferenceList>
<!-- CSharpCoreTargetsPath and VisualBasicCoreTargetsPath point to the same location, Microsoft.CodeAnalysis.CSharp and Microsoft.CodeAnalysis.VisualBasic
are on the same directory as Microsoft.CodeAnalysis. So there is no need to distinguish between csproj or vbproj. -->
<RoslynAssembliesPath Condition="$(_packageReferenceList.Contains('Microsoft.Net.Compilers.Toolset'))">$([System.IO.Path]::GetDirectoryName($(CSharpCoreTargetsPath)))</RoslynAssembliesPath>
<RoslynAssembliesPath Condition="'$(MSBuildRuntimeType)' == 'Core'">$([System.IO.Path]::Combine('$(RoslynAssembliesPath)', bincore))</RoslynAssembliesPath>
</PropertyGroup>
</Target>

<Import Project="Microsoft.DotNet.GenPartialFacadeSource.targets" Condition="'$(IsPartialFacadeAssembly)' == 'true'" />
<Import Project="Microsoft.DotNet.GenFacadesNotSupported.targets" Condition="'$(GeneratePlatformNotSupportedAssembly)' == 'true' or
'$(GeneratePlatformNotSupportedAssemblyMessage)' != ''" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,16 +47,18 @@
</Target>

<Target Name="AddGenFacadeNotSupportedCompileItem"
DependsOnTargets="_GetGenFacadeNotSupportedCompileInputs"
DependsOnTargets="_GetGenFacadeNotSupportedCompileInputs;_GetRoslynAssembliesPath"
AfterTargets="ResolveProjectReferences"
Condition="'$(GeneratePlatformNotSupportedAssemblyMessage)' != ''"
Inputs="@(GenFacadeNotSupportedCompileInput)"
Outputs="@(GenFacadeNotSupportedCompileInput->'%(OutputPath)')" >

<NotSupportedAssemblyGenerator
SourceFiles="@(GenFacadeNotSupportedCompileInput)"
LangVersion="$(LangVersion)"
Message="$(GeneratePlatformNotSupportedAssemblyMessage)"
ApiExclusionListPath="$(ApiExclusionListPath)" />
ApiExclusionListPath="$(ApiExclusionListPath)"
RoslynAssembliesPath="$(RoslynAssembliesPath)" />

<ItemGroup>
<Compile Include="@(GenFacadeNotSupportedCompileInput->'%(OutputPath)')" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
<GenFacadesReferencePathItemName Condition="'$(GenFacadesReferencePathItemName)' == ''">ReferencePath</GenFacadesReferencePathItemName>
<!-- Tell ResolveMatchingContract to run and resolve contract to project reference -->
<ResolveMatchingContract>true</ResolveMatchingContract>
<GeneratePartialFacadeSourceDependsOn>$(GeneratePartialFacadeSourceDependsOn);ResolveMatchingContract</GeneratePartialFacadeSourceDependsOn>
<GeneratePartialFacadeSourceDependsOn>$(GeneratePartialFacadeSourceDependsOn);ResolveMatchingContract;_GetRoslynAssembliesPath</GeneratePartialFacadeSourceDependsOn>
<CoreCompileDependsOn Condition="'$(DesignTimeBuild)' != 'true'">$(CoreCompileDependsOn);GeneratePartialFacadeSource</CoreCompileDependsOn>
<!-- Support zero version seeds by rewriting the output of the compiler. -->
<TargetsTriggeredByCompilation Condition="'$(DesignTimeBuild)' != 'true' and '$(GenFacadesForceZeroVersionSeeds)' == 'true'">
Expand All @@ -30,11 +30,13 @@
ReferenceAssembly="$(ReferenceAssembly)"
CompileFiles="@(Compile)"
DefineConstants="$(DefineConstants)"
LangVersion="$(LangVersion)"
IgnoreMissingTypes="$(GenFacadesIgnoreMissingTypes)"
IgnoreMissingTypesList="@(GenFacadesIgnoreMissingType)"
OmitTypes="@(GenFacadesOmitType)"
OutputSourcePath="$(OutputSourcePath)"
SeedTypePreferences="@(SeedTypePreference)" />
SeedTypePreferences="@(SeedTypePreference)"
RoslynAssembliesPath="$(RoslynAssembliesPath)" />

<ItemGroup Condition="Exists('$(OutputSourcePath)')">
<Compile Include="$(OutputSourcePath)" />
Expand Down