Skip to content

Commit bfab785

Browse files
author
pmosk
committed
Create first version
1 parent 52b2ff5 commit bfab785

32 files changed

+916
-0
lines changed

.github/workflows/dotnet.yml

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
name: .NET
2+
3+
on:
4+
push:
5+
branches: [ main, dev, feature/*, fix/*, release/* ]
6+
7+
pull_request:
8+
branches: [ main ]
9+
10+
release:
11+
types: [ published ]
12+
13+
jobs:
14+
build:
15+
16+
runs-on: ubuntu-latest
17+
18+
steps:
19+
- name: Checkout repository
20+
uses: actions/checkout@v4
21+
22+
- name: Setup .NET
23+
uses: actions/setup-dotnet@v4
24+
with:
25+
dotnet-version: |
26+
8.0.x
27+
28+
# Create Local NuGet Source
29+
30+
- name: Create Local NuGet Directory
31+
run: mkdir ~/nuget
32+
33+
- name: Add Local Nuget Source
34+
run: dotnet nuget add source ~/nuget
35+
36+
# CodeAnalysis.Extensions
37+
38+
- name: Restore CodeAnalysis.Extensions
39+
run: dotnet restore ./src/*/CodeAnalysis.Extensions.csproj
40+
41+
- name: Build CodeAnalysis.Extensions
42+
run: dotnet build ./src/*/CodeAnalysis.Extensions.csproj --no-restore -c Release
43+
44+
- name: Pack CodeAnalysis.Extensions
45+
run: dotnet pack ./src/*/CodeAnalysis.Extensions.csproj --no-restore -o ~/nuget -c Release
46+
47+
# CodeAnalysis.SourceBuilder
48+
49+
- name: Restore CodeAnalysis.SourceBuilder
50+
run: dotnet restore ./src/*/CodeAnalysis.SourceBuilder.csproj
51+
52+
- name: Build CodeAnalysis.SourceBuilder
53+
run: dotnet build ./src/*/CodeAnalysis.SourceBuilder.csproj --no-restore -c Release
54+
55+
- name: Pack CodeAnalysis.SourceBuilder
56+
run: dotnet pack ./src/*/CodeAnalysis.SourceBuilder.csproj --no-restore -o ~/nuget -c Release
57+
58+
# Push
59+
60+
- name: Push Packages
61+
if: ${{ github.event_name == 'release' }}
62+
run: >
63+
dotnet nuget push "../../../nuget/*.nupkg"
64+
-s https://api.nuget.org/v3/index.json
65+
-k ${{ secrets.NuGetSourcePassword }}
66+
--skip-duplicate

CodeAnalysis.sln

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
2+
Microsoft Visual Studio Solution File, Format Version 12.00
3+
# Visual Studio Version 17
4+
VisualStudioVersion = 17.0.31903.59
5+
MinimumVisualStudioVersion = 10.0.40219.1
6+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CodeAnalysis.Extensions", "src\CodeAnalysis.Extensions\CodeAnalysis.Extensions.csproj", "{B6C02DFB-D60E-4199-B880-F4CB6037B428}"
7+
EndProject
8+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CodeAnalysis.SourceBuilder", "src\CodeAnalysis.SourceBuilder\CodeAnalysis.SourceBuilder.csproj", "{AFF0B5A4-3339-4CA8-B875-0D66CB784005}"
9+
EndProject
10+
Global
11+
GlobalSection(SolutionConfigurationPlatforms) = preSolution
12+
Debug|Any CPU = Debug|Any CPU
13+
Release|Any CPU = Release|Any CPU
14+
EndGlobalSection
15+
GlobalSection(ProjectConfigurationPlatforms) = postSolution
16+
{B6C02DFB-D60E-4199-B880-F4CB6037B428}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
17+
{B6C02DFB-D60E-4199-B880-F4CB6037B428}.Debug|Any CPU.Build.0 = Debug|Any CPU
18+
{B6C02DFB-D60E-4199-B880-F4CB6037B428}.Release|Any CPU.ActiveCfg = Release|Any CPU
19+
{B6C02DFB-D60E-4199-B880-F4CB6037B428}.Release|Any CPU.Build.0 = Release|Any CPU
20+
{AFF0B5A4-3339-4CA8-B875-0D66CB784005}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
21+
{AFF0B5A4-3339-4CA8-B875-0D66CB784005}.Debug|Any CPU.Build.0 = Debug|Any CPU
22+
{AFF0B5A4-3339-4CA8-B875-0D66CB784005}.Release|Any CPU.ActiveCfg = Release|Any CPU
23+
{AFF0B5A4-3339-4CA8-B875-0D66CB784005}.Release|Any CPU.Build.0 = Release|Any CPU
24+
EndGlobalSection
25+
GlobalSection(SolutionProperties) = preSolution
26+
HideSolutionNode = FALSE
27+
EndGlobalSection
28+
EndGlobal
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>netstandard2.0</TargetFramework>
5+
<LangVersion>latest</LangVersion>
6+
<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>
7+
<ImplicitUsings>disable</ImplicitUsings>
8+
<Nullable>enable</Nullable>
9+
<InvariantGlobalization>true</InvariantGlobalization>
10+
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
11+
<NoWarn>$(NoWarn);IDE0130;IDE0290</NoWarn>
12+
<PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance>
13+
<PackageLicenseFile>LICENSE</PackageLicenseFile>
14+
<PackageReadmeFile>README.md</PackageReadmeFile>
15+
<PackageProjectUrl>https://github.com/pfpack/early-codeanalysis</PackageProjectUrl>
16+
<RepositoryUrl>https://github.com/pfpack/early-codeanalysis</RepositoryUrl>
17+
<Company>pfpack</Company>
18+
<Authors>Andrei Sergeev, Pavel Moskovoy</Authors>
19+
<Copyright>Copyright © 2024 Andrei Sergeev, Pavel Moskovoy</Copyright>
20+
<Description>EarlyFuncPack CodeAnalysis is a library for .NET for use in building source generators.</Description>
21+
<RootNamespace>PrimeFuncPack</RootNamespace>
22+
<AssemblyName>EarlyFuncPack.CodeAnalysis.Extensions</AssemblyName>
23+
<Version>0.0.1-build.1</Version>
24+
</PropertyGroup>
25+
26+
<ItemGroup>
27+
<None Include="..\..\LICENSE">
28+
<Pack>True</Pack>
29+
<PackagePath></PackagePath>
30+
</None>
31+
<None Include="..\..\README.md">
32+
<Pack>True</Pack>
33+
<PackagePath></PackagePath>
34+
</None>
35+
</ItemGroup>
36+
37+
<ItemGroup>
38+
<None Include="$(OutputPath)\$(AssemblyName).dll" Pack="true" PackagePath="analyzers/dotnet/cs" Visible="false" />
39+
</ItemGroup>
40+
41+
<ItemGroup>
42+
<PackageReference Include="Microsoft.CodeAnalysis" Version="4.10.0" />
43+
</ItemGroup>
44+
45+
</Project>
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
namespace PrimeFuncPack;
2+
3+
public static partial class CodeAnalysisExtensions
4+
{
5+
private const string SystemNamespace = "System";
6+
7+
private const string SystemTextJsonSerializationNamespace = "System.Text.Json.Serialization";
8+
9+
private static string InnerWithCamelCase(this string source)
10+
{
11+
if (string.IsNullOrEmpty(source))
12+
{
13+
return string.Empty;
14+
}
15+
16+
if (source.Length is 1)
17+
{
18+
return source.ToLowerInvariant();
19+
}
20+
21+
return source[0].ToString().ToLowerInvariant() + source.Substring(1);
22+
}
23+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
namespace PrimeFuncPack;
2+
3+
partial class CodeAnalysisExtensions
4+
{
5+
public static string AsStringSourceCodeOr(this string? source, string defaultSourceCode = "\"\"")
6+
=>
7+
string.IsNullOrEmpty(source) ? defaultSourceCode : InnerWrapStringSourceCode(source!);
8+
9+
public static string AsStringSourceCodeOrStringEmpty(this string? source)
10+
=>
11+
string.IsNullOrEmpty(source) ? "string.Empty" : InnerWrapStringSourceCode(source!);
12+
13+
private static string InnerWrapStringSourceCode(string source)
14+
{
15+
var encodedString = source.Replace("\"", "\\\"");
16+
return $"\"{encodedString}\"";
17+
}
18+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
using Microsoft.CodeAnalysis;
2+
3+
namespace PrimeFuncPack;
4+
5+
partial class CodeAnalysisExtensions
6+
{
7+
public static T? GetConstructorArgumentValue<T>(this AttributeData attributeData, int constructorOrder)
8+
=>
9+
(T?)attributeData.InnerGetConstructorArgumentValue(constructorOrder);
10+
11+
private static object? InnerGetConstructorArgumentValue(this AttributeData? attributeData, int constructorOrder)
12+
{
13+
if (attributeData?.ConstructorArguments.Length <= constructorOrder)
14+
{
15+
return default;
16+
}
17+
18+
return attributeData?.ConstructorArguments[constructorOrder].Value;
19+
}
20+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using Microsoft.CodeAnalysis;
5+
6+
namespace PrimeFuncPack;
7+
8+
partial class CodeAnalysisExtensions
9+
{
10+
public static T? GetNamedArgumentValue<T>(this AttributeData attributeData, string propertyName)
11+
=>
12+
(T?)attributeData.InnerGetNamedArgumentValue(propertyName);
13+
14+
private static object? InnerGetNamedArgumentValue(this AttributeData? attributeData, string propertyName)
15+
{
16+
return attributeData?.NamedArguments.FirstOrDefault(IsNameMatched).Value.Value;
17+
18+
bool IsNameMatched(KeyValuePair<string, TypedConstant> pair)
19+
=>
20+
string.Equals(pair.Key, propertyName, StringComparison.InvariantCulture);
21+
}
22+
}
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
using System;
2+
using System.Linq;
3+
using Microsoft.CodeAnalysis;
4+
5+
namespace PrimeFuncPack;
6+
7+
partial class CodeAnalysisExtensions
8+
{
9+
public static ITypeSymbol? GetCollectionTypeOrDefault(this ITypeSymbol typeSymbol)
10+
{
11+
if (typeSymbol is IArrayTypeSymbol arrayTypeSymbol)
12+
{
13+
return arrayTypeSymbol.ElementType;
14+
}
15+
16+
if (typeSymbol is not INamedTypeSymbol namedTypeSymbol)
17+
{
18+
return null;
19+
}
20+
21+
var enumerableInterface = namedTypeSymbol.AllInterfaces.FirstOrDefault(IsGenericEnumerable);
22+
if (enumerableInterface is not null)
23+
{
24+
return enumerableInterface.TypeArguments[0];
25+
}
26+
27+
return typeSymbol.GetMembers().OfType<IMethodSymbol>().Where(IsEnumeratorMethod).Select(GetEnumeratorType).FirstOrDefault(NotNull);
28+
29+
static bool IsGenericEnumerable(INamedTypeSymbol symbol)
30+
=>
31+
InnerIsType(symbol, "System.Collections.Generic", "IEnumerable") && symbol.TypeArguments.Length is 1;
32+
33+
static bool IsEnumeratorMethod(IMethodSymbol methodSymbol)
34+
=>
35+
methodSymbol.IsGenericMethod is false &&
36+
methodSymbol.Parameters.Length is 0 &&
37+
string.Equals(methodSymbol.Name, "GetEnumerator");
38+
39+
static ITypeSymbol? GetEnumeratorType(IMethodSymbol methodSymbol)
40+
=>
41+
methodSymbol.ReturnType?.InnerGetEnumeratorTypeOrDefault();
42+
43+
static bool NotNull(ITypeSymbol? typeSymbol)
44+
=>
45+
typeSymbol is not null;
46+
}
47+
48+
private static ITypeSymbol? InnerGetEnumeratorTypeOrDefault(this ITypeSymbol typeSymbol)
49+
{
50+
if (typeSymbol is not INamedTypeSymbol namedTypeSymbol)
51+
{
52+
return null;
53+
}
54+
55+
var enumeratorInterface = namedTypeSymbol.AllInterfaces.FirstOrDefault(IsGenericEnumerator);
56+
if (enumeratorInterface is not null)
57+
{
58+
return enumeratorInterface.TypeArguments[0];
59+
}
60+
61+
if (namedTypeSymbol.GetMembers().OfType<IMethodSymbol>().Any(IsMoveNextMethod) is false)
62+
{
63+
return null;
64+
}
65+
66+
return namedTypeSymbol.GetMembers().OfType<IPropertySymbol>().FirstOrDefault(IsCurrentProperty)?.Type;
67+
68+
static bool IsGenericEnumerator(INamedTypeSymbol symbol)
69+
=>
70+
InnerIsType(symbol, "System.Collections.Generic", "IEnumerator") && symbol.TypeArguments.Length is 1;
71+
72+
static bool IsMoveNextMethod(IMethodSymbol methodSymbol)
73+
=>
74+
methodSymbol.IsGenericMethod is false &&
75+
methodSymbol.Parameters.Length is 0 &&
76+
methodSymbol.ReturnType.InnerIsType(SystemNamespace, "Boolean") &&
77+
string.Equals(methodSymbol.Name, "MoveNext");
78+
79+
static bool IsCurrentProperty(IPropertySymbol propertySymbol)
80+
=>
81+
string.Equals(propertySymbol.Name, "Current", StringComparison.InvariantCulture);
82+
}
83+
}

0 commit comments

Comments
 (0)