Skip to content

Commit 49cbf69

Browse files
committed
tracking down weird issue with xml reader and line numbers being off
1 parent 0a52e6c commit 49cbf69

File tree

6 files changed

+168
-43
lines changed

6 files changed

+168
-43
lines changed
Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,23 @@
11
<component name="ProjectRunConfigurationManager">
2-
<configuration
3-
default="false"
4-
name="Debug msbuild"
5-
type="DotNetProject"
6-
factoryName=".NET Project"
7-
>
2+
<configuration default="false" name="Debug CLI" type="DotNetProject" factoryName=".NET Project">
83
<option
94
name="EXE_PATH"
10-
value="$PROJECT_DIR$/Src/CSharpier.Cli/bin/Debug/net8.0/CSharpier.exe"
5+
value="$PROJECT_DIR$/Src/CSharpier.Cli/bin/Debug/net8.0/CSharpier.dll"
116
/>
127
<option
138
name="PROGRAM_PARAMETERS"
14-
value="check C:\projects\csharpier-msbuild --log-format MsBuild"
9+
value="format C:\projects\csharpier-repos\mono/mcs/docs/ecma334/14.5.10.1.xml"
1510
/>
1611
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/Src/CSharpier.Cli/bin/Debug/net8.0" />
1712
<option name="PASS_PARENT_ENVS" value="1" />
1813
<option name="USE_EXTERNAL_CONSOLE" value="0" />
1914
<option name="ENV_FILE_PATHS" value="" />
2015
<option name="REDIRECT_INPUT_PATH" value="" />
2116
<option name="PTY_MODE" value="Auto" />
17+
<option name="MIXED_MODE_DEBUG" value="0" />
2218
<option name="USE_MONO" value="0" />
2319
<option name="RUNTIME_ARGUMENTS" value="" />
2420
<option name="AUTO_ATTACH_CHILDREN" value="0" />
25-
<option name="MIXED_MODE_DEBUG" value="0" />
2621
<option name="PROJECT_PATH" value="$PROJECT_DIR$/Src/CSharpier.Cli/CSharpier.Cli.csproj" />
2722
<option name="PROJECT_EXE_PATH_TRACKING" value="1" />
2823
<option name="PROJECT_ARGUMENTS_TRACKING" value="1" />

.run/Playground.run.xml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
name="LAUNCH_PROFILE_PROJECT_FILE_PATH"
1010
value="$PROJECT_DIR$/Src/CSharpier.Playground/CSharpier.Playground.csproj"
1111
/>
12-
<option name="LAUNCH_PROFILE_TFM" value="net9.0" />
12+
<option name="LAUNCH_PROFILE_TFM" value="net10.0" />
1313
<option name="LAUNCH_PROFILE_NAME" value="Watch" />
1414
<option name="USE_EXTERNAL_CONSOLE" value="0" />
1515
<option name="USE_MONO" value="0" />
@@ -18,6 +18,7 @@
1818
<option name="SHOW_IIS_EXPRESS_OUTPUT" value="0" />
1919
<option name="SEND_DEBUG_REQUEST" value="1" />
2020
<option name="ADDITIONAL_IIS_EXPRESS_ARGUMENTS" value="" />
21+
<option name="AUTO_ATTACH_CHILDREN" value="0" />
2122
<method v="2">
2223
<option name="Build" />
2324
</method>

Src/CSharpier.Core/Xml/RawAttributeReader.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ private string GetRawAttribute(string attributeName)
3737
var lineNumber = this.xmlLineInfo.LineNumber - 1;
3838
var line = this.lines[lineNumber];
3939

