1using Microsoft.CodeAnalysis;
2using Microsoft.CodeAnalysis.CSharp.Syntax;
3using Microsoft.CodeAnalysis.Text;
5using System.Collections.Generic;
6using System.Collections.Immutable;
28 public void Initialize(IncrementalGeneratorInitializationContext context)
30 IncrementalValueProvider<AttributeSymbolSet> attributeSymbolsProvider =
31 context.CompilationProvider.Select(
37 IncrementalValueProvider<ImmutableArray<AutoWiringCandidate>> candidatesProvider =
38 context.SyntaxProvider
39 .CreateSyntaxProvider(
41 transform:
static (syntaxContext, _) => syntaxContext)
42 .Combine(attributeSymbolsProvider)
44 .Where(
static candidate => candidate is not
null)
45 .Select(
static (candidate, _) => candidate!)
48 context.RegisterSourceOutput(candidatesProvider,
static (sourceProductionContext, candidates) =>
50 Emit(sourceProductionContext, candidates);
61 if (node is not VariableDeclaratorSyntax variable)
66 if (variable.Parent is not VariableDeclarationSyntax variableDeclaration)
71 if (variableDeclaration.Parent is not FieldDeclarationSyntax fieldDeclaration)
76 return fieldDeclaration.AttributeLists.Count > 0;
86 GeneratorSyntaxContext context,
94 if (context.Node is not VariableDeclaratorSyntax variable)
99 if (context.SemanticModel.GetDeclaredSymbol(variable) is not IFieldSymbol fieldSymbol)
104 if (fieldSymbol.ContainingType is
null)
110 if (fieldSymbol.ContainingType.ContainingType is not
null)
115 AttributeData? matchedAttribute;
117 if (kind is
null || matchedAttribute is
null)
125 if (
string.IsNullOrWhiteSpace(generatedPropertyName))
132 fieldSymbol.ContainingType,
134 generatedPropertyName!);
143 SourceProductionContext context,
144 ImmutableArray<AutoWiringCandidate> candidates)
146 if (candidates.IsDefaultOrEmpty)
161 context.AddSource(fileName, SourceText.From(source, Encoding.UTF8));
173 IFieldSymbol fieldSymbol,
175 out AttributeData? matchedAttribute)
177 foreach (AttributeData attribute
in fieldSymbol.GetAttributes())
179 INamedTypeSymbol? attributeClass = attribute.AttributeClass;
180 if (attributeClass is
null)
185 if (SymbolEqualityComparer.Default.Equals(attributeClass, attributeSymbols.ModelAttribute))
187 matchedAttribute = attribute;
191 if (SymbolEqualityComparer.Default.Equals(attributeClass, attributeSymbols.EventAttribute))
193 matchedAttribute = attribute;
197 if (SymbolEqualityComparer.Default.Equals(attributeClass, attributeSymbols.PropertyAttribute))
199 matchedAttribute = attribute;
204 matchedAttribute =
null;
215 foreach (KeyValuePair<string, TypedConstant> namedArgument
in attribute.NamedArguments)
217 if (namedArgument.Key ==
"PropertyName" &&
218 namedArgument.Value.Value is
string namedValue &&
219 !
string.IsNullOrWhiteSpace(namedValue))
225 if (attribute.ConstructorArguments.Length > 0 &&
226 attribute.ConstructorArguments[0].Value is
string ctorValue &&
227 !
string.IsNullOrWhiteSpace(ctorValue))
243 if (!
string.IsNullOrWhiteSpace(configuredPropertyName))
245 return configuredPropertyName;
248 string normalized = fieldName.TrimStart(
'_');
249 if (
string.IsNullOrWhiteSpace(normalized))
254 if (normalized.Length == 1)
256 return normalized.ToUpperInvariant();
259 return char.ToUpperInvariant(normalized[0]) + normalized.Substring(1);
270 return typeSymbol.GetMembers(memberName).Length > 0;
280 string namespaceName = candidate.ContainingType.ContainingNamespace.IsGlobalNamespace
284 return namespaceName +
"_" +
285 candidate.ContainingType.Name +
"_" +
286 candidate.GeneratedPropertyName +
"_AutoWiring.g.cs";
296 string namespaceName = candidate.ContainingType.ContainingNamespace.IsGlobalNamespace
301 string typeName = candidate.
FieldSymbol.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
305 StringBuilder builder =
new StringBuilder();
306 builder.AppendLine(
"// <auto-generated />");
307 builder.AppendLine(
"#nullable enable");
308 builder.AppendLine(
"using Dreamine.MVVM.Core;");
310 if (!
string.IsNullOrWhiteSpace(namespaceName))
312 builder.AppendLine(
"namespace " + namespaceName);
313 builder.AppendLine(
"{");
316 builder.AppendLine(
" public partial class " + className);
317 builder.AppendLine(
" {");
321 builder.AppendLine(
" }");
323 if (!
string.IsNullOrWhiteSpace(namespaceName))
325 builder.AppendLine(
"}");
328 return builder.ToString();
341 StringBuilder builder,
343 IFieldSymbol fieldSymbol,
351 builder.AppendLine(
" /// <summary>");
352 builder.AppendLine(
" /// 생성된 프로퍼티입니다.");
353 builder.AppendLine(
" /// </summary>");
354 builder.AppendLine(
" public " + typeName +
" " + propertyName);
355 builder.AppendLine(
" {");
356 builder.AppendLine(
" get => " + fieldName +
";");
357 builder.AppendLine(
" set => SetProperty(ref " + fieldName +
", value);");
358 builder.AppendLine(
" }");
368 "new " + typeName +
"()");
378 "DMContainer.Resolve<" + typeName +
">()");
393 StringBuilder builder,
394 IFieldSymbol fieldSymbol,
398 string initializerExpression)
400 builder.AppendLine(
" /// <summary>");
401 builder.AppendLine(
" /// 생성된 참조 프로퍼티입니다.");
402 builder.AppendLine(
" /// </summary>");
404 if (fieldSymbol.IsReadOnly || fieldSymbol.Type.IsValueType)
406 builder.AppendLine(
" public " + typeName +
" " + propertyName +
" => " + fieldName +
";");
410 builder.AppendLine(
" public " + typeName +
" " + propertyName);
411 builder.AppendLine(
" {");
412 builder.AppendLine(
" get");
413 builder.AppendLine(
" {");
414 builder.AppendLine(
" if (" + fieldName +
" is null)");
415 builder.AppendLine(
" {");
416 builder.AppendLine(
" " + fieldName +
" = " + initializerExpression +
";");
417 builder.AppendLine(
" }");
418 builder.AppendLine();
419 builder.AppendLine(
" return " + fieldName +
";");
420 builder.AppendLine(
" }");
421 builder.AppendLine(
" }");
431 return name.Replace(
'.',
'_').Replace(
'+',
'_');
456 INamedTypeSymbol? modelAttribute,
457 INamedTypeSymbol? eventAttribute,
458 INamedTypeSymbol? propertyAttribute)
507 IFieldSymbol fieldSymbol,
508 INamedTypeSymbol containingType,
510 string generatedPropertyName)
DreamineModelAttribute, DreamineEventAttribute, DreaminePropertyAttribute가 적용된 필드를 기반으로 보조 프로퍼티 코드를 생...
static void Emit(SourceProductionContext context, ImmutableArray< AutoWiringCandidate > candidates)
수집된 후보를 기반으로 소스를 생성합니다.
static string BuildSource(AutoWiringCandidate candidate)
생성 코드를 만듭니다.
const string EventAttributeMetadataName
CandidateKind
생성 대상 종류를 나타냅니다.
static bool IsCandidateSyntax(SyntaxNode node)
후보가 될 수 있는 구문인지 확인합니다.
const string PropertyAttributeMetadataName
static void AppendPropertyCode(StringBuilder builder, CandidateKind kind, IFieldSymbol fieldSymbol, string typeName, string fieldName, string propertyName)
종류에 맞는 프로퍼티 코드를 생성합니다.
void Initialize(IncrementalGeneratorInitializationContext context)
증분 생성기 파이프라인을 초기화합니다.
static string BuildFileName(AutoWiringCandidate candidate)
생성 파일 이름을 만듭니다.
static bool HasConflictingMember(INamedTypeSymbol typeSymbol, string memberName)
이미 같은 이름의 멤버가 존재하는지 확인합니다.
static ? AutoWiringCandidate TryCreateCandidate(GeneratorSyntaxContext context, AttributeSymbolSet attributeSymbols)
구문/시맨틱 정보를 바탕으로 생성 대상 후보를 만듭니다.
static string Sanitize(string name)
파일 이름에 안전한 문자열로 변환합니다.
static void AppendLazyAccessPropertyCode(StringBuilder builder, IFieldSymbol fieldSymbol, string typeName, string fieldName, string propertyName, string initializerExpression)
지연 초기화 기반 읽기 전용 프로퍼티 코드를 생성합니다.
const string ModelAttributeMetadataName
static ? string ResolveGeneratedPropertyName(string fieldName, string? configuredPropertyName)
생성할 프로퍼티 이름을 결정합니다.
static ? CandidateKind GetCandidateKind(IFieldSymbol fieldSymbol, AttributeSymbolSet attributeSymbols, out AttributeData? matchedAttribute)
필드에 적용된 Attribute 종류를 판별합니다.
static ? string GetConfiguredPropertyName(AttributeData attribute)
Attribute에 지정된 명시적 프로퍼티 이름을 가져옵니다.
INamedTypeSymbol? ModelAttribute
Model Attribute 심볼을 가져옵니다.
INamedTypeSymbol? EventAttribute
Event Attribute 심볼을 가져옵니다.
AttributeSymbolSet(INamedTypeSymbol? modelAttribute, INamedTypeSymbol? eventAttribute, INamedTypeSymbol? propertyAttribute)
AttributeSymbolSet 클래스의 새 인스턴스를 초기화합니다.
INamedTypeSymbol? PropertyAttribute
Property Attribute 심볼을 가져옵니다.
bool IsIncomplete
필수 심볼이 모두 준비되었는지 여부를 가져옵니다.
자동 생성 대상 필드 메타데이터를 나타냅니다.
INamedTypeSymbol ContainingType
필드를 포함하는 타입을 가져옵니다.
IFieldSymbol FieldSymbol
대상 필드 심볼을 가져옵니다.
string GeneratedPropertyName
생성할 프로퍼티 이름을 가져옵니다.
CandidateKind Kind
후보 종류를 가져옵니다.
AutoWiringCandidate(IFieldSymbol fieldSymbol, INamedTypeSymbol containingType, CandidateKind kind, string generatedPropertyName)
AutoWiringCandidate 클래스의 새 인스턴스를 초기화합니다.