Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add analyzer for weighing-machine #109

Merged
merged 22 commits into from
Nov 30, 2021
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
6c63798
Start writing code
Grenkin1988 Nov 13, 2021
3b18356
Add more analysis for properties
Grenkin1988 Nov 13, 2021
118bdd7
Provate field analysis
Grenkin1988 Nov 13, 2021
b140a1e
Add IsRoundMethodCalledInDisplayWeightProperty
Grenkin1988 Nov 13, 2021
559a7ab
Fix some comments
Grenkin1988 Nov 16, 2021
4eea02f
Merge branch 'exercism:main' into weighing-machine-analyzer
Grenkin1988 Nov 16, 2021
895d47d
Add some comments, and implemet finding correct baking field
Grenkin1988 Nov 17, 2021
41c9b12
More comments
Grenkin1988 Nov 17, 2021
6cb3e15
Include files and revert launch
Grenkin1988 Nov 17, 2021
5c1fb57
Try adding tests
Grenkin1988 Nov 18, 2021
f8772de
Remove explicit solution entries in csproj
ErikSchierboom Nov 19, 2021
a0fd8d0
Remove comments integration from analysis
ErikSchierboom Nov 19, 2021
5f43e51
Merge remote-tracking branch 'ErikSchierboom/remove-website-copy-inte…
Grenkin1988 Nov 19, 2021
8278ab6
Merge remote-tracking branch 'origin/main' into weighing-machine-anal…
Grenkin1988 Nov 19, 2021
f3716b3
Merge branch 'weighing-machine-analyzer' of https://github.com/Grenki…
Grenkin1988 Nov 19, 2021
1e537c7
All tests pass
Grenkin1988 Nov 19, 2021
0cb889d
Auto property case
Grenkin1988 Nov 19, 2021
0c72335
csharp.weighingmachine.property_setter_is_not_private
Grenkin1988 Nov 19, 2021
062d381
All tests done and fixed logic
Grenkin1988 Nov 20, 2021
1216c5c
Merge remote-tracking branch 'origin/main' into weighing-machine-anal…
Grenkin1988 Nov 25, 2021
c780424
Move some comments to shared and add approve case
Grenkin1988 Nov 27, 2021
8f9d964
Fix project and test
Grenkin1988 Nov 30, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -34,5 +34,8 @@ public static SolutionComment MissingMethod(string method) =>

public static SolutionComment InvalidMethodSignature(string method, string signature) =>
new SolutionComment("csharp.general.invalid_method_signature", new SolutionCommentParameter(Name, method), new SolutionCommentParameter(Signature, signature));

public static SolutionComment MissingProperty(string property) =>
new("csharp.general.missing_property", new SolutionCommentParameter(Name, property));
Grenkin1988 marked this conversation as resolved.
Show resolved Hide resolved
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@

using Exercism.Analyzers.CSharp.Analyzers.Shared;

using static Exercism.Analyzers.CSharp.Analyzers.Shared.SharedComments;
using static Exercism.Analyzers.CSharp.Analyzers.WeighingMachine.WeighingMachineSolution;

