Skip to content

Commit 7a799ce

Browse files
jnm2danmoseley
andauthored
Fix invalid CodeDom comment generation when a new line starts with a slash (#56640)
* Failing tests where CodeDom creates invalid comment types * Prevent CodeDom from creating invalid comment types * Make C# generator impl more consistent with VB generator * Switch attribute from ActiveIssue to SkipOnTargetFramework * Be even more conservative about when the space is needed * Attempt to improve readability * Update VBCodeGenerationTests.cs Co-authored-by: Dan Moseley <danmose@microsoft.com>
1 parent c36ba2d commit 7a799ce

File tree

6 files changed

+157
-2
lines changed

6 files changed

+157
-2
lines changed

src/libraries/System.CodeDom/src/Microsoft/CSharp/CSharpCodeGenerator.cs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -851,10 +851,20 @@ private void GenerateComment(CodeComment e)
851851
string commentLineStart = e.DocComment ? "///" : "//";
852852
Output.Write(commentLineStart);
853853
Output.Write(' ');
854+
bool isAfterCommentLineStart = false;
854855

855856
string value = e.Text;
856857
for (int i = 0; i < value.Length; i++)
857858
{
859+
if (isAfterCommentLineStart)
860+
{
861+
if (value[i] == '/' && (e.DocComment || !value.HasCharAt(i + 1, '/')))
862+
{
863+
Output.Write(' ');
864+
}
865+
isAfterCommentLineStart = false;
866+
}
867+
858868
if (value[i] == '\u0000')
859869
{
860870
continue;
@@ -863,22 +873,25 @@ private void GenerateComment(CodeComment e)
863873

864874
if (value[i] == '\r')
865875
{
866-
if (i < value.Length - 1 && value[i + 1] == '\n')
876+
if (value.HasCharAt(i + 1, '\n'))
867877
{ // if next char is '\n', skip it
868878
Output.Write('\n');
869879
i++;
870880
}
871881
_output.InternalOutputTabs();
872882
Output.Write(commentLineStart);
883+
isAfterCommentLineStart = true;
873884
}
874885
else if (value[i] == '\n')
875886
{
876887
_output.InternalOutputTabs();
877888
Output.Write(commentLineStart);
889+
isAfterCommentLineStart = true;
878890
}
879891
else if (value[i] == '\u2028' || value[i] == '\u2029' || value[i] == '\u0085')
880892
{
881893
Output.Write(commentLineStart);
894+
isAfterCommentLineStart = true;
882895
}
883896
}
884897
Output.WriteLine();

src/libraries/System.CodeDom/src/Microsoft/VisualBasic/VBCodeGenerator.cs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1224,29 +1224,43 @@ protected override void GenerateComment(CodeComment e)
12241224
{
12251225
string commentLineStart = e.DocComment ? "'''" : "'";
12261226
Output.Write(commentLineStart);
1227+
bool isAfterCommentLineStart = true;
1228+
12271229
string value = e.Text;
12281230
for (int i = 0; i < value.Length; i++)
12291231
{
1232+
if (isAfterCommentLineStart)
1233+
{
1234+
if (value[i] == '\'' && (e.DocComment || (value.HasCharAt(i + 1, '\'') && !value.HasCharAt(i + 2, '\''))))
1235+
{
1236+
Output.Write(' ');
1237+
}
1238+
isAfterCommentLineStart = false;
1239+
}
1240+
12301241
Output.Write(value[i]);
12311242

12321243
if (value[i] == '\r')
12331244
{
1234-
if (i < value.Length - 1 && value[i + 1] == '\n')
1245+
if (value.HasCharAt(i + 1, '\n'))
12351246
{ // if next char is '\n', skip it
12361247
Output.Write('\n');
12371248
i++;
12381249
}
12391250
((ExposedTabStringIndentedTextWriter)Output).InternalOutputTabs();
12401251
Output.Write(commentLineStart);
1252+
isAfterCommentLineStart = true;
12411253
}
12421254
else if (value[i] == '\n')
12431255
{
12441256
((ExposedTabStringIndentedTextWriter)Output).InternalOutputTabs();
12451257
Output.Write(commentLineStart);
1258+
isAfterCommentLineStart = true;
12461259
}
12471260
else if (value[i] == '\u2028' || value[i] == '\u2029' || value[i] == '\u0085')
12481261
{
12491262
Output.Write(commentLineStart);
1263+
isAfterCommentLineStart = true;
12501264
}
12511265
}
12521266
Output.WriteLine();
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
namespace System.CodeDom
5+
{
6+
internal static class StringExtensions
7+
{
8+
internal static bool HasCharAt(this string value, int index, char character)
9+
{
10+
return index < value.Length && value[index] == character;
11+
}
12+
}
13+
}

src/libraries/System.CodeDom/src/System.CodeDom.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ Microsoft.VisualBasic.VBCodeProvider</PackageDescription>
136136
<Compile Include="System\CodeDom\MemberAttributes.cs" />
137137
<Compile Include="System\Collections\Specialized\FixedStringLookup.cs" />
138138
<Compile Include="$(CommonPath)System\CSharpHelpers.cs" />
139+
<Compile Include="StringExtensions.cs" />
139140
</ItemGroup>
140141
<ItemGroup Condition="'$(TargetFrameworkIdentifier)' == '.NETCoreApp'">
141142
<Reference Include="System.Collections" />

src/libraries/System.CodeDom/tests/System/CodeDom/Compiler/CSharpCodeGenerationTests.cs

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3516,5 +3516,61 @@ private void Test<T1, T2, T3, T4>()
35163516
}
35173517
");
35183518
}
3519+
3520+
[Fact]
3521+
[SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "The bug was present on .NET Framework: https://github.com/dotnet/runtime/issues/56267")]
3522+
public void OrdinaryCommentsDoNotAccidentallyBecomeDocumentationComments()
3523+
{
3524+
var codeTypeDeclaration = new CodeTypeDeclaration("ClassWithCommment")
3525+
{
3526+
IsClass = true,
3527+
Comments =
3528+
{
3529+
new CodeCommentStatement(
3530+
"/ Lines starting with exactly one slash" + Environment.NewLine +
3531+
"/ each get a separating space," + Environment.NewLine +
3532+
"but other lines do not get a space. This way generated files only change on tool upgrade where there were generation bugs."+ Environment.NewLine +
3533+
"// This includes lines starting with more than one slash.",
3534+
docComment: false),
3535+
},
3536+
};
3537+
3538+
AssertEqualPreserveLineBreaks(codeTypeDeclaration,
3539+
@"
3540+
// / Lines starting with exactly one slash
3541+
// / each get a separating space,
3542+
//but other lines do not get a space. This way generated files only change on tool upgrade where there were generation bugs.
3543+
//// This includes lines starting with more than one slash.
3544+
public class ClassWithCommment {
3545+
}
3546+
");
3547+
}
3548+
3549+
[Fact]
3550+
[SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "The bug was present on .NET Framework: https://github.com/dotnet/runtime/issues/56267")]
3551+
public void DocumentationCommentsDoNotAccidentallyBecomeOrdinaryComments()
3552+
{
3553+
var codeTypeDeclaration = new CodeTypeDeclaration("ClassWithCommment")
3554+
{
3555+
IsClass = true,
3556+
Comments =
3557+
{
3558+
new CodeCommentStatement(
3559+
"/ Lines starting with a slash each get a separating space," + Environment.NewLine +
3560+
"// including lines starting with more than one slash," + Environment.NewLine +
3561+
"but other lines do not get a space. This way generated files only change on tool upgrade where there were generation bugs.",
3562+
docComment: true),
3563+
},
3564+
};
3565+
3566+
AssertEqualPreserveLineBreaks(codeTypeDeclaration,
3567+
@"
3568+
/// / Lines starting with a slash each get a separating space,
3569+
/// // including lines starting with more than one slash,
3570+
///but other lines do not get a space. This way generated files only change on tool upgrade where there were generation bugs.
3571+
public class ClassWithCommment {
3572+
}
3573+
");
3574+
}
35193575
}
35203576
}

