Skip to content

Commit

Permalink
VS-106: Refactor out SyntaxElements in TemplateBuilders (#86)
Browse files Browse the repository at this point in the history
* VS-106: Refactor out SyntaxElements in TemplateBuilders

---------

Co-authored-by: Ravi Raghavan <ravi.raghavan@mongodb.com>
  • Loading branch information
BorisDog and RaviRaghavan1234 authored Aug 13, 2024
1 parent e116a40 commit 7c3cc17
Show file tree
Hide file tree
Showing 8 changed files with 74 additions and 70 deletions.
2 changes: 1 addition & 1 deletion src/MongoDB.Analyzer/AnalyzerReleases.Shipped.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ Rule ID | Category | Severity | Notes
MAPoco1001 | MongoDB.Analyzer.Poco | Info | Poco to Json [Documentation](https://www.mongodb.com/docs/mongodb-analyzer/current/rules/#mapoco1001)
MAPoco2001 | MongoDB.Analyzer.Poco | Warning | Unsupported POCO [Documentation](https://www.mongodb.com/docs/mongodb-analyzer/current/rules/#mapoco2001)

## Release 1.4.0
## Release 1.5.0

### New Rules

Expand Down
6 changes: 3 additions & 3 deletions src/MongoDB.Analyzer/Core/Builders/AnalysisCodeGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,14 @@ namespace MongoDB.Analyzer.Core.Builders;

internal static class AnalysisCodeGenerator
{
private static readonly BuildersMqlGeneratorTemplateBuilder.SyntaxElements s_mqlGeneratorSyntaxElements;
private static readonly MqlGeneratorTestMethodTemplate s_testMethodTemplate;
private static readonly CSharpParseOptions s_parseOptions;
private static readonly SyntaxTreesCache s_syntaxTreesCache;

static AnalysisCodeGenerator()
{
var mqlGeneratorSyntaxTree = GetCodeResource(ResourceNames.Builders.MqlGenerator);
s_mqlGeneratorSyntaxElements = BuildersMqlGeneratorTemplateBuilder.CreateSyntaxElements(mqlGeneratorSyntaxTree);
s_testMethodTemplate = BuildersMqlGeneratorTemplateBuilder.CreateTestMethodTemplate(mqlGeneratorSyntaxTree);

s_parseOptions = (CSharpParseOptions)mqlGeneratorSyntaxTree.Options;
s_syntaxTreesCache = new SyntaxTreesCache(s_parseOptions, ResourceNames.Builders.Renderer);
Expand Down Expand Up @@ -68,7 +68,7 @@ public static CompilationResult Compile(MongoAnalysisContext context, Expression

private static SyntaxTree GenerateMqlGeneratorSyntaxTree(ExpressionsAnalysis builderExpressionAnalysis)
{
var testCodeBuilder = new BuildersMqlGeneratorTemplateBuilder(s_mqlGeneratorSyntaxElements);
var testCodeBuilder = new BuildersMqlGeneratorTemplateBuilder(s_testMethodTemplate);
var generatedMqlMethodDeclarations = new List<MethodDeclarationSyntax>(builderExpressionAnalysis.AnalysisNodeContexts.Length);

foreach (var builderContext in builderExpressionAnalysis.AnalysisNodeContexts)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,36 +12,27 @@
// See the License for the specific language governing permissions and
// limitations under the License.

using MongoDB.Analyzer.Core.Utilities;
using static MongoDB.Analyzer.Core.HelperResources.MqlGeneratorSyntaxElements.Builders;

namespace MongoDB.Analyzer.Core.Builders;

internal sealed class BuildersMqlGeneratorTemplateBuilder
{
internal record SyntaxElements(
SyntaxNode Root,
ClassDeclarationSyntax ClassDeclarationSyntax,
MethodDeclarationSyntax TestMethodNode,
SyntaxNode BuilderDefinitionNode,
SyntaxNode CollectionTypeNode)
{
public SyntaxNode[] NodesToReplace { get; } = new[] { BuilderDefinitionNode, CollectionTypeNode };
}

private ClassDeclarationSyntax _mqlGeneratorDeclarationSyntaxNew;
private int _nextTestMethodIndex;
private readonly SyntaxElements _syntaxElements;
private readonly MqlGeneratorTestMethodTemplate _testMethodTemplate;

public BuildersMqlGeneratorTemplateBuilder(SyntaxElements syntaxElements)
public BuildersMqlGeneratorTemplateBuilder(MqlGeneratorTestMethodTemplate testMethodTemplate)
{
_syntaxElements = syntaxElements;
_mqlGeneratorDeclarationSyntaxNew = _syntaxElements.ClassDeclarationSyntax;
_testMethodTemplate = testMethodTemplate;
_mqlGeneratorDeclarationSyntaxNew = _testMethodTemplate.ClassDeclarationSyntax;
}

public void AddMqlGeneratorMethods(MemberDeclarationSyntax[] methodDeclarations) =>
_mqlGeneratorDeclarationSyntaxNew = _mqlGeneratorDeclarationSyntaxNew.AddMembers(methodDeclarations);

public static SyntaxElements CreateSyntaxElements(SyntaxTree mqlGeneratorSyntaxTree)
public static MqlGeneratorTestMethodTemplate CreateTestMethodTemplate(SyntaxTree mqlGeneratorSyntaxTree)
{
var root = mqlGeneratorSyntaxTree.GetRoot();

Expand All @@ -50,25 +41,25 @@ public static SyntaxElements CreateSyntaxElements(SyntaxTree mqlGeneratorSyntaxT
var builderDefinitionNode = mainTestMethodNode.GetSingleIdentifier(FilterName).Parent.Parent;
var collectionTypeNode = mainTestMethodNode.GetIdentifiers(MqlGeneratorTemplateType).ElementAt(0);

return new SyntaxElements(root, classDeclarationSyntax, mainTestMethodNode, builderDefinitionNode, collectionTypeNode);
return new MqlGeneratorTestMethodTemplate(root, classDeclarationSyntax, mainTestMethodNode, builderDefinitionNode, collectionTypeNode, AnalysisType.Builders);
}

public (string newMethodName, MethodDeclarationSyntax newMethodDeclaration) GenerateMqlGeneratorMethod(string typeArgumentName, SyntaxNode buildersExpression)
{
var newMethodDeclaration = _syntaxElements.TestMethodNode.ReplaceNodes(_syntaxElements.NodesToReplace, (n, _) =>
var newMethodDeclaration = _testMethodTemplate.TestMethodNode.ReplaceNodes(_testMethodTemplate.NodesToReplace, (n, _) =>
n switch
{
_ when n == _syntaxElements.BuilderDefinitionNode => buildersExpression,
_ when n == _syntaxElements.CollectionTypeNode => SyntaxFactory.IdentifierName(typeArgumentName),
_ when n == _testMethodTemplate.ExpressionNode => buildersExpression,
_ when n == _testMethodTemplate.TypeNode => SyntaxFactory.IdentifierName(typeArgumentName),
_ => throw new Exception($"Unrecognized node {n}")
});

var newMqlGeneratorMethodName = $"{_syntaxElements.TestMethodNode.Identifier.Value}_{_nextTestMethodIndex++}";
var newMqlGeneratorMethodName = $"{_testMethodTemplate.TestMethodNode.Identifier.Value}_{_nextTestMethodIndex++}";
newMethodDeclaration = newMethodDeclaration.WithIdentifier(SyntaxFactory.Identifier(newMqlGeneratorMethodName));

return (newMqlGeneratorMethodName, newMethodDeclaration);
}

public SyntaxTree GenerateSyntaxTree() =>
_syntaxElements.Root.ReplaceNode(_syntaxElements.ClassDeclarationSyntax, _mqlGeneratorDeclarationSyntaxNew).SyntaxTree;
_testMethodTemplate.Root.ReplaceNode(_testMethodTemplate.ClassDeclarationSyntax, _mqlGeneratorDeclarationSyntaxNew).SyntaxTree;
}
6 changes: 3 additions & 3 deletions src/MongoDB.Analyzer/Core/Linq/AnalysisCodeGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,13 @@ namespace MongoDB.Analyzer.Core.Linq;
internal static class AnalysisCodeGenerator
{
private static readonly SyntaxTreesCache s_syntaxTreesCache;
private static readonly LinqMqlGeneratorTemplateBuilder.SyntaxElements s_mqlGeneratorSyntaxElements;
private static readonly MqlGeneratorTestMethodTemplate s_testMethodTemplate;
private static readonly CSharpParseOptions s_parseOptions;

static AnalysisCodeGenerator()
{
var mqlGeneratorSyntaxTree = GetCodeResource(ResourceNames.Linq.MqlGenerator);
s_mqlGeneratorSyntaxElements = LinqMqlGeneratorTemplateBuilder.CreateSyntaxElements(mqlGeneratorSyntaxTree);
s_testMethodTemplate = LinqMqlGeneratorTemplateBuilder.CreateTestMethodTemplate(mqlGeneratorSyntaxTree);

s_parseOptions = (CSharpParseOptions)mqlGeneratorSyntaxTree.Options;
s_syntaxTreesCache = new SyntaxTreesCache(s_parseOptions, ResourceNames.Linq.QueryableProvider);
Expand Down Expand Up @@ -72,7 +72,7 @@ public static CompilationResult Compile(MongoAnalysisContext context, Expression

private static SyntaxTree GenerateMqlGeneratorSyntaxTree(ExpressionsAnalysis linqExpressionAnalysis)
{
var testCodeBuilder = new LinqMqlGeneratorTemplateBuilder(s_mqlGeneratorSyntaxElements);
var testCodeBuilder = new LinqMqlGeneratorTemplateBuilder(s_testMethodTemplate);
var generatedMqlMethodDeclarations = new List<MethodDeclarationSyntax>(linqExpressionAnalysis.AnalysisNodeContexts.Length);

foreach (var linqContext in linqExpressionAnalysis.AnalysisNodeContexts)
Expand Down
33 changes: 12 additions & 21 deletions src/MongoDB.Analyzer/Core/Linq/LinqMqlGeneratorTemplateBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,36 +12,27 @@
// See the License for the specific language governing permissions and
// limitations under the License.

using MongoDB.Analyzer.Core.Utilities;
using static MongoDB.Analyzer.Core.HelperResources.MqlGeneratorSyntaxElements.Linq;

namespace MongoDB.Analyzer.Core.Linq;

internal sealed class LinqMqlGeneratorTemplateBuilder
{
internal record SyntaxElements(
SyntaxNode Root,
ClassDeclarationSyntax ClassDeclarationSyntax,
MethodDeclarationSyntax TestMethodNode,
SyntaxNode LinqExpressionNode,
SyntaxNode QueryableTypeNode)
{
public SyntaxNode[] NodesToReplace { get; } = new[] { LinqExpressionNode, QueryableTypeNode };
}

private readonly SyntaxElements _syntaxElements;
private readonly MqlGeneratorTestMethodTemplate _testMethodTemplate;
private ClassDeclarationSyntax _mqlGeneratorDeclarationSyntaxNew;
private int _nextTestMethodIndex;

public LinqMqlGeneratorTemplateBuilder(SyntaxElements syntaxElements)
public LinqMqlGeneratorTemplateBuilder(MqlGeneratorTestMethodTemplate testMethodTemplate)
{
_syntaxElements = syntaxElements;
_mqlGeneratorDeclarationSyntaxNew = _syntaxElements.ClassDeclarationSyntax;
_testMethodTemplate = testMethodTemplate;
_mqlGeneratorDeclarationSyntaxNew = _testMethodTemplate.ClassDeclarationSyntax;
}

public void AddMqlGeneratorMethods(MemberDeclarationSyntax[] methodDeclarations) =>
_mqlGeneratorDeclarationSyntaxNew = _mqlGeneratorDeclarationSyntaxNew.AddMembers(methodDeclarations);

public static SyntaxElements CreateSyntaxElements(SyntaxTree mqlGeneratorSyntaxTree)
public static MqlGeneratorTestMethodTemplate CreateTestMethodTemplate(SyntaxTree mqlGeneratorSyntaxTree)
{
var root = mqlGeneratorSyntaxTree.GetRoot();

Expand All @@ -50,25 +41,25 @@ public static SyntaxElements CreateSyntaxElements(SyntaxTree mqlGeneratorSyntaxT
var queryableTypeNode = mainTestMethodNode.GetSingleIdentifier(MqlGeneratorTemplateType);
var linqExpressionNode = mainTestMethodNode.GetSingleIdentifier(LinqMethodName).Parent.Parent;

return new SyntaxElements(root, classDeclarationSyntax, mainTestMethodNode, linqExpressionNode, queryableTypeNode);
return new MqlGeneratorTestMethodTemplate(root, classDeclarationSyntax, mainTestMethodNode, linqExpressionNode, queryableTypeNode, AnalysisType.Linq);
}

public (string newMethodName, MethodDeclarationSyntax newMethodDeclaration) GenerateMqlGeneratorMethod(string collectionTypeName, SyntaxNode linqExpression)
{
var newMethodDeclaration = _syntaxElements.TestMethodNode.ReplaceNodes(_syntaxElements.NodesToReplace, (n, _) =>
var newMethodDeclaration = _testMethodTemplate.TestMethodNode.ReplaceNodes(_testMethodTemplate.NodesToReplace, (n, _) =>
n.Kind() switch
{
_ when n == _syntaxElements.LinqExpressionNode => linqExpression,
_ when n == _syntaxElements.QueryableTypeNode => SyntaxFactory.IdentifierName(collectionTypeName),
_ when n == _testMethodTemplate.ExpressionNode => linqExpression,
_ when n == _testMethodTemplate.TypeNode => SyntaxFactory.IdentifierName(collectionTypeName),
_ => throw new Exception($"Unrecognized node {n}")
});

var newMqlGeneratorMethodName = $"{_syntaxElements.TestMethodNode.Identifier.Value}_{_nextTestMethodIndex++}";
var newMqlGeneratorMethodName = $"{_testMethodTemplate.TestMethodNode.Identifier.Value}_{_nextTestMethodIndex++}";
newMethodDeclaration = newMethodDeclaration.WithIdentifier(SyntaxFactory.Identifier(newMqlGeneratorMethodName));

return (newMqlGeneratorMethodName, newMethodDeclaration);
}

public SyntaxTree GenerateSyntaxTree() =>
_syntaxElements.Root.ReplaceNode(_syntaxElements.ClassDeclarationSyntax, _mqlGeneratorDeclarationSyntaxNew).SyntaxTree;
_testMethodTemplate.Root.ReplaceNode(_testMethodTemplate.ClassDeclarationSyntax, _mqlGeneratorDeclarationSyntaxNew).SyntaxTree;
}
6 changes: 3 additions & 3 deletions src/MongoDB.Analyzer/Core/Poco/AnalysisCodeGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,13 @@ namespace MongoDB.Analyzer.Core.Poco;

internal static class AnalysisCodeGenerator
{
private static readonly PocoJsonGeneratorTemplateBuilder.SyntaxElements s_jsonGeneratorSyntaxElements;
private static readonly MqlGeneratorTestMethodTemplate s_testMethodTemplate;
private static readonly ParseOptions s_parseOptions;

static AnalysisCodeGenerator()
{
var jsonGeneratorSyntaxTree = GetCodeResource(ResourceNames.Poco.JsonGenerator);
s_jsonGeneratorSyntaxElements = PocoJsonGeneratorTemplateBuilder.CreateSyntaxElements(jsonGeneratorSyntaxTree);
s_testMethodTemplate = PocoJsonGeneratorTemplateBuilder.CreateTestMethodTemplate(jsonGeneratorSyntaxTree);
s_parseOptions = jsonGeneratorSyntaxTree.Options;
}

Expand Down Expand Up @@ -59,7 +59,7 @@ public static CompilationResult Compile(MongoAnalysisContext context, Expression

public static SyntaxTree GenerateJsonGeneratorSyntaxTree(ExpressionsAnalysis pocoExpressionAnalysis)
{
var testCodeBuilder = new PocoJsonGeneratorTemplateBuilder(s_jsonGeneratorSyntaxElements);
var testCodeBuilder = new PocoJsonGeneratorTemplateBuilder(s_testMethodTemplate);
var generatedJsonMethodDeclarations = new List<MethodDeclarationSyntax>(pocoExpressionAnalysis.AnalysisNodeContexts.Length);

foreach (var pocoContext in pocoExpressionAnalysis.AnalysisNodeContexts)
Expand Down
28 changes: 10 additions & 18 deletions src/MongoDB.Analyzer/Core/Poco/PocoJsonGeneratorTemplateBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,35 +12,27 @@
// See the License for the specific language governing permissions and
// limitations under the License.

using MongoDB.Analyzer.Core.Utilities;
using static MongoDB.Analyzer.Core.HelperResources.JsonSyntaxElements.Poco;

namespace MongoDB.Analyzer.Core.Poco;

internal sealed class PocoJsonGeneratorTemplateBuilder
{
internal record SyntaxElements(
SyntaxNode Root,
ClassDeclarationSyntax ClassDeclarationSyntax,
MethodDeclarationSyntax TestMethodNode,
PredefinedTypeSyntax PredefinedTypeNode)
{
public SyntaxNode[] NodesToReplace { get; } = new[] { PredefinedTypeNode };
}

private readonly SyntaxElements _syntaxElements;
private readonly MqlGeneratorTestMethodTemplate _testMethodTemplate;
private ClassDeclarationSyntax _jsonGeneratorDeclarationSyntaxNew;
private int _nextTestMethodIndex;

public PocoJsonGeneratorTemplateBuilder(SyntaxElements syntaxElements)
public PocoJsonGeneratorTemplateBuilder(MqlGeneratorTestMethodTemplate syntaxElements)
{
_syntaxElements = syntaxElements;
_jsonGeneratorDeclarationSyntaxNew = _syntaxElements.ClassDeclarationSyntax;
_testMethodTemplate = syntaxElements;
_jsonGeneratorDeclarationSyntaxNew = _testMethodTemplate.ClassDeclarationSyntax;
}

public void AddJsonGeneratorMethods(MemberDeclarationSyntax[] methodDeclarations) =>
_jsonGeneratorDeclarationSyntaxNew = _jsonGeneratorDeclarationSyntaxNew.AddMembers(methodDeclarations);

public static SyntaxElements CreateSyntaxElements(SyntaxTree jsonGeneratorSyntaxTree)
public static MqlGeneratorTestMethodTemplate CreateTestMethodTemplate(SyntaxTree jsonGeneratorSyntaxTree)
{
var root = jsonGeneratorSyntaxTree.GetRoot();
var classDeclarationSyntax = root.GetSingleClassDeclaration(JsonGenerator);
Expand All @@ -49,17 +41,17 @@ public static SyntaxElements CreateSyntaxElements(SyntaxTree jsonGeneratorSyntax
var localDeclaration = mainTestMethodNode.DescendantNodes().OfType<LocalDeclarationStatementSyntax>().FirstOrDefault();
var predefinedType = localDeclaration.DescendantNodes().OfType<PredefinedTypeSyntax>().FirstOrDefault();

return new(root, classDeclarationSyntax, mainTestMethodNode, predefinedType);
return new(root, classDeclarationSyntax, mainTestMethodNode, null, predefinedType, AnalysisType.Poco);
}

public (string newMethodName, MethodDeclarationSyntax newMethodDeclaration) GenerateJsonGeneratorMethod(ClassDeclarationSyntax poco)
{
var newMethodDeclaration = _syntaxElements.TestMethodNode.ReplaceNode(_syntaxElements.PredefinedTypeNode, SyntaxFactory.IdentifierName(poco.Identifier.ValueText));
var newJsonGeneratorMethodName = $"{_syntaxElements.TestMethodNode.Identifier.Value}_{_nextTestMethodIndex++}";
var newMethodDeclaration = _testMethodTemplate.TestMethodNode.ReplaceNode(_testMethodTemplate.TypeNode, SyntaxFactory.IdentifierName(poco.Identifier.ValueText));
var newJsonGeneratorMethodName = $"{_testMethodTemplate.TestMethodNode.Identifier.Value}_{_nextTestMethodIndex++}";
newMethodDeclaration = newMethodDeclaration.WithIdentifier(SyntaxFactory.Identifier(newJsonGeneratorMethodName));
return (newJsonGeneratorMethodName, newMethodDeclaration);
}

public SyntaxTree GenerateSyntaxTree() =>
_syntaxElements.Root.ReplaceNode(_syntaxElements.ClassDeclarationSyntax, _jsonGeneratorDeclarationSyntaxNew).SyntaxTree;
_testMethodTemplate.Root.ReplaceNode(_testMethodTemplate.ClassDeclarationSyntax, _jsonGeneratorDeclarationSyntaxNew).SyntaxTree;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Copyright 2021-present MongoDB Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License")
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

namespace MongoDB.Analyzer.Core.Utilities;

internal sealed record MqlGeneratorTestMethodTemplate(
SyntaxNode Root,
ClassDeclarationSyntax ClassDeclarationSyntax,
MethodDeclarationSyntax TestMethodNode,
SyntaxNode ExpressionNode,
SyntaxNode TypeNode,
AnalysisType AnalysisType)
{
public SyntaxNode[] NodesToReplace => AnalysisType switch
{
AnalysisType.Poco => new[] { TypeNode },
_ => new[] { ExpressionNode, TypeNode },
};
}

0 comments on commit 7c3cc17

Please sign in to comment.