namespace Exercism.Analyzers.CSharp.Analyzers.WeighingMachine
{
internal class WeighingMachineAnalyzer : SharedAnalyzer<WeighingMachineSolution>
{
protected override SolutionAnalysis DisapproveWhenInvalid(WeighingMachineSolution solution)
{
if (solution.MissingWeighingMachineClass)
solution.AddComment(MissingClass(WeighingMachineClassName));

foreach (var missing in solution.MissingRequiredProperties())
{
solution.AddComment(MissingProperty(missing));
}
Grenkin1988 marked this conversation as resolved.
Show resolved Hide resolved

if (!solution.PrecisionIsAutoProperty)
{
solution.AddComment(new SolutionComment("not PrecisionIsAutoProperty"));
Grenkin1988 marked this conversation as resolved.
Show resolved Hide resolved
}

if (!solution.TareAdjustmentIsAutoProperty)
{
solution.AddComment(new SolutionComment("not TareAdjustmentIsAutoProperty"));
}

if (solution.PrecisionPropertyHasNonPrivateSetter())
{
solution.AddComment(new SolutionComment("PrecisionPropertyHasNonPrivateSetter"));
}

if (!solution.WeightFieldNameIsPrivate)
{
solution.AddComment(new SolutionComment("not WeightFieldNameIsPrivate"));
}

if (!solution.IsRoundMethodCalledInDisplayWeightProperty())
{
solution.AddComment(new SolutionComment("not IsRoundMethodCalledInDisplayWeightProperty"));
}

return solution.HasComments
? solution.Disapprove()
: solution.ContinueAnalysis();
}

protected override SolutionAnalysis ApproveWhenValid(WeighingMachineSolution solution)
{
if (solution.TareAdjustmentHasInitializer)
{
solution.AddComment(new SolutionComment("TareAdjustmentHasInitializer"));
}

if (solution.HasComments)
{
return solution.Approve();
}

return solution.ContinueAnalysis();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Exercism.Analyzers.CSharp.Analyzers.WeighingMachine
{
public class WeighingMachineComments
{

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using Exercism.Analyzers.CSharp.Syntax;

using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;

namespace Exercism.Analyzers.CSharp.Analyzers.WeighingMachine
{
internal class WeighingMachineSolution : Solution
{
public const string WeighingMachineClassName = "WeighingMachine";
public const string PrecisionPropertyName = "Precision";
public const string WeightPropertyName = "Weight";
public const string TareAdjustmentPropertyName = "TareAdjustment";
public const string DisplayWeightPropertyName = "DisplayWeight";
public const string WeightFieldName = "weight";
Grenkin1988 marked this conversation as resolved.
Show resolved Hide resolved

public WeighingMachineSolution(Solution solution) : base(solution)
{
}

private ClassDeclarationSyntax WeighingMachineClass => SyntaxRoot.GetClass(WeighingMachineClassName);

public bool MissingWeighingMachineClass => WeighingMachineClass is null;

private PropertyDeclarationSyntax PrecisionProperty => WeighingMachineClass?.GetProperty(PrecisionPropertyName);

private PropertyDeclarationSyntax WeightProperty => WeighingMachineClass?.GetProperty(WeightPropertyName);

private PropertyDeclarationSyntax TareAdjustmentProperty => WeighingMachineClass?.GetProperty(TareAdjustmentPropertyName);

private PropertyDeclarationSyntax DisplayWeightProperty => WeighingMachineClass?.GetProperty(DisplayWeightPropertyName);

public IEnumerable<string> MissingRequiredProperties()
{
if (PrecisionProperty is null) yield return PrecisionPropertyName;
if (WeightProperty is null) yield return WeightPropertyName;
if (TareAdjustmentProperty is null) yield return TareAdjustmentPropertyName;
if (DisplayWeightProperty is null) yield return DisplayWeightPropertyName;
}

public bool PrecisionIsAutoProperty => PrecisionProperty?.IsAutoProperty() == true;

public bool TareAdjustmentIsAutoProperty => TareAdjustmentProperty?.IsAutoProperty() == true;

public bool TareAdjustmentHasInitializer => TareAdjustmentProperty?.HasInitializer() == true;

public bool PrecisionPropertyHasNonPrivateSetter()
{
var setAccessor = PrecisionProperty.GetSetAccessor();
return setAccessor is not null &&
(setAccessor.Modifiers.Count == 0 ||
!setAccessor.Modifiers.Any(m => m.IsKind(Microsoft.CodeAnalysis.CSharp.SyntaxKind.PrivateKeyword)));
}

public bool WeightFieldNameIsPrivate => WeighingMachineClass?.GetField(WeightFieldName)?.IsPrivate() ?? false;

public bool IsRoundMethodCalledInDisplayWeightProperty() =>
DisplayWeightProperty?.GetMethodCalled("Round") is not null;


Grenkin1988 marked this conversation as resolved.
Show resolved Hide resolved
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using System.Linq;

using Exercism.Analyzers.CSharp.Syntax;

using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;

namespace Exercism.Analyzers.CSharp.Analyzers.WeighingMachine
{
public static class WeighingMachineSyntaxFactory
{
public static MemberAccessExpressionSyntax GetMethodCalled(this SyntaxNode syntaxNode, string methodName) =>
Grenkin1988 marked this conversation as resolved.
Show resolved Hide resolved
syntaxNode
.DescendantNodes<InvocationExpressionSyntax>()
.Select(s => s.Expression)
.OfType<MemberAccessExpressionSyntax>()
.FirstOrDefault(s => s.Name.Identifier.Text == methodName);
}
}
1 change: 1 addition & 0 deletions src/Exercism.Analyzers.CSharp/Exercises.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@ internal static class Exercises
public const string TwoFer = "two-fer";
public const string Gigasecond = "gigasecond";
public const string Leap = "leap";
public const string WeighingMachine = "weighing-machine";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@
<ItemGroup>
<PackageReference Include="CommandLineParser" Version="2.8.0" />
<PackageReference Include="Humanizer.Core" Version="2.11.10" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="3.8.0" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.0.1" />
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.3" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="3.11.0" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="4.0.1" />
<PackageReference Include="Serilog" Version="2.10.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="4.0.0" />
</ItemGroup>
Expand Down
3 changes: 3 additions & 0 deletions src/Exercism.Analyzers.CSharp/SolutionAnalyzer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using Exercism.Analyzers.CSharp.Analyzers.Gigasecond;
using Exercism.Analyzers.CSharp.Analyzers.Leap;
using Exercism.Analyzers.CSharp.Analyzers.TwoFer;
using Exercism.Analyzers.CSharp.Analyzers.WeighingMachine;

namespace Exercism.Analyzers.CSharp
{
Expand All @@ -17,6 +18,8 @@ public static SolutionAnalysis Analyze(Solution solution)
return new GigasecondAnalyzer().Analyze(new GigasecondSolution(solution));
case Exercises.Leap:
return new LeapAnalyzer().Analyze(new LeapSolution(solution));
case Exercises.WeighingMachine:
return new WeighingMachineAnalyzer().Analyze(new WeighingMachineSolution(solution));
default:
return new DefaultExerciseAnalyzer().Analyze(new DefaultSolution(solution));
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using Microsoft.CodeAnalysis.CSharp.Syntax;

namespace Exercism.Analyzers.CSharp.Syntax
{
public static class PropertyDeclarationSyntaxExtensions
Grenkin1988 marked this conversation as resolved.
Show resolved Hide resolved
{
public const string GetAccessorName = "get";
public const string SetAccessorName = "set";

public static bool IsAutoProperty(this PropertyDeclarationSyntax property)
{
if (property.AccessorList != null)
{
return property.AccessorList.Accessors.All(a => a.Body is null || a.ExpressionBody is null);
}

return property.ExpressionBody is null;
}

public static bool HasInitializer(this PropertyDeclarationSyntax property) => property?.Initializer is not null;

public static AccessorDeclarationSyntax GetAccessor(this PropertyDeclarationSyntax property, string accessor)
{
return property?.AccessorList?.Accessors.Where(ac => ac.Keyword.Text == accessor).FirstOrDefault();
}

public static AccessorDeclarationSyntax GetGetAccessor(this PropertyDeclarationSyntax property) =>
property?.GetAccessor(GetAccessorName);

public static AccessorDeclarationSyntax GetSetAccessor(this PropertyDeclarationSyntax property) =>
property?.GetAccessor(SetAccessorName);
}
}
28 changes: 28 additions & 0 deletions src/Exercism.Analyzers.CSharp/Syntax/SyntaxNodeExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,34 @@ public static IEnumerable<MethodDeclarationSyntax> GetMethods(this SyntaxNode sy
.DescendantNodes<MethodDeclarationSyntax>()
.Where(syntax => syntax.Identifier.Text == methodName) ?? Enumerable.Empty<MethodDeclarationSyntax>();

public static PropertyDeclarationSyntax GetProperty(this SyntaxNode syntaxNode, string propertyName) =>
syntaxNode?
.DescendantNodes<PropertyDeclarationSyntax>()
.FirstOrDefault(syntax => syntax.Identifier.Text == propertyName);

public static IEnumerable<PropertyDeclarationSyntax> GetProperties(this SyntaxNode syntaxNode, string propertyName) =>
syntaxNode?
.DescendantNodes<PropertyDeclarationSyntax>()
.Where(syntax => syntax.Identifier.Text == propertyName) ?? Enumerable.Empty<PropertyDeclarationSyntax>();

public static FieldDeclarationSyntax GetField(this SyntaxNode syntaxNode, string variable) =>
syntaxNode?
.DescendantNodes<VariableDeclaratorSyntax>()
.FirstOrDefault(syntax => IsEqualFieldName(syntax.Identifier.Text, variable))
?.FieldDeclaration();

public static IEnumerable<FieldDeclarationSyntax> GetFields(this SyntaxNode syntaxNode, string variable) =>
syntaxNode?
.DescendantNodes<VariableDeclaratorSyntax>()
.Where(syntax => IsEqualFieldName(syntax.Identifier.Text, variable))
.Select(v => v?.FieldDeclaration()) ?? Enumerable.Empty<FieldDeclarationSyntax>();

public static bool IsEqualFieldName(string name, string expectedName)
{
var expected = new[] { expectedName, expectedName.StartsWith("_") ? expectedName.Substring(1) : "_" + expectedName };
return expected.Contains(name);
}

public static bool AssignsToIdentifier(this SyntaxNode syntaxNode, IdentifierNameSyntax identifierName) =>
syntaxNode?
.DescendantNodes<AssignmentExpressionSyntax>()
Expand Down