Skip to content

Commit

Permalink
Switch new analyzer to just apply to WPIMath.
Browse files Browse the repository at this point in the history
  • Loading branch information
ThadHouse committed Feb 29, 2024
1 parent 38f6736 commit be30eed
Show file tree
Hide file tree
Showing 10 changed files with 110 additions and 17 deletions.
1 change: 1 addition & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ csharp_preserve_single_line_statements = true
csharp_preserve_single_line_blocks = true
# enable internal WPILib diagnostics
dotnet_diagnostic.WPILIB1100.severity = warning
dotnet_diagnostic.WPILIB1101.severity = warning
###############################
# VB Coding Conventions #
###############################
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,76 @@ namespace WPILib.CodeHelpers.ParameterlessStructs.Analyzer;
public sealed class ParameterlessStructDetector : DiagnosticAnalyzer
{
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; } = ImmutableArray.Create([
ParameterlessStructDiagnostics.ParameterlessConstructorMissing
ParameterlessStructDiagnostics.WPIMathHasFieldInitializers,
ParameterlessStructDiagnostics.WPIMathMustHaveParameterlessConstructor
]);

public override void Initialize(AnalysisContext context)
{
context.EnableConcurrentExecution();
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);

context.RegisterSymbolAction(AnalyzeWPIMathStruct, SymbolKind.NamedType);

context.RegisterOperationAction(AnalyzeFieldOperation, OperationKind.FieldInitializer);
context.RegisterOperationAction(AnalyzePropertyOperation, OperationKind.PropertyInitializer);
}

private static bool IsWPIMathNamespace(ISymbol symbol)
{
var rootNamespace = symbol.ContainingNamespace;
if (rootNamespace == null)
{
return false;
}
while (rootNamespace != null)
{
if (rootNamespace.IsGlobalNamespace)
{
// We went too far, but this is definitly not it
return false;
}
var currentName = rootNamespace.Name;
rootNamespace = rootNamespace.ContainingNamespace;
if (rootNamespace.IsGlobalNamespace)
{
// Found the actual root namespace
if (currentName == "WPIMath")
{
break;
}
// Not what we are looking for
return false;
}
}
return true;
}

private static void AnalyzeWPIMathStruct(SymbolAnalysisContext context)
{
// Find root namespace
if (context.Symbol is not INamedTypeSymbol symbol)
{
return;
}
if (symbol.TypeKind != TypeKind.Struct)
{
return;
}
if (!IsWPIMathNamespace(symbol))
{
return;
}

// If we're here, we're a WPIMath struct
var parameterless = symbol.InstanceConstructors.Where(x => x.Parameters.IsEmpty).First();

if (parameterless.IsImplicitlyDeclared)
{
ReportDiagnostic(context, symbol);
}
}

private static void AnalyzeFieldOperation(OperationAnalysisContext context)
{
var fieldInitOperation = (IFieldInitializerOperation)context.Operation;
Expand All @@ -35,10 +93,8 @@ private static void AnalyzeFieldOperation(OperationAnalysisContext context)
return;
}

// If we're here, we have an initializer, check to see if we have a default parameterless constructor
var parameterless = field.ContainingType.InstanceConstructors.Where(x => x.Parameters.IsEmpty).First();

if (parameterless.IsImplicitlyDeclared)
// If we're here, we have an initializer, check to see if we're in WPIMath
if (IsWPIMathNamespace(field.ContainingType))
{
ReportDiagnostic(context, fieldInitOperation);
}
Expand All @@ -58,17 +114,23 @@ private static void AnalyzePropertyOperation(OperationAnalysisContext context)
return;
}

// If we're here, we have an initializer, check to see if we have a default parameterless constructor
var parameterless = property.ContainingType.InstanceConstructors.Where(x => x.Parameters.IsEmpty).First();

if (parameterless.IsImplicitlyDeclared)
// If we're here, we have an initializer, check to see if we're in WPIMath
if (IsWPIMathNamespace(property.ContainingType))
{
ReportDiagnostic(context, propertyInitOperation);
}
}

private static void ReportDiagnostic(OperationAnalysisContext context, IOperation symbol)
{
context.ReportDiagnostic(Diagnostic.Create(ParameterlessStructDiagnostics.ParameterlessConstructorMissing, symbol.Syntax.GetLocation()));
context.ReportDiagnostic(Diagnostic.Create(ParameterlessStructDiagnostics.WPIMathHasFieldInitializers, symbol.Syntax.GetLocation()));
}

