Skip to content

[generator] Provide line/col numbers for api.xml warnings. #715

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Sep 18, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions tests/generator-Tests/Unit-Tests/XmlApiImporterTests.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Linq;
using System.Xml.Linq;
using MonoDroid.Generation;
using NUnit.Framework;
Expand Down Expand Up @@ -226,5 +227,22 @@ public void CreateParameter_NotNull ()

Assert.True (p.NotNull);
}

[Test]
public void PreserveSourceLineInfo ()
{
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);
var klass = XmlApiImporter.CreateClass (xml.Root, xml.Root.Element ("class"), new CodeGenerationOptions { ApiXmlFile = "obj/Debug/api.xml" });

Assert.AreEqual (2, klass.LineNumber);
Assert.AreEqual (2, klass.LinePosition);
Assert.AreEqual ("obj/Debug/api.xml", klass.SourceFile);

var method = klass.Methods.Single ();

Assert.AreEqual (3, method.LineNumber);
Assert.AreEqual (2, method.LinePosition);
Assert.AreEqual ("obj/Debug/api.xml", method.SourceFile);
}
}
}
1 change: 1 addition & 0 deletions tools/generator/CodeGenerationOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ internal CodeGenerator CreateCodeGenerator (TextWriter writer)
readonly SortedSet<string> jni_marshal_delegates = new SortedSet<string> ();
readonly object jni_marshal_delegates_lock = new object ();