40+
DebugLogger.Log("Reading attribute " + attributeName + " on line " + lineNumber);
41+
4042
var index = line.IndexOf(
4143
attributeName,
4244
this.xmlLineInfo.LinePosition - 1,

Src/CSharpier.Core/Xml/RawNodeReader.cs

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,20 @@ class RawNodeReader
1616
private static partial Regex NewlineRegex();
1717
#endif
1818

19-
public static List<RawNode> ReadAllNodes(
20-
string originalXml,
21-
string lineEnding,
22-
XmlReader xmlReader
23-
)
19+
public static List<RawNode> ReadAllNodes(string originalXml, string lineEnding)
2420
{
21+
// xml reader can get confused about line numbers if we don't normalize them
22+
originalXml = NewlineRegex
23+
#if !NETSTANDARD2_0
24+
()
25+
#endif
26+
.Replace(originalXml, "\n");
27+
28+
using var xmlReader = XmlReader.Create(
29+
new StringReader(originalXml),
30+
new XmlReaderSettings { IgnoreWhitespace = false }
31+
);
32+
2533
var elements = new List<RawNode>();
2634
var elementStack = new Stack<RawNode>();
2735
var attributeReader = new RawAttributeReader(originalXml, lineEnding, xmlReader);

Src/CSharpier.Core/Xml/XmlFormatter.cs

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,8 @@ internal static CodeFormatterResult Format(string xml, PrinterOptions printerOpt
1919
{
2020
try
2121
{
22-
using var xmlReader = XmlReader.Create(
23-
new StringReader(xml),
24-
new XmlReaderSettings { IgnoreWhitespace = false }
25-
);
26-
2722
var lineEnding = PrinterOptions.GetLineEnding(xml, printerOptions);
28-
var elements = RawNodeReader.ReadAllNodes(xml, lineEnding, xmlReader);
23+
var elements = RawNodeReader.ReadAllNodes(xml, lineEnding);
2924
var printingContext = new PrintingContext
3025
{
3126
Options = new PrintingContext.PrintingContextOptions

Src/CSharpier.Tests/RawNodeReaderTests.cs

Lines changed: 146 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,9 @@ public void Should_Read_Text()
1717
<Text>text</Text>
1818
""";
1919

20-
var elements = ReadAllElements(xml);
21-
elements.Count.Should().Be(1);
22-
elements[0].Nodes.Count.Should().Be(1);
20+
var nodes = ReadAllNodes(xml);
21+
nodes.Count.Should().Be(1);
22+
nodes[0].Nodes.Count.Should().Be(1);
2323
}
2424

2525
[Test]
@@ -33,8 +33,8 @@ public void Should_Read_Comments()
3333
</Root>
3434
""";
3535

36-
var elements = ReadAllElements(xml);
37-
elements.Count.Should().Be(2);
36+
var nodes = ReadAllNodes(xml);
37+
nodes.Count.Should().Be(2);
3838
}
3939

4040
[Test]
@@ -48,12 +48,12 @@ public void Should_Populate_Previous_And_Next()
4848
</Root>
4949
""";
5050

51-
var elements = ReadAllElements(xml);
52-
elements.Count.Should().Be(1);
53-
elements[0].Nodes.Count.Should().Be(3);
54-
var first = elements[0].Nodes[0];
55-
var second = elements[0].Nodes[1];
56-
var third = elements[0].Nodes[2];
51+
var nodes = ReadAllNodes(xml);
52+
nodes.Count.Should().Be(1);
53+
nodes[0].Nodes.Count.Should().Be(3);
54+
var first = nodes[0].Nodes[0];
55+
var second = nodes[0].Nodes[1];
56+
var third = nodes[0].Nodes[2];
5757

5858
first.NextNode.Should().Be(second);
5959
first.PreviousNode.Should().BeNull();
@@ -70,9 +70,9 @@ public void Should_Read_Attributes()
7070
<element one="1" two="2" />
7171
""";
7272

73-
var elements = ReadAllElements(xml);
73+
var nodes = ReadAllNodes(xml);
7474

75-
var attributes = elements.First().Attributes;
75+
var attributes = nodes.First().Attributes;
7676

7777
attributes.Length.Should().Be(2);
7878
attributes[0].Name.Should().Be("one");
@@ -101,20 +101,144 @@ public void Should_Read_Attributes()
101101
)]
102102
public void Should_Read_Various_Attributes(string xml, string attributeValue)
103103
{
104-
var elements = ReadAllElements(xml);
105-
var attribute = elements.First().Attributes.First();
104+
var nodes = ReadAllNodes(xml);
105+
var attribute = nodes.First().Attributes.First();
106106

107107
attribute.Name.Should().Be("Attribute");
108-
attribute.Value.Should().Be(attributeValue.Replace("\"", "&quote;"));
108+
attribute.Value.Should().Be(attributeValue.Replace("\"", "&quot;"));
109109
}
110110

111-
private static List<RawNode> ReadAllElements(string xml)
111+
[Test]
112+
public void Should_Work_With_This_File_And_CRLF()
112113
{
113-
using var reader = XmlReader.Create(
114-
new StringReader(xml),
115-
new XmlReaderSettings { IgnoreWhitespace = false }
116-
);
114+
var xml = """
115+
<?xml version="1.0"?>
116+
<clause number="14.5.10.1" title="Object creation expressions">
117+
<paragraph
118+
>An <non_terminal where="14.5.10.1"
119+
>object-creation-expression</non_terminal
120+
> is used to create a new instance of a <non_terminal where="11.2"
121+
>class-type</non_terminal
122+
> or a <non_terminal where="11.1">value-type</non_terminal>. <grammar_production>
123+
<name>
124+
<non_terminal where="14.5.10.1">object-creation-expression</non_terminal>
125+
</name> : <rhs>
126+
<keyword>new</keyword>
127+
<non_terminal where="11">type</non_terminal>
128+
<terminal>(</terminal>
129+
<non_terminal where="14.4.1">argument-list</non_terminal>
130+
<opt />
131+
<terminal>)</terminal>
132+
</rhs>
133+
</grammar_production>
134+
</paragraph>
135+
<paragraph
136+
>The type of an <non_terminal where="14.5.10.1"
137+
>object-creation-expression</non_terminal
138+
> must be a <non_terminal where="11.2">class-type</non_terminal> or a <non_terminal where="11.1"
139+
>value-type</non_terminal
140+
>. The type cannot be an abstract <non_terminal where="11.2"
141+
>class-type</non_terminal
142+
>. </paragraph
143+
>
144+
<paragraph
145+
>The optional <non_terminal where="14.4.1"
146+
>argument-list</non_terminal
147+
> (<hyperlink>14.4.1</hyperlink>) is permitted only if the type is a <non_terminal where="11.2"
148+
>class-type</non_terminal
149+
> or a <non_terminal where="11.1">struct-type</non_terminal>. </paragraph
150+
>
151+
<paragraph
152+
>The compile-time processing of an <non_terminal where="14.5.10.1"
153+
>object-creation-expression</non_terminal
154+
> of the form new T(A), where T is a <non_terminal where="11.2"
155+
>class-type</non_terminal
156+
> or a <non_terminal where="11.1">value-type</non_terminal> and A is an optional <non_terminal
157+
where="14.4.1"
158+
>argument-list</non_terminal
159+
>, consists of the following steps: <list>
160+
<list_item
161+
> If T is a <non_terminal where="11.1"
162+
>value-type</non_terminal
163+
> and A is not present: </list_item
164+
>
165+
<list>
166+
<list_item
167+
> The <non_terminal where="14.5.10.1"
168+
>object-creation-expression</non_terminal
169+
> is a default constructor invocation. The result of the <non_terminal where="14.5.10.1"
170+
>object-creation-expression</non_terminal
171+
> is a value of type T, namely the default value for T as defined in <hyperlink>11.1.1</hyperlink>. </list_item
172+
>
173+
</list>
174+
<list_item
175+
> Otherwise, if T is a <non_terminal where="11.2"
176+
>class-type</non_terminal
177+
> or a struct-type: </list_item
178+
>
179+
<list>
180+
<list_item
181+
> If T is an abstract <non_terminal where="11.2"
182+
>class-type</non_terminal
183+
>, a compile-time error occurs. </list_item
184+
>
185+
<list_item
186+
> The instance constructor to invoke is determined using the overload resolution rules of <hyperlink>14.4.2</hyperlink>. The set of candidate instance constructors consists of all accessible instance constructors declared in T. If the set of candidate instance constructors is empty, or if a single best instance constructor cannot be identified, a compile-time error occurs. </list_item
187+
>
188+
<list_item
189+
> The result of the <non_terminal where="14.5.10.1"
190+
>object-creation-expression</non_terminal
191+
> is a value of type T, namely the value produced by invoking the instance constructor determined in the step above. </list_item
192+
>
193+
</list>
194+
<list_item
195+
> Otherwise, the <non_terminal where="14.5.10.1"
196+
>object-creation-expression</non_terminal
197+
> is invalid, and a compile-time error occurs. </list_item
198+
>
199+
</list>
200+
</paragraph>
201+
<paragraph
202+
>The run-time processing of an <non_terminal where="14.5.10.1"
203+
>object-creation-expression</non_terminal
204+
> of the form new T(A), where T is <non_terminal where="11.2"
205+
>class-type</non_terminal
206+
> or a <non_terminal where="11.1">struct-type</non_terminal> and A is an optional <non_terminal
207+
where="14.4.1"
208+
>argument-list</non_terminal
209+
>, consists of the following steps: <list>
210+
<list_item> If T is a class-type: </list_item>
211+
<list>
212+
<list_item> A new instance of class T is allocated. If there is not enough memory available to allocate the new instance, a System.OutOfMemoryException is thrown and no further steps are executed. </list_item>
213+
<list_item
214+
> All fields of the new instance are initialized to their default values (<hyperlink>12.2</hyperlink>). </list_item
215+
>
216+
<list_item
217+
> The instance constructor is invoked according to the rules of function member invocation (<hyperlink>14.4.3</hyperlink>). A reference to the newly allocated instance is automatically passed to the instance constructor and the instance can be accessed from within that constructor as this. </list_item
218+
>
219+
</list>
220+
<list_item> If T is a struct-type: </list_item>
221+
<list>
222+
<list_item
223+
> An instance of type T is created by allocating a temporary local variable. Since an instance constructor of a <non_terminal
224+
where="11.1"
225+
>struct-type</non_terminal
226+
> is required to definitely assign a value to each field of the instance being created, no initialization of the temporary variable is necessary. </list_item
227+
>
228+
<list_item
229+
> The instance constructor is invoked according to the rules of function member invocation (<hyperlink>14.4.3</hyperlink>). A reference to the newly allocated instance is automatically passed to the instance constructor and the instance can be accessed from within that constructor as this. </list_item
230+
>
231+
</list>
232+
</list>
233+
</paragraph>
234+
</clause>
235+
""".Replace(Environment.NewLine, "\r\n");
236+
237+
var nodes = ReadAllNodes(xml);
238+
}
117239

118-
return RawNodeReader.ReadAllNodes(xml, Environment.NewLine, reader);
240+
private static List<RawNode> ReadAllNodes(string xml)
241+
{
242+
return RawNodeReader.ReadAllNodes(xml, Environment.NewLine);
119243
}
120244
}

0 commit comments

Comments
 (0)