• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

AndreuCodina / CrossValidation / 6144686796

11 Sep 2023 09:42AM UTC coverage: 94.616% (+1.2%) from 93.42%
6144686796

Pull #88

github

web-flow
Merge 9ca8cf464 into d3ce57897
Pull Request #88: Update problem details

454 of 502 branches covered (0.0%)

Branch coverage included in aggregate %.

71 of 71 new or added lines in 10 files covered. (100.0%)

1602 of 1671 relevant lines covered (95.87%)

168.3 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

91.27
/src/CrossValidation.SourceGenerators/ResxBusinessExceptionSourceGenerator.cs
1
using System.Collections.Immutable;
2
using System.Text;
3
using Microsoft.CodeAnalysis;
4
using Microsoft.CodeAnalysis.CSharp;
5
using Microsoft.CodeAnalysis.CSharp.Syntax;
6

7
namespace CrossValidation.SourceGenerators;
8

9
[Generator(LanguageNames.CSharp)]
10
public class ResxBusinessExceptionSourceGenerator : IIncrementalGenerator
11
{
12
    public void Initialize(IncrementalGeneratorInitializationContext context)
13
    {
14
        var businessExceptions = context.SyntaxProvider
3✔
15
            .CreateSyntaxProvider(
3✔
16
                predicate: static (syntaxNode, _) => IsTargetForGeneration(syntaxNode),
87✔
17
                transform: GetTargetForGeneration)
3✔
18
            .Where(static syntax => syntax is not null)
2✔
19
            .Select(static (syntax, _) => syntax!.Value)
1✔
20
            .Collect();
3✔
21
        context.RegisterSourceOutput(businessExceptions, Execute);
3✔
22
    }
3✔
23

24
    private static ResxBusinessExceptionInformation? GetTargetForGeneration(
25
        GeneratorSyntaxContext context,
26
        CancellationToken cancellationToken)
27
    {
28
        var classSyntax = (ClassDeclarationSyntax)context.Node;
2✔
29
        ITypeSymbol? symbol = context.SemanticModel.GetDeclaredSymbol(classSyntax);
2✔
30
        
31
        if (symbol is null)
2!
32
        {
33
            return null;
×
34
        }
35

36
        var isPartialClassDefinedSeveralTimes = symbol.DeclaringSyntaxReferences.Length > 1;
2✔
37
        
38
        if (isPartialClassDefinedSeveralTimes)
2✔
39
        {
40
            return null;
1✔
41
        }
42

43
        var attributeContainingTypeSymbol = symbol.ContainingNamespace;
1✔
44

45
        if (attributeContainingTypeSymbol is null)
1!
46
        {
47
            return null;
×
48
        }
49
        
50
        cancellationToken.ThrowIfCancellationRequested();
1✔
51
        return new ResxBusinessExceptionInformation(classSyntax, symbol);
1✔
52
    }
53

54
    private static bool IsTargetForGeneration(SyntaxNode syntaxNode)
55
    {
56
        if (syntaxNode is not ClassDeclarationSyntax classSyntax)
87✔
57
        {
58
            return false;
81✔
59
        }
60

61
        var isPartial = classSyntax.Modifiers
6✔
62
            .Any(x => x.IsKind(SyntaxKind.PartialKeyword));
17✔
63
        
64
        if (!isPartial)
6✔
65
        {
66
            return false;
1✔
67
        }
68

69
        var baseList = classSyntax.BaseList;
5✔
70

71
        if (baseList is null)
5✔
72
        {
73
            return false;
3✔
74
        }
75
        
76
        var baseIdentifierNameSyntax = (IdentifierNameSyntax)baseList.Types[0]
2✔
77
            .Type;
2✔
78
        var baseClassName = baseIdentifierNameSyntax.Identifier
2✔
79
            .Text;
2✔
80
        return baseClassName is "ResxBusinessException" or "FrontBusinessException";
2!
81
    }
82
    
83
    private static void Execute(
84
        SourceProductionContext context,
85
        ImmutableArray<ResxBusinessExceptionInformation> businessExceptions)
86
    {
87
        if (businessExceptions.IsDefaultOrEmpty)
3✔
88
        {
89
            return;
2✔
90
        }
91

92
        foreach (var businessException in businessExceptions)
4✔
93
        {
94
            var typeNamespace = businessException.Symbol.ContainingNamespace.IsGlobalNamespace
1!
95
                ? null
1✔
96
                : $"{businessException.Symbol.ContainingNamespace}.";
1✔
97
            var parentClass = GetParentClass(businessException.ClassSyntax);
1✔
98
            var classPath = GetClassPath(parentClass);
1✔
99
            var code = CreateCode(businessException, parentClass);
1✔
100
            context.AddSource($"{typeNamespace}{classPath}.{businessException.Symbol.Name}.generated.cs", code);
1✔
101
        }
102
    }
1✔
103

104
    private static string GetClassPath(ParentClassInformation? parentClass)
105
    {
106
        var classPathStringBuilder = new StringBuilder();
1✔
107
        var currentParentClass = parentClass;
1✔
108
        var isFirstClassVisited = true;
1✔
109

110
        while (currentParentClass is not null)
3✔
111
        {
112
            if (isFirstClassVisited)
2✔
113
            {
114
                isFirstClassVisited = false;
1✔
115
            }
116
            else
117
            {
118
                classPathStringBuilder.Append(".");
1✔
119
            }
120

121
            classPathStringBuilder.Append(currentParentClass.Name);
2✔
122
            currentParentClass = currentParentClass.Child;
2✔
123
        }
124
        
125
        return classPathStringBuilder.ToString();
1✔
126
    }
127

128
    private static string CreateCode(
129
        ResxBusinessExceptionInformation resxBusinessExceptionInformation,
130
        ParentClassInformation? parentClass)
131
    {
132
        var @namespace = resxBusinessExceptionInformation.Symbol.ContainingNamespace.IsGlobalNamespace
1!
133
            ? null
1✔
134
            : resxBusinessExceptionInformation.Symbol.ContainingNamespace.ToString();
1✔
135
        var modifiers = resxBusinessExceptionInformation.ClassSyntax.Modifiers.ToString();
1✔
136
        var numberOfParentClasses = 0;
1✔
137
        var constructorParameterList = resxBusinessExceptionInformation
1✔
138
            .ClassSyntax
1✔
139
            .ChildNodes()
1✔
140
            .FirstOrDefault(x => x.IsKind(SyntaxKind.ParameterList));
3✔
141
        var constructorParameterNames = constructorParameterList?.ChildNodes()
1!
142
            .Cast<ParameterSyntax>()
1✔
143
            .Select(x => x.Identifier)
3✔
144
            .Select(x => x.Text)
3✔
145
            .ToArray();
1✔
146
        var constructorParameters = constructorParameterList?.ChildNodes()
1!
147
            .Cast<ParameterSyntax>()
1✔
148
            .ToArray();
1✔
149
        var code = new StringBuilder();
1✔
150
        GenerateDirectivesStart(code);
1✔
151
        GenerateNamespacesStart(code, @namespace);
1✔
152
        GenerateParentClassesStart(code, parentClass, ref numberOfParentClasses);
1✔
153
        GenerateClassStart(code, modifiers, resxBusinessExceptionInformation);
1✔
154
        GenerateProperties(code, constructorParameters);
1✔
155
        GenerateAddParametersAsPlaceholderValuesMethod(code, constructorParameterNames);
1✔
156
        GenerateClassEnd(code);
1✔
157
        GenerateParentClassesEnd(code, numberOfParentClasses);
1✔
158
        GenerateNamespacesEnd(code, @namespace);
1✔
159
        GenerateDirectivesEnd(code);
1✔
160
        return code.ToString();
1✔
161
    }
162

163
    private static string GetClassNameWithRestrictions(ClassDeclarationSyntax classDeclarationSyntax)
164
    {
165
        return $"{classDeclarationSyntax.Identifier.ToString()}{classDeclarationSyntax.TypeParameterList} {classDeclarationSyntax.ConstraintClauses.ToString()}";
1✔
166
    }
167

168
    private static void GenerateAddParametersAsPlaceholderValuesMethod(StringBuilder code, string[]? constructorParameterNames)
169
    {
170
        if (constructorParameterNames is null)
1!
171
        {
172
            return;
×
173
        }
174
        
175
        code.AppendLine(
1✔
176
            """
1✔
177
            public override void AddParametersAsPlaceholderValues()
1✔
178
            {
1✔
179
            """);
1✔
180
        
181
        foreach (var constructorParameterName in constructorParameterNames)
8✔
182
        {
183
            code.AppendLine(
3✔
184
                $$"""
3✔
185
                  AddPlaceholderValue({{constructorParameterName}});
3✔
186
                  """);
3✔
187
        }
188

189
        code.AppendLine("}");
1✔
190
    }
1✔
191

192
    private static void GenerateClassEnd(StringBuilder code)
193
    {
194
        code.AppendLine("}");
1✔
195
    }
1✔
196

197
    private static void GenerateClassStart(
198
        StringBuilder code,
199
        string modifiers,
200
        ResxBusinessExceptionInformation resxBusinessExceptionInformation)
201
    {
202
        var classNameWithRestrictions = GetClassNameWithRestrictions(resxBusinessExceptionInformation.ClassSyntax);
1✔
203
        code.AppendLine(
1✔
204
            $$"""
1✔
205
              {{modifiers}} class {{classNameWithRestrictions}}
1✔
206
              {
1✔
207
              """);
1✔
208
    }
1✔
209

210
    private static void GenerateDirectivesEnd(StringBuilder code)
211
    {
212
        code.AppendLine("#nullable restore");
1✔
213
    }
1✔
214

215
    private static void GenerateDirectivesStart(StringBuilder code)
216
    {
217
        code.AppendLine(
1✔
218
            """
1✔
219
            // <auto-generated />
1✔
220
            #nullable enable
1✔
221
            """);
1✔
222
    }
1✔
223

224
    private static void GenerateProperties(StringBuilder code, ParameterSyntax[]? constructorParameters)
225
    {
226
        if (constructorParameters is null)
1!
227
        {
228
            return;
×
229
        }
230
        
231
        foreach (var parameter in constructorParameters)
8✔
232
        {
233
            var identifier = parameter.Identifier.ToString();
3✔
234
            code.AppendLine($"public {parameter.Type!.ToString()} {char.ToUpper(identifier[0])}{identifier.Substring(1)} => {identifier};");
3✔
235
        }
236
    }
1✔
237

238
    private static void GenerateParentClassesEnd(StringBuilder code, int numberOfParentClasses)
239
    {
240

241
        for (var i = 0; i < numberOfParentClasses; i++)
6✔
242
        {
243
            if (numberOfParentClasses > 0)
2✔
244
            {
245
                code.AppendLine();
2✔
246
            }
247
            
248
            code.AppendLine("}");
2✔
249
        }
250
    }
1✔
251

252
    private static void GenerateParentClassesStart(StringBuilder code, ParentClassInformation? parentClass,
253
        ref int numberOfParentClasses)
254
    {
255
        var parentClassIterated = parentClass;
1✔
256
        
257
        while (parentClassIterated is not null)
3✔
258
        {
259
            code.AppendLine(
2✔
260
                $$"""
2✔
261
                  {{parentClassIterated.Modifiers}} {{parentClassIterated.StructureType}} {{parentClassIterated.NameWithRestrictions}}
2✔
262
                  {
2✔
263
                  """);
2✔
264
            numberOfParentClasses++;
2✔
265
            parentClassIterated = parentClassIterated.Child;
2✔
266
        }
267
    }
1✔
268

269
    private static void GenerateNamespacesStart(StringBuilder code, string? @namespace)
270
    {
271
        if (@namespace is null)
1!
272
        {
273
            return;
×
274
        }
275

276
        code.AppendLine(
1✔
277
            $$"""
1✔
278
              namespace {{@namespace}}
1✔
279
              {
1✔
280
              """);
1✔
281
    }
1✔
282
    
283
    private static void GenerateNamespacesEnd(StringBuilder code, string? @namespace)
284
    {
285
        if (@namespace is null)
1!
286
        {
287
            return;
×
288
        }
289

290
        code.AppendLine("}");
1✔
291
    }
1✔
292
    
293
    private static ParentClassInformation? GetParentClass(BaseTypeDeclarationSyntax typeSyntax)
294
    {
295
        TypeDeclarationSyntax? parentSyntax = typeSyntax.Parent as TypeDeclarationSyntax;
1✔
296
        ParentClassInformation? parentClassInfo = null;
1✔
297

298
        while (parentSyntax != null
3!
299
               && (parentSyntax.IsKind(SyntaxKind.ClassDeclaration)
3✔
300
               || parentSyntax.IsKind(SyntaxKind.StructDeclaration)
3✔
301
               || parentSyntax.IsKind(SyntaxKind.RecordDeclaration)))
3✔
302
        {
303
            parentClassInfo = new ParentClassInformation(
2✔
304
                child: parentClassInfo,
2✔
305
                modifiers: parentSyntax.Modifiers.ToString(),
2✔
306
                structureType: parentSyntax.Keyword.ValueText,
2✔
307
                name: parentSyntax.Identifier.ToString(),
2✔
308
                genericDeclaration: parentSyntax.TypeParameterList?.ToString(),
2✔
309
                constraints: parentSyntax.ConstraintClauses.ToString());
2✔
310
            parentSyntax = parentSyntax.Parent as TypeDeclarationSyntax;
2✔
311
        }
312

313
        return parentClassInfo;
1✔
314
    }
315
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc