diff --git a/src/Java.Interop.Localization/Resources.Designer.cs b/src/Java.Interop.Localization/Resources.Designer.cs
index b8c1dc8fe..d989a63e4 100644
--- a/src/Java.Interop.Localization/Resources.Designer.cs
+++ b/src/Java.Interop.Localization/Resources.Designer.cs
@@ -222,6 +222,15 @@ public static string Generator_BG8402 {
}
}
+ ///
+ /// Looks up a localized string similar to Type '{0}' has a type name which matches the enclosing namespace name. See https://aka.ms/BG8403 for more information..
+ ///
+ public static string Generator_BG8403 {
+ get {
+ return ResourceManager.GetString("Generator_BG8403", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Unexpected child element of '<interface>': '{0}'..
///
diff --git a/src/Java.Interop.Localization/Resources.resx b/src/Java.Interop.Localization/Resources.resx
index d441772e5..36c78f600 100644
--- a/src/Java.Interop.Localization/Resources.resx
+++ b/src/Java.Interop.Localization/Resources.resx
@@ -201,6 +201,10 @@ In this message, the term "constants" refers to class or interface members that
{1} - .NET field name.
{2} - Java type.
+
+ Type '{0}' has a type name which matches the enclosing namespace name. See https://aka.ms/BG8403 for more information.
+ {0} - Java type.
+
Unexpected child element of '<interface>': '{0}'.{0} - XML element name.
diff --git a/src/Java.Interop.Localization/xlf/Resources.cs.xlf b/src/Java.Interop.Localization/xlf/Resources.cs.xlf
index 573b63ea0..ed9a52db8 100644
--- a/src/Java.Interop.Localization/xlf/Resources.cs.xlf
+++ b/src/Java.Interop.Localization/xlf/Resources.cs.xlf
@@ -104,6 +104,11 @@ In this message, the term "constants" refers to class or interface members that
{1} - .NET field name.
{2} - Java type.
+
+ Type '{0}' has a type name which matches the enclosing namespace name. See https://aka.ms/BG8403 for more information.
+ Type '{0}' has a type name which matches the enclosing namespace name. See https://aka.ms/BG8403 for more information.
+ {0} - Java type.
+ Unexpected child element of '<interface>': '{0}'.Unexpected child element of '<interface>': '{0}'.
diff --git a/src/Java.Interop.Localization/xlf/Resources.de.xlf b/src/Java.Interop.Localization/xlf/Resources.de.xlf
index 3bc4cd696..158396efb 100644
--- a/src/Java.Interop.Localization/xlf/Resources.de.xlf
+++ b/src/Java.Interop.Localization/xlf/Resources.de.xlf
@@ -104,6 +104,11 @@ In this message, the term "constants" refers to class or interface members that
{1} - .NET field name.
{2} - Java type.
+
+ Type '{0}' has a type name which matches the enclosing namespace name. See https://aka.ms/BG8403 for more information.
+ Type '{0}' has a type name which matches the enclosing namespace name. See https://aka.ms/BG8403 for more information.
+ {0} - Java type.
+ Unexpected child element of '<interface>': '{0}'.Unexpected child element of '<interface>': '{0}'.
diff --git a/src/Java.Interop.Localization/xlf/Resources.es.xlf b/src/Java.Interop.Localization/xlf/Resources.es.xlf
index 2f3abf14a..9956cc2be 100644
--- a/src/Java.Interop.Localization/xlf/Resources.es.xlf
+++ b/src/Java.Interop.Localization/xlf/Resources.es.xlf
@@ -104,6 +104,11 @@ In this message, the term "constants" refers to class or interface members that
{1} - .NET field name.
{2} - Java type.
+
+ Type '{0}' has a type name which matches the enclosing namespace name. See https://aka.ms/BG8403 for more information.
+ Type '{0}' has a type name which matches the enclosing namespace name. See https://aka.ms/BG8403 for more information.
+ {0} - Java type.
+ Unexpected child element of '<interface>': '{0}'.Unexpected child element of '<interface>': '{0}'.
diff --git a/src/Java.Interop.Localization/xlf/Resources.fr.xlf b/src/Java.Interop.Localization/xlf/Resources.fr.xlf
index dbe446898..54402c21d 100644
--- a/src/Java.Interop.Localization/xlf/Resources.fr.xlf
+++ b/src/Java.Interop.Localization/xlf/Resources.fr.xlf
@@ -104,6 +104,11 @@ In this message, the term "constants" refers to class or interface members that
{1} - .NET field name.
{2} - Java type.
+
+ Type '{0}' has a type name which matches the enclosing namespace name. See https://aka.ms/BG8403 for more information.
+ Type '{0}' has a type name which matches the enclosing namespace name. See https://aka.ms/BG8403 for more information.
+ {0} - Java type.
+ Unexpected child element of '<interface>': '{0}'.Unexpected child element of '<interface>': '{0}'.
diff --git a/src/Java.Interop.Localization/xlf/Resources.it.xlf b/src/Java.Interop.Localization/xlf/Resources.it.xlf
index 2dbdd4112..6a12b2a35 100644
--- a/src/Java.Interop.Localization/xlf/Resources.it.xlf
+++ b/src/Java.Interop.Localization/xlf/Resources.it.xlf
@@ -104,6 +104,11 @@ In this message, the term "constants" refers to class or interface members that
{1} - .NET field name.
{2} - Java type.
+
+ Type '{0}' has a type name which matches the enclosing namespace name. See https://aka.ms/BG8403 for more information.
+ Type '{0}' has a type name which matches the enclosing namespace name. See https://aka.ms/BG8403 for more information.
+ {0} - Java type.
+ Unexpected child element of '<interface>': '{0}'.Unexpected child element of '<interface>': '{0}'.
diff --git a/src/Java.Interop.Localization/xlf/Resources.ja.xlf b/src/Java.Interop.Localization/xlf/Resources.ja.xlf
index c5219f984..ae898064f 100644
--- a/src/Java.Interop.Localization/xlf/Resources.ja.xlf
+++ b/src/Java.Interop.Localization/xlf/Resources.ja.xlf
@@ -104,6 +104,11 @@ In this message, the term "constants" refers to class or interface members that
{1} - .NET field name.
{2} - Java type.
+
+ Type '{0}' has a type name which matches the enclosing namespace name. See https://aka.ms/BG8403 for more information.
+ Type '{0}' has a type name which matches the enclosing namespace name. See https://aka.ms/BG8403 for more information.
+ {0} - Java type.
+ Unexpected child element of '<interface>': '{0}'.Unexpected child element of '<interface>': '{0}'.
diff --git a/src/Java.Interop.Localization/xlf/Resources.ko.xlf b/src/Java.Interop.Localization/xlf/Resources.ko.xlf
index de12f99b9..0e5868ae8 100644
--- a/src/Java.Interop.Localization/xlf/Resources.ko.xlf
+++ b/src/Java.Interop.Localization/xlf/Resources.ko.xlf
@@ -104,6 +104,11 @@ In this message, the term "constants" refers to class or interface members that
{1} - .NET field name.
{2} - Java type.
+
+ Type '{0}' has a type name which matches the enclosing namespace name. See https://aka.ms/BG8403 for more information.
+ Type '{0}' has a type name which matches the enclosing namespace name. See https://aka.ms/BG8403 for more information.
+ {0} - Java type.
+ Unexpected child element of '<interface>': '{0}'.Unexpected child element of '<interface>': '{0}'.
diff --git a/src/Java.Interop.Localization/xlf/Resources.pl.xlf b/src/Java.Interop.Localization/xlf/Resources.pl.xlf
index 9ab39c8f1..e4e8c187a 100644
--- a/src/Java.Interop.Localization/xlf/Resources.pl.xlf
+++ b/src/Java.Interop.Localization/xlf/Resources.pl.xlf
@@ -104,6 +104,11 @@ In this message, the term "constants" refers to class or interface members that
{1} - .NET field name.
{2} - Java type.
+
+ Type '{0}' has a type name which matches the enclosing namespace name. See https://aka.ms/BG8403 for more information.
+ Type '{0}' has a type name which matches the enclosing namespace name. See https://aka.ms/BG8403 for more information.
+ {0} - Java type.
+ Unexpected child element of '<interface>': '{0}'.Unexpected child element of '<interface>': '{0}'.
diff --git a/src/Java.Interop.Localization/xlf/Resources.pt-BR.xlf b/src/Java.Interop.Localization/xlf/Resources.pt-BR.xlf
index 8df0f8d90..d8ffb28c8 100644
--- a/src/Java.Interop.Localization/xlf/Resources.pt-BR.xlf
+++ b/src/Java.Interop.Localization/xlf/Resources.pt-BR.xlf
@@ -104,6 +104,11 @@ In this message, the term "constants" refers to class or interface members that
{1} - .NET field name.
{2} - Java type.
+
+ Type '{0}' has a type name which matches the enclosing namespace name. See https://aka.ms/BG8403 for more information.
+ Type '{0}' has a type name which matches the enclosing namespace name. See https://aka.ms/BG8403 for more information.
+ {0} - Java type.
+ Unexpected child element of '<interface>': '{0}'.Unexpected child element of '<interface>': '{0}'.
diff --git a/src/Java.Interop.Localization/xlf/Resources.ru.xlf b/src/Java.Interop.Localization/xlf/Resources.ru.xlf
index 69192c753..1828c8286 100644
--- a/src/Java.Interop.Localization/xlf/Resources.ru.xlf
+++ b/src/Java.Interop.Localization/xlf/Resources.ru.xlf
@@ -104,6 +104,11 @@ In this message, the term "constants" refers to class or interface members that
{1} - .NET field name.
{2} - Java type.
+
+ Type '{0}' has a type name which matches the enclosing namespace name. See https://aka.ms/BG8403 for more information.
+ Type '{0}' has a type name which matches the enclosing namespace name. See https://aka.ms/BG8403 for more information.
+ {0} - Java type.
+ Unexpected child element of '<interface>': '{0}'.Unexpected child element of '<interface>': '{0}'.
diff --git a/src/Java.Interop.Localization/xlf/Resources.tr.xlf b/src/Java.Interop.Localization/xlf/Resources.tr.xlf
index 36225df9d..37d702290 100644
--- a/src/Java.Interop.Localization/xlf/Resources.tr.xlf
+++ b/src/Java.Interop.Localization/xlf/Resources.tr.xlf
@@ -104,6 +104,11 @@ In this message, the term "constants" refers to class or interface members that
{1} - .NET field name.
{2} - Java type.
+
+ Type '{0}' has a type name which matches the enclosing namespace name. See https://aka.ms/BG8403 for more information.
+ Type '{0}' has a type name which matches the enclosing namespace name. See https://aka.ms/BG8403 for more information.
+ {0} - Java type.
+ Unexpected child element of '<interface>': '{0}'.Unexpected child element of '<interface>': '{0}'.
diff --git a/src/Java.Interop.Localization/xlf/Resources.zh-Hans.xlf b/src/Java.Interop.Localization/xlf/Resources.zh-Hans.xlf
index 21db2e011..b4611d809 100644
--- a/src/Java.Interop.Localization/xlf/Resources.zh-Hans.xlf
+++ b/src/Java.Interop.Localization/xlf/Resources.zh-Hans.xlf
@@ -104,6 +104,11 @@ In this message, the term "constants" refers to class or interface members that
{1} - .NET field name.
{2} - Java type.
+
+ Type '{0}' has a type name which matches the enclosing namespace name. See https://aka.ms/BG8403 for more information.
+ Type '{0}' has a type name which matches the enclosing namespace name. See https://aka.ms/BG8403 for more information.
+ {0} - Java type.
+ Unexpected child element of '<interface>': '{0}'.Unexpected child element of '<interface>': '{0}'.
diff --git a/src/Java.Interop.Localization/xlf/Resources.zh-Hant.xlf b/src/Java.Interop.Localization/xlf/Resources.zh-Hant.xlf
index 01dbdf7f2..3fff3b789 100644
--- a/src/Java.Interop.Localization/xlf/Resources.zh-Hant.xlf
+++ b/src/Java.Interop.Localization/xlf/Resources.zh-Hant.xlf
@@ -104,6 +104,11 @@ In this message, the term "constants" refers to class or interface members that
{1} - .NET field name.
{2} - Java type.
+
+ Type '{0}' has a type name which matches the enclosing namespace name. See https://aka.ms/BG8403 for more information.
+ Type '{0}' has a type name which matches the enclosing namespace name. See https://aka.ms/BG8403 for more information.
+ {0} - Java type.
+ Unexpected child element of '<interface>': '{0}'.Unexpected child element of '<interface>': '{0}'.
diff --git a/src/Java.Interop.Tools.Generator/Utilities/Report.cs b/src/Java.Interop.Tools.Generator/Utilities/Report.cs
index a556a3d93..a5878d822 100644
--- a/src/Java.Interop.Tools.Generator/Utilities/Report.cs
+++ b/src/Java.Interop.Tools.Generator/Utilities/Report.cs
@@ -1,4 +1,5 @@
using System;
+using System.Diagnostics;
using System.Xml;
using System.Xml.Linq;
@@ -7,6 +8,7 @@ namespace Java.Interop.Tools.Generator
public class Report
{
public static int? Verbosity { get; set; }
+ public static Action? OutputDelegate { get; set; }
public class LocalizedMessage
{
@@ -43,6 +45,7 @@ public LocalizedMessage (int code, string value)
public static LocalizedMessage WarningFieldNameCollision_Method => new LocalizedMessage (0x8401, Localization.Resources.Generator_BG8401_Method);
public static LocalizedMessage WarningFieldNameCollision_NestedType => new LocalizedMessage (0x8401, Localization.Resources.Generator_BG8401_NestedType);
public static LocalizedMessage WarningDuplicateField => new LocalizedMessage (0x8402, Localization.Resources.Generator_BG8402);
+ public static LocalizedMessage WarningTypeNameMatchesEnclosingNamespace => new LocalizedMessage (0x8403, Localization.Resources.Generator_BG8403);
public static LocalizedMessage WarningUnexpectedInterfaceChild => new LocalizedMessage (0x8500, Localization.Resources.Generator_BG8500);
public static LocalizedMessage WarningEmptyEventName => new LocalizedMessage (0x8501, Localization.Resources.Generator_BG8501);
public static LocalizedMessage WarningInvalidDueToInterfaces => new LocalizedMessage (0x8502, Localization.Resources.Generator_BG8502);
@@ -95,7 +98,7 @@ public static void LogCodedError (LocalizedMessage message, XNode? node, params
public static void LogCodedError (LocalizedMessage message, string? sourceFile, int line, int column, params string? [] args)
{
- Console.Error.WriteLine (Format (true, message.Code, sourceFile, line, column, message.Value, args));
+ WriteOutput (TraceLevel.Error, Format (true, message.Code, sourceFile, line, column, message.Value, args));
}
public static void LogCodedWarning (int verbosity, LocalizedMessage message, params string? [] args)
@@ -120,17 +123,17 @@ public static void LogCodedWarning (int verbosity, LocalizedMessage message, Exc
return;
var supp = innerException != null ? " For details, see verbose output." : null;
- Console.Error.WriteLine (Format (false, message.Code, sourceFile, line, column, message.Value, args) + supp);
+ WriteOutput (TraceLevel.Warning, Format (false, message.Code, sourceFile, line, column, message.Value, args) + supp);
if (innerException != null)
- Console.Error.WriteLine (innerException);
+ WriteOutput (TraceLevel.Warning, innerException.ToString ());
}
public static void Verbose (int verbosity, string format, params object?[] args)
{
if (verbosity > (Verbosity ?? 0))
return;
- Console.Error.WriteLine (format, args);
+ WriteOutput (TraceLevel.Verbose, format, args);
}
public static string FormatCodedMessage (bool error, LocalizedMessage message, params object? [] args)
@@ -169,6 +172,18 @@ public static string Format (bool error, int errorCode, string? sourceFile, int
return (file, pos?.LineNumber ?? -1, pos?.LinePosition ?? -1);
}
+
+ static void WriteOutput (TraceLevel traceLevel, string format, params object?[] args)
+ {
+ // Write to overridden output if requested
+ if (OutputDelegate != null) {
+ OutputDelegate (traceLevel, string.Format (format, args));
+ return;
+ }
+
+ // Write to Console.Error
+ Console.Error.WriteLine (format, args);
+ }
}
///
diff --git a/tests/generator-Tests/Unit-Tests/CodeGeneratorTests.cs b/tests/generator-Tests/Unit-Tests/CodeGeneratorTests.cs
index 9217999a6..22c2ec849 100644
--- a/tests/generator-Tests/Unit-Tests/CodeGeneratorTests.cs
+++ b/tests/generator-Tests/Unit-Tests/CodeGeneratorTests.cs
@@ -1,7 +1,10 @@
using System;
using System.Collections.Generic;
+using System.Diagnostics;
using System.Linq;
+using System.Text;
using generator.SourceWriters;
+using Java.Interop.Tools.Generator;
using MonoDroid.Generation;
using NUnit.Framework;
using Xamarin.Android.Binder;
@@ -527,6 +530,47 @@ public void ObsoleteBoundMethodAbstractDeclaration ()
// Ensure [Obsolete] was written
Assert.True (writer.ToString ().Contains ("[Obsolete (@\"This is so old!\")]"), writer.ToString ());
}
+
+ [Test]
+ [NonParallelizable] // We are setting a static property on Report
+ public void WarnIfTypeNameMatchesNamespace ()
+ {
+ var @class = new TestClass ("Object", "java.myclass.MyClass");
+ var sb = new StringBuilder ();
+
+ var write_output = new Action ((t, s) => { sb.AppendLine (s); });
+ Report.OutputDelegate = write_output;
+
+ generator.Context.ContextTypes.Push (@class);
+ generator.WriteType (@class, string.Empty, new GenerationInfo ("", "", "MyAssembly"));
+ generator.Context.ContextTypes.Pop ();
+
+ Report.OutputDelegate = null;
+
+ // Ensure the warning was raised
+ Assert.True (sb.ToString ().Contains ("warning BG8403"));
+ }
+
+ [Test]
+ [NonParallelizable] // We are setting a static property on Report
+ public void DontWarnIfNestedTypeNameMatchesNamespace ()
+ {
+ var @class = new TestClass ("Object", "java.myclass.MyParentClass");
+ @class.NestedTypes.Add (new TestClass ("Object", "java.myclass.MyParentClass.MyClass"));
+ var sb = new StringBuilder ();
+
+ var write_output = new Action ((t, s) => { sb.AppendLine (s); });
+ Report.OutputDelegate = write_output;
+
+ generator.Context.ContextTypes.Push (@class);
+ generator.WriteType (@class, string.Empty, new GenerationInfo ("", "", "MyAssembly"));
+ generator.Context.ContextTypes.Pop ();
+
+ Report.OutputDelegate = null;
+
+ // The warning should not be raised if the nested type matches enclosing namespace
+ Assert.False (sb.ToString ().Contains ("warning BG8403"));
+ }
}
[TestFixture]
diff --git a/tools/generator/Extensions/StringExtensions.cs b/tools/generator/Extensions/StringExtensions.cs
new file mode 100644
index 000000000..3ba6bce0a
--- /dev/null
+++ b/tools/generator/Extensions/StringExtensions.cs
@@ -0,0 +1,45 @@
+using System;
+using System.Diagnostics.CodeAnalysis;
+
+namespace generator
+{
+ static class StringExtensions
+ {
+ ///
+ /// Shortcut for !string.IsNullOrWhiteSpace (s)
+ ///
+ public static bool HasValue (this string s) => !string.IsNullOrWhiteSpace (s);
+
+ ///
+ /// Removes the final subset of a delimited string. ("127.0.0.1" -> "127.0.0")
+ ///
+ public static string ChompLast (this string s, char separator)
+ {
+ if (!s.HasValue ())
+ return s;
+
+ var index = s.LastIndexOf (separator);
+
+ if (index < 0)
+ return string.Empty;
+
+ return s.Substring (0, index);
+ }
+
+ ///
+ /// Returns the final subset of a delimited string. ("127.0.0.1" -> "1")
+ ///
+ public static string LastSubset (this string s, char separator)
+ {
+ if (!s.HasValue ())
+ return s;
+
+ var index = s.LastIndexOf (separator);
+
+ if (index < 0)
+ return s;
+
+ return s.Substring (index + 1);
+ }
+ }
+}
diff --git a/tools/generator/Java.Interop.Tools.Generator.CodeGeneration/JavaInteropCodeGenerator.cs b/tools/generator/Java.Interop.Tools.Generator.CodeGeneration/JavaInteropCodeGenerator.cs
index 3c2af3e78..f84e298f4 100644
--- a/tools/generator/Java.Interop.Tools.Generator.CodeGeneration/JavaInteropCodeGenerator.cs
+++ b/tools/generator/Java.Interop.Tools.Generator.CodeGeneration/JavaInteropCodeGenerator.cs
@@ -44,6 +44,10 @@ public override void WriteType (GenBase gen, string indent, GenerationInfo gen_i
else
throw new InvalidOperationException ("Unknown GenBase type");
+ // We do this here because we only want to check for top-level types,
+ // we should not check types nested in other types.
+ SourceWriterExtensions.WarnIfTypeNameMatchesNamespace (type_writer, gen);
+
var cw = new CodeWriter (writer, indent);
type_writer.Write (cw);
}
diff --git a/tools/generator/SourceWriters/Extensions/SourceWriterExtensions.cs b/tools/generator/SourceWriters/Extensions/SourceWriterExtensions.cs
index e3466ee42..39241081b 100644
--- a/tools/generator/SourceWriters/Extensions/SourceWriterExtensions.cs
+++ b/tools/generator/SourceWriters/Extensions/SourceWriterExtensions.cs
@@ -397,5 +397,18 @@ public static TypeWriter BuildManagedTypeModel (GenBase gen, CodeGenerationOptio
throw new InvalidOperationException ("Unknown GenBase type");
}
+
+ public static void WarnIfTypeNameMatchesNamespace (TypeWriter type, GenBase gen)
+ {
+ // We only care about the last part of a namespace
+ // eg: android.graphics.drawable -> drawable
+ var ns = gen.Namespace?.LastSubset ('.');
+
+ if (!ns.HasValue ())
+ return;
+
+ if (string.Equals (ns, type.Name, StringComparison.OrdinalIgnoreCase))
+ Report.LogCodedWarning (0, Report.WarningTypeNameMatchesEnclosingNamespace, $"{gen.Namespace}.{type.Name}");
+ }
}
}