public string ApiXmlFile { get; set; }
public bool UseGlobal { get; set; }
public bool IgnoreNonPublicType { get; set; }
public string AssemblyName { get; set; }
Expand Down
1 change: 1 addition & 0 deletions tools/generator/CodeGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ static void Run (CodeGeneratorOptions options, DirectoryAssemblyResolver resolve
string api_xml_adjuster_output = options.ApiXmlAdjusterOutput;
var apiSource = "";
var opt = new CodeGenerationOptions () {
ApiXmlFile = options.ApiDescriptionFile,
CodeGenerationTarget = options.CodeGenerationTarget,
UseGlobal = options.GlobalTypeNames,
IgnoreNonPublicType = true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -393,11 +393,11 @@ public bool WriteFields (List<Field> fields, string indent, GenBase gen, HashSet
bool needsProperty = false;
foreach (Field f in fields) {
if (gen.ContainsName (f.Name)) {
Report.LogCodedWarning (0, SourceWriterExtensions.GetFieldCollisionMessage (gen, f), gen.FullName, f.Name, gen.JavaName);
Report.LogCodedWarning (0, SourceWriterExtensions.GetFieldCollisionMessage (gen, f), f, gen.FullName, f.Name, gen.JavaName);
continue;
}
if (seen != null && seen.Contains (f.Name)) {
Report.LogCodedWarning (0, Report.WarningDuplicateField, gen.FullName, f.Name, gen.JavaName);
Report.LogCodedWarning (0, Report.WarningDuplicateField, f, gen.FullName, f.Name, gen.JavaName);
continue;
}
if (f.Validate (opt, gen.TypeParameters, Context)) {
Expand Down Expand Up @@ -935,11 +935,11 @@ public void WriteInterfaceListenerEventsAndProperties (InterfaceGen @interface,
foreach (var method in eventMethods) {
string name = method.CalculateEventName (target.ContainsName);
if (String.IsNullOrEmpty (name)) {
Report.LogCodedWarning (0, Report.WarningEmptyEventName, @interface.FullName, method.Name);
Report.LogCodedWarning (0, Report.WarningEmptyEventName, method, @interface.FullName, method.Name);
continue;
}
if (opt.GetSafeIdentifier (name) != name) {
Report.LogCodedWarning (0, Report.WarningInvalidEventName, @interface.FullName, method.Name);
Report.LogCodedWarning (0, Report.WarningInvalidEventName, method, @interface.FullName, method.Name);
continue;
}
var prop = target.Properties.FirstOrDefault (p => p.Setter == method);
Expand Down Expand Up @@ -996,7 +996,7 @@ public void WriteInterfaceListenerEventOrProperty (InterfaceGen @interface, Meth
full_delegate_name += "Handler";
if (m.RetVal.IsVoid || m.IsEventHandlerWithHandledProperty) {
if (opt.GetSafeIdentifier (name) != name) {
Report.LogCodedWarning (0, Report.WarningInvalidEventName2, @interface.FullName, name);
Report.LogCodedWarning (0, Report.WarningInvalidEventName2, m, @interface.FullName, name);
return;
} else {
var mt = target.Methods.Where (method => string.Compare (method.Name, connector_fmt, StringComparison.OrdinalIgnoreCase) == 0 && method.IsListenerConnector).FirstOrDefault ();
Expand All @@ -1005,7 +1005,7 @@ public void WriteInterfaceListenerEventOrProperty (InterfaceGen @interface, Meth
}
} else {
if (opt.GetSafeIdentifier (name) != name) {
Report.LogCodedWarning (0, Report.WarningInvalidEventPropertyName, @interface.FullName, name);
Report.LogCodedWarning (0, Report.WarningInvalidEventPropertyName, m, @interface.FullName, name);
return;
}
writer.WriteLine ("{0}WeakReference{2} weak_implementor_{1};", indent, name, opt.NullableOperator);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Linq;
using System.Text.RegularExpressions;
using System.Xml;
using System.Xml.Linq;
using Java.Interop.Tools.JavaCallableWrappers;
using MonoDroid.Utils;
Expand All @@ -26,6 +27,7 @@ public static ClassGen CreateClass (XElement pkg, XElement elem, CodeGenerationO
};

FillApiSince (klass, pkg, elem);
SetLineInfo (klass, elem, options);

foreach (var child in elem.Elements ()) {
switch (child.Name.LocalName) {
Expand All @@ -35,26 +37,26 @@ public static ClassGen CreateClass (XElement pkg, XElement elem, CodeGenerationO
klass.AddImplementedInterface (iname);
break;
case "method":
klass.AddMethod (CreateMethod (klass, child));
klass.AddMethod (CreateMethod (klass, child, options));
break;
case "constructor":
klass.Ctors.Add (CreateCtor (klass, child));
klass.Ctors.Add (CreateCtor (klass, child, options));
break;
case "field":
klass.AddField (CreateField (klass, child));
klass.AddField (CreateField (klass, child, options));
break;
case "typeParameters":
break; // handled at GenBaseSupport
default:
Report.LogCodedWarning (0, Report.WarningUnexpectedChild, child.Name.ToString ());
Report.LogCodedWarning (0, Report.WarningUnexpectedChild, klass, child.Name.ToString ());
break;
}
}

return klass;
}

public static Ctor CreateCtor (GenBase declaringType, XElement elem)
public static Ctor CreateCtor (GenBase declaringType, XElement elem, CodeGenerationOptions options = null)
{
var ctor = new Ctor (declaringType) {
ApiAvailableSince = declaringType.ApiAvailableSince,
Expand All @@ -65,6 +67,8 @@ public static Ctor CreateCtor (GenBase declaringType, XElement elem)
Visibility = elem.Visibility ()
};

SetLineInfo (ctor, elem, options);

var idx = ctor.Name.LastIndexOf ('.');

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

if (enclosingType == null) {
ctor.MissingEnclosingClass = true;
Report.LogCodedWarning (0, Report.WarningMissingClassForConstructor, ctor.Name, expectedEnclosingName);
Report.LogCodedWarning (0, Report.WarningMissingClassForConstructor, ctor, ctor.Name, expectedEnclosingName);
} else
ctor.Parameters.AddFirst (CreateParameterFromClassElement (enclosingType));
ctor.Parameters.AddFirst (CreateParameterFromClassElement (enclosingType, options));
}

foreach (var child in elem.Elements ()) {
if (child.Name == "parameter")
ctor.Parameters.Add (CreateParameter (child));
ctor.Parameters.Add (CreateParameter (child, options));
}

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

public static Field CreateField (GenBase declaringType, XElement elem)
public static Field CreateField (GenBase declaringType, XElement elem, CodeGenerationOptions options = null)
{
var field = new Field {
ApiAvailableSince = declaringType.ApiAvailableSince,
Expand All @@ -110,7 +114,7 @@ public static Field CreateField (GenBase declaringType, XElement elem)
IsStatic = elem.XGetAttribute ("static") == "true",
JavaName = elem.XGetAttribute ("name"),
NotNull = elem.XGetAttribute ("not-null") == "true",
SetterParameter = CreateParameter (elem),
SetterParameter = CreateParameter (elem, options),
TypeName = elem.XGetAttribute ("type"),
Value = elem.XGetAttribute ("value"), // do not trim
Visibility = elem.XGetAttribute ("visibility")
Expand All @@ -131,6 +135,7 @@ public static Field CreateField (GenBase declaringType, XElement elem)
}

FillApiSince (field, elem);
SetLineInfo (field, elem, options);

return field;
}
Expand Down Expand Up @@ -214,6 +219,7 @@ public static InterfaceGen CreateInterface (XElement pkg, XElement elem, CodeGen
};

FillApiSince (iface, pkg, elem);
SetLineInfo (iface, elem, options);

foreach (var child in elem.Elements ()) {
switch (child.Name.LocalName) {
Expand All @@ -223,23 +229,23 @@ public static InterfaceGen CreateInterface (XElement pkg, XElement elem, CodeGen
iface.AddImplementedInterface (iname);
break;
case "method":
iface.AddMethod (CreateMethod (iface, child));
iface.AddMethod (CreateMethod (iface, child, options));
break;
case "field":
iface.AddField (CreateField (iface, child));
iface.AddField (CreateField (iface, child, options));
break;
case "typeParameters":
break; // handled at GenBaseSupport
default:
Report.LogCodedWarning (0, Report.WarningUnexpectedInterfaceChild, child.ToString ());
Report.LogCodedWarning (0, Report.WarningUnexpectedInterfaceChild, iface, child.ToString ());
break;
}
}

return iface;
}

public static Method CreateMethod (GenBase declaringType, XElement elem)
public static Method CreateMethod (GenBase declaringType, XElement elem, CodeGenerationOptions options = null)
{
var method = new Method (declaringType) {
ApiAvailableSince = declaringType.ApiAvailableSince,
Expand Down Expand Up @@ -284,19 +290,20 @@ public static Method CreateMethod (GenBase declaringType, XElement elem)

foreach (var child in elem.Elements ()) {
if (child.Name == "parameter")
method.Parameters.Add (CreateParameter (child));
method.Parameters.Add (CreateParameter (child, options));
}

method.Name = EnsureValidIdentifer (method.Name);

method.FillReturnType ();

FillApiSince (method, elem);
SetLineInfo (method, elem, options);

return method;
}

public static Parameter CreateParameter (XElement elem)
public static Parameter CreateParameter (XElement elem, CodeGenerationOptions options = null)
{
string managedName = elem.XGetAttribute ("managedName");
string name = !string.IsNullOrEmpty (managedName) ? managedName : TypeNameUtilities.MangleName (EnsureValidIdentifer (elem.XGetAttribute ("name")));
Expand All @@ -308,15 +315,19 @@ public static Parameter CreateParameter (XElement elem)
var result = new Parameter (name, enum_type ?? java_type, enum_type ?? managed_type, enum_type != null, java_type, not_null);
if (elem.Attribute ("sender") != null)
result.IsSender = true;
SetLineInfo (result, elem, options);
return result;
}

public static Parameter CreateParameterFromClassElement (XElement elem)
public static Parameter CreateParameterFromClassElement (XElement elem, CodeGenerationOptions options)
{
string name = "__self";
string java_type = elem.XGetAttribute ("name");
string java_package = elem.Parent.XGetAttribute ("name");
return new Parameter (name, java_package + "." + java_type, null, false);
var p = new Parameter (name, java_package + "." + java_type, null, false);

SetLineInfo (p, elem, options);
return p;
}

static string EnsureValidIdentifer (string name)
Expand Down Expand Up @@ -403,5 +414,15 @@ static bool IsPrefixableName (string name)
// IBlahBlah is not prefixed with 'I'
return name.Length <= 2 || name [0] != 'I' || !char.IsUpper (name [1]);
}

static void SetLineInfo (ISourceLineInfo model, XNode node, CodeGenerationOptions options)
{
model.SourceFile = options?.ApiXmlFile;

if (node is IXmlLineInfo info && info.HasLineInfo ()) {
model.LineNumber = info.LineNumber;
model.LinePosition = info.LinePosition;
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -294,13 +294,13 @@ protected override bool OnValidate (CodeGenerationOptions opt, GenericParameterD

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

if ((base_symbol != null && !base_symbol.Validate (opt, TypeParameters, context)) || !base.OnValidate (opt, type_params, context)) {
Report.LogCodedWarning (0, Report.WarningInvalidBaseType, FullName, BaseType);
Report.LogCodedWarning (0, Report.WarningInvalidBaseType, this, FullName, BaseType);
IsValid = false;
return false;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

namespace MonoDroid.Generation
{
public class Field : ApiVersionsSupport.IApiAvailability
public class Field : ApiVersionsSupport.IApiAvailability, ISourceLineInfo
{
public string Annotation { get; set; }
public int ApiAvailableSince { get; set; }
Expand All @@ -25,6 +25,10 @@ public class Field : ApiVersionsSupport.IApiAvailability
public string Value { get; set; }
public string Visibility { get; set; }

public int LineNumber { get; set; } = -1;
public int LinePosition { get; set; } = -1;
public string SourceFile { get; set; }

internal string GetMethodPrefix => TypeNameUtilities.GetCallPrefix (Symbol);

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

if (Symbol == null || !Symbol.Validate (opt, type_params, context)) {
Report.LogCodedWarning (0, Report.WarningUnexpectedFieldType, TypeName, context.GetContextTypeMember ());
Report.LogCodedWarning (0, Report.WarningUnexpectedFieldType, this, TypeName, context.GetContextTypeMember ());
return false;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

namespace MonoDroid.Generation
{
public abstract class GenBase : IGeneratable, ApiVersionsSupport.IApiAvailability
public abstract class GenBase : IGeneratable, ApiVersionsSupport.IApiAvailability, ISourceLineInfo
{
bool enum_updated;
bool property_filled;
Expand All @@ -33,6 +33,10 @@ protected GenBase (GenBaseSupport support)
public string DefaultValue { get; set; }
public bool HasVirtualMethods { get; set; }

public int LineNumber { get; set; } = -1;
public int LinePosition { get; set; } = -1;
public string SourceFile { get; set; }

public string ReturnCast => string.Empty;

// This means Ctors/Methods/Properties/Fields has not been populated yet.
Expand Down Expand Up @@ -662,9 +666,9 @@ protected virtual bool OnValidate (CodeGenerationOptions opt, GenericParameterDe
Interfaces.Add (isym);
else {
if (isym == null)
Report.LogCodedWarning (0, Report.WarningBaseInterfaceNotFound, FullName, iface_name);
Report.LogCodedWarning (0, Report.WarningBaseInterfaceNotFound, this, FullName, iface_name);
else
Report.LogCodedWarning (0, Report.WarningBaseInterfaceInvalid, FullName, iface_name);
Report.LogCodedWarning (0, Report.WarningBaseInterfaceInvalid, this, FullName, iface_name);
iface_validation_failed = true;
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MonoDroid.Generation
{
public interface ISourceLineInfo
{
int LineNumber { get; set; }
int LinePosition { get; set; }
string SourceFile { get; set; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -204,9 +204,9 @@ protected override bool OnValidate (CodeGenerationOptions opt, GenericParameterD

if (!base.OnValidate (opt, type_params, context) || iface_validation_failed || MethodValidationFailed) {
if (iface_validation_failed)
Report.LogCodedWarning (0, Report.WarningInvalidDueToInterfaces, FullName);
Report.LogCodedWarning (0, Report.WarningInvalidDueToInterfaces, this, FullName);
else if (MethodValidationFailed)
Report.LogCodedWarning (0, Report.WarningInvalidDueToMethods, FullName);
Report.LogCodedWarning (0, Report.WarningInvalidDueToMethods, this, FullName);
foreach (GenBase nest in NestedTypes)
nest.Invalidate ();
IsValid = false;
Expand Down
Loading