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

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

17 files changed

Lines changed: 126 additions & 48 deletions

File tree

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

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
usingSystem;
2+
usingSystem.Linq;
23
usingSystem.Xml.Linq;
34
usingMonoDroid.Generation;
45
usingNUnit.Framework;
@@ -226,5 +227,22 @@ public void CreateParameter_NotNull ()
226227

227228
Assert.True(p.NotNull);
228229
}
230+
231+
[Test]
232+
publicvoidPreserveSourceLineInfo()
233+
{
234+
varxml=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+
varklass=XmlApiImporter.CreateClass(xml.Root,xml.Root.Element("class"),newCodeGenerationOptions{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+
varmethod=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‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ internal CodeGenerator CreateCodeGenerator (TextWriter writer)
4848
readonlySortedSet<string>jni_marshal_delegates=newSortedSet<string>();
4949
readonlyobjectjni_marshal_delegates_lock=newobject();
5050

51+
publicstringApiXmlFile{get;set;}
5152
publicboolUseGlobal{get;set;}
5253
publicboolIgnoreNonPublicType{get;set;}
5354
publicstringAssemblyName{get;set;}

‎tools/generator/CodeGenerator.cs‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ static void Run (CodeGeneratorOptions options, DirectoryAssemblyResolver resolve
6060
stringapi_xml_adjuster_output=options.ApiXmlAdjusterOutput;
6161
varapiSource="";
6262
varopt=newCodeGenerationOptions(){
63+
ApiXmlFile=options.ApiDescriptionFile,
6364
CodeGenerationTarget=options.CodeGenerationTarget,
6465
UseGlobal=options.GlobalTypeNames,
6566
IgnoreNonPublicType=true,

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

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -393,11 +393,11 @@ public bool WriteFields (List<Field> fields, string indent, GenBase gen, HashSet
393393
boolneedsProperty=false;
394394
foreach(Fieldfinfields){
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(varmethodineventMethods){
936936
stringname=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
varprop=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
varmt=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‎

Lines changed: 39 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
usingSystem;
22
usingSystem.Linq;
33
usingSystem.Text.RegularExpressions;
4+
usingSystem.Xml;
45
usingSystem.Xml.Linq;
56
usingJava.Interop.Tools.JavaCallableWrappers;
67
usingMonoDroid.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(varchildinelem.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
returnklass;
5557
}
5658

57-
publicstaticCtorCreateCtor(GenBasedeclaringType,XElementelem)
59+
publicstaticCtorCreateCtor(GenBasedeclaringType,XElementelem,CodeGenerationOptionsoptions=null)
5860
{
5961
varctor=newCtor(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
varidx=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(varchildinelem.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
returnctor;
99103
}
100104

101-
publicstaticFieldCreateField(GenBasedeclaringType,XElementelem)
105+
publicstaticFieldCreateField(GenBasedeclaringType,XElementelem,CodeGenerationOptionsoptions=null)
102106
{
103107
varfield=newField{
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
returnfield;
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(varchildinelem.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
returniface;
240246
}
241247

242-
publicstaticMethodCreateMethod(GenBasedeclaringType,XElementelem)
248+
publicstaticMethodCreateMethod(GenBasedeclaringType,XElementelem,CodeGenerationOptionsoptions=null)
243249
{
244250
varmethod=newMethod(declaringType){
245251
ApiAvailableSince=declaringType.ApiAvailableSince,
@@ -284,19 +290,20 @@ public static Method CreateMethod (GenBase declaringType, XElement elem)
284290

285291
foreach(varchildinelem.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
returnmethod;
297304
}
298305

299-
publicstaticParameterCreateParameter(XElementelem)
306+
publicstaticParameterCreateParameter(XElementelem,CodeGenerationOptionsoptions=null)
300307
{
301308
stringmanagedName=elem.XGetAttribute("managedName");
302309
stringname=!string.IsNullOrEmpty(managedName)?managedName:TypeNameUtilities.MangleName(EnsureValidIdentifer(elem.XGetAttribute("name")));
@@ -308,15 +315,19 @@ public static Parameter CreateParameter (XElement elem)
308315
varresult=newParameter(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
returnresult;
312320
}
313321

314-
publicstaticParameterCreateParameterFromClassElement(XElementelem)
322+
publicstaticParameterCreateParameterFromClassElement(XElementelem,CodeGenerationOptionsoptions)
315323
{
316324
stringname="__self";
317325
stringjava_type=elem.XGetAttribute("name");
318326
stringjava_package=elem.Parent.XGetAttribute("name");
319-
returnnewParameter(name,java_package+"."+java_type,null,false);
327+
varp=newParameter(name,java_package+"."+java_type,null,false);
328+
329+
SetLineInfo(p,elem,options);
330+
returnp;
320331
}
321332

322333
staticstringEnsureValidIdentifer(stringname)
@@ -403,5 +414,15 @@ static bool IsPrefixableName (string name)
403414
// IBlahBlah is not prefixed with 'I'
404415
returnname.Length<=2||name[0]!='I'||!char.IsUpper(name[1]);
405416
}
417+
418+
staticvoidSetLineInfo(ISourceLineInfomodel,XNodenode,CodeGenerationOptionsoptions)
419+
{
420+
model.SourceFile=options?.ApiXmlFile;
421+
422+
if(nodeisIXmlLineInfoinfo&&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‎

Lines changed: 2 additions & 2 deletions
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
returnfalse;
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
returnfalse;
306306
}

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

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
namespaceMonoDroid.Generation
66
{
7-
publicclassField:ApiVersionsSupport.IApiAvailability
7+
publicclassField:ApiVersionsSupport.IApiAvailability,ISourceLineInfo
88
{
99
publicstringAnnotation{get;set;}
1010
publicintApiAvailableSince{get;set;}
@@ -25,6 +25,10 @@ public class Field : ApiVersionsSupport.IApiAvailability
2525
publicstringValue{get;set;}
2626
publicstringVisibility{get;set;}
2727

28+
publicintLineNumber{get;set;}=-1;
29+
publicintLinePosition{get;set;}=-1;
30+
publicstringSourceFile{get;set;}
31+
2832
internalstringGetMethodPrefix=>TypeNameUtilities.GetCallPrefix(Symbol);
2933

3034
internalstringID=>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
returnfalse;
4347
}
4448

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

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
namespaceMonoDroid.Generation
88
{
9-
publicabstractclassGenBase:IGeneratable,ApiVersionsSupport.IApiAvailability
9+
publicabstractclassGenBase:IGeneratable,ApiVersionsSupport.IApiAvailability,ISourceLineInfo
1010
{
1111
boolenum_updated;
1212
boolproperty_filled;
@@ -33,6 +33,10 @@ protected GenBase (GenBaseSupport support)
3333
publicstringDefaultValue{get;set;}
3434
publicboolHasVirtualMethods{get;set;}
3535

36+
publicintLineNumber{get;set;}=-1;
37+
publicintLinePosition{get;set;}=-1;
38+
publicstringSourceFile{get;set;}
39+
3640
publicstringReturnCast=>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
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
usingSystem;
2+
usingSystem.Collections.Generic;
3+
usingSystem.Linq;
4+
usingSystem.Text;
5+
usingSystem.Threading.Tasks;
6+
7+
namespaceMonoDroid.Generation
8+
{
9+
publicinterfaceISourceLineInfo
10+
{
11+
intLineNumber{get;set;}
12+
intLinePosition{get;set;}
13+
stringSourceFile{get;set;}
14+
}
15+
}

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

Lines changed: 2 additions & 2 deletions
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
elseif(MethodValidationFailed)
209-
Report.LogCodedWarning(0,Report.WarningInvalidDueToMethods,FullName);
209+
Report.LogCodedWarning(0,Report.WarningInvalidDueToMethods,this,FullName);
210210
foreach(GenBasenestinNestedTypes)
211211
nest.Invalidate();
212212
IsValid=false;

0 commit comments

Comments
 (0)