Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
61 changes: 61 additions & 0 deletions Coder.Test/Languages/NonFiniteDoubleLiteralTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,46 @@
directory => File.WriteAllText(Path.Combine(directory, "go.mod"), "module limits\n\ngo 1.21\n"));
}

/// <summary>
/// Tests that a constant field or local holding NaN or an infinity is a Go <c>var</c>, because
/// <c>math.NaN()</c> and <c>math.Inf</c> are calls and a Go <c>const</c> cannot hold one.
/// </summary>
[TestMethod]
public void GoConstantNonFiniteValue_IsAVar()
{
string code = new GoGenerator().Generate(FileWithNonFiniteConstants());

StringAssert.Contains(code, "var Unset float64 = math.NaN()", StringComparison.Ordinal);

Check warning on line 166 in Coder.Test/Languages/NonFiniteDoubleLiteralTests.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use 'Assert.Contains' instead of 'StringAssert.Contains'

See more on https://sonarcloud.io/project/issues?id=ktsu-dev_Coder&issues=AaEejDRODOOFX2T2w57p&open=AaEejDRODOOFX2T2w57p&pullRequest=196
StringAssert.Contains(code, "var ceiling float64 = math.Inf(1)", StringComparison.Ordinal);

Check warning on line 167 in Coder.Test/Languages/NonFiniteDoubleLiteralTests.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use 'Assert.Contains' instead of 'StringAssert.Contains'

See more on https://sonarcloud.io/project/issues?id=ktsu-dev_Coder&issues=AaEejDRODOOFX2T2w57q&open=AaEejDRODOOFX2T2w57q&pullRequest=196
Assert.DoesNotContain("const Unset", code, StringComparison.Ordinal, code);
Assert.DoesNotContain("const ceiling", code, StringComparison.Ordinal, code);
}

/// <summary>
/// Tests that a Go file declaring a constant NaN field and a constant infinite local compiles.
/// </summary>
[TestMethod]
public void GeneratedGoConstants_Compile()
{
if (ToolchainHarness.FindOnPath("version", "go") is null)
{
Assert.Inconclusive("No Go toolchain on the path, so nothing was compiled.");
return;
}

ToolchainHarness.InTemporaryDirectory(directory =>
{
File.WriteAllText(Path.Combine(directory, "go.mod"), "module limits\n\ngo 1.21\n");

string code = new GoGenerator().Generate(FileWithNonFiniteConstants());
File.WriteAllText(Path.Combine(directory, "limits.go"), code);

(int exitCode, string output) = ToolchainHarness.Run("go", "build ./...", directory);

Assert.AreEqual(0, exitCode, $"go rejected the generated source:{Environment.NewLine}{code}{Environment.NewLine}{output}");
});
}

/// <summary>
/// Tests that a Rust file returning NaN and both infinities compiles.
/// </summary>
Expand Down Expand Up @@ -198,6 +238,27 @@
return file;
}

/// <summary>
/// Builds a file with a constant NaN field and a function holding a constant infinite local.
/// </summary>
private static SourceFile FileWithNonFiniteConstants()
{
SourceFile file = new("limits");
file.Members.Add(new FieldDeclaration("Unset", new TypeReference("double"))
{
IsConstant = true,
IsStatic = true,
InitialValue = new LiteralExpression<double>(double.NaN),
});

FunctionDeclaration function = new("ceiling") { ReturnType = new TypeReference("double") };
function.Body.Add(new VariableDeclaration("ceiling", "double", new LiteralExpression<double>(double.PositiveInfinity)) { IsConstant = true });
function.Body.Add(new ReturnStatement(new VariableReference("ceiling")));
file.Members.Add(function);

return file;
}

private static int Occurrences(string text, string value) =>
text.Split(value, StringSplitOptions.None).Length - 1;

Expand Down
19 changes: 18 additions & 1 deletion Coder/Languages/GoGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,7 @@
/// holds one.
/// </remarks>
protected override string SpellNonFiniteDouble(double value) =>
double.IsNaN(value) ? "math.NaN()" : value > 0 ? "math.Inf(1)" : "math.Inf(-1)";

Check warning on line 272 in Coder/Languages/GoGenerator.cs

View workflow job for this annotation

GitHub Actions / ci / .NET / Analyze & Release

Extract this nested ternary operation into an independent statement.

Check warning on line 272 in Coder/Languages/GoGenerator.cs

View workflow job for this annotation

GitHub Actions / ci / .NET / Analyze & Release

Extract this nested ternary operation into an independent statement.

Check warning on line 272 in Coder/Languages/GoGenerator.cs

View workflow job for this annotation

GitHub Actions / ci / .NET / Analyze & Release

Extract this nested ternary operation into an independent statement.

Check warning on line 272 in Coder/Languages/GoGenerator.cs

View workflow job for this annotation

GitHub Actions / ci / .NET / Analyze & Release

Extract this nested ternary operation into an independent statement.

Check warning on line 272 in Coder/Languages/GoGenerator.cs

View workflow job for this annotation

GitHub Actions / ci / .NET / Analyze & Release

Extract this nested ternary operation into an independent statement.

Check warning on line 272 in Coder/Languages/GoGenerator.cs

View workflow job for this annotation

GitHub Actions / ci / .NET / Analyze & Release

Extract this nested ternary operation into an independent statement.

Check warning on line 272 in Coder/Languages/GoGenerator.cs

View workflow job for this annotation

GitHub Actions / ci / .NET / Analyze & Release

Extract this nested ternary operation into an independent statement.

Check warning on line 272 in Coder/Languages/GoGenerator.cs

View workflow job for this annotation

GitHub Actions / ci / .NET / Analyze & Release

Extract this nested ternary operation into an independent statement.

/// <inheritdoc/>
/// <remarks>
Expand Down Expand Up @@ -1370,7 +1370,24 @@
/// <param name="wanted">Whether the declaration says its value never changes.</param>
/// <param name="value">What it starts at.</param>
/// <returns>True when Go will hold it as a constant.</returns>
private static bool IsConstant(bool wanted, AstNode? value) => wanted && IsCompileTimeValue(value);
/// <remarks>
/// NaN and the infinities are the one kind of number that is not: Go has no constant spelling for
/// them, and <c>math.NaN()</c> and <c>math.Inf(1)</c> are calls, which a <c>const</c> cannot hold.
/// </remarks>
private static bool IsConstant(bool wanted, AstNode? value) =>
wanted && IsCompileTimeValue(value) && !IsNonFinite(value);

/// <summary>
/// Reports whether a value is a NaN or infinite floating-point literal.
/// </summary>
/// <param name="value">The value to test.</param>
/// <returns>True when it is.</returns>
private static bool IsNonFinite(AstNode? value) => value switch
{
LiteralExpression<double> literal => !double.IsFinite(literal.Value),
LiteralExpression<float> literal => !float.IsFinite(literal.Value),
_ => false,
};

/// <summary>
/// Writes what a declaration starts at, giving a bare list the declaration's own type.
Expand Down
Loading