Skip to content

Commit b858dc5

Browse files
authored
[generator] Provide line/col numbers for api.xml warnings (#715)
Previously, when `generator` emitted a warning while trying to bind an `api.xml` file, no source information was provided: BINDINGSGENERATOR : warning BG8700: Unknown return type 'com.blah.a.a.a.a' for member 'Com.Blah.Foo.Sdk.LoggerGetUserLogger ()'. Improve the user experience (if only slightly) by adding the file name and line & column information in standard MSBuild format. This allows users to double click the error in Visual Studio and be taken directly to the correct location in the `api.xml` file: BINDINGSGENERATOR : obj\Debug\api.xml(1684, 8) warning BG8700: Unknown return type 'com.blah.a.a.a.a' for member 'Com.Blah.Foo.Sdk.LoggerGetUserLogger ()'.
1 parent 9be92a0 commit b858dc5

File tree

17 files changed

+126
-48
lines changed

17 files changed

+126
-48
lines changed

tests/generator-Tests/Unit-Tests/XmlApiImporterTests.cs

+18
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using System.Linq;
23
using System.Xml.Linq;
34
using MonoDroid.Generation;
45
using NUnit.Framework;
@@ -226,5 +227,22 @@ public void CreateParameter_NotNull ()
226227

227228
Assert.True (p.NotNull);
228229
}
230+
231+
[Test]
232+
public void PreserveSourceLineInfo ()
233+
{
234+
var xml = XDocument.Parse ("<package name=\"com.example.test\" jni-name=\"com/example/test\">\n<class name=\"test\">\n<method name=\"add-h-_1V8i\" final=\"false\" />\n</class>\n</package>", LoadOptions.SetBaseUri | LoadOptions.SetLineInfo);
235+
var klass = XmlApiImporter.CreateClass (xml.Root, xml.Root.Element ("class"), new CodeGenerationOptions { ApiXmlFile = "obj/Debug/api.xml" });
236+
237+
Assert.AreEqual (2, klass.LineNumber);
238+
Assert.AreEqual (2, klass.LinePosition);
239+
Assert.AreEqual ("obj/Debug/api.xml", klass.SourceFile);
240+
241+
var method = klass.Methods.Single ();
242+
243+
Assert.AreEqual (3, method.LineNumber);
244+
Assert.AreEqual (2, method.LinePosition);
245+
Assert.AreEqual ("obj/Debug/api.xml", method.SourceFile);
246+
}
229247
}
230248
}

tools/generator/CodeGenerationOptions.cs

+1
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ internal CodeGenerator CreateCodeGenerator (TextWriter writer)
4848
readonly SortedSet<string> jni_marshal_delegates = new SortedSet<string> ();
4949
readonly object jni_marshal_delegates_lock = new object ();
5050

51+
public string ApiXmlFile { get; set; }
5152
public bool UseGlobal { get; set; }
5253
public bool IgnoreNonPublicType { get; set; }
5354
public string AssemblyName { get; set; }

tools/generator/CodeGenerator.cs