src/libraries/System.CodeDom/tests/System/CodeDom/Compiler/VBCodeGenerationTests.cs

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3264,5 +3264,63 @@ End Sub
32643264
End Class
32653265
End Namespace");
32663266
}
3267+
3268+
[Fact]
3269+
[SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "The bug was present on .NET Framework: https://github.com/dotnet/runtime/issues/56267")]
3270+
public void OrdinaryCommentsDoNotAccidentallyBecomeDocumentationComments()
3271+
{
3272+
var codeTypeDeclaration = new CodeTypeDeclaration("ClassWithCommment")
3273+
{
3274+
IsClass = true,
3275+
Comments =
3276+
{
3277+
new CodeCommentStatement(
3278+
"'' Lines starting with exactly two single quotes" + Environment.NewLine +
3279+
"'' each get a separating space," + Environment.NewLine +
3280+
"but other lines do not get a space. This way generated files only change on tool upgrade where there were generation bugs." + Environment.NewLine +
3281+
"' Not even lines starting with only one single quote" + Environment.NewLine +
3282+
"''' or three single quotes.",
3283+
docComment: false),
3284+
},
3285+
};
3286+
3287+
AssertEqualPreserveLineBreaks(codeTypeDeclaration,
3288+
@"
3289+
' '' Lines starting with exactly two single quotes
3290+
' '' each get a separating space,
3291+
'but other lines do not get a space. This way generated files only change on tool upgrade where there were generation bugs.
3292+
'' Not even lines starting with only one single quote
3293+
'''' or three single quotes.
3294+
Public Class ClassWithCommment
3295+
End Class
3296+
");
3297+
}
3298+
3299+
[Fact]
3300+
[SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "The bug was present on .NET Framework: https://github.com/dotnet/runtime/issues/56267")]
3301+
public void DocumentationCommentsDoNotAccidentallyBecomeOrdinaryComments()
3302+
{
3303+
var codeTypeDeclaration = new CodeTypeDeclaration("ClassWithCommment")
3304+
{
3305+
IsClass = true,
3306+
Comments =
3307+
{
3308+
new CodeCommentStatement(
3309+
"' Lines starting with a single quote" + Environment.NewLine +
3310+
"'' or more than one quote, each get a separating space," + Environment.NewLine +
3311+
"but other lines do not get a space. This way generated files only change on tool upgrade where there were generation bugs.",
3312+
docComment: true),
3313+
},
3314+
};
3315+
3316+
AssertEqualPreserveLineBreaks(codeTypeDeclaration,
3317+
@"
3318+
''' ' Lines starting with a single quote
3319+
''' '' or more than one quote, each get a separating space,
3320+
'''but other lines do not get a space. This way generated files only change on tool upgrade where there were generation bugs.
3321+
Public Class ClassWithCommment
3322+
End Class
3323+
");
3324+
}
32673325
}
32683326
}

0 commit comments

Comments
 (0)