Skip to content

Commit 9638a6c

Browse files
authored
Merge pull request github#14095 from hvitved/csharp/stub-generator
C#: Roslyn-based stub generation
2 parents e417e86 + 831baa8 commit 9638a6c

File tree

281 files changed

+69104
-81502
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

281 files changed

+69104
-81502
lines changed

csharp/CSharp.sln

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
2-
Microsoft Visual Studio Solution File, Format Version 12.00
1+
Microsoft Visual Studio Solution File, Format Version 12.00
32
# Visual Studio 15
43
VisualStudioVersion = 15.0.27130.2036
54
MinimumVisualStudioVersion = 10.0.40219.1
@@ -15,6 +14,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Semmle.Extraction.CSharp.De
1514
EndProject
1615
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Semmle.Extraction.CSharp.Standalone", "extractor\Semmle.Extraction.CSharp.Standalone\Semmle.Extraction.CSharp.Standalone.csproj", "{D00E7D25-0FA0-48EC-B048-CD60CE1B30D8}"
1716
EndProject
17+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Semmle.Extraction.CSharp.StubGenerator", "extractor\Semmle.Extraction.CSharp.StubGenerator\Semmle.Extraction.CSharp.StubGenerator.csproj", "{B7C9FD47-A78C-4C20-AC29-B0AE638ADE9D}"
18+
EndProject
19+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Semmle.Extraction.CSharp.Util", "extractor\Semmle.Extraction.CSharp.Util\Semmle.Extraction.CSharp.Util.csproj", "{998A0D4C-8BFC-4513-A28D-4816AFB89882}"
20+
EndProject
1821
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Semmle.Extraction.CIL.Driver", "extractor\Semmle.Extraction.CIL.Driver\Semmle.Extraction.CIL.Driver.csproj", "{EFA400B3-C1CE-446F-A4E2-8B44E61EF47C}"
1922
EndProject
2023
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Semmle.Extraction.CSharp.Driver", "extractor\Semmle.Extraction.CSharp.Driver\Semmle.Extraction.CSharp.Driver.csproj", "{C36453BF-0C82-448A-B15D-26947503A2D3}"
@@ -29,6 +32,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Semmle.Autobuild.CSharp", "
2932
EndProject
3033
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Semmle.Autobuild.CSharp.Tests", "autobuilder\Semmle.Autobuild.CSharp.Tests\Semmle.Autobuild.CSharp.Tests.csproj", "{34256E8F-866A-46C1-800E-3DF69FD1DCB7}"
3134
EndProject
35+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Semmle.Extraction.CSharp.DependencyStubGenerator", "extractor\Semmle.Extraction.CSharp.DependencyStubGenerator\Semmle.Extraction.CSharp.DependencyStubGenerator.csproj", "{0EDA21A3-ADD8-4C10-B494-58B12B526B76}"
36+
EndProject
3237
Global
3338
GlobalSection(SolutionConfigurationPlatforms) = preSolution
3439
Debug|Any CPU = Debug|Any CPU
@@ -85,6 +90,18 @@ Global
8590
{34256E8F-866A-46C1-800E-3DF69FD1DCB7}.Debug|Any CPU.Build.0 = Debug|Any CPU
8691
{34256E8F-866A-46C1-800E-3DF69FD1DCB7}.Release|Any CPU.ActiveCfg = Release|Any CPU
8792
{34256E8F-866A-46C1-800E-3DF69FD1DCB7}.Release|Any CPU.Build.0 = Release|Any CPU
93+
{B7C9FD47-A78C-4C20-AC29-B0AE638ADE9D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
94+
{B7C9FD47-A78C-4C20-AC29-B0AE638ADE9D}.Debug|Any CPU.Build.0 = Debug|Any CPU
95+
{B7C9FD47-A78C-4C20-AC29-B0AE638ADE9D}.Release|Any CPU.ActiveCfg = Release|Any CPU
96+
{B7C9FD47-A78C-4C20-AC29-B0AE638ADE9D}.Release|Any CPU.Build.0 = Release|Any CPU
97+
{998A0D4C-8BFC-4513-A28D-4816AFB89882}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
98+
{998A0D4C-8BFC-4513-A28D-4816AFB89882}.Debug|Any CPU.Build.0 = Debug|Any CPU
99+
{998A0D4C-8BFC-4513-A28D-4816AFB89882}.Release|Any CPU.ActiveCfg = Release|Any CPU
100+
{998A0D4C-8BFC-4513-A28D-4816AFB89882}.Release|Any CPU.Build.0 = Release|Any CPU
101+
{0EDA21A3-ADD8-4C10-B494-58B12B526B76}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
102+
{0EDA21A3-ADD8-4C10-B494-58B12B526B76}.Debug|Any CPU.Build.0 = Debug|Any CPU
103+
{0EDA21A3-ADD8-4C10-B494-58B12B526B76}.Release|Any CPU.ActiveCfg = Release|Any CPU
104+
{0EDA21A3-ADD8-4C10-B494-58B12B526B76}.Release|Any CPU.Build.0 = Release|Any CPU
88105
EndGlobalSection
89106
GlobalSection(SolutionProperties) = preSolution
90107
HideSolutionNode = FALSE
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
using Semmle.Extraction.CSharp.DependencyFetching;
2+
using Semmle.Extraction.CSharp.StubGenerator;
3+
using Semmle.Util.Logging;
4+
5+
var logger = new ConsoleLogger(Verbosity.Info, logThreadId: false);
6+
using var dependencyManager = new DependencyManager(".", DependencyOptions.Default, logger);
7+
StubGenerator.GenerateStubs(logger, dependencyManager.ReferenceFiles, "codeql_csharp_stubs");
8+
9+
return 0;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<OutputType>Exe</OutputType>
5+
<TargetFramework>net7.0</TargetFramework>
6+
<AssemblyName>Semmle.Extraction.CSharp.DependencyStubGenerator</AssemblyName>
7+
<RootNamespace>Semmle.Extraction.CSharp.DependencyStubGenerator</RootNamespace>
8+
<ImplicitUsings>enable</ImplicitUsings>
9+
<Nullable>enable</Nullable>
10+
</PropertyGroup>
11+
12+
<ItemGroup>
13+
<ProjectReference Include="..\Semmle.Util\Semmle.Util.csproj" />
14+
<ProjectReference Include="..\Semmle.Extraction.CSharp.DependencyFetching\Semmle.Extraction.CSharp.DependencyFetching.csproj" />
15+
<ProjectReference Include="..\Semmle.Extraction.CSharp.StubGenerator\Semmle.Extraction.CSharp.StubGenerator.csproj" />
16+
</ItemGroup>
17+
18+
</Project>
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
using System.Linq;
2+
3+
using Microsoft.CodeAnalysis;
4+
using Microsoft.CodeAnalysis.CSharp;
5+
6+
using Semmle.Util;
7+
8+
namespace Semmle.Extraction.CSharp.StubGenerator;
9+
10+
internal class RelevantSymbol
11+
{
12+
private readonly IAssemblySymbol assembly;
13+
private readonly MemoizedFunc<INamedTypeSymbol, bool> isRelevantNamedType;
14+
private readonly MemoizedFunc<INamespaceSymbol, bool> isRelevantNamespace;
15+
16+
public RelevantSymbol(IAssemblySymbol assembly)
17+
{
18+
this.assembly = assembly;
19+
20+
isRelevantNamedType = new(symbol =>
21+
StubVisitor.IsRelevantBaseType(symbol) &&
22+
SymbolEqualityComparer.Default.Equals(symbol.ContainingAssembly, assembly));
23+
24+
isRelevantNamespace = new(symbol =>
25+
symbol.GetTypeMembers().Any(IsRelevantNamedType) ||
26+
symbol.GetNamespaceMembers().Any(IsRelevantNamespace));
27+
}
28+
29+
public bool IsRelevantNamedType(INamedTypeSymbol symbol) => isRelevantNamedType.Invoke(symbol);
30+
31+
public bool IsRelevantNamespace(INamespaceSymbol symbol) => isRelevantNamespace.Invoke(symbol);
32+
}
33+
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
<PropertyGroup>
3+
<TargetFramework>net7.0</TargetFramework>
4+
<AssemblyName>Semmle.Extraction.CSharp.StubGenerator</AssemblyName>
5+
<RootNamespace>Semmle.Extraction.CSharp.StubGenerator</RootNamespace>
6+
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
7+
<RuntimeIdentifiers>win-x64;linux-x64;osx-x64</RuntimeIdentifiers>
8+
<Nullable>enable</Nullable>
9+
</PropertyGroup>
10+
<ItemGroup>
11+
<ProjectReference Include="..\Semmle.Util\Semmle.Util.csproj" />
12+
<ProjectReference Include="..\Semmle.Extraction.CSharp.DependencyFetching\Semmle.Extraction.CSharp.DependencyFetching.csproj" />
13+
<ProjectReference Include="..\Semmle.Extraction.CSharp.Util\Semmle.Extraction.CSharp.Util.csproj" />
14+
</ItemGroup>
15+
<ItemGroup>
16+
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.4.0" />
17+
</ItemGroup>
18+
</Project>
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
using System.Collections.Concurrent;
2+
using System.Collections.Generic;
3+
using System.IO;
4+
using System.Linq;
5+
using System.Text;
6+
using System.Threading.Tasks;
7+
8+
using Microsoft.CodeAnalysis;
9+
using Microsoft.CodeAnalysis.CSharp;
10+
11+
using Semmle.Util;
12+
using Semmle.Util.Logging;
13+
14+
namespace Semmle.Extraction.CSharp.StubGenerator;
15+
16+
public static class StubGenerator
17+
{
18+
/// <summary>
19+
/// Generates stubs for all the provided assembly paths.
20+
/// </summary>
21+
/// <param name="referencesPaths">The paths of the assemblies to generate stubs for.</param>
22+
/// <param name="outputPath">The path in which to store the stubs.</param>
23+
public static void GenerateStubs(ILogger logger, IEnumerable<string> referencesPaths, string outputPath)
24+
{
25+
var stopWatch = new System.Diagnostics.Stopwatch();
26+
stopWatch.Start();
27+
28+
var threads = EnvironmentVariables.GetDefaultNumberOfThreads();
29+
30+
using var references = new BlockingCollection<(MetadataReference Reference, string Path)>();
31+
32+
Parallel.ForEach(referencesPaths, new ParallelOptions { MaxDegreeOfParallelism = threads }, path =>
33+
{
34+
var reference = MetadataReference.CreateFromFile(path);
35+
references.Add((reference, path));
36+
});
37+
38+
logger.Log(Severity.Info, $"Generating stubs for {references.Count} assemblies.");
39+
40+
var compilation = CSharpCompilation.Create(
41+
"stubgenerator.dll",
42+
null,
43+
references.Select(tuple => tuple.Item1),
44+
new CSharpCompilationOptions(OutputKind.ConsoleApplication, allowUnsafe: true));
45+
46+
Parallel.ForEach(references, new ParallelOptions { MaxDegreeOfParallelism = threads }, @ref =>
47+
{
48+
StubReference(logger, compilation, outputPath, @ref.Reference, @ref.Path);
49+
});
50+
51+
stopWatch.Stop();
52+
logger.Log(Severity.Info, $"Stub generation took {stopWatch.Elapsed}.");
53+
}
54+
55+
private static void StubReference(ILogger logger, CSharpCompilation compilation, string outputPath, MetadataReference reference, string path)
56+
{
57+
if (compilation.GetAssemblyOrModuleSymbol(reference) is not IAssemblySymbol assembly)
58+
return;
59+
60+
var relevantSymbol = new RelevantSymbol(assembly);
61+
62+
if (!assembly.Modules.Any(m => relevantSymbol.IsRelevantNamespace(m.GlobalNamespace)))
63+
return;
64+
65+
using var fileStream = new FileStream(FileUtils.NestPaths(logger, outputPath, path.Replace(".dll", ".cs")), FileMode.Create, FileAccess.Write);
66+
using var writer = new StreamWriter(fileStream, new UTF8Encoding(false));
67+
68+
var visitor = new StubVisitor(writer, relevantSymbol);
69+
70+
writer.WriteLine("// This file contains auto-generated code.");
71+
writer.WriteLine($"// Generated from `{assembly.Identity}`.");
72+
73+
visitor.StubAttributes(assembly.GetAttributes(), "assembly: ");
74+
75+
foreach (var module in assembly.Modules)
76+
{
77+
module.GlobalNamespace.Accept(visitor);
78+
}
79+
}
80+
}
81+

0 commit comments

Comments
 (0)