Skip to content
This repository was archived by the owner on Jul 6, 2026. It is now read-only.

Commit 265ad76

Browse files
authored
[Java.Interop.Tools.JavaSource] Fix tag parsing errors (#997)
Improve tag parsing by adding grammar rules for `@hide`, `@inheritDoc`, and `{@see}`. The new grammar rules are not complete, but these new grammar rules, along with improvements to existing rules, reduces the number of `## Unable to translate remarks for …` errors from 740 to 391, nearly in half. The rules for `@param`, `@return`, `@throws`, and `@unknown` have been updated to handle more variations of these tags. In some cases, these tags are only followed by a single word or no content at all. New line character filters and tab have been added to `nonSpaceTerm` to fix instances where an "empty" tag would be merged with the tag on the following line. See [`Android.Database.DatabaseUtils.GetCollationKey(String)`][0] as an example. Before `nonSpaceTerm` change: <param name="name">To be added.</param> <param name="name&#xA;@return&#xA;@return">the collation key</param> <summary>return the collation key</summary> <returns>To be added.</returns> After `nonSpaceTerm` change: <param name="name">name</param> <summary>return the collation key</summary> <returns>the collation key</returns> [0]: https://github.com/xamarin/android-api-docs/blob/a7e914965a0e80c9454149399551d87a18997b2e/docs/Mono.Android/en/Android.Database/DatabaseUtils.xml#L1505-L1508
1 parent 99c68a8 commit 265ad76

6 files changed

Lines changed: 115 additions & 15 deletions

File tree

‎src/Java.Interop.Tools.JavaSource/Java.Interop.Tools.JavaSource/SourceJavadocToXmldocGrammar.BlockTagsBnfTerms.cs‎

Lines changed: 39 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ internal void CreateRules (SourceJavadocToXmldocGrammar grammar)
2525
|DeprecatedDeclaration
2626
|DeprecatedSinceDeclaration
2727
|ExceptionDeclaration
28+
|InheritDocDeclaration
29+
|HideDeclaration
2830
|ParamDeclaration
2931
|ReturnDeclaration
3032
|SeeDeclaration
@@ -78,7 +80,7 @@ internal void CreateRules (SourceJavadocToXmldocGrammar grammar)
7880
parseNode.AstNode=p;
7981
};
8082

