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
2 changes: 2 additions & 0 deletions Coder.Test/Languages/CGeneratedSourceCompilesTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,8 @@ private static SourceFile Exemplar()
file.Members.Add(CompiledExemplar.Shape());
file.Members.Add(CompiledExemplar.OriginAlias());
file.Members.Add(CompiledExemplar.OriginTable());
file.Members.Add(new EnumDeclaration("Empty"));
file.Members.Add(new EnumDeclaration("EmptyFlags") { UnderlyingType = "long" });
file.Members.Add(new CompileTimeAssertion
{
Condition = "sizeof(Point) == 2 * sizeof(int)",
Expand Down
15 changes: 15 additions & 0 deletions Coder.Test/Languages/CGeneratorTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,21 @@
Assert.IsFalse(code.Contains("enum Colour : int", StringComparison.Ordinal));
}

/// <summary>
/// Tests that an enumeration with no members is a typedef of the type that would hold one, since
/// ISO C has no empty enumerator list.
/// </summary>
[TestMethod]
public void Enum_WithNoMembers_IsATypedefOfItsType()
{
string bare = Generator.Generate(new EnumDeclaration("Empty"));
string typed = Generator.Generate(new EnumDeclaration("Flags") { UnderlyingType = "long" });

StringAssert.EndsWith(bare, $"typedef int Empty;{NewLine}", StringComparison.Ordinal);

Check warning on line 417 in Coder.Test/Languages/CGeneratorTests.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use 'Assert.EndsWith' instead of 'StringAssert.EndsWith'

See more on https://sonarcloud.io/project/issues?id=ktsu-dev_Coder&issues=AaEeiBGtFg1XlrnQfKHO&open=AaEeiBGtFg1XlrnQfKHO&pullRequest=195
StringAssert.EndsWith(typed, $"typedef long long Flags;{NewLine}", StringComparison.Ordinal);

Check warning on line 418 in Coder.Test/Languages/CGeneratorTests.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use 'Assert.EndsWith' instead of 'StringAssert.EndsWith'

See more on https://sonarcloud.io/project/issues?id=ktsu-dev_Coder&issues=AaEeiBGtFg1XlrnQfKHP&open=AaEeiBGtFg1XlrnQfKHP&pullRequest=195
Assert.IsFalse(bare.Contains("typedef enum", StringComparison.Ordinal));
}

/// <summary>
/// Tests that a constant is <c>static const</c>. A file-scope <c>const</c> in C has external
/// linkage, so a header declaring one and included twice is the same object defined twice.
Expand Down
1 change: 1 addition & 0 deletions Coder.Test/Languages/RustGeneratedSourceCompilesTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,7 @@ private static SourceFile Exemplar()
geometry.Members.Add(CompiledExemplar.Measure());
geometry.Members.Add(Boxed());
geometry.Members.Add(Clamp());
geometry.Members.Add(new EnumDeclaration("Empty") { UnderlyingType = "int" });
geometry.Members.Add(new CompileTimeAssertion
{
Condition = "std::mem::size_of::<i32>() == 4",
Expand Down
13 changes: 13 additions & 0 deletions Coder.Test/Languages/RustGeneratorTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -562,6 +562,19 @@
code);
}

/// <summary>
/// Tests that an enumeration with no variants leaves off <c>#[repr]</c>, which rustc rejects on
/// one (E0084).
/// </summary>
[TestMethod]
public void Enum_WithNoVariants_HasNoRepr()
{
string code = Generator.Generate(new EnumDeclaration("Empty") { UnderlyingType = "int" });

Assert.IsFalse(code.Contains("#[repr", StringComparison.Ordinal));
StringAssert.StartsWith(code, "pub enum Empty {", StringComparison.Ordinal);

Check warning on line 575 in Coder.Test/Languages/RustGeneratorTests.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use 'Assert.StartsWith' instead of 'StringAssert.StartsWith'

See more on https://sonarcloud.io/project/issues?id=ktsu-dev_Coder&issues=AaEeiBBVFg1XlrnQfKHN&open=AaEeiBBVFg1XlrnQfKHN&pullRequest=195
}

/// <summary>
/// Tests that a constant is a <c>const</c> and an ordinary field a <c>static</c>, and that an
/// array one is a borrowed slice — the bound-free spelling Rust does have, which is what a table
Expand Down
12 changes: 12 additions & 0 deletions Coder/Languages/CGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@
/// The <c>&lt;math.h&gt;</c> macros, which is why a file holding one includes it.
/// </remarks>
protected override string SpellNonFiniteDouble(double value) =>
double.IsNaN(value) ? "NAN" : value > 0 ? "INFINITY" : "-INFINITY";

Check warning on line 169 in Coder/Languages/CGenerator.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 169 in Coder/Languages/CGenerator.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 169 in Coder/Languages/CGenerator.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 169 in Coder/Languages/CGenerator.cs

View workflow job for this annotation

GitHub Actions / ci / .NET / Analyze & Release

Extract this nested ternary operation into an independent statement.

/// <inheritdoc/>
protected override IEnumerable<string> RequiredImports(SourceFile file)
Expand Down Expand Up @@ -878,6 +878,10 @@
/// that needs the guarantee can assert it with a <see cref="CompileTimeAssertion"/>, which is a
/// thing the AST can already say.
/// </para>
/// <para>
/// An enumeration with no members is a typedef of its underlying type, or of <c>int</c>, since
/// ISO C has no empty enumerator list and <c>typedef enum Name { } Name;</c> does not compile.
/// </para>
/// </remarks>
protected override void GenerateEnumDeclaration(EnumDeclaration enumDecl, CodeBlocker code)
{
Expand All @@ -886,6 +890,14 @@

string name = enumDecl.Name ?? "UnnamedEnum";

if (enumDecl.Members.Count == 0)
{
WriteInexpressible(code, $"enum {name} has no members: C has no empty enum, so it is the type that would hold one");
WritePreamble(enumDecl, code, PreambleSite.Enum);
code.WriteLine($"typedef {(enumDecl.UnderlyingType is TypeReference type ? MapToCType(type) : "int")} {name};");
return;
}

// Above the documentation rather than below it, so the comment block in front of the
// declaration stays one block rather than the note splitting it in two.
if (enumDecl.UnderlyingType is TypeReference underlying)
Expand Down
5 changes: 3 additions & 2 deletions Coder/Languages/RustGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -341,7 +341,7 @@
/// The associated constants of <c>f64</c>, which need nothing imported.
/// </remarks>
protected override string SpellNonFiniteDouble(double value) =>
double.IsNaN(value) ? "f64::NAN" : value > 0 ? "f64::INFINITY" : "f64::NEG_INFINITY";

Check warning on line 344 in Coder/Languages/RustGenerator.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 344 in Coder/Languages/RustGenerator.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 344 in Coder/Languages/RustGenerator.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 344 in Coder/Languages/RustGenerator.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 @@ -782,7 +782,7 @@
HashSet<string> reassigned = op.ReturnsBool
? []
: ReassignedNames(funcDecl.Body, [SelfName, .. funcDecl.Parameters.Select(OperandName)]);
string self = op.ReturnsBool ? "&self" : reassigned.Contains(SelfName) ? $"mut {SelfName}" : SelfName;

Check warning on line 785 in Coder/Languages/RustGenerator.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 785 in Coder/Languages/RustGenerator.cs

View workflow job for this annotation

GitHub Actions / ci / .NET / Analyze & Release

Extract this nested ternary operation into an independent statement.
code.Write($"fn {op.Method}({self}");

foreach (Parameter parameter in funcDecl.Parameters)
Expand Down Expand Up @@ -1335,7 +1335,8 @@
/// An enumeration's variants are named through it — <c>Colour::Red</c> — so nothing has to be
/// prefixed to keep two enumerations with a <c>None</c> each apart, which is the thing C cannot
/// do. A fixed underlying type is <c>#[repr]</c>, which is the guarantee rather than a comment
/// about one.
/// about one. An enumeration with no variants has no representation to fix, and rustc rejects
/// <c>#[repr]</c> on one (E0084), so it is left off.
/// </remarks>
protected override void GenerateEnumDeclaration(EnumDeclaration enumDecl, CodeBlocker code)
{
Expand All @@ -1344,7 +1345,7 @@

WritePreamble(enumDecl, code, PreambleSite.Enum);

if (enumDecl.UnderlyingType is TypeReference underlying)
if (enumDecl.UnderlyingType is TypeReference underlying && enumDecl.Members.Count > 0)
{
code.WriteLine($"#[repr({SpellType(underlying)})]");
}
Expand Down
Loading