+1
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ static void Run (CodeGeneratorOptions options, DirectoryAssemblyResolver resolve
6060
string api_xml_adjuster_output = options.ApiXmlAdjusterOutput;
6161
var apiSource = "";
6262
var opt = new CodeGenerationOptions () {
63+
ApiXmlFile = options.ApiDescriptionFile,
6364
CodeGenerationTarget = options.CodeGenerationTarget,
6465
UseGlobal = options.GlobalTypeNames,
6566
IgnoreNonPublicType = true,

tools/generator/Java.Interop.Tools.Generator.CodeGeneration/CodeGenerator.cs

+6-6
Original file line numberDiff line numberDiff line change
@@ -393,11 +393,11 @@ public bool WriteFields (List<Field> fields, string indent, GenBase gen, HashSet
393393
bool needsProperty = false;
394394
foreach (Field f in fields) {
395395
if (gen.ContainsName (f.Name)) {
396-
Report.LogCodedWarning (0, SourceWriterExtensions.GetFieldCollisionMessage (gen, f), gen.FullName, f.Name, gen.JavaName);
396+
Report.LogCodedWarning (0, SourceWriterExtensions.GetFieldCollisionMessage (gen, f), f, gen.FullName, f.Name, gen.JavaName);
397397
continue;
398398
}
399399
if (seen != null && seen.Contains (f.Name)) {
400-
Report.LogCodedWarning (0, Report.WarningDuplicateField, gen.FullName, f.Name, gen.JavaName);
400+
Report.LogCodedWarning (0, Report.WarningDuplicateField, f, gen.FullName, f.Name, gen.JavaName);
401401
continue;
402402
}
403403
if (f.Validate (opt, gen.TypeParameters, Context)) {
@@ -935,11 +935,11 @@ public void WriteInterfaceListenerEventsAndProperties (InterfaceGen @interface,
935935
foreach (var method in eventMethods) {
936936
string name = method.CalculateEventName (target.ContainsName);
937937
if (String.IsNullOrEmpty (name)) {
938-
Report.LogCodedWarning (0, Report.WarningEmptyEventName, @interface.FullName, method.Name);
938+
Report.LogCodedWarning (0, Report.WarningEmptyEventName, method, @interface.FullName, method.Name);
939939
continue;
940940
}
941941
if (opt.GetSafeIdentifier (name) != name) {
942-
Report.LogCodedWarning (0, Report.WarningInvalidEventName, @interface.FullName, method.Name);
942+
Report.LogCodedWarning (0, Report.WarningInvalidEventName, method, @interface.FullName, method.Name);
943943
continue;
944944
}
945945
var prop = target.Properties.FirstOrDefault (p => p.Setter == method);
@@ -996,7 +996,7 @@ public void WriteInterfaceListenerEventOrProperty (InterfaceGen @interface, Meth
996996
full_delegate_name += "Handler";
997997
if (m.RetVal.IsVoid || m.IsEventHandlerWithHandledProperty) {
998998
if (opt.GetSafeIdentifier (name) != name) {
999-
Report.LogCodedWarning (0, Report.WarningInvalidEventName2, @interface.FullName, name);
999+
Report.LogCodedWarning (0, Report.WarningInvalidEventName2, m, @interface.FullName, name);
10001000
return;
10011001
} else {
10021002
var mt = target.Methods.Where (method => string.Compare (method.Name, connector_fmt, StringComparison.OrdinalIgnoreCase) == 0 && method.IsListenerConnector).FirstOrDefault ();
@@ -1005,7 +1005,7 @@ public void WriteInterfaceListenerEventOrProperty (InterfaceGen @interface, Meth
10051005
}
10061006
} else {
10071007
if (opt.GetSafeIdentifier (name) != name) {
1008-
Report.LogCodedWarning (0, Report.WarningInvalidEventPropertyName, @interface.FullName, name);
1008+
Report.LogCodedWarning (0, Report.WarningInvalidEventPropertyName, m, @interface.FullName, name);
10091009
return;
10101010
}
10111011
writer.WriteLine ("{0}WeakReference{2} weak_implementor_{1};", indent, name, opt.NullableOperator);

tools/generator/Java.Interop.Tools.Generator.Importers/XmlApiImporter.cs

+39-18
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System;
22
using System.Linq;
33
using System.Text.RegularExpressions;
4+
using System.Xml;
45
using System.Xml.Linq;
56
using Java.Interop.Tools.JavaCallableWrappers;
67
using MonoDroid.Utils;
@@ -26,6 +27,7 @@ public static ClassGen CreateClass (XElement pkg, XElement elem, CodeGenerationO
2627
};
2728

2829
FillApiSince (klass, pkg, elem);
30+
SetLineInfo (klass, elem, options);
2931

3032
foreach (var child in elem.Elements ()) {
3133
switch (child.Name.LocalName) {
@@ -35,26 +37,26 @@ public static ClassGen CreateClass (XElement pkg, XElement elem, CodeGenerationO
3537
klass.AddImplementedInterface (iname);
3638
break;
3739
case "method":
38-
klass.AddMethod (CreateMethod (klass, child));
40+
klass.AddMethod (CreateMethod (klass, child, options));
3941
break;
4042
case "constructor":
41-
klass.Ctors.Add (CreateCtor (klass, child));
43+
klass.Ctors.Add (CreateCtor (klass, child, options));
4244
break;
4345
case "field":
44-
klass.AddField (CreateField (klass, child));
46+
klass.AddField (CreateField (klass, child, options));
4547
break;
4648
case "typeParameters":
4749
break; // handled at GenBaseSupport
4850
default:
49-
Report.LogCodedWarning (0, Report.WarningUnexpectedChild, child.Name.ToString ());
51+
Report.LogCodedWarning (0, Report.WarningUnexpectedChild, klass, child.Name.ToString ());
5052
break;
5153
}
5254
}
5355

5456
return klass;
5557
}
5658

57-
public static Ctor CreateCtor (GenBase declaringType, XElement elem)
59+
public static Ctor CreateCtor (GenBase declaringType, XElement elem, CodeGenerationOptions options = null)
5860
{
5961
var ctor = new Ctor (declaringType) {
6062
ApiAvailableSince = declaringType.ApiAvailableSince,
@@ -65,6 +67,8 @@ public static Ctor CreateCtor (GenBase declaringType, XElement elem)
6567
Visibility = elem.Visibility ()
6668
};
6769

70+
SetLineInfo (ctor, elem, options);
71+
6872
var idx = ctor.Name.LastIndexOf ('.');
6973

7074
if (idx > 0)
@@ -81,14 +85,14 @@ public static Ctor CreateCtor (GenBase declaringType, XElement elem)
8185

8286
if (enclosingType == null) {
8387
ctor.MissingEnclosingClass = true;
84-
Report.LogCodedWarning (0, Report.WarningMissingClassForConstructor, ctor.Name, expectedEnclosingName);
88+
Report.LogCodedWarning (0, Report.WarningMissingClassForConstructor, ctor, ctor.Name, expectedEnclosingName);
8589
} else
86-
ctor.Parameters.AddFirst (CreateParameterFromClassElement (enclosingType));
90+
ctor.Parameters.AddFirst (CreateParameterFromClassElement (enclosingType, options));
8791
}
8892

8993
foreach (var child in elem.Elements ()) {
9094
if (child.Name == "parameter")
91-
ctor.Parameters.Add (CreateParameter (child));
95+
ctor.Parameters.Add (CreateParameter (child, options));
9296
}
9397

9498
ctor.Name = EnsureValidIdentifer (ctor.Name);
@@ -98,7 +102,7 @@ public static Ctor CreateCtor (GenBase declaringType, XElement elem)
98102
return ctor;
99103
}
100104

101-
public static Field CreateField (GenBase declaringType, XElement elem)
105+
public static Field CreateField (GenBase declaringType, XElement elem, CodeGenerationOptions options = null)
102106
{
103107
var field = new Field {
104108
ApiAvailableSince = declaringType.ApiAvailableSince,
@@ -110,7 +114,7 @@ public static Field CreateField (GenBase declaringType, XElement elem)
110114
IsStatic = elem.XGetAttribute ("static") == "true",
111115
JavaName = elem.XGetAttribute ("name"),
112116
NotNull = elem.XGetAttribute ("not-null") == "true",
113-
SetterParameter = CreateParameter (elem),
117+
SetterParameter = CreateParameter (elem, options),
114118
TypeName = elem.XGetAttribute ("type"),
115119
Value = elem.XGetAttribute ("value"), // do not trim
116120
Visibility = elem.XGetAttribute ("visibility")
@@ -131,6 +135,7 @@ public static Field CreateField (GenBase declaringType, XElement elem)
131135
}
132136

133137
FillApiSince (field, elem);
138+
SetLineInfo (field, elem, options);
134139

135140
return field;
136141
}
@@ -214,6 +219,7 @@ public static InterfaceGen CreateInterface (XElement pkg, XElement elem, CodeGen
214219
};
215220

216221
FillApiSince (iface, pkg, elem);
222+
SetLineInfo (iface, elem, options);
217223

218224
foreach (var child in elem.Elements ()) {
219225
switch (child.Name.LocalName) {
@@ -223,23 +229,23 @@ public static InterfaceGen CreateInterface (XElement pkg, XElement elem, CodeGen
223229
iface.AddImplementedInterface (iname);
224230
break;
225231
case "method":
226-
iface.AddMethod (CreateMethod (iface, child));
232+
iface.AddMethod (CreateMethod (iface, child, options));
227233
break;
228234
case "field":
229-
iface.AddField (CreateField (iface, child));
235+
iface.AddField (CreateField (iface, child, options));
230236
break;
231237
case "typeParameters":
232238
break; // handled at GenBaseSupport
233239
default:
234-
Report.LogCodedWarning (0, Report.WarningUnexpectedInterfaceChild, child.ToString ());
240+
Report.LogCodedWarning (0, Report.WarningUnexpectedInterfaceChild, iface, child.ToString ());
235241
break;
236242
}
237243
}
238244

239245
return iface;
240246
}
241247

242-
public static Method CreateMethod (GenBase declaringType, XElement elem)
248+
public static Method CreateMethod (GenBase declaringType, XElement elem, CodeGenerationOptions options = null)
243249
{
244250
var method = new Method (declaringType) {
245251
ApiAvailableSince = declaringType.ApiAvailableSince,
@@ -284,19 +290,20 @@ public static Method CreateMethod (GenBase declaringType, XElement elem)
284290

285291
foreach (var child in elem.Elements ()) {
286292
if (child.Name == "parameter")
287-
method.Parameters.Add (CreateParameter (child));
293+
method.Parameters.Add (CreateParameter (child, options));
288294
}
289295

290296
method.Name = EnsureValidIdentifer (method.Name);
291297

292298
method.FillReturnType ();
293299

294300
FillApiSince (method, elem);
301+
SetLineInfo (method, elem, options);
295302

296303
return method;
297304
}
298305

299-
public static Parameter CreateParameter (XElement elem)
306+
public static Parameter CreateParameter (XElement elem, CodeGenerationOptions options = null)
300307
{
301308
string managedName = elem.XGetAttribute ("managedName");
302309
string name = !string.IsNullOrEmpty (managedName) ? managedName : TypeNameUtilities.MangleName (EnsureValidIdentifer (elem.XGetAttribute ("name")));
@@ -308,15 +315,19 @@ public static Parameter CreateParameter (XElement elem)
308315
var result = new Parameter (name, enum_type ?? java_type, enum_type ?? managed_type, enum_type != null, java_type, not_null);
309316
if (elem.Attribute ("sender") != null)
310317
result.IsSender = true;
318+
SetLineInfo (result, elem, options);
311319
return result;
312320
}
313321

314-
public static Parameter CreateParameterFromClassElement (XElement elem)
322+
public static Parameter CreateParameterFromClassElement (XElement elem, CodeGenerationOptions options)
315323
{
316324
string name = "__self";
317325
string java_type = elem.XGetAttribute ("name");
318326
string java_package = elem.Parent.XGetAttribute ("name");
319-
return new Parameter (name, java_package + "." + java_type, null, false);
327+
var p = new Parameter (name, java_package + "." + java_type, null, false);
328+
329+
SetLineInfo (p, elem, options);
330+
return p;
320331
}
321332

322333
static string EnsureValidIdentifer (string name)
@@ -403,5 +414,15 @@ static bool IsPrefixableName (string name)
403414
// IBlahBlah is not prefixed with 'I'
404415
return name.Length <= 2 || name [0] != 'I' || !char.IsUpper (name [1]);
405416
}
417+
418+
static void SetLineInfo (ISourceLineInfo model, XNode node, CodeGenerationOptions options)
419+
{
420+
model.SourceFile = options?.ApiXmlFile;
421+
422+
if (node is IXmlLineInfo info && info.HasLineInfo ()) {
423+
model.LineNumber = info.LineNumber;
424+
model.LinePosition = info.LinePosition;
425+
}
426+
}
406427
}
407428
}

tools/generator/Java.Interop.Tools.Generator.ObjectModel/ClassGen.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -294,13 +294,13 @@ protected override bool OnValidate (CodeGenerationOptions opt, GenericParameterD
294294

295295
base_symbol = IsAnnotation ? opt.SymbolTable.Lookup ("java.lang.Object") : BaseType != null ? opt.SymbolTable.Lookup (BaseType) : null;
296296
if (base_symbol == null && FullName != "Java.Lang.Object" && FullName != "System.Object") {
297-
Report.LogCodedWarning (0, Report.WarningUnknownBaseType, FullName, BaseType);
297+
Report.LogCodedWarning (0, Report.WarningUnknownBaseType, this, FullName, BaseType);
298298
IsValid = false;
299299
return false;
300300
}
301301

302302
if ((base_symbol != null && !base_symbol.Validate (opt, TypeParameters, context)) || !base.OnValidate (opt, type_params, context)) {
303-
Report.LogCodedWarning (0, Report.WarningInvalidBaseType, FullName, BaseType);
303+
Report.LogCodedWarning (0, Report.WarningInvalidBaseType, this, FullName, BaseType);
304304
IsValid = false;
305305
return false;
306306
}

tools/generator/Java.Interop.Tools.Generator.ObjectModel/Field.cs

+6-2
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
namespace MonoDroid.Generation
66
{
7-
public class Field : ApiVersionsSupport.IApiAvailability
7+
public class Field : ApiVersionsSupport.IApiAvailability, ISourceLineInfo
88
{
99
public string Annotation { get; set; }
1010
public int ApiAvailableSince { get; set; }
@@ -25,6 +25,10 @@ public class Field : ApiVersionsSupport.IApiAvailability
2525
public string Value { get; set; }
2626
public string Visibility { get; set; }
2727

28+
public int LineNumber { get; set; } = -1;
29+
public int LinePosition { get; set; } = -1;
30+
public string SourceFile { get; set; }
31+
2832
internal string GetMethodPrefix => TypeNameUtilities.GetCallPrefix (Symbol);
2933

3034
internal string ID => JavaName + "_jfieldId";
@@ -38,7 +42,7 @@ public bool Validate (CodeGenerationOptions opt, GenericParameterDefinitionList
3842
Symbol = opt.SymbolTable.Lookup (TypeName, type_params);
3943

4044
if (Symbol == null || !Symbol.Validate (opt, type_params, context)) {
41-
Report.LogCodedWarning (0, Report.WarningUnexpectedFieldType, TypeName, context.GetContextTypeMember ());
45+
Report.LogCodedWarning (0, Report.WarningUnexpectedFieldType, this, TypeName, context.GetContextTypeMember ());
4246
return false;
4347
}
4448

tools/generator/Java.Interop.Tools.Generator.ObjectModel/GenBase.cs

+7-3
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
namespace MonoDroid.Generation
88
{
9-
public abstract class GenBase : IGeneratable, ApiVersionsSupport.IApiAvailability
9+
public abstract class GenBase : IGeneratable, ApiVersionsSupport.IApiAvailability, ISourceLineInfo
1010
{
1111
bool enum_updated;
1212
bool property_filled;
@@ -33,6 +33,10 @@ protected GenBase (GenBaseSupport support)
3333
public string DefaultValue { get; set; }
3434
public bool HasVirtualMethods { get; set; }
3535

36+
public int LineNumber { get; set; } = -1;
37+
public int LinePosition { get; set; } = -1;
38+
public string SourceFile { get; set; }
39+
3640
public string ReturnCast => string.Empty;
3741

3842
// This means Ctors/Methods/Properties/Fields has not been populated yet.
@@ -662,9 +666,9 @@ protected virtual bool OnValidate (CodeGenerationOptions opt, GenericParameterDe
662666
Interfaces.Add (isym);
663667
else {
664668
if (isym == null)
665-
Report.LogCodedWarning (0, Report.WarningBaseInterfaceNotFound, FullName, iface_name);
669+
Report.LogCodedWarning (0, Report.WarningBaseInterfaceNotFound, this, FullName, iface_name);
666670
else
667-
Report.LogCodedWarning (0, Report.WarningBaseInterfaceInvalid, FullName, iface_name);
671+
Report.LogCodedWarning (0, Report.WarningBaseInterfaceInvalid, this, FullName, iface_name);
668672
iface_validation_failed = true;
669673
}
670674
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Threading.Tasks;
6+
7+
namespace MonoDroid.Generation
8+
{
9+
public interface ISourceLineInfo
10+
{
11+
int LineNumber { get; set; }
12+
int LinePosition { get; set; }
13+
string SourceFile { get; set; }
14+
}
15+
}

tools/generator/Java.Interop.Tools.Generator.ObjectModel/InterfaceGen.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -204,9 +204,9 @@ protected override bool OnValidate (CodeGenerationOptions opt, GenericParameterD
204204

205205
if (!base.OnValidate (opt, type_params, context) || iface_validation_failed || MethodValidationFailed) {
206206
if (iface_validation_failed)
207-
Report.LogCodedWarning (0, Report.WarningInvalidDueToInterfaces, FullName);
207+
Report.LogCodedWarning (0, Report.WarningInvalidDueToInterfaces, this, FullName);
208208
else if (MethodValidationFailed)
209-
Report.LogCodedWarning (0, Report.WarningInvalidDueToMethods, FullName);
209+
Report.LogCodedWarning (0, Report.WarningInvalidDueToMethods, this, FullName);
210210
foreach (GenBase nest in NestedTypes)
211211
nest.Invalidate ();
212212
IsValid = false;

0 commit comments

Comments
 (0)