diff --git a/src/HotChocolate/Language/src/Language.SyntaxTree/InputObjectTypeDefinitionNode.cs b/src/HotChocolate/Language/src/Language.SyntaxTree/InputObjectTypeDefinitionNode.cs index 9d17e2f65b9..c9eab1c1917 100644 --- a/src/HotChocolate/Language/src/Language.SyntaxTree/InputObjectTypeDefinitionNode.cs +++ b/src/HotChocolate/Language/src/Language.SyntaxTree/InputObjectTypeDefinitionNode.cs @@ -220,11 +220,28 @@ public override bool Equals(object? obj) public override int GetHashCode() => HashCode.Combine(base.GetHashCode(), Description); + /// + /// The equal operator. + /// + /// The left parameter + /// The right parameter + /// + /// true if and are equal. + /// + public static bool operator ==( InputObjectTypeDefinitionNode? left, InputObjectTypeDefinitionNode? right) => Equals(left, right); + /// + /// The not equal operator. + /// + /// The left parameter + /// The right parameter + /// + /// true if and are not equal. + /// public static bool operator !=( InputObjectTypeDefinitionNode? left, InputObjectTypeDefinitionNode? right) diff --git a/src/HotChocolate/Language/src/Language.SyntaxTree/RequiredModifierNode.cs b/src/HotChocolate/Language/src/Language.SyntaxTree/RequiredModifierNode.cs index d80a1783247..633aa397405 100644 --- a/src/HotChocolate/Language/src/Language.SyntaxTree/RequiredModifierNode.cs +++ b/src/HotChocolate/Language/src/Language.SyntaxTree/RequiredModifierNode.cs @@ -4,10 +4,22 @@ namespace HotChocolate.Language; +/// +/// Represents the required modifier syntax. +/// public sealed class RequiredModifierNode : INullabilityModifierNode , IEquatable { + /// + /// Initializes a new instance of . + /// + /// + /// The location of the syntax node within the original source text. + /// + /// + /// The list nullability modifier. + /// public RequiredModifierNode(Location? location, ListNullabilityNode? element) { Location = location; @@ -55,9 +67,19 @@ public IEnumerable GetNodes() /// public string ToString(bool indented) => SyntaxPrinter.Print(this, indented); + /// + /// Indicates whether the current object is equal to another object of the same type. + /// + /// + /// An object to compare with this object. + /// + /// + /// true if the current object is equal to the parameter; + /// otherwise, false. + /// public bool Equals(RequiredModifierNode? other) { - if (ReferenceEquals(null, other)) + if (other is null) { return false; } @@ -70,16 +92,47 @@ public bool Equals(RequiredModifierNode? other) return Equals(Element, other.Element); } + /// + /// Determines whether the specified object is equal to the current object. + /// + /// + /// The object to compare with the current object. + /// + /// + /// true if the specified object is equal to the current object; otherwise, false. + /// public override bool Equals(object? obj) => ReferenceEquals(this, obj) || (obj is RequiredModifierNode other && Equals(other)); + /// + /// Serves as the default hash function. + /// + /// + /// A hash code for the current object. + /// public override int GetHashCode() => HashCode.Combine(Kind, Element); + /// + /// The equal operator. + /// + /// The left parameter + /// The right parameter + /// + /// true if and are equal. + /// public static bool operator ==(RequiredModifierNode? left, RequiredModifierNode? right) => Equals(left, right); + /// + /// The not equal operator. + /// + /// The left parameter + /// The right parameter + /// + /// true if and are not equal. + /// public static bool operator !=(RequiredModifierNode? left, RequiredModifierNode? right) => !Equals(left, right); } diff --git a/src/HotChocolate/Language/src/Language.SyntaxTree/ScalarTypeDefinitionNode.cs b/src/HotChocolate/Language/src/Language.SyntaxTree/ScalarTypeDefinitionNode.cs index 7875b7ab926..dda4494d376 100644 --- a/src/HotChocolate/Language/src/Language.SyntaxTree/ScalarTypeDefinitionNode.cs +++ b/src/HotChocolate/Language/src/Language.SyntaxTree/ScalarTypeDefinitionNode.cs @@ -1,12 +1,38 @@ +using System; using System.Collections.Generic; using HotChocolate.Language.Utilities; namespace HotChocolate.Language; +/// +/// Represents the scalar definition syntax. +/// +/// Scalar types represent primitive leaf values in a GraphQL type system. +/// GraphQL responses take the form of a hierarchical tree; +/// the leaves of this tree are typically GraphQL Scalar types +/// (but may also be Enum types or null values). +/// +/// public sealed class ScalarTypeDefinitionNode - : ScalarTypeDefinitionNodeBase + : NamedSyntaxNode , ITypeDefinitionNode + , IEquatable { + /// + /// Initializes a new instance of . + /// + /// + /// The location of the syntax node within the original source text. + /// + /// + /// The name of the scalar. + /// + /// + /// The description of the scalar. + /// + /// + /// The applied directives. + /// public ScalarTypeDefinitionNode( Location? location, NameNode name, @@ -17,10 +43,16 @@ public ScalarTypeDefinitionNode( Description = description; } - public override SyntaxKind Kind { get; } = SyntaxKind.ScalarTypeDefinition; + /// + public override SyntaxKind Kind => SyntaxKind.ScalarTypeDefinition; + /// + /// Gets the scalar description. + /// + /// public StringValueNode? Description { get; } + /// public override IEnumerable GetNodes() { if (Description is { }) @@ -57,33 +89,124 @@ public override IEnumerable GetNodes() /// public override string ToString(bool indented) => SyntaxPrinter.Print(this, indented); + /// + /// Creates a new node from the current instance and replaces the + /// with . + /// + /// + /// The location that shall be used to replace the current location. + /// + /// + /// Returns the new node with the new . + /// public ScalarTypeDefinitionNode WithLocation(Location? location) - { - return new ScalarTypeDefinitionNode( - location, Name, Description, - Directives); - } + => new(location, Name, Description, Directives); + /// + /// Creates a new node from the current instance and replaces the + /// with . + /// + /// + /// The name that shall be used to replace the current name. + /// + /// + /// Returns the new node with the new . + /// public ScalarTypeDefinitionNode WithName(NameNode name) - { - return new ScalarTypeDefinitionNode( - Location, name, Description, - Directives); - } + => new(Location, name, Description, Directives); - public ScalarTypeDefinitionNode WithDescription( - StringValueNode? description) - { - return new ScalarTypeDefinitionNode( - Location, Name, description, - Directives); - } + /// + /// Creates a new node from the current instance and replaces the + /// with . + /// + /// + /// The description that shall be used to replace the current description. + /// + /// + /// Returns the new node with the new . + /// + public ScalarTypeDefinitionNode WithDescription(StringValueNode? description) + => new(Location, Name, description, Directives); - public ScalarTypeDefinitionNode WithDirectives( - IReadOnlyList directives) - { - return new ScalarTypeDefinitionNode( - Location, Name, Description, - directives); - } + /// + /// Creates a new node from the current instance and replaces the + /// with . + /// + /// + /// The directives that shall be used to replace the current + /// . + /// + /// + /// Returns the new node with the new . + /// + public ScalarTypeDefinitionNode WithDirectives(IReadOnlyList directives) + => new(Location, Name, Description, directives); + + /// + /// Determines whether the specified + /// is equal to the current . + /// + /// + /// The to compare with the current + /// . + /// + /// + /// true if the specified is equal + /// to the current ; + /// otherwise, false. + /// + public bool Equals(ScalarTypeDefinitionNode? other) + => base.Equals(other) && Description.IsEqualTo(other.Description); + + /// + /// Determines whether the specified is equal to + /// the current . + /// + /// + /// The to compare with the current + /// . + /// + /// + /// true if the specified is equal to the + /// current ; otherwise, false. + /// + public override bool Equals(object? obj) + => Equals(obj as ScalarTypeDefinitionNode); + + /// + /// Serves as a hash function for a + /// object. + /// + /// + /// A hash code for this instance that is suitable for use in + /// hashing algorithms and data structures such as a hash table. + /// + public override int GetHashCode() + => HashCode.Combine(base.GetHashCode(), Kind, Description?.GetHashCode()); + + /// + /// The equal operator. + /// + /// The left parameter + /// The right parameter + /// + /// true if and are equal. + /// + public static bool operator ==( + ScalarTypeDefinitionNode? left, + ScalarTypeDefinitionNode? right) + => Equals(left, right); + + /// + /// The not equal operator. + /// + /// The left parameter + /// The right parameter + /// + /// true if and are not equal. + /// + public static bool operator !=( + ScalarTypeDefinitionNode? left, + ScalarTypeDefinitionNode? right) + => !Equals(left, right); } diff --git a/src/HotChocolate/Language/src/Language.SyntaxTree/ScalarTypeDefinitionNodeBase.cs b/src/HotChocolate/Language/src/Language.SyntaxTree/ScalarTypeDefinitionNodeBase.cs deleted file mode 100644 index 43763efc141..00000000000 --- a/src/HotChocolate/Language/src/Language.SyntaxTree/ScalarTypeDefinitionNodeBase.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System.Collections.Generic; - -namespace HotChocolate.Language; - -public abstract class ScalarTypeDefinitionNodeBase - : NamedSyntaxNode -{ - protected ScalarTypeDefinitionNodeBase( - Location? location, - NameNode name, - IReadOnlyList directives) - : base(location, name, directives) - { } -} diff --git a/src/HotChocolate/Language/src/Language.SyntaxTree/ScalarTypeExtensionNode.cs b/src/HotChocolate/Language/src/Language.SyntaxTree/ScalarTypeExtensionNode.cs index 3fe0f696664..bfed6303c44 100644 --- a/src/HotChocolate/Language/src/Language.SyntaxTree/ScalarTypeExtensionNode.cs +++ b/src/HotChocolate/Language/src/Language.SyntaxTree/ScalarTypeExtensionNode.cs @@ -3,10 +3,30 @@ namespace HotChocolate.Language; +/// +/// Scalar type extensions are used to represent a scalar type which has been +/// extended from some original scalar type. For example, this might be used +/// by a GraphQL tool or service which adds directives to an existing scalar. +/// public sealed class ScalarTypeExtensionNode - : ScalarTypeDefinitionNodeBase + : NamedSyntaxNode , ITypeExtensionNode { + /// + /// Initializes a new instance of . + /// + /// + /// The location of the syntax node within the original source text. + /// + /// + /// The name of the scalar. + /// + /// + /// The description of the scalar. + /// + /// + /// The applied directives. + /// public ScalarTypeExtensionNode( Location? location, NameNode name, @@ -15,8 +35,10 @@ public ScalarTypeExtensionNode( { } - public override SyntaxKind Kind { get; } = SyntaxKind.ScalarTypeExtension; + /// + public override SyntaxKind Kind => SyntaxKind.ScalarTypeExtension; + /// public override IEnumerable GetNodes() { yield return Name; @@ -48,22 +70,110 @@ public override IEnumerable GetNodes() /// public override string ToString(bool indented) => SyntaxPrinter.Print(this, indented); + /// + /// Creates a new node from the current instance and replaces the + /// with . + /// + /// + /// The location that shall be used to replace the current location. + /// + /// + /// Returns the new node with the new . + /// public ScalarTypeExtensionNode WithLocation(Location? location) - { - return new ScalarTypeExtensionNode( - location, Name, Directives); - } + => new(location, Name, Directives); + /// + /// Creates a new node from the current instance and replaces the + /// with . + /// + /// + /// The name that shall be used to replace the current name. + /// + /// + /// Returns the new node with the new . + /// public ScalarTypeExtensionNode WithName(NameNode name) - { - return new ScalarTypeExtensionNode( - Location, name, Directives); - } + => new(Location, name, Directives); + /// + /// Creates a new node from the current instance and replaces the + /// with . + /// + /// + /// The directives that shall be used to replace the current + /// . + /// + /// + /// Returns the new node with the new . + /// public ScalarTypeExtensionNode WithDirectives( IReadOnlyList directives) - { - return new ScalarTypeExtensionNode( - Location, Name, directives); - } + => new(Location, Name, directives); + + /// + /// Determines whether the specified + /// is equal to the current . + /// + /// + /// The to compare with the current + /// . + /// + /// + /// true if the specified is equal + /// to the current ; + /// otherwise, false. + /// + public bool Equals(ScalarTypeExtensionNode? other) => base.Equals(other); + + /// + /// Determines whether the specified is equal to + /// the current . + /// + /// + /// The to compare with the current + /// . + /// + /// + /// true if the specified is equal to the + /// current ; otherwise, false. + /// + public override bool Equals(object? obj) + => Equals(obj as ScalarTypeExtensionNode); + + /// + /// Serves as a hash function for a + /// object. + /// + /// + /// A hash code for this instance that is suitable for use in + /// hashing algorithms and data structures such as a hash table. + /// + public override int GetHashCode() => base.GetHashCode(); + + /// + /// The equal operator. + /// + /// The left parameter + /// The right parameter + /// + /// true if and are equal. + /// + public static bool operator ==( + ScalarTypeExtensionNode? left, + ScalarTypeExtensionNode? right) + => Equals(left, right); + + /// + /// The not equal operator. + /// + /// The left parameter + /// The right parameter + /// + /// true if and are not equal. + /// + public static bool operator !=( + ScalarTypeExtensionNode? left, + ScalarTypeExtensionNode? right) + => !Equals(left, right); } diff --git a/src/HotChocolate/Language/test/Language.SyntaxTree.Tests/HotChocolate.Language.SyntaxTree.Tests.csproj b/src/HotChocolate/Language/test/Language.SyntaxTree.Tests/HotChocolate.Language.SyntaxTree.Tests.csproj index 6e2d5fd50c6..b3cb6b9bbfc 100644 --- a/src/HotChocolate/Language/test/Language.SyntaxTree.Tests/HotChocolate.Language.SyntaxTree.Tests.csproj +++ b/src/HotChocolate/Language/test/Language.SyntaxTree.Tests/HotChocolate.Language.SyntaxTree.Tests.csproj @@ -27,6 +27,6 @@ - + diff --git a/src/HotChocolate/Language/test/Language.SyntaxTree.Tests/RequiredModifierNodeTests.cs b/src/HotChocolate/Language/test/Language.SyntaxTree.Tests/RequiredModifierNodeTests.cs new file mode 100644 index 00000000000..5c23a6803db --- /dev/null +++ b/src/HotChocolate/Language/test/Language.SyntaxTree.Tests/RequiredModifierNodeTests.cs @@ -0,0 +1,75 @@ +using Xunit; + +namespace HotChocolate.Language.SyntaxTree; + +public class RequiredModifierNodeTests +{ + private readonly ListNullabilityNode _element1 = + new(new Location(1, 1, 1, 1), null); + private readonly ListNullabilityNode _element2 = + new(new Location(1, 1, 1, 1), new RequiredModifierNode(null, null)); + + [Fact] + public void Equals_With_Same_Location() + { + // arrange + var a = new RequiredModifierNode(new Location(1, 1, 1, 1), _element1); + var b = new RequiredModifierNode(new Location(1, 1, 1, 1), _element1); + var c = new RequiredModifierNode(new Location(1, 1, 1, 1), _element2); + + // act + var abResult = a.Equals(b); + var aaResult = a.Equals(a); + var acResult = a.Equals(c); + var aNullResult = a.Equals(default); + + // assert + Assert.True(abResult); + Assert.True(aaResult); + Assert.False(acResult); + Assert.False(aNullResult); + } + + [Fact] + public void Equals_With_Different_Location() + { + // arrange + var a = new RequiredModifierNode(new Location(1, 1, 1, 1), _element1); + var b = new RequiredModifierNode(new Location(2, 2, 2, 2), _element1); + var c = new RequiredModifierNode(new Location(3, 3, 3, 3), _element2); + + // act + var abResult = a.Equals(b); + var aaResult = a.Equals(a); + var acResult = a.Equals(c); + var aNullResult = a.Equals(default); + + // assert + Assert.True(abResult); + Assert.True(aaResult); + Assert.False(acResult); + Assert.False(aNullResult); + } + + [Fact] + public void GetHashCode_With_Location() + { + // arrange + var a = new RequiredModifierNode(new Location(1, 1, 1, 1), _element1); + var b = new RequiredModifierNode(new Location(2, 2, 2, 2), _element1); + var c = new RequiredModifierNode(new Location(1, 1, 1, 1), _element2); + var d = new RequiredModifierNode(new Location(2, 2, 2, 2), _element2); + + // act + var aHash = a.GetHashCode(); + var bHash = b.GetHashCode(); + var cHash = c.GetHashCode(); + var dHash = d.GetHashCode(); + + // assert + Assert.Equal(aHash, bHash); + Assert.NotEqual(aHash, cHash); + Assert.Equal(cHash, dHash); + Assert.NotEqual(aHash, dHash); + } +} diff --git a/src/HotChocolate/Language/test/Language.SyntaxTree.Tests/ScalarTypeDefinitionNodeTests.cs b/src/HotChocolate/Language/test/Language.SyntaxTree.Tests/ScalarTypeDefinitionNodeTests.cs new file mode 100644 index 00000000000..78a74507ca1 --- /dev/null +++ b/src/HotChocolate/Language/test/Language.SyntaxTree.Tests/ScalarTypeDefinitionNodeTests.cs @@ -0,0 +1,84 @@ +using System; +using System.Collections.Generic; +using Xunit; + +namespace HotChocolate.Language.SyntaxTree; + +public class ScalarTypeDefinitionNodeTests +{ + private readonly NameNode _name1 = new("name1"); + private readonly NameNode _name2 = new("name2"); + private readonly StringValueNode _description1 = new("value1"); + private readonly StringValueNode _description2 = new("value2"); + private readonly IReadOnlyList _directives = Array.Empty(); + + [Fact] + public void Equals_With_Same_Location() + { + // arrange + var a = new ScalarTypeDefinitionNode(TestLocations.Location1, _name1, _description1, _directives); + var b = new ScalarTypeDefinitionNode(TestLocations.Location1, _name1, _description1, _directives); + var c = new ScalarTypeDefinitionNode(TestLocations.Location1, _name1, _description2, _directives); + var d = new ScalarTypeDefinitionNode(TestLocations.Location1, _name2, _description2, _directives); + + // act + var abResult = a.Equals(b); + var aaResult = a.Equals(a); + var acResult = a.Equals(c); + var cdResult = c.Equals(d); + var aNullResult = a.Equals(default); + + // assert + Assert.True(abResult); + Assert.True(aaResult); + Assert.False(acResult); + Assert.False(cdResult); + Assert.False(aNullResult); + } + + [Fact] + public void Equals_With_Different_Location() + { + // arrange + var a = new ScalarTypeDefinitionNode(TestLocations.Location1, _name1, _description1, _directives); + var b = new ScalarTypeDefinitionNode(TestLocations.Location2, _name1, _description1, _directives); + var c = new ScalarTypeDefinitionNode(TestLocations.Location3, _name1, _description2, _directives); + + // act + var abResult = a.Equals(b); + var aaResult = a.Equals(a); + var acResult = a.Equals(c); + var aNullResult = a.Equals(default); + + // assert + Assert.True(abResult); + Assert.True(aaResult); + Assert.False(acResult); + Assert.False(aNullResult); + } + + [Fact] + public void GetHashCode_With_Location() + { + // arrange + var a = new ScalarTypeDefinitionNode(TestLocations.Location1, _name1, _description1, _directives); + var b = new ScalarTypeDefinitionNode(TestLocations.Location2, _name1, _description1, _directives); + var c = new ScalarTypeDefinitionNode(TestLocations.Location1, _name1, _description2, _directives); + var d = new ScalarTypeDefinitionNode(TestLocations.Location2, _name1, _description2, _directives); + var e = new ScalarTypeDefinitionNode(TestLocations.Location1, _name2, _description2, _directives); + + // act + var aHash = a.GetHashCode(); + var bHash = b.GetHashCode(); + var cHash = c.GetHashCode(); + var dHash = d.GetHashCode(); + var eHash = e.GetHashCode(); + + // assert + Assert.Equal(aHash, bHash); + Assert.NotEqual(aHash, cHash); + Assert.Equal(cHash, dHash); + Assert.NotEqual(aHash, dHash); + Assert.NotEqual(dHash, eHash); + } +} diff --git a/src/HotChocolate/Language/test/Language.SyntaxTree.Tests/ScalarTypeExtensionNodeTests.cs b/src/HotChocolate/Language/test/Language.SyntaxTree.Tests/ScalarTypeExtensionNodeTests.cs new file mode 100644 index 00000000000..61935413b53 --- /dev/null +++ b/src/HotChocolate/Language/test/Language.SyntaxTree.Tests/ScalarTypeExtensionNodeTests.cs @@ -0,0 +1,76 @@ +using System; +using System.Collections.Generic; +using Xunit; + +namespace HotChocolate.Language.SyntaxTree; + +public class ScalarTypeExtensionNodeTests +{ + private readonly NameNode _name1 = new("name1"); + private readonly NameNode _name2 = new("name2"); + private readonly IReadOnlyList _directives = Array.Empty(); + + [Fact] + public void Equals_With_Same_Location() + { + // arrange + var a = new ScalarTypeExtensionNode(TestLocations.Location1, _name1, _directives); + var b = new ScalarTypeExtensionNode(TestLocations.Location1, _name1, _directives); + var c = new ScalarTypeExtensionNode(TestLocations.Location1, _name2, _directives); + + // act + var abResult = a.Equals(b); + var aaResult = a.Equals(a); + var acResult = a.Equals(c); + var aNullResult = a.Equals(default); + + // assert + Assert.True(abResult); + Assert.True(aaResult); + Assert.False(acResult); + Assert.False(aNullResult); + } + + [Fact] + public void Equals_With_Different_Location() + { + // arrange + var a = new ScalarTypeExtensionNode(TestLocations.Location1, _name1, _directives); + var b = new ScalarTypeExtensionNode(TestLocations.Location2, _name1, _directives); + var c = new ScalarTypeExtensionNode(TestLocations.Location3, _name2, _directives); + + // act + var abResult = a.Equals(b); + var aaResult = a.Equals(a); + var acResult = a.Equals(c); + var aNullResult = a.Equals(default); + + // assert + Assert.True(abResult); + Assert.True(aaResult); + Assert.False(acResult); + Assert.False(aNullResult); + } + + [Fact] + public void GetHashCode_With_Location() + { + // arrange + var a = new ScalarTypeExtensionNode(TestLocations.Location1, _name1, _directives); + var b = new ScalarTypeExtensionNode(TestLocations.Location2, _name1, _directives); + var c = new ScalarTypeExtensionNode(TestLocations.Location1, _name2, _directives); + var d = new ScalarTypeExtensionNode(TestLocations.Location2, _name2, _directives); + + // act + var aHash = a.GetHashCode(); + var bHash = b.GetHashCode(); + var cHash = c.GetHashCode(); + var dHash = d.GetHashCode(); + + // assert + Assert.Equal(aHash, bHash); + Assert.NotEqual(aHash, cHash); + Assert.Equal(cHash, dHash); + Assert.NotEqual(aHash, dHash); + } +}