private static void ReportDiagnostic(SymbolAnalysisContext context, ISymbol symbol)
{
foreach (var location in symbol.Locations)
{
context.ReportDiagnostic(Diagnostic.Create(ParameterlessStructDiagnostics.WPIMathMustHaveParameterlessConstructor, location));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,14 @@ public static class ParameterlessStructDiagnostics
public class Ids
{
public const string Prefix = "WPILIB";
public const string ParameterlessConstructorMissing = Prefix + "1100";
public const string WPIMathHasFieldInitializers = Prefix + "1100";
public const string WPIMathMustHaveParameterlessConstructor = Prefix + "1101";
}

private const string Category = "ParameterlessStructs";

public static readonly DiagnosticDescriptor ParameterlessConstructorMissing = new(
Ids.ParameterlessConstructorMissing, "Struct has field initializers but no parameterless constructor", "Struct has field initializers but no parameterless constructor", Category, DiagnosticSeverity.Warning, false);
public static readonly DiagnosticDescriptor WPIMathHasFieldInitializers = new(
Ids.WPIMathHasFieldInitializers, "WPIMath struct has field initializers", "All structs in WPIMath must not have field or property initializers", Category, DiagnosticSeverity.Warning, false);
public static readonly DiagnosticDescriptor WPIMathMustHaveParameterlessConstructor = new(
Ids.WPIMathMustHaveParameterlessConstructor, "WPIMath struct does not have explicit parameterless constructor", "All structs in WPIMath must have explicit parameterless constructors", Category, DiagnosticSeverity.Warning, false);
}
4 changes: 4 additions & 0 deletions src/wpimath/Geometry/Pose2d.cs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,10 @@ public partial class Pose2dJsonContext : JsonSerializerContext
[JsonIgnore]
public Length Y => Translation.Y;

public Pose2d() : this(new(), new())
{

}

[JsonConstructor]
public Pose2d(Translation2d translation, Rotation2d rotation)
Expand Down
9 changes: 5 additions & 4 deletions src/wpimath/Geometry/Rotation2d.cs
Original file line number Diff line number Diff line change
Expand Up @@ -100,16 +100,17 @@ internal Rotation2d(double radians) : this(radians.Radians())
[JsonPropertyName("radians")]
internal double Radians => Angle.Radians;

public Rotation2d() {
public Rotation2d() : this(0.Radians())
{

}

[JsonIgnore]
public Angle Angle { get; } = 0.Radians();
public Angle Angle { get; }
[JsonIgnore]
public double Cos { get; } = 1;
public double Cos { get; }
[JsonIgnore]
public double Sin { get; } = 0;
public double Sin { get; }
[JsonIgnore]
public double Tan => Sin / Cos;

Expand Down
5 changes: 5 additions & 0 deletions src/wpimath/Geometry/Rotation3d.cs
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,11 @@ public partial class Rotation3dJsonContext : JsonSerializerContext
[JsonPropertyName("quaternion")]
public Quaternion Quaternion { get; init; }

public Rotation3d() : this(new())
{

}

[JsonConstructor]
public Rotation3d(Quaternion quaternion)
{
Expand Down
5 changes: 5 additions & 0 deletions src/wpimath/Geometry/Transform2d.cs
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,11 @@ public partial class Transform2dJsonContext : JsonSerializerContext

public static Transform2d AdditiveIdentity => new();

public Transform2d() : this(new Translation2d(), new Rotation2d())
{

}

[JsonConstructor]
public Transform2d(Translation2d translation, Rotation2d rotation)
{
Expand Down
4 changes: 4 additions & 0 deletions src/wpimath/Geometry/Translation2d.cs
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,10 @@ internal Translation2d(double x, double y)
[JsonPropertyName("y")]
internal double JsonY => Y.Meters;

public Translation2d() : this(0.Meters(), 0.Meters())
{
}

public Translation2d(Length x, Length y)
{
X = x;
Expand Down
4 changes: 4 additions & 0 deletions src/wpimath/Geometry/Twist2d.cs
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,10 @@ internal Twist2d(double dx, double dy, double dtheta)
Dtheta = dtheta.Radians();
}

public Twist2d() : this(0.Meters(), 0.Meters(), 0.Radians())
{
}

public Twist2d(Length dx, Length dy, Angle dtheta)
{
Dx = dx;
Expand Down
4 changes: 4 additions & 0 deletions src/wpimath/Geometry/Twist3d.cs
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,10 @@ internal Twist3d(double dx, double dy, double dz, double rx, double ry, double r
Rz = rz.Radians();
}

public Twist3d() : this(0.Meters(), 0.Meters(), 0.Meters(), 0.Radians(), 0.Radians(), 0.Radians())
{
}

public Twist3d(Length dx, Length dy, Length dz, Angle rx, Angle ry, Angle rz)
{
Dx = dx;
Expand Down

0 comments on commit be30eed

Please sign in to comment.