81-
varnonSpaceTerm=newRegexBasedTerminal("[^ ]","[^ ]+"){
83+
varnonSpaceTerm=newRegexBasedTerminal("[^ \n\r\t]","[^ \n\r\t]+"){
8284
AstConfig=newAstNodeConfig{
8385
NodeCreator=(context,parseNode)=>parseNode.AstNode=parseNode.Token.Value,
8486
},
@@ -99,19 +101,42 @@ internal void CreateRules (SourceJavadocToXmldocGrammar grammar)
99101
FinishParse(context,parseNode);
100102
};
101103

102-
ParamDeclaration.Rule="@param"+nonSpaceTerm+BlockValues;
104+
// Ignore @hide tags
105+
HideDeclaration.Rule="@hide";
106+
HideDeclaration.AstConfig.NodeCreator=(context,parseNode)=>{
107+
FinishParse(context,parseNode);
108+
};
109+
110+
InheritDocDeclaration.Rule="@inheritDoc";
111+
InheritDocDeclaration.AstConfig.NodeCreator=(context,parseNode)=>{
112+
if(!grammar.ShouldImport(ImportJavadoc.InheritDocTag)){
113+
return;
114+
}
115+
// TODO: Iterate through parents for corresponding javadoc element.
116+
FinishParse(context,parseNode);
117+
};
118+
119+
ParamDeclaration.Rule="@param"+nonSpaceTerm
120+
|"@param"+nonSpaceTerm+BlockValues
121+
;
103122
ParamDeclaration.AstConfig.NodeCreator=(context,parseNode)=>{
104123
if(!grammar.ShouldImport(ImportJavadoc.ParamTag)){
105124
return;
106125
}
107-
varp=newXElement("param",
108-
newXAttribute("name",string.Join("",AstNodeToXmlContent(parseNode.ChildNodes[1]))),
109-
AstNodeToXmlContent(parseNode.ChildNodes[2]));
126+
varparamName=string.Join("",AstNodeToXmlContent(parseNode.ChildNodes[1]));
127+
varp=newXElement("param",newXAttribute("name",paramName));
128+
if(parseNode.ChildNodes.Count>=3){
129+
p.Add(AstNodeToXmlContent(parseNode.ChildNodes[2]));
130+
}else{
131+
p.Add(paramName);
132+
}
110133
FinishParse(context,parseNode).Parameters.Add(p);
111134
parseNode.AstNode=p;
112135
};
113136

114-
ReturnDeclaration.Rule="@return"+BlockValues;
137+
ReturnDeclaration.Rule="@return"
138+
|"@return"+BlockValues
139+
;
115140
ReturnDeclaration.AstConfig.NodeCreator=(context,parseNode)=>{
116141
if(!grammar.ShouldImport(ImportJavadoc.ReturnTag)){
117142
return;
@@ -158,7 +183,9 @@ internal void CreateRules (SourceJavadocToXmldocGrammar grammar)
158183
parseNode.AstNode=p;
159184
};
160185

161-
ThrowsDeclaration.Rule="@throws"+nonSpaceTerm+BlockValues;
186+
ThrowsDeclaration.Rule="@throws"+nonSpaceTerm
187+
|"@throws"+nonSpaceTerm+BlockValues
188+
;
162189
ThrowsDeclaration.AstConfig.NodeCreator=(context,parseNode)=>{
163190
if(!grammar.ShouldImport(ImportJavadoc.ExceptionTag)){
164191
return;
@@ -205,7 +232,9 @@ internal void CreateRules (SourceJavadocToXmldocGrammar grammar)
205232
parseNode.AstNode=parseNode.Token.Value.ToString();
206233

207234

208-
UnknownTagDeclaration.Rule=unknownTagTerminal+BlockValues;
235+
UnknownTagDeclaration.Rule=unknownTagTerminal
236+
|unknownTagTerminal+BlockValues
237+
;
209238
UnknownTagDeclaration.AstConfig.NodeCreator=(context,parseNode)=>{
210239
if(!grammar.ShouldImport(ImportJavadoc.Remarks)){
211240
return;
@@ -242,6 +271,8 @@ internal void CreateRules (SourceJavadocToXmldocGrammar grammar)
242271
publicreadonlyNonTerminalDeprecatedDeclaration=newNonTerminal(nameof(DeprecatedDeclaration));
243272
publicreadonlyNonTerminalDeprecatedSinceDeclaration=newNonTerminal(nameof(DeprecatedSinceDeclaration));
244273
publicreadonlyNonTerminalExceptionDeclaration=newNonTerminal(nameof(ExceptionDeclaration));
274+
publicreadonlyNonTerminalHideDeclaration=newNonTerminal(nameof(HideDeclaration));
275+
publicreadonlyNonTerminalInheritDocDeclaration=newNonTerminal(nameof(InheritDocDeclaration));
245276
publicreadonlyNonTerminalParamDeclaration=newNonTerminal(nameof(ParamDeclaration));
246277
publicreadonlyNonTerminalReturnDeclaration=newNonTerminal(nameof(ReturnDeclaration));
247278
publicreadonlyNonTerminalSeeDeclaration=newNonTerminal(nameof(SeeDeclaration));

‎src/Java.Interop.Tools.JavaSource/Java.Interop.Tools.JavaSource/SourceJavadocToXmldocGrammar.InlineTagsBnfTerms.cs‎

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ internal void CreateRules (SourceJavadocToXmldocGrammar grammar)
2626
|LinkDeclaration
2727
|LinkplainDeclaration
2828
|LiteralDeclaration
29+
|SeeDeclaration
2930
|ValueDeclaration
3031
;
3132

@@ -75,6 +76,14 @@ internal void CreateRules (SourceJavadocToXmldocGrammar grammar)
7576
parseNode.AstNode=newXText(content);
7677
};
7778

79+
SeeDeclaration.Rule=grammar.ToTerm("{@see")+InlineValue+"}";
80+
SeeDeclaration.AstConfig.NodeCreator=(context,parseNode)=>{
81+
// TODO: @see supports multiple forms; see: https://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#see
82+
// Also need to convert to appropriate CREF value, ignore for now
83+
vartarget=parseNode.ChildNodes[1].AstNode;
84+
parseNode.AstNode=newXElement("c",target);
85+
};
86+
7887
ValueDeclaration.Rule=grammar.ToTerm("{@value}")
7988
|grammar.ToTerm("{@value")+InlineValue+"}";
8089
ValueDeclaration.AstConfig.NodeCreator=(context,parseNode)=>{
@@ -114,10 +123,12 @@ internal void CreateRules (SourceJavadocToXmldocGrammar grammar)
114123
publicreadonlyNonTerminalLinkplainDeclaration=newNonTerminal(nameof(LinkplainDeclaration));
115124

116125
// https://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#literal
117-
publicreadonlyNonTerminalLiteralDeclaration=newNonTerminal(nameof(LinkplainDeclaration));
126+
publicreadonlyNonTerminalLiteralDeclaration=newNonTerminal(nameof(LiteralDeclaration));
127+
128+
publicreadonlyNonTerminalSeeDeclaration=newNonTerminal(nameof(SeeDeclaration));
118129

119130
// https://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#value
120-
publicreadonlyNonTerminalValueDeclaration=newNonTerminal(nameof(ValueDeclaration));
131+
publicreadonlyNonTerminalValueDeclaration=newNonTerminal(nameof(ValueDeclaration));
121132
}
122133
}
123134
}

‎src/Java.Interop.Tools.JavaSource/Java.Interop.Tools.JavaSource/SourceJavadocToXmldocParser.cs‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ internal enum ImportJavadoc {
2626
SinceTag=1<<9,
2727
VersionTag=1<<10,
2828
ExtraRemarks=1<<11,
29+
InheritDocTag=1<<12,
2930
}
3031

3132
[Flags]
@@ -43,6 +44,7 @@ public enum XmldocStyle {
4344
|ImportJavadoc.SinceTag
4445
|ImportJavadoc.VersionTag
4546
|ImportJavadoc.ExtraRemarks
47+
|ImportJavadoc.InheritDocTag
4648
,
4749
IntelliSense=ImportJavadoc.Summary
4850
|ImportJavadoc.ExceptionTag

‎tests/Java.Interop.Tools.JavaSource-Tests/SourceJavadocToXmldocGrammar.BlockTagsBnfTermsTests.cs‎

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -56,14 +56,39 @@ public void ExceptionDeclaration ()
5656
//Assert.AreEqual ("<exception cref=\"Throwable\">Just Because.</exception>", r.Root.AstNode.ToString ());
5757
}
5858

59+
[Test]
60+
publicvoidInheritDocDeclaration()
61+
{
62+
varp=CreateParser(g =>g.BlockTagsTerms.InheritDocDeclaration);
63+
64+
varr=p.Parse("@inheritDoc");
65+
Assert.IsFalse(r.HasErrors(),"@inheritDoc: "+DumpMessages(r,p));
66+
// TODO: Enable after adding support for @inheritDoc
67+
Assert.IsNull(r.Root.AstNode,"@inheritDoc should be ignored, but node was not null.");
68+
}
69+
70+
[Test]
71+
publicvoidHideDeclaration()
72+
{
73+
varp=CreateParser(g =>g.BlockTagsTerms.HideDeclaration);
74+
75+
varr=p.Parse("@hide");
76+
Assert.IsFalse(r.HasErrors(),"@hide: "+DumpMessages(r,p));
77+
Assert.IsNull(r.Root.AstNode,"@hide should be ignored, but node was not null.");
78+
}
79+
5980
[Test]
6081
publicvoidParamDeclaration()
6182
{
6283
varp=CreateParser(g =>g.BlockTagsTerms.ParamDeclaration);
6384

64-
varr=p.Parse("@param a Insert description here\n");
85+
varr=p.Parse("@param a Insert description here\nand here.");
6586
Assert.IsFalse(r.HasErrors(),"@param: "+DumpMessages(r,p));
66-
Assert.AreEqual("<param name=\"a\">Insert description here</param>",r.Root.AstNode.ToString());
87+
Assert.AreEqual($"<param name=\"a\">Insert description here{Environment.NewLine}and here.</param>",r.Root.AstNode.ToString());
88+
89+
r=p.Parse("@param b");
90+
Assert.IsFalse(r.HasErrors(),"name only @param: "+DumpMessages(r,p));
91+
Assert.AreEqual("<param name=\"b\">b</param>",r.Root.AstNode.ToString());
6792
}
6893

6994
[Test]
@@ -120,16 +145,21 @@ public void ThrowsDeclaration ()
120145
varp=CreateParser(g =>g.BlockTagsTerms.ThrowsDeclaration);
121146

122147
varr=p.Parse("@throws Throwable the {@code Exception} raised by this method");
123-
Assert.IsFalse(r.HasErrors(),"@throws: "+DumpMessages(r,p));
148+
Assert.IsFalse(r.HasErrors(),"{@code} @throws: "+DumpMessages(r,p));
124149
Assert.IsNull(r.Root.AstNode,"@throws should be ignored, but node with code block was not null.");
125150
// TODO: Re-enable when we can generate valid crefs
126151
//Assert.AreEqual ("<exception cref=\"Throwable\">the <c>Exception</c> raised by this method</exception>", r.Root.AstNode.ToString ());
127152

128153
r=p.Parse("@throws Throwable something <i>or other</i>!");
129-
Assert.IsFalse(r.HasErrors(),"@throws: "+DumpMessages(r,p));
130-
Assert.IsNull(r.Root.AstNode,"@throws should be ignored, but node was not null.");
154+
Assert.IsFalse(r.HasErrors(),"<i> @throws: "+DumpMessages(r,p));
155+
Assert.IsNull(r.Root.AstNode,"@throws should be ignored, but node with <i> was not null.");
131156
// TODO: Re-enable when we can generate valid crefs
132157
//Assert.AreEqual ("<exception cref=\"Throwable\">something <i>or other</i>!</exception>", r.Root.AstNode.ToString ());
158+
159+
r=p.Parse("@throws android.content.ActivityNotFoundException");
160+
Assert.IsFalse(r.HasErrors(),"name only @throws: "+DumpMessages(r,p));
161+
// TODO: Re-enable when we can generate valid crefs
162+
Assert.IsNull(r.Root.AstNode,"@throws should be ignored, but name only node was not null.");
133163
}
134164

135165
[Test]
@@ -140,6 +170,10 @@ public void UnknownTagDeclaration ()
140170
varr=p.Parse("@this-is-not-supported something {@code foo} else.");
141171
Assert.IsFalse(r.HasErrors(),"@this-is-not-supported: "+DumpMessages(r,p));
142172
Assert.AreEqual(null,r.Root.AstNode);
173+
174+
r=p.Parse("@standalonetag");
175+
Assert.IsFalse(r.HasErrors(),"@standalonetag: "+DumpMessages(r,p));
176+
Assert.AreEqual(null,r.Root.AstNode);
143177
}
144178
}
145179
}

‎tests/Java.Interop.Tools.JavaSource-Tests/SourceJavadocToXmldocGrammar.InlineTagsBnfTermsTests.cs‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,16 @@ public void LiteralDeclaration ()
7575
Assert.AreEqual("A&lt;B&gt;C",r.Root.AstNode.ToString());
7676
}
7777

78+
[Test]
79+
publicvoidSeeDeclaration()
80+
{
81+
varp=CreateParser(g =>g.InlineTagsTerms.SeeDeclaration);
82+
83+
varr=p.Parse("{@see #cancelNotification(String, String, int)}");
84+
Assert.IsFalse(r.HasErrors(),DumpMessages(r,p));
85+
Assert.AreEqual("<c>#cancelNotification(String, String, int)</c>",r.Root.AstNode.ToString());
86+
}
87+
7888
[Test]
7989
publicvoidValueDeclaration()
8090
{

‎tests/Java.Interop.Tools.JavaSource-Tests/SourceJavadocToXmldocParserTests.cs‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,11 +104,15 @@ What about soft paragraphs?
104104
<p>What about <i>hard</i> paragraphs?
105105
106106
@param a something
107+
@param b
108+
@param c
107109
@see #method()
108110
@apiSince 1
109111
",
110112
FullXml=@"<member>
111113
<param name=""a"">something</param>
114+
<param name=""b"">b</param>
115+
<param name=""c"">c</param>
112116
<summary>This is the summary sentence.</summary>
113117
<remarks>
114118
<para>This is the summary sentence. Insert
@@ -120,6 +124,8 @@ more description here.</para>
120124
</member>",
121125
IntelliSenseXml=@"<member>
122126
<param name=""a"">something</param>
127+
<param name=""b"">b</param>
128+
<param name=""c"">c</param>
123129
<summary>This is the summary sentence.</summary>
124130
</member>",
125131
},
@@ -183,19 +189,25 @@ more description here.
183189
@param manifest The value of the <a
184190
href=""{@docRoot}guide/topics/manifest/manifest-element.html#vcode"">{@code
185191
android:versionCode}</a> manifest attribute.
192+
@param empty
193+
@return the return value
186194
",
187195
FullXml=$@"<member>
188196
<param name=""manifest"">The value of the <see href=""{DocRootPrefixExpected}guide/topics/manifest/manifest-element.html#vcode""><c>android:versionCode</c></see> manifest attribute.</param>
197+
<param name=""empty"">empty</param>
189198
<summary>See <see href=""http://man7.org/linux/man-pages/man2/accept.2.html"">accept(2)</see>.</summary>
190199
<remarks>
191200
<para>See <see href=""http://man7.org/linux/man-pages/man2/accept.2.html"">accept(2)</see>. Insert
192201
more description here.
193202
How about another link <see href=""http://man7.org/linux/man-pages/man2/accept.2.html"">accept(2)</see></para>
194203
</remarks>
204+
<returns>the return value</returns>
195205
</member>",
196206
IntelliSenseXml=$@"<member>
197207
<param name=""manifest"">The value of the <see href=""{DocRootPrefixExpected}guide/topics/manifest/manifest-element.html#vcode""><c>android:versionCode</c></see> manifest attribute.</param>
208+
<param name=""empty"">empty</param>
198209
<summary>See <see href=""http://man7.org/linux/man-pages/man2/accept.2.html"">accept(2)</see>.</summary>
210+
<returns>the return value</returns>
199211
</member>",
200212
},
201213
};

0 commit comments

Comments